npm安装
npm i @types/frida-gum
python安装
pip install frida
pip install frida-tools
执行一段简单的js代码
function main(){
Java.perform(()=>{
console.log("成功运行")
})
}
setImmediate(main)执行命令
frida -U -f 包名 -l 脚本.js其中-U是指定设置,-f是以spawn模式启动,也可以
frida -H 192.168.0.100:14725 -F -l 脚本.js
-F是以attach模式启动,-H是连接这个地址,frida-server也需要改掉相关的端口
frida-server -l 0.0.0.0:14725
基本上用的最多的运行命令就是这些
如果不是app,可以这样子
attach
frida -U -p 3173 -l your_script.js
spawn
frida -U -f /path/to/your/executable -l your_script.js
//由于有些请求头会使用这个添加,可能通过okhttp直接增加
var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function (a, b) {
console.log("hashMap.put: ", a, b);
return this.put(a, b);
}
// java.util.concurrent.ConcurrentHashMap
var ConcurrentHashMap = Java.use("java.util.concurrent.ConcurrentHashMap");
ConcurrentHashMap.put.implementation = function (a, b) {
console.log("ConcurrentHashMap.put: ", a, b);
return this.put(a, b);
}
// java.util.LinkedHashMap
var LinkedHashMapClass = Java.use("java.util.LinkedHashMap");
LinkedHashMapClass.put.implementation = function (key, value) {
console.log("LinkedHashMap key:", key, "value:", value);
return this.put(key, value);
};// hook java.net.URL
var URL = Java.use('java.net.URL');
URL.$init.overload('java.lang.String').implementation = function (a) {
console.log('java.net.URL ' + a)
this.$init(a)
}
//hook okhttp3 HttpUrl
var Builder = Java.use('okhttp3.Request$Builder');
Builder.url.overload('okhttp3.HttpUrl').implementation = function (a) {
var res = this.url(a);
console.log("okhttp3.HttpUrl result: " + res)
return res;
}var Builder = Java.use("okhttp3.Request$Builder");
Builder["addHeader"].implementation = function (str, str2) {
console.log("okhttp3.Request$Builder.addHeader key: " + str)
console.log("okhttp3.Request$Builder.addHeader val: " + str2)
var result = this["addHeader"](str, str2);
console.log("okhttp3.Request$Builder.addHeader result: " + result);
return result;
};var log = Java.use("android.util.Log");
log.w.overload('java.lang.String', 'java.lang.String').implementation = function (tag, message) {
console.log("log.w: ", tag, message);
return this.w(tag, message);
}一般来说json都会使用gson这个库
// JSON处理
var jSONObject = Java.use("org.json.JSONObject");
jSONObject.put.overload('java.lang.String', 'java.lang.Object').implementation = function (a, b) {
console.log("jSONObject.put: ", a, b);
return this.put(a, b);
}
jSONObject.getString.implementation = function (a) {
console.log("jSONObject.getString: ", a);
var result = this.getString(a);
console.log("jSONObject.getString result: ", result);
return result;
}
JSONObject['optString'].overload('java.lang.String').implementation = function (str) {
if(str === "data"){
console.log('str', str)
getStackTraceString();
}
let result = this['optString'](str);
return result;
};// 弹窗关键类
var toast = Java.use("android.widget.Toast");
toast.show.implementation = function () {
console.log("toast.show: ");
return this.show();
}var base64 = Java.use("android.util.Base64");
base64.encodeToString.overload('[B', 'int').implementation = function (a, b) {
console.log("base64.encodeToString: ", JSON.stringify(a));
var result = this.encodeToString(a, b);
console.log("base64.encodeToString result: ", result)
return result;
}sp是存储数据的一个类似于哈希表的一个东西,可以存储数据,提取数据,存储类似于字符串等相关数据
// hook内部存储api,打印出存储的数据
var sp = Java.use("android.app.SharedPreferencesImpl$EditorImpl");
sp.putBoolean.overload('java.lang.String', 'boolean').implementation = function(arg1,arg2){
console.log("[SharedPreferencesImpl ] putBoolean -> key: "+arg1+" = "+arg2);
return this.putBoolean(arg1,arg2);
}
sp.putString.overload('java.lang.String', 'java.lang.String').implementation = function(arg1,arg2){
console.log("[SharedPreferencesImpl] putString -> key: "+arg1+" = "+arg2);
return this.putString(arg1,arg2);
}
sp.putInt.overload('java.lang.String', 'int').implementation = function(arg1,arg2){
console.log("[SharedPreferencesImpl] putInt -> key: "+arg1+" = "+arg2);
return this.putInt(arg1,arg2);
}
sp.putFloat.overload('java.lang.String', 'float').implementation = function(arg1,arg2){
console.log("[SharedPreferencesImpl] putFloat -> key: "+arg1+" = "+arg2);
return this.putFloat(arg1,arg2);
}
sp.putLong.overload('java.lang.String', 'long').implementation = function(arg1,arg2){
console.log("[SharedPreferencesImpl] putLong -> key: "+arg1+" = "+arg2);
return this.putLong(arg1,arg2);
}
// hook应用程序间数据传递的api,打印出传递数据的uri与具体的字段
var content = Java.use("android.content.ContentResolver");
content.insert.overload("android.net.Uri","android.content.ContentValues").implementation = function(arg1,arg2){
console.log("[ContentResolver] *insert -> Uri: "+arg1+" Values: "+arg2);
return this.insert(arg1,arg2);
}
content.delete.overload("android.net.Uri","java.lang.String","[Ljava.lang.String;").implementation = function(arg1,arg2,arg3){
console.log("[ContentResolver] *delete -> Uri: "+arg1+"\n -> arg2: "+arg2+"\n -> arg3: "+arg3);
return this.delete(arg1,arg2,arg3);
}
content.update.overload('android.net.Uri','android.content.ContentValues','java.lang.String','[Ljava.lang.String;').implementation = function(arg1,arg2,arg3,arg4){
console.log("[ContentResolver] *update -> Uri: "+arg1+"\n -> arg2: "+arg2+"\n -> arg3: "+arg3+"\n -> arg4: "+arg4);
return this.update(arg1,arg2,arg3,arg4);
}
content.query.overload('android.net.Uri', '[Ljava.lang.String;', 'android.os.Bundle', 'android.os.CancellationSignal').implementation = function(arg1,arg2,arg3,arg4){
console.log("[ContentResolver] *query -> Uri: "+arg1+"\n -> arg2: "+arg2+"\n -> arg3: "+arg3+"\n -> arg4: "+arg4);
return this.query(arg1,arg2,arg3,arg4);
}
content.query.overload('android.net.Uri', '[Ljava.lang.String;', 'java.lang.String', '[Ljava.lang.String;', 'java.lang.String').implementation = function(arg1,arg2,arg3,arg4,arg5){
console.log("[ContentResolver] *query -> Uri: "+arg1+"\n -> arg2: "+arg2+"\n -> arg3: "+arg3+"\n -> arg4: "+arg4+"\n -> arg5: "+arg5);
return this.query(arg1,arg2,arg3,arg4,arg5);
}
content.query.overload('android.net.Uri', '[Ljava.lang.String;', 'java.lang.String', '[Ljava.lang.String;', 'java.lang.String', 'android.os.CancellationSignal').implementation = function(arg1,arg2,arg3,arg4,arg5,arg6){
console.log("[ContentResolver] *query -> Uri: "+arg1+"\n -> arg2: "+arg2+"\n -> arg3: "+arg3+"\n -> arg4: "+arg4+"\n -> arg5: "+arg5+"\n arg6: "+arg6);
return this.query(arg1,arg2,arg3,arg4,arg5,arg6);
}function WebView() {
let WebView = Java.use("android.webkit.WebView");
WebView["postUrl"].implementation = function (str, bArr) {
var string = java.use('java.lang.String').$new(bArr);
console.log(`WebView.postUrl is called: str=${str}, string=${string}`);
this["postUrl"](str, bArr);
};
WebView["loadUrl"].overload('java.lang.String').implementation = function (str) {
console.log(`WebView.loadUrl is called: str=${str}`);
var s = Java.use('java.lang.String').$new(str);
var t = Java.use('java.lang.String').$new("https");
if (s.contains(t)) {
getStackTraceString();
}
this["loadUrl"](str);
};
WebView["loadUrl"].overload('java.lang.String', 'java.util.Map').implementation = function (str, map) {
console.log(`WebView.loadUrl 2is called: str=${str}, map=${map}`);
this["loadUrl"](str, map);
};
}加壳之后的hook需要使用classLoader,每一个加载的dex都对应有一个classLoader,然后它们之间互相之间的函数调用,也需要使用到对方的classLoader才可以,没有办法直接使用,我们hook也需要使用到这些,因为这样子才能hook到这个java函数的具体地址,然后变成一个native函数再来进行hook
Java.enumerateClassLoadersSync().forEach(classLoader => {
try {
if (classLoader.loadClass("ot2.b")) {
Java.classFactory.loader = classLoader;
console.log(classLoader)
let C82252b = Java.use("ot2.b");
C82252b["getBdOz"].implementation = function (context) {
console.log(`C82252b.getBdOz is called: context=${context}`);
let result = this["getBdOz"](context);
console.log(`C82252b.getBdOz result=${result}`);
return result;
};
}
} catch (e) {
// console.log(e)
}
})// 获取 Java 类的引用
var ExampleClass = Java.use('com.example.ExampleClass');
// 使用 new 关键字来创建 ExampleClass 的一个实例
var exampleObject = ExampleClass.$new();
// 使用对象进行一些操作,例如调用其方法
exampleObject.someMethod();
// 强制转换,obj转String
var castValue = Java.cast(obj, Java.use("java.lang.String"))
// 获取对象属性内容,得到的这个对象可以使用.value
// 也可以直接执行函数,是a.函数,而不是a.value.函数
obj.a.valueJava.perform(function () {
console.log("");
console.log("[.] Cert Pinning Bypass/Re-Pinning");
var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
var FileInputStream = Java.use("java.io.FileInputStream");
var BufferedInputStream = Java.use("java.io.BufferedInputStream");
var X509Certificate = Java.use("java.security.cert.X509Certificate");
var KeyStore = Java.use("java.security.KeyStore");
var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
var SSLContext = Java.use("javax.net.ssl.SSLContext");
// Load CAs from an InputStream
console.log("[+] Loading our CA...")
var cf = CertificateFactory.getInstance("X.509");
// 这里需要ca证书
try {
console.log("加载ca 文件")
var fileInputStream = FileInputStream.$new("/data/local/tmp/cer/desktop.cer");
} catch (err) {
console.log("[o] " + err);
return
}
var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
var ca = cf.generateCertificate(bufferedInputStream);
bufferedInputStream.close();
var certInfo = Java.cast(ca, X509Certificate);
console.log("[o] Our CA Info: " + certInfo.getSubjectDN());
// Create a KeyStore containing our trusted CAs
console.log("[+] Creating a KeyStore for our CA...");
var keyStoreType = KeyStore.getDefaultType();
var keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
console.log("[+] Our TrustManager is ready...");
console.log("[+] Hijacking SSLContext methods now...")
console.log("[-] Waiting for the app to invoke SSLContext.init()...")
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function (a, b, c) {
console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
console.log("[+] SSLContext initialized with our custom TrustManager!");
}
});(r0capture的)
if (Java.available) {
Java.perform(function () {
function storeP12(pri, p7, p12Path, p12Password) {
var X509Certificate = Java.use("java.security.cert.X509Certificate")
var p7X509 = Java.cast(p7, X509Certificate);
var chain = Java.array("java.security.cert.X509Certificate", [p7X509])
var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC");
ks.load(null, null);
ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain);
try {
var out = Java.use("java.io.FileOutputStream").$new(p12Path);
ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray())
} catch (exp) {
console.log(exp)
}
}
//在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue
Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () {
var result = this.getPrivateKey()
var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
// /data/data/com.paokeji.yiqu/files/cer
storeP12(this.getPrivateKey(), this.getCertificate(), '/data/data/' + packageName + "/files/" + "base" + '.p12', 'r0ysue');
console.log("dumpClinetCertificate=>" + '/data/data/' + packageName + "/files/" + "base" + '.p12' + ' pwd: r0ysue');
// console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
return result;
}
Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () {
var result = this.getCertificateChain()
var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
storeP12(this.getPrivateKey(), this.getCertificate(), '/data/data/' + packageName + "/files/base.p12", 'r0ysue');
console.log("dumpClinetCertificate=>" + '/data/data/' + packageName + "/files/base.p12" + ' pwd: r0ysue');
return result;
}
console.log("开始hook")
})
}function dumpAllFieldValue(obj) {
if (obj === null) {
return;
}
console.log("Dump all fields value for " + obj.getClass() + " :");
var cls = obj.getClass();
while (cls !== null && !cls.equals(Java.use("java.lang.Object").class)) {
var fields = cls.getDeclaredFields();
if (fields === null || fields.length === 0) {
cls = cls.getSuperclass();
continue;
}
if (!cls.equals(obj.getClass())) {
console.log("Dump super class " + cls.getName() + " fields:");
}
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
field.setAccessible(true);
var name = field.getName();
var value = field.get(obj);
var type = field.getType();
console.log(type + " " + name + "=" + value);
}
cls = cls.getSuperclass();
}
}function utf8Bytes(exampleBytes) {
let StringClass = Java.use("java.lang.String");
let CharsetClass = Java.use("java.nio.charset.Charset");
let utf8Charset = CharsetClass.forName("UTF-8");
return StringClass.$new(exampleBytes, utf8Charset);
}function maptoJson(map){
var Gson = Java.use('com.google.gson.Gson').$new();
Gson.toJsonTree(map).getAsJsonObject();
}function getStackTraceString() {
console.log(Java.use('android.util.Log')
.getStackTraceString(Java.use('java.lang.Throwable')
.$new()));
}function hook_NewStringUTF() {
var artModule = Process.findModuleByName("libart.so");
var symbols = artModule.enumerateSymbols();
var newStringUTF = null;
for (let i = 0; i < symbols.length; i++) {
let symbol = symbols[i];
if (symbol.name.indexOf("NewStringUTF") !== -1) {
console.log(symbol.name);
newStringUTF = symbol.address;
}
}
if (newStringUTF) {
Interceptor.attach(newStringUTF, {
onEnter: function (args) {
let string = args[1].readCString()
console.log("[字符串]:", string);
console.log("[调用栈]:",Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');
}, onLeave: function (retval) {
}
})
} else {
console.log("没有这个函数")
}
}这个函数是用来查询具体函数地址的,参数为handle(so文件基地址),函数名称,返回为这个函数的地址
function hook_dlsym(){
// 获取dlsym函数的地址
let dlsymAddr = Module.findExportByName("libdl.so","dlsym");
console.log(dlsymAddr);
// hook dlsym
Interceptor.attach(dlsymAddr,{
onEnter:function(args){
this.args1 = args[1];
},
onLeave:function(retval){
let md= Process.findModuleByAddress(retval);
if(md==null)return;
console.log("函数:"+this.args1.readCString(),"模块:"+md.name,"地址:"+retval,"偏移:"+retval.sub(md.base));
}
})
}我们静态注册的函数,native层和java层联系起来的函数,就会经过这个,这个代码也可以被用来查找相关的静态注册的代码。
第一个
function inline_hook(_module) {
}
//8.0以下所有的so加载都通过dlopen
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
onEnter: function (args) {
this.call_hook = false;
this.modulePath = ptr(args[0]).readCString();
console.log("[dlopen] call dlopen:", this.modulePath);
// if ( this.modulePath.indexOf("libxx.so") >= 0) {
// console.log("dlopen:", ptr(args[0]).readCString());
// this.call_hook = true;//dlopen函数找到了
// }
}, onLeave: function (retval) {
if (this.call_hook) {//dlopen函数找到了就hook so
let targetModule = Process.findModuleByName(this.modulePath);
inline_hook(targetModule);
}
}
});
// 高版本Android系统使用android_dlopen_ext
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
this.modulePath = ptr(pathptr).readCString();
console.log("[dlopen] call android_dlopen_ext:", this.modulePath);
// if ( this.modulePath.indexOf("libxxx.so") !== -1) {
// this.BookReader4Android = true
// }
}
},
onLeave: function (retval) {
if (this.BookReader4Android) {
let targetModule = Process.findModuleByName(this.modulePath);
inline_hook(targetModule);
}
}
});
}
setImmediate(hook_dlopen);第二个
function hook_dlopen(so_name) {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log(path)
if (path.indexOf(so_name) !== -1) {
this.match = true
}
}
},
onLeave: function (retval) {
if (this.match) {
console.log(so_name, "加载成功")
}
}
});
}var open_addr = Module.findExportByName(null, "open");
Interceptor.attach(open_addr, {
onEnter: function (args) {
const filename = args[0].readCString()
console.log(filename)
},
onLeave: function (retval){
if (this.maps_file){
retval.replace(0)
}
}
})但是基本上没什么用,以为在so里面,可以通过系统调用,即svc 0这种命令,具体不清楚,但是也可以关闭当前进程
function hook_exit() {
var exit = Module.findExportByName(null, "exit")
console.log("exit", exit)
null !== exit && Interceptor.attach(exit,
{
onEnter: function (args) {
var args1 = args[0];
if (args1 !== undefined && args1 != null) {
var status = args1.readInt();
console.log("exit " + status);
}
}
}
);
}
function hook_kill() {
const killAddr = Module.findExportByName(null, 'kill');
console.log("kill", killAddr)
null !== killAddr && Interceptor.attach(killAddr, {
onEnter: function (args) {
console.log('kill called with pid: ' + args[0].toInt32() + ', signal: ' + args[1].toInt32());
// 在这里可以修改 args 来改变行为
},
onLeave: function (retval) {
console.log('kill returned: ' + retval.toInt32());
// 在这里可以修改返回值
}
});
}一般来说这个函数是frida检测的时候就创建一个线程,具体hook如下
function hook_pthread_create() {
var pthread_create_addr = Module.findExportByName(null, "pthread_create");
Interceptor.attach(pthread_create_addr, {
onEnter(args) {
var parg2 = args[2]
var parg3 = args[3]
var so_name = Process.findModuleByAddress(parg2).name;
const baseAddr = Module.findBaseAddress(so_name)
console.log("pthread_create", so_name, parg3.toString(16), parg2.sub(baseAddr).toString(16))
}
})
}// Frida script to hook pthread_start
function hook__pthread_start() {
// 查找 pthread_start 函数地址
var __pthread_start = DebugSymbol.fromName("_ZL15__pthread_startPv").address;
console.log(__pthread_start)
__pthread_start && Interceptor.attach(__pthread_start, {
onEnter: function (args) {
var addr = args[0].add(12 * 8).readPointer();
var so_name = Process.findModuleByAddress(addr).name;
var so_base = Module.getBaseAddress(so_name);
console.log(so_name, so_base, addr.sub(so_base))
}
});
}
hook__pthread_start()function waitForModule(moduleName) {
return new Promise((resolve, reject) => {
const checkInterval = setInterval(() => {
const baseAddr = Module.findBaseAddress(moduleName);
if (baseAddr !== null) {
clearInterval(checkInterval);
resolve(baseAddr);
}
}, 1000); // 检查频率为每秒一次
});
}对于一些在.init_xxx执行的代码,是在dlopen运行中执行的代码,就必须要找到在最开始使用的系统函数中找到系统调用然后进行hook,类似于这个是在open的某个函数下hook的
var open_addr = Module.findExportByName(null, "open");
Interceptor.attach(open_addr, {
onEnter: function (args) {
const filename = args[0].readCString()
if (filename.indexOf("maps") !== -1) {
console.log(filename)
console.log('RegisterNatives called from:\n' + Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');
// Thread.sleep(40)
this.maps_file = true
}
if (!is_run) {
locate_init()
}
is_run = true
},
onLeave: function (retval){
if (this.maps_file){
retval.replace(0)
}
}
})动态注册的函数
function hook_RegisterNatives() {
var RegisterNatives_addr = null;
var symbols = Process.findModuleByName("libart.so").enumerateSymbols();
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i].name;
if ((symbol.indexOf("CheckJNI") == -1) && (symbol.indexOf("JNI") >= 0)) {
if (symbol.indexOf("RegisterNatives") >= 0) {
RegisterNatives_addr = symbols[i].address;
console.log("RegisterNatives_addr: ", RegisterNatives_addr);
}
}
}
Interceptor.attach(RegisterNatives_addr, {
onEnter: function (args) {
var env = args[0];
var jclass = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(jclass);
var methods_ptr = ptr(args[2]);
var method_count = args[3].toInt32();
console.log("RegisterNatives method counts: ", method_count);
for (var i = 0; i < method_count; i++) {
var name = methods_ptr.add(i * Process.pointerSize * 3).readPointer().readCString();
var sig = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString();
var fnPtr_ptr = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
var find_module = Process.findModuleByAddress(fnPtr_ptr);
console.log("类: ", class_name, "方法: ", name, "签名: ", sig, "函数地址: ", fnPtr_ptr, "模块名: ", find_module.name, "函数偏移: ", ptr(fnPtr_ptr).sub(find_module.base));
}
},
onLeave: function (retval) {}
});
}
hook_RegisterNatives() Interceptor.attach(Module.findExportByName("libc.so", "memcpy"), {
onEnter: function (args) {
this.dst = args[0];
this.src = args[1];
this.size = args[2].toInt32();
},
onLeave: function (retval) {
// ARM64指令是4字节
if (this.size === 4) {
// 检查地址是否落在代码段
if (textRange && (this.src.compare(textRange.base) >= 0 && this.src.compare(textRange.base.add(textRange.size)) < 0)) {
console.log("可能是ARM64指令的memcpy:");
console.log("复制地址: " + this.dst);
console.log("源地址: " + this.src);
console.log("复制位数大小: " + this.size);
console.log("字节打印:")
console.log(hexdump(this.dst, { offset: 0, length: this.size }));
// 可以根据常见的ARM64指令模式进一步确认,比如分析前几个bit
var instruction = Memory.readU32(this.dst);
console.log("Instruction (hex): " + instruction.toString(16));
}
}
}
});const l = Module.getExportByName("libc.so", "pthread_create")
// 地址对齐,其实默认就是对齐的
const i = l.and(-2)
// 如果这个是成功的
const r = Interceptor.attach(Module.getExportByName("libc.so", "memcmp"), {
onEnter: function (e) {
this.match = 0 === e[0].compare(i) || 0 === e[1].compare(i)
}, onLeave: function (e) {
// 销毁掉
this.match && (e.replace(new NativePointer(0)), r.detach())
}
});strstr
function hook_strstr() {
var strstr = Module.findExportByName(null, "strstr");
console.log("strstr", strstr)
null !== strstr && Interceptor.attach(strstr, {
onEnter: function (e) {
var haystack = e[0].readCString();
var needle = e[1].readCString();
// 上面两个都是字符串
}, onLeave: function (e) {
// 返回值0就是没有检测到
}
})
}strcmp
function hook_strcmp() {
Interceptor.attach(Module.findExportByName(null, 'strcmp'), {
onEnter: function (args) {
// 获取 strcmp 的参数(两个字符串)
var str1 = args[0].readUtf8String();
var str2 = args[1].readUtf8String();
// 输出调用 strcmp 时传入的两个字符串
console.log('strcmp called with:');
console.log(' str1: ' + str1);
console.log(' str2: ' + str2);
},
onLeave: function (retval) {
// 输出 strcmp 返回值
console.log('strcmp returned: ' + retval.toInt32());
}
});
}hook这个call_constructors,这个call_constructors就是用来执行so初始化函数的,它会去执行so的初始化函数
在dlopen开始所执行的位置。
function hook_init_array() {
let linker = null;
if (Process.pointerSize === 4) {
linker = Process.findModuleByName("linker");
} else {
linker = Process.findModuleByName("linker64");
}
let call_constructors_addr, get_soname
let symbols = linker.enumerateSymbols();
for (let index = 0; index < symbols.length; index++) {
let symbol = symbols[index];
if (symbol.name === "__dl__ZN6soinfo17call_constructorsEv") {
call_constructors_addr = symbol.address;
} else if (symbol.name === "__dl__ZNK6soinfo10get_sonameEv") {
get_soname = new NativeFunction(symbol.address, "pointer", ["pointer"]);
}
}
console.log(call_constructors_addr)
Interceptor.attach(call_constructors_addr, {
onEnter: function (args) {
let soinfo = args[0];
let soname = get_soname(soinfo).readCString();
// 此时还没有执行完成初始化函数
// 这个时候就可以加载so了,可以在这个位置nop掉其他的东西
},
onLeave: function (retval) {
if (this.match) {
console.log("libDexHelper.so", "加载成功")
const module = Process.findModuleByName("libDexHelper.so");
// 执行结束初始化函数了,接下来会跑dlopen
}
}
});
}function antifrida() {
var strstr = Module.findExportByName(null, "strstr");
console.log("strstr", strstr)
null !== strstr && Interceptor.attach(strstr, {
onEnter: function (e) {
this.frida = Boolean(0), this.haystack = e[0], this.needle = e[1],
null !== this.haystack.readCString() && null !== this.needle.readCString() &&
(-1 === this.haystack.readCString().indexOf("frida") &&
-1 === this.needle.readCString().indexOf("frida") &&
-1 === this.haystack.readCString().indexOf("gum-js-loop") &&
-1 === this.needle.readCString().indexOf("gum-js-loop") &&
-1 === this.haystack.readCString().indexOf("gmain") &&
-1 === this.needle.readCString().indexOf("gmain") &&
-1 === this.haystack.readCString().indexOf("linjector") &&
-1 === this.needle.readCString().indexOf("linjector") ||
(this.frida = Boolean(1)))
}, onLeave: function (e) {
if (this.frida) {
if (this.haystack.readCString().indexOf(this.needle.readCString()) !== -1) {
// console.log("[haystack] =>", this.haystack.readCString())
// console.log("[needle] =>", this.needle.readCString())
}
e.replace(0);
}
}
})
}但是对于一些不使用strstr或者使用其他的就没有办法进行检测了
function hook_native(){
var modules = Process.enumerateModules();
for (var i in modules){
var module = modules[i];
console.log(module.name);
console.log(module.size);
console.log(module.base);
console.log(module.path);
}
}const hooks = Module.load('/data/app/com.example.hookdemo-xMdClW3H4V5El06BIcGN0A==/lib/arm64/libhookdemo.so');
var Imports = hooks.enumerateImports();
for(var i = 0; i < Imports.length; i++) {
//函数类型
console.log("type:",Imports[i].type);
//函数名称
console.log("name:",Imports[i].name);
//属于的模块
console.log("module:",Imports[i].module);
//函数地址
console.log("address:",Imports[i].address);
}// 创建一个字符串
var r = Memory.allocUtf8String("muyang");
console.log(Memory.readUtf8String(r))
// 创建一个r内存
var newaddress = Memory.alloc(10);
// 赋值过去
Memory.copy(newaddress,r,10);
console.log(hexdump(newaddress));
console.log(hexdump(r,{
offset:0,
length:10,
header:true,
ansi:true
}))
var arr = [ 0x6d,0x75,0x79,0x61,0x6e,0x67];
//申请一个新的内存空间 返回指针 大小是arr.length
const r = Memory.alloc(arr.length);
//将arr数组写入R地址中
Memory.writeByteArray(r,arr);
//输出
console.log(hexdump(r, {
offset: 0,
length: arr.length,
header: true,
ansi: false
}));
var buffer = Memory.readByteArray(r,arr.length);
console.log(buffer)具体的内存操作可以让gpt写
function create_pthread_create() {
const pthread_create_addr = Module.findExportByName(null, "pthread_create")
const pthread_create = new NativeFunction(pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]);
return new NativeCallback((parg0, parg1, parg2, parg3) => {
const module = Process.findModuleByAddress(parg2);
const so_name = module.name;
const baseAddr = module.base
//console.log("pthread_create", so_name, "0x" + parg2.sub(baseAddr).toString(16), "0x" + parg3.toString(16))
if(so_name === TARGET_SO_NAME)
{
// 找出來那些thread_create是在TARGET_SO_NAME中建立的
console.log("pthread_create", so_name, "0x" + parg2.sub(baseAddr).toString(16), "0x" + parg3.toString(16))
// 一個一個阻止建立看看哪些是檢查frida的
// pthread_create libpbb_qr_biz_release.so 0x5599b0 0x0
// pthread_create libpbb_qr_biz_release.so 0x3ac820 0xb40000712cece450
// pthread_create libpbb_qr_biz_release.so 0x1c831c 0xb40000713cf2cfc0
// pthread_create libpbb_qr_biz_release.so 0x19bf50 0xb40000712cecca10
// pthread_create libpbb_qr_biz_release.so 0x3999fc 0xb40000712cef0ff0
// pthread_create libpbb_qr_biz_release.so 0x3999fc 0xb40000712cf07d10
//return 0;
}
// 成功的返回值是0
return pthread_create(parg0, parg1, parg2, parg3)
}, "int", ["pointer", "pointer", "pointer", "pointer"])
}
//libpbb_qr_biz_release.so 0x7039c29000 0x5599b0
function replace_thread() {
var new_pthread_create = create_pthread_create()
var pthread_create_addr = Module.findExportByName(null, "pthread_create")
// 函数替换
Interceptor.replace(pthread_create_addr, new_pthread_create);
}
replace_thread();console.log('RegisterNatives called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
console.log('RegisterNatives called from:\n' + Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');
Thread.currentContext();
Memory.protect(ptr(地址), 4, 'rwx');
// 使用 Arm64Writer 写入 'ret' 指令
var writer = new Arm64Writer(parg2);
writer.putRet();
writer.flush();
writer.dispose();
console.log("ret success");
// 写入nop指令
var writer = new Arm64Writer(parg2);
writer.putNop(); // 写入 nop 指令
writer.flush(); // 刷新写入到内存
console.log("ret success");frida-trace | Frida • A world-class dynamic instrumentation toolkit
会在handlers目录下生成关于libc.so全部的hook函数
frida-trace -U -f com.lucky.luckyclient -a "libc.so!"获取全部已经加载的so文件
Process.enumerateModules()
获取当前线程的id
Process.getCurrentThreadId();
查找模块
Process.findModuleByName("libc.so")
[MI 6::com.chehejia.oc.m01 ]-> Process.findModuleByName("libc.so")
{
"base": "0x7b38cc8000",
"name": "libc.so",
"path": "/system/lib64/libc.so",
"size": 1163264
}
获取模块基础地址
const baseAddress = Process.getBaseAddress("libc.so")
baseAddress.add(0x111)
...
16进制地址转换为frida指针
ptr("0x111")
找到堆地址
var heap = Process.getHeap();
var startAddress = heap.start; // 假设我们从堆的起始地址开始
var size = heap.size;
var endAddress = startAddress.add(size);Frida的Memory对象是用于访问和操作进程内存的核心组件。以下是Memory对象常用的方法和属性的详细解释:
-
Memory.readByteArray(address, size): 从指定地址读取字节数组。var data = Memory.readByteArray(ptr("0x12345678"), 16);
-
Memory.readUtf8String(address): 从指定地址读取UTF-8字符串。var str = Memory.readUtf8String(ptr("0x12345678"));
-
Memory.writeByteArray(address, data): 将字节数组写入指定地址。Memory.writeByteArray(ptr("0x12345678"), new Uint8Array([0x01, 0x02, 0x03]));
-
Memory.writeUtf8String(address, string): 将UTF-8字符串写入指定地址。Memory.writeUtf8String(ptr("0x12345678"), "Hello, world!");
-
Memory.protect(address, size, permissions): 设置指定地址范围的内存权限。常用的权限包括'r'(只读)、'w'(可写)和'x'(可执行)。Memory.protect(ptr("0x12345678"), 4096, 'rwx');
-
Memory.alloc(size): 分配指定大小的内存并返回起始地址。var ptr = Memory.alloc(1024); // 分配1KB
-
Memory.allocUtf8String(string): 分配内存并存储指定的UTF-8字符串。var ptr = Memory.allocUtf8String("Hello, Frida!");
-
Memory.scan(address, size, pattern, options): 在指定地址范围内搜索特定模式。 扩展使用,支持使用正则表达式搜索字节模式。// 进行内存搜索 // frida的hex const searchValue = "66 72 69 64 61" const startAddress = ptr(0x7583c0c000) const endAddress = ptr(0x7583d1e000) Memory.scan(startAddress, endAddress.sub(startAddress).toInt32(), searchValue, { onMatch: function(address, size) { console.log("找到数据!地址:" + address); // 你可以在这里修改数据,例如: }, onComplete: function() { console.log("搜索完成。"); } }); // 特殊匹配手法 Memory.scan(ptr("0x10000000"), 0x10000, "00 01 02 03 ?? ?? 06 07 08 09 ?? ?? ?? ?? 0e 0f", { onMatch: function(address) { console.log("找到数据!地址:" + address); }, onComplete: function() { console.log("搜索完成。"); } });
-
Memory.readPointer(address): 从指定地址读取指针值。var ptrValue = Memory.readPointer(ptr("0x12345678"));
-
Memory.writePointer(address, value): 将指针值写入指定地址。Memory.writePointer(ptr("0x12345678"), ptr("0x87654321"));
-
Memory.readInt(address): 从指定地址读取32位整数。var intValue = Memory.readInt(ptr("0x12345678"));
-
Memory.writeInt(address, value): 将32位整数写入指定地址。Memory.writeInt(ptr("0x12345678"), 42);
-
Memory.readFloat(address): 读取32位浮点数。var floatValue = Memory.readFloat(ptr("0x12345678"));
-
Memory.writeFloat(address, value): 写入32位浮点数。Memory.writeFloat(ptr("0x12345678"), 3.14);
-
Memory.readStruct(address, layout): 读取一个结构体,结构体布局由一个对象定义。var layout = { field1: 'int', field2: 'pointer' }; var struct = Memory.readStruct(ptr("0x12345678"), layout);
-
Memory.writeStruct(address, layout, struct): 将结构体写入指定地址。Memory.writeStruct(ptr("0x12345678"), layout, { field1: 100, field2: ptr("0x87654321") });
Memory.free(ptr): 释放之前分配的内存。注意并不是所有的分配都可以手动释放,特别是如果使用了系统的分配方法。
Memory.protect(address, size, permissions): 允许动态更改内存权限,这对于监控和调试非常有用。
Memory.patchCode(parg2, 4, function (code) {
var retBytes = new Uint8Array([0xC0, 0x03, 0x5F, 0xD6]);
code.writeByteArray(retBytes);
});Memory.patchCode(address, 4, function (code) {
// 创建一个字节数组,包含 NOP 指令的机器码
code.writeByteArray([0x1F, 0x20, 0x03, 0xD5]); // 将字节数组写入目标内存
});const address = module.base.add(0xE8E60);
// 使用 Memory.readU64 读取 64 位数据
const value = Memory.readU64(address);
// 打印模块和相关符号
const func_module = Process.findModuleByAddress(ptr(value))
console.log(func_module.name)
console.log(func_module.base)
const symbol = DebugSymbol.fromAddress(ptr(value));
console.log(symbol.name)function fridaReplace(startAddress, endAddress) {
const searchValue = "6c 69 62 66 72 69 64 61 2d 61 67 65 6e 74"; // "libfrida-agent" 的十六进制表示
let matchedAddresses = []; // 用于存储匹配的地址
Memory.scan(startAddress, endAddress.sub(startAddress).toInt32(), searchValue, {
onMatch: function (address, size) {
console.log("找到数据!地址:" + address);
matchedAddresses.push(address); // 将匹配的地址存储到数组中
},
onComplete: function () {
console.log("搜索完成,共找到 " + matchedAddresses.length + " 个匹配项。");
// 遍历所有匹配的地址并进行修改
matchedAddresses.forEach(function (address) {
try {
// 计算所在的页地址,并将权限设置为可写
const size = searchValue.split(" ").length
Memory.protect(address, size, 'rwx');
// 修改 "frida" 为 "wzdnb"
const replacement = "6c 69 62 77 7a 64 6e 62 2d 61 67 65 6e 74"; // "libwzdnb-agent" 的十六进制表示
const bytes = replacement.split(" ").map(byte => parseInt(byte, 16));
Memory.writeByteArray(address, bytes);
console.log("已将 'frida' 替换为 'wzdnb',地址:" + address);
// 恢复原始内存权限(可根据需要设置合适的权限)
Memory.protect(address, size, 'r--');
} catch (error) {
// console.error("修改地址 " + address + " 时出错:" + error);
}
});
}
});
}
function readFile(fileName) {
console.log("> Reading file: ", fileName);
const JString = Java.use("java.lang.String");
const Files = Java.use("java.nio.file.Files");
const Paths = Java.use("java.nio.file.Paths");
const URI = Java.use("java.net.URI");
const pathName = "file://" + fileName;
const path = Paths.get(URI.create(pathName));
const fileBytes = Files.readAllBytes(path);
return JString.$new(fileBytes);
}
function replaceFrida() {
const data = readFile("/proc/self/maps");
// 马上去除全部hook
Interceptor.detachAll();
// 解析出来我们想要的数据位置
let num = 0;
let startAddress, endAddress;
data.split("\n").forEach(line => {
if (line.indexOf("/memfd") !== -1) {
console.log(line);
const match = line.match(/^([0-9a-f]+)-([0-9a-f]+)/);
if (num === 0) {
startAddress = ptr(parseInt(match[1], 16));
endAddress = ptr(parseInt(match[2], 16));
}
num += 1;
}
})
if (startAddress && endAddress) {
fridaReplace(startAddress, endAddress);
console.log("检测成功");
}
}
replaceFrida();原理把原本的汇编代码复制过来,在每一行汇编代码下面膨胀代码,使得代码变大了很多,已实现回调函数。
在一开始的时候终止线程运行,然后通过pc知道下一个命令在哪获取到第一个基本块,创建到一个内存区域里面去,把原本的代码修改为跳转过去,执行内存块的代码,执行之后在块的结尾,也就是b或者bl之前,跳转到stalker创建的块里面去,然后在里面拿到跳转的位置再创建一个新的代码块,判断需要跳转到哪个代码块里面去。
使用stalker之后相关的块基本上bl都不是执行的原本的bl了,而是我们stalker修改了之后的bl,跳转到了stalker的相关位置去,而不是原本的直接执行了。
基础使用,打印每一个块的汇编代码,方便知道哪些块被调用了,被调用的顺序。
Interceptor.attach(JNI_OnLoad, {
onEnter: function (args) {
var curTid = Process.getCurrentThreadId();
Stalker.follow(curTid, {
transform: function (iterator) {
let instruction = iterator.next();
const baseFirstAddress = instruction.address;
const isModuleCode = baseFirstAddress.compare(startBase) >= 0 &&
baseFirstAddress.compare(startBase.add(size)) <= 0;
const module = Process.findModuleByAddress(baseFirstAddress);
if (isModuleCode) {
if (module) {
const name = module.name;
const offset = baseFirstAddress.sub(module.base);
const base = module.base;
console.log(`[transform] start: ${baseFirstAddress} name:${name} offset: ${offset} base: ${base}`);
} else {
console.log(`[transform] start: ${baseFirstAddress}`);
}
}
do {
const curRealAddr = instruction.address;
const curOffset = curRealAddr.sub(baseFirstAddress);
const curOffsetInt = curOffset.toInt32()
const instructionStr = instruction.toString()
if (isModuleCode){
console.log("\t" + curRealAddr + " <+" + curOffsetInt + ">: " + instructionStr);
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
if (isModuleCode){
console.log()
}
}
})
},
onLeave: function (retval) {
console.log("结束");
Stalker.unfollow();
Stalker.garbageCollect();
}
}
)测试是否会卡死的代码
const module = Process.findModuleByName("libDexHelper.so");
Interceptor.attach(module.base.add(0x4b614), {
onEnter: function (args) {
var curTid = Process.getCurrentThreadId();
Stalker.follow(curTid, {
// 直接创建block块,什么都不做
transform: function (iterator) {
while (true){
if (iterator.next() === null){
break;
}
iterator.keep();
}
}
})
},
onLeave: function (retval) {
console.log("结束");
Stalker.unfollow();
Stalker.garbageCollect();
}
}
)如果log有问题的情况下,使用缓存的方式
class LogBuffer {
constructor() {
this.queue = [];
this.lock = false;
}
// 添加日志信息
push(message) {
this.queue.push(message);
}
// 获取并清空日志信息
flush() {
const messages = this.queue.slice();
this.queue = [];
return messages;
}
// 检查队列是否有内容
hasLogs() {
return this.queue.length > 0;
}
}
// 创建一个日志缓冲区实例
const logBuffer = new LogBuffer();如果出现js变量问题,可以采用如下方式
transform: function (iterator) {
let curOffset = 0;
while (true) {
const instruction = iterator.next();
if (instruction === null) {
break;
}
const address = instruction.address;
const isModuleCode = address.compare(base) >= 0 &&
address.compare(base.add(size)) <= 0;
if (isModuleCode) {
const curRealAddr = instruction.address;
const curOffsetInt = curOffset;
const instructionStr = instruction.toString();
if (curOffset) {
logBuffer.push("\t" + curRealAddr + " <+" + curOffsetInt + ">: " + instructionStr);
} else {
const offset = curRealAddr.sub(base);
const name = "libDexHelper.so";
logBuffer.push(`[transform] start: ${curRealAddr} name:${name} offset: ${offset} base: ${base}`);
logBuffer.push("\t" + curRealAddr + " <+" + curOffsetInt + ">: " + instructionStr);
}
curOffset += 4;
}
iterator.keep();
}
if (curOffset) {
logBuffer.push()
}
}排除这个区域
Stalker.exclude({base,size});停止对某个基本块的跟踪
Stalker.unfollow();
清理 Stalker 使用的内存和资源
Stalker.garbageCollect();强制 Stalker立即清空这个缓冲区
Stalker.flush()
事件回调
const cm = new CModule(`
#include <gum/gumstalker.h>
#include <stdio.h>
void onEvent(const GumEvent *event, GumCpuContext *cpu_context, gpointer user_data) {
printf("成功");
}
`);
Stalker.follow(curTid, {
events: {
call: true,
},
onEvent: cm.onEvent,
transform: function (iterator) {
while (true) {
if (iterator.next() === null) {
break;
}
iterator.keep();
}
}
})示例代码
function excludeLibs(libs) {
new ModuleMap().values().map(m => {
const { name, base, size } = m;
if (libs.includes(name)) return;
console.log('exclude', JSON.stringify(m));
Stalker.exclude({base,size});
});
}
function main() {
excludeLibs(['frida-agent-64.so', 'libc-2.31.so']);
const onReceive = blob => {
const events = Stalker.parse(blob, { annotate: true, stringify: true });
console.log('onReceive', events.length, events);
};
const cm = new CModule(`
#include <gum/gumstalker.h>
#include <stdio.h>
void onEvent(const GumEvent *event, GumCpuContext *cpu_context, gpointer user_data) {
printf("type %d\n", event->type);
}
`);
Stalker.follow(Process.id, {
events: {
call: true, // CALL instructions: yes please
ret: true, // RET instructions
exec: false, // all instructions
block: true, // block executed: coarse execution trace
compile: false // block compiled: useful for coverage
},
// onReceive,
onEvent: cm.onEvent
});
}
setTimeout(main, 100);一般来说正常arm64都是4个字节为一条指令
Instruction.parse(ptr(地址))直接看hex也可以
console.log(hexdump(ptr(地址), {
length: 4,
header: true,
ansi: true
}))ptr把地址转换为指针
ptr(0x4cb54 + 0x700c113000)
注意Thmub指令需要and(-2)才可以正常使用hexdump还有readArray这些函数,但是Instruction不需要and(-2)
可以判读是否在当前模块
const isModuleCode = baseFirstAddress.compare(base) >= 0 &&
baseFirstAddress.compare(base.add(size)) < 0;
iterator.putCallout可以在指令之后设置回调,注意这个和keep是配合的,如图
iterator.putCallout((context) => {
console.log("\t" + curRealAddr + " <+" + curOffsetInt + ">: " + instructionStr,
"x8="+ context.x8);
})context.x8 = 0x12345678; // 修改 x8 寄存器
context.x0 = 0x9abcdef0; // 修改 x0 寄存器
context.pc = context.pc.add(4); // 修改 pc 寄存器,跳过当前指令(示例)function nopFunc(parg2) {
// 修改内存保护,使其可写
Memory.protect(parg2, 4, 'rwx');
// 使用 Arm64Writer 写入 'ret' 指令
var writer = new Arm64Writer(parg2);
writer.putRet();
writer.flush();
writer.dispose();
console.log("nop " + parg2 + " success");
}对于函数可以ret
function nopFunc(parg2) {
// 修改内存保护,使其可写
Memory.protect(parg2, 4, 'rwx');
// 使用 Arm64Writer 写入 'nop' 指令
var writer = new Arm64Writer(parg2);
writer.putNop();
writer.flush();
writer.dispose();
console.log("nop " + parg2 + " success");
}简单用法
const cm = new CModule(`
#include <gum/gumstalker.h>
#include <stdio.h>
void test(){
console.log("成功");
}
`);
// 使用 NativeFunction 来调用该函数指针
const testFunc = new NativeFunction(cm.test, "void", []);
// 调用 test 函数
testFunc()const config = {
showStacks: false,
showDivider: true,
}
Java.perform(function () {
// console.log('frida 已启动');
function showStacks(name = '') {
if (config.showStacks) {
console.log(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Throwable').$new(name)))
}
}
function showDivider(name = '') {
if (config.showDivider) {
console.log(`==============================${name}==============================`)
}
}
function showArguments() {
console.log('arguments: ', ...arguments)
}
const ByteString = Java.use('com.android.okhttp.okio.ByteString')
const Encode = {
toBase64(tag, data) {
console.log(tag + ' Base64: ', ByteString.of(data).base64())
// console.log(tag + ' Base64: ', bytesToBase64(data));
},
toHex(tag, data) {
console.log(tag + ' Hex: ', ByteString.of(data).hex())
// console.log(tag + ' Hex: ', bytesToHex(data));
},
toUtf8(tag, data) {
console.log(tag + ' Utf8: ', ByteString.of(data).utf8())
// console.log(tag + ' Utf8: ', bytesToString(data));
},
toAll(tag, data) {
Encode.toUtf8(tag, data)
Encode.toHex(tag, data)
Encode.toBase64(tag, data)
},
toResult(tag, data) {
Encode.toHex(tag, data)
Encode.toBase64(tag, data)
},
}
const MessageDigest = Java.use('java.security.MessageDigest')
{
let overloads_update = MessageDigest.update.overloads
for (const overload of overloads_update) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
Encode.toAll(`${algorithm} update data`, arguments[0])
return this.update(...arguments)
}
}
let overloads_digest = MessageDigest.digest.overloads
for (const overload of overloads_digest) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
const result = this.digest(...arguments)
if (arguments.length === 1) {
Encode.toAll(`${algorithm} update data`, arguments[0])
} else if (arguments.length === 3) {
Encode.toAll(`${algorithm} update data`, arguments[0])
}
Encode.toResult(`${algorithm} digest result`, result)
return result
}
}
}
const Mac = Java.use('javax.crypto.Mac')
{
Mac.init.overload('java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (key, AlgorithmParameterSpec) {
return this.init(key, AlgorithmParameterSpec)
}
Mac.init.overload('java.security.Key').implementation = function (key) {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
const keyBytes = key.getEncoded()
Encode.toAll(`${algorithm} init Key`, keyBytes)
return this.init(...arguments)
}
let overloads_doFinal = Mac.doFinal.overloads
for (const overload of overloads_doFinal) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
const result = this.doFinal(...arguments)
if (arguments.length === 1) {
Encode.toAll(`${algorithm} update data`, arguments[0])
} else if (arguments.length === 3) {
Encode.toAll(`${algorithm} update data`, arguments[0])
}
Encode.toResult(`${algorithm} doFinal result`, result)
return result
}
}
}
const Cipher = Java.use('javax.crypto.Cipher')
{
let overloads_init = Cipher.init.overloads
for (const overload of overloads_init) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
if (arguments[0]) {
const mode = arguments[0]
console.log(`${algorithm} init mode`, mode)
}
if (arguments[1]) {
const className = JSON.stringify(arguments[1])
// 安卓10以上私钥是有可能输出不了的
if (className.includes('OpenSSLRSAPrivateKey')) {
// const keyBytes = arguments[1];
// console.log(`${algorithm} init key`, keyBytes);
} else {
const keyBytes = arguments[1].getEncoded()
Encode.toAll(`${algorithm} init key`, keyBytes)
}
}
if (arguments[2]) {
const className = JSON.stringify(arguments[2])
if (className.includes('javax.crypto.spec.IvParameterSpec')) {
const iv = Java.cast(arguments[2], Java.use('javax.crypto.spec.IvParameterSpec'))
const ivBytes = iv.getIV()
Encode.toAll(`${algorithm} init iv`, ivBytes)
} else if (className.includes('java.security.SecureRandom')) {
}
}
return this.init(...arguments)
}
}
let overloads_doFinal = Cipher.doFinal.overloads
for (const overload of overloads_doFinal) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
const result = this.doFinal(...arguments)
if (arguments.length === 1) {
Encode.toAll(`${algorithm} update data`, arguments[0])
} else if (arguments.length === 3) {
Encode.toAll(`${algorithm} update data`, arguments[0])
}
Encode.toResult(`${algorithm} doFinal result`, result)
return result
}
}
}
const Signature = Java.use('java.security.Signature')
{
let overloads_update = Signature.update.overloads
for (const overload of overloads_update) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
Encode.toAll(`${algorithm} update data`, arguments[0])
return this.update(...arguments)
}
}
let overloads_sign = Signature.sign.overloads
for (const overload of overloads_sign) {
overload.implementation = function () {
const algorithm = this.getAlgorithm()
showDivider(algorithm)
showStacks(algorithm)
const result = this.sign()
Encode.toResult(`${algorithm} sign result`, result)
return this.sign(...arguments)
}
}
}
})