最近又复习了一下PE结构中重定位相关的内容,又想到内存加载dll这个未曾涉足的领域。
便想着自己实现一波。
可参考此篇博客,本人觉得结构清晰,简洁明了。
内存直接加载运行DLL
我写的应该比较口语化,流水账,啰嗦。
重定位主要是对代码里面使用绝对地址的地方进行修改。
方便起见,就写一个简单的dll,然后手动映射,实现dllMain和正常加载一样,弹出信息框。
写一个带重定位的DLL
reloc.asm
.386 .model flat,stdcall option casemap:none include windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kernel32.lib .data titleText db "This is dll onl oad",0 .code DllEntry proc _hInstance,_dwReason,_dwReserved invoke MessageBox,NULL,NULL,addr titleText,MB_OK mov eax,TRUE ret DllEntry endp addNum proc numA:DWORD,numB:DWORD mov eax,numA add eax,numB ret addNum endp End DllEntry
简单的dll,就只是有个弹窗以及一个加法的导出函数
reloc.def
EXPORTS addNum
编译链接:
C:\Users\yyjeqhc\Desktop\memoryLoad>ml -c -coff reloc.asm Microsoft (R) Macro Assembler Version 6.14.8444 Copyright (C) Microsoft Corp 1981-1997. All rights reserved. Assembling: reloc.asm *********** ASCII build *********** C:\Users\yyjeqhc\Desktop\memoryLoad>link -subsystem:windows -DLL -def:reloc.def reloc.obj Microsoft (R) Incremental Linker Version 5.12.8078 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Creating library reloc.lib and object reloc.exp C:\Users\yyjeqhc\Desktop\memoryLoad>
即可生成reloc.dll
生成了dll,还是需要验证一下正常加载的功能。
直接vs2015创建一个工程;添加一个main.cpp
main.cpp:
#include<iostream> #include<windows.h> using namespace std; int main() { HMODULE module = LoadLibrary("reloc.dll"); if (!module) { cout << "加载失败\n"; } else { cout << "加载成功\n"; using Add = int(__stdcall *)(int, int);//因为汇编里面是stdcall的调用方式,所以指针前面也要加stdcall Add add = (Add)GetProcAddress(module, "addNum"); cout << add(1, 5) << endl; } system("pause"); }
再把reloc.dll复制到release文件夹里面,点击运行即可。
测试完了。我们再看一下PE结构
这是自己写的peinfo工具,按照自己习惯就好了。
可以知道0xA00处开始的0xC字节就是重定位块的所有内容。
再贴上winnt里面重定位相关的结构:
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress;//起始偏移地址 DWORD SizeOfBlock;//重定位块的大小 // WORD TypeOffset[1]; } IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
也就是从0xA00开始的8个字节代表上述结构。这个重定位块的大小正好和数据目录表的重定位块大小相同,所以这个文件就这一个重定位块。假设数据目录表里面比0x0C大,那么下一个重定位块就是从0xA00+0xC开始的。
例子比较小,常见的PE文件中,重定位块就是
重定位结构+重定位数据 /重定位结构+重定位数据 /重定位结构+重定位数据
这样紧凑的一个接着一个,每个块里面的起始偏移地址相同。
结合调试器就好理解了。
在这个dll里面,只有dllMain函数有用到全局变量,也就需要重定位。
上面图片的第一处就对应调试器里面的偏移量为1000的位置,也是重定位结构的起始偏移地址,也就是相对PE文件加载基址的偏移。
上面图片的第二处就是从该重定位结构开始的字节长度。
重定位结构结束以后,该重定位块剩下的字节以字为单位构成一个一个重定位的项。一般忽略高4位即可,因为一般最高4位都是3,有其特殊含义,这里不细讲了。
上述的第3和第4处就是这里面的2处需要重定位的地方。(C-8)/2=2,需要修改两处
具体来看需要重定位的地方就是相对于PE映像基址 起始偏移地址+重定位项低12位代表的地址 的偏移量处的地方。
看调试器即可。
0x10001005处push一个全局变量(绝对地址),去掉开头的push占用的一个字节,也就是从0x10001006处开始的4个字节需要修改。对应上述第3处。1000+(3)0006处
添加资源。
然后点击自定义,新建资源类型即可。名称自己随意输入。
再次点击资源,这次选择导入,再选择刚才的reloc.dll,即可添加dll到资源
加载资源
#include<iostream> #include<windows.h> #include<winnt.h> #include "resource1.h"//这个因为摸索测试,所以多了一些资源文件 using namespace std; int main() { HRSRC rsrc = FindResource(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_DLL2), "dll");//一般都是用的ID,类似于动态加载dll里面用需要获取函数地址一样,不知道怎么用字符串名称来查找资源 if (!rsrc) { cout << "查找资源失败\n"; system("pause"); } else { cout << "查找资源成功 rsrc = " << rsrc << endl; } HGLOBAL global = LoadResource(NULL, rsrc); if (!global) { cout << "加载资源失败\n"; system("pause"); } else { cout << "加载资源成功 global = " << global << endl; } LPVOID addr = LockResource(global); if (!addr) { cout << "锁定资源失败\n"; system("pause"); } else { printf("锁定资源成功 addr = %X\n", addr); } system("pause"); }
手动映射导入表
直接看PEinfo的信息,比对文件里面的数据和程序加载dll后的相应位置内存里面的数据即可。
文件:
内存:
这里就是说文件0x600处的数据被映射到内存0x2000偏移的地方。
好在,导入表其实不需要改什么,需要动手的是IAT表(IAT在dll加载后会被修改为对应引入函数的地址)。这里dll只引入了user32.dll以及它的MessageBoxA函数.
直接看代码实现吧。
last.cpp
#include<iostream> #include<windows.h> #include<winnt.h> #include "peinfo.h" #include "resource1.h"//这个因为摸索测试,所以多了一些资源文件 using namespace std; LPVOID loadResource(int resourceID, char* resourceType) { HRSRC rsrc = FindResource(GetModuleHandle(NULL), MAKEINTRESOURCE(resourceID), resourceType);//一般都是用的ID,类似于动态加载dll里面用需要获取函数地址一样,不知道怎么用字符串名称来查找资源 if (!rsrc) { cout << "查找资源失败\n"; return NULL; } else { cout << "查找资源成功 rsrc = " << rsrc << endl; } HGLOBAL global = LoadResource(NULL, rsrc); if (!global) { cout << "加载资源失败\n"; return NULL; } else { cout << "加载资源成功 global = " << global << endl; } LPVOID addr = LockResource(global); if (!addr) { cout << "锁定资源失败\n"; return NULL; } else { printf("锁定资源成功 addr = %X\n", addr); } return addr; } int main() { int resourceId = IDR_DLL2; char resourceType[] = "dll"; LPVOID resourceAddr = loadResource(resourceId, resourceType); if (!resourceAddr) { cout << "DLL加载失败,无法内存调用\n"; system("pause"); } HMODULE module = memoryLoad((char*)resourceAddr); using Add = int(__stdcall *)(int, int);//因为汇编里面是stdcall的调用方式,所以指针前面也要加stdcall Add adda = (Add)GetProcAddress(module, "addNum"); if (!adda) { printf("获取函数地址失败\n"); } else { cout << adda(1, 5) << endl; } system("pause"); }
peinfo.h
#pragma once #include<iostream> #include<windows.h> #include<winnt.h> #include<time.h> int add(int, int); HMODULE memoryLoad(char* addr);
peinfo.cpp
#include "peinfo.h" using namespace std; const static char* tableName[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] = { "导出表","导入表","资源表","异常表",\ "安全表","重定位表",\ "调试表","版权表","全局指针表","线程本地存储",\ "加载配置表","绑定导入表","IAT表",\ "延迟导入表","CLR表","保留未用" }; DWORD RvaToFva(char* base, DWORD Va) { int sectionNum = ((PIMAGE_NT_HEADERS32)(base + ((PIMAGE_DOS_HEADER)base)->e_lfanew))->FileHeader.NumberOfSections; PIMAGE_SECTION_HEADER sectionTable = IMAGE_FIRST_SECTION((PIMAGE_NT_HEADERS32)(base + ((PIMAGE_DOS_HEADER)base)->e_lfanew)); for (int i = 0; i<sectionNum; i++) { if ((sectionTable[i].VirtualAddress + sectionTable[i].SizeOfRawData)>Va)//考虑是一个内存中的VA而不是PE里面的VA { return sectionTable[i].PointerToRawData + (Va - sectionTable[i].VirtualAddress); } } return NULL; } //这个自己加载dll大体上就是自己解析一下pe结构,然后映射到内存里面;直接把之前写的pe解析拿来修改修改就好了。 //直接对照正常exe动态加载dll时候,dll的内存映像进行修改即可 HMODULE memoryLoad(char* base) { PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)base; PIMAGE_NT_HEADERS32 nth = (PIMAGE_NT_HEADERS32)(base + dosHeader->e_lfanew); PIMAGE_FILE_HEADER fileh = (PIMAGE_FILE_HEADER)&(nth->FileHeader); PIMAGE_OPTIONAL_HEADER32 ophead = (PIMAGE_OPTIONAL_HEADER32)&(nth->OptionalHeader); char str[100]; if (ophead->SizeOfHeaders > 0x1000) { cout << "PE头太大了,超过0x1000,需要修改\n"; return NULL; } int imageSize = ophead->SizeOfImage; int imageBase = ophead->ImageBase; printf("PE映像大小: %8X\n",imageSize); //直接根据映像大小申请空间 char* baseAddr = (char*)VirtualAlloc(NULL, imageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!baseAddr) { cout << "申请空间失败\n"; return NULL; } else { printf("申请空间成功 baseAddr = %X\n", baseAddr); memset(baseAddr, 0, imageSize); } //1.直接把PE文件按照头/节拷贝过去吧,然后再慢慢处理节区里面的内容。 memcpy(baseAddr, base, ophead->SizeOfHeaders); cout << "\n数据目录表----------------------------------------------\n"; cout << "名称 \t内存偏移\t数据大小\t文件偏移\t指向文件偏移\n"; for (int i = 0; i<IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { printf("%-12s\t0x%-8X\t0x%-8X\t0x%-8X\t0x%-8X\n", tableName[i], ophead->DataDirectory[i].VirtualAddress, ophead->DataDirectory[i].Size, ((char*)&ophead->DataDirectory[i] - (char*)base), ophead->DataDirectory[i].VirtualAddress == 0 ? 0 : RvaToFva(base, ophead->DataDirectory[i].VirtualAddress)); } cout << "--------------------------------------------------------\n"; cout << endl; PIMAGE_SECTION_HEADER sectionTable = IMAGE_FIRST_SECTION(nth); cout << "节区表----------------------------------------------------------------------------------\n"; cout << "节区名称\t节区文件偏移\t节区内存偏移\t节区大小\t节区对齐大小\t节区属性\t属性解释\n"; sprintf(str, "%-8X\t%-12X\t%-12X\t%-8X\t%-12X\t%-8X\n", sizeof(IMAGE_SECTION_HEADER::Name), sizeof(IMAGE_SECTION_HEADER::PointerToRawData), sizeof(IMAGE_SECTION_HEADER::VirtualAddress), sizeof(IMAGE_SECTION_HEADER::VirtualAddress), sizeof(IMAGE_SECTION_HEADER::SizeOfRawData), sizeof(IMAGE_SECTION_HEADER::Characteristics)); cout << str; string attr; for (int i = 0; i<fileh->NumberOfSections; i++) { if (true) { IMAGE_SECTION_HEADER data = sectionTable[i]; attr = ""; if ((data.Characteristics & 0x20000000) == 0x20000000) { attr += "E"; } if ((data.Characteristics & 0x40000000) == 0x40000000) { attr += "R"; } if ((data.Characteristics & 0x80000000) == 0x80000000) { attr += "W"; } if ((data.Characteristics & 0x20) == 0x20) { attr += "C"; } if ((data.Characteristics & 0x10000000) == 0x10000000) { attr += "S"; } if ((data.Characteristics & 0x8000000) == 0x8000000) { attr += " no up"; } if ((data.Characteristics & 0x4000000) == 0x4000000) { attr += " no chche"; } if ((data.Characteristics & 0x2000000) == 0x2000000) { attr += " reloc"; } if ((data.Characteristics & 0x80) == 0x80) { attr += " uninitdata"; } if ((data.Characteristics & 0x40) == 0x40) { attr += " initdata"; } } sprintf(str, "%-8s\t0x%-12X\t%-12X\t%-8X\t%-12X\t%-8X\t%-s\n", sectionTable[i].Name, sectionTable[i].PointerToRawData, sectionTable[i].VirtualAddress, sectionTable[i].Misc.VirtualSize, sectionTable[i].SizeOfRawData, sectionTable[i].Characteristics, attr.c_str()); cout << str; memcpy(baseAddr + sectionTable[i].VirtualAddress, base + sectionTable[i].PointerToRawData, sectionTable[i].Misc.VirtualSize); } cout << "指向文件偏移--------------------------------------------------------------------------\n"; for (int i = 0; i<fileh->NumberOfSections; i++) { sprintf(str, "FVA:0x%-8X\t0x%-12X\t0x%-12X\t0x%-8X\t0x%-12X\t0x%-8X\n", ((char*)§ionTable[i] - (char*)base), ((char*)§ionTable[i].PointerToRawData - (char*)base), ((char*)§ionTable[i].VirtualAddress - (char*)base), ((char*)§ionTable[i].Misc.VirtualSize - (char*)base), ((char*)§ionTable[i].SizeOfRawData - (char*)base), ((char*)§ionTable[i].Characteristics - (char*)base)); cout << str; } cout << "----------------------------------------------------------------------------------------\n"; //导入表,这个需要自己改吧,主要是需要自己加载所需的动态库,以及修复IAT表 if (ophead->DataDirectory[1].VirtualAddress) { cout << "导入表---------------------------------------------------------------\n"; DWORD importAddrBegin = RvaToFva(base, ophead->DataDirectory[1].VirtualAddress); int importDllNum = ophead->DataDirectory[1].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);//最后多20个字节的NULL,但是也可能是别人手动修改的 PIMAGE_IMPORT_DESCRIPTOR importTable = (PIMAGE_IMPORT_DESCRIPTOR)((unsigned char*)base + importAddrBegin); for (int i = 0; i<importDllNum; i++) { if (importTable[i].Name == 0 || importTable[i].Characteristics == 0) { break; } HMODULE dllModule = LoadLibrary((char*)(base + RvaToFva(base, importTable[i].Name)));//加载动态库 if (!dllModule) { cout << "导入表加载动态库失败! " << (char*)(base + RvaToFva(base, importTable[i].Name)) << endl; return NULL; } cout << "属性名称 \t属性 \t文件偏移\t指向文件偏移\n"; printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "OriginalFirstThunk", importTable[i].OriginalFirstThunk, ((char*)&importTable[i].OriginalFirstThunk - (char*)base), RvaToFva(base, importTable[i].OriginalFirstThunk)); printf("%-20s\t0x%-8X\t0x%-8X\n", "TimeDateStamp", importTable[i].TimeDateStamp, ((char*)&importTable[i].TimeDateStamp - (char*)base)); printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "Name", importTable[i].Name, ((char*)&importTable[i].Name - (char*)base), RvaToFva(base, importTable[i].Name)); printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "FirstThunk", importTable[i].FirstThunk, ((char*)&importTable[i].FirstThunk - (char*)base), RvaToFva(base, importTable[i].FirstThunk)); cout << "dllname: " << (char*)(base + RvaToFva(base, importTable[i].Name)) << endl; PIMAGE_THUNK_DATA32 thunkData = (PIMAGE_THUNK_DATA32)((unsigned char*)base + RvaToFva(base, importTable[i].OriginalFirstThunk)); cout << "\n文件偏移 \tHint \tName\n"; int funcIndex = 0; while (!(thunkData->u1.AddressOfData & 0x80000000) && (thunkData->u1.AddressOfData))//修复IAT { DWORD funcBegin = RvaToFva(base, thunkData->u1.AddressOfData); PIMAGE_IMPORT_BY_NAME func = (PIMAGE_IMPORT_BY_NAME)((unsigned char*)base + funcBegin); printf("0x%-18X\t0x%-8X\t%s\n", funcBegin, func->Hint, func->Name); LPVOID funcAddr = GetProcAddress(dllModule, func->Name); printf("funcAddr = %X\n", funcAddr); if (!funcAddr) { cout << "获取函数地址失败!\n"; return NULL; } memcpy((char*)(baseAddr + importTable[i].FirstThunk + funcIndex * 4), (char*)&funcAddr, 4);//依照顺序修复IAT printf("写入后 %X\n", *(DWORD*)(baseAddr + importTable[i].FirstThunk) + funcIndex * 4); funcIndex++; thunkData++; } cout << "----------------------------------\n"; } } //重定位表 if (ophead->DataDirectory[5].VirtualAddress) { cout << "重定位表还需要处理\n"; cout << "重定位表\n"; DWORD relocBegin = RvaToFva(base, ophead->DataDirectory[5].VirtualAddress); PIMAGE_BASE_RELOCATION relocTable = (PIMAGE_BASE_RELOCATION)((char*)base + relocBegin); while (relocTable->VirtualAddress) { int relocCount = (relocTable->SizeOfBlock - 8) / 2; WORD *table = new WORD[relocCount]; memcpy(table, (char*)relocTable + 8, relocCount*2); for (int i = 0; i < relocCount; i++) { table[i] &= 0x0FFF;//去掉前面高4位 printf("table = %X\n", table[i]); printf("写入前 %X\n", *(DWORD*)(baseAddr + relocTable->VirtualAddress + table[i])); *(DWORD*)(baseAddr + relocTable->VirtualAddress + table[i]) += DWORD(baseAddr - imageBase); printf("写入后 %X\n", *(DWORD*)(baseAddr + relocTable->VirtualAddress + table[i])); } relocTable = (PIMAGE_BASE_RELOCATION)((char*)relocTable + relocTable->SizeOfBlock);//不断遍历重定位块 } } //导出表,好像没有什么需要修改的地方,不用管 //if (ophead->DataDirectory[0].VirtualAddress) if(false) { cout << "导出表----------------------------------------------------------------\n"; DWORD exportBegin = RvaToFva(base, ophead->DataDirectory[0].VirtualAddress); cout << "属性名称 \t属性 \t文件偏移\t指向文件偏移\n"; PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)((char*)base + exportBegin); time_t time = exportTable->TimeDateStamp; struct tm* ttime; ttime = localtime(&time); char now[24]; strftime(now, 24, "%Y-%m-%d %H:%M:%S", ttime); printf("%-20s\t0x%-8X\t0x%-8X\n", "Characteristics", exportTable->Characteristics, ((char*)&exportTable->Characteristics - (char*)base)); printf("%-20s\t0x%-8X\t0x%-8X\t%s\n", "TimeDateStamp", exportTable->TimeDateStamp, ((char*)&exportTable->TimeDateStamp - (char*)base), now); printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "Name", exportTable->Name, ((char*)&exportTable->Name - (char*)base), RvaToFva(base, exportTable->Name)); printf("%-20s\t0x%-8X\t0x%-8X\n", "Base", exportTable->Base, ((char*)&exportTable->Base - (char*)base)); printf("%-20s\t%-8d\t0x%-8X\n", "NumberOfFunctions", exportTable->NumberOfFunctions, ((char*)&exportTable->NumberOfFunctions - (char*)base)); printf("%-20s\t%-8d\t0x%-8X\n", "NumberOfNames", exportTable->NumberOfNames, ((char*)&exportTable->NumberOfNames - (char*)base)); printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "AddressOfFunctions", exportTable->AddressOfFunctions, ((char*)&exportTable->AddressOfFunctions - (char*)base), RvaToFva(base, exportTable->AddressOfFunctions)); printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "AddressOfNames", exportTable->AddressOfNames, ((char*)&exportTable->AddressOfNames - (char*)base), RvaToFva(base, exportTable->AddressOfNames)); printf("%-20s\t0x%-8X\t0x%-8X\t0x%-8X\n", "AddressOfNameOrdinals", exportTable->AddressOfNameOrdinals, ((char*)&exportTable->AddressOfNameOrdinals - (char*)base), RvaToFva(base, exportTable->AddressOfNameOrdinals)); cout << "dllName = " << (char*)(base + RvaToFva(base, exportTable->Name)) << endl; int nameNum = exportTable->NumberOfNames; int funcNum = exportTable->NumberOfFunctions; // printf("AddressOfFunctions = %X\n",exportTable->AddressOfFunctions); // printf("AddressOfNames = %X\n",exportTable->AddressOfNames); // printf("AddressOfNameOrdinals = %X\n",exportTable->AddressOfNameOrdinals); WORD* hint = (WORD*)((char*)base + RvaToFva(base, exportTable->AddressOfNameOrdinals)); DWORD* names = (DWORD*)((char*)base + RvaToFva(base, exportTable->AddressOfNames)); DWORD* funcs = (DWORD*)((char*)base + RvaToFva(base, exportTable->AddressOfFunctions)); bool *noName = new bool[funcNum]; memset(noName, 1, funcNum); cout << "内存偏移\t文件偏移\tHint\t访问标号\tName\n"; for (int i = 0; i<nameNum; i++) { // printf("hint:%d name:%s %X\n",hint[i],((char*)base + RvaToFva(base,names[i])),funcs[i]); printf("0x%-8X\t0x%-8X\t0x%-4X\t%-8d\t%s\n", funcs[i], RvaToFva(base, funcs[i]), hint[i], (exportTable->Base + hint[i]), ((char*)base + RvaToFva(base, names[i]))); noName[hint[i]] = false; } cout << "内存偏移\t文件偏移\t访问标号\n"; bool haveNoName = false; for (int i = 0; i<funcNum; i++) { if (noName[i]) { printf("0x%-8X\t0x%-8X\t%d\n", funcs[i], RvaToFva(base, funcs[i]), (exportTable->Base + i)); haveNoName = true; } } if (!haveNoName) { cout << "没有无名函数\n"; } cout << "----------------------------------------------------------------------\n"; } //延迟导入表,也先不管 //if (ophead->DataDirectory[13].VirtualAddress) if(false) { cout << "延迟导入表\n"; DWORD delayBegin = RvaToFva(base, ophead->DataDirectory[13].VirtualAddress); PIMAGE_DELAYLOAD_DESCRIPTOR delayTable = (PIMAGE_DELAYLOAD_DESCRIPTOR)((char*)base + delayBegin); int delayDllNum = ophead->DataDirectory[13].Size / sizeof(IMAGE_DELAYLOAD_DESCRIPTOR); while (delayTable->DllNameRVA != 0) { printf("dllName = %s\n", ((char*)base + RvaToFva(base, delayTable->DllNameRVA))); cout << "属性 \t内存偏移\t文件偏移\t指向文件偏移\n"; printf("%-22s\t0x%-8X\t0x%-8X\t0x%-8X\n", "DllNameRVA", delayTable->DllNameRVA, ((char*)&delayTable->DllNameRVA - (char*)base), RvaToFva(base, delayTable->DllNameRVA)); printf("%-22s\t0x%-8X\t0x%-8X\t0x%-8X\n", "ModuleHandleRVA", delayTable->ModuleHandleRVA, ((char*)&delayTable->ModuleHandleRVA - (char*)base), RvaToFva(base, delayTable->ModuleHandleRVA)); printf("%-22s\t0x%-8X\t0x%-8X\t0x%-8X\n", "ImportAddressTableRVA", delayTable->ImportAddressTableRVA, ((char*)&delayTable->ImportAddressTableRVA - (char*)base), RvaToFva(base, delayTable->ImportAddressTableRVA)); printf("%-22s\t0x%-8X\t0x%-8X\t0x%-8X\n", "ImportNameTableRVA", delayTable->ImportNameTableRVA, ((char*)&delayTable->ImportNameTableRVA - (char*)base), RvaToFva(base, delayTable->ImportNameTableRVA)); printf("%-22s\t0x%-8X\t0x%-8X\n", "TimeDateStamp", delayTable->TimeDateStamp, ((char*)&delayTable->TimeDateStamp - (char*)base)); // printf("ModuleHandleRVA = %X\n",delayTable->ModuleHandleRVA); // printf("TimeDateStamp = %X\n",delayTable->TimeDateStamp); // printf("ImportNameTableRVA = %X\n",delayTable->ImportNameTableRVA); // printf("ImportAddressTableRVA = %X\n",delayTable->ImportAddressTableRVA); DWORD* importTable = (DWORD*)((char*)base + RvaToFva(base, delayTable->ImportNameTableRVA)); cout << "文件偏移 \t标号 \t名称\n"; while (*importTable) { DWORD funcBegin = RvaToFva(base, *importTable); PIMAGE_IMPORT_BY_NAME func = (PIMAGE_IMPORT_BY_NAME)((unsigned char*)base + funcBegin); // printf("Hint:%X %s\n",func->Hint,func->Name); printf("0x%-20X\t%-8d\t%s\n", funcBegin, func->Hint, func->Name); importTable++; } cout << endl; delayTable++; } } //最后都修改完了,可以直接运行dll入口函数了 using DLLMAIN = BOOL(APIENTRY*)(HMODULE, DWORD, LPVOID); DLLMAIN DllMain = (DLLMAIN)(baseAddr + ophead->AddressOfEntryPoint); DllMain((HMODULE)baseAddr, DLL_PROCESS_ATTACH, NULL); return (HMODULE)baseAddr; } int isPEfile(char* imageBase) { PIMAGE_DOS_HEADER dosHeader; dosHeader = (PIMAGE_DOS_HEADER)imageBase; if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { cout << "不是MZ文件头\n"; return 0; } PIMAGE_NT_HEADERS32 ntHeader; ntHeader = (PIMAGE_NT_HEADERS32)(imageBase + dosHeader->e_lfanew); if (ntHeader->Signature != IMAGE_NT_SIGNATURE) { cout << "是MZ,但不是PE\n"; return 0; } if (ntHeader->FileHeader.SizeOfOptionalHeader == 0xf0) { cout << "可选头大小为F0,判断为PE32+\n"; return 2; } else { cout << "根据可选头大小,判断为PE32\n"; return 1; } return 3; } int add(int a, int b) { return a + b; }
最后导入表和重定位表都处理好了,但是动态调用DLL中函数的时候失败了,本来想跟进GetProcAddress看看的,但是网上查了查,毕竟是手动映射的,和系统加载不一样。虽然应该是可以不断修改,做得跟系统加载一样。但是太麻烦了。
这里要调用dll的导出函数,就需要自己动手写一个类似于GrtProcAddress的函数了,应该也不复杂,就是对导出表的处理。
也是有点意兴阑珊了,重定位的效果也验证了,就不多写了。
ts:这个例子太小了,运行dllMain函数还行,但是大多数时候,你要内存运行的dll会很复杂。对导入表以及其他表的处理可能就不像上面这么简单了。
项目工程下载地址:
蓝奏云