- Hook Dlopen
//第一種方式(針對較老的系統(tǒng)版本)
var dlopen = Module.findExportByName(null, "dlopen");
console.log(dlopen);
if(dlopen != null){
Interceptor.attach(dlopen,{
onEnter: function(args){
var soName = args[0].readCString();
console.log(soName);
if(soName.indexOf("libc.so") != -1){
this.hook = true;
}
},
onLeave: function(retval){
if(this.hook) {
dlopentodo();
};
}
});
}
//第二種方式(針對新系統(tǒng)版本)
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
console.log(android_dlopen_ext);
if(android_dlopen_ext != null){
Interceptor.attach(android_dlopen_ext,{
onEnter: function(args){
var soName = args[0].readCString();
console.log(soName);
if(soName.indexOf("libc.so") != -1){
this.hook = true;
}
},
onLeave: function(retval){
if(this.hook) {
dlopentodo();
};
}
});
}
function dlopentodo(){
//todo ...
}
- Java堆棧打印
function show_java_trace(){
Java.perform(function(){
var MessageDigest = Java.use("java.security.MessageDigest");
MessageDigest.digest.overload().implementation = function(){
//var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
console.log(stack);
return this.digest();
}
});
}
- Native堆棧打印
function show_native_trace(){
var func = Module.findBaseAddress("libil2cpp.so").add(0x56FCA8);
Interceptor.attach(func, {
onEnter: function(args){
console.log("called from:\n"+
Thread.backtrace(this.context,Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join("\n"));
},
onLeave: function(retval){
}
});
}
- HookJava中的loadLibrary并打印堆棧
function hook_library(){
Java.perform(function() {
const System = Java.use('java.lang.System');
const Runtime = Java.use('java.lang.Runtime');
const VMStack = Java.use('dalvik.system.VMStack');
System.loadLibrary.implementation = function(library) {
try {
console.log('System.loadLibrary("' + library + '")');
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
const loaded = Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), library);
return loaded;
} catch(ex) {
console.log(ex);
}
};
System.load.implementation = function(library) {
try {
console.log('System.load("' + library + '")');
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
const loaded = Runtime.getRuntime().load0(VMStack.getCallingClassLoader(), library);
return loaded;
} catch(ex) {
console.log(ex);
}
};
});
}
- String轉(zhuǎn)Byte
function stringToBytes(str) {
var ch, st, re = [];
for (var i = 0; i < str.length; i++ ) {
ch = str.charCodeAt(i);
st = [];
do {
st.push( ch & 0xFF );
ch = ch >> 8;
}
while ( ch );
re = re.concat( st.reverse() );
}
return re;
}
- hexToBytes
function hexToBytes(str) {
var pos = 0;
var len = str.length;
if (len % 2 != 0) {
return null;
}
len /= 2;
var hexA = new Array();
for (var i = 0; i < len; i++) {
var s = str.substr(pos, 2);
var v = parseInt(s, 16);
hexA.push(v);
pos += 2;
}
return hexA;
}
- bytes2Hex
function bytes2Hex(arr) {
var str = "[";
for (var i = 0; i < arr.length; i++) {
var z = parseInt(arr[i]);
if (z < 0) z = 255 + z;
var tmp = z.toString(16);
if (tmp.length == 1) {
tmp = "0" + tmp;
}
str = str + " " + tmp;
}
return (str + " ]").toUpperCase();
}
- ArrayBuffer 轉(zhuǎn)換
function ab2Hex(buffer) {
var arr = Array.prototype.map.call(new Uint8Array(buffer), function (x) {return ('00' + x.toString(16)).slice(-2)}).join(" ").toUpperCase();
return "[" + arr + "]";
}
function ab2Str(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
- jstring, jbytearray顯示
function jstring2Str(jstring) {
var ret;
Java.perform(function() {
var String = Java.use("java.lang.String");
ret = Java.cast(jstring, String);
});
return ret;
}
function jbyteArray2Array(jbyteArray) {
var ret;
Java.perform(function() {
var b = Java.use('[B');
var buffer = Java.cast(jbyteArray, b);
ret = Java.array('byte', buffer);
});
return ret;
}
- 主線程調(diào)用
function RunOnMain(){
Java.perform(function(){
var cls_main = null
//獲取Context
Java.choose("com.lzy.ndk.MainActivity",{
onMatch:function(clazz){
cls_main = clazz
},
onComplete:function(){}
})
//動態(tài)注冊一個(gè)類實(shí)現(xiàn)Runnable方法
var cls_run = Java.registerClass({
name:"com.lzy.frida.runnable",
implements:[Java.use("java.lang.Runnable")],
//創(chuàng)建類成員變量
fields:{
description: 'java.lang.String',
limit: 'int'
},
//創(chuàng)建方法以及重載方法的用法
methods:{
run:function(){
Java.use("android.widget.Toast").makeText(cls_main,Java.use("java.lang.String").$new("this is a test Toast"),1).show()
},
add:[{
returnType:'java.lang.String',
argumentTypes:['java.lang.String','java.lang.String'],
implementation:function(str1,str2){
return str1+"+++"+str2
}
},
{
returnType:'java.lang.String',
argumentTypes:['java.lang.String'],
implementation:function(str1){
return str1+"==="
}
}
]
}
})
//這里的實(shí)現(xiàn)主線程調(diào)用方法很多漓拾,這里舉例一種
//1.隨便在MainActivity找一個(gè)View奇昙,View.post(Runnable)
cls_main.bt1.value.post(cls_run.$new())
//2.Activity的方法runOnUiThread()
cls_main.runOnUiThread(cls_run.$new())
//3.new Handler(getMainLooper()).post()
Java.use("android.os.Handler").$new(cls_main.getMainLooper()).post(cls_run.$new())
//4.Java.scheduleOnMainThread(function(){}) 不推薦蹬跃,不好用總是出問題
Java.scheduleOnMainThread(function(){
console.log(Java.isMainThread())
})
})
}
- 寫文件(需要存儲卡讀寫權(quán)限)
function writeFile() {
//frida 的api來寫文件
var file = new File("/sdcard/a.txt", "w")
file.write("123123123")
file.flush()
file.close()
}
/**
* 從內(nèi)存指定位置讀取指定長度并寫入指定文件目錄
* PS:需要APP有文件讀寫權(quán)限
* @param {String} path 存放路徑
* @param {String} name 文件名
* @param {Number} mPtr 內(nèi)存起始位置
* @param {Number} length 內(nèi)存區(qū)域的長度
*/
function writeFile(path,name,mPtr,length) {
var fopen = new NativeFunction(Module.findExportByName("libc.so", "fopen"), "pointer", ["pointer", "pointer"])
var fwrite = new NativeFunction(Module.findExportByName("libc.so", "fwrite"), "int", ["pointer",'int','int', "pointer"])
var fclose = new NativeFunction(Module.findExportByName("libc.so", "fclose"), "int", ["pointer"])
var system=new NativeFunction(Module.findExportByName("libc.so","system"),"pointer",["pointer"])
system(Memory.allocUtf8String("mkdir -p "+path))
var filename = Memory.allocUtf8String(path+"/"+name+".dll")
var open_mode = Memory.allocUtf8String("a")
var file = fopen(filename, open_mode)
fwrite(mPtr,1,length,file)
fclose(file)
}
- 批量斷點(diǎn)定位調(diào)用
function add_native_break_points(){
// 結(jié)合Il2CppDumper使用副渴,用于批量快速下斷點(diǎn)腋腮,跟蹤native函數(shù)調(diào)用
// frida -U -f <PackageName> -l C:\Users\lzy\utils\bpoints.js --no-pause
const soName = "libil2cpp.so"
const arrayAddr =
['0x71541c', '0x715b38', '0x715be4', '0x715c61']
const arrayName =
['GameManager$$Awake', 'GameManager$$GetParam', 'GameManager$$SaveParam', 'GameManager$$ActivatePrivacyButton']
function breakPoints(){
const soAddr = Module.findBaseAddress(soName);
console.error('\nsoAddr:' + soAddr + "\n");
Java.perform(function(){
arrayAddr
.map(function(temp){return soAddr.add(temp)})
.forEach(function(value,index,array){
console.log("-------------------------");
console.log('currentAddr:' + value);
try{
funcTmp(value,index,arrayName);
}catch(e){
//Thumb指令集地址要加一
funcTmp(value.add(1),soAddr,index,arrayName);
}
console.log("\t\t---->"+index,value+" is prepared ");
})
console.log("\n")
})
function funcTmp(currentAddr,index,arrayName){
Interceptor.attach(currentAddr, {
onEnter: function(args){
console.log("called : "+arrayName[index]+" ----- addr : " + currentAddr.sub(soAddr) +"\n");
this.temp = currentAddr.sub(soAddr);
if(this.temp === 0xef3080) {
console.log('CCCryptorCreate called from:\n' +
Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n') + '\n');
}
},
onLeave: function(retval){
}
});
}
}
function hook_dlopen() {
// const dlopen = Module.findExportByName(null, "dlopen");
const dlopen = Module.findExportByName(null, "android_dlopen_ext");
if (dlopen != null) {
Interceptor.attach(dlopen, {
onEnter: function (args) {
var l_soName = args[0].readCString()
console.log(l_soName)
if (l_soName.indexOf(soName) != -1) {
this.hook = true
}
},
onLeave: function (retval) {
if (this.hook) {
console.warn("\nLoaded "+soName + " add break points")
breakPoints()
}
}
})
}
}
setImmediate(hook_dlopen())
}
配合Il2CppDumper生成的script.json食用更香
import json
if __name__ == '__main__':
# json地址
f = open('C:\\Users\\lzy\\Desktop\\Il2CppDumper-v6.2.1\\script.json',encoding='utf-8')
# 查找的字符串
searchStr = "Network"
j = json.load(f)
ScriptMethod = j['ScriptMethod']
ScriptMetadataMethod = j['ScriptMetadataMethod']
temp_name = []
temp_addr = []
for temp in ScriptMethod:
if searchStr in temp["Name"]:
temp_name.append(temp["Name"])
temp_addr.append(hex(temp["Address"]))
print('Found : '+str(len(temp_name))+' Function')
print('--------------------------------')
print(temp_addr)
print('--------------------------------')
print(temp_name)
print('--------------------------------')
- TracerPid fgets 反調(diào)試
var anti_fgets = function () {
show_log("anti_fgets");
var fgetsPtr = Module.findExportByName("libc.so", "fgets");
var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
var retval = fgets(buffer, size, fp);
var bufstr = Memory.readUtf8String(buffer);
if (bufstr.indexOf("TracerPid:") > -1) {
Memory.writeUtf8String(buffer, "TracerPid:\t0");
// dmLogout("tracerpid replaced: " + Memory.readUtf8String(buffer));
}
return retval;
}, 'pointer', ['pointer', 'int', 'pointer']));
};
- 日志打印
function log(str){
var threadid = Process.getCurrentThreadId()
var date = new Date()
var month = date.getMonth() + 1
var strDate = date.getDate()
var hour = date.getHours()
var Minutes = date.getMinutes()
var Seconds = date.getSeconds()
if (month >= 1 && month <= 9) {
month = "0" + month
}
if (strDate >= 0 && strDate <= 9) {
strDate = "0" + strDate
}
if (hour >= 0 && hour <= 9) {
hour = "0" + hour
}
if (Minutes >= 0 && Minutes <= 9) {
Minutes = "0" + Minutes
}
if (Seconds >= 0 && Seconds <= 9) {
Second = "0" + Seconds
}
var currentDate = date.getFullYear() + "-" + month + "-" + strDate
+ " " + hour + ":" + Minutes + ":" + Seconds
var log = "["+threadid+"][" + currentDate + "] --- " + str
console.log('\x1b[3' + '6;01' + 'm', log, '\x1b[39;49;00m')
}
--------------------------------------------------------
//native日志函數(shù)
var nativeLogF
var nativeLogPossible
function initNativeLog() {
try {
var native_log_function = Module.findExportByName(null, "__android_log_print")
native_log_function ? (nativeLogPossible = true, nativeLogF = new NativeFunction(native_log_function, "int", ["int", "pointer", "pointer", "...", "pointer"])) : LOG("initNativeLog Cound not find export")
} catch (e) {
LOG(e.message)
}
}
function NLOG(msg) {
try {
if (!nativeLogPossible) return;
var str_tip = Memory.allocUtf8String("Native"),
str_formart = Memory.allocUtf8String("%s"),
str_msg = Memory.allocUtf8String(msg)
nativeLogF(6, str_tip, str_formart, str_msg)
} catch (e) {
console.error(e.message)
}
}
function JLOG(msg) {
try {
Java.available && Java.perform(function () {
Java.use("android.util.Log").e("Native", msg)
})
} catch (e) { }
}
- frida api 翻譯指定內(nèi)存位置的代碼匯編
function toAssembler(pointer,length){
length = arguments[1] ? arguments[1] : 10
var baseAddr = Module.findBaseAddress(soName)
console.error("-----------------------------------")
console.warn("Module Addr = \t" + baseAddr)
console.error("------------------")
var target = ptr(pointer)
for (var t = 0; t < length; t++) {
var t_addr = target.add(Process.pointerSize * t)
console.log(t_addr + " -> " + t_addr.sub(baseAddr) + "\t" + Instruction.parse(t_addr))
}
console.error("-----------------------------------")
}
- 內(nèi)存dump so(脫殼upx殼)
function dump_so(so_name) {
Java.perform(function () {
var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
var libso = Process.getModuleByName(so_name);
console.error("------------------------------");
console.warn("[name]:", libso.name);
console.warn("[base]:", libso.base);
console.warn("[size]:", libso.size);
console.warn("[path]:", libso.path);
console.error("------------------------------");
var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
var file_handle = new File(file_path, "wb");
if (file_handle && file_handle != null) {
Memory.protect(ptr(libso.base), libso.size, 'rwx');
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.log("[dump]:", file_path);
}
});
}
- 動態(tài)加載Dex / SO
function loadDex(){
//這里只是提一下可以使用Frida提供的Api加載dex倔毙,你也可以解包再打包氏堤,但是顯然這個(gè)方便得多
//手動去加載一些工具類(Gson腐缤,AndroidUtilCode瘫拣,自己寫的工具類等等)
Java.openClassFile("/data/local/tmp/helper.dex").load()
var gson = Java.use("com.google.gson.Gson").$new()
...
}
const m = Module.load("libnative.so');
- 計(jì)劃任務(wù)
function ScheduledTask(){
//用在Spawn啟動的時(shí)候
setImmediate(function(){
console.log("立即執(zhí)行亿絮,只執(zhí)行一次")
})
setTimeout(function(){
console.log("一秒后執(zhí)行,只執(zhí)行一次")
},1000)
//Frida Api
setInterval(function(){
console.log("每隔一秒執(zhí)行一次")
},1000)
// Java Api
Java.perform(function(){
Java.registerClass({
name:"com.lzy.frida.tsk",
superClass:Java.use("java.util.TimerTask"),
methods:{
run:function(){
console.log("等待兩秒后每隔一秒調(diào)用一次")
}
}
})
Java.use("java.util.Timer").$new().schedule(Java.use("com.lzy.frida.tsk").$new(),2000,1000)
})
}
- JNI函數(shù)Trace Demo
function TraceJni(){
Java.perform(function(){
var pSize = Process.pointerSize
var env = Java.vm.getEnv()
//JNI函數(shù)相對env偏移位置參考:
//https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewStringUTF
var GetStaticMethodID = 113,findclass = 6,RegisterNatives = 215;
function getNativeAddress(idx) {
return env.handle.readPointer().add(idx * pSize).readPointer()
}
Interceptor.attach(getNativeAddress(findclass),{
onEnter:function(args){
console.error("-------------findClass-------------")
console.warn("env\t--->\t"+args[0])
console.warn("class\t--->\t"+args[1].readCString())
},
onLeave:function(retval){}
})
Interceptor.attach(getNativeAddress(GetStaticMethodID),{
onEnter:function(args){
console.error("\n-------------GetStaticMethodID-------------")
console.warn(args[0])
console.warn(args[1])
console.warn(args[2].readCString())
},
onLeave:function(retval){}
})
//RegisterNative結(jié)構(gòu)體參照:
//https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
Interceptor.attach(getNativeAddress(RegisterNatives), {
onEnter: function(args) {
console.log(parseInt(args[3]))
for (var i = 0,nMethods = parseInt(args[3]); i < nMethods; i++) {
var structSize = pSize * 3; // = sizeof(JNINativeMethod)
var methodsPtr = ptr(args[2]);
var signature = methodsPtr.add(i * structSize + pSize).readPointer();
var fnPtr = methodsPtr.add(i * structSize + (pSize * 2)).readPointer(); // void* fnPtr
var jClass = jclassAddress2NameMap[args[0]].split('/');
var methodName = methodsPtr.add(i * structSize).readPointer().readCString();
console.log('\x1b[3' + '6;01' + 'm', JSON.stringify({
module: DebugSymbol.fromAddress(fnPtr)['moduleName'],
// https://www.frida.re/docs/javascript-api/#debugsymbol
package: jClass.slice(0, -1).join('.'),
class: jClass[jClass.length - 1],
method: methodName,
// methodsPtr.readPointer().readCString(), // char* name
signature: signature.readCString(),
// char* signature TODO Java bytecode signature parser { Z: 'boolean', B: 'byte', C: 'char', S: 'short', I: 'int', J: 'long', F: 'float', D: 'double', L: 'fully-qualified-class;', '[': 'array' } https://github.com/skylot/jadx/blob/master/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java
address: fnPtr
}), '\x1b[39;49;00m');
}
}
});
})
}
- Native函數(shù)斷點(diǎn)/調(diào)用/替換
//主動調(diào)用native函數(shù)
var soAddr = Module.findBaseAddress("libil2cpp.so");
new NativeFunction(soAddr.add(0x4c33b0),"void",['pointer'])(Java.vm.tryGetEnv())
//替換native函數(shù)
//支持的類型:void,pointer,int,uint,long,ulong,char,uchar,float,double,int8,uint8,int16,uint16,int32,uint32,int64,uint64,bool
Interceptor.replace(new NativeFunction(soAddr.add(0x58F0F4),'void', ['pointer']), new NativeCallback(function (arg) {
console.log("called from:\n"+
Thread.backtrace(this.context,Backtracer.FUZZY)
.map(DebugSymbol.fromAddress).join("\n"));
}, 'void', ['pointer']));
//攔截native函數(shù)
Interceptor.attach(soAddr.add(0xb7a93c),{
onEnter:function(arg){
console.log("called 0xb7a93c")
},
onLeave:function(retval){
console.warn(retval)
}
})
- 獲取類型
function getParamType(obj) {
return obj == null ? String(obj) : Object.prototype.toString.call(obj).replace(/\[object\s+(\w+)\]/i, "$1") || "object";
}
- hook 所有重載函數(shù)
function hookAllOverloads(targetClass, targetMethod) {
Java.perform(function () {
var targetClassMethod = targetClass + '.' + targetMethod;
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function() {
var retval = this[targetMethod].apply(this, arguments);
//這里可以打印結(jié)果和參數(shù)
return retval;
}
}
});
}
/**
* 這里記錄一下杠精日常(durk不必這么操作):用java反射來實(shí)現(xiàn)frida的函數(shù)調(diào)用
* 詳情見下代碼,歸納一下來說就是:
* 獲取反射方法的時(shí)候傳遞的是基本數(shù)據(jù)類型派昧,調(diào)用反射方法的時(shí)候參數(shù)用基礎(chǔ)類型的封裝類型
*/
function java_invoke(){
/**
* java code clas ==> com.lzy.dobbytest.test
*
* public void showLog(int a){
System.out.println("this is a string showLog! "+a);
}
public void showLog(String a){
System.out.println("this is a string showLog! "+a + "String");
}
public void showLog(char a){
System.out.println("this is a string showLog! "+a + "Int");
}
public void showLog(byte a){
System.out.println("this is a string showLog! "+a + "Byte");
}
public void showLog(boolean a,int b){
System.out.println("this is a string showLog! "+a + b + "boolean int");
}
}
*/
Java.perform(function(){
var cls_utils = Java.use("com.lzy.dobbytest.MD5Utils")
var newCls = cls_utils.$new()
var str_obj = "java.lang.Object"
//拿到基本數(shù)據(jù)類型
var type_int = Java.use("java.lang.Class").getPrimitiveClass("int")
var type_boolean = Java.use("java.lang.Class").getPrimitiveClass("boolean")
var type_byte = Java.use("java.lang.Class").getPrimitiveClass("byte")
//String 參數(shù)的方法
var String = Java.use("java.lang.String");
var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[String.class]))
method.invoke(newCls,Java.array(str_obj,["test"])) //這里有自動的類型轉(zhuǎn)換 String.$new("test") == "test"
//Int 參數(shù)的方法
var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_int]))
method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Integer").$new('12')])) //等價(jià)于Java.use("java.lang.Integer").valueOf('12')
//Boolean 參數(shù)的方法
var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_boolean]))
method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Boolean").$new('true')]))
//Byte 參數(shù)的方法
var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_byte]))
method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Byte").$new(String.$new('92'))]))
//Boolean 和 Int 參數(shù)的方法
var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_boolean,type_int]))
method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Boolean").$new('true'),Java.use("java.lang.Integer").$new('12')]))
})
}
pip的源碼安裝在site-packages目錄下
frida 使用小技巧
-
免root使用frida → 重打包
- 反編譯后重寫將 libfrida-gadget.so 打包進(jìn)app
并在smali代碼中加載- 注意
這里去下載的so一定得是和你本地當(dāng)前的frida版本一致
使用pip list 查看本地的版本
- 注意
const-string v1, "frida-gadget" invoke-static {v1}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
- 為app添加網(wǎng)絡(luò)訪問權(quán)限黔姜,以便frida-gadget可以打開套接字
<uses-permission android:name="android.permission.INTERNET" />
- 出現(xiàn)日志:Frida: Listening on TCP port 27042,這時(shí)候是處于一個(gè)等待frida連接狀態(tài)
- frida-ps -U可以查看到當(dāng)前程序PID 以及默認(rèn)的名字Gadget
- 使用以上的PID或者是默認(rèn)的名字Gadget連接frida
- 反編譯后重寫將 libfrida-gadget.so 打包進(jìn)app
-
免root使用frida → ptrace注入
(這里我是用的是別人對其封裝一層方便使用的 腳本 蒂萎, 原腳本在這里 )
- 首先是在linux(kali)環(huán)境下完成
- git clone請?jiān)趌inux下完成(坑秆吵!別再windows下拷貝過去,可能出現(xiàn)編碼問題)
- adb shell am start -D -n com.lzy.xxxx/.MainActivity以調(diào)試模式啟動
除了命令啟動五慈,也可以再手機(jī)開發(fā)者選項(xiàng)中找到調(diào)試應(yīng)用并開啟等待調(diào)試器也行
(這里的話僅再app中debuggable=“true“時(shí)候能這么啟動) - 再就是參照作者寫的,一句話就搞定注入即可
./jdwp-lib-injector.sh frida-gadget.so
-
root使用frida → root 身份運(yùn)行 frida_server
(這也算是我們使用最頻繁的場景)
- 首先還是去官網(wǎng)搞一個(gè)frida_server
- push 到 /data/local/tmp/frida/目錄之下
- 在上面目錄給android_server權(quán)限 chmod 777 ./*
- ./frida_server 跑起來 或者是 cmd 中使用
adb shell "su -c ./data/local/tmp/frida/frida_server"
(后面這條命令不是所有手機(jī)都適用)
-
重打包或者root纳寂,LIEF改so,實(shí)現(xiàn)對frida-gadget.so的加載
參考原文 原文Demo
這種操作也是非常的方便實(shí)用泻拦,前提也是需要重打包apk毙芜,
當(dāng)然你手機(jī)有root的時(shí)候直接copy替換原so也是可行的
(_init,constructor争拐,jni_onload()這里的jni_onload并非立即執(zhí)行爷肝,實(shí)際demo中感覺有一點(diǎn)點(diǎn)的延遲,歡迎大佬科普為什么延遲)- 首先還是拿到我們的工具LIEF
- 然后寫一個(gè)簡單的腳本
import lief libnative = lief.parse("libnative-lib.so") //待修改的so libnative.add_library("libfrida-gadget.so") //在frida官網(wǎng)搞來的和本地版本一致gadget libnative.write("libfrida-gadget1.so") //輸出的文件名稱
- 然后用我們新的so改回原名稱陆错,替換原so,并同時(shí)把gadget.so放回lib重打包即可
-
使用Xpatch實(shí)現(xiàn)免Root的Frida功能
-
root使用frida → 本地化腳本調(diào)用
- 重打包部分和上文 [免root使用frida → 重打包] 一樣
- 新增一步操作灯抛,創(chuàng)建一個(gè)和lib同名但是后綴為conf.so的文本文件
libfrida-gadget.so → libfrida-gadget.config.so
libfrida-gadget.config.so的文本內(nèi)容為以下
{ "interaction": { "type": "script", "path": "/sdcard/script.js", "on_change": "reload" } }
- 在上述位置放上我們的hook腳本即可
參考文章:文章 官網(wǎng)教程
實(shí)現(xiàn)frida的注入有以下方法
- 重打包手動注入frida-gadget.so(使用方法一)
- 改機(jī)重刷系統(tǒng),實(shí)現(xiàn)全局可調(diào)式(使用方法二)
- 使用xposed的插件BDOpener音瓷,實(shí)現(xiàn)全局可調(diào)式(使用方法二)
- 修改app清單文件重打包(使用方法二)
- Root身份運(yùn)行frida_server(使用方法三)