前言

学pe的时候被困扰了很久,终于在某处给我找到了,打算打出来读一读代码



这边我们是从文件中的pe转到运行中的pe,然后再缩小存储到文件的pe

这边我们需要知道内存中对齐是0x1000,文件对齐是0x200(这边是16进制的,误踩坑)

我们第一步是先要把pe文件读出来,存储起来

1、根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer).

2、根据SizeOfHeader的大小,将头信息从FileBuffer拷贝到ImageBuffer

3、根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.

4、Misc.VirtualSize 和 SizeOfRawData谁大?

5、FileBuffer与ImageBuffer谁大?

0x01. exe->filebufeer,返回值为计算所得的文件大小

DWORD ReadPEFile(char* file_path, PVOID* pFileBuffer)
{
FILE* pfile = NULL; // 文件指针
DWORD file_size = 0;
LPVOID pTempFilebuffer = NULL; // 打开文件
pfile = fopen(file_path, "rb"); // 如果有新的指针,就要进行判断
if (!pfile)
{
printf("打开exe文件失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
return 0;
}
// 读取文件大小
fseek(pfile, 0, SEEK_END);
file_size = ftell(pfile);
fseek(pfile, 0, SEEK_SET);
// 分配空间
pTempFilebuffer = malloc(file_size); // 如果有新的指针,就要进行判断
if (!pTempFilebuffer)
{
printf("分配空间失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
fclose(pfile);
return 0;
}
// 将数据读取到内存中
size_t n = fread(pTempFilebuffer, file_size, 1, pfile);
if (!n)
{
printf("数据读取到内存中失败!\n"); //如果分配失败就要关闭文件、释放动态内存、指针指向NULL
fclose(pfile);
free(pTempFilebuffer);
return 0;
}
// 关闭文件(已经读取到内存了)
*pFileBuffer = pTempFilebuffer;
pTempFilebuffer = NULL;
fclose(pfile);
return file_size;
}

1、dword是4字节类型,其实只是int的另外一种写法而已,里面有两个参数,一个是文件路径,一个是文件数据

2、先是常规的读取文件步骤,文件指针,然后定义一个文件大小的变量,一个是临时存放pe文件的变量

3、使用fopen打开文件,判断是否打开失败,其实判断的话也可以删掉,方便调试就留着

4、使用fseek函数 第一个参数是文件指针,第二个是 0,第三个是宏,移动到末尾处,然后通过ftell函数得到文件的大小

5、再把指针复位,也可以使用rewind(pFile);来复位,在通过刚刚获取到的变量file_size的值去开辟一个内存空间

6、然后使用fread函数把文件读取出来,但是我们读取出来还要让计算机知道,所以赋值给了变量n

7、变量n判断是否为为空,如果不为空,则进入下一步,把临时存储文件pTempFilebuffer变量的地址给了*pFileBuffer



从这张图可以看出两者的区别,那就是pFileBuffer上面存储着地址



这时候把刚刚那个变量清空,关闭文件,返回file_size

0x02.filebuffer -> imagebuffer

(1)

然后把文件转到内存中的话需要注意一点(文件对其0x200,内存对其0x1000)

这里也是个难点了,我们一段一段的分析

DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{ }

这边是有两个参数,一个是文件中的pe,另外一个是内存中的pe

注意PVOID其实就是 void*PVOID* 也就是 void**

(2)

// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 初始化IMAGE_BUFFER指针(temparay)
LPVOID pTempImagebuffer = NULL; if (!pFileBuffer)
{
printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");
return 0;
}
// 判断是否是可执行文件
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD)
{
printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
//强制结构体类型转换pDosHeader
pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
//判断是否含有PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
{ //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
printf("(2pimagebuffer阶段)不是有效的PE标志!\n");
return 0;
}



通过这张图我们可以知道pe文件有dos头(PIMAGE_DOS_HEADER)nt头(PIMAGE_NT_HEADERS)、b

再细细分,nt头中有PE签名、然后再PE头(PIMAGE_FILE_HEADER)PE可选头(PIMAGE_OPTIONAL_HEADER32)

1、就先把我们所需要的结构体先初始化为0,然后再初始化一下临时存储内存中PE的变量 pTempImagebuff

2、然后使用if判断读取到内存中的pfilebuffer有没有问题; 再判断是否可执行文件,IMAGE_DOS_SIGNATURE是个宏,里面是0x5A4D



通过这张图知道 e_magic是word字节的,所以我们只需要word字节,由于这是指针,所以是PWORD

(PWORD)pFileBuffer

但是我们这是强转,告诉编译器我们需要的是内存中双字(word)的值的地址



我们还需要再用一个*号来取值

3、再把pFileBuffer中的dos头分出来,可能会有人好奇怎么分,我按照我想法和你说一下

因为dos头也就这几个字节,所以把这个pe文件的前这几个字节分出来,基本就这样,其他的就不多细想了

4、这时候再判断是否存在PE标志

PIMAGE_DOS_HEADER之后的四个字节就是PE标志位了,我们上面得到了 pDosHeader ,所以我们在pFileBuffer+ 248



(注意1:248是10进制的,需要换成16进制的

注意2:pFileBuffer存储的是地址,地址上的内容是pe文件,所以先修改地址,再取值就是另外一个地方)

(3)

pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

1、pFileBuffer 存储的是pe文件的首地址,首地址加上e_lfanew成员,就是nt头的第一个成员

2、pe头:

因为nt头的第一个成员固定是4字节,所以加上4,就是pe头的起始地址,在通过强转分开,填充进PIMAGE_FILE_HEADER结构体中,得到了pe头的内容

3、OptionHeader 可选头:

上面得到了pe头首地址,pe头大小为 IMAGE_SIZEOF_FILE_HEADER

所以 pPEHeader + IMAGE_SIZEOF_FILE_HEADER 就得到了可选头的地址

4、节表:

由于可选头大小无法确定,但是PE头里面SizeOfOptionalHeader有可选头大小

这是上面得到了OptionHeader首地址,首地址加上可选头大小,就是节表的首地址了

总结:PE文件中每一块都是相连的

(4)

      // 分配动态内存
pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImagebuffer)
{
printf("分配动态内存失败!\n");
free(pTempImagebuffer);
return 0;
}
// 初始化动态内存
memset(pTempImagebuffer, 0, pOptionHeader->SizeOfImage);
// 拷贝头部
memcpy(pTempImagebuffer, pDosHeader, pOptionHeader->SizeOfHeaders);

1、sizeofimage是内存中整个PE文件的映射的尺寸,可以比实际的值大

因为我们要从filebuffer转为imagebuffer,需要 内存中的pe + 基址 = 运行中的pe

2、memset(pTempImagebuffer, 0, pOptionHeader->SizeOfImage);

把刚刚开辟的内存全部初始化为0

3、memcpy(pTempImagebuffer, pDosHeader, pOptionHeader->SizeOfHeaders);

拷贝dos头

destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。

source-- 指向要复制的数据源,类型强制转换为 void* 指针。

n-- 要被复制的字节数。

(5)

// 循环拷贝节表
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD i = 0; i<pPEHeader->NumberOfSections; i++, pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImagebuffer + pTempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), pTempSectionHeader->SizeOfRawData);
}
// 返回数据
*pImageBuffer = pTempImagebuffer;
pTempImagebuffer = NULL;
return pOptionHeader->SizeOfImage;

然后就开始拷贝节,使用for循环把节拷进去

然后再把地址存储在*pImageBuffer,最后返回一下内存中的文件大小

可能没有概念,内存对齐中的是0x1000,但是其他数据都是连在一起的,比如dos头,nt头,节表这三个连在一起,然后因为内存1000,就直接在后面全部为0,然后在下一个0x1000的地址再添上新数据

源代码:

DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 初始化IMAGE_BUFFER指针(temparay)
LPVOID pTempImagebuffer = NULL; if (!pFileBuffer)
{
printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");
return 0;
}
// 判断是否是可执行文件
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD)
{
printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
//强制结构体类型转换pDosHeader
pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
//判断是否含有PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
{ //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
printf("(2pimagebuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); // 分配动态内存
pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImagebuffer)
{
printf("分配动态内存失败!\n");
free(pTempImagebuffer);
return 0;
}
// 初始化动态内存
memset(pTempImagebuffer, 0, pOptionHeader->SizeOfImage);
// 拷贝头部
memcpy(pTempImagebuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
// 循环拷贝节表
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD i = 0; i<pPEHeader->NumberOfSections; i++, pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImagebuffer + pTempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), pTempSectionHeader->SizeOfRawData);
}
// 返回数据
*pImageBuffer = pTempImagebuffer;
pTempImagebuffer = NULL;
return pOptionHeader->SizeOfImage;
}DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 初始化IMAGE_BUFFER指针(temparay)
LPVOID pTempImagebuffer = NULL; if (!pFileBuffer)
{
printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");
return 0;
}
// 判断是否是可执行文件
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD)
{
printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
//强制结构体类型转换pDosHeader
pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
//判断是否含有PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
{ //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
printf("(2pimagebuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); // 分配动态内存
pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImagebuffer)
{
printf("分配动态内存失败!\n");
free(pTempImagebuffer);
return 0;
}
// 初始化动态内存
memset(pTempImagebuffer, 0, pOptionHeader->SizeOfImage);
// 拷贝头部
memcpy(pTempImagebuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
// 循环拷贝节表
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD i = 0; i<pPEHeader->NumberOfSections; i++, pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImagebuffer + pTempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), pTempSectionHeader->SizeOfRawData);
}
// 返回数据
*pImageBuffer = pTempImagebuffer;
pTempImagebuffer = NULL;
return pOptionHeader->SizeOfImage;
}

0x03.imagebuffer -> newbuffer

DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer, PVOID* pNewBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL; // 初始化NEW_BUFFER指针(temparay)
LPVOID pTempNewbuffer = NULL; // 判断pImageBuffer是否有效
if (!pImageBuffer)
{
printf("(2pnewbuffer阶段)读取到内存的pimagebuffer无效!\n");
return 0;
}
//判断是不是exe文件
if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("(2pnewbuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
// 强制结构体类型转换
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if (*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("(2pnewbuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); // 这里必须强制类型转换
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); //获取new_buffer的大小
int new_buffer_size = pOptionHeader->SizeOfHeaders;
for (DWORD i = 0; i<pPEHeader->NumberOfSections; i++)
{
new_buffer_size += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一种加法
}
// 分配内存(newbuffer)
pTempNewbuffer = malloc(new_buffer_size);
if (!pTempNewbuffer)
{
printf("(2pnewbuffer阶段)分配Newbuffer失败!\n");
return 0;
}
memset(pTempNewbuffer, 0, new_buffer_size);
// 拷贝头部
memcpy(pTempNewbuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
// 循环拷贝节区
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD j = 0; j<pPEHeader->NumberOfSections; j++, pTempSectionHeader++)
{ //PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸
memcpy((PDWORD)((DWORD)pTempNewbuffer + pTempSectionHeader->PointerToRawData), (PDWORD)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress), pTempSectionHeader->SizeOfRawData);
}
//返回数据
*pNewBuffer = pTempNewbuffer; //暂存的数据传给参数后释放
pTempNewbuffer = NULL;
return new_buffer_size; // 返回计算得到的分配内存的大小
}

1、这边也是传入两个参数,一个是内存中的pe文件 pimagebuffer,一个新的pe文件

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL; // 初始化NEW_BUFFER指针(temparay)
LPVOID pTempNewbuffer = NULL;

也是初始化这些自定义好的结构体,然后初始化,创建一个临时存储数据的指针类型pTempNewbuffer

// 判断pImageBuffer是否有效
if (!pImageBuffer)
{
printf("(2pnewbuffer阶段)读取到内存的pimagebuffer无效!\n");
return 0;
}
//判断是不是exe文件
if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("(2pnewbuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
// 强制结构体类型转换
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if (*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("(2pnewbuffer阶段)不是有效的PE标志!\n");
return 0;
}

判断是否有读取到内存中imagebuffer

if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)

然后这边强转pImageBuffer,意思就是把 pImageBuffer当成一个PWORD读出它指向的东西,然后再进行对比

接下来就跟之前一样了

newbuffer->写入exe

int newbuffer_write2_exe(PVOID NewFileBuffer, DWORD FileSize, char* FilePath)
{
FILE* fp1 = fopen(FilePath, "wb");
if (fp1 != NULL)
{
fwrite(NewFileBuffer, FileSize, 1, fp1);
}
fclose(fp1);
return 1; }

最后就完成了,源代码如下


#include <stdio.h>
#include <malloc.h>
#include <windows.h> // exe->filebuffer 返回值为计算所得文件大小
int ReadPEFile(char* file_path, PVOID* pFileBuffer)
{
FILE* pfile = NULL; // 文件指针
DWORD file_size = 0;
LPVOID pTempFilebuffer = NULL; // 打开文件
pfile = fopen(file_path, "rb"); // 如果有新的指针,就要进行判断
if (!pfile)
{
printf("打开exe文件失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
return 0;
}
// 读取文件大小
fseek(pfile, 0, SEEK_END);
file_size = ftell(pfile);
fseek(pfile, 0, SEEK_SET);
// 分配空间
pTempFilebuffer = malloc(file_size); // 如果有新的指针,就要进行判断
if (!pTempFilebuffer)
{
printf("分配空间失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
fclose(pfile);
return 0;
}
// 将数据读取到内存中
size_t n = fread(pTempFilebuffer, file_size, 1, pfile);
if (!n)
{
printf("数据读取到内存中失败!\n"); //如果分配失败就要关闭文件、释放动态内存、指针指向NULL
fclose(pfile);
free(pTempFilebuffer);
return 0;
}
// 关闭文件(已经读取到内存了)
*pFileBuffer = pTempFilebuffer;
pTempFilebuffer = NULL;
fclose(pfile);
return file_size;
} // filebuffer -> imagebuffer
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 初始化IMAGE_BUFFER指针(temparay)
LPVOID pTempImagebuffer = NULL; if (!pFileBuffer)
{
printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");
return 0;
}
// 判断是否是可执行文件
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD)
{
printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
//强制结构体类型转换pDosHeader
pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
//判断是否含有PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
{ //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
printf("(2pimagebuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); // 分配动态内存
pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImagebuffer)
{
printf("分配动态内存失败!\n");
free(pTempImagebuffer);
return 0;
}
// 初始化动态内存
memset(pTempImagebuffer, 0, pOptionHeader->SizeOfImage);
// 拷贝头部
memcpy(pTempImagebuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
// 循环拷贝节表
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD i = 0; i<pPEHeader->NumberOfSections; i++, pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImagebuffer + pTempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), pTempSectionHeader->SizeOfRawData);
}
// 返回数据
*pImageBuffer = pTempImagebuffer;
pTempImagebuffer = NULL;
return pOptionHeader->SizeOfImage;
} //imagebuffer->newbuffer
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer, PVOID* pNewBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL; // 初始化NEW_BUFFER指针(temparay)
LPVOID pTempNewbuffer = NULL; // 判断pImageBuffer是否有效
if (!pImageBuffer)
{
printf("(2pnewbuffer阶段)读取到内存的pimagebuffer无效!\n");
return 0;
}
//判断是不是exe文件
if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("(2pnewbuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
// 强制结构体类型转换
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if (*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("(2pnewbuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); // 这里必须强制类型转换
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); //获取new_buffer的大小
int new_buffer_size = pOptionHeader->SizeOfHeaders;
for (DWORD i = 0; i<pPEHeader->NumberOfSections; i++)
{
new_buffer_size += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一种加法
}
// 分配内存(newbuffer)
pTempNewbuffer = malloc(new_buffer_size);
if (!pTempNewbuffer)
{
printf("(2pnewbuffer阶段)分配Newbuffer失败!\n");
return 0;
}
memset(pTempNewbuffer, 0, new_buffer_size);
// 拷贝头部
memcpy(pTempNewbuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
// 循环拷贝节区
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for (DWORD j = 0; j<pPEHeader->NumberOfSections; j++, pTempSectionHeader++)
{ //PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸
memcpy((PDWORD)((DWORD)pTempNewbuffer + pTempSectionHeader->PointerToRawData), (PDWORD)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress), pTempSectionHeader->SizeOfRawData);
}
//返回数据
*pNewBuffer = pTempNewbuffer; //暂存的数据传给参数后释放
pTempNewbuffer = NULL;
return new_buffer_size; // 返回计算得到的分配内存的大小
} //newbuffer->存盘
int newbuffer_write2_exe(PVOID NewFileBuffer, DWORD FileSize, char* FilePath)
{
FILE* fp1 = fopen(FilePath, "wb");
if (fp1 != NULL)
{
fwrite(NewFileBuffer, FileSize, 1, fp1);
}
fclose(fp1);
return 1; } void operate_pe()
{ // 初始化操作
PVOID pFileBuffer = NULL;
PVOID pImageBuffer = NULL;
PVOID pNewFileBuffer = NULL;
DWORD NewFileBufferSize = 0;
//char file_path[] = "D:\\Lib\\IPMSG2007.exe";
char file_path[] = "C:/Users/86183/Desktop/PEtest.exe";
char write_file_path[] = "C:/Users/86183/Desktop/result.exe"; // exe->filebuffer
int ret1 = ReadPEFile(file_path, &pFileBuffer); // &pFileBuffer(void**类型) 传递地址对其值可以进行修改
printf("exe->filebuffer 返回值为计算所得文件大小:%#x\n", ret1);
// filebuffer -> imagebuffer
int ret2 = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
printf("filebuffer -> imagebuffer返回值为计算所得文件大小:%#x\n", ret2);
//imagebuffer->newbuffer
int FileSize = CopyImageBufferToNewBuffer(pImageBuffer, &pNewFileBuffer);
printf("imagebuffer->newbuffer返回值为计算所得文件大小:%#x\n", FileSize);
//newbuffer->存盘
int ret4 = newbuffer_write2_exe(pNewFileBuffer, FileSize, write_file_path);
printf("newbuffer->存盘返回值为:%d\n", ret4);
} int main()
{
operate_pe();
getchar();
return 0;
}

文件中pe到内存中pe的更多相关文章

  1. Java——动态创建Class(不写入文件,直接从内存中创建class)

    原文:https://blog.csdn.net/zhao_xinhu/article/details/82499062#commentsedit 参考:https://www.cnblogs.com ...

  2. C#中string在内存中是如何表示的

    不知道你是否有过和我一样的疑问,不同编码的字符串是如何存储在运行时的内存中的呢,计算机在操作string类型的对象时,如何知道这个string是什么编码呢?和文本文件那样有类似BOM的东东在strin ...

  3. VB6之多维数组中元素在内存中的排列情况

    Private Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal ...

  4. Java中数组在内存中的图解

    Java中的数组在内存中的图解,其实对于数组,还是比较熟悉的,平时用的也是很多的,在看数据结构与算法的极客时间专栏,最常用的10个数据结构:数组.链表.栈.队列.散列表.二叉树.堆.跳表.图.Trie ...

  5. C语言中浮点数在内存中的存储方式

    关于多字节数据类型在内存中的存储问题 //////////////////////////////////////////////////////////////// int ,short 各自是4. ...

  6. python中变量在内存中的存储与地址关系解析、浅度/深度copy、值传递、引用传递

    ---恢复内容开始--- 1.变量.地址 变量的实现方式有:引用语义.值语义 python语言中变量的实现方式就是引用语义,在变量里面保存的是值(对象)的引用(值所在处内存空间的地址).采用这种方式, ...

  7. Java中对象在内存中的大小、分配等问题

    Java创建一个对象的过程 是否对象指向的类已经加载到内存了 如果没有加载,就要经过load.linking(verification.preparation.resolution).initiali ...

  8. 【POI】导出excel文件,不生成中间文件,直接将内存中的数据创建对象下载到浏览器

    不是从InputStream中read,然后outputStream再write @RequestMapping("download4Excel") public void dow ...

  9. Java中数组在内存中的存放原理?

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/yangyong0717/article/details/79165685Java中数组被实现为对象, ...

随机推荐

  1. tf.split函数的用法(tensorflow1.13.0)

    tf.split(input, num_split, dimension): dimension指输入张量的哪一个维度,如果是0就表示对第0维度进行切割:num_split就是切割的数量,如果是2就表 ...

  2. python语言概述

    python语言的发展 python语言诞生于1990年,由Guide van Rossum设计并领导开发. python语言是开源项目的优秀代表,其解释器的全部代码都是开源的. 编写Hello程序 ...

  3. 微服务实战系列(七)-网关springcloud gateway

    1. 场景描述 springcloud刚推出的时候用的是netflix全家桶,路由用的zuul,但是据说zull1.0在大数据量访问的时候存在较大性能问题,2.0就没集成到springcloud中了, ...

  4. APPCNA 指纹验证登录

    今天在APP中集成了指纹与手势登录功能,本文章分两部分进行记录.一是手势功能的逻辑.二是代码实现.该APP是采用APPCAN开发,直接用其已写好的插件,调用相当接口就要可以了. 1.在APP的个人中心 ...

  5. 在KEIL下查看单片机编程内存使用情况

    原文链接:https://blog.csdn.net/D_azzle/article/details/83410141 截至到目前为止,本人接触单片机也有将近一年的时间.这一年以来也接触过了很具代表性 ...

  6. ByPass Mode(略过模式或旁路模式)

    参考: 1. https://baike.baidu.com/item/%E6%97%81%E8%B7%AF%E6%A8%A1%E5%BC%8F/3120563 2. https://zhidao.b ...

  7. 转C++了

    积极响应"某王"的号召,联赛之后转C辣!(好吧,其实是它拿着一把西瓜刀顶在我背后逼我转的)

  8. redis 开启AOF

    找到redis 安装目录 例如 cd /usr/local/redis 打开 redis.conf  修改以下参数: # vi /usr/local/redis/etc/redis.conf appe ...

  9. 53.Qt-QPdfWriter绘制PDF,支持表单输出

    之前打印PDF都是通过html形式来实现的,但是这次要做的东西,需要打印界面控件,所以需要使用QPdfWriter. 通过QPdfWriter来获取QPainter对象,就能实现在PDF上来画画啦. ...

  10. intelliJ 软件项目打开运行

    1.导入项目 2.首先更改数据库,找到application-dev.yml文件,更改数据源 3.配置tomcat端口  找到application.yml 文件 然后打开pom.xml 更改版本号 ...