文章目录[隐藏]
java to frida 类型
java type |
frida type |
---|---|
int |
int |
float |
float |
boolean |
boolean |
string |
java.lang.String |
byte |
[B |
frida 构建不同类型的 array
java type |
so type |
---|---|
int |
I |
float |
F |
boolean |
Z |
string |
java.lang.String |
byte |
B |
long |
J |
short |
S |
double |
D |
char |
C |
在Frida中用 [ 表示数组。
例如是 int 类型的数组,写法为:[I
如果是 String 类型的数组,则写法为: [java.lang.String;
注意:后面还有个 ; 号
frida 常用工具对应版本
frida-server= 12.9.4
frida=12.11.18
frida-tools=7.2.2
jnitrace=3.0.1
objection=1.8.4
frida-server=14.2.18
frida=14.2.18
frida-tools=9.2.5
jnitrace=3.2.2
objection=1.11.0
frida-server=15.0.18
frida=15.0.18
frida-tools=10.2.1
jnitrace=3.2.2
objection=1.11.0
frida hook
hook 构造函数
JavaClass.$init.implementation = function () {}
hook 实例化
JavaClass.$new.implementation = function () {}
hook 普通函数
JavaClass.funcA.implementation = function () {}
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) {}
});
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) {}
});
frida call
创建 new 一个实例对象
var javaString = Java.use("java.lang.String");
javaString.$new();
调用静态函数
var javaString = Java.use("java.lang.String");
javaString.valueOf(1);
调用实例函数
var javaString = Java.use("java.lang.String");
javaString.$new().toString();
frida 常用操作
frida 获取 android context
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
frida 调用 JNIEnv 函数
// 获取 JNIEnv
var env = Java.vm.getEnv();
var jstring = env.newStringUtf('xiaojianbang');
frida 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();
frida 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);
frida 注册 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"
}));
}
}
});
frida 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) {}
});
}
frida 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
}
}
frida 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;
}
frida 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;
}
frida 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;
}
frida bytes to base64
function bytes2Base64(e) {
var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var r, a, c, h, o, t;
for (c = e.length, a = 0, r = ''; a < c;) {
if (h = 255 & e[a++], a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4),
r += '==';
break
}
if (o = e[a++], a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2),
r += '=';
break
}
t = e[a++],
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
r += base64EncodeChars.charAt(63 & t)
}
return r
}
frida 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);
}
}
}
}
frida 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)
frida 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;
}
})
最后
目前就搜集到这么多,后面会持续更新