Frida简介
Frida是一款基于Python + JavaScript 的hook框架,本质是一种动态插桩技术。可以用于Android、Windows、iOS等各大平台,其执行脚本基于Python或者Node.js写成,而注入代码用JavaScript写成,所以有必要了解一些这些语言的语法。本文主要讲述了Android上Frida框架的使用。
原理
frida使用的是动态二进制插桩技术(DBI)。
插桩技术是指将额外的代码注入程序中以收集运行时的信息,可分为两种:
- 源代码插桩[Source Code Instrumentation(SCI)]:顾名思义,在程序源代码的基础上增加(注入)额外的代码,从而达到预期目的或者功能;
- 二进制插桩(Binary Instrumentation):额外代码注入到二进制可执行文件中,通过修改汇编地址,改变程序运行内容,运行后再返回到原来程序运行出处,从而实现程序的额外功能。
●静态二进制插桩[Static Binary Instrumentation(SBI)]:在程序执行前插入额外的代码和数据,生成一个永久改变的可执行文件。
●动态二进制插桩[Dynamic Binary Instrumentation(DBI)]:在程序运行时实时地插入额外代码和数据,对可执行文件没有任何永久改变。
DBI能做什么?
(1)访问进程的内存
(2)在应用程序运行时覆盖一些功能
(3)从导入的类中调用函数
(4)在堆上查找对象实例并使用这些对象实例
(5)Hook,跟踪和拦截函数等等
环境搭建
conda 安装python 环境
# 创建环境
conda create -n frida_ python=3.7
# 进入环境
# conda activate frida
安装frida 包
pip install frida -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install frida-tools -i https://pypi.mirrors.ustc.edu.cn/simple/
vscode 配置代码提示
首先安装Node.js (nodejs.org),新建一个文件夹,在目录下执行下面这条命令。
npm i @types/frida-gum
g)
配置成功,可以在当前目录下编写js代码了。
Server环境配置
Releases · frida/frida (github.com)
下载Frida的Server端->frida-server-15.1.4-android-arm64.xz【这里版本要与hook程序架构适配】
然后push到手机的data/local/tmp目录下,和IDA的动态调试有点类似
adb devices # 查看端口
adb.exe connect 127.0.0.1:62026
adb push .\frida-server-16.0.11-android-x86_64
/data/local/tmp/frida
adb shell
su
cd /data/local/tmp/frida
# 然后修改权限
chmod 777 frida-server-16.0.11-android-x86_64
# 直接运行frida服务
./frida-server-16.0.11-android-x86_64
# 开启端口转发,转发android TCP端口到本地
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
如果能显示进程列表说明环境搭建完成
# PC端输入
frida-ps -R
Frida Hook
执行HOOK
首先来说一下如何执行hook代码
启动时hook,CMD输入
# 将 xxxxxx 换成你手机里安装好的任意apk包名
frida -U -f com.test.apk -l test.js --no-pause
启动apk后 hook
frida -U -f com.test.apk --no-pause
%load test.js
Java层hook
Frida的Java层重要函数
使用java平台—>Java.perform(function () {}
获取Java类 —>Java.use(className)
当我们获取到Java类之后,我们直接通过 <wrapper>.<method>.implementations =function() {}
的方式来hook wrapper类的method方法,不管是实例方法还是静态方法都可以。
Hook普通方法
被hook的代码如下;
package com.example.xxx;
public class Student {
static public int Add(int a,int b)
{
return a+b;
}
}
接着编写Hook代码
function main()
{
//使用java平台
Java.perform(
function() {
//获取java类
var student=Java.use("com.example.xxx.Student");
//hook Add方法(重写Add方法)
student.Add.implementation=function(a,b)
{
//修改参数
a=123;
b=456;
//调用原来的函数
var res = this.Add(a,b);
//输出结果
console.log(a,b,res);
return res;
}
}
);
}
setImmediate(main)
Hook 执行so 函数
hook有导出函数
通过导出函数名称找到函数地址即可进行hook
// # com.xfast.hgr:bg
function main()
{
Java.perform(function () {
var JavaString = Java.use("java.lang.String");
var adk_addr = Module.findExportByName("libnativelib.so","Java_com_faster_nativelib_NativeLib_adk");
console.log("[*] 目标hook函数的内存地址是: " + adk_addr);
// var aes_128 = new NativeFunction(aes_addr , 'pointer', ['pointer', 'pointer']); // 返回值,参数
Interceptor.attach(adk_addr,{
//在hook函数之前执行的语句
onEnter: function(args)
{
console.log("[*] Success Hook So!");
},
//在hook函数之后执行的语句
onLeave:function(retval)
{
console.log("[*] 原始的So层函数返回值是:"+ Java.vm.getEnv().getStringUtfChars(retval,null).readCString());
// var change=1;
// retval.replace(change);
// console.log("[*] 篡改的So层函数返回值是:"+retval);
}
});
});
}
// frida print String 类型返回值
// //方式一
// var element = Java.cast(obj,Java.use("java.lang.String"));
// //方式二
// var element = Java.vm.getEnv().getStringUtfChars(obj,null).readCString();
setImmediate(main)
执行
// 配置好环境
frida -UF com.xfast.hgr -l test.js
或者
frida -UF com.xfast.hgr
%load test.js
成功hook 函数返回值。
# sdk() ret= faaf332a
# return outline.outline20("vs3ad", new NativeLib().adk(), "abb");
key = "vs3adfaaf332aabb"
hook无导出函数
.so 文件被去符号,无法找到对应函数名称,通过偏移地址调用函数。
function hookTest9()
{
//so名称
var so_name="libnative-lib.so";
//要Hook的函数偏移
var fun_off=0x7078;
//加载到内存后,函数地址=so地址+函数偏移
var so_add=Module.findBaseAddress(so_name);
var add_func=parseInt(so_add,16)+fun_off;
var ptr_fun=new NativePointer(add_func);
Interceptor.attach(ptr_fun,{
//在hook函数之前执行
onEnter:function(args)
{
console.log("hook enter");
},
//在hook函数之后执行
onLeave:function(retval)
{
console.log("hook leaver");
}
});
}