导入地址表钩取技术解析

news2025/1/11 15:11:03

前置知识

导入表

在一个可执行文件需要用到其余DLL文件中的函数时,就需要用到导入表,用于记录需要引用的函数。例如我们编写的可执行文件需要用到CreateProcess函数,就需要用到kernel32.dll文件并且将其中的CreateProcess函数的信息导入到我们的可执行文件中,然后再调用。

为了管理这些导入函数,就构建了一个导入表进行统一的管理,简单来说,当我们编写的可执行文件中使用到导入函数就会去导入表中去搜索找到指定的导入函数,获取该导入函数的地址并调用。

image-20240424192254287

因此加载器在调用导入函数之前需要先找到导入表的所在处。在可执行文件映射到内存空间时,都是以Dos Header开始的,在该头部存在elfanew的字段,用于记录PE文件头的偏移,在PE文件头存在可选头的结构体,该结构体中存储数据目录项,其中就包括了导入表。因此在内存中我们需要通过Dos Header -> Nt Header -> Option Header -> Import Table的顺序获取导入表。

image-20240424193422922

这里使用《加密与解密》的图来看一下导入表的结构体,如下图。

image-20240424195938393

可以看到导入表涉及的变量非常多,这里重点关注OriginalFirstThunkFistThunk以及Name

  • Name:指向导入库的名称。
  • OriginalFistThunk:指向输入名称表,里面存储了导入函数的信息。
  • FirstThunk:指向输入地址表,可以看到在初始化的时候OriginalFistThunkFirstThunk指向的是同一块区域,即导入函数的信息。

输入名称表的结构体如下图,这里重点关注OrdinalAddressOfData

  • Ordinal:记录函数的序号,即导入函数以序号存储
  • AdressOfData:以函数命的形式记录导入函数

image-20240424201610702

那么INTIAT的区别在于,加载器会在从导入表中获取了导入函数名称后,会搜索该函数的名称并获取该函数的地址并填入到IAT中,因此在经历了加载器后,IAT中存储了实际地址。如下图。

image-20240424202850423

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

导入地址表钩取技术

输入地址表钩取技术就是通过修改输入地址表的地址值,因此当调用该导入函数时会跳转到被篡改的地址上。

在钩取之前的状态如下图

image-20240424204535373

在钩取之后的状态如下图

image-20240424204853835

因此总结一下输入地址表钩取技术的流程

  • 确定需要钩取的导入函数
  • 获取输入地址表的地址
  • 在输入地址表中搜索需要钩取的导入函数地址并且将导入函数地址修改为自定义的函数
  • 在处理完之后需要在自定义函数中重新调用被钩取的函数

确定需要钩取的导入函数

首先确定可执行文件中存在什么导入函数,可以发现目标的可执行文件中导入了kernel32.dll的系统库,并且导入的CreateProcessW

image-20240424205932029

那么采用输入地址表钩取方法钩取CreateProcessW函数。

获取导入地址表的地址

根据DOS Header -> Nt Header -> Option Header ->Import Table的顺序进行搜索,即可获取导入地址表的地址。

代码如下

...
        //获取当前进程的基地址
    hMod = GetModuleHandle(NULL);
    pBase = (PBYTE)hMod;
	//进程的基地址是从DOS头开始的
    pImageDosHeader = (PIMAGE_DOS_HEADER)hMod;
	//通过e_lfanew变量获取NT头的偏移,然后加上基地址及NT头的位置
    pImageNtHeaders = (PIMAGE_NT_HEADERS)(pBase + pImageDosHeader->e_lfanew);
	//数据目录项下标为1的项是导入表
    pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(pImageNtHeaders->OptionalHeader.DataDirectory[1].VirtualAddress + pBase)
...

获取导入函数地址并修改

在获取导入地址表的地址后,首先通过遍历导入表的结构体,提取其中的Name字段,判断是否为我们需要钩取的导入库名。在匹配完成后则选择继续遍历IAT中的函数地址,找到需要钩取的函数地址,找到后则修改为自定义函数的地址。

image-20240424212559365

代码如下

    ...
    //遍历导入表项
    for (; pImageImportDescriptor->Name; pImageImportDescriptor++)
    {
        //获取导入库的名称
        szLibName = (LPCSTR)(pImageImportDescriptor->Name + pBase);
        //比较导入库的名称,判断是否为kernel32.dll
        if (!_stricmp(szLibName, szDllName))
        {
            //获取IAT
            PIMAGE_THUNK_DATA pImageThunkData = (PIMAGE_THUNK_DATA)(pImageImportDescriptor->FirstThunk + pBase);
            //获取导入函数地址
            for (; pImageThunkData->u1.Function; pImageThunkData++)
            {
                //判断函数地址是否是需要钩取的函数地址,这里需要注意的是64位与32位地址的区别
                if (pImageThunkData->u1.Function == (ULONGLONG)pfnOrg)
                {
                    //修改IAT的权限为可写
                    VirtualProtect(&pImageThunkData->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
                    //将原始的地址修改为自定义函数地址
                    pImageThunkData->u1.Function = (ULONGLONG)pfnNew;
                    //将权限恢复
                    VirtualProtect(&pImageThunkData->u1.Function, 4, dwOldProtect, &dwOldProtect);
                    return TRUE;
                }
            }
        }
        ...

在自定义函数中重新调用被钩取的函数

这里需要注意的是,我们需要构建一个自定函数,该函数的返回类型与参数需要与钩取的函数一模一样,这样我们就可以获取所有参数的信息,然后篡改后重新传递给原始的导入函数,即可完成钩取。

image-20240424213549505

代码如下,这里篡改原始CreateaProcessW函数的第一个参数,使计算器

...
    LPCWSTR applicationName = L"C:\\Windows\\System32\\calc.exe";
    
    return ((LPFN_CreateProcessW)g_pOrgFunc)(applicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        bInheritHandles,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        lpStartupInfo,
        lpProcessInformation);
...

完整代码:https://github.com/h0pe-ay/HookTechnology/blob/main/Hook-IAT/iat.cpp

调试

刚开始使用的是xdbg调试,但是用的不太习惯,后面改用WinDbg还可以源码调试,这里记录一下需要用到的操作与指令。

符号表与源码加载

在设置中可以选择源码默认的目录以及符号表默认的目录,符号文件则是利用Visutal Studio编译生成的pdb文件。

其中srv*c:\Symbols*https://msdl.microsoft.com/download/symbols是下载官方的符号表文件,这里可以选择删掉只调试我们设置的文件。不然每次都需要下载一遍影响时间。

源码文件也可以在侧边栏选择Open source file选项打开。

image-20240425103000930

DLL加载调试

由于钩取时需要先使用DLL注入技术将自定义的DLL文件注入进去,因此想要调试钩取过程则需要在DLL附着的时候打下断点。

利用sxe ld:xxx.dll即可在加载xxx.dll的时候打下断点。

利用sxe ud:xxx.dll即在卸载xxx.dll的时候打下断点。

image-20240425103802758

关闭优化调试

防止自定义函数中的变量被优化导致不方便单步调试,在Visual Studio中可以选择关闭优化进行编译。

image-20240425104053895

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1791893.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库管理-第198期 升级Oracle ACE Pro,新赛季继续努力(20240605)

数据库管理198期 2024-06-05 数据库管理-第198期 升级ACE Pro,新赛季继续努力(20240605)1 惊喜2 变化3 Oracle ACE总结 数据库管理-第198期 升级ACE Pro,新赛季继续努力(20240605) 作者:胖头鱼的…

【PCB]射频电路pcb设计

学习改变命运,技能成就未来!❤~~ 1射频信号的基础知识及工作原理介绍 射频的基础知识介绍 2射频板PCB的布局要求 3射频板布局要求 4屏蔽帐设计 5射频板的层叠阻抗设计 6射频板的PCB布线原则 7射频板的PCB布线要求 8射频板的设计实战

kubeedge v1.17.0部署教程

文章目录 前言一、安装k8s平台二、部署kubeedge1.部署MetalLB(可选)2.cloud3.edge4. 部署nginx到edge端 总结参考 前言 本文主要介绍kubeedge v1.17.0的安装过程 主要环境如下表 应用版本centos7.0k8s1.28.2kubeedge1.17.0docker24.0.8centos7.0 一、安装k8s平台 本文主要参…

2024年6月1日 (周六) 叶子游戏新闻

Embracer探讨单机游戏大作涨价超过70美元的可能性在Embracer集团等待公布新公司名称的同时,他们对游戏大作的价格上涨做出了评论。几年来,游戏大作的价格已经达到了70美元的门槛。Embracer集团的CEO Lars Wingefors在采访中表示,电子游戏行业…

MySQL—多表查询—内连接

一、引言 (1)内连接查询语法 内连接查询的是两张表的交集部分的数据。(也就是绿色部分展示的数据) (2)内连接有两种形式: 1、隐式内连接 语法结构: 2、显示内连接 语法结构&#xf…

AIGC绘画设计——midjourney有哪些好用的关键词?

midjourney有哪些高级关键词? 这一期继续分享一些高级的关键词, 我有一些案例也是从其他博主那学习来的, 但为了尽可能不出错,每个案例都是自己尝试了很多次后才拿出来的。 挑选了几个效果比较好,使用场景较高的类型…

lux和ffmpeg进行下载各大主流自媒体平台视频

1、lux下载,链接:https://pan.baidu.com/s/1WjGbouL3KFTU6LeqZmACpA?pwdagpp 提取码:agpp 2、ffmpeg下载,跟lux放在同一个目录; 3、为lux、ffmpeg设置环境变量; 4、WINR,打开运行&#xff0…

手眼标定学习笔记

目录 标定代码: 手眼标定原理学习 什么是手眼标定 手眼标定的目的 eye in hand eye to hand AXXB问题的求解 标定代码: GitHub - pumpkin-ws/HandEyeCalib 推荐博文: https://zhuanlan.zhihu.com/p/486592374 手眼标定原理学习 参…

nexus搭建npm前端项目的私服

一、为什么要搭建私库 节省外网带宽加速maven构建部署第三方构件(特别是无法从公共仓库下载的构件)提高稳定性(内网部署,更少地依赖外网)降低中央仓库的负荷 构件,好比我们的藏书,去书店或商城…

【数据库】SQL--DDL(初阶)

文章目录 DDL1. 数据库操作1.1. 表操作1.1.1 创建1.1.2. 查询 2. 数据类型及案例2.1 数值类型2.2 字符串类型2.3 日期时间类型2.4 案例练习 3. 表操作--修改3.1 添加字段3.2 修改字段3.3 修改表名 4. 表操作-删除4.1 删除字段4.2 删除表 5. DDL小结 更多数据库MySQL系统内容就在…

如何在强数据一致性要求下设计数据库的高可用架构

在高可用的三大架构设计(基于数据层的高可用、基于业务层的高可用,以及融合的高可用架构设计)中。仅仅解决了业务连续性的问题:也就是当服务器因为各种原因,发生宕机,导致MySQL 数据库不可用之后,快速恢复业务。但对有状态的数据库服务来说,在一些核心业务系统中,比如…

svn的使用

【图文详解】入职必备——SVN使用教程-CSDN博客 使用SVNBucket作为服务端,来辅助学习. 什么时候会产生冲突呢? 原本A,B,服务器的版本都一致,都是最新版. A修改文件m,向服务器提交 B修改文件m,向服务器提交,这时候出现了冲突 双击冲突的文件,手动修改

鬼畜恶搞类型的视频素材哪里找?热门搞笑素材网站分享

在当今数字媒体时代,寻找优质的视频素材变得尤为重要,尤其是对于喜欢鬼畜恶搞风格的创作者来说,选择合适的素材网站可以大大提升视频的吸引力和观看体验。本文将为短视频创作者和自媒体运营者介绍一些顶级的视频素材网站和工具,特…

方案设计|汽车轮胎数显胎压计方案

一、引言 数显轮胎胎压计是一个专门测量车辆轮胎气压的工具,它具有高精度测量的功能,能够帮助快速准确获取轮胎气压正确数值,保证轮胎使用安全。本文将对数显轮胎胎压计的方案技术进行分析,包括其基本原理、硬件构成、软件设计等方…

nvme-cli常见命令分析

一、背景 nvme-cli命令常常用于获取或者设置SSD参数,比如常见的nvme list,nvme id-ctrl等,都是获取SSD的基本信息,也有nvme admin-passthru用于读取或者设置自定义命令。作为使用者,我们并不知道nvme-cli源码怎么实现…

k8s怎么监听自定义资源的变更?(2)

接上一篇当生成下面代码之后怎么去使用呢? 1.生成crd文件 这里我们通过kubebuilder的一个子项目 controller-gen 来生成crd文件 https://github.com/kubernetes-sigs/controller-tools curl -L -o https://github.com/kubernetes-sigs/controller-tools; go ins…

关于远程销售的电子课程开发

一家国际网络安全公司委托我们开发用于培训销售代表远程和电话销售的互动电子内容。我们在 Articulate Storyline 中创建了情节脚本和二维动画,以解释关键概念和销售技巧。互动元素使学习者可以按照自己的节奏进行学习,而我们的动画插图则使材料生动起来…

【嵌入式DIY实例】-OLED显示BME280传感器数据

OLED显示BME280传感器数据 文章目录 OLED显示BME280传感器数据1、硬件准备与接线2、代码实现本文将介绍如何使用 ESP8266 NodeMCU 开发板(ESP12-E 模块)和 BME280 气压、温度和湿度传感器构建本地气象站。 NodeMCU 从 BME280 传感器读取温度、湿度和压力值,并将它们(分别以…

使用apifox进行并发测试

1.普通的接口测试就不说了,非常简单 2.自动化测试

某铁路信息中心运营监测项目

某铁路信息中心承担大量实时监测、例行巡检和排障维护等工作,为巩固信息化建设成果,提高整体运维效果,保障铁路信息系统稳定运行,需对现有网络监测系统进行升级改造。 设备类型:服务器、交换机、数据库、中间件、虚拟…