首页
论坛
课程
招聘

[原创]frida入门总结

2020-3-11 16:35 6733

[原创]frida入门总结

2020-3-11 16:35
6733

一、Frida概述

    Frida是一款轻量级HOOK框架,可用于多平台上,例如android、windows、ios等。
    frida分为两部分,服务端运行在目标机上,通过注入进程的方式来实现劫持应用函数,另一部分运行在系统机器上。
    frida上层接口支持js、python、c等。
    Frida官方github地址为:frida官方github地址


二、Frida安装

    1 、安装python3.7并配置好环境变量(官方推荐python3以上版本至少为3.7),python安装包官方下载地址:https://www.python.org/downloads/

 

    2 、安装frida模块,命令为pip install frida(配置了多个python版本环境的可以使用命令python -m pip install frida防止用pip install frida命令报错)。

 

1.png

 

    3、安装frida-tools模块,命令同上,pip install frida-tools或者python -m pip install frida-tools

 

2.png

 

    4、下载运行在目标机上的frida-sever端,官方下载地址:https://github.com/frida/frida/releases,下载时要选择对应的版本下载,例如我的机器为arm32为架构,就选择frida-server-12.8.14-android-arm.xz下载。(可以在adb使用命令cat /proc/cpuinfo查询)

 

3.png

 

4.png

 

    5、将第四步下载好的文件解压,然后通过命令adb push 你的电脑是存放位置 /data/local/tmp将文件传输到手机中,然后通过adb shell进入手机端,给文件赋权777,并于root权限启动。

 

5.png

 

6.png

 

    6、做完以上几步后,新开一个命令行输入命令frida-ps -U查看手机进程,如果出现以下结果,则frida安装成功。

 

7.png


三、Frida Hook Java层

    1、编写一个小demo用来hook,该demo关键部分代码如下:

 

8.png

 

    2、现在我们将该apk安装好,运行看一下未Hook前的显示字符串!!!

 

9.png

 

    3、现在来编写Hook的Python脚本,脚本代码如下:

import frida  #导入frida模块
import sys    #导入sys模块

jscode = """  #从此处开始定义用来Hook的javascript代码
    Java.perform(function(){  
        var MainActivity = Java.use('com.example.testfrida.MainActivity'); //获得MainActivity类
        MainActivity.testFrida.implementation = function(){ //Hook testFrida函数,用js自己实现
            send('Statr! Hook!'); //发送信息,用于回调python中的函数
            return 'Change String!' //劫持返回值,修改为我们想要返回的字符串
        }
    });
"""

def on_message(message,data): #js中执行send函数后要回调的函数
    print(message)

process = frida.get_remote_device().attach('com.example.testfrida') #得到设备并劫持进程com.example.testfrida(该开始用get_usb_device函数用来获取设备,但是一直报错找不到设备,改用get_remote_device函数即可解决这个问题)
script = process.create_script(jscode) #创建js脚本
script.on('message',on_message) #加载回调函数,也就是js中执行send函数规定要执行的python函数
script.load() #加载脚本
sys.stdin.read()

    4、现在python脚本编写完毕,我们来执行该脚本,首先手机端执行frida,然后通过命令adb forward tcp:27043 tcp:27043adb forward tcp:27042 tcp:27042来转发这两个端口,接着在手机上运行该应用程序,在命令行中执行脚本,最后点击应用的按钮,即可看到字符串已经被替换成我们要替换的了!!!

 

6.png

 

10.png

 

11.png

 

12.png


四、Frida Hook Native层

  4.1、Hook native层返回值为int类型的demo

    1、还是先写一个小demo,下面贴一下关键代码(很简单c语言代码就不再解释了,至于native层函数怎么编写,由于本篇主要不是讲怎么编写so函数,就不过多叙述了,实在不会的可以看一下我的一篇博客,我觉得写得还是挺详细的,博客编写native层函数链接:https://www.cnblogs.com/aWxvdmVseXc0/p/11564809.html)和未Hook前截图:

 

23.png

 

代码:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_fridaso_FridaSoDefine */

#ifndef _Included_com_example_fridaso_FridaSoDefine
#define _Included_com_example_fridaso_FridaSoDefine
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_example_fridaso_FridaSoDefine
* Method:    FridaSo
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_fridaso_FridaSoDefine_FridaSo(JNIEnv *env, jclass obj, jint a, jint b)
{
    int c;
    c = a + b;
    return c;
}

#ifdef __cplusplus
}
#endif
#endif  

13.png

 

14.png

 

    2、接下来我们来写python hook脚本,我们需要hook native层这个函数,达到返回值修改为0的效果。写到这里需要说明一下关于so文件当中的函数,分为导出函数和未导出函数两种,导出函数打开IDA后能够在导出表中找到的函数就是导出函数,未导出函数则在导出表中寻找不到,一般来说静态编写的native函数都能在导出表中寻找到,而动态加载的则无法在导出表中发现!!!

 

15.png

 

代码如下:(跟上面hook java层重复的代码不在注释详讲了!!!)

import frida
import sys

jscode = """
Java.perform(function(){
    //下面这一句代码是指定要Hook的so文件名和要Hook的函数名,函数名就是上面IDA导出表中显示的那个函数名
    Interceptor.attach(Module.findExportByName("libfridaso.so","Java_com_example_fridaso_FridaSoDefine_FridaSo"),{
        //onEnter: function(args)顾名思义就是进入该函数前要执行的代码,其中args是传入的参数,一般so层函数第一个参数都是JniEnv,第二个参数是jclass,从第三个参数开始才是我们java层传入的参数
        onEnter: function(args) {
            send("Hook start");
            send("args[2]=" + args[2]); //打印我们java层第一个传入的参数
            send("args[3]=" + args[3]); //打印我们java层传入的第二个参数
        },
        onLeave: function(retval){ //onLeave: function(retval)是该函数执行结束要执行的代码,其中retval参数即是返回值
            send("return:"+retval); //打印返回值
            retval.replace(0); //替换返回值为0
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print('[*] {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach('com.example.fridaso')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

    3、最后在手机端执行frida-server,转发端口,开启应用,执行脚本,点击按钮,即可看到返回值已经被修改成了0,效果图如下:

 

16.png

 

17.png

  4.2、Hook native层返回值为String类型的demo

    1、上面已经写了怎么Hook修改native层函数返回值为int类型的情况,使用replace()函数直接修改即可,但是返回情况为字符串则不一样,在c语言中,返回值为字符串其实是返回了一个char *(字符串指针),所以简单的替换是无法取效果的,具体怎么修改返回值,接着看下面,下面还是贴上demo的关键代码和未Hook前截图:

 

22.png

 

代码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_fridasostring_fridaSoString */

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_fridasostring_fridaSoString */

#ifndef _Included_com_example_fridasostring_fridaSoString
#define _Included_com_example_fridasostring_fridaSoString
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_example_fridasostring_fridaSoString
* Method:    FridaSo
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_fridasostring_fridaSoString_FridaSo(JNIEnv *env, jclass obj, jstring str)
{
    return str;
}

#ifdef __cplusplus
}
#endif
#endif

未Hook前运行截图:

 

18.png

 

    2、接下来是python Hook脚本(只解释与上面有差异的代码),Hook的函数具体函数名还是使用IDA去寻找

 

19.png

 

python代码:

import frida
import sys

jscode = """
Java.perform(function(){
    Interceptor.attach(Module.findExportByName("libfridaso.so","Java_com_example_fridasostring_fridaSoString_FridaSo"),{
        onEnter: function(args) {
            send("Hook start");
            send("args[2]=" + args[2]);
        },
        onLeave: function(retval){
            send("return:"+retval);
            var env = Java.vm.getEnv(); //获取env对象,也就是native函数的第一个参数
            var jstrings = env.newStringUtf("tamper"); //因为返回的是字符串指针,使用我们需要构造一个newStringUtf对象,用来代替这个指针
            retval.replace(jstrings); //替换返回值
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print('[*] {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach('com.example.fridasostring')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

    3、运行脚本后,点击按钮,我们可以看到字符串已经被替换成了tamper,如下所示:

 

20.png

 

21.png

 

    4、关于为导出函数的Hook,大体上差不多,差别在于需要通过ida找到偏移值计算地址,而不是像导出函数这么方便罢了,但原理都是差不多的,就不在细说了!!!


五、相关附件

    python Hook代码已经demo下载链接:https://pan.baidu.com/s/1ZCIeJXzeTpQ8uJ9Ew5nnGQ
    提取码:z94i



[求职]想求职找工作,请来看雪招聘投递简历!

最后于 2020-3-11 16:36 被windy_ll编辑 ,原因: 添加附件
上传的附件:
最新回复 (37)
熊猫吃鱼 2020-3-11 17:06
2
0
稳 老哥
剁辣椒炒肉 2020-3-11 18:11
3
0
mark学习了,顺便请教一下要Hook OnCreate的话怎么定义函数参数啊
Lixinist 1 2020-3-11 21:05
4
0
没人点赞???
windy_ll 2020-3-12 11:30
5
0
剁辣椒炒肉 mark学习了,顺便请教一下要Hook OnCreate的话怎么定义函数参数啊
MainActivity.onCreate.implementation = function () {}
Editor 2020-3-12 14:32
6
0
感谢分享!
Editor 2020-3-12 14:32
7
0
点赞收藏评论 三连支持!
lukarl 2020-3-13 08:42
8
0
Frida是个好东西,用起来非常舒服
愤怒的平头哥 2020-3-13 16:15
9
0
感谢大佬分享
tDasm 2020-3-13 16:41
10
0
windy_ll MainActivity.onCreate.implementation = function () {}
overload(参数类型) 不需要吗?
windy_ll 2020-3-13 17:58
11
0
tDasm overload(参数类型) 不需要吗?
一般不需要
mb_xghoecki 2020-3-13 18:03
12
0
感谢分享
tDasm 2020-3-16 10:17
13
0
请教一下2个问题:
1、一个类有2个同名方法,比如a(int)、a(string) 怎么hook?
2、一个类有有2个同名方法入口参数类型一样,但是返回值类型不一样,怎么hook?比如a(string)返回string、a(string)返回[B数组  
最后于 2020-3-16 10:18 被tDasm编辑 ,原因:
bjhrwzh 2020-3-16 10:29
14
0
tDasm 请教一下2个问题:1、一个类有2个同名方法,比如a(int)、a(string) 怎么hook?2、一个类有有2个同名方法入口参数类型一样,但是返回值类型不一样,怎么hook?比如a(string)返 ...
多api 文档,同名的方法不同参数可以通过overlad来区分   类.方法.overload('java.lang.String','java.lang.String','boolean').implementation =function(arg1,arg2,arg3){
                
               
       }
tDasm 2020-3-16 10:44
15
0
bjhrwzh 多api 文档,同名的方法不同参数可以通过overlad来区分 类.方法.overload('java.lang.String','java.lang.String','boolean').imp ...
谢谢指教。第2个呢?
Is大龙 2020-3-16 10:54
16
0
这个就很稳了
IMZCF 2020-3-19 00:19
17
0
Error: expected a pointer运行后报这个错误。。。。。。。。。。
windy_ll 2020-3-19 08:27
18
0
IMZCF Error: expected a pointer运行后报这个错误。。。。。。。。。。[em_2]
你把注释全部删除再跑一遍
IMZCF 2020-3-19 22:05
19
0
windy_ll 你把注释全部删除再跑一遍
不是这个问题,是因为so是延迟加载的,拦截的时候还没加载,就报空了,得在dlopen那拦截
windy_ll 2020-3-19 22:51
20
0
IMZCF 不是这个问题,是因为so是延迟加载的,拦截的时候还没加载,就报空了,得在dlopen那拦截
或者你多跑几遍就行了,我刚开始以为是因为注释符号写错了
xueyudon 2020-3-20 21:18
21
0
很详细了,感谢
开心张大炮 2020-3-22 15:35
22
0
tDasm 请教一下2个问题:1、一个类有2个同名方法,比如a(int)、a(string) 怎么hook?2、一个类有有2个同名方法入口参数类型一样,但是返回值类型不一样,怎么hook?比如a(string)返 ...
你说的第二种不存在,先去了解下重载
jack.zhang 2020-3-29 18:07
23
0
详细的不行,支持 
shuichon 2020-3-30 14:13
24
0
很详细和生动,赞。
钞sir 1 2020-3-31 23:20
25
0
Java.perform(function(){ 
        var MainActivity = Java.use('com.example.testfrida.MainActivity'); //获得MainActivity类
        MainActivity.testFrida.implementation = function(){ //Hook testFrida函数,用js自己实现
            send('Statr! Hook!'); //发送信息,用于回调python中的函数
            return 'Change String!' //劫持返回值,修改为我们想要返回的字符串
        }
    });

请问这个代码中的testFrida这个函数名如果没有源码,怎么得到这个函数名呢?
每个反汇编软件反汇编出来的函数名会一样吗?....

windy_ll 2020-4-1 08:03
26
0
钞sir ```javascript Java.perform(function(){ var MainActivity = Java.use('com.example.testfrida ...
反编译得到的,反编译出来的只要没有经过混淆都跟你源码中命名是一样的
mb_ukjlpcwh 2020-4-5 15:11
27
0
Hambur 2020-4-10 10:11
28
0
你好。我想请教下如果想知道某个native层函数是在哪个地址被调用的应该怎么做?
windy_ll 2020-4-10 19:14
29
0
Hambur 你好。我想请教下如果想知道某个native层函数是在哪个地址被调用的应该怎么做?
打印地址即可(.base),可以参考一下这篇文章https://blog.csdn.net/weixin_42486644/article/details/90312019?depth_1-utm_source=distribute.wap_relevant.none-task-blog-BlogCommendFromBaidu-3&utm_source=distribute.wap_relevant.none-task-blog-BlogCommendFromBaidu-3
Hambur 2020-4-10 19:39
30
0
windy_ll 打印地址即可(.base),可以参考一下这篇文章https://blog.csdn.net/weixin_42486644/article/details/90312019?depth_1-utm_s ...
但是这个好像只是获取函数的注册地址。我举个例子吧,函数A被混淆过后的代码里出现了几十个函数B,但是只有一个函数B是真正被调用了的,我是想通过frida动态的获取到这个真正被调用的地址是哪一个
windy_ll 2020-4-10 20:04
31
0
Hambur 但是这个好像只是获取函数的注册地址。我举个例子吧,函数A被混淆过后的代码里出现了几十个函数B,但是只有一个函数B是真正被调用了的,我是想通过frida动态的获取到这个真正被调用的地址是哪一个
混淆过后的我没研究过,建议你看一下无名侠大佬的利用unicorn反混淆
Hambur 2020-4-10 20:12
32
0
windy_ll 混淆过后的我没研究过,建议你看一下无名侠大佬的利用unicorn反混淆
好的 谢谢
mb_pdqozeux 2020-4-11 15:06
33
0
我想请教下,如果有一个java方法,而这个函数他里面的一个参数是传入了一个类,并且这个类里面没有其余别的成员方法(除构造方法),成员变量是public的,如果我想通过frida HOOK这个java方法,来获得里面的参数类 如何打印他的成员变量呢? 我试了类型转换,都不能正常打印  求教
windy_ll 2020-4-11 22:25
34
0
mb_pdqozeux 我想请教下,如果有一个java方法,而这个函数他里面的一个参数是传入了一个类,并且这个类里面没有其余别的成员方法(除构造方法),成员变量是public的,如果我想通过frida HOOK这个java方 ...
试一下这个喃console.log(arguments[类参数位置].成员变量名);
最后于 2020-4-11 22:27 被windy_ll编辑 ,原因:
Hambur 2020-4-11 23:23
35
0
windy_ll 混淆过后的我没研究过,建议你看一下无名侠大佬的利用unicorn反混淆
找到解决方法了
onEnter: function (args) {
               console.log(this.returnAddress);
    },
windy_ll 2020-4-12 09:07
36
0
Hambur 找到解决方法了 onEnter: function (args) { console.log(this.returnAddress); },
mb_gdaapnut 2020-5-15 21:04
37
0
谢谢大佬
muyile 2020-5-21 14:31
38
0
非常感谢!
游客
登录 | 注册 方可回帖
返回