PE加载过程

目录

前言

过程

1.char* Loading_File(char* path);

2.char* FileBuffer_To_ImageBuffer(char * FileBuf);

3.char* ImageBuffer_To_FileBuffer(char* ImageBuf, int * len)

4.void Dump_File(char* FileBuf, int num, char * path)

5.函数调用

总结


前言

        当我们双击一个可执行程序的时候,操作系统会把文件从硬盘上读入到内存中,那么读到内存中的文件和硬盘中的文件是否有所区别呢?答案是肯定的,今天我们来说一下操作系统在PE加载过程中所作的第一步,那就是文件从硬盘载入内存的拉伸过程,也就是FILE_BUFFER到IMAGE_BUFFER,我这篇博客中又多做了一步,也就是从IMAGE_BUFFER再压缩回FILE_BUFFER,然后存盘,看看是否可以正常使用。

        在写代码之前给大家说一下思路:

                ①将文件从硬盘中读出到内存。

                ②通过寻找一些关键性成员确定IMAGE_BUFFER的大小和进行拉伸

                ③将IMAGE_BUFFER按照和第二步类似的思路压缩回去

                ④存盘

        搞定!!

过程

首先先看一下封装的函数和包含的库

通过下列四个函数完成功能,下面详细介绍一下每个函数的过程。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

//将文件读入内存
char* Loading_File(char* path);

//进行PE加载
char* FileBuffer_To_ImageBuffer(char * FileBuf);

//将ImageBUFFer转换成FILEbuffer
char* ImageBuffer_To_FileBuffer(char* ImageBuf, int * len);

//存盘
void Dump_File(char* FileBuf, int num, char * path);

1.char* Loading_File(char* path);

这个是文件的读入,简单说一下,通过fopen打开一个文件,然后通过fseek将文件指针置到文件末尾,通过ftell算出文件的大小,通过ftell的大小分配内存,然后通过fread读入内存。

//参数:传入文件路径
char* Loading_File(char* path)
{
	if (path == NULL)
	{
		printf("路径为空\n");
		return 0;
	}
	FILE* fp = fopen(path, "rb");
	if (fp == NULL)
	{
		printf("打开文件失败\n");
		return 0;
	}
	fseek(fp, 0, SEEK_END);
	int len = ftell(fp);

	fseek(fp, 0, SEEK_SET);
	char* FileBuf = (char*)malloc(len);
	if (FileBuf == 0)
	{
		printf("分配内存失败\n");
		return 0;
	}
	fread(FileBuf, 1, len, fp);
	fclose(fp);
	return FileBuf;
}

2.char* FileBuffer_To_ImageBuffer(char * FileBuf);

要实现把FileBuffer拉伸成ImageBuffer,大家务必需要知道的几个知识就是,如果文件的文件对齐和内存对齐是相同的就不需要拉伸了,如果不同就需要拉伸。

那么在拉伸的过程中需要怎么做呢?

首先我们应该先知道我们拉伸后的ImageBuffer有多大,这个在可选PE头里面的SizeOfImage中存放着。

 char * FileBuf中存放着FileBuffer的首地址,我们知道这是指向DOS头部的,DOS头部的大小是64个字节,最后一个成员记录着NT头的开始位置偏移(相对于FILEBUFFER)

我们通过int* IntPointTemp = (int*)(FileBuf + 0x3c);拿到PE头偏移。

拿到偏移之后,指向的是50450000的标识,也就是PE标识,跳过这四个字节就是标准PE头了,为了下面的功能实现,我们还需要拿到SizeOfOptionHeaders(可选PE头的大小)和NumberOfSection(节的数量,同样是节表的数量),这是为了锁定到节表,然后这里我们拿到这些之后,CharPointTemp是指向NT头的,通过CharPointTemp +4(PE标识)+20(标准PE头)+0x38(SizeOfImage的相对偏移),就找到了SizeOfImage,再+2(SizeOfHeaders的相对偏移),然后这里我们都已经找到了所需要的东西。

然后下面开始我们代码的编写:

这里给大家说一下这个代码的思路,所有的代码也已经给大家贴在下面了,

1.拿到SizeOfImage之后,我们就可以分配内存了,这里我们要注意把内存清0。

2.先拷贝PE头,通过SizeOfHeaders。

PE头拷贝完成之后,接下来就是循环拷贝节了,节的VirtualAddress,和SizeOfRawData,PointOfRawData,是我们拷贝的关键。

3.这里写个循环,外层循环条件是NumberOfSection,然后内部每一次都获取到节的VirtualAddress,和SizeOfRawData,PointOfRawData,内层循环以SizeOfRawData为循环条件,这里大家要注意VirtualAddress,PointOfRawData是相对与ImageBuffer和FileBuffer的偏移,对应到这里为其在堆中分配的内存首地址,VirtualAddress,PointOfRawData加上他们才是真正的目的地址和源地址。每一个节表的长度是40个字节,每次循环之后记得+40索引下一个节表。

//参数FileBuf:传入FileBuf在内存当作的首地址
char* FileBuffer_To_ImageBuffer(char * FileBuf)
{
	if (FileBuf == NULL)
	{
		printf("FileBuf载入失败");
		return 0;
	}
	//拿到PE头偏移
	int* IntPointTemp = (int*)(FileBuf + 0x3c);
	int Turn = *IntPointTemp;
	char* CharPointTemp = (FileBuf + Turn);
	char* CharPointTemp_Bak = CharPointTemp;
	//拿到节的数量和可选PE头
	short* ShortPointTemp = (short*)(CharPointTemp+4+2);
	short NumberOfSections = *ShortPointTemp;
	ShortPointTemp += 7;
	short SizeOfOptionalHeader = *ShortPointTemp;
	CharPointTemp = (CharPointTemp + 24 + 0x38);
	
	//拿到Size_Of_Image
	IntPointTemp = (int*)CharPointTemp;
	int SizeOfImage = *IntPointTemp;
	//拿到SizeOfHeaders
	int SizeOfHeaders = *(IntPointTemp + 1);
	
	
	//分配内存ImageBuffer
	char* ImageBuffer = (char*)malloc(SizeOfImage);
	if (ImageBuffer == NULL)
	{
		printf("ImageBuffer内存分配失败\n");
		return 0;
	}


	//ImageBuffer清0
	memset(ImageBuffer, 0, SizeOfImage);
	
	//PE头拷贝
	char* FileBuf_Bak = FileBuf;
	char* ImageBuffer_Bak = ImageBuffer;
	int i = 0;
	while (i < SizeOfHeaders)
	{
		*(ImageBuffer_Bak + i) = *(FileBuf_Bak + i);
		i++;
	}

	//节的拷贝
	FileBuf_Bak = FileBuf;
	ImageBuffer_Bak = ImageBuffer;
	//CharPointTemp_Bak指向节表
	CharPointTemp_Bak = (CharPointTemp_Bak + 24 + SizeOfOptionalHeader);
	i = 0;

	
	while (i < NumberOfSections)
	{
		int* VirtualAddress = (int*)(CharPointTemp_Bak + 0xc);
		int* SizeOfRawData = (int*)(CharPointTemp_Bak + 0x10);
		int* PointerToRawData = (int*)(CharPointTemp_Bak + 0x14);
		char* VirtualAddress_02 = ((*VirtualAddress) + ImageBuffer_Bak);
		char* PointerToRawData_02 = ((*PointerToRawData) + FileBuf_Bak);
		int j = 0;
		
		while (j < (*SizeOfRawData))
		{
			*(VirtualAddress_02 + j) = *(PointerToRawData_02 + j);
			j++;
		}
		CharPointTemp_Bak += 40;
		i++;
	}

	return ImageBuffer;
}

3.char* ImageBuffer_To_FileBuffer(char* ImageBuf, int * len)

这里一个要注意的点是我们要计算应该为File_Buffer分配多大的内存空间。

思路是SizeOfHeaders+每一个节表中的SizeOfRawData

1.找到应该为File_Buffer分配的内存大小

2.先拷贝PE头

3.一样的我们还是需要获取VirtualAddress,和SizeOfRawData,PointOfRawData,只不过拷贝的方向相反了。

//ImageBuF:ImageBuffer在内存的首地址,len为了返回压缩后的FileBuffer的长度
char* ImageBuffer_To_FileBuffer(char* ImageBuf, int * len)
{
	if (ImageBuf == NULL)
	{
		return 0;
	}
	char* ImageBuf_Bak = ImageBuf;
	//拿到PE头偏移
	int* IntPointTemp = (int*)(ImageBuf + 0x3c);
	int Turn = *IntPointTemp;
	//指向PE头
	char* CharPointTemp = (ImageBuf + Turn);
	char* CharPointTemp_Bak = CharPointTemp;
	//拿到节的数量和可选PE头
	short* ShortPointTemp = (short*)(CharPointTemp + 4 + 2);
	short NumberOfSections = *ShortPointTemp;
	ShortPointTemp += 7;
	short SizeOfOptionalHeader = *ShortPointTemp;
	CharPointTemp = (CharPointTemp + 24 + 0x38);

	//拿到Size_Of_Image
	IntPointTemp = (int*)CharPointTemp;
	
	int SizeOfImage = *IntPointTemp;
	//拿到SizeOfHeaders
	int SizeOfHeaders = *(IntPointTemp + 1);
	
	//计算为File_Buffer分配的内存数量
	int Total_Len_Of_File_Buffer = SizeOfHeaders;
	CharPointTemp_Bak = (CharPointTemp_Bak + 4 + 20 + SizeOfOptionalHeader);
	char* CharPointTemp_Bak_2 = (CharPointTemp_Bak + 0x10);
	int i = 0;
	while (i < NumberOfSections)
	{
		int* SizeOfRawData = (int*)CharPointTemp_Bak_2;

		Total_Len_Of_File_Buffer += *(SizeOfRawData);
		CharPointTemp_Bak_2 += 40;
		i++;
	}
	//分配内存
	char* FileBuf = (char*)malloc(Total_Len_Of_File_Buffer);
	if (FileBuf == NULL)
	{
		printf("FileBuf分配内存失败\n");
		return 0;
	}
	memset(FileBuf, 0, Total_Len_Of_File_Buffer);
	char* FileBuf_Bak = FileBuf;
	//写入FileBuffer
	i = 0;
	while (i < SizeOfHeaders)
	{
		if ((FileBuf + i == NULL) || (ImageBuf + i == NULL))
		{
			printf("内存越界了\n");
			return 0;
		}
		*(FileBuf + i) = *(ImageBuf + i);
		i++;
	}
	
	i = 0;
	CharPointTemp_Bak_2 = CharPointTemp_Bak;
	
	while (i < NumberOfSections)
	{
		int* VirtualAddress = (int*)(CharPointTemp_Bak_2 + 0xc);
		int * PointerToRawData = (int*)(CharPointTemp_Bak_2 + 0x14);
		int* SizeOfRawData = (int*)(CharPointTemp_Bak_2 + 0x10);
		int j = 0;
		while(j < (*SizeOfRawData))
		{
			*(FileBuf_Bak+(*PointerToRawData) + j) = *(ImageBuf_Bak + (*VirtualAddress) + j);
			j++;
		}
		CharPointTemp_Bak_2 += 40;
		i++;
	}
	*len = Total_Len_Of_File_Buffer;
	return FileBuf_Bak;

}

4.void Dump_File(char* FileBuf, int num, char * path)

这个就是一个内存写入到文件的过程,我们上一个函数返回的len就是为了传入给这个函数做为读出的长度。

//FileBuf:内存首地址
//num:要拷贝的字节数
//写入文件的路径
void Dump_File(char* FileBuf, int num, char * path)
{
	FILE* fp = fopen(path, "wb");
	fwrite(FileBuf, 1, num, fp);
}

5.函数调用

int main(void)
{
	char path[] = "D:\\QQcache\\notepad++.exe";
	//加载内存
	char* FileBuf = Loading_File(path);
	
	if (FileBuf == NULL)
	{
		printf("Failed to FileBuf\n");
		return 0;
	}
	//filebuf到imageBuf
	char * Image_Buf = FileBuffer_To_ImageBuffer(FileBuf);
	if (Image_Buf == NULL)
	{
		printf("Failed to FileBuffer_To_ImageBuffer\n");
	}
	
	//imageBuf到filebuf
	int num = 0;
	char* FileBuf02 = ImageBuffer_To_FileBuffer(Image_Buf, &num);
	if (FileBuf02 == NULL)
	{
		printf("Failed to ImageBuffer_To_FileBuffer");
	}
	
	//写入磁盘
	char path02[] = "D:\\QQcache\\notepad++2.exe";
	Dump_File(FileBuf02, num, path02);
	

	return 0;
}

总结

 这里验证可以运行。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值