吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3984|回复: 24
收起左侧

[原创] 萌新逆向学习笔记——IAT钩取

[复制链接]
psycongroo 发表于 2020-9-2 18:18

前言

在上一篇文章中介绍了以模拟调试器的方式来对特定的API地址进行钩取。而这次本篇文章介绍的是修改IAT来同样达到钩取API的目的,这也是一种“劫持”API的方法。

本文为个人学习《逆向工程核心原理》书籍的学习笔记,如果想深入学习逆向工程,推荐去观摩一下该本书籍。因为它实在是十分的易懂。

原理

当程序调用函数的时候,它会使用函数所在模块名称进行一个查表操作。这个表就是俗称的IID(IMAGE_IMPORT_DESCRIPTOR)。表上记载着程序调用目标函数的地址又称为IAT。待查询到目标函数地址后,程序才会真正的通过这个函数地址来进行函数调用:

4.png

而我们只要修改IID中记录的对应的函数地址IAT,使它指向我们自己定义的另一个MessageBoxA,这样函数查表返回的地址就不是原来程序中定义的地址,而是我们修改后指向自己定义函数的地址了,这就完成了一次IAT“劫持”。

1.png

实现思路

整体思路:将修改函数地址的实现,以及新函数的实现写在一个DLL里,再编写一个注入器,将该DLL注入到目标程序中。DLL注入成功后便会修改IAT函数地址,达到IAT钩取的目的。

细节思路:主要是如何找到该程序的IID里对应的IAT。我们可以先使用getModuleHandle(NULL)函数找到当前被注入程序的基址pfile,该基址即是指向当前PE文件DOS头的起始地址。通过DOS头结构体的e_lfanew属性,获取到NT头的虚拟内存空间的偏移量RVA,将该值加上基址pfile即可得到NT头的虚拟内存地址。而最后通过相关的属性操作,就可以从NT头中获取对应的IAT地址。

实践

获取IID:

void findIID() {
    // 找到IID所在的内存地址
    //获取当前被注入DLL的程序模块基址
    pfile = (PBYTE)GetModuleHandle(NULL);  
    //转换成DOS头
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pfile;  
    //通过DOS头的e_lfanew找到NT头
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(pfile + dosHeader->e_lfanew); 
    //NT头中的可选NT头里的DataDirectory数组,记载着IID的虚拟内存偏移地址
    IMAGE_DATA_DIRECTORY IATSection = (IMAGE_DATA_DIRECTORY)(ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
    //加上基址,获取到IID虚拟内存地址
    currentIID = PIMAGE_IMPORT_DESCRIPTOR(pfile + IATSection.VirtualAddress);
}

修改IAT

LPCSTR dialogDLL = "user32.dll";
void hookIAT() {
    // 修改IAT
    while (currentIID)
    {
        //去IID中的Name字段,其为IAT对应的模块名称虚拟内存地址偏移量,如MessageBoxA是在系统函数user32.dll中
        DWORD RVAName = currentIID->Name;
        char* VAName = (char*)(pfile + RVAName);
        //对比是否为user32.dll
        if (strcmp(VAName, dialogDLL))
        {
            // 符合条件,取出IAT列表
            DWORD RVAIAT = currentIID->FirstThunk;
            PIMAGE_THUNK_DATA pthunk = (PIMAGE_THUNK_DATA)(pfile + RVAIAT);
            //循环IAT函数地址列表,找到MessageBoxA函数地址
            while (pthunk->u1.Function)
            {
                DWORD funAddress = pthunk->u1.Function;
                if (funAddress == (DWORD)oriAddress)
                {
                    //修改IAT
                    DWORD oldProtect = NULL;
                    VirtualProtect((LPVOID)&pthunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
                    pthunk->u1.Function = (DWORD)hookMessageBox;
                    VirtualProtect((LPVOID)&pthunk->u1.Function, 4, oldProtect, &oldProtect);
                    break;
                }
                pthunk++;
            }
            return;
        }
        currentIID++;
    }
}

结果

例中使用的依旧是上篇文章中编写的可怜的弹窗程序:

2.png

3.png

结论

IAT钩取具有一定的局限性,我们只能钩取IAT中存在的函数,而不能无中生有。而我们定义的函数签名也必须符合钩取的原函数前的签名;如参数类型,函数返回等须相同。

总的来说IAT钩取还是比较简单快速的,其难点是寻找IAT的过程。倘若我们对PE文件结构毫无所知,不能理解DOS头,NT头等重要结构,想必就要多费一些心思了吧。

源码及例子

https://share.weiyun.com/iCyzPWuM

免费评分

参与人数 7吾爱币 +13 热心值 +7 收起 理由
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Moriarty_Jim + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
plasd + 1 + 1 用心讨论,共获提升!
lemon__star + 1 + 1 我很赞同!
为之奈何? + 1 + 1 我很赞同!
chinawolf2000 + 1 + 1 热心回复!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

lvchuanya 发表于 2020-9-2 18:24
学习一下
 楼主| psycongroo 发表于 2020-9-2 23:03
某科学的阿卡林 发表于 2020-9-2 21:54
if (strcmp(VAName, dialogDLL))
建议查一下strcmp用法

受教了,strcmp对比参数p1和参数p2的值,相等才为0。所以应该改成if (strcmp(VAName, dialogDLL)==0)
 楼主| psycongroo 发表于 2020-9-2 18:27
文中还需要用到一个关键的技术:DLL注入,可以使用CreateRemoteThread创建远程线程来实现DLL注入,因为之前说过就不再累述。可以看看之前的文章
techliu 发表于 2020-9-2 18:45
PE结构都忘的差不多了
别路山川 发表于 2020-9-2 18:56
学习了,感谢
Linshengqiang 发表于 2020-9-2 19:01
哇 大师们挺懂程序代码的
sizhubiao 发表于 2020-9-2 19:31
不明觉厉,感谢楼主分享!
wjl521 发表于 2020-9-2 19:52
tql,学习一下
XCTF 发表于 2020-9-2 21:22
支持一下可以多发些
某科学的阿卡林 发表于 2020-9-2 21:54
if (strcmp(VAName, dialogDLL))
建议查一下strcmp用法
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-27 03:12

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表