Skip to content

frida hook笔记

中途启动hook

import sys
import frida

with open('hook_newSign_v1.js', 'r',encoding='utf-8') as f:
    jsHookCode = f.read()

# 中途启动
redv = frida.get_remote_device()
s = redv.attach('酷安')
script = s.create_script(jsHookCode)
script.load()
sys.stdin.read()

# 或
# device = frida.get_usb_device(-1)
# process = device.attach('酷安')
# script = process.create_script(jsHookCode)
# # script.on('message', )
# print(f'[*] running')
# script.load()
# device.resume(pid)
# sys.stdin.read()

hook APP重启阶段

spawn方式重启APP, hook APP的启动阶段

import sys
import frida

with open('hook_newSign_v1.js', 'r',encoding='utf-8') as f:
    jsHookCode = f.read()

device = frida.get_usb_device(-1)
pid = device.spawn(['com.coolapk.market'])
process = device.attach(pid)
script = process.create_script(jsHookCode)
# script.on('message', )
print(f'[*] running')

script.load()
device.resume(pid)
sys.stdin.read()

访问外部类的成员变量

this.this$0.value.变量名.value = 1

使用js脚本调用方法

frida -U com.app.app -l js.js
重启
frida -U -f com.app.app -l js.js --no-pause

apktool

# d decode 解压
java -jar apktool.jar d name.apk
# b bulid 重新构建 
java -jar apktool.jar b name.apk

androidkiller

打印调用堆栈

function print_stack() {
    // java打印堆栈
    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
    // SO打印堆栈
    console.log('called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
}

调用方式

Java.perform(function () {
    var num = 1

    let AuthUtils = Java.use("com.coolapk.market.util.AuthUtils");
    AuthUtils["getAS"].implementation = function (context, str) {
        console.log(num, 'getAS is called' + ', ' + 'context: ' + context + ', ' + 'str: ' + str);
        let ret = this.getAS(context, str);
        console.log(num, 'getAS ret value is ' + ret);
        print_stack();
        num += 1;
        return ret;
    };
    function print_stack() {
        // java打印堆栈
        console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        // SO打印堆栈
        console.log('called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
    }
})

反射调用

obj.getClass().getMethod("方法名字符串",new Class[0]).invoke(obj, new Object[0]);

context.getClass().getMethod("getPackageManager", new Class[0]).invoke(context, new Context[0]);

参考文档

java.lang.Class class类

安卓开发API官网

安卓开发API手册(中文) matools.com/api/android

java API

新建实例化对象

var a = Java.use("com.coolapk.market.util.AuthUtils").$new();

打印object

1 确定object类型, console.log(p.$className) 查看p的数据类型

2 Java.cast() 强制转换p的对应类型

3 调用该类的输出方法, 通常都有一个toString()方法

使用js的json类

尝试console.log(JSON.stringify(p))

可能打印不出来字符串, 一般能打印p的字节数组(可以用数据和真实数据进行对比)

bytes array是object

String 和 bytes array可以相互转化

new String(bytes) bytes转换为字符串. 本身如果是不可打印的字符串, 打印是乱码

hook so

Java.perform(function () {
    var so = Module.getExportByName('liba.so', 'Java_com_coolapk_market_util_AuthUtils_getAS');
    Interceptor.attach(so, {
        onEnter: function (args) {
            console.log(`3: ${args[3]}`)
        },
        onLeave: function (retval) {

        }
    })

})

hook jni

hook类的所有方法

Java.perform(function () {
    hook_class('com.coolapk.market.AppConfig');
});


function hook_class(className) {
    var myClass = Java.use(className);
    var methods = myClass.class.getDeclaredMethods();
    methods.forEach(function (method) {
        var methodName = method.getName();
        // 获取所有重载
        var overloads = myClass[methodName].overloads;
        overloads.forEach(function (overload) {
            overload.implementation = function () {
                // arguments js自带的形参的集合
                // argumentsType 自带的形参类型的集合
                for (let i = 0; i < arguments.length; i++) {
                    console.log(`[${methodName}] > ${i} > [${overload.argumentTypes[i].className}] : > ${arguments[i]}`);
                }
                var ret = this[methodName].apply(this, arguments);
                console.log(`[${methodName}] return: ${ret}`);
                return ret;
            }
        });
        // console.log(methodName);
    })
}

打印调用堆栈这些

Java.perform(function () {
    hook_class('com.coolapk.market.AppConfig');
});


function hook_class(className) {


    var myClass = Java.use(className);
    var methods = myClass.class.getDeclaredMethods();
    methods.forEach(function (method) {
        var methodName = method.getName();
        // 获取所有重载
        var overloads = myClass[methodName].overloads;
        overloads.forEach(function (overload) {
            overload.implementation = function () {
                console.log(`[${methodName}] 调用堆栈: `)
                print_stack()

                // arguments js自带的形参的集合
                // argumentsType 自带的形参类型的集合
                for (let i = 0; i < arguments.length; i++) {
                    // 打印堆栈
                    console.log(`[${methodName}] > ${i} > [${overload.argumentTypes[i].className}] : > ${arguments[i]}`);
                    // JSON.stringify()打印一次
                    console.log(`[${methodName}] > ${i} > [${overload.argumentTypes[i].className}] : > ${JSON.stringify(arguments[i])}`);
                }
                var ret = this[methodName].apply(this, arguments);
                console.log(`[${methodName}] return: ${ret}`);
                return ret;
            }
        });
        // console.log(methodName);
    })

    function print_stack() {
        // java打印堆栈
        console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        // SO打印堆栈
        // console.log('called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
    }
}

jadx有代码, 但hook不到类

可能动态加载dex, 类可能在另外的classloader中, 需切换到classloader再hook

classloader

Java.enmerateClassLoaders()

通用加解密hook

搜: frida hook所有加密类

打印jsstring

let v3 = Java.vm.getEnv().getStringUtfChars(args[3], null).readCString()
console.log(3,v3);