标签 Frida 下的文章

Frida 脱壳与Hook笔记

环境搭建

安装Frida

pip3 install frida
pip3 install frida-tools

模拟器配置

我的使用环境在Mac上,Mac上的模拟器都不太稳定,所以我选择在Parallels Desktop虚拟机中安装了一台WIndows,并在Windows中安装了夜神模拟器.
夜神模拟器默认的adb端口是62001,但是只绑定在了127.0.0.1上

我们借助一款小工具tcpmapping,把62001转发到0.0.0.0上,方便我们用物理机或其他虚拟机进行连接。

然后就可以通过链接虚拟机的内网IP进行连接了

运行Frida-server

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

应用

脱壳

现在很多安卓的壳都是动态加载dex文件进行执行的,如果正常解压apk得到的dex一般只是一个加载器,但是最终一定会加载真实的dex文件,我们只需要Hook系统打开Dex文件的函数,最后将dex文件导出即可。

在Android 4 上是通过libdvm.so中的dvmDexFileOpenPartial函数实现打开dex文件的。
Android > 5 以上时通过libdvm.so中的OpenCommon函数进行操作的。
当然你实际把这俩个so文件拖到ida里面去搜函数名字,出来的函数名称是很长的,例如

_ZN3art7DexFile10OpenCommonEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_PNS0_12VerifyResultE

这里直接使用一个别人开发好的脚本进行脱壳就可以了,有兴趣的可以仔细看一下脚本,还是很值得学习的。
使用方法:

frida -l dexDump.js -f com.sskj.com -U --no-pause

最后导出的dex文件会在这个目录下:

"/data/data/" + processName + "/" + dex_size + ".dex";

https://github.com/GuoQiang1993/Frida-Apk-Unpack

Hook指定API

我这里用老版本的[微博绿洲 1.4.2]进行实验,因为没有加壳直接使用jadx就可以查看源码了,先找到了Java实现的签名算法的调用类

package com.weibo.xvideo.util;

import android.content.Context;
import com.tencent.open.SocialOperation;

/* compiled from: SignatureUtil.kt */
public final class SignatureUtil {
    public static final SignatureUtil a = new SignatureUtil();

    static {
        System.loadLibrary(SocialOperation.GAME_SIGNATURE);
    }

    public final native String signature(Context context, String str, boolean z);
}

发现其是通过加载so文件实现签名的,其实按照正常破解加密算法的操作,应该去逆向so文件,然后重写加密算法,但是既然用了frida就不需要那么复杂,几行hook就可以实现了。

setTimeout(function() {
    Java.perform(function() {

        var HttpRequestEntity = Java.use('com.weibo.xvideo.util.SignatureUtil'); //要hook的类名完整路径
        // var func = new HttpRequestEntity();
        // func.signature('1', '13123', false);
        HttpRequestEntity.signature.implementation = function(arg1, arg2, arg3) { // 重写要hook的方法getSign,当有多个重名函数时需要重载,function括号为函数的参数个数

            var Sign = this.signature(arg1, arg2, arg3); //调用原始的函数实现并且获得返回值,如果不写的话我们下面的代码会全部替换原函数

            send("arg1:" + arg1); //打印参数值
            send("arg2:" + arg2);
            send("arg3:" + arg3);

            send("result:" + Sign); //打印返回值
            return Sign; //函数有返回值时需要返回
        };
        var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
        var context = currentApplication.getApplicationContext();
        var func = HttpRequestEntity.$new()
        send("runit:" + func.signature(context, '13123', false));
    })

});

然后执行

frida -U -l hook.js com.sina.oasis

然后在app中随便操作一些需要使用到签名函数的功能,将会看到下列输出

可以发现已经hook到了签名函数的参数和返回值,下面我们就是需要把我们自己的参数传递进去让他进行签名,然后获取签名值了。
写一个python脚本,frida支持消息的传递,我们通过写一个recv监听事件,然后在通过frida的接口发送我们需要签名的内容去触发这个事件,然后在通过frida主动去调用这个签名类并取出返回值

import frida, sys
from urllib import parse
#接受message信息,可以把签名提交到其他功能上
def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

jscode = """
function sign(args) {
    Java.perform(function () {
        send('send:'+args)
        var HttpRequestEntity = Java.use('com.weibo.xvideo.util.SignatureUtil');
        var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
        var context = currentApplication.getApplicationContext();
        var func = HttpRequestEntity.$new()
        var result = func.signature(context, args, false)
        send("sign:" + result);
        return result;
    })
}
#接受到信号就调用签名函数
recv('sign', function onMessage(pokeMessage) { sign(pokeMessage['line']); });

"""
#处理参数 按照key的字母进行排序
dicts='source_cid=0&source_uid=0&ua=OPPO-OPPO%20R11_oasis_1.4.2_Android_5.1.1&sid=4441027085236588&cuid=7341062735&device_id=5959f99a7cb193c2c053ee4977ab46fc&timestamp=1574308247514&version=1.4.2&channel=ViVo&comment=1111&noncestr=425gbJ1zuDWh15618n5ThvN685QR95&platform=ANDROID'
dicts=dicts.split('&')
dicts_info={}
dicts_info_result={}
for d in dicts:
    d=d.split('=')
    dicts_info.update({d[0]:d[1]})

keys=sorted(dicts_info)
for k in keys:
    dicts_info_result.update({k:dicts_info[k]})
line='&'
for k,v in dicts_info_result.items():
    line+=k+'='+v+'&'
    
line=line[0:-1]
line=parse.unquote_plus(line)
print(line)
print('--------------------------------')
process = frida.get_usb_device().attach('com.sina.oasis')
print(123123)
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
#发送信号
script.post({"type": "sign",'line':line})
sys.stdin.read()