解析导入表及自实现GetProcAddress

注:这里的自实现GetProcAddress是不完美的,有一些函数例如 HeapAlloc是获取不到的,具体原因以及解决方法,请给博主一些时间,后续会进行更新
在解析之前我们需要明白PE结构:DOS头、NT头、节等

了解RVA(relative Virtual Address) 相对虚拟地址偏移、(VA (virtual Address) 虚拟地址、FA(RAW)(File Address)文件地址之间的转换

1.首先在Visual stdio中创建一个控制台文件,头文件windows.h

2.我们需要知道PE中存储的地址都是RVA所以需要转换为VA

我们定义一个函数RVATOVA

DWORD RVATOVA(DWORD RVA, DWORD hModule)
{
    return RVA + hModule;
}

首先要自实现GetProcAddress  我们需要了解它的结构 ,右键F12进入GetProcAddress()观察它的结构

int main()
{
    HMODULE Hmodule = GetModuleHandleA("ntdll.dll");
    void *p=GetProcAddress(Hmodule,"RtlValidateHeap");
    void* p1 = MyGetProcAddress(Hmodule, "RtlValidateHeap");
    printf("%x", p);
    std::cout << "Hello World!\n";
}

我们把这部分复制出来

FARPROC
WINAPI
MyGetProcAddress(
    _In_ HMODULE hModule,
    _In_ LPCSTR lpProcName
)

 函数中两个参数,一个句柄,一个名称

我们再来看一下导出表的结构

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

我们需要重点注意的是DWORD Name、Base(序号) 以及最后三项分别是函数地址、函数名称,函数序号的RVA。 

我们先定位到导出表

PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pIMAGE_NT_HEADERS = (PIMAGE_NT_HEADERS)(pIMAGE_DOS_HEADER->e_lfanew + (DWORD)hModule);
PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORY_RVA = (PIMAGE_EXPORT_DIRECTORY)(pIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[0].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORY = (PIMAGE_EXPORT_DIRECTORY)RVATOVA((DWORD)pIMAGE_EXPORT_DIRECTORY_RVA, (DWORD)hModule);//导出表项

   此时我们定位到了导出表, 我们此刻就可以获取到导出模块儿的名称

DWORD MODULE_NAME = RVATOVA(pIMAGE_EXPORT_DIRECTORY->Name, (DWORD)hModule);

 //导出函数名称表
DWORD NameAddressRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfNames;
DWORD* NameAddress = (DWORD*)RVATOVA(NameAddressRVA, (DWORD)hModule);

//名称序号表
WORD* NameOrdinalsAddress = (WORD*)RVATOVA(pIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals, (DWORD)hModule);

//函数地址表
DWORD* FunctionsAddress = (DWORD*)RVATOVA(pIMAGE_EXPORT_DIRECTORY->AddressOfFunctions, (DWORD)hModule);

用一个FOR循环,i<导出模块中的函数名称个数----  用strcmp去判断我们得到的函数名称还传入的函数名称是否一致,一致则打印出来它的序号,以及地址

 for (size_t i = 0; i < pIMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
        {
            char* FunName = (char*)RVATOVA(NameAddress[i], (DWORD)hModule);
            if (strcmp(FunName, (char*)lpProcName) == 0)
            {
                printf("%d\n", NameOrdinalsAddress[i] + pIMAGE_EXPORT_DIRECTORY->Base);
                printf("%x\n", RVATOVA(FunctionsAddress[NameOrdinalsAddress[i]], (DWORD)hModule));
            }
        }

运行,我们即可知道ntdll.dll中的RtlValidateHeap序号为1600  自实现函数打印出来的地址与库函数打印出的地址也一致。

接下来把文件拖入x32dbg中观察是否打印正确

 此时我们就实现了通过函数名称获取句柄的操作(通过函数名称比对去获取序号表中获取函数的序号)

那么还可以通过函数序号来获取,我们提前判断一下传入的是名称还是序号---微软中使用的判断方法是看名称字符是否使用了两个字节以上的空间(0xffff),如果是则传入的是字符,否则传入的是序号。

if ((DWORD)lpProcName >= 0xffff)
 printf("%x\n", RVATOVA(FunctionsAddress[NameOrdinalsAddress[(WORD)lpProcName-pIMAGE_EXPORT_DIRECTORY->Base]], (DWORD)hModule));

若传入的是序号,那我们直接打印。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值