diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e9bc0f8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1 @@ +max_line_length = 130 diff --git a/README.md b/README.md index d03d2e3..3b4925a 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,22 @@ Reversing a camera Per [pictures](https://github.com/DavidVentura/cam-reverse/blob/master/pics/pcb.jpg?raw=true) the main chip is TXW817 ([chinese](https://www.taixin-semi.com/Product/ProductDetail?productId=306), [eng, google translate](https://www-taixin--semi-com.translate.goog/Product/ProductDetail?productId=306&_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=wapp)) + +The interesting implementation is in `libvdp.so`, part of the apk bundle. This repo uses Frida for live analysis of the .so file. + + +### Take APK from emulator/sacrificial device +``` +adb shell pm list packages | grep ysx +adb shell pm path com.ysxlite.cam +adb shell pm path com.ysxlite.cam | while read -r line ; do adb pull $(echo $line | cut -d: -f2-) ; done +``` +### Push to sacrificial device +``` +adb install-multiple *apk +``` + +### Frida install Android + +[docs](https://frida.re/docs/android/) + diff --git a/asd.js b/asd.js new file mode 100644 index 0000000..1d89eae --- /dev/null +++ b/asd.js @@ -0,0 +1,181 @@ +function hook_fn(name_in_elf, enter, leave) { + var symbol_addr = DebugSymbol.fromName(name_in_elf).address; + console.log(`${name_in_elf} addr is: ${symbol_addr}`); + Interceptor.attach(symbol_addr, { + onEnter: enter, + onLeave: leave, + }); + console.log(`Hooked ${name_in_elf}`); +} + +function hook_export_fn(name_in_elf, enter, leave) { + var symbol_addr = Module.findExportByName("libvdp.so", name_in_elf); + console.log(`${name_in_elf} addr is: ${symbol_addr}`); + Interceptor.attach(symbol_addr, { + onEnter: enter, + onLeave: leave, + }); + console.log(`Hooked ${name_in_elf}`); +} + +const hook_p2p_read = () => { + var mangled_sym = "_Z8p2p_readPiihPci"; + hook_export_fn( + mangled_sym, + (args) => { + // console.log(hexdump( + // args[0], {offset : 0, length : 0x200, header : true, ansi : + // false})); + + let m_type = args[1]; + let m_unk3 = args[2]; + + let m_size = args[4].toInt32(); + console.log(m_type, m_unk3, m_size); + }, + () => {}, + ); +}; + +const hook_pack_ClntPkt = () => { + let sym = "pack_ClntPkt"; + hook_fn( + sym, + (args) => { + console.log(`onEnter ${sym}`); + + /* + * onEnter pack_ClntPkt + 2 0x7b5ef9c640 + onExit pack_ClntPkt, ret=24 + 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0123456789ABCDEF 00000000 f1 67 00 14 42 41 54 43 00 00 00 00 00 09 4c + fb .g..BATC......L. 00000010 45 58 4c 56 53 00 00 00 EXLVS... + */ + // f1 30 00 00 heartbeat? + // int pack_ClntPkt(int m_type_or_len,ushort *pkt_buf,long + // something_out) + let m_ptype = args[0].toInt32(); + // type = 2 == LanSearch, LanSearchExt, ServerReq, Hello, DevQuery + // 2 = P2P? + // let m_buf = args[1].readByteArray(m_ptype); // len?? + let m_out = args[2]; + this.outBuf = args[2]; + this.inBuf = args[1]; + var trace = Thread.backtrace(this.context, Backtracer.ACCURATE).map( + DebugSymbol.fromAddress, + ); + for (var j in trace) { + console.log(trace[j]); + } + console.log(m_ptype); + }, + (retval) => { + console.log(`onExit ${sym}, ret=${retval.toInt32()}`); + console.log("out\n", this.outBuf.readByteArray(retval.toInt32())); + console.log("in\n", this.inBuf.readByteArray(retval.toInt32())); + console.log( + "############################################################", + ); + }, + ); +}; + +const hook___android_log_print = () => { + const sym = "__android_log_print"; + hook_fn( + sym, + (args) => { + // (ushort *param_1,int param_2,undefined4 *param_3,undefined8 param_4 + let _prio = args[0].toInt32(); + let _tag = args[1].readCString(); + let fmt = args[2].readCString(); + // console.log(fmt); // debug if crashes due to missing placeholder + const types = placeholderTypes(fmt); // ['s', 'd', ..]` + + let o = { + s: (x) => x.readCString(), + d: (x) => x.toInt32(), + u: (x) => x.toInt32(), + x: (x) => x.toInt32().toString(16), + f: (x) => x.toFloat(), + }; + + const values = types.map((t, idx) => o[t](args[idx + 3])); + const newStr = sprintf(fmt, values); + console.log(newStr); + }, + () => {}, + ); +}; +const hook_pack_P2pId = () => { + var sym = "pack_P2pId"; + sym = "pack_P2pHdr"; + sym = "Send_Pkt"; + hook_fn( + sym, + (args) => { + console.log(`onEnter ${sym}`); + // (ushort *param_1,int param_2,undefined4 *param_3,undefined8 param_4 + let m_ptype = args[1].toInt32(); + let m_data = args[0].readByteArray(m_ptype); // len?? + let m_sock = args[2]; + let m_p4 = args[3]; + console.log(m_data, m_ptype, m_sock, m_p4); + }, + () => {}, + ); +}; + +let indent = 0; +function doHooks() { + var libnative_addr = Module.findBaseAddress("libvdp.so"); + // const prefixes = ["Send_Pkt*", "P2P*", "*RcvTh*", "parse_*"]; // "XQP2P*", + const prefixes = ["parse_*", "pack_*", "Send_Pkt*"]; // "XQP2P*", + const spam = ["XQP2P_Check_Buffer", "P2P_ChannelBufferCheck"]; + prefixes + .map((prefix) => DebugSymbol.findFunctionsMatching(prefix)) + .flat() + .map(DebugSymbol.fromAddress) + .filter((dbg) => !spam.includes(dbg.name)) + .map((dbg) => { + Interceptor.attach(dbg.address, { + onEnter: (args) => { + indent = indent + 1; + console.log(" ".repeat(indent) + dbg.name); + }, + onLeave: (retval) => { + indent = indent - 1; + }, + }); + console.log(`Hooked ${dbg.name}`); + }); + + if (libnative_addr) { + hook___android_log_print(); + // hook_p2p_read(); + // hook_pack_P2pId(); + // hook_pack_ClntPkt(); + } else { + console.log("NO WORKING"); + } +} + +const matchy = /%[0-9.-]*([a-z])/g; +const replacy = /(.*?)%[0-9.-]*([a-z])/g; + +const placeholderTypes = (str) => { + // '%-16s, line %4d, %-16s:ret=%d,broadcast lan_seach to %s:%.3f!!!!' + // => + // [ 's', 'd', 's', 'd', 's', 'f' ] + return [...str.matchAll(matchy)].map((m) => m[1]); +}; +const sprintf = (str, values) => { + // '%-16s, line %4d, %-16s:ret=%d,broadcast lan_seach to %s:%.3f!!!!' + + // ["asd", 20, ...] + // => + // asd, line 20, ... + const matches = str.matchAll(replacy); + return [...matches].map((m, idx) => m[1] + values[idx].toString()).join(""); +}; +setImmediate(doHooks); diff --git a/loader3.py b/loader3.py new file mode 100755 index 0000000..3a8cdce --- /dev/null +++ b/loader3.py @@ -0,0 +1,47 @@ +#!/bin/env python +import frida + +# Define the JavaScript code to hook System.loadLibrary() +js_code = """ +const System = Java.use('java.lang.System'); + +System.loadLibrary.implementation = function(libraryName) { + console.log('Loading library: ' + libraryName); + // You can add your custom logic here before calling the original function. + return this.loadLibrary(libraryName); +}; +""" +target_package_name = "com.ysxlite.cam" +shared_library_name = "vdp.so" +native_function_name = "p2p_read" +js_code = open('asd.js').read() +def on_message(m, _data): + print('got', m, _data) + +def main(): + # Replace 'com.example.targetapp' with the actual package name of your target app. + app_id = "com.ysxlite.cam" + target_app_package = 'com.ysxlite.cam' + target_app_package = 'YsxLite' + + device = frida.get_usb_device() + session = device.attach(target_app_package) + #pid = device.spawn([app_id]) + #print(pid) + #session = device.attach(pid) + # Attach to the target app + print(session) + + # Create a script and load the JavaScript code + script = session.create_script(js_code) + + script.on('message', on_message) + script.load() + # device.resume(target_app_package) + + # Keep the script running to continue monitoring the app + input("Press Enter to stop...") + +if __name__ == '__main__': + main() +