常用frida hook

frida hook

1、hook system.loadLibrary调用的so文件

function HookApp(str){
    Java.perform(function () {
        var Class = Java.use('com.App.name');
        var Method = "getString2";
        var result = Class[Method](str);
        console.log(result);
    })
}
setImmediate(HookApp);

2、hook 函数入参和结果

function hook_Method() {
    Java.perform(function () {
        var Class = Java.use('com.App.name');
        var Method = "a";
        # overload参数类型和个数
        Class[Method].overload('android.content.Context', 'java.lang.String'
        ).implementation = function () {
            var result = this[Method]['apply'](this, arguments);
            console.log('----------------------');
            console.log('arg1:' + arguments[0]);
            console.log('arg2:' + arguments[1]);
            console.log('result:' + result);
            console.log('----------------------');
            return result;
        }
    })
}
setImmediate(hook_Method);

3、hook类的构造函数

function main() {
    Java.perform(function () {
        Java.use("com.App.name").$init.overload("java.lang.String", "java.lang.String").implementation =
            function (args1, args2) {
                // 调用原函数
                var result = this.$init(args1, args2);
                // 打印参数
                console.log("args1, args12: ", args1, args2);
                return result;
            };
    });
}

setImmediate(main);

4、hook修改类/实例参数

function main() {
    Java.perform(function () {
        Java.choose("com.App.name", {
            onMatch: function (instance) {
                console.log("instance.mProjectId", instance.args1.value);
                console.log("instance.mUrlScheme", instance.args2.value);
                instance.args1.value = "args1";
                instance.args2.value = "args2";
            },
            onComplete: function () {
            },
        });
    });
}

setImmediate(main);

5、构造数组

function main() {
    Java.perform(function () {
        // 构造byte数组
        var byteArray = Java.array("byte", [0x46, 0x72, 0x69, 0x64, 0x61]);
        // 输出为: Frida
        console.log(Java.use("java.lang.String").$new(byteArray));

        // 构造char数组
        var charArray = Java.array("char", ["F", "r", "i", "d", "a"]);
        console.log(Java.use("java.lang.String").$new(charArray));
    });
}

setImmediate(main);

6、静态函数主动调用

function main() {
    Java.perform(function () {
        // setWebContentsDebuggingEnabled 需要在主线程调用
        Java.scheduleOnMainThread(function () {
            console.log("isMainThread", Java.isMainThread());
            // 主动触发静态函数调用, 允许WebView调试
            Java.use("android.webkit.WebView").setWebContentsDebuggingEnabled(true);
        });
    });
}

setImmediate(main);

7、动态函数主动调用

function main() {
    Java.perform(function () {
        Java.choose("com.App.name", {
            onMatch: function (instance) {
                // 主动触发动态函数调用
                console.log("instance.isDebugEnabled: ", instance.isDebugEnabled());
                console.log("instance.getChannel: ", instance.getChannel());
            },
            onComplete: function () {
            },
        });
    });
}

8、定义一个类

function main() {
    Java.perform(function () {
        var TestRunnable = Java.registerClass({
            name: "com.example.TestRunnable",
            // 实现接口
            implements: [Java.use("java.lang.Runnable")],
            // 成员变量
            fields: {
                testFields: "java.lang.String",
            },
            methods: {
                // 构造函数
                $init: [
                    {
                        returnType: "void",
                        argumentTypes: ["java.lang.String"],
                        implementation: function (testFields) {
                            // 调用父类构造函数
                            this.$super.$init();
                            // 给成员变量赋值
                            this.testFields.value = testFields;
                            console.log("$init: ", this.testFields.value);
                        },
                    },
                ],
                // 方法
                run: [
                    {
                        returnType: "void",
                        implementation: function () {
                            console.log(
                                "testFields: ",
                                this.testFields.value
                            );
                        },
                    },
                ],
            },
        });

        TestRunnable.$new("simple test").run();
    });
}

setImmediate(main);

9、打印函数调用堆栈

function main() {
    Java.perform(function () {
        Java.use(
            "com.growingio.android.sdk.autotrack.click.ViewClickInjector"
        ).viewOnClick.overload(
            "android.view.View$OnClickListener",
            "android.view.View"
        ).implementation = function (listener, view) {
            // 打印当前调用堆栈信息
            console.log(
                Java.use("android.util.Log").getStackTraceString(
                    Java.use("java.lang.Throwable").$new()
                )
            );
            return this.viewOnClick(listener, view);
        };
    });
}

setImmediate(main);

10、枚举classLoader

function main() {
    Java.perform(function () {
        Java.enumerateClassLoaders({
            onMatch: function (loader) {
                try {
                    // 判断该loader中是否存在我们需要hook的类
                    if (loader.findClass("com.growingio.android.sdk.CoreConfiguration")) {
                        console.log("found loader:", loader);
                        Java.classFactory.loader = loader;
                    }
                } catch (error) {
                    console.log("found error: ", error);
                    console.log("failed loader: ", loader);
                }
            },
            onComplete: function () {
                console.log("enum completed!");
            },
        });
        console.log(
            Java.use("com.growingio.android.sdk.CoreConfiguration").$className
        );
    });
}

setImmediate(main);

11、枚举类

function main() {
    Java.perform(function () {
        Java.enumerateLoadedClasses({
            onMatch: function (name, handle) {
                // 判断是否是我们要查找的类
                if (name.toString() == "com.growingio.android.sdk.CoreConfiguration") {
                    console.log("name, handle", name, handle);
                    Java.use(name).isDebugEnabled.implementation = function () {
                        return true;
                    };
                }
            },
            onComplete: function () {
            },
        });
    });
}

setImmediate(main);

12、hook so 文件函数 - 导出符号

var native_func = Module.findExportByName("${so file name}", "${so export function name}");

Interceptor.attach(native_func, {
    // 函数开始
    onEnter: function (args) {},
    // 函数结束
    onLeave: function (return_val) {}
});

13、hook so 文件函数 - 偏移地址

var native_func_addr = Module.findBaseAddress("${so file name}");
var native_addr = native_func_addr.add(${so 函数偏移地址});

Interceptor.attach(native_addr, {
    // 函数开始
    onEnter: function (args) {
        // 读取 r0 的数据
        this.context.r0.readCString()
    },
    // 函数结束
    onLeave: function (return_val) {}
});

14、获取 android context

var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();

15、调用 JNIEnv 函数

// 获取 JNIEnv
var env = Java.vm.getEnv();
var jstring = env.newStringUtf('maple');

16、byte array to string

// 方法一
var JavaString = Java.use("java.lang.String");
JavaString.$new('byte array').toString();

// 方法二
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
// 转成 hex 在转 string
ByteString.of('byte array').hex();

17、java 类型转换

// 使用 Java.cast 把 java byet 转成 java Object
var javaBytes = Java.use('java.lang.String').$new("aaaaa").getBytes();
var javaBytesClass = Java.cast(javaBytes, Java.use('java.lang.Object')).getClass();

// 使用 Java.array 把 js array 转成 java Object array
var params = [
    Java.use('java.lang.String').$new('str1'),
    Java.use('java.lang.String').$new('str2'),
    Java.use('java.lang.Boolean').$new(false),
    Java.use('java.lang.Integer').$new(0)
];
var ps = Java.array('Ljava.lang.Object;', params);

18、注册 java 类

Java.openClassFile('/data/local/tmp/androidAsync-2.2.1.dex').load();
const HttpServerRequestCallback = Java.use('com.koushikdutta.async.http.server.HttpServerRequestCallback');

// 构建一个默认请求
const RequestTestCallback = Java.registerClass({
    name: "RequestTestCallback",
    implements: [HttpServerRequestCallback],
    methods: {
        onRequest: function (request, response) {
            // 主动调用代码直接写这里
            response.send(JSON.stringify({
                "code": 0,
                "message": " 服务已经注册成功, 默认端口8181"
            }));
        }
    }
});

19、hook 动态 dex 切换 classLoader

// 方法一
var className = 'com.taobao.wireless.security.adapter.JNICLibrary';
Java.enumerateClassLoaders({
    onMatch: function (loader) {
        try {
            if (loader.findClass(className)) {
                Java.classFactory.loader = loader;
            }
        } catch (error) {}
    },
    onComplete: function () {}
})

// 方法二
var classApplication = Java.use('android.app.Application');
classApplication.onCreate.implementation = function () {
    Java.enumerateClassLoadersSync().forEach(function (loader) {
        try {
            if (loader.loadClass(className)) {
                Java.classFactory.loader = loader;
            }
        } catch (error) {}
    });
}

20、map 转 js json

function getMapData(mapSet) {
    try {
        var result = {};
        var key_set = mapSet.keySet();
        var it = key_set.iterator();
        while (it.hasNext()) {
            var key_str = it.next().toString();
            result[key_str] = mapSet.get(key_str).toString();
        }
        return result
    } catch (error) {
        return mapSet
    }
}

21、bytes to hex

function bytes2Hex(arrBytes){
    var str = "";
    for (var i = 0; i < arrBytes.length; i++) {
        var tmp;
        var num = arrBytes[i];
        if (num < 0) {
            //此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
            tmp = (255 + num + 1).toString(16);
        } else {
            tmp = num.toString(16);
        }
        if (tmp.length == 1) {
            tmp = "0" + tmp;
        }
        if(i>0){
            str += " "+tmp;
        }else{
            str += tmp;
        }
    }
    return str;
}

22、string to bytes

function string2Bytes(str) {
    var bytes = new Array();
    var len, c;
    len = str.length;
    for(var i = 0; i < len; i++) {
        c = str.charCodeAt(i);
        if(c >= 0x010000 && c <= 0x10FFFF) {
            bytes.push(((c >> 18) & 0x07) | 0xF0);
            bytes.push(((c >> 12) & 0x3F) | 0x80);
            bytes.push(((c >> 6) & 0x3F) | 0x80);
            bytes.push((c & 0x3F) | 0x80);
        } else if(c >= 0x000800 && c <= 0x00FFFF) {
            bytes.push(((c >> 12) & 0x0F) | 0xE0);
            bytes.push(((c >> 6) & 0x3F) | 0x80);
            bytes.push((c & 0x3F) | 0x80);
        } else if(c >= 0x000080 && c <= 0x0007FF) {
            bytes.push(((c >> 6) & 0x1F) | 0xC0);
            bytes.push((c & 0x3F) | 0x80);
        } else {
            bytes.push(c & 0xFF);
        }
    }
    return bytes;
}

23、bytes to string

function bytes2String(arr) {
    if(typeof arr === 'string') {
        return arr;
    }
    var str = '',
        _arr = arr;
    for(var i = 0; i < _arr.length; i++) {
        var one = _arr[i].toString(2),
            v = one.match(/^1+?(?=0)/);
        if(v && one.length == 8) {
            var bytesLength = v[0].length;
            var store = _arr[i].toString(2).slice(7 - bytesLength);
            for(var st = 1; st < bytesLength; st++) {
                store += _arr[st + i].toString(2).slice(2);
            }
            try {
                str += String.fromCharCode(parseInt(store, 2));
            } catch (error) {
                str += parseInt(store, 2).toString(); 
                console.log(error);
            }
            i += bytesLength - 1;
        } else {
            try {
                str += String.fromCharCode(_arr[i]);
            } catch (error) {
                str += parseInt(store, 2).toString(); 
                console.log(error);
            }
        }
    }
    return str;
}

24、bytes to base64

function hookLoggerAllMethod() {
    const cls = Java.use('com.virjar.sekiro.business.api.log.SekiroLogger');
    const mhd_array = cls.class.getDeclaredMethods();

    // hook 类所有方法 (所有重载方法也要hook)
    for (var i = 0; i < mhd_array.length; i++) {
        // 当前方法签名
        const mhd_cur = mhd_array[i];
        // 当前方法名
        const str_mhd_name = mhd_cur.getName();

        // 当前方法重载方法的个数
        const n_overload_cnt = cls[str_mhd_name].overloads.length;
        console.log('n_overload_cnt: ', n_overload_cnt);

        for (var index = 0; index < n_overload_cnt; index++) {
            cls[str_mhd_name].overloads[index].implementation = function () {
                // 参数个数
                var n_arg_cnt = arguments.length;
                for (var idx_arg = 0; idx_arg < n_arg_cnt; n_arg_cnt++) {
                    console.log(arguments[idx_arg]);
                }
                console.log(str_mhd_name + ' --- ' + n_arg_cnt);
                return this[str_mhd_name].apply(this, arguments);
            }
        }
    }
}

25、hook class 的所有方法

function hookLoggerAllMethod() {
    const cls = Java.use('com.virjar.sekiro.business.api.log.SekiroLogger');
    const mhd_array = cls.class.getDeclaredMethods();

    // hook 类所有方法 (所有重载方法也要hook)
    for (var i = 0; i < mhd_array.length; i++) {
        // 当前方法签名
        const mhd_cur = mhd_array[i];
        // 当前方法名
        const str_mhd_name = mhd_cur.getName();

        // 当前方法重载方法的个数
        const n_overload_cnt = cls[str_mhd_name].overloads.length;
        console.log('n_overload_cnt: ', n_overload_cnt);

        for (var index = 0; index < n_overload_cnt; index++) {
            cls[str_mhd_name].overloads[index].implementation = function () {
                // 参数个数
                var n_arg_cnt = arguments.length;
                for (var idx_arg = 0; idx_arg < n_arg_cnt; n_arg_cnt++) {
                    console.log(arguments[idx_arg]);
                }
                console.log(str_mhd_name + ' --- ' + n_arg_cnt);
                return this[str_mhd_name].apply(this, arguments);
            }
        }
    }
}

26、js sleep

function sleep(numberMillis) {
    var now = new Date();
    var exitTime = now.getTime() + numberMillis;
    while (true) {
        now = new Date();
        if (now.getTime() > exitTime)
            return;
    }
}

// 毫秒级别
sleep(1000)

27、hook 常见加密算法

Java.perform(function () {
    var ByteString = Java.use("com.android.okhttp.okio.ByteString");
    var MessageDigest = Java.use('java.security.MessageDigest');
    var StringClass = Java.use("java.lang.String");

    // hook md5
    md.getInstance.overload("java.lang.String").implementation = function (str) {
        return this.getInstance(str)
    }
    md.update.overload('[B').implementation = function (a) {
        var result = this.update(a);
        return result;
    }
    md.digest.overload().implementation = function () {
        var result = this.digest();
        return result;
    }

    // hook aes
    var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    var iv = Java.use("javax.crypto.spec.IvParameterSpec");
    var cipher = Java.use("javax.crypto.Cipher");
    // hook aes key
    secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) {
        console.log("secretKeySpec.$init - UTF8密钥: " + StringClass.$new(a));
        console.log("secretKeySpec.$init - hex密钥 : " + ByteString.of(a).hex());
        console.log("secretKeySpec.$init - 算法类型: " + b);
        var result = this.$init(a, b);
        return result;
    }
    // hook aes 偏移
    iv.$init.overload('[B').implementation = function (a) {
        console.log("IvParameterSpec.$init - 向量偏移量utf8: ", StringClass.$new(a));
        console.log("IvParameterSpec.$init - 向量偏移量hex: ", bytes2Hex(a))
        var result = this.$init(a);
        return result;
    }
    // hook aes 加密模式
    cipher.getInstance.overload('java.lang.String').implementation = function (a) {
        console.log("cipher.getInstance - 加密模式、填充类型: ", a)
        var result = this.getInstance(a);
        return result;
    }
    cipher.doFinal.overload('[B').implementation = function (a) {
        console.log("Cipher.doFinal - 待加密字符串: ", bytes2Hex(a))
        var result = this.doFinal(a);
        console.log("Cipher.doFinal - 加密后字符串: ", bytes2Hex(result))
        return result;
    }
})
posted @ 2022-12-01 15:01  Maple_feng  阅读(1695)  评论(0编辑  收藏  举报