/** * fake_device_v2.js - Fake Saleae device via JSON RPC interception * * Hooks DriveRequest/PopResponse at known addresses to capture the * JSON RPC protocol between Electron UI and the graph server. * * This is the CORRECT interception layer - DriveRequest takes JSON, * not raw USB binary. The graph server internally translates JSON * commands into the USB binary protocol. * * Usage: frida -q -p $(pgrep -f "Saleae Logic Helper \(Renderer\)") -l fake_device_v2.js */ console.log("[*] Fake Saleae Device v2 (JSON RPC Layer)"); console.log("[*] ======================================\n"); var mod = Process.enumerateModules().find(function(m) { return m.name.indexOf("libgraph_server_shared") >= 0; }); if (!mod) { console.log("[-] dylib not found"); throw ""; } console.log("[+] Module base: " + mod.base); // Known offsets from r2 static analysis (Mach-O __TEXT at file offset 0) var DRIVE_REQUEST_OFFSET = 0x5e7b8; var POP_RESPONSE_OFFSET = 0x5f078; var CREATE_GRAPH_OFFSET = 0x5e4dc; var FREE_RESPONSE_OFFSET = 0x5f3bc; var driveReqAddr = mod.base.add(DRIVE_REQUEST_OFFSET); var popRespAddr = mod.base.add(POP_RESPONSE_OFFSET); console.log("[+] DriveRequest @ " + driveReqAddr); console.log("[+] PopResponse @ " + popRespAddr); // ============================================================ // Hook DriveRequest(server, json_ptr, json_len) // ============================================================ Interceptor.attach(driveReqAddr, { onEnter: function(args) { var jsonPtr = args[1]; var jsonLen = args[2].toInt32(); if (jsonLen > 0 && jsonLen < 1000000) { try { var jsonStr = jsonPtr.readUtf8String(jsonLen); console.log("\n>>> DRIVE REQUEST (" + jsonLen + "B):"); try { var parsed = JSON.parse(jsonStr); console.log(JSON.stringify(parsed, null, 2)); } catch(e) { console.log(jsonStr.substring(0, 2000)); } } catch(e) { console.log(" (read error: " + e.message + ")"); } } } }); // ============================================================ // Hook PopResponse(server, out_data_ptr, out_len_ptr, ...) // ============================================================ Interceptor.attach(popRespAddr, { onEnter: function(args) { this.outDataPtr = args[1]; this.outLenPtr = args[2]; }, onLeave: function(retval) { try { var dp = this.outDataPtr.readPointer(); var dl = this.outLenPtr.readU32(); if (dl > 0 && dl < 1000000 && !dp.isNull()) { var s = dp.readUtf8String(dl); console.log("\n<<< POP RESPONSE (" + dl + "B):"); try { var parsed = JSON.parse(s); console.log(JSON.stringify(parsed, null, 2)); } catch(e) { console.log(s.substring(0, 2000)); } } } catch(e) {} } }); console.log("\n[*] Hooks installed."); console.log("[*] Interact with Logic 2 to capture JSON RPC traffic."); console.log("[*] Without a device: captures graph kernel requests (UI operations)."); console.log("[*] With a device: captures FULL device command sequence.");