最近在复习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;
}