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;
}
})