本文代码很受用,而且还有代码段讲解,特此收藏!
目标
写一个程序,读取磁盘上exe程序,并且在内存中展开,且运行
代码
我先给全部代码,下面是解释部分
// RunExeThread.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> PIMAGE_DOS_HEADER dosHeader; PIMAGE_NT_HEADERS ntHeader; PIMAGE_FILE_HEADER fileHeader; PIMAGE_EXPORT_DIRECTORY exportDirectory; PIMAGE_SECTION_HEADER pSectionHeader; PIMAGE_IMPORT_DESCRIPTOR importDescriptor; PIMAGE_THUNK_DATA thunkData; PIMAGE_IMPORT_BY_NAME ordAndName; PIMAGE_BASE_RELOCATION baseRelocation; char file[]="路径\\game.exe"; //这是读取文件的路径 LPBYTE lpBaseAddress; int _tmain(int argc, _TCHAR* argv[]) { //将磁盘文件映射到内存中 HANDLE hcf = CreateFileA(file,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); HANDLE hcfm = CreateFileMappingA(hcf,NULL,PAGE_READONLY,0,0,0); lpBaseAddress=(LPBYTE)MapViewOfFile(hcfm,FILE_MAP_READ,0,0,0); //获取一些基本变量 dosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; int lfanew = dosHeader->e_lfanew; ntHeader = (PIMAGE_NT_HEADERS)(lpBaseAddress+lfanew); int sizeOfImage = ntHeader->OptionalHeader.SizeOfImage; int sizeOfHeader = ntHeader->OptionalHeader.SizeOfHeaders; pSectionHeader = (PIMAGE_SECTION_HEADER)(lpBaseAddress+lfanew+sizeof(IMAGE_NT_HEADERS)); //创建内存空间 LPBYTE loca = (LPBYTE)VirtualAlloc(NULL,sizeOfImage,MEM_COMMIT,PAGE_EXECUTE_READWRITE); //拷贝内存空间 LPBYTE virseek = (LPBYTE)loca; memcpy(loca,lpBaseAddress,sizeOfHeader); if(sizeOfHeader%0x1000!=0) { virseek+=0x1000; } virseek += (sizeOfHeader/0x1000)*0x1000; LPBYTE sectionStartAddr = lpBaseAddress+sizeOfHeader; for(int i=0;i<(ntHeader->FileHeader.NumberOfSections);i++,pSectionHeader++) { int sectionSize = pSectionHeader->Misc.VirtualSize/0x1000; if(pSectionHeader->Misc.VirtualSize%0x1000!=0) { sectionSize++; } memcpy(virseek,sectionStartAddr,pSectionHeader->SizeOfRawData); sectionStartAddr+=pSectionHeader->SizeOfRawData; virseek += sectionSize*0x1000; } //修复IAT表 ntHeader = (PIMAGE_NT_HEADERS)(loca+lfanew); int vAddr = ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress; importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(loca + vAddr); while(importDescriptor->Name) { HMODULE moduleHandle = LoadLibraryA((LPCSTR)(loca+importDescriptor->Name)); thunkData = (PIMAGE_THUNK_DATA)(importDescriptor->FirstThunk+loca); DWORD * indexAddr = (DWORD *)(importDescriptor->FirstThunk+loca); while(thunkData->u1.AddressOfData) { if(IMAGE_SNAP_BY_ORDINAL(thunkData->u1.AddressOfData)) { LPCSTR addr = (LPCSTR)thunkData->u1.AddressOfData-0x80000000; *indexAddr = (DWORD)GetProcAddress(moduleHandle,addr); }else{ ordAndName = (PIMAGE_IMPORT_BY_NAME)(loca + thunkData->u1.AddressOfData); *indexAddr = (DWORD)GetProcAddress(moduleHandle,(LPCSTR)(ordAndName->Name)); } thunkData++; indexAddr++; } importDescriptor++; } //修复重定位表 baseRelocation = (PIMAGE_BASE_RELOCATION)(loca + (ntHeader->OptionalHeader.DataDirectory[5].VirtualAddress)); int num; WORD *data; while(baseRelocation->VirtualAddress+baseRelocation->SizeOfBlock != 0) { data = (WORD *)((LPBYTE)baseRelocation+0x8); num = (baseRelocation->SizeOfBlock-0x8)/sizeof(WORD); for(int i = 0;i<num;i++) { if((DWORD)(data[i]&0x0000F000)==0x00003000) { DWORD* addr = (DWORD *)(loca+baseRelocation->VirtualAddress+(data[i]&0x00000FFF)); *addr +=(DWORD)(loca - ntHeader->OptionalHeader.ImageBase); } } baseRelocation = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseRelocation + baseRelocation->SizeOfBlock); } HANDLE h =CreateThread(0,0,(LPTHREAD_START_ROUTINE)(loca+ntHeader->OptionalHeader.AddressOfEntryPoint),0,0,0); DWORD d = WaitForSingleObject(h,INFINITE); VirtualFree(loca,0,MEM_RELEASE); CloseHandle(hcfm); CloseHandle(hcf); return 0; }
文件映射到内存中,且初始化一些变量
//将磁盘文件映射到内存中 //打开文件 HANDLE hcf = CreateFileA(file,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); //创建文件映射 HANDLE hcfm = CreateFileMappingA(hcf,NULL,PAGE_READONLY,0,0,0); //将文件映射视图映射到调用进程的地址空间。基址 lpBaseAddress=(LPBYTE)MapViewOfFile(hcfm,FILE_MAP_READ,0,0,0); //获取一些基本变量 dosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; int lfanew = dosHeader->e_lfanew; ntHeader = (PIMAGE_NT_HEADERS)(lpBaseAddress+lfanew); int sizeOfImage = ntHeader->OptionalHeader.SizeOfImage; int sizeOfHeader = ntHeader->OptionalHeader.SizeOfHeaders; pSectionHeader = (PIMAGE_SECTION_HEADER)(lpBaseAddress+lfanew+sizeof(IMAGE_NT_HEADERS));
以0x1000粒度拷贝到内存
拷贝过程
1.拷贝各个头部,即节区头以上的部分。(PE结构中在可选头中有SizeOfHeaders指明了大小)
2.各个节区块,如下图
//创建内存空间 LPBYTE loca = (LPBYTE)VirtualAlloc(NULL,sizeOfImage,MEM_COMMIT,PAGE_EXECUTE_READWRITE); //拷贝内存空间 LPBYTE virseek = (LPBYTE)loca; //拷贝头 memcpy(loca,lpBaseAddress,sizeOfHeader); //第一个头拷贝完后要先对齐一下,再进行拷贝 if(sizeOfHeader%0x1000!=0) { virseek+=0x1000; } virseek += (sizeOfHeader/0x1000)*0x1000; //拷贝节区块 LPBYTE sectionStartAddr = lpBaseAddress+sizeOfHeader; for(int i=0;i<(ntHeader->FileHeader.NumberOfSections);i++,pSectionHeader++) { int sectionSize = pSectionHeader->Misc.VirtualSize/0x1000; if(pSectionHeader->Misc.VirtualSize%0x1000!=0) { sectionSize++; } memcpy(virseek,sectionStartAddr,pSectionHeader->SizeOfRawData); sectionStartAddr+=pSectionHeader->SizeOfRawData; virseek += sectionSize*0x1000; }
修改导出表
有人会问为什么要修复导出表呢~
这就跟PE文件展开有关
导入表在磁盘中的存储值如下图
导出表中有两个成员,OriginalFirstThunk和FirstThunk。他们一开始指向的是不同的IMAGE_THUNK_DATA结构体数组。但是这两个数组指向的内容却是相同的。
在程序运行后
OriginalFirstThunk没有发生变化
但是FirstThunk却发生了改变
他在运行完成后指向的地方变成了函数的入口地址。
因为它的改变,我们就要去修改它。
//修复IAT表 ntHeader = (PIMAGE_NT_HEADERS)(loca+lfanew); int vAddr = ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress; importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(loca + vAddr); while(importDescriptor->Name) { HMODULE moduleHandle = LoadLibraryA((LPCSTR)(loca+importDescriptor->Name)); thunkData = (PIMAGE_THUNK_DATA)(importDescriptor->FirstThunk+loca); DWORD * indexAddr = (DWORD *)(importDescriptor->FirstThunk+loca); while(thunkData->u1.AddressOfData) { if(IMAGE_SNAP_BY_ORDINAL(thunkData->u1.AddressOfData)) { LPCSTR addr = (LPCSTR)thunkData->u1.AddressOfData-0x80000000; *indexAddr = (DWORD)GetProcAddress(moduleHandle,addr); }else{ ordAndName = (PIMAGE_IMPORT_BY_NAME)(loca + thunkData->u1.AddressOfData); *indexAddr = (DWORD)GetProcAddress(moduleHandle,(LPCSTR)(ordAndName->Name)); } thunkData++; indexAddr++; } importDescriptor++; }
修复重定向
这里又要了解下为啥修复重定向咯
我们程序有个基址重定向机制,有了这个机制,我们才能在一台电脑中跑多个程序。不然默认的exe基址都是0x400000,会有冲突。
在带来了更好的体验的同时,也给我带来了小困扰 --无明
总而言之,言而总之。就是基址变化,重定位表里面存放的是针对默认的基址准备的VA地址
现在我们基址改变了。我们就要改变他。
//修复重定位表 baseRelocation = (PIMAGE_BASE_RELOCATION)(loca + (ntHeader->OptionalHeader.DataDirectory[5].VirtualAddress)); int num; WORD *data; while(baseRelocation->VirtualAddress+baseRelocation->SizeOfBlock != 0) { data = (WORD *)((LPBYTE)baseRelocation+0x8); num = (baseRelocation->SizeOfBlock-0x8)/sizeof(WORD); for(int i = 0;i<num;i++) { if((DWORD)(data[i]&0x0000F000)==0x00003000) { DWORD* addr = (DWORD *)(loca+baseRelocation->VirtualAddress+(data[i]&0x00000FFF)); *addr +=(DWORD)(loca - ntHeader->OptionalHeader.ImageBase); } } baseRelocation = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseRelocation + baseRelocation->SizeOfBlock); }
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2299
- 用户1336
- 访客10618802
每日一句
You leave, or I go with you.
你留下,或者我跟你走。——《海角七号》
你留下,或者我跟你走。——《海角七号》
新会员