PE知识之实现GetProcAddress

最近在复习PE结构知识,试着自己实现了一下GetProcAddress

费话不多说,上代码

首先是写一个dll

#include"pch.h"
#include"dll.h"

int fun1(int a, int b) {
	return 1;
}

int fun2() {
	return 2;
}

int fun3() {
	return 3;
}

定义模块导出(为了方便后续观察序号)

LIBRARY
EXPORTS
fun1 @2
fun2 @5
fun3 @3 NONAME

然后简单介绍一下实现原理

其实只是利用了导出表的知识

1.利用名字去找函数地址(假设找fun1)

  • RVA转FOA找到导出名称表,得到一个RVA,这个RVA记录着真正的名称导出表的RVA(套娃),再转到FOA得到字符串数组,记录着导出函数函数的名字。

  • 比较字符串,如果相等,得到这个数组的下标,拿着这个下标去序号表中查对应下标里的值,它的下标为0,注意下序号表数组是WORD类型的
  • 序号表数组里下标为0的值是0,拿着这个0去函数地址表的对应下标去找真正的地址

 2.利用序号去找函数地址

  • 这个就比较简单了,只需要用序号-Base,然后用这个值去函数地址表里找对应下标的值,假设我们找序号为5的函数地址,也就是fun2,下标就为3

 上完整代码

#include<Windows.h>
#include<tchar.h>
#include<iostream>

#define path2 L"C:\\Users\\罗辑\\Desktop\\Dll1.dll"

//通过函数名从导出表中去找函数地址
DWORD GetProcByName(DWORD buf, PCHAR name);
//通过序号从导出表中去找函数地址
DWORD GetProcByOrdinals(DWORD buf, WORD order);

DWORD RVA2FOA(DWORD buf, DWORD RVA) {
	PIMAGE_DOS_HEADER dos_header = PIMAGE_DOS_HEADER(buf);
	PIMAGE_NT_HEADERS nt_header = PIMAGE_NT_HEADERS((DWORD)buf + dos_header->e_lfanew);
	PIMAGE_FILE_HEADER file_header = PIMAGE_FILE_HEADER(&(nt_header->FileHeader));
	PIMAGE_OPTIONAL_HEADER option_header = PIMAGE_OPTIONAL_HEADER(&(nt_header->OptionalHeader));
	PIMAGE_SECTION_HEADER section_header = PIMAGE_SECTION_HEADER(IMAGE_FIRST_SECTION(nt_header));

	for (int i = 0; i < file_header->NumberOfSections; i++) {
		if (RVA >= section_header->VirtualAddress &&
			RVA < section_header->VirtualAddress + section_header->SizeOfRawData) {
			return (RVA - section_header->VirtualAddress + section_header->PointerToRawData);
		}
		section_header++;
	}
	return 0;
}

VOID EnumExport() {
	HANDLE hFile = CreateFile(path2, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	DWORD size = GetFileSize(hFile, NULL);
	LPVOID buf = new char[size] {0};
	DWORD readSize = 0;
	DWORD err_t = ReadFile(hFile, buf, size, &readSize, NULL);
	if (!err_t) {
		return;
	}
	PIMAGE_DOS_HEADER dos_header = PIMAGE_DOS_HEADER(buf);
	PIMAGE_NT_HEADERS nt_header = PIMAGE_NT_HEADERS((DWORD)buf + dos_header->e_lfanew);
	PIMAGE_FILE_HEADER file_header = PIMAGE_FILE_HEADER(&(nt_header->FileHeader));
	PIMAGE_OPTIONAL_HEADER option_header = PIMAGE_OPTIONAL_HEADER(&(nt_header->OptionalHeader));
	PIMAGE_SECTION_HEADER section_header = PIMAGE_SECTION_HEADER(IMAGE_FIRST_SECTION(nt_header));

	DWORD export_table_RVA = option_header->DataDirectory[0].VirtualAddress;
	PIMAGE_EXPORT_DIRECTORY export_table = PIMAGE_EXPORT_DIRECTORY(RVA2FOA((DWORD)buf, export_table_RVA) + (DWORD)buf);

	//函数地址表
	DWORD FunAddress_table = RVA2FOA((DWORD)buf, export_table->AddressOfFunctions) + (DWORD)buf;
	printf("***函数地址****\n");
	for (int i = 0; i < export_table->NumberOfFunctions; i++) {
		printf("0x%08X\n", ((PDWORD)FunAddress_table)[i]);
	}
	//函数名称表
	DWORD NameAddress_RVATable = RVA2FOA((DWORD)buf, export_table->AddressOfNames) + (DWORD)buf;
	DWORD NameAddress = 0;
	printf("***函数名称***\n");
	for (int i = 0; i < export_table->NumberOfNames; i++) {
		NameAddress = RVA2FOA((DWORD)buf, *(PDWORD)NameAddress_RVATable) + (DWORD)buf;
		printf("%s\n", (PCHAR)NameAddress);
		//下一个位置
		NameAddress_RVATable += 4;
	}

	//函数序号表
	DWORD order = RVA2FOA((DWORD)buf, export_table->AddressOfNameOrdinals) + (DWORD)buf;
	printf("***序号表***\n");
	for (int i = 0; i < export_table->NumberOfNames; i++) {
		printf("%d\n", ((PWORD)order)[i] + 1);
	}

	//通过名称找函数(这里得到的是函数RVA,要想得到运行时地址,还需要得到dll的加载基址)
	/*typedef int(*fun)(int a, int b);
	DWORD addfun1 = GetProcByName((DWORD)buf, (PCHAR)"fun1");
	CloseHandle(hFile);
	HMODULE hModule = LoadLibrary(path2);
	addfun1 += (DWORD)hModule;
	fun fun1 = (fun)addfun1;
	int temp = fun1(1, 1);*/

	//通过序号去找函数
	DWORD addfun5 = GetProcByOrdinals((DWORD)buf, 5);

	int a = 0;

}

//通过名字去找函数地址
DWORD GetProcByName(DWORD buf, PCHAR name) {
	//耳熟能详
	PIMAGE_DOS_HEADER dos_header = PIMAGE_DOS_HEADER(buf);
	PIMAGE_NT_HEADERS nt_header = PIMAGE_NT_HEADERS((DWORD)buf + dos_header->e_lfanew);
	PIMAGE_FILE_HEADER file_header = PIMAGE_FILE_HEADER(&(nt_header->FileHeader));
	PIMAGE_OPTIONAL_HEADER option_header = PIMAGE_OPTIONAL_HEADER(&(nt_header->OptionalHeader));
	PIMAGE_SECTION_HEADER section_header = PIMAGE_SECTION_HEADER(IMAGE_FIRST_SECTION(nt_header));
	DWORD export_table_RVA = option_header->DataDirectory[0].VirtualAddress;
	PIMAGE_EXPORT_DIRECTORY export_table = PIMAGE_EXPORT_DIRECTORY(RVA2FOA((DWORD)buf, export_table_RVA) + (DWORD)buf);

	DWORD name_RVATable = RVA2FOA((DWORD)buf, export_table->AddressOfNames) + (DWORD)buf;
	DWORD nameAddress = 0;
	DWORD ordinal_table = RVA2FOA((DWORD)buf, export_table->AddressOfNameOrdinals) + (DWORD)buf;
	DWORD funAddress_table = RVA2FOA((DWORD)buf, export_table->AddressOfFunctions) + (DWORD)buf;

	for (int i = 0; i < export_table->NumberOfFunctions; i++) {
		nameAddress = RVA2FOA((DWORD)buf, *(PDWORD)name_RVATable) + (DWORD)buf;
		if (strcmp(name, (PCHAR)nameAddress)==0) {
			//去找序号表的下标i
			WORD order = ((PWORD)ordinal_table)[i];
			return ((PDWORD)funAddress_table)[order];
		}
	}
	return 0;
}

//通过序号去找函数地址
DWORD GetProcByOrdinals(DWORD buf, WORD order) {
	//耳熟能详
	PIMAGE_DOS_HEADER dos_header = PIMAGE_DOS_HEADER(buf);
	PIMAGE_NT_HEADERS nt_header = PIMAGE_NT_HEADERS((DWORD)buf + dos_header->e_lfanew);
	PIMAGE_FILE_HEADER file_header = PIMAGE_FILE_HEADER(&(nt_header->FileHeader));
	PIMAGE_OPTIONAL_HEADER option_header = PIMAGE_OPTIONAL_HEADER(&(nt_header->OptionalHeader));
	PIMAGE_SECTION_HEADER section_header = PIMAGE_SECTION_HEADER(IMAGE_FIRST_SECTION(nt_header));
	DWORD export_table_RVA = option_header->DataDirectory[0].VirtualAddress;
	PIMAGE_EXPORT_DIRECTORY export_table = PIMAGE_EXPORT_DIRECTORY(RVA2FOA((DWORD)buf, export_table_RVA) + (DWORD)buf);

	DWORD funAddressTable = RVA2FOA((DWORD)buf, export_table->AddressOfFunctions) + (DWORD)buf;
	return ((PDWORD)funAddressTable)[order - export_table->Base];
	
}


int _tmain(char* argv, char* args[]) {
	
	EnumExport();

	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值