Frida技术:App逆向开发屠龙刀

2,969 阅读2分钟

Frida是一种基于JavaScript的动态分析工具,可以用于逆向开发、应用程序的安全测试、反欺诈技术等领域。Frida主要用于在已安装的应用程序上运行自己的JavaScript代码,从而进行动态分析、调试、修改等操作,能够绕过应用程序的安全措施,可以助力于对应用程序进行逆向分析。

Frida不需要依赖于任何特殊的工具或设备,只需要在目标设备上安装Frida服务器,就可以使用Frida客户端与之通信。

一、环境搭建

1.1 安装Frida客户端

首先,在Windows、macOS、Linux平台上使用以下命令安装Frida客户端,安装的命令如下:

pip install frida
pip install frida-tools

如果没有安装python环境,请先安装python环境后再运行上面的命令,安装完成之后,可以使用下面的命令来检查是否安装成功。

frida --version

还可以使用pip list命令来列出所有安装的模块,如下图。

image.png

1.2 frida-server服务端

对于Android、iOS平台上,可以直接从Frida官方网站下载相应的安装包。下载frida-server服务端,下载地址:github.com/frida/frida…。注意frida版本要与frida-server版本一致,比如我本地安装的16.0.19。

如果不知道自己手机CPU的版本和型号,可以使用下面的命令查看:

adb shell 
getprop ro.product.cpu.abi #输出:arm64-v8a

那么我们只需要找到相应的服务器server:frida-core-devkit-16.0.19-android-arm64.tar.xz下载即可。

image.png

1.3 在安卓设备上安装frida-server服务端

将上面下载的压缩包解压,改名为frida-server,然后推送到手机中,添加执行权限,启动服务操作。

# push进手机
adb push frida-core-devkit-16.0.19-android-arm64.tar.xz /data/local/tmp


# 端口转发
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043


# 运行frida
adb shell 
walleye:/ $ su
walleye:/ # cd /data/local/tmp
walleye:/ # chmod 777 frida-core-devkit-16.0.19-android-arm64.tar.xz
walleye:/ # ./frida-core-devkit-16.0.19-android-arm64.tar.xz
walleye:/ #

执行完上面的操作后,可以新打开一个新的命令行窗口,然后执行frida-ps -U命令查看是否能显示模拟器上的安卓进程。

image.png

注意:如果出现失败可能就是frida-server版本出问题了,还有需要root权限,建议使用夜神模拟器。

二、基本使用

2.1 示例

下面是一段测试的示例,我Hook的是com.xxx.test.ec.util.Md5Util这个方法,这个需要根据app的实际方法来,我是通过jadx反编译后复制出来的类名。

image.png

下面是Hook的代码:

# -*- coding: utf-8 -*-
# 导入 frida, sys 库
import frida, sys
#md5--hook-
jscode = """
Java.perform(function(){
  <!-- 获取 MD5Util 这个类的对象 -->
  var utils = Java.use("com.xxx.test.ec.util.Md5Util");
  <!-- 调用 ToMd5 这个,这个方法的参数有1个 -->
    utils.md5.overload('java.lang.String').implementation = function(a){
    send("Hook Start...");
    send("传入参数: " +a);
    <!-- 获取 ToMD5 这个方法执行的结果,并打印到控制台上 -->
        var res= this.md5(a);
    send("ToMD5 方法返回值: " +res);
    return res;
  }
});
"""
# 这下面的代码的作用就是为了执行上面 JS 代码,输出 JS 中 send() 方法中的内容
def message(message, data):
  if message["type"] == 'send':
    print("[*] {0}".format(message['payload']))
  else:
    print(message)
# 获取这个 APK 的进程
#process = frida.get_remote_device().attach('com.xxx.test:pushcore')
#process = frida.get_usb_device().attach(3281)
process = frida.get_remote_device().attach('这里填目标app的名字')
# 加载 JS 代码
script = process.create_script(jscode)
script.on("message", message)
print('[*] Running CTF')
script.load()
sys.stdin.read()

2.2 其他操作

Hook函数

使用JavaScript代码Hook应用程序中的某个函数,示例代码:

Interceptor.attach(Module.findExportByName("libexample.so", "example_func"), {
  onEnter: function(args) {
    console.log("example_func enter");
},
  onLeave: function(retval) {
    console.log("example_func leave");
}
});

该代码将Hook应用程序中名为example_func的函数,当进入函数时,会打印"example_func enter",当离开函数时,会打印"example_func leave"。

 

内存读取

使用JavaScript代码读取目标进程中的内存,示例代码:

var addr = Module.findExportByName("libexample.so", "example_data");
var data = Memory.readByteArray(addr, 0x100);
console.log(hexdump(data));

该代码读取了名为example_data的变量,并将其打印到控制台上。

 

其他操作

劫持Java的函数调用,使用示例:

Java.perform(function() {
    var MainActivity = Java.use('com.example.MainActivity');
    MainActivity.onCreate.implementation = function(savedInstanceState) {
        console.log("[*] onCreate hooked");
        this.onCreate(savedInstanceState);
    };
​
    var TextView = Java.use('android.widget.TextView');
    TextView.setText.implementation = function(text) {
        console.log("[*] setText hooked");
        this.setText(text);
    };
});

模拟按钮点击事件,使用示例:

var button = Java.use('android.widget.Button');
var view = Java.cast(button.$new(), Java.use('android.view.View'));
Java.perform(function() {
    view.performClick();
});

绕过SSL Pinning,使用示例:

var SSLPinning = Java.use('com.example.SSLPinning');
SSLPinning.execute.overload('javax.net.ssl.SSLSocketFactory', 'java.lang.String', 'int').implementation = function(socketFactory, hostname, port) {
    console.log("[*] SSLPinning.execute(" + socketFactory + ", " + hostname + ", " + port + ")");
    var allowAllHostnameVerifier = Java.use('javax.net.ssl.HttpsURLConnection').getDefaultHostnameVerifier();
    var nullarray = Java.array('java.lang.Object', [null]);
    allowAllHostnameVerifier.verify(hostname, socketFactory.createSocket(hostname, port).getSession());
    return true;
};

以上是Frida逆向开发的基本操作代码示例,使用Frida可以进行更多复杂的逆向操作和应用程序开发,更多的详情还是参考:frida.re/docs/home/