IAT Hook

news2024/11/27 5:25:31

一、IAT HOOK介绍

IAT (Import Address Table) HOOK 是一种在 Windows 程序中进行函数钩子的技术。它通过修改程序的导入地址表来实现对目标函数的替换或拦截。

在 Windows 运行时,程序需要调用其他模块(DLL)中的函数来完成特定的功能。为了实现这一点,程序会在导入地址表中存储这些函数的地址。IAT 是一个数据结构,存储了程序所依赖的外部模块的函数地址。

IAT HOOK 的基本原理是通过修改导入地址表中的函数地址,将原始函数地址替换为自定义的函数地址。这样,在程序调用原始函数时,实际上会执行被替换的自定义函数,从而实现对目标函数的拦截和修改。

1、以下是 IAT HOOK 的一般步骤:

  1. 定位目标函数:首先,需要确定要钩子的目标函数。可以使用工具如 Dependency Walker、IDA Pro 等进行静态分析,或者使用动态调试工具来监视函数调用。

  2. 获取目标模块的基址:根据目标函数的模块名称或函数地址,可以通过 Windows API 函数如 GetModuleHandleLoadLibrary 来获取目标模块的基址。

  3. 获取导入地址表(IAT):通过解析目标模块的导入描述符表(Import Descriptor Table),可以获得导入地址表的位置。

  4. 修改导入地址表:将目标函数的地址替换为自定义函数的地址,即进行钩子操作。可以使用 VirtualProtect 函数来修改内存页面的访问权限,确保可以写入。

  5. 自定义函数的实现:编写自定义函数的代码,用于替代原始函数的功能。在自定义函数中,可以执行一些额外的操作,如记录日志、修改参数、阻止函数调用等。

  6. 调用原始函数:在自定义函数中,如果需要调用原始函数,可以通过函数指针来调用被替换的原始函数。

需要注意的是,IAT HOOK 是一种对二进制代码进行修改的技术,属于比较底层的操作。在实施 IAT HOOK 时,需要小心处理内存权限和线程同步等问题,以确保安全性和稳定性。同时,对于一些保护机制较为严密的程序,IAT HOOK 可能会受到一些防护措施的干扰,需要针对具体情况进行适配和调试。

二、编程前准备工作:

1、PE文件结构图

2、PE加载过程

1)根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer).

2)根据SizeOfHeader的大小,将头信息从FileBuffer拷贝到ImageBuffer

3)根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.

PE文件的组成: 

数据目录表一共15张表:

导入表的数据结构: 

在PE文件加载之前,两张表里的内容完全一样,都是存放的函数名称 

加载之后:函数地址将被写入IAT表当中,INT依旧保留dll的函数名称

关于PE先讲这些,如果感兴趣专门做一期讲。

3、什么是IAT表和INT表

  1. IAT(Import Address Table):IAT 是 PE 文件中的一个数据结构,用于存储被导入函数的地址。IAT 是由一系列函数指针构成的表格,每个函数指针指向一个被导入函数的地址。当 PE 文件加载到内存中时,操作系统会填充 IAT 表格,将被导入函数的地址写入相应的函数指针位置。通过 IAT 表,程序可以直接调用被导入函数,而无需在运行时解析函数地址。这样可以提高程序的执行效率。

  2. INT(Import Name Table):INT 表是 PE 文件中的另一个数据结构,用于存储被导入函数的名称。INT 表包含一系列函数名称的字符串,每个字符串对应一个被导入函数的名称。INT 表的每个条目与 IAT 表的相应条目一一对应。在加载 PE 文件时,操作系统会根据 INT 表中的函数名称进行动态链接,查找并填充 IAT 表中的函数地址。

综合起来,IAT 表存储被导入函数的地址,INT 表存储被导入函数的名称。操作系统在加载 PE 文件时,会先根据 INT 表中的函数名称解析出函数地址,然后将地址填充到 IAT 表中。这样,在程序运行时,可以直接通过 IAT 表中的函数指针调用被导入函数,而无需进行动态链接和解析函数地址的过程。

三、案例介绍

事情是这样的,有一天坤坤需要在全民制作大赛上进行基本功大比武,他准备把自己练习两年半的篮球🏀舞展示给观众,但是由于台下ikuns太多了,有点影响周边的居民休息,于是我有一个朋友委托我可不可以把ikuns换成小黑子,这样就会少很多尖叫,进而达到控制噪声的效果,我想了一下这个功能还算比较简单,只需要hook掉ikuns的尖叫就可以了,于是就答应了他。

四、整体思路分析

1)首先使用GetFuncAddr获取需要替换的原函数的地址

2)使用新的函数地址(DWORD)MyFunc替换原来的地址

那么如何获取需要hook的函数地址呢?

以MessageBoxW为例,他存在于user32.dll这个dll文件当中,因此我们只需要遍历注入进程的导入表,首先找到user32.dll这个dll,然后再匹配这个dll里的MessageBoxW这个函数,此时*pIAT里保存的就是MessageBoxW的函数地址。

如何替换地址?

》使用VirtualProtect修改内存属性,然后*g_iatAddr=(DWORD)MyFunc;修改为我们自定义的函数地址即可。

注意事项:

hook的时候VirtualProtect一般成对出现,这就像做完坏事,记得要恢复现场😂😂😂

指针保存的是地址,而地址里的内容才是真正的数据

五、具体编程

以下是hookMsgBox.dll的完整代码,如果你需要ikun舞台的代码可以来私信我,或者评论区留言。

#include "pch.h"
#include<iostream>
using namespace std;
#include<Windows.h>

DWORD* g_iatAddr = NULL;
DWORD g_oldFuncAddr = 0;
DWORD g_newFuncAddr = 0;

void MessageBoxFormatted(const char* format, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, format);
    vsprintf_s(buffer, sizeof(buffer), format, args);
    va_end(args);

    MessageBoxA(NULL, buffer, "Formatted MessageBox", MB_OK);
}

//自定义函数
int WINAPI MyMessageBoxA(
    HWND   hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT   uType
)
{
    MessageBoxA(0, "你干嘛,食不食油饼", "粉龄:两秒半", MB_OK);
    return 0;
}


BOOL startHook() {
    DWORD oldProtect;
    g_newFuncAddr = (DWORD)(MyMessageBoxA);
    VirtualProtect(g_iatAddr, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &oldProtect);
    *g_iatAddr = g_newFuncAddr;
    VirtualProtect(g_iatAddr, sizeof(DWORD), oldProtect, &oldProtect);
    MessageBoxA(0, "原函数已被替换!", "标题", MB_OK);
    return TRUE;
}

BOOL endHook() {
    DWORD oldProtect;
    VirtualProtect(g_iatAddr, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &oldProtect);
    *g_iatAddr = g_oldFuncAddr;
    VirtualProtect(g_iatAddr, sizeof(DWORD), oldProtect, &oldProtect);
    MessageBoxA(0, "已经修改回来了", "标题", 0);
    return TRUE;
}

DWORD* getFuncAddr(const char* dllName, const char* funcName) {
    //获取exe的模块基地址(ImageBase)
    HMODULE hModule = GetModuleHandleA(0);
    //内存当中
    int x = 0;
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char*)hModule + pDosHeader->e_lfanew);
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = &(pNtHeader->OptionalHeader);
    //导入表在数据目录表的第1张表
    IMAGE_DATA_DIRECTORY dataDirectory = pOptionHeader->DataDirectory[1];
    //datadirectory[1]里面保存了导入表所在的虚拟地址
    //这里的virtualAddr是RVA
    PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(((DWORD)(hModule) + dataDirectory.VirtualAddress));

    while (pImportTable->Name) {
        char* DLLName = (char*)((DWORD)hModule + pImportTable->Name);
        if (_stricmp(dllName, DLLName) == 0) {

            MessageBoxFormatted("%s已经被找到了!", DLLName);
            //获取INT表地址
            PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)((DWORD)hModule + pImportTable->OriginalFirstThunk);
            //获取IAT表地址
            PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)(((DWORD)hModule + pImportTable->FirstThunk));

            while (pINT->u1.Function) {

                //如果按照名称导入
                if ((pINT->u1.Ordinal & 0x80000000) == 0) {
                    PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)((DWORD)hModule + pINT->u1.Ordinal);
                    if (strcmp(pImportName->Name, funcName) == 0) {
                        MessageBoxFormatted("函数名称:%s", pImportName->Name);
                        //*IAT里保存的是函数地址
                        return (DWORD*)pIAT;
                    }
                }
                //遍历导入表的下一张导入名称表
                pIAT++;
                pINT++;
            }
        }
        //遍历下一张导入表
        pImportTable++;
    }
    return NULL;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        MessageBoxA(0, "dll已加载到目标进程", "标题", MB_OK);
        g_iatAddr = getFuncAddr("user32.dll", "MessageBoxW");
        if (g_iatAddr == NULL) {
            MessageBoxA(0, "没有找到需要hook的函数", "标题", MB_OK);
            return -1;
        }
        g_oldFuncAddr = *g_iatAddr;
        bool bRet = startHook();
        break;
    }
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH: {
        endHook();
        break;
    }
    }
    return TRUE;
}

六、运行结果

修改之前:

 IAT hook之后:

今天的内容就到这里了,喜欢的话多多点赞、收藏、关注吧!💗💗💗 

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

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

相关文章

java项目打包方式

普通项目打包 项目内容很简单&#xff0c;只是引用了一个三方包。 打包步骤 File-Project Structure... 点击确定后选择Build - Build Artifacts.. 选择build即可&#xff0c;可以查看编译日志 maven项目打包 若果是普通项目就先转为maven项目。 右键项目选择第二项add frame…

【Netty】Netty 解码器(十二)

文章目录 前言一、编解码概述1.1、编解码器概述1.2、Netty 内嵌的编码器 二、解码器2.1、ByteToMessageDecoder 抽象类2.1.1、常用方法2.1.2、将字节转为整形的解码器示例 三、ReplayingDecoder 抽象类四、MessageToMessageDecoder 抽象类总结 前言 回顾Netty系列文章&#xf…

K-Means算法实现鸢尾花数据集聚类

目录 1. 作者介绍2. K-Means聚类算法2.1 基本概念2.2 算法流程 3. K-Means聚类算法实现3.1 鸢尾花数据集3.2 准备工作3.3 代码实现3.4 结果展示 4. 问题与解析参考链接 1. 作者介绍 张勇&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向…

第3章“程序的机器级表示”:算术和逻辑操作

文章目录 3.5 算术和逻辑操作3.5.1 加载有效地址3.5.2 一元和二元操作3.5.3 移位操作3.5.4 讨论3.5.5 特殊的算术操作 3.5 算术和逻辑操作 下图列出了一些双字整数操作&#xff0c;分为四类。 二元操作有两个操作数&#xff0c;而一元操作只有一个操作数。 描述这些操作数的…

极光笔记 | EngageLab Push的多时区解决方案

01、引言 多时区问题一直是全球客户和终端用户面临的挑战之一。EngageLab Push 致力于解决这个问题&#xff0c;确保全球各地的终端用户可以平等地享受到同样的推送服务&#xff0c;同时让客户能够更好地管理不同时区的应用和对应的终端用户。 02、解决多时区问题的总体方案 1…

软件测试----软件开发模型

1、瀑布模型 &#xff08;1&#xff09;瀑布模型如下 &#xff08;2&#xff09;瀑布模型的缺点&#xff1a; 在瀑布模型中&#xff0c;测试是在编码结束后才介入&#xff0c;对软件开发流程前期质量是没有保障的 &#xff08;3&#xff09;采用瀑布模型的场景&#xff1a; …

31 KVM管理系统资源-管理虚拟内存NUMA

文章目录 31 KVM管理系统资源-管理虚拟内存NUMA31.1 NUMA简介31.2 配置Host-NUMA操作步骤 31.3 配置Guest-NUMA操作步骤 31 KVM管理系统资源-管理虚拟内存NUMA 31.1 NUMA简介 传统的多核运算使用SMP&#xff08;Symmetric Multi-Processor&#xff09;模式&#xff1a;将多个…

Flume系列:Flume 自定义Interceptor拦截器

目录 Apache Hadoop生态-目录汇总-持续更新 1&#xff1a;Interceptor拦截器的使用场景 2&#xff1a;Interceptor拦截器在采集流程中的位置 3&#xff1a;自定义Interceptor拦截器 pom.xml 拦截器java代码 打包上传 4&#xff1a;使用自定义的拦截器 方式一&#xff1…

Springboot +spring security,自定义认证和授权异常处理器

一.简介 在Spring Security中异常分为两种&#xff1a; AuthenticationException 认证异常AccessDeniedException 权限异常 我们先给大家演示下如何自定义异常处理器&#xff0c;然后再结合源码帮助大家进行分析 二.创建项目 如何创建一个SpringSecurity项目&#xff0c;前…

分布式锁和事务关系的细节

使用redssion在redis上以及结合自定义注解利用spring的环绕切面来实现分布式锁功能 代码示例 controller、service层 RequestMapping("insertNumber/{number}/{id}") public boolean insertNumber(PathVariable Long number,PathVariable Long id){return testSer…

rust 中protobuf生成与使用

首先创建一个项目proto 进入到这个文件夹中 创建我们的proto文件 初始化的项目结构是这个样子的 新建一个hello.proto文件内容如下 syntax "proto3";package hello;service Greeter {rpc SayHello (HelloRequest) returns (HelloReply) {} }message HelloRequest …

干货 | 师兄手把手教你如何踏上科研道路

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐&#xff5e; 今天&#xff0c;邀请到鲁小白&#xff0c;给大家分享一下他踏上科研道路的心路历程。 大家好&#xff0c;我是鲁小白&#xff0c;我真正进入科研的时间&#xff0c;研究生3年再…

【C++】类和对象——类的引入、类的访问限定符、类的作用域、类的实例化、类的储存、this指针的引出和特性

文章目录 1.类的引入2.类的访问限定符3.类的作用域4.类的实例化5.类的储存6.this指针6.1this指针的引出6.2this指针的特性 1.类的引入 C是在C的基础上加以扩展。 在C语言中&#xff0c;我们想要让一个类型含有多种成员变量&#xff0c;我们使用结构体&#xff1b;而在C中我们可…

Doris节点扩容及数据表

扩容和缩容 上篇文章简单讲了doris的安装&#xff0c;本章分享的是doris中fe和be节点的扩容缩容以及doris的数据表1、FE 扩容和缩容 使用 MySQL 登录客户端后&#xff0c;可以使用 sql 命令查看 FE 状态&#xff0c;目前就一台 FE mysql -h linux -P 9030 -uroot -p mysql&…

python+django乡村居民数据的可视化平台

本论文主要论述了如何使用Django框架开发一个乡村振兴数据的可视化平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述乡村振兴数据的可视化平台的当前背景以…

拼多多二面,原来是我对自动化测试的理解太浅了

如果你入职一家新的公司&#xff0c;领导让你开展自动化测试&#xff0c;作为一个新人&#xff0c;你肯定会手忙脚乱&#xff0c;你会如何落地自动化测试呢&#xff1f; 01 什么是自动化 有很多人做了很长时间的自动化但却连自动化的概念都不清楚&#xff0c;这样的人也是很悲…

Android之 MVC到MVVM架构发展和封装

一 简介 1.1 软件架构发展趋势是解耦&#xff0c;即分离数据层和视图层&#xff0c;使得数据层专注于业务的数据和逻辑处理。从而提高代码的可读可编辑效率&#xff0c;提高团队协作能力&#xff0c;项目的生产能力&#xff0c;降低后期维护成本。 1.2 Android架构发展MVC -…

计算机组成原理实验四 微程序控制器实验报告

我班算是几乎最后一个做实验的班级了&#xff0c;报告参考了一些朋友提供的数据加上一些自己的主观拙见&#xff0c;本人水平有限加之制作仓促难免有错误&#xff0c;望大家批评指正。 4.1 微程序控制器实验 一、实验目的 (1) 掌握微程序控制器的组成原理。 (2) 掌握微程序的…

【蓝桥杯计算思维题】少儿编程 蓝桥杯青少组计算思维真题及详细解析第5套

少儿编程 蓝桥杯青少组计算思维真题及详细解析第5套 1、北京冬奥会经历 17( ),中国体育代表团收获的金牌数和奖牌数均创历史新高 A、年 B、月 C、天 D、小时 答案:C 考点分析:主要考查小朋友们对时事的了解,北京冬奥会总共经历了17天,所以答案C 2、下面图形的周长是…

Python系列模块之标准库json详解

感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01; 目录 一、Json介绍 二、JSON 函数 2.1 json.dumps 2.2 json.loads 2.3 实战案例&#xff1a;钉钉消息发送 一、Json介绍 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它使得人们…