diff --git a/frida/script.js b/frida/script.js index e078f41..e831f1a 100644 --- a/frida/script.js +++ b/frida/script.js @@ -1,54 +1,412 @@ -const mod = Process.getModuleByName("WeChat"); -const realAddr = ptr("0x102BAA1E0").sub("0x100000000").add(mod.base); +// pure_js_keyboard_injector.js +console.log("Pure JS Keyboard Injector - Starting..."); -console.log("[+] Real Function Address:", realAddr); +// 常量定义 +const NSEventTypeKeyDown = 10; +const NSEventTypeKeyUp = 11; +const kVK_Return = 36; // 回车键 -Interceptor.attach(realAddr, { - onEnter(args) { - for (let i = 0; i < 20; i++) { - try { - if (args[i].isNull()) { - continue; +// 1. Hook QNSView的handleKeyEvent方法 +if (ObjC.available) { + const QNSView = ObjC.classes.QNSView; + + if (QNSView) { + console.log("✓ Found QNSView class"); + + // Hook方法 + const handleKeyEventMethod = QNSView['- handleKeyEvent:eventType:']; + if (handleKeyEventMethod) { + Interceptor.attach(handleKeyEventMethod.implementation, { + onEnter: function(args) { + console.log("\n[QNSView Hook]"); + const event = new ObjC.Object(args[2]); + const eventType = args[3]; + console.log(`Event Type (a4): ${eventType}`); + console.log(`KeyCode: ${event.keyCode()}`); + console.log(`Characters: ${event.characters()}`); } + }); + console.log("✓ QNSView handleKeyEvent hooked"); + } + } +} - console.log(`\n[+] arg${i} ${args[i]}`, args[i].compare(ptr("0x600000000000")) >= 0, args[i].compare(ptr("0x700000000000")) < 0); - if (args[i].compare(ptr("0x600000000000")) >= 0 && args[i].compare(ptr("0x700000000000")) < 0) { - console.log(`\n[+] enter arg${i} ${args[i]}`); - const buf = args[i].readByteArray(128) - if (!buf) { - continue; - } - let s = ""; - const u8 = new Uint8Array(buf); - for (let b of u8) { - if (b >= 0x20 && b <= 0x7E) { - s += String.fromCharCode(b); - } else { - s += "."; - } - } +// 2. 发送回车键的函数 +function sendEnterKey() { + try { + console.log("\n=== 发送回车键 ==="); - console.log("show s is", s); - } - } catch (e) { - console.log("Enter Error:", e); - } + const NSApplication = ObjC.classes.NSApplication; + const NSEvent = ObjC.classes.NSEvent; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) { + console.log("❌ 没有找到活动窗口"); + return; } + console.log(`窗口: ${keyWindow}`); - }, + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') { + return view; + } - onLeave(retval) { - console.log("===== sub_105808800 LEAVE ====="); - console.log("Return value:", retval); - // try { - // console.log("Return hexdump:"); - // console.log(hexdump(retval, { - // offset: 0, - // length: 128 - // })); - // } catch (_) { - // } + try { + const subviews = view.subviews(); + const count = subviews.count(); + for (let i = 0; i < count; i++) { + const subview = subviews.objectAtIndex_(i); + const found = findQNSView(subview); + if (found) return found; + } + } catch (e) { + // 忽略错误 + } + return null; + } + + const contentView = keyWindow.contentView(); + const qnsView = findQNSView(contentView); + + if (!qnsView) { + console.log("❌ 没有找到QNSView,使用备用方法"); + sendEnterKeyAlternative(); + return; + } + + console.log(`✓ 找到QNSView: ${qnsView}`); + + // 创建回车键按下事件 + const keyDownEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyDown, // type = 10 (按下) + { x: 100, y: 100 }, // 位置 + 0, // 修饰键 + Date.now() / 1000, // 时间戳(秒) + keyWindow.windowNumber(), // 窗口编号 + NULL, // 上下文 + '\r', // 字符(回车) + '\r', // 忽略修饰键的字符 + 0, // 是否重复 + kVK_Return // 键码36=回车 + ); + + // 创建回车键释放事件 + const keyUpEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyUp, // type = 11 (释放) + { x: 100, y: 100 }, // 位置 + 0, // 修饰键 + (Date.now() / 1000) + 0.05, // 稍后的时间 + keyWindow.windowNumber(), // 窗口编号 + NULL, // 上下文 + '\r', // 字符 + '\r', // 忽略修饰键的字符 + 0, // 是否重复 + kVK_Return // 键码 + ); + + // 发送按键按下(根据逆向分析,a4=6可能是按键按下) + console.log("发送回车键按下..."); + qnsView.handleKeyEvent_eventType_(keyDownEvent, 6); + + // 延迟发送按键释放 + setTimeout(() => { + console.log("发送回车键释放..."); + qnsView.handleKeyEvent_eventType_(keyUpEvent, 7); // 猜测7是按键释放 + }, 50); + + console.log("✓ 回车键发送完成"); + + } catch (error) { + console.error(`❌ 发送回车键失败: ${error}`); } -}); +} +// 3. 备用方法:使用CGEvent +function sendEnterKeyAlternative() { + try { + console.log("尝试使用CGEvent发送回车键..."); + + const CGEventCreateKeyboardEvent = Module.findExportByName('CoreGraphics', 'CGEventCreateKeyboardEvent'); + const CGEventPost = Module.findExportByName('CoreGraphics', 'CGEventPost'); + + if (CGEventCreateKeyboardEvent && CGEventPost) { + const kCGHIDEventTap = 0; + + // 发送回车键按下 + const keyDown = new NativeFunction(CGEventCreateKeyboardEvent, 'pointer', ['pointer', 'uint64', 'bool'])( + NULL, + kVK_Return, + true + ); + new NativeFunction(CGEventPost, 'void', ['uint32', 'pointer'])(kCGHIDEventTap, keyDown); + + // 延迟发送释放 + setTimeout(() => { + const keyUp = new NativeFunction(CGEventCreateKeyboardEvent, 'pointer', ['pointer', 'uint64', 'bool'])( + NULL, + kVK_Return, + false + ); + new NativeFunction(CGEventPost, 'void', ['uint32', 'pointer'])(kCGHIDEventTap, keyUp); + console.log("✓ CGEvent 回车键发送完成"); + }, 50); + } else { + console.log("❌ CGEvent API 不可用"); + } + } catch (error) { + console.error(`❌ CGEvent方法失败: ${error}`); + } +} + +// 4. 发送文本"123"的函数 +function sendText123() { + try { + console.log("\n=== 发送文本 '123' ==="); + + const NSApplication = ObjC.classes.NSApplication; + const NSEvent = ObjC.classes.NSEvent; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) { + console.log("❌ 没有找到活动窗口"); + return; + } + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') return view; + try { + const subviews = view.subviews(); + const count = subviews.count(); + for (let i = 0; i < count; i++) { + const found = findQNSView(subviews.objectAtIndex_(i)); + if (found) return found; + } + } catch (e) {} + return null; + } + + const qnsView = findQNSView(keyWindow.contentView()); + + if (!qnsView) { + console.log("❌ 没有找到QNSView"); + return; + } + + // 要发送的字符和对应的键码 + const textToSend = [ + { char: '1', keyCode: 18 }, + { char: '2', keyCode: 19 }, + { char: '3', keyCode: 20 } + ]; + + // 逐个发送字符 + textToSend.forEach((item, index) => { + setTimeout(() => { + try { + console.log(`发送字符: ${item.char}`); + + // 创建按键按下事件 + const keyDownEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyDown, + { x: 100, y: 100 }, + 0, + Date.now() / 1000, + keyWindow.windowNumber(), + NULL, + item.char, + item.char, + 0, + item.keyCode + ); + + // 创建按键释放事件 + const keyUpEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyUp, + { x: 100, y: 100 }, + 0, + (Date.now() / 1000) + 0.03, + keyWindow.windowNumber(), + NULL, + item.char, + item.char, + 0, + item.keyCode + ); + + // 发送按键按下 + qnsView.handleKeyEvent_eventType_(keyDownEvent, 6); + + // 延迟发送按键释放 + setTimeout(() => { + qnsView.handleKeyEvent_eventType_(keyUpEvent, 7); + }, 30); + + } catch (error) { + console.error(`发送字符 ${item.char} 失败: ${error}`); + } + }, index * 100); // 每个字符间隔100ms + }); + + console.log("✓ 文本'123'发送中..."); + + } catch (error) { + console.error(`❌ 发送文本失败: ${error}`); + } +} + +// 5. 组合函数:先发送123,然后回车 +function send123AndEnter() { + console.log("\n=== 开始发送: 123 + 回车 ==="); + + // 先发送123 + sendText123(); + + // 延迟500ms后发送回车 + setTimeout(() => { + console.log("\n=== 发送回车键 ==="); + sendEnterKey(); + }, 500); +} + +// 6. 通用按键注入函数 +function injectKey(keyCode, eventType, characters = '') { + try { + const NSApplication = ObjC.classes.NSApplication; + const NSEvent = ObjC.classes.NSEvent; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) { + console.log("❌ 没有活动窗口"); + return false; + } + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') return view; + try { + const subviews = view.subviews(); + for (let i = 0; i < subviews.count(); i++) { + const found = findQNSView(subviews.objectAtIndex_(i)); + if (found) return found; + } + } catch (e) {} + return null; + } + + const qnsView = findQNSView(keyWindow.contentView()); + + if (qnsView) { + const event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + eventType, // 10=按下, 11=释放 + { x: 100, y: 100 }, + 0, + Date.now() / 1000, + keyWindow.windowNumber(), + NULL, + characters, + characters, + 0, + keyCode + ); + + // 根据逆向分析,a4参数:6可能是按下,7可能是释放 + const a4Param = eventType === NSEventTypeKeyDown ? 6 : 7; + qnsView.handleKeyEvent_eventType_(event, a4Param); + + console.log(`✓ 发送按键: keyCode=${keyCode}, type=${eventType}, a4=${a4Param}`); + return true; + } + + return false; + + } catch (error) { + console.error(`❌ 注入按键失败: ${error}`); + return false; + } +} + +// 7. 直接调用Qt事件发送(基于逆向分析) +function sendQtKeyEvent(keyCode, text, modifiers = 0) { + try { + console.log(`\n=== 直接发送Qt键盘事件: ${text} ===`); + + // 尝试找到QNSView并调用底层函数 + const NSApplication = ObjC.classes.NSApplication; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) return; + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') return view; + try { + const subviews = view.subviews(); + for (let i = 0; i < subviews.count(); i++) { + const found = findQNSView(subviews.objectAtIndex_(i)); + if (found) return found; + } + } catch (e) {} + return null; + } + + const qnsView = findQNSView(keyWindow.contentView()); + + if (qnsView && qnsView.handleKeyEvent) { + // 创建模拟的事件对象 + const fakeEvent = { + keyCode: function() { return keyCode; }, + characters: function() { return text; }, + charactersIgnoringModifiers: function() { return text; }, + timestamp: function() { return Date.now() / 1000; }, + modifierFlags: function() { return modifiers; }, + isARepeat: function() { return 0; } + }; + + // 包装成ObjC对象 + const eventWrapper = new ObjC.Object(fakeEvent); + + // 发送事件 + qnsView.handleKeyEvent_eventType_(eventWrapper, 6); + + console.log(`✓ Qt事件发送: ${text} (keyCode: ${keyCode})`); + } + + } catch (error) { + console.error(`❌ Qt事件发送失败: ${error}`); + } +} + +// 9. 创建交互式菜单 +function showMenu() { + console.log("\n" + "=".repeat(50)); + console.log("🎹 键盘注入器 - 纯JS版本"); + console.log("=".repeat(50)); + console.log("可用命令:"); + console.log("1. sendEnterKey() - 发送回车键"); + console.log("2. sendText123() - 发送文本 '123'"); + console.log("3. send123AndEnter() - 发送 '123' 然后回车"); + console.log("4. injectKey(36, 10) - 发送回车键按下"); + console.log("5. injectKey(36, 11) - 发送回车键释放"); + console.log("6. sendQtKeyEvent(18, '1') - 直接发送Qt事件"); + console.log("=".repeat(50)); + console.log("示例: 发送 '123' 然后回车:"); + console.log(" send123AndEnter()"); + console.log("=".repeat(50)); +} + +// 10. 自动执行(可选) +// 取消下面行的注释可以自动发送 +// setTimeout(send123AndEnter, 1000); + +// 显示菜单 +showMenu(); + +console.log("\n✅ 键盘注入器加载完成!"); +console.log("📝 输入命令开始注入键盘事件..."); \ No newline at end of file diff --git a/frida/send_key.js b/frida/send_key.js new file mode 100644 index 0000000..bf4f228 --- /dev/null +++ b/frida/send_key.js @@ -0,0 +1,419 @@ +// pure_js_keyboard_injector.js +console.log("Pure JS Keyboard Injector - Starting..."); + +// 常量定义 +const NSEventTypeKeyDown = 10; +const NSEventTypeKeyUp = 11; +const kVK_Return = 36; // 回车键 + +// 1. Hook QNSView的handleKeyEvent方法 +if (ObjC.available) { + const QNSView = ObjC.classes.QNSView; + + if (QNSView) { + console.log("✓ Found QNSView class"); + + // Hook方法 + const handleKeyEventMethod = QNSView['- handleKeyEvent:eventType:']; + if (handleKeyEventMethod) { + Interceptor.attach(handleKeyEventMethod.implementation, { + onEnter: function(args) { + console.log("\n[QNSView Hook]"); + const event = new ObjC.Object(args[2]); + const eventType = args[3]; + console.log(`Event Type (a4): ${eventType}`); + console.log(`KeyCode: ${event.keyCode()}`); + console.log(`Characters: ${event.characters()}`); + } + }); + console.log("✓ QNSView handleKeyEvent hooked"); + } + } +} + +// 2. 发送回车键的函数 +function sendEnterKey() { + try { + console.log("\n=== 发送回车键 ==="); + + const NSApplication = ObjC.classes.NSApplication; + const NSEvent = ObjC.classes.NSEvent; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) { + console.log("❌ 没有找到活动窗口"); + return; + } + + console.log(`窗口: ${keyWindow}`); + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') { + return view; + } + + try { + const subviews = view.subviews(); + const count = subviews.count(); + for (let i = 0; i < count; i++) { + const subview = subviews.objectAtIndex_(i); + const found = findQNSView(subview); + if (found) return found; + } + } catch (e) { + // 忽略错误 + } + return null; + } + + const contentView = keyWindow.contentView(); + const qnsView = findQNSView(contentView); + + if (!qnsView) { + console.log("❌ 没有找到QNSView,使用备用方法"); + sendEnterKeyAlternative(); + return; + } + + console.log(`✓ 找到QNSView: ${qnsView}`); + + // 创建回车键按下事件 + const keyDownEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyDown, // type = 10 (按下) + { x: 100, y: 100 }, // 位置 + 0, // 修饰键 + Date.now() / 1000, // 时间戳(秒) + keyWindow.windowNumber(), // 窗口编号 + NULL, // 上下文 + '\r', // 字符(回车) + '\r', // 忽略修饰键的字符 + 0, // 是否重复 + kVK_Return // 键码36=回车 + ); + + // 创建回车键释放事件 + const keyUpEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyUp, // type = 11 (释放) + { x: 100, y: 100 }, // 位置 + 0, // 修饰键 + (Date.now() / 1000) + 0.05, // 稍后的时间 + keyWindow.windowNumber(), // 窗口编号 + NULL, // 上下文 + '\r', // 字符 + '\r', // 忽略修饰键的字符 + 0, // 是否重复 + kVK_Return // 键码 + ); + + // 发送按键按下(根据逆向分析,a4=6可能是按键按下) + console.log("发送回车键按下..."); + qnsView.handleKeyEvent_eventType_(keyDownEvent, 6); + + // 延迟发送按键释放 + setTimeout(() => { + console.log("发送回车键释放..."); + qnsView.handleKeyEvent_eventType_(keyUpEvent, 7); // 猜测7是按键释放 + }, 50); + + console.log("✓ 回车键发送完成"); + + } catch (error) { + console.error(`❌ 发送回车键失败: ${error}`); + } +} + +// 3. 备用方法:使用CGEvent +function sendEnterKeyAlternative() { + try { + console.log("尝试使用CGEvent发送回车键..."); + + const CGEventCreateKeyboardEvent = Module.findExportByName('CoreGraphics', 'CGEventCreateKeyboardEvent'); + const CGEventPost = Module.findExportByName('CoreGraphics', 'CGEventPost'); + + if (CGEventCreateKeyboardEvent && CGEventPost) { + const kCGHIDEventTap = 0; + + // 发送回车键按下 + const keyDown = new NativeFunction(CGEventCreateKeyboardEvent, 'pointer', ['pointer', 'uint64', 'bool'])( + NULL, + kVK_Return, + true + ); + new NativeFunction(CGEventPost, 'void', ['uint32', 'pointer'])(kCGHIDEventTap, keyDown); + + // 延迟发送释放 + setTimeout(() => { + const keyUp = new NativeFunction(CGEventCreateKeyboardEvent, 'pointer', ['pointer', 'uint64', 'bool'])( + NULL, + kVK_Return, + false + ); + new NativeFunction(CGEventPost, 'void', ['uint32', 'pointer'])(kCGHIDEventTap, keyUp); + console.log("✓ CGEvent 回车键发送完成"); + }, 50); + } else { + console.log("❌ CGEvent API 不可用"); + } + } catch (error) { + console.error(`❌ CGEvent方法失败: ${error}`); + } +} + +// 4. 发送文本"123"的函数 +function sendText123() { + try { + console.log("\n=== 发送文本 '123' ==="); + + const NSApplication = ObjC.classes.NSApplication; + const NSEvent = ObjC.classes.NSEvent; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) { + console.log("❌ 没有找到活动窗口"); + return; + } + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') return view; + try { + const subviews = view.subviews(); + const count = subviews.count(); + for (let i = 0; i < count; i++) { + const found = findQNSView(subviews.objectAtIndex_(i)); + if (found) return found; + } + } catch (e) {} + return null; + } + + const qnsView = findQNSView(keyWindow.contentView()); + + if (!qnsView) { + console.log("❌ 没有找到QNSView"); + return; + } + + // 要发送的字符和对应的键码 + const textToSend = [ + { char: '1', keyCode: 18 }, + { char: '2', keyCode: 19 }, + { char: '3', keyCode: 20 } + ]; + + // 逐个发送字符 + textToSend.forEach((item, index) => { + setTimeout(() => { + try { + console.log(`发送字符: ${item.char}`); + + // 创建按键按下事件 + const keyDownEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyDown, + { x: 100, y: 100 }, + 0, + Date.now() / 1000, + keyWindow.windowNumber(), + NULL, + item.char, + item.char, + 0, + item.keyCode + ); + + // 创建按键释放事件 + const keyUpEvent = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + NSEventTypeKeyUp, + { x: 100, y: 100 }, + 0, + (Date.now() / 1000) + 0.03, + keyWindow.windowNumber(), + NULL, + item.char, + item.char, + 0, + item.keyCode + ); + + // 发送按键按下 + qnsView.handleKeyEvent_eventType_(keyDownEvent, 6); + + // 延迟发送按键释放 + setTimeout(() => { + qnsView.handleKeyEvent_eventType_(keyUpEvent, 7); + }, 30); + + } catch (error) { + console.error(`发送字符 ${item.char} 失败: ${error}`); + } + }, index * 100); // 每个字符间隔100ms + }); + + console.log("✓ 文本'123'发送中..."); + + } catch (error) { + console.error(`❌ 发送文本失败: ${error}`); + } +} + +// 5. 组合函数:先发送123,然后回车 +function send123AndEnter() { + console.log("\n=== 开始发送: 123 + 回车 ==="); + + // 先发送123 + sendText123(); + + // 延迟500ms后发送回车 + setTimeout(() => { + console.log("\n=== 发送回车键 ==="); + sendEnterKey(); + }, 500); +} + +// 6. 通用按键注入函数 +function injectKey(keyCode, eventType, characters = '') { + try { + const NSApplication = ObjC.classes.NSApplication; + const NSEvent = ObjC.classes.NSEvent; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) { + console.log("❌ 没有活动窗口"); + return false; + } + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') return view; + try { + const subviews = view.subviews(); + for (let i = 0; i < subviews.count(); i++) { + const found = findQNSView(subviews.objectAtIndex_(i)); + if (found) return found; + } + } catch (e) {} + return null; + } + + const qnsView = findQNSView(keyWindow.contentView()); + + if (qnsView) { + const event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( + eventType, // 10=按下, 11=释放 + { x: 100, y: 100 }, + 0, + Date.now() / 1000, + keyWindow.windowNumber(), + NULL, + characters, + characters, + 0, + keyCode + ); + + // 根据逆向分析,a4参数:6可能是按下,7可能是释放 + const a4Param = eventType === NSEventTypeKeyDown ? 6 : 7; + qnsView.handleKeyEvent_eventType_(event, a4Param); + + console.log(`✓ 发送按键: keyCode=${keyCode}, type=${eventType}, a4=${a4Param}`); + return true; + } + + return false; + + } catch (error) { + console.error(`❌ 注入按键失败: ${error}`); + return false; + } +} + +// 7. 直接调用Qt事件发送(基于逆向分析) +function sendQtKeyEvent(keyCode, text, modifiers = 0) { + try { + console.log(`\n=== 直接发送Qt键盘事件: ${text} ===`); + + // 尝试找到QNSView并调用底层函数 + const NSApplication = ObjC.classes.NSApplication; + const app = NSApplication.sharedApplication(); + const keyWindow = app.keyWindow(); + + if (!keyWindow) return; + + // 查找QNSView + function findQNSView(view) { + if (view.$className === 'QNSView') return view; + try { + const subviews = view.subviews(); + for (let i = 0; i < subviews.count(); i++) { + const found = findQNSView(subviews.objectAtIndex_(i)); + if (found) return found; + } + } catch (e) {} + return null; + } + + const qnsView = findQNSView(keyWindow.contentView()); + + if (qnsView && qnsView.handleKeyEvent) { + // 创建模拟的事件对象 + const fakeEvent = { + keyCode: function() { return keyCode; }, + characters: function() { return text; }, + charactersIgnoringModifiers: function() { return text; }, + timestamp: function() { return Date.now() / 1000; }, + modifierFlags: function() { return modifiers; }, + isARepeat: function() { return 0; } + }; + + // 包装成ObjC对象 + const eventWrapper = new ObjC.Object(fakeEvent); + + // 发送事件 + qnsView.handleKeyEvent_eventType_(eventWrapper, 6); + + console.log(`✓ Qt事件发送: ${text} (keyCode: ${keyCode})`); + } + + } catch (error) { + console.error(`❌ Qt事件发送失败: ${error}`); + } +} + +// 8. 导出函数到全局 +global.sendEnterKey = sendEnterKey; +global.sendText123 = sendText123; +global.send123AndEnter = send123AndEnter; +global.injectKey = injectKey; +global.sendQtKeyEvent = sendQtKeyEvent; + +// 9. 创建交互式菜单 +function showMenu() { + console.log("\n" + "=".repeat(50)); + console.log("🎹 键盘注入器 - 纯JS版本"); + console.log("=".repeat(50)); + console.log("可用命令:"); + console.log("1. sendEnterKey() - 发送回车键"); + console.log("2. sendText123() - 发送文本 '123'"); + console.log("3. send123AndEnter() - 发送 '123' 然后回车"); + console.log("4. injectKey(36, 10) - 发送回车键按下"); + console.log("5. injectKey(36, 11) - 发送回车键释放"); + console.log("6. sendQtKeyEvent(18, '1') - 直接发送Qt事件"); + console.log("=".repeat(50)); + console.log("示例: 发送 '123' 然后回车:"); + console.log(" send123AndEnter()"); + console.log("=".repeat(50)); +} + +// 10. 自动执行(可选) +// 取消下面行的注释可以自动发送 +// setTimeout(send123AndEnter, 1000); + +// 显示菜单 +showMenu(); + +console.log("\n✅ 键盘注入器加载完成!"); +console.log("📝 输入命令开始注入键盘事件..."); \ No newline at end of file diff --git a/function.md b/function.md index 9c74967..06d462b 100644 --- a/function.md +++ b/function.md @@ -1,12 +1,17 @@ - - - 装填数据,发到jobqueue是他们的目地,我感觉重点是在这里 sub_1032003B0 -> sub_1024803E4 -> sub_1024C6354 可能是统一入口 sub_10237997C image_handler.cc -sub_1023E73E8 text_handler.cc -sub_102363BB0 file_handler.cc +sub_1023E73E8 text_handler.cc ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_ +sub_102363BB0 file_handler.cc sub_100400950 装填消息 +每次都是这个175ED5D10指针装填数据 + +sub_10250D878 是整体的发消息入口有多个阶段来自这里 +StartSendMessageSerial sub_1024C4CB4 +CoSendMessageWithUploadInfo sub_1023E8108 +CoAddSendMessageToDb sub_1023C09D0 +CoPrepareShowSendMessage sub_1023BC4E0 + 键盘事件触发 @@ -15,15 +20,15 @@ sub_100662CC4 处理消息的关键函数 sub_100662CC4 -> sub_100668580 -> sub_10064DD2C sub_100662CC4 -> sub_10063F318 -> sub_1006DDDBC 处理发送消息结构体 sub_1006DDDBC 消息体在这个函数 X1的第一个指针式utf16的发送值,X0不知道是啥,并且X1都是一个地址,追查一下地址 16FDFD688 -sub_105A25B30 获取值的函数 -16FDFD6F0 +100179130 可以增加字段 和 sub_105A25B30 获取值的函数 +16FDFD6F0 指针 + 真正的发送阶段 -sub_10250D878 是整体的发消息入口有多个阶段来自这里 -StartSendMessageSerial sub_1024C4CB4 -CoSendMessageWithUploadInfo sub_1023E8108 -CoAddSendMessageToDb sub_1023C09D0 -CoPrepareShowSendMessage sub_1023BC4E0 sub_1024C7FB4 -> sub_102481CA0 -> sub_105268848 -sub_1024C7FB4 sendMessage 入口 \ No newline at end of file +sub_1024C7FB4 sendMessage 入口 + + +sub_1023BB1F0->sub_1023990EC->realSendMsg_10239B4C4 发消息的真正函数 +又调用了sendMsgCoroutine_105261924 进行一步操作但是这个好像是只有发消息才会走的核心coroutine \ No newline at end of file diff --git a/idapro/analysis_x.py b/idapro/analysis_x.py index a0afb38..0f2187d 100644 --- a/idapro/analysis_x.py +++ b/idapro/analysis_x.py @@ -116,9 +116,7 @@ def print_register_struct(reg_name, struct_size=64, max_depth=3): :param max_depth: 最大递归深度 """ print(f"\n{'='*60}") - print(f"分析寄存器: {reg_name}") - print(f"结构体大小: {struct_size} 字节") - print(f"{'='*60}") + print(f"分析寄存器: {reg_name} 结构体大小: {struct_size} 字节") traceMap = {} try: @@ -195,4 +193,4 @@ analyze_all_args() example_usage() """ -print_register_struct("X1", 128, 3) \ No newline at end of file +print_register_struct("X0", 128, 4) \ No newline at end of file diff --git a/idapro/keyword_dump.py b/idapro/keyword_dump.py new file mode 100644 index 0000000..c6da3ca --- /dev/null +++ b/idapro/keyword_dump.py @@ -0,0 +1,84 @@ +import ida_bytes +import ida_dbg + + +def is_printable_string(data): + if not data: + return False, "" + + result = "" + is_str = True + for byte in data[:-1]: + if 32 <= byte <= 126: + result += chr(byte) + else: + result += "." + is_str = False + + return is_str, result + + +def print_register_struct(reg_name, struct_size=64, max_depth=3): + # 获取寄存器值 + try: + reg_value = ida_dbg.get_reg_val(reg_name) + except: + print(f"无法读取寄存器 {reg_name}") + return + + # print(f"寄存器 {reg_name} = 0x{reg_value:X} 结构体大小: {struct_size} 字节 最大深度: {max_depth}") + + if reg_value == 0: + print("寄存器值为0 (NULL)") + return + + # 开始递归打印 + print_str(reg_value, struct_size, max_depth, 0) + print(f"{'=' * 60}") + + +def print_str(addr, struct_size, max_depth, current_depth): + """递归打印字符串或指针内容""" + if current_depth >= max_depth: + print(f"达到最大递归深度 {max_depth}") + return + + indent = " " * current_depth + + try: + # 尝试读取最多256字节 + max_read = min(256, struct_size) + data = bytearray() + + for i in range(max_read): + byte = ida_bytes.get_byte(addr + i) + data.append(byte) + if byte == 0: # 遇到null终止符 + break + + # 检查是否为可打印字符串 + is_str, str_val = is_printable_string(data) + + if is_str and len(str_val) > 0: + # if str_val.find('http') != -1: + print(f"0x{addr:X} {indent}字符串: \"{str_val}\"") + else: + # 不是字符串,尝试作为指针处理 + try: + ptr_value = ida_bytes.get_qword(addr) + # 检查指针是否有效(非空且对齐) + if ptr_value != 0 and ptr_value % 8 == 0: + # 递归检查下一层 + print_str(ptr_value, struct_size, max_depth, current_depth + 1) + else: + # 显示原始数据 + print(f"0x{addr:X} {indent}原始数据: {data.hex()}") + except: + # 无法读取指针,显示原始数据 + print(f"0x{addr:X} {indent}原始数据: {data.hex()}") + + except Exception as e: + print(f"{indent}读取地址 0x{addr:X} 失败: {e}") + + +print_register_struct("X2", 64, 5) diff --git a/idapro/trigger_func.py b/idapro/trigger_func.py new file mode 100644 index 0000000..71a983c --- /dev/null +++ b/idapro/trigger_func.py @@ -0,0 +1,76 @@ +import ida_dbg +import ida_kernwin +import ida_bytes +import random + +# --- 配置 --- +TARGET_FUNC_EA = 0x1006DDE48 # 目标函数的虚拟地址 (EA) +STRUCT_SIZE = 4 * 8 # 4 个 8 字节指针 = 32 字节 + +# 假设您已经知道这 4 个指针应该指向的目标地址 +# 这里的地址应该是目标程序内存中已有的有效地址 +PTR1_TARGET_VA = 0x108276190 +PTR2_TARGET_VA = 0x1082f6288 +PTR3_TARGET_VA = 0x0000600000614820 # 对应内存中的 P3 +PTR4_TARGET_VA = 0x0000000032AAAAA7 # 对应内存中的 P4 (如果它是地址的话) + +def create_and_set_pointer_arg(): + """在目标进程中创建结构体,并将其地址设置为 X0 参数""" + + # 1. 在目标进程中分配内存 (例如 32 字节) + # flag 0x01表示分配内存,返回分配内存的起始地址 + struct_addr = ida_dbg.alloc_dealloc_memory(STRUCT_SIZE, 0x01) + if struct_addr == ida_idaapi.BADADDR: + ida_kernwin.msg("错误:无法在目标进程中分配内存。") + return + + ida_kernwin.msg(f"已在 0x{struct_addr:X} 处分配 {STRUCT_SIZE} 字节内存。") + + # 2. 写入 4 个指针的值 (QWORD - 8 字节) + try: + ida_bytes.patch_qword(struct_addr + 0, PTR1_TARGET_VA) + ida_bytes.patch_qword(struct_addr + 8, PTR2_TARGET_VA) + ida_bytes.patch_qword(struct_addr + 16, PTR3_TARGET_VA) + ida_bytes.patch_qword(struct_addr + 24, PTR4_TARGET_VA) + ida_kernwin.msg("已成功写入 4 个指针到新分配的内存。") + except Exception as e: + ida_kernwin.msg(f"写入内存失败: {e}") + ida_dbg.alloc_dealloc_memory(struct_addr, 0) # 清理内存 + return + + # 3. 设置 X0 寄存器为这个结构体的地址 + ida_dbg.set_reg("X0", struct_addr) + ida_kernwin.msg(f"已设置 X0 = 结构体指针地址: 0x{struct_addr:X}") + + # 返回地址,供后续调用函数使用 + return struct_addr + +# --- 完整的调用函数 --- +def remote_call_with_pointer(): + # ... (省略检查和上下文保存代码,与之前相同) + + # 1. 创建并设置指针参数 + arg_x0_ptr = create_and_set_pointer_arg() + if arg_x0_ptr is None: + return + + # 2. 设置其他参数 (如果需要) + # ida_dbg.set_reg("X1", other_arg_value) + + # 3. 执行函数调用 (X0 已经设置完毕) + ida_dbg.call_user_func(TARGET_FUNC_EA, [], ida_dbg.CUF_WAIT) + + # 4. 获取返回值 (X0) + return_value = ida_dbg.get_reg("X0") + + # 5. 恢复寄存器上下文 (与之前相同) + # ... + + # 6. 清理新分配的内存 + ida_dbg.alloc_dealloc_memory(arg_x0_ptr, 0) # flag 0x00表示释放内存 + ida_kernwin.msg(f"已释放分配的内存: 0x{arg_x0_ptr:X}") + + ida_kernwin.msg(f"返回值 (X0): {return_value} (0x{return_value:X})") + ida_dbg.run_requests() + +# remote_call_with_pointer() \ No newline at end of file