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

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

  • 相关阅读:
    java线程小结1
    String和StringBuffer
    java队列的实现
    java栈的实现
    java链表
    this与super关键字总结
    JVM内存杂记1
    面试题18:删除链表节点
    面试题17:打印从 1 到最大的 n 位数
    面试题16:数值的整数次方
  • 原文地址:https://www.cnblogs.com/dayq/p/16209466.html
Copyright © 2020-2023  润新知