获取动态库中Windows API的地址(以ntdll.dll为例子)

一、任务
打印Windows 2003中ntdll.dll的所有函数名及其地址。

二、流程
1.获取ntdll.dll的基址
(1)汇编代码

	unsigned long addr;
    __asm{
        mov eax, fs:30h;
        mov eax, [eax+0ch];
        mov ebx, [eax+1ch];
        mov eax, [ebx+8];
        mov addr, eax;
    };

(2)解读
mov eax, fs:30h;
此时,eax存储了当前进程PEB的首地址。

mov eax, [eax+0ch];
此时,eax存储了PEB_LDR_DATA的首地址;

mov ebx, [eax+1ch];
此时,ebx存储了InInitOrderModuleList的首地址。

mov eax, [ebx+8];
InInitOrderModuleList属于LIST_ENTRY结构(可通俗理解为双链表结构),每个节点表示一个LDR_MODULE。
首个LDR_MODULE包含的是ntdll.dll,其Flink指向的LDR_MODULE包含的是kernel32.dll。
由LIST_ENTRY结构可知,[ebx+8]的内容为ntdll.dll的首地址,存储于eax中。

那要得到kernel32.dll的首地址呢?
mov ebx, [ebx];
mov eax, [ebx + 8];

mov addr, eax;
把ntdll.dll的首地址存储于addr中。
在这里插入图片描述
在这里插入图片描述
(2)验证正确性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此处的操作在之前的博文中讲过,不再赘述;如有需要,请查看:
Win32的缓冲区溢出攻击(涉及用WinDbg分析 overflow函数的返回地址所在的地址与buffer首地址的距离 OFF_SET)

2.获取ntdll.dll的API
(1)源代码

#include <stdio.h>
#include <stdlib.h>

unsigned long GetNtdllFuncAddr()
{
    unsigned long BaseAddrOfModule, NameOfModule;
    unsigned long AddressOfFunctions, Address0fNames, NumberOfFunctions;

    __asm {
        mov edx, fs:30h;
        mov	edx, [edx+0ch];
        mov edx, [edx+1ch];
        mov eax, [edx+8];
        mov BaseAddrOfModule,eax;
        mov ebx, eax;
        mov edx,[ebx+3ch];
        mov edx,[edx+ebx+78h];
        add edx,ebx;
        mov esi,edx;
        mov edx,[esi+0ch];
        add edx,ebx;
        mov NameOfModule,edx;
        mov edx, [esi + 14h];
        mov NumberOfFunctions, edx;
        mov edx,[esi+1ch];
        add edx,ebx;
        mov AddressOfFunctions,edx;
        mov edx,[esi+20h];
        add edx,ebx;
        mov Address0fNames,edx;
    }

    FILE * fp = fopen("C:\\work\\output.txt","w");
    fprintf(fp, "Name of Module:%s\n\tBaseAddr of Moudle=0x%p\n",
           (char *)NameOfModule, BaseAddrOfModule);
    fprintf(fp, "Number of Functions is %ld\n", NumberOfFunctions);

    for(int i = 0; i < NumberOfFunctions; i++) 
    {
         fprintf(fp, "第%d个Function:\n\tAddress=0x%p\n\tName=%s\n", i + 1,
            (BaseAddrOfModule + *((unsigned long *)(AddressOfFunctions) + i)),
            (char *)(BaseAddrOfModule + *((unsigned long *)(Address0fNames) + i)));
    }
	fclose(fp);
}

void main(void)
{
    GetNtdllFuncAddr();
}

(2)其中的汇编代码解读
1)获取ntdll.dll的基址(见"二、1.")
在这里插入图片描述

将ntdll.dll的基址存储在ebx中。

2)第2部分
在这里插入图片描述
①从加载地址(ebx存储的地址)开始,内存映像(PE文件的内存映像)存放的是IMAGE_DOS_HEADER结果。(通俗理解,IMAGE_DOS_HEADER的首地址即加载地址)

mov edx, [edx+3ch];
即把e_lfanew的值存储在edx中,它值为IMAGE_NT_HEADERS的偏移
在这里插入图片描述
mov edx, [edx+ebx+78h];
在这里插入图片描述
在这里插入图片描述

①由上2张图可知,要指向DataDirectory,则edx + 18h + 60h = edx + 78h;
②由于edx存储的是IMAGE_NT_HEADERS的偏移,故还需要加上基址,即ebx + edx + 78h指向的便是DataDirectory的首地址;

在这里插入图片描述

③[edx+ebx+78h]便是DataDirectory[0], 即VirtualAddress,指向IMAGE_EXPORT_DIRECTORY。

add edx,ebx; mov esi,edx;
但凡是地址,都需要加上基址;完成④中的2条指令后,esi指向的便是IMAGE_EXPORT_DIRECTORY在内存中的首地址。

3)第3部分
在这里插入图片描述

如下图所示:
在这里插入图片描述
将NameOfModule, NumberOfFunctions, AddressOfFunctions, AddressOfNames读出并存储于变量中。

(3)对输出的一些解读
在这里插入图片描述
在这里插入图片描述

①图片来源博客地址将在“扩展阅读”中注明。
②以AddressOfname为例,其指向函数名字符串首地址(RVA)构成的数组的首地址,即(unsigned long *)(AddressOfNames),+i表示指向下一个 RVA,故*((unsigned long *)(AddressOfNames) + i)表示的是所指向的第i个RVA的值,即函数名字符串的首地址。
③只要是地址,就需要加上基址。

(4)结果
在这里插入图片描述
在这里插入图片描述
三、扩展阅读
1.PE文件详解七:IMAGE_EXPORT_DIRECTORY STRUCT导出表(“二、2.(3)的图片来源”)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值