PE学习之重定位,内存加载dll
作者:互联网
PE学习之重定位,内存加载dll
最近又复习了一下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会很复杂。对导入表以及其他表的处理可能就不像上面这么简单了。
项目工程下载地址:
蓝奏云
标签:printf,之重,dll,char,8X,base,t0x%,PE,exportTable 来源: https://www.cnblogs.com/dayq/p/16209466.html