Hook的新大陆:frida

体验了一把frida,感觉像是发现了新大陆一样,不信你继续看~

frida跟xposed对比:
xposed:root + xposed环境 + 写xposed模块
frida:root + 写js脚本

frida只需要设备有root权限,无需刷xposed环境,就能实现hook功能,而且不需要重启App,修改js脚本之后直接注入即可。本文将手把手带你上手frida。

frida github:
https://github.com/frida/frida

一、准备

frida分客户端环境和服务端环境。

在客户端我们可以编写js代码或者Python代码,连接远程设备,提交要注入的代码到远程,接受服务端的发来的消息等。

在服务端,我们需要用Javascript代码注入到目标进程,操作内存数据,给客户端发送消息等操作。我们也可以把客户端理解成控制端,服务端理解成被控端。

假如我们要用PC来对Android设备上的某个进程进行操作,那么PC就是客户端,而Android设备就是服务端。

1.1 准备frida服务端环境

根据自己的平台下载frida服务端并解压
https://github.com/frida/frida/releases

解压,重命名为 frida-server

电脑连接手机,通过adb执行以下命令将服务端推到手机的 /data/local/tmp 目录

adb push frida-server /data/local/tmp/frida-server

执行以下命令修改frida-server文件权限

adb shell chmod 777 /data/local/tmp/frida-server

启动服务

adb shell
cd /data/local/tmp
su
./frida-server

1.2 准备客户端环境

我用的是mac,windows同理,
首先需要python环境,这个自己安装,然后通过pip安装frida

命令如下

pip install frida-tools
pip install frida

等待一会儿,测试frida是否安装成功


1.3 测试

查看进程

frida-ps -U

参数解释:

-U 指定对USB设备操作
-l 指定加载一个Javascript脚本
最后指定一个进程名,如果想指定进程pid,用-p选项。正在运行的进程可以用frida-ps -U命令查看

这个报错是由于服务端没启动,前面的启动服务是不是没成功,回去看 1.1节。
然后在另一个控制台执行 frida-ps -U

可以看到手机正在运行的进程pid都列出来了。

接下来将一个脚本 myhook.js 注入到Android目标进程

frida -U -l myhook.js com.lanshifu.demo_module

成功了,这个myhook.js内容如下

if(Java.available){
    Java.perform(function(){
        var MainActivity = Java.use("com.lanshifu.demo_module.ui.activity.DemoMainActivity");
        MainActivity.testCrash.overload("int").implementation=function(chinese,math){
            console.log("[javascript] testCrash method be called.");
            send("hook  testCrash method success>>>");
            return this.testCrash(12345678);      
        }
    });
}

拦截DemoMainActivitytestCrash(int)方法,调用的时候打印信息并且将入参改为12345678。日志打印出来了,界面上看也成功了,就不上图了,下面会详细介绍hook的各种操作。

1.4 控制脚本命令

frida运行过程中,执行%resume重新注入,执行%reload来重新加载脚本;执行exit结束脚本注入

一般情况下,修改js脚本,然后输入exit结束脚本注入,再重新输入注入命令即可

二、Hook 实战

2.1 载入类

Java.use方法用于声明一个Java类,在用一个Java类之前首先得声明。比如声明一个String类,要指定完整的类名:
var StringClass=Java.use("java.lang.String");

2.2 参数

修改一个函数的实现是逆向调试中相当有用的。修改一个函数的实现后,如果这个函数被调用,我们的Javascript代码里的函数实现也会被调用。

参数类型表示(跟JNI里面的方法签名基本一样)
  1. 对于基本类型,直接用它在Java中的表示方法就可以了,不用改变,例如:
    int
    short
    char
    byte
    boolean
    float
    double
    long

  2. 基本类型数组,用左中括号接上基本类型的缩写

基本类型 缩写:
boolean: Z
byte: B
char: C
double: D
float: F
int: I
long: J
short: S

例如:int[]类型,在重载时要写成[I

  1. 任意类,直接写完整类名即可

例如:java.lang.String

  1. 对象数组,用左中括号接上完整类名再接上分号

例如:[java.lang.String;

2.3 hook 无参构造函数

//声明一个Java类
var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
        
//hook 无参构造
MyClass.$init.overload().implementation=function(){
     //调用原来构造
     this.$init();
     send("hook 无参构造 ");
 }

2.3 hook 有参构造函数

var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
 //hook 有参构造,入参是 int[]
MyClass.$init.overload('[I').implementation=function(param){
     send("hook  有参构造 init(int[] i) method ");
}

2.4 hook 多参数的构造函数的实现

ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){
    //do something
}

注意:当构造函数(函数)有多种重载形式,比如一个类中有两个形式的func:void func()和void func(int),要加上overload来对函数进行重载,否则可以省略overload

2.5 hook 一般函数

 //void函数
MyClass.method1.overload().implementation=function(){
    send("hook  method1  ");
}
        
//有入参的函数
MyClass.method2.overload("int","int").implementation=function(param1,param2){
     send("hook  method2  ");
     this.method2(100,100)
}
        

2.6 调用函数

和Java一样,创建类实例就是调用构造函数,而在这里用$new表示一个构造函数。

//实例化一个类并调用它的method3方法
var ClassName=Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
var instance = ClassName.$new();
instance.method3(1,2,3);

2.7 类型转换

用Java.cast方法来对一个对象进行类型转换,如将variable转换成java.lang.String:

var StringClass=Java.use("java.lang.String");
var NewTypeClass=Java.cast(variable,StringClass);

2.8 Java.perform方法

Java.perform(fn)在Javascript代码成功被附加到目标进程时调用,我们核心的代码要在里面写。格式:

Java.perform(function(){
//do something...
});

三、配置python脚本注入

3.1 创建 FridaDemo.py

我直接在Pycharm 里面创建了 FridaDemo.py,然后代码如下

# -*- coding: UTF-8 -*-

import frida, sys

jscode = """
if(Java.available){
    Java.perform(function(){
        var MainActivity = Java.use("com.lanshifu.demo_module.ui.activity.DemoMainActivity");
        
        MainActivity.testCrash.overload("int").implementation=function(chinese,math){
            console.log("[javascript] testCrash method be called.");
            send("hook  testCrash method success>>>");
            return this.testCrash(12345678);      
        }
        
        //声明一个Java类
        var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
        
        //hook 无参构造
        MyClass.$init.overload().implementation=function(){
                //调用原来构造
                this.$init();
                send("hook 无参构造 ");
        }
        
        //hook 有参构造,入参是 int[]
        MyClass.$init.overload('[I').implementation=function(param){
             send("hook  有参构造 init(int[] i) method ");
        }
        
        //hook 多个参数构造,入参是 int,int
        MyClass.$init.overload("int","int").implementation=function(param1,param2){
             send("hook  success  ");
             send(param1);
             send(param2);
             //修改返回值
             return 100;
        }
        
        //void函数
        MyClass.method1.overload().implementation=function(){
            send("hook  method1  ");
            this.method1();

        }
        
        //有入参的函数
        MyClass.method2.overload("int","int").implementation=function(param1,param2){
            send("hook  method2  ");
            
            //实例化一个类并调用它的method3方法
            var ClassName=Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
           var instance = ClassName.$new();
           instance.method3(1,2,3);
        
            this.method2(100,100)
        }
        
        //
        MyClass.method3.overload("int","int","int").implementation=function(param1,param2,param3){
            send("hook  method3  ");
            this.method3(100,100,100)
        }
        
        
    });
}
"""

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)
pass

# 查找USB设备并附加到目标进程
session = frida.get_usb_device().attach('com.lanshifu.demo_module')
# 在目标进程里创建脚本
script = session.create_script(jscode)
# 注册消息回调
script.on('message', on_message)
print('[*] Start attach')
# 加载创建好的javascript脚本
script.load()
# 读取系统输入
sys.stdin.read()

执行这个python脚本,手机打开app执行到该方法,看到打印了日志


ctrl + c停止脚本,看起来方便一些。

接下来分析一下步骤:

  1. 通过调用frida.get_usb_device()方法来得到一个连接中的USB设备(Device类)实例
  2. 调用attach附加到目标进程
  3. 调用Session类的create_script()方法创建一个脚本,传入需要注入的javascript代码并得到Script类实例
  4. 调用Script类的on()方法添加一个消息回调,第一个参数是信号名,乖乖传入message就行,第二个是回调函数
  5. 最后调用Script类的load()方法来加载刚才创建的脚本。

四、总结

  1. 手机配置frida 服务端环境
  2. 电脑配置frida 客户端环境
  3. 编写js脚本
  4. 通过adb启动服务,adb shell 'su -c /data/local/tmp/frida-server'
  5. 电脑执行脚本:frida -U -l myhook.js com.lanshifu.demo_module
  6. 在命令行输入exit,回车,停止注入代码

体验了一把frida之后, 发现一个很明显的优势,就是不需要写xposed模块,直接一个js脚本,就能马上测试hook点,不需要重启应用。

手机frida服务端的配置踩了一个坑,下载错了,我的6.0 手机是arm架构的,我下载了arm64的,导致注入js之后手机崩溃。


本文参考:
https://www.jianshu.com/p/f98aca8f3c05
https://www.frida.re/docs/examples/android/

如有雷同,纯属copy。

我的简书主页:
蓝师傅_Android

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 如需转载请注明出处 by SimonKoh 0x01 前言 关于android的hook以前一直用的xposed来...
    SimonKoh阅读 68,336评论 5 21
  • frida是一款方便并且易用的跨平台Hook工具,使用它不仅可以Hook Java写的应用程序,而且还可以Hook...
    luoyesiqiu阅读 16,466评论 1 10
  • 0x01 简介 frida 是一款基于 python+javascript 的 hook 框架,可运行在 andr...
    simplehych阅读 48,006评论 12 39
  • 0x0 零 Hook是我们常用的一种技术手段,在开发中,也经常看到Self-Hook等操作,而在逆向分析中,Hoo...
    老江_阅读 121,879评论 1 7
  • 我想告诉你我追求的,缺只是我在追求着,我想给你去我不曾拥有的力量,但只是我一厢情愿罢了,奈何天地间,山河恋,渡过难...
    酷起阅读 164评论 0 0