为什么修改pe文件入口点后会显示不是有效的PE文件

md名字又重复 2019-04-25 04:22:06
各位大佬们救救孩子!!我想请问一下为什么修改入口点后会变成一个不是有效的PE文件
入口点的值也已经修改成我新增代码的首地址
如果没有修改入口点的话就不会出现这个问题






下面是我的代码。谢谢各位大佬们了
#include <windows.h>
#include <Commdlg.h>
#include <stdio.h>

//引入外部库,也可以在project----setting---link中设置
#ifndef _IMAGEHLP_H
#include "imagehlp.h"
#pragma comment ( lib, "imagehlp.lib" )
#endif
#pragma warning(disable:4996)

// 根据传入的dwAlign粒度调整内存中或文件中对齐后的大小
//DWORD全称Double Word,是指注册表的键值,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。
//Align:对齐
DWORD Align(DWORD dwNum, DWORD dwAlign)
{
if (dwNum % dwAlign == 0)
{
return dwNum;
}
else
{
return (dwNum / dwAlign + 1) * dwAlign;
}
}

//Shellcode
char codes[] = "\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9\x64\x8b"
"\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08\x8b\x7e\x20\x8b"
"\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1\xff\xe1\x60\x8b\x6c\x24"
"\x24\x8b\x45\x3c\x8b\x54\x28\x78\x01\xea\x8b\x4a\x18\x8b\x5a"
"\x20\x01\xeb\xe3\x34\x49\x8b\x34\x8b\x01\xee\x31\xff\x31\xc0"
"\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x3b\x7c"
"\x24\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5a"
"\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc3\xb2"
"\x08\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e\x0e\xec\x52\xe8\x9f"
"\xff\xff\xff\x89\x45\x04\xbb\x7e\xd8\xe2\x73\x87\x1c\x24\x52"
"\xe8\x8e\xff\xff\xff\x89\x45\x08\x68\x6c\x6c\x20\x41\x68\x33"
"\x32\x2e\x64\x68\x75\x73\x65\x72\x88\x5c\x24\x0a\x89\xe6\x56"
"\xff\x55\x04\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c\x24\x52"
"\xe8\x61\xff\xff\xff\x68\x6f\x78\x58\x20\x68\x61\x67\x65\x42"
"\x68\x4d\x65\x73\x73\x31\xdb\x88\x5c\x24\x0a\x89\xe3\x68\x58"
"\x20\x20\x20\x68\x4d\x53\x46\x21\x68\x72\x6f\x6d\x20\x68\x6f"
"\x2c\x20\x66\x68\x48\x65\x6c\x6c\x31\xc9\x88\x4c\x24\x10\x89"
"\xe1\x31\xd2\x52\x53\x51\x52\xff\xd0\x31\xc0\x50\xff\x55\x08";


int main(int argc, char* argv[])
{
char szFilePath[MAX_PATH];//要分析的文件名及路径
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结构的指针
//SECTION TABLE就是节表
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

DWORD* oldentry, newentry;

//必要的初始换
memset(szFilePath, 0, MAX_PATH);
memset(&ofn, 0, sizeof(ofn));//memset是计算机中C / C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
ofn.lStructSize = sizeof(ofn); //结构的大小
ofn.hwndOwner = NULL; //窗口标示 也就是句柄
ofn.hInstance = GetModuleHandle(NULL);//实例标示 //GetModuleHandle功能是获取一个应用程序或动态链接库的模块句柄。
ofn.nMaxFile = MAX_PATH; //指定lpstrFileTitle缓冲的大小,以TCHARs为单位。
ofn.lpstrInitialDir = "."; //指向以空字符结束的字符串,可以在这个字符串中指定初始目录。
ofn.lpstrFile = szFilePath; //文件路径缓存区,不获取则设为NULL
ofn.lpstrTitle = "选择 PE文件打开 by For"; //文件名缓存区
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步打开并映射选择的文件到虚拟内存中
//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);//将hFile映射到hMapping
if (!hMapping)//如果无法创建文件映射
{
CloseHandle(hFile);//关闭hFile句柄
return FALSE;
}

//3.将文件数据映射到进程的地址空间,返回的映射基址保存在ImageBase中
ImageBase = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
if (!ImageBase)
{
CloseHandle(hMapping);//关闭hMapping句柄
CloseHandle(hFile);//关闭hFile句柄
return FALSE;
}

//IMAGE_DOS Header结构指针
pDH = (PIMAGE_DOS_HEADER)ImageBase;//ImageBase是PE文件的优先装载地址。
//IMAGE_NT Header结构指针
pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDH + pDH->e_lfanew); //DOS头加偏移量
//IMAGE_File Header结构指针
pFH = &pNtH->FileHeader;//标准pe头,最基本的文件信息

//IMAGE_Optional Header结构指针
pOH = &pNtH->OptionalHeader; //可选pe头
oldentry = &pOH->AddressOfEntryPoint;



//IMAGE_SECTION_TABLE(节表)结构的指针3种方法
pSH1 = IMAGE_FIRST_SECTION(pNtH);//1.利用IMAGE_FIRST_SECTION宏来得到第一个IMAGE_SECTION_HEADER的位置

pSH2 = (PIMAGE_SECTION_HEADER)((DWORD)pNtH + sizeof(IMAGE_NT_HEADERS));//DOS头加偏移量+IMAGE_NT_HEADERS的大小

pSH3 = (PIMAGE_SECTION_HEADER)((DWORD)pDH + pOH->SizeOfHeaders); //PE文件的优先装载地址+(DOS头(64B)++PE标记(4B)+标准PE头(20B)+可选PE头+节表的总大小)

// 检查文件是否是一个有效的PE文件的步骤
/*
1.检验文件头部第一个字的值是否等于IMAGE_DOS_SIGNATURE。(即检查MZ头是否为0x4D5A)
2.然后用e_lfanew来定位PE header
3.比较PE header的第一个字的值是否等于IMAGE_NT_HEADER。(即判断pe头是否为0x4550)
符合以上全部条件即为有效的PE文件
*/
if (pDH->e_magic != IMAGE_DOS_SIGNATURE || pNtH->Signature != IMAGE_NT_SIGNATURE)
//e_magic !是DOS签名“MZ"
{
printf("Not valid PE file...");
return -1;
}

PIMAGE_SECTION_HEADER pSection = NULL;
IMAGE_SECTION_HEADER secToAdd = { 0 };
pSection = (PIMAGE_SECTION_HEADER)((BYTE*)pOH + pFH->SizeOfOptionalHeader);//指向原程序中的第一个SECTION。
//可选PE头+IMAGE_OPTIONAL_HEADER32 结构大小


DWORD dwSectionNum = pFH->NumberOfSections; //文件的区块(节表)数目
DWORD dwSectionAlign = pOH->SectionAlignment; //内存对齐数
DWORD dwFileAlign = pOH->FileAlignment;//文件对齐数
DWORD dwOEP = pOH->AddressOfEntryPoint; // 程序执行入口地址
dwOEP = (DWORD)(pOH->ImageBase + dwOEP); // 映射起始地址+执行入口地址

// 将PSection指向原程序中最后一个Section,根据最后一个Section的内容设置新的Section
pSection = pSection + dwSectionNum - 1;

// 设置新添加的section的名字
strcpy((char*)secToAdd.Name, ".PATCHPASSWORD");
// 设置新添加的section的属性值,与最后一个section取值相同
secToAdd.Characteristics = pSection->Characteristics;

// 新section大小设置
DWORD vsize = sizeof(codes);
secToAdd.Misc.VirtualSize = vsize; //VirtualSize是节在内存中的长度(是对齐前的长度)
// 根据之前定义的Align函数调用,得到经过处理后的section尺寸大小
secToAdd.SizeOfRawData = Align(secToAdd.Misc.VirtualSize, dwFileAlign);//Align是上面定义的对齐函数

// 调用Align函数,得到经过内存对齐处理后的尺寸
secToAdd.VirtualAddress = pSection->VirtualAddress + Align(pSection->Misc.VirtualSize, dwSectionAlign);

// 新Section的FA地址等于最后一个Section的FA加上该节的文件对齐尺寸
secToAdd.PointerToRawData = pSection->PointerToRawData + pSection->SizeOfRawData;
//char* newDirrection =secToAdd.PointerToRawData;

//写入代码
SetFilePointer(hFile, secToAdd.PointerToRawData, NULL, FILE_END);
DWORD direction = SetFilePointer(hFile, secToAdd.PointerToRawData, NULL, FILE_END);
printf("0x%08x\n", SetFilePointer(hFile, secToAdd.PointerToRawData, NULL, FILE_END));
WriteFile(hFile, codes, secToAdd.Misc.VirtualSize, NULL, NULL);
printf("0x%08x\n", secToAdd.Misc.VirtualSize);


// pSection指向原程序中最后一个节表的下一个,写入新的节表结构
pSection++;
secToAdd.Characteristics = 0xE00000E0;

//将secToAdd的字节(信息)到新添加的节区里
memcpy(pSection, §oAdd, sizeof(IMAGE_SECTION_HEADER));



// 更改PE文件中节表的数量
WORD dwSizeAdd = 0x1;
pNtH->FileHeader.NumberOfSections += dwSizeAdd;


// 修改程序的映像大小
pOH->SizeOfImage = pOH->SizeOfImage + Align(secToAdd.Misc.VirtualSize, dwSectionAlign);

// 修改文件大小
BYTE bNum = '\x0';
DWORD dwWritten = 0;

::SetFilePointer(hFile, 0, 0, FILE_END);
::WriteFile(hFile, &bNum, secToAdd.SizeOfRawData, &dwWritten, NULL);
//重定位入口
pOH->AddressOfEntryPoint = direction;
printf("0x%08x", pOH->AddressOfEntryPoint);

//UnmapViewOfFile,是停止当前程序的一个内存映射
::UnmapViewOfFile(ImageBase);
//关闭句柄
::CloseHandle(hMapping);
::CloseHandle(hFile);

return 0;
}
...全文
253 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧