解析 kernel32.dll 的导出目录并查找特定 API 函数的内存地址

注意:有些函数在kernel32.dll的导出目录中存储的并不是函数的真实地址,可能会通过导出表的得到的地址去调用另一个模块中的内核中的函数,例如heapalloc函数。具体以后会将,总之就是目前的自定义函数是有局限性的

首先必须明白PE结构

基本上做一个思路以及简化的实例代码:

主要包括以下步骤:

  1. 获取 kernel32.dll 模块的句柄:

你需要获取 kernel32.dll 模块在当前进程中的句柄,可以使用 GetModuleHandle 函数来获取。

  1. 获取导出表 RVA:

从获取的模块句柄中,你需要找到导出表的 RVA(Relative Virtual Address,相对虚拟地址)。导出表是一个数据结构,包含了模块中所有导出的函数名称和地址。

  1. 解析导出表:

在导出表的 RVA 处,你需要解析导出表结构,找到需要的 API 函数的名称在导出表中的位置。

  1. 获取 API 函数的内存地址:

通过获取 API 函数的名称在导出表中的位置,你可以计算出函数的内存地址。通常,导出表中存储的是相对虚拟地址(RVA),你需要将其转换为实际的内存地址。

代码如下:

#include<windows.h>
#include <stdio.h>
FARPROC GetFunctionAddress(const char* moduleName, const char* functionName, DWORD functionOrdinal) {
    HMODULE hModule = GetModuleHandleA(moduleName);
    //获取 kernel32.dll 模块的句柄
    if (hModule == NULL) {
        return NULL; // 模块未找到
    }

    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;//DOS头,入口地址
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((BYTE*)pDosHeader + pDosHeader->e_lfanew);
    DWORD exportRVA = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hModule + exportRVA);

    PDWORD pAddressOfFunctions = (PDWORD)((BYTE*)hModule + pExportDir->AddressOfFunctions);
    PDWORD pAddressOfNames = (PDWORD)((BYTE*)hModule + pExportDir->AddressOfNames);
    PWORD pAddressOfNameOrdinals = (PWORD)((BYTE*)hModule + pExportDir->AddressOfNameOrdinals);

    FARPROC functionAddress = NULL;

    if (functionName != NULL) {
        // 根据函数名称查找
        for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i) {
            if (strcmp((char*)((BYTE*)hModule + pAddressOfNames[i]), functionName) == 0) {
                functionAddress = (FARPROC)((BYTE*)hModule + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
                break;
            }
        }
    } else if (functionOrdinal != 0) {
        // 根据函数序号查找
        if (functionOrdinal <= pExportDir->NumberOfFunctions) {
            functionAddress = (FARPROC)((BYTE*)hModule + pAddressOfFunctions[functionOrdinal - 1]);
        }
    }

    return functionAddress;
}
int main()
{
    
    FARPROC loadLibraryAAddress = GetFunctionAddress("kernel32.dll", "LoadLibraryA", 0);
}

在导出表中,函数序号是从 1 开始的,而在数组中的索引是从 0 开始的。因此,为了获取正确的函数地址,需要将函数序号减去 1,以匹配数组的索引。

那我们判断是利用序号还是名称获取导入表函数,一般是通过 (DOWRD)lpProcName>0xffff 判断是否参数大于两个字节(windows是这样判断的)

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值