frida hook windows

news/2025/10/26 18:04:34/文章来源:https://www.cnblogs.com/msjs/p/19167107

一,前言

frida hook windows其实和 frida hook android 的 so库是差不多的

都是先找地址和偏移,然后通过模板来修改。

我的 frida版本是17.4.0,所以如果你的版本小于17.0,那这个的一些内容并不适用你,因为17.0后,frida删除和更改了很多函数。

二,操作

零,通用模板

是不是很眼熟?我先把模板放这,后面会用的

Interceptor.attach(addr, {onEnter: function(args) {//在进入函数的你想做的事},onLeave: function(retval) {//在退出函数时你想做的事}
});

一,寻找基地址和偏移地址

#include <stdio.h>int testadd(int a,int b){return a+b;
}
int main(){printf("%d\n",testadd(1,2));printf("%d\n",testadd(2,3));return 0;
}

我这里有一个这样的C代码,我想要hook testadd 函数,当期被调用时获取其输入和返回值

首先我要获取其基地址

可以通过这个函数,他返回程序的基地址

Process.getModuleByName("文件名");

例如,我把这个程序编译为a.exe

那么我就这样使用

var a_base = Process.getModuleByName("a.exe");

使用后a_base的值就为基地址。

获取基地址后,我们就要获取偏移地址,那怎么获取呢?

使用IDA打开,在IDA中找到函数的地址

.text:0000000140001450                 public testadd
.text:0000000140001450 testadd         proc near               ; CODE XREF: main+17↓p
.text:0000000140001450                                         ; main+37↓p
.text:0000000140001450                                         ; DATA XREF: ...
.text:0000000140001450

可以看到地址为0x140001450,但这是虚拟地址,我们应该用IDA这个地址减去IDA中基地址

可根据.text段的起始减去0x1000就是基地址

.text:0000000140001000 ;
.text:0000000140001000 ; +-------------------------------------------------------------------------+
.text:0000000140001000 ; |      This file was generated by The Interactive Disassembler (IDA)      |
.text:0000000140001000 ; |           Copyright (c) 2024 Hex-Rays, <support@hex-rays.com>           |

所以我的基地址是0x140000000

那么testadd的偏移地址即为0x1450(怎么那么巧?)狠狠注入

二,获取输入和返回值

既然我们在之前了解了怎么获取地址了,根据通用脚本来写一下把

var a_base = Process.getModuleByName("a.exe");
var offset = 0x1450;
var testadd = a_base.base.add(offset);
Interceptor.attach(testadd, {onEnter: function(args) {//在进入函数的你想做的事},onLeave: function(retval) {//在退出函数时你想做的事}
});

那如何获取输入呢?

函数的输入被放到了args数组里所以可以使用

args[0],args[1]来获取

console.log("[*] testadd called with a=" + args[0] + ", b=" + args[1]);

使用这个即可输出,不过输出的是16进制,想要输出10进制

可以这样弄

console.log("[*] testadd called with a=" + args[0].toInt32() + ", b=" + args[1].toInt32());

那返回值呢?

这个retval就是返回值

我们直接输出他即可

 console.log("[*] testadd returned: " + retval.toInt32());

完整脚本

var a_base = Process.getModuleByName("a.exe");
var offset = 0x1450;
var testadd = a_base.base.add(offset);
Interceptor.attach(testadd, {onEnter: function(args) {console.log("[*] testadd called with a=" + args[0].toInt32() + ", b=" + args[1].toInt32());},onLeave: function(retval) {console.log("[*] testadd returned: " + retval.toInt32());}
});

将这个脚本保存下来,我命名为a.js

如何使用?

frida -f a.exe -l a.js

输出

frida -f a.exe -l a.js____/ _  |   Frida 17.4.0 - A world-class dynamic instrumentation toolkit| (_| |> _  |   Commands:/_/ |_|       help      -> Displays the help system. . . .       object?   -> Display information about 'object'. . . .       exit/quit -> Exit. . . .. . . .   More info at https://frida.re/docs/home/. . . .. . . .   Connected to Local System (id=local)
Spawned `a.exe`. Resuming main thread!
3
5
[*] testadd called with a=1, b=2
[*] testadd returned: 3
[*] testadd called with a=2, b=3
[*] testadd returned: 5
[Local::a.exe ]-> Process terminated
[Local::a.exe ]->Thank you for using Frida!

三,修改参数和返回值

我想要修改这个函数和返回值该怎么做呢,我们只需要在之前的脚本上略微修改即可

var a_base = Process.getModuleByName("a.exe");
var offset = 0x1450;
var testadd = a_base.base.add(offset);
var ci=1;
Interceptor.attach(testadd, {onEnter: function(args) {console.log("[*]" +"第"+ci+"次"+"before a=" + args[0].toInt32() + ", b=" + args[1].toInt32());args[0] = ptr(12);args[1] = ptr(13);console.log("[*]" +"第"+ci+"次"+"after a=" + args[0].toInt32() + ", b=" + args[1].toInt32());},onLeave: function(retval) {console.log("[*]"+"第"+ci+"次"+" before returned: " + retval.toInt32());retval.replace(666);console.log("[*]"+"第"+ci+"次"+" after returned: " + retval.toInt32());ci++;}
});

修改参数只需要添加args[i]=ptr(x);就可以了

我这个例子就是

args[0] = ptr(12);

修改返回值

使用retval.replace();函数

直接输入你想要的值即可

输出

frida -f a.exe -l a.js____/ _  |   Frida 17.4.0 - A world-class dynamic instrumentation toolkit| (_| |> _  |   Commands:/_/ |_|       help      -> Displays the help system. . . .       object?   -> Display information about 'object'. . . .       exit/quit -> Exit. . . .. . . .   More info at https://frida.re/docs/home/. . . .. . . .   Connected to Local System (id=local)
Spawned `a.exe`. Resuming main thread!
666
[*]第1次before a=1, b=2
666
[*]第1次after a=12, b=13
[*]第1次 before returned: 25
[*]第1次 after returned: 666
[*]第2次before a=2, b=3
[*]第2次after a=12, b=13
[*]第2次 before returned: 25
[*]第2次 after returned: 666
[Local::a.exe ]-> Process terminated
[Local::a.exe ]->                                                                                                                   Thank you for using Frida!

运行未调用的函数

我写了一个代码

#include <stdio.h>void get_flag() {printf("flag{114514}\n");
}int main() {volatile int condition = 1; if (condition) {printf("fake");} else {printf("right");}return 0;
}

我在住函数中没有调用get_flag函数,所以永远不会打印flag,而我在这部分的目的就是运行那个函数

依照之前的方法,先找地址,偏移依然是0x1450............难不成第一个自定义的函数都会放到偏移0x1450位置?

text:0000000140001450
.text:0000000140001450     ; int get_flag()
.text:0000000140001450     public get_flag
.text:0000000140001450     get_flag proc near

脚本

var a_base = Process.getModuleByName("ab.exe");
var offset = 0x1450;
var getflag = a_base.base.add(offset);const get_flag= new NativeFunction(getflag, 'void', []);
console.log("hooking");
get_flag();
console.log("done");

这里

const get_flag= new NativeFunction(getflag, 'void', []);

这一行代码的作用是创建一个JS对象,调用的是getflag地址上的函数,没有返回值,没有参数

我们试着运行

Spawning `ab.exe`...
flag{114514}
hooking
done
Spawned `ab.exe`. Resuming main thread!
fake[Local::ab.exe Process terminated
[Local::ab.exe ]->                          

可以看到的确输出flag了,但顺序比console.log输出的快

如果我们想要稍微改写一下这个函数,我们可以再hook一下get_flag()函数

var a_base = Process.getModuleByName("ab.exe");
var offset = 0x1450;
var getflag = a_base.base.add(offset);const get_flag= new NativeFunction(getflag, 'void', []);
Interceptor.attach(getflag, {onEnter: function(args) {console.log("[+] getflag() called");console.log("end...");},onLeave: function(retval) {}
});
get_flag();

这个printf怎么那么快啊,我设置延迟都没有用,算了,不考虑输出顺序了,有能力的可以试着调一下

Spawning `ab.exe`...
flag{114514}
[+] getflag() called
end...
Spawned `ab.exe`. Resuming main thread!
fake[Local::ab.exe Process terminated
[Local::ab.exe ]->                                     

四,修改函数逻辑

这个板块的目的是修改之前的get_flag函数

模板

Interceptor.replace(addr, new NativeCallback(function() {//你的自定义函数
}, "void", []));

void是返回值的类型,后面的[]是参数类型,有就填入,没有就空着

我的get_flag函数没有输入和返回值就是void

脚本

var a_base = Process.getModuleByName("ab.exe");
var offset = 0x1450;
var getflag = a_base.base.add(offset);
const get_flag= new NativeFunction(getflag, 'void', []);
get_flag();
Interceptor.replace(getflag, new NativeCallback(function() {console.log("[*] getflag() called");
}, "void", []));
get_flag();

输出,可见逻辑被修改了,里面的printf没有了。

Spawning `ab.exe`...
flag{114514}
[*] getflag() called
Spawned `ab.exe`. Resuming main thread!
fake[Local::ab.exe Process terminated
[Local::ab.exe ]->Thank you for using Frida!

五,修改汇编

再放一遍我的主函数

#include <stdio.h>void get_flag() {printf("flag{114514}\n");
}int main() {volatile int condition = 1; if (condition) {printf("fake");} else {printf("right");}return 0;
}

可以看到永远不会运行到right分支

IDA的伪代码也不会显示

int __fastcall main(int argc, const char **argv, const char **envp)
{_main();printf("fake");return 0;
}

但实际上汇编是有的

image

可以看到如果ZF标志位是否为1,为1就跳转左边,为0就跳转右边

我们可以把JZ,改为JNZ,这样就是ZF为0跳左边,为1跳右边

模板


Memory.patchCode(addr, x, code => {//你的逻辑
});

x是要修改的指令的多少,addr是地址

根据上面的图片我们可以得知,jz的偏移地址是0x1487

我们只要修改一位即可,所以X为1,JNZ的汇编代码为0x75,所以我们要把那个字节改为0x75

以下是脚本


var a_base = Process.getModuleByName("ab.exe");
var offset = 0x1487;
var a = a_base.base.add(offset);Memory.patchCode(a, 1, code => {var original = code.readU8();console.log("original: 0x" + original.toString(16));code.writeU8(0x75);
});

运行:

Spawning `ab.exe`...
original: 0x74
Spawned `ab.exe`. Resuming main thread!
right[Local::ab.exeProcess terminated
[Local::ab.exe ]->                                                                                                                  Thank you for using Frida!

成功输出right

三,函数总结

以下是AI总结这里面出现的函数,仅供参考

1. Process.getModuleByName

  • 参数:字符串(模块名,如 "a.exe"
  • 输出:Module 对象(包含模块信息)
  • 作用:根据模块名获取模块对象,用于后续计算基地址

2. NativePointer.add

  • 参数:数值(偏移地址)
  • 输出:新的 NativePointer(绝对地址)
  • 作用:通过基地址 + 偏移量计算目标函数的绝对地址

3. Interceptor.attach

  • 参数
    • 目标地址(NativePointer)
    • 包含回调的对象:{ onEnter: function, onLeave: function }
  • 输出:无
  • 作用:挂钩目标函数,在函数进入/退出时执行自定义逻辑

4. NativePointer.toInt32

  • 参数:无
  • 输出:32 位有符号整数
  • 作用:将指针值转换为十进制整数(用于参数/返回值解析)

5. ptr

  • 参数:数值
  • 输出:NativePointer 对象
  • 作用:将数值转换为指针对象(用于修改函数参数)

6. NativePointer.replace

  • 参数:数值(新返回值)
  • 输出:无
  • 作用:修改函数的返回值

7. new NativeFunction

  • 参数
    • 函数地址(NativePointer)
    • 返回类型(字符串,如 "void"
    • 参数类型数组(如 []
  • 输出:可调用的函数对象
  • 作用:创建本地函数的 JS 代理,用于主动调用未执行的函数

8. Interceptor.replace

  • 参数
    • 目标地址(NativePointer)
    • NativeCallback 对象
  • 输出:无
  • 作用:完全替换原函数的逻辑

9. new NativeCallback

  • 参数
    • 替换函数(function)
    • 返回类型(字符串)
    • 参数类型数组
  • 输出:NativeCallback 对象
  • 作用:创建用于函数替换的回调对象

10. Memory.patchCode

  • 参数
    • 目标地址(NativePointer)
    • 修改长度(数值)
    • 修改回调(function)
  • 输出:无
  • 作用:安全修改内存中的机器码(用于汇编级修改)

11. Memory.writeCallback.readU8

  • 参数:无
  • 输出:无符号 8 位整数
  • 作用:读取当前地址的 1 字节值(在 patchCode 回调中使用)

12. Memory.writeCallback.writeU8

  • 参数:无符号 8 位整数
  • 输出:无
  • 作用:向当前地址写入 1 字节值(用于修改单字节指令)



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

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

相关文章

Python图表库Matplotlib 组成部分介绍(Good)

来自: https://www.cnblogs.com/rustfisher/p/15042912.htmlPython图表库Matplotlib 组成部分介绍 图表有很多个组成部分,例如标题、x/y轴名称、大刻度小刻度、线条、数据点、注释说明等等。 我们来看官方给的图,图…

2025年饮料包装设备缠膜机厂家推荐排行榜:全自动缠膜机、饮料包装机、热收缩包装机、流水线缠膜设备源头厂家精选

2025年饮料包装设备缠膜机厂家推荐排行榜:全自动缠膜机、饮料包装机、热收缩包装机、流水线缠膜设备源头厂家精选 随着饮料行业的快速发展,包装设备的技术创新与性能提升成为行业关注焦点。饮料包装设备缠膜机作为生…

[Ubuntu] Ubuntu24.04环境配置(音视频开发)

[Ubuntu] Ubuntu24.04环境配置(音视频开发)$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");目录Ubuntu 24.04 开发环境初始化配置手册1. 系统信息2. 系统更…

Python图表库Matplotlib 组成部分介绍

来自: https://www.cnblogs.com/rustfisher/p/15042912.htmlPython图表库Matplotlib 组成部分介绍 图表有很多个组成部分,例如标题、x/y轴名称、大刻度小刻度、线条、数据点、注释说明等等。 我们来看官方给的图,图…

2025年自动上料机厂家权威推荐榜:螺旋上料机、真空上料机、粉末上料机、管链输送机全系列选购指南

2025年自动上料机厂家权威推荐榜:螺旋上料机、真空上料机、粉末上料机、管链输送机全系列选购指南 在工业自动化快速发展的今天,自动上料机作为生产线的重要设备,其性能直接影响生产效率和产品质量。随着技术的不断…

计数题合集

Arena 设 \(f_{i,j}\) 表示当前考虑 \(i\) 个英雄,这 \(i\) 个英雄初始血量最大值为 \(j\) 的方案数。 显然第一轮中,每个英雄血量都会减少 \(i-1\):若 \(i-1\ge j\),所有英雄都会在第一轮死亡,显然合法,方案数为…

ida动调pyd

首先我们要先写一个py脚本引入pyd 然后引入os和sys库 之后使用print(sys.executable) print(os.getpid()) 加载解释器和进程pid 再之后使用input暂停终端 在IDA中选择Local Windows debugger 再点击Debugger,选择atta…

so文件找不到却可以使用的解决

在用frida hook so库的时候,软件的so一直找不到,而程序却能正常运行 找了半天终于找到解决方法了。 参考https://bbs.kanxue.com/thread-281802.htm查看安装包的AndroidManifest.xml,观察里面的android:extractNati…

继承与多态动手动脑 - 20243867孙堃2405

实验代码// 祖父类 class GrandParent {public GrandParent() {System.out.println("GrandParent 无参构造方法");}public GrandParent(String msg) {System.out.println("GrandParent 带参构造方法:&…

pyarmor解密

一,介绍: pyarmor是一个对python代码的混淆工具,可以使用Pyarmor-Static-Unpack-1shot来解密 https://github.com/GTedd/Pyarmor-Static-Unpack-1shot-GT 二,解密工具使用 在工具文件夹里有一个名为oneshot的文件夹…

简单的CNN实现

import matplotlib.pyplot as plt import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms import matplotlib.pyplot as p…

pyd逆向处理

一,初查: 1.查版本 先用die或者ida或者010查看pyd对应的版本号 假如版本不对的话是不能运行的 2.查函数和参数 可以使用 python自带的help()和dir()函数,查看里面有什么函数或者参数 二,静态分析 通过查字符串可以…

2025年包装机厂家权威推荐排行榜:自动包装机,食品包装机,真空包装机,颗粒包装机优质品牌深度解析

2025年包装机厂家权威推荐排行榜:自动包装机,食品包装机,真空包装机,颗粒包装机优质品牌深度解析 行业背景与发展趋势 包装机械行业作为制造业的重要支撑领域,近年来在智能化、自动化浪潮的推动下呈现出快速发展的…

ia16生成8086汇编

配置:Ubuntu-24.04 ia16 # 添加 PPA 仓库 sudo add-apt-repository ppa:tkchia/build-ia16 sudo apt-get update# 安装预编译包 sudo apt-get install gcc-ia16-elf libi86-ia16-elf# 验证安装 ia16-elf-gcc -v # 应该…

太突然!湘潭大学计算机学院刘昊霖教授不幸逝世,年仅37岁。

近日,我国计算机科学领域一颗冉冉升起的学术之星骤然陨落。湘潭大学计算机学院网络空间安全学院教授、博士生导师刘昊霖因突发疾病,经抢救无效不幸去世,年仅37岁。他的英年早逝,是学校、学界和我国科研事业的重大损…

解包魔改pyinstaller

在做NSSCTF 4th 的CrackMe&F***Me也是见到被魔改的pyinstaller生成的exe文件 直接用工具直接就报错了,这里来写一写这道题的魔改点,虽然我不会魔改pyinstaller,但 1.魔改判断标识: 正常文件结尾应该是这个: 但…

反编译解包微信小程序

做湾区杯时也是遇到了,记录一下 小程序的后缀应为.wxapg使用 npm i wedecode -g安装工具 运行 命令行直接输入 wedecode 即可运行, 全程自动引导wedecode 命令行直接指定参数 # 手动指定一个包wedecode ./name.wxapkg…

浅谈C++中的作用域

C++的作用域是每个初学者很容易混淆的一个知识点,C++ 中的作用域(scope)指的是变量、函数或其他标识符的可见和可访问的范围,如果对于C++的作用域没有一个清晰的认知的话很容易会出现空值或者空指针还有值未能及时…

2025年摩托车厂家权威推荐榜:覆盖街车、跑车、巡航车及越野车型的全方位选购指南与实力解析

2025年摩托车厂家权威推荐榜:覆盖街车、跑车、巡航车及越野车型的全方位选购指南与实力解析 摩托车作为现代交通工具与休闲生活方式的重要组成部分,其市场呈现出多元化、专业化的发展趋势。随着消费者对骑行体验要求…

AIGC图片视频制作通用提示词 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …