使用汇编和反汇编引擎写一个x86任意地址hook

最简单的Hook

刚开始学的时候,用的hook都是最基础的5字节hook,也不会使用hook框架,hook流程如下:

  1. 构建一个jmp指令跳转到你的函数(函数需定义为裸函数)
  2. 保存被hook地址的至少5字节机器码,然后写入构建的jmp指令
  3. 接着在你的代码里做你想要的操作
  4. 以内联汇编的形式执行被hook地址5字节机器码对应的汇编指令
  5. 跳转回被hook的地址下一条指令

这样操作比较繁琐,每次hook都要定义一堆东西,还得自己补充hook地址被修改的汇编指令,最重要的是这种hook无法扩展到Python里使用。

加入反汇编和汇编引擎

csdn有一篇文章说了可以通过引入汇编和反汇编引擎来去掉第二步和第四步,也就是不需要关心hook地址的汇编是什么。

文章中用的汇编引擎是XEDParse,我试了下用vs2017编译不通过,看了文档和issue,必须得使用vs2013及以下的版本才能编译成功,所以就放弃了,改成使用keystone。想编译keystone和Beaengine可以看另一篇文章keystone和beaengine的编译

我也对文章中的代码进行了一些小优化,这也是为了方便引入到Python中使用。

开始写代码

下面的说明可能会啰嗦一些,对每行代码都做了解释。你也可以去看c++ 源码,也对每行代码做了注释。

定义一个hook函数, 参数有四个,返回值是被修改的字节数:

  • hookAddress: 要hook的地址
  • hookFunc: hook的回调函数
  • hookOldCode:保存被修改的字节
  • hookOldSize:hookOldCode的缓冲区大小

size_t HookAnyAddress(__in DWORD hookAddress, __in AnyHookFunc hookFunc, __out BYTE* hookOldCode, __in size_t hookOldSize)

AnyHookFunc的函数指针定义:

typedef void(_stdcall * AnyHookFunc)(RegisterContext*);

RegisterContext结构体的定义

struct RegisterContext
{DWORD EFLAGS;DWORD EDI;DWORD ESI;DWORD EBP;DWORD ESP;DWORD EBX;DWORD EDX;DWORD ECX;DWORD EAX;
};

首先定义一个内存的shellcode,用来存放裸函数里的指令

BYTE ShellCode[0x40] = {0x60,    //pushad0x9C,    //pushfd0x54,    //push esp0xB8, 0x90, 0x90, 0x90, 0x90,  //mov eax,hookFunc0xFF, 0xD0,     //call eax0x9D, //popfd0x61, //popad
};

这里的4个0x90是存放hook回调函数的地址,接着写入回调函数地址

memcpy(&ShellCode[0x4], &hookFunc, 4);

分配一块可执行的内存, 用于存放这段shellcode

DWORD shellcodeMemAddr = (DWORD)VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (shellcodeMemAddr == 0) {return 0;
}

因为shellcode已经写了0xC个字节,所以后面的指令从+0xC开始写

DWORD shellcodeMemAddrStart = shellcodeMemAddr + 0xC;

定义反汇编引擎和汇编引擎,keystone也是老朋友了,之前x86发消息的时候就已经用过了:

// 定义反汇编引擎
DISASM MyDisasm;
memset(&MyDisasm, 0, sizeof(DISASM));
MyDisasm.EIP = (UIntPtr)hookAddress;
// 设置为32位x86平台
MyDisasm.Archi = 32;
MyDisasm.Options = PrefixedNumeral + ShowSegmentRegs;
// PrefixedNumeral: 数值前加0x,ShowSegmentRegs: 显示段寄存器的值// 定义汇编引擎
ks_engine *ks;
ks_err err = ks_open(KS_ARCH_X86, KS_MODE_32, &ks);
if (err != KS_ERR_OK) {return 0;
}

开始计算hook地址的指令,并将指令写到shellcodeMemAddr里

// 保存返回hook地址下一条指令的地址
DWORD hookRetAddr = 0;
// 记录被修改的指令长度
size_t hookSize = 0;
// 开始循环反汇编,直到满足5个字节
while (true) {// 开始反汇编,每次反汇编一条指令,返回这条指令的长度int DisasmCodeSize = Disasm(&MyDisasm);if (DisasmCodeSize < 1) {return 0;}// hook的地址不能包含ret指令if (MyDisasm.Instruction.BranchType == RetType){return 0;}hookSize += DisasmCodeSize;// 保存汇编指令条数size_t encodingCount;// 保存汇编后的指令unsigned char *encodingCode;// 保存汇编后的指令长度size_t encodingSize;// 利用keystone将反汇编后的指令再转为机器码,这么操作可以自动处理相对地址// 前三个参数是输入参数,第二个参数是反汇编会的指令,第三个参数是指令所在的内存地址(用于计算相对偏移)// 后三个参数为输出参数,见定义处if (ks_asm(ks, MyDisasm.CompleteInstr, shellcodeMemAddrStart, &encodingCode, &encodingSize, &encodingCount) != KS_ERR_OK) {return 0;}// 将汇编后的机器码写到shellcodememcpy(&ShellCode[shellcodeMemAddrStart - shellcodeMemAddr], encodingCode, encodingSize);ks_free(encodingCode);// 注意: 反汇编和汇编的机器码和长度可能是不一样的shellcodeMemAddrStart += encodingSize;// 开始下一条指令的反汇编和汇编MyDisasm.EIP += DisasmCodeSize;// 如果指令达到5个字节就结束if (hookSize >= 5){hookRetAddr = MyDisasm.EIP;break;}
}
ks_close(ks);

开始构建跳转指令,跳转回hook地址的下一条指令的位置

// 保存原始内存属性值
DWORD dwOldProtect = 0;
// 给hook的地址赋予可写权限
BOOL bRet = VirtualProtect((LPVOID)hookAddress, 0x20, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (!bRet) {return 0;
}
// 保存被覆盖的机器码
memcpy(hookOldCode, (LPVOID)hookAddress, hookSize);
// 构建跳转指令
BYTE pushRetCode[6] = {0x68, 0x90, 0x90, 0x90, 0x90, // push hookRetAddr0xC3  // ret
};
memcpy(&pushRetCode[1], &hookRetAddr, 4);

将构架的跳转指令写入到shellcode里,并将shellcode写到申请的内存shellcodeMemAddr里

memcpy(&ShellCode[shellcodeMemAddrStart - shellcodeMemAddr], pushRetCode, sizeof(pushRetCode));
// 将shellcode写入申请的内存地址
memcpy((LPVOID)shellcodeMemAddr, ShellCode, sizeof(ShellCode));

开始修改hook地址的机器码,跳转到申请的内存地址shellcodeMemAddr

BYTE jmpCode[5] = { 0xE9, 0xFF, 0xFF, 0xFF, 0xFF };
*(DWORD*)(jmpCode + 1) = shellcodeMemAddr - (DWORD)hookAddress - 5;
memcpy((LPVOID)hookAddress, jmpCode, 5);
BYTE nopCode[2] = { 0x90,0x90};

如果被修改的指令超过了五个字节,其他字节用nop填充

if (hookSize > 5) {memset((LPVOID)(hookAddress + 5), 0x90, hookSize - 5);
}

最后还原内存属性,返回被修改的指令长度

VirtualProtect((LPVOID)hookAddress, 0x20, dwOldProtect, &dwOldProtect);
return hookSize;

取消hook,只需要将保存的机器码还原:

DWORD UnHookAnyAddress(__in DWORD hookAddress, __in BYTE* hookOldCode, __in size_t hookOldSize) {DWORD dwOldProtect = 0;VirtualProtect((LPVOID)hookAddress, 0x20, PAGE_EXECUTE_READWRITE, &dwOldProtect);memcpy((LPVOID)hookAddress, hookOldCode, hookOldSize);VirtualProtect((LPVOID)hookAddress, 0x20, dwOldProtect, &dwOldProtect);return 0;
}
Python中使用

将这个编译成dll就能在Python里加载了,不过dll只能用于hook当前进程,这是因为函数不能跨进程调用,你创建的回调函数,其他进程无法调用。

解决这个问题也很简单,可以在目标进程申请一块可执行的内存,用汇编引擎和反汇编引擎将回调函数写到这块内存里。

不过我的使用场景是将Python注入到了进程,Python作为线程在目标进程里运行,不用这么繁琐。使用案例看另一篇文章封装32位和64位hook框架实战hook日志

参考

  • https://blog.csdn.net/sunflover454/article/details/49029615

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

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

相关文章

Layui 2.9.2 列表商品展示页 用模板引擎 laytpl Ajax 读取json 数据 筛选数组 filter css 限制文体显示过长用。。。代替

全代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title>软件管理器</title><meta name"renderer" content"webkit"><meta http-equiv"X-UA-Compatible" conten…

Flink系列之:Elasticsearch SQL 连接器

Flink系列之&#xff1a;Elasticsearch SQL 连接器 一、Elasticsearch SQL 连接器二、创建 Elasticsearch表三、连接器参数四、Key 处理五、动态索引六、数据类型映射 一、Elasticsearch SQL 连接器 Sink: BatchSink: Streaming Append & Upsert ModeElasticsearch 连接器…

Graylog配置日志保留策略

找了半天没找到说的清楚的&#xff0c;只能抠官方文档 graylog的归档&#xff08;日志持久化&#xff09;只有付费版才能用&#xff0c;所以日志只能存在es中 1.理解官方给出的几个概念 轮转策略 (Index Rotation Strategy): 轮转策略定义了何时创建新的索引以及何时关闭旧的索…

pytorch-模型预测概率值为负数

在进行ocr识别模型预测的时候&#xff0c;发现预测的结果是正确的&#xff0c;但是概率值是负数&#xff1a; net_out net(img) #torch.Size([70, 1, 41]) logit, preds net_out.max(2) #41是类别 需要对类别取最大值 preds preds.transpose(1, 0).contiguous().view(-1) …

Win10安装Gogs保姆级教程

什么是 Gogs? Gogs 是一款极易搭建的自助 Git 服务。 开发目的 Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发&#xff0c;并且支持 Go 语言支持的 所有平台&#xff0c;包括 Linux、Mac OS X…

微软官方出品:GPT大模型编排工具,支持C#、Python等多个语言版本

随着ChatGPT的火热&#xff0c;基于大模型开发应用已经成为新的风口。虽然目前的大型模型已经具备相当高的智能水平&#xff0c;但它们仍然无法完全实现业务流程的自动化&#xff0c;从而达到用户的目标。 微软官方开源的Semantic Kernel的AI编排工具&#xff0c;就可以很好的…

C语言struct,union内存对齐

测试环境&#xff1a; #include<stdio.h> int main(){//1字节对齐struct XXX{unsigned char ch;unsigned int in;unsigned short si;}__attribute__((packed));struct XXX xxx;printf("%zd\n",sizeof(xxx));//7#pragma pack(1)struct YYY{unsigned char ch;u…

057:vue组件方法中加载匿名函数

第057个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

CSS:元素显示模式与背景

CSS&#xff1a;元素显示模式与背景 元素显示模式什么是元素显示模式块级元素 block行内元素 inline行内块元素 inline-block元素显示模式对比元素显示模式转换 display 背景背景颜色 background-color背景图片 background-image背景平铺 background-repeat背景图片位置 backgr…

MIT6.5840-2023-Lab4B: Sharded K/V Service-Sharded K/V Server

实验内容 实现一个分片 k/v 存储系统&#xff0c;分片指如所有以“a”开头的键可能是一个分片&#xff0c;所有以 “b”开头的键可能是另一个分片。每个副本组仅处理几个分片的 Put、Append 操作&#xff0c;实现并行操作&#xff0c;系统总吞吐量&#xff08;单位时间的放入和…

恶意软件样本行为分析——Process Monitor和Wireshark

1.1 实验名称 恶意软件样本行为分析 1.2 实验目的 1) 熟悉 Process Monitor 的使用 2) 熟悉抓包工具 Wireshark 的使用 3) VMware 的熟悉和使用 4) 灰鸽子木马的行为分析 1.3 实验步骤及内容 第一阶段&#xff1a;熟悉 Process Monitor 的使用 利用 Process …

在Linux上安装CLion

本教程将指导你如何在Linux系统上安装CLion&#xff0c;下载地址为&#xff1a;https://download.jetbrains.com.cn/cpp/CLion-2022.3.3.tar.gz。以下是详细的安装步骤&#xff1a; 步骤1&#xff1a;下载CLion 首先&#xff0c;你需要使用wget命令从提供的URL下载CLion的tar…

Redis安全性加强:认证与加密实践

大家好&#xff0c;我是升仔 引言 Redis作为一个广泛使用的高性能键值存储系统&#xff0c;在众多应用场景中扮演着重要角色。然而&#xff0c;由于其默认配置不强调安全性&#xff0c;因此在生产环境中部署Redis时&#xff0c;加强其安全性是非常重要的。 1、实际使用场景&…

前端学习——关于前端框架的思考

前端框架 我们知道在AngularJS&#xff0c;react&#xff0c;vue等前端框架出现之前&#xff0c;前端开发都是通过js直接操作dom树来实现的&#xff0c;而有了前端框架之后&#xff0c;前端开发基本上不需要在直接操作dom树&#xff0c;相当于在原生html的dom树之间和前端程序…

springboot(ssm小学生身体素质测评管理系统 学生体测平台Java系统

springboot(ssm小学生身体素质测评管理系统 学生体测平台Java系统 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&#…

Linux笔记---文件和目录操作

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux学习 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 命令 ls (List): pwd (Print Working Directory): cp (Copy): mv (Move): rm (Remove): 结语 我的其他博客 前言 学习Linux命令…

Centos 7.9安装Oracle19c步骤亲测可用有视频

视频介绍了在虚拟机安装centos 7.9并安装数据库软件的全过程 视频链接&#xff1a;https://www.zhihu.com/zvideo/1721267375351996416 下面的文字描述是安装数据库的部分介绍 一.安装环境准备 链接&#xff1a;https://pan.baidu.com/s/1Ogn47UZQ2w7iiHAiVdWDSQ 提取码&am…

页面级UI状态存储LocalStorage

目录 1、LocalStorageProp 2、LocalStorageLink 3、LocalStorage的使用 4、从UI内部使用LocalStorage 5、LocalStorageProp和LocalStorage单向同步的简单场景 6、LocalStorageLink和LocalStorage双向同步的简单场景 7、兄弟节点之间同步状态变量 LocalStorage是页面级的…

JMeter常见配置及常见问题修改

一、设置JMeter默认打开字体 1、进入安装目录&#xff1a;apache-jmeter-x.x.x\bin\ 2、找到 jmeter.properties&#xff0c;打开。 3、搜索“ languageen ”&#xff0c;前面带有“#”号.。 4、去除“#”号&#xff0c;并修改为&#xff1a;languagezh_CN 或 直接新增一行&…