#include #ifndef _IMAGEHLP_H
#include "imagehlp.h"
#pragma comment ( lib, "imagehlp.lib" )
#endif
#pragma warning(disable : 4996)
// 根据传入的dwAlign粒度调整内存中或文件中对齐后的大小
DWORD Align(DWORD dwNum, DWORD dwAlign){
if (dwNum % dwAlign == 0){
return dwNum;
}else{
return (dwNum / dwAlign + 1) * dwAlign;
}
}
int main(int argc, char* argv[]){
char szFilePath[MAX_PATH];// PE 路径
OPENFILENAME ofn;
HANDLE hFile;// 文件句柄
HANDLE hMapping;// 映射文件句柄
LPVOID ImageBase;// 映射基址
PIMAGE_DOS_HEADER pDH = NULL;// 指向 IMAGE_DOS 结构的指针
PIMAGE_NT_HEADERS pNtH = NULL;// 指向 IMAGE_NT 结构的指针
PIMAGE_FILE_HEADER pFH = NULL;// 指向 IMAGE_FILE 结构的指针
PIMAGE_OPTIONAL_HEADER pOH = NULL;// 指向 IMAGE_OPTIONALE 结构的指针
PIMAGE_SECTION_HEADER pSH1 = NULL;// 指向 IMAGE_SECTION_TABLE 结构的指针first
PIMAGE_SECTION_HEADER pSH2 = NULL;// 指向 IMAGE_SECTION_TABLE 结构的指针two
PIMAGE_SECTION_HEADER pSH3 = NULL;// 指向 IMAGE_SECTION_TABLE 结构的指针three
memset(szFilePath, 0, MAX_PATH);
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.hInstance = GetModuleHandle(NULL);
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = ".";
ofn.lpstrFile = szFilePath;
ofn.lpstrTitle = "选择 PE 文件打开";
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrFilter = "*.exe\0*.exe\0";
if (!GetOpenFileName(&ofn)){
MessageBox(NULL, "打开文件错误", NULL, MB_OK);
return 0;
}
// 选择要分析的文件后,经过3步打开并映射 PE 到虚拟内存中_CreateFile_CreateFileMapping_MapViewOfFile
// 1.创建文件内核对象,其句柄保存于 hFile,将文件在物理存储器的位置通告给操作系统
hFile = CreateFile(szFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (!hFile){
MessageBox(NULL, "打开文件错误", NULL, MB_OK);
return 0;
}
// 2.创建文件映射内核对象(分配虚拟内存),句柄保存于 hFileMapping
hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (!hMapping){
CloseHandle(hFile);
return FALSE;
}
// 3.将文件数据映射到进程的地址空间,返回的映射基址保存在 ImageBase 中
ImageBase = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
if (!ImageBase){
CloseHandle(hMapping);
CloseHandle(hFile);
return FALSE;
}
pDH = (PIMAGE_DOS_HEADER)ImageBase;// IMAGE_DOS Header 结构指针
pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDH + pDH->e_lfanew);// IMAGE_NT Header 结构指针
pFH = &pNtH->FileHeader;// IMAGE_File Header 结构指针
pOH = &pNtH->OptionalHeader;// IMAGE_Optional Header结构指针
pSH1 = IMAGE_FIRST_SECTION(pNtH);// IMAGE_FIRST_SECTION 宏
pSH2 = (PIMAGE_SECTION_HEADER)((DWORD)pNtH + sizeof(IMAGE_NT_HEADERS));
pSH3 = (PIMAGE_SECTION_HEADER)((DWORD)pDH + pOH->SizeOfHeaders);
if (pDH->e_magic != IMAGE_DOS_SIGNATURE || pNtH->Signature != IMAGE_NT_SIGNATURE){
printf("Not a valid PE file...");
return -1;
}
// 创建PSection指针指向原程序中的第一个Section,并创建一个新的Section结构体 secToAdd
PIMAGE_SECTION_HEADER pSection = NULL;
IMAGE_SECTION_HEADER secToAdd = { 0 };// 节区名 Name
// 属性 Characteristics
// 实际被使用的区块大小 Misc.VirtualSize
// 节区在磁盘文件中的大小 SizeOfRawData
// 节区在内存中的RVA VirtualAddress
// 节区在磁盘中的偏移 PointerToRawData
pSection = (PIMAGE_SECTION_HEADER)((BYTE*)pOH + pFH->SizeOfOptionalHeader);// 定位到节区表
DWORD dwSectionNum = pFH->NumberOfSections;
DWORD dwSectionAlign = pOH->SectionAlignment;
DWORD dwFileAlign = pOH->FileAlignment;
DWORD dwOEP = pOH->AddressOfEntryPoint;// 程序执行入口地址VA
dwOEP = (DWORD)(pOH->ImageBase + dwOEP);// 映射起始地址+执行入口地址
pSection = pSection + dwSectionNum - 1;// 将PSection指向原程序节区表中最后一个Section表,这是关键一步,因为新节区所有属性和原来最后一个节区是相同的
strcpy((char *)secToAdd.Name, ".newSec");// 设置新添加的section的名字
secToAdd.Characteristics = pSection->Characteristics;// 设置新添加的section的属性值,与最后一个section取值相同
DWORD vsize = 0x2A01;// 新section大小设置
secToAdd.Misc.VirtualSize = vsize;
secToAdd.SizeOfRawData = Align(secToAdd.Misc.VirtualSize, dwFileAlign);// 根据之前定义的Align函数调用,得到经过处理后的section尺寸大小,经调整后实际大小为0x234,对齐后大小为0x400
secToAdd.VirtualAddress = pSection->VirtualAddress +
Align(pSection->Misc.VirtualSize, dwSectionAlign);// 调用Align函数,得到经过内存对齐处理后的尺寸,新Section的RVA等于原程序最后一个Section的RVA加上该节在内存中的映射尺寸
secToAdd.PointerToRawData = pSection->PointerToRawData + pSection->SizeOfRawData;// 新Section的FA地址等于最后一个Section的FA加上该节的文件对齐尺寸
pSection++;// pSection指向原程序中最后一个节表的下一个,写入新的节表结构
secToAdd.Characteristics = 0xE00000E0;// pSection->Characteristics = 0xE00000E0;
memcpy(pSection, &secToAdd, sizeof(IMAGE_SECTION_HEADER));// 将最后一个节区拷贝到新建的节区
// 打印新节区信息
printf("\n节表添加成功,新节表的信息为:\n");
printf("\nName = %s", secToAdd.Name);
printf("\nVirtualSize = %08lX", secToAdd.Misc.VirtualSize);
printf("\nVirtualAddress = %08lX", secToAdd.VirtualAddress);
printf("\nSizeOfRawData = %08lX", secToAdd.SizeOfRawData);
printf("\nPointerToRawData = %08lX", secToAdd.PointerToRawData);
printf("\n");
// 添加完信息之后要进行三个修改:①修改节区数量NumberOfSections
//②映像大小
//③扩充原PE文件的大小,将Section真正写入到尾部
pNtH->FileHeader.NumberOfSections += 0x1;// 更改PE文件中节表的数量
pOH->SizeOfImage = pOH->SizeOfImage + Align(secToAdd.Misc.VirtualSize, dwSectionAlign);// 修改程序的映像大小,比如VirtualSize=200,SectionAlign=200,那么最后就是400
BYTE bNum = '\x0';// 修改文件大小
DWORD dwWritten = 0;
::SetFilePointer(hFile, 0, 0, FILE_END);
::WriteFile(hFile, &bNum, secToAdd.SizeOfRawData, &dwWritten, NULL);
::UnmapViewOfFile(ImageBase);
::CloseHandle(hMapping);
::CloseHandle(hFile);
return 0;
}