From 764cd8fa40d49f31ded0a4c3a6d3531ab7f42ba5 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Wed, 9 Jul 2025 21:58:42 +0800 Subject: [PATCH 1/2] bulk page write --- StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m | 93 +++++++++++++++++++ StikJIT/JSSupport/JSSupport.h | 1 + StikJIT/JSSupport/RunJSView.swift | 5 + 3 files changed, 99 insertions(+) diff --git a/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m b/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m index 400d920a..c8a155ea 100644 --- a/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m +++ b/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m @@ -31,3 +31,96 @@ idevice_string_free(attach_response); return commandResponse; } + +// 0 <= val <= 15 +char u8toHexChar(uint8_t val) { + if(val < 10) { + return val + '0'; + } else { + return val + 87; + } +} + +void calcAndWriteCheckSum(char* commandStart) { + uint8_t sum = 0; + char* cur = commandStart; + for(; *cur != '#'; ++cur) { + sum += *cur; + } + cur[1] = u8toHexChar((sum & 0xf0) >> 4); + cur[2] = u8toHexChar(sum & 0xf); +} + +// support up to 9 digit +void writeAddress(char* writeStart, uint64_t addr) { + writeStart[0] = u8toHexChar((addr & 0xf00000000) >> 32); + writeStart[1] = u8toHexChar((addr & 0xf0000000) >> 28); + writeStart[2] = u8toHexChar((addr & 0xf000000) >> 24); + writeStart[3] = u8toHexChar((addr & 0xf00000) >> 20); + writeStart[4] = u8toHexChar((addr & 0xf0000) >> 16); + writeStart[5] = u8toHexChar((addr & 0xf000) >> 12); + writeStart[6] = u8toHexChar((addr & 0xf00) >> 8); + writeStart[7] = u8toHexChar((addr & 0xf0) >> 4); + writeStart[8] = u8toHexChar((addr & 0xf)); +} + +// you need to free generated buffer +char* getBulkMemWriteCommand(uint64_t startAddr, uint64_t JITPagesSize, uint32_t* commandCountOut, uint32_t* bufferLengthOut) { + // $M10c128000,1:69#12 + uint32_t commandCount = (uint32_t)(JITPagesSize >> 14); + uint32_t commandBufferSize = commandCount * 19; + *commandCountOut = commandCount; + *bufferLengthOut = commandBufferSize; + char* buffer = malloc(commandBufferSize + 1); + char* bufferEnd = buffer + commandBufferSize; + buffer[commandBufferSize] = 0; + + uint64_t curAddr = startAddr; + for(char* curBufferPtr = buffer; curBufferPtr < bufferEnd; curBufferPtr += 19) { + curBufferPtr[0] = '$'; + curBufferPtr[1] = 'M'; + curBufferPtr[11] = ','; + curBufferPtr[12] = '1'; + curBufferPtr[13] = ':'; + curBufferPtr[14] = '6'; + curBufferPtr[15] = '9'; + curBufferPtr[16] = '#'; + writeAddress(curBufferPtr + 2, curAddr); + calcAndWriteCheckSum(curBufferPtr + 1); + curAddr += 16384; + } + return buffer; +} + +NSString* handleJITPageWrite(JSContext* context, uint64_t startAddr, uint64_t JITPagesSize, DebugProxyHandle* debugProxy) { + uint32_t bufferLength = 0; + uint32_t commandCount = 0; + char* commandBuffer = getBulkMemWriteCommand(startAddr, JITPagesSize, &commandCount, &bufferLength); + // we send 1024 commands at a time + for(int curCommand = 0; curCommand < commandCount; curCommand += 1024) { + uint32_t commandsToSend = (commandCount - curCommand > 1024) ? 1024 : (commandCount - curCommand); + IdeviceFfiError* err = debug_proxy_send_raw(debugProxy, (const uint8_t *)commandBuffer + curCommand * 19, commandsToSend * 19); + if(err) { + context.exception = [JSValue valueWithObject:[NSString stringWithFormat:@"error code %d, msg %s", err->code, err->message] inContext:context]; + free(commandBuffer); + idevice_error_free(err); + return nil; + } + + for(int i = 0; i < commandsToSend; ++i) { + char* response = 0; + IdeviceFfiError* err = debug_proxy_read_response(debugProxy, &response); + if(response) { + idevice_string_free(response); + } + if(err) { + context.exception = [JSValue valueWithObject:[NSString stringWithFormat:@"error code %d, msg %s", err->code, err->message] inContext:context]; + free(commandBuffer); + idevice_error_free(err); + return nil; + } + } + } + free(commandBuffer); + return @"OK"; +} diff --git a/StikJIT/JSSupport/JSSupport.h b/StikJIT/JSSupport/JSSupport.h index bd4f51e2..13a4cc40 100644 --- a/StikJIT/JSSupport/JSSupport.h +++ b/StikJIT/JSSupport/JSSupport.h @@ -9,3 +9,4 @@ #include "../idevice/jit.h" NSString* handleJSContextSendDebugCommand(JSContext* context, NSString* commandStr, DebugProxyHandle* debugProxy); +NSString* handleJITPageWrite(JSContext* context, uint64_t startAddr, uint64_t JITPagesSize, DebugProxyHandle* debugProxy); diff --git a/StikJIT/JSSupport/RunJSView.swift b/StikJIT/JSSupport/RunJSView.swift index dbe48c16..6a8ec878 100644 --- a/StikJIT/JSSupport/RunJSView.swift +++ b/StikJIT/JSSupport/RunJSView.swift @@ -50,9 +50,14 @@ class RunJSViewModel: ObservableObject { } } + let prepareMemoryRegionFunction: @convention(block) (UInt64, UInt64) -> String = { startAddr, regionSize in + return handleJITPageWrite(self.context, startAddr, regionSize, self.debugProxy) ?? "" + } + context = JSContext() context?.setObject(getPidFunction, forKeyedSubscript: "get_pid" as NSString) context?.setObject(sendCommandFunction, forKeyedSubscript: "send_command" as NSString) + context?.setObject(prepareMemoryRegionFunction, forKeyedSubscript: "prepare_memory_region" as NSString) context?.setObject(logFunction, forKeyedSubscript: "log" as NSString) context?.evaluateScript(scriptContent) From 5c9f5940fe5aa09488fd3ec60e1ef8a7e6dc8a20 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Wed, 9 Jul 2025 22:01:27 +0800 Subject: [PATCH 2/2] txm check Co-authored-by: Stossy11 --- StikJIT/JSSupport/RunJSView.swift | 5 +++++ StikJIT/Utilities/Extensions.swift | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 StikJIT/Utilities/Extensions.swift diff --git a/StikJIT/JSSupport/RunJSView.swift b/StikJIT/JSSupport/RunJSView.swift index 6a8ec878..bb260c58 100644 --- a/StikJIT/JSSupport/RunJSView.swift +++ b/StikJIT/JSSupport/RunJSView.swift @@ -54,7 +54,12 @@ class RunJSViewModel: ObservableObject { return handleJITPageWrite(self.context, startAddr, regionSize, self.debugProxy) ?? "" } + let hasTXMFunction: @convention(block) () -> Bool = { + return ProcessInfo.processInfo.hasTXM + } + context = JSContext() + context?.setObject(hasTXMFunction, forKeyedSubscript: "hasTXM" as NSString) context?.setObject(getPidFunction, forKeyedSubscript: "get_pid" as NSString) context?.setObject(sendCommandFunction, forKeyedSubscript: "send_command" as NSString) context?.setObject(prepareMemoryRegionFunction, forKeyedSubscript: "prepare_memory_region" as NSString) diff --git a/StikJIT/Utilities/Extensions.swift b/StikJIT/Utilities/Extensions.swift new file mode 100644 index 00000000..31fd2c75 --- /dev/null +++ b/StikJIT/Utilities/Extensions.swift @@ -0,0 +1,19 @@ +// +// Extensions.swift +// StikDebug +// +// Created by s s on 2025/7/9. +// + +extension FileManager { + func filePath(atPath path: String, withLength length: Int) -> String? { + guard let file = try? contentsOfDirectory(atPath: path).filter({ $0.count == length }).first else { return nil } + return "\(path)/\(file)" + } +} + +public extension ProcessInfo { + var hasTXM: Bool { + { if let boot = FileManager.default.filePath(atPath: "/System/Volumes/Preboot", withLength: 36), let file = FileManager.default.filePath(atPath: "\(boot)/boot", withLength: 96) { return access("\(file)/usr/standalone/firmware/FUD/Ap,TrustedExecutionMonitor.img4", F_OK) == 0 } else { return (FileManager.default.filePath(atPath: "/private/preboot", withLength: 96).map { access("\($0)/usr/standalone/firmware/FUD/Ap,TrustedExecutionMonitor.img4", F_OK) == 0 }) ?? false } }() + } +}