ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

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

2022-04-30 09:31:40  阅读:277  来源: 互联网

标签:printf 之重 dll char 8X base t0x% PE exportTable


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会很复杂。对导入表以及其他表的处理可能就不像上面这么简单了。

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

标签:printf,之重,dll,char,8X,base,t0x%,PE,exportTable
来源: https://www.cnblogs.com/dayq/p/16209466.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有