记录一些可复用的frida脚本
dump so
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
function dump(soName: string, timeout: number = 3000) { setTimeout(() => { let libSo = Process.getModuleByName(soName); let base = libSo.base; let size = libSo.size; let sectionRanges = libSo.enumerateRanges(""); for (let i = 0; i < sectionRanges.length; i++) { console.log(sectionRanges[i].base.sub(base), sectionRanges[i].size, sectionRanges[i].base.add(sectionRanges[i].size).sub(base), sectionRanges[i].protection); Memory.protect(sectionRanges[i].base, sectionRanges[i].size, 'rwx'); let buffer = sectionRanges[i].base.readByteArray(sectionRanges[i].size); console.log(`write ${sectionRanges[i].size} bytes sections`); send(["dumpso", soName], buffer); if (i + 1 < sectionRanges.length && sectionRanges[i].base.add(sectionRanges[i].size).compare(sectionRanges[i + 1].base) !== 0) { let gap = Memory.alloc(sectionRanges[i + 1].base.sub(sectionRanges[i].base.add(sectionRanges[i].size)).toUInt32()); let buffer = gap.readByteArray(sectionRanges[i + 1].base.sub(sectionRanges[i].base.add(sectionRanges[i].size)).toUInt32()); console.log(`write ${sectionRanges[i + 1].base.sub(sectionRanges[i].base.add(sectionRanges[i].size)).toUInt32()} bytes gap`); send(["dumpso", soName], buffer); } } console.log("base: ", base); console.log("size: ", size); console.log("base + size: ", base.add(size)); }, timeout); } dump("libil2cpp.so");
|
把内存中完整的so dump下来,包括仅运行时分配空间的段
HookGetStaticMethodID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
function hookJNIgetStaticMethodID() { console.log("hook JNIgetStaticMethodID"); let symbols = Module.load("libart.so").enumerateSymbols(); for (let i = 0; i < symbols.length; i++) { let symbol = symbols[i]; if (symbol.name.indexOf("art") >= 0 && symbol.name.indexOf("JNI") >= 0 && symbol.name.indexOf("GetStaticMethodID") >= 0 && symbol.name.indexOf("CheckJNI") < 0) { console.log(symbol.name); Interceptor.attach(symbol.address, { onEnter: function (args) { var Name = args[2].readUtf8String(); var sig = args[3].readUtf8String(); let whoCallIt = DebugSymbol.fromAddress(this.returnAddress).toString(); send(["getStaticMethodID", Name, sig, whoCallIt]); } }) } } } setTimeout(hookJNIgetStaticMethodID, 3000);
|
HookRegisterNative
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
Java.perform(() => { hookJNIregisterNatives(); }) function hookJNIregisterNatives() { console.log("hook JNIregisterNatives"); let symbols = Process.getModuleByName("libart.so").enumerateSymbols(); for (let i = 0; i < symbols.length; i++) { let symbol = symbols[i]; if (symbol.name.indexOf("art") >= 0 && symbol.name.indexOf("JNI") >= 0 && symbol.name.indexOf("RegisterNatives") >= 0) { Interceptor.attach(symbol.address, { onEnter: function (args) { var jclass = args[1]; let className = Java.vm.getEnv().getClassName(jclass); var methods = args[2]; var nMethods = args[3].toInt32(); for (let i = 0; i < nMethods; i++) { let nativeMethodPtr = methods.add(i * Process.pointerSize * 3); let namePtr = nativeMethodPtr.add(0).readPointer(); let signaturePtr = nativeMethodPtr.add(Process.pointerSize).readPointer(); let methodPtr = nativeMethodPtr.add(Process.pointerSize * 2).readPointer(); let methodName = namePtr.readUtf8String(); let methodSignature = signaturePtr.readUtf8String(); let whoCallIt = DebugSymbol.fromAddress(this.returnAddress).toString(); let methodPtrStr = methodPtr.sub(Module.load(DebugSymbol.fromAddress(this.returnAddress).moduleName as string).base).toString(16); send(["registerNatives", className, methodName, methodSignature, methodPtrStr, whoCallIt]); } } }); } } }
|
traceStack
全量hook脚本,用于追踪执行流
脚本中遇到的函数表可以由这个脚本生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| ''' 用bn导出函数表和函数地址,方便批量添加frida跟踪调用链 ''' from binaryninja import *
def solve(bv: BinaryView): names = [] addresses = [] with open("functionDump.txt", "w") as f: for func in bv.functions: names.append(func.name) addresses.append(func.start) print("var functionNames = [", file=f) for i in range(len(names)): if i == len(names) - 1: print(" \"{}\"".format(names[i]), file=f) else: print(" \"{}\",".format(names[i]), file=f) print("];", file=f) print("var functionAddresses = [", file=f) for i in range(len(addresses)): if i == len(addresses) - 1: print(" 0x{:x}".format(addresses[i]), file=f) else: print(" 0x{:x},".format(addresses[i]), file=f) print("];", file=f) print("export finish") print("total function count: {}".format(len(names)))
solve(bv)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
var soName = "libsec2023.so"
function sendWrapper(msg) { send(["traceStack", msg]); }
function startTrace(soName) { var tid = Process.getCurrentThreadId(); var lib = Process.getModuleByName(soName); var cnt = 1; sendWrapper("========================trace started========================"); Stalker.follow(tid, { events: { call: false, ret: false, exec: false, block: false, compile: false }, transform: (iterator) => { var insn; while ((insn = iterator.next()) != null) { const addr = ptr(insn.address); if (addr.compare(lib.base) < 0 || addr.compare(lib.base.add(lib.size)) >= 0) { iterator.keep(); continue; } if (functionAddresses.indexOf(addr.sub(lib.base).toUInt32()) != -1) { sendWrapper("at " + cnt + " " + functionNames[functionAddresses.indexOf(addr.sub(lib.base).toUInt32())]); cnt++; } iterator.keep(); } } }) }
Java.perform(() => { var dlopen_addr = Module.getExportByName("libdl.so", "dlopen"); var dlopen_ext_addr = Module.getExportByName("libdl.so", "android_dlopen_ext");
Interceptor.attach(dlopen_addr, { onEnter: function (args) { var path = args[0].readUtf8String(); if (path?.indexOf(soName) != -1) { this.getSo = 1; } }, onLeave: function (retval) { if (this.getSo) { sendWrapper("dlopen: " + soName); startTrace(soName); } } }) Interceptor.attach(dlopen_ext_addr, { onEnter: function (args) { var path = args[0].readUtf8String(); if (path?.indexOf(soName) != -1) { this.getSo = 1; } }, onLeave: function (retval) { if (this.getSo) { sendWrapper("dlopen_ext: " + soName); startTrace(soName); } } }) })
|
python段解析数据的部分,因为trace的数据量实际上相当大,并且有大量重复,所以采用分批合并输出的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def handleTraceStack(message): traceLimit = 1000000000 global TraceStacklogs global startPrintLog if len(TraceStacklogs) == 0: TraceStacklogs.append([message['payload'][1], 1]) else: if message['payload'][1] == TraceStacklogs[-1][0]: TraceStacklogs[-1][1] += 1 else: TraceStacklogs.append([message['payload'][1], 1])
def print_log(): global TraceStacklogs for item, cnt in TraceStacklogs: print(f"{item} x{cnt}") TraceStacklogs.clear() threading.Timer(3.0, print_log).start() if not startPrintLog: startPrintLog = True print_log()
|