Java教程

PE学习之重定位,内存加载dll

本文主要是介绍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文件夹里面,点击运行即可。

image

测试完了。我们再看一下PE结构

image

这是自己写的peinfo工具,按照自己习惯就好了。

可以知道0xA00处开始的0xC字节就是重定位块的所有内容。

image

再贴上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文件中,重定位块就是

重定位结构+重定位数据 /重定位结构+重定位数据 /重定位结构+重定位数据

这样紧凑的一个接着一个,每个块里面的起始偏移地址相同。

结合调试器就好理解了。

image

在这个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处

添加资源。

image

然后点击自定义,新建资源类型即可。名称自己随意输入。

再次点击资源,这次选择导入,再选择刚才的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后的相应位置内存里面的数据即可。

image

文件:

image

内存:
image

这里就是说文件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*)&sectionTable[i] - (char*)base), ((char*)&sectionTable[i].PointerToRawData - (char*)base), ((char*)&sectionTable[i].VirtualAddress - (char*)base), ((char*)&sectionTable[i].Misc.VirtualSize - (char*)base), ((char*)&sectionTable[i].SizeOfRawData - (char*)base), ((char*)&sectionTable[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会很复杂。对导入表以及其他表的处理可能就不像上面这么简单了。

项目工程下载地址:
蓝奏云

这篇关于PE学习之重定位,内存加载dll的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!