From b4a8be7d5f9c604d3456b43c5e640cf030b54cdf Mon Sep 17 00:00:00 2001 From: yincong Date: Mon, 2 Mar 2026 11:56:50 +0800 Subject: [PATCH] add audio --- frida/receiver.js | 50 ++++-- frida/send_key.js | 419 -------------------------------------------- onebot/go.mod | 11 +- onebot/go.sum | 26 +++ onebot/http.go | 27 ++- onebot/main.go | 23 ++- onebot/script.js | 80 +++++++-- onebot/utils.go | 61 +++++++ onebot/websocket.go | 44 +++-- 9 files changed, 258 insertions(+), 483 deletions(-) delete mode 100644 frida/send_key.js diff --git a/frida/receiver.js b/frida/receiver.js index 200fa68..4f34bc6 100644 --- a/frida/receiver.js +++ b/frida/receiver.js @@ -9,7 +9,8 @@ var buf2RespAddr = baseAddr.add(0x37173B0) function setReceiver() { // 3. 开始拦截 - Interceptor.attach(buf2RespAddr, { + Interceptor.attach(buf2RespAddr, + { onEnter: function (args) { const currentPtr = this.context.x1; let start = 0x1e; @@ -52,23 +53,13 @@ function setReceiver() { var msgType = "private" var groupId = "" var senderUser = sender - var messages = []; var senderNickname = "" + var messages = getMessages(content, sender, mediaContent); if (sender.includes("@chatroom")) { msgType = "group" groupId = sender - let splitIndex = content.indexOf(':') - let pureContent = content.substring(splitIndex + 1).trim(); - const parts = pureContent.split('\u2005'); - for (let part of parts) { - part = part.trim(); - if (!part.startsWith("@")) { - messages.push({type: "text", data: {text: part}}); - } - } - const sendUserStart = content.indexOf('wxid_') senderUser = content.substring(sendUserStart, splitIndex).trim(); @@ -102,7 +93,6 @@ function setReceiver() { if (!senderNickname) { senderNickname = sender } - messages.push({type: "text", data: {text: content}}); } send({ @@ -119,7 +109,6 @@ function setReceiver() { sender: {user_id: senderUser, nickname: senderNickname}, msgsource: xml, raw_message: content, - // media: mediaContent, show_content:userContent }) }, @@ -130,6 +119,39 @@ function setReceiver() { // 使用 setImmediate 确保在模块加载后执行 setImmediate(setReceiver) +function getMessages(content, sender, mediaContent) { + var messages = []; + if (sender.includes("@chatroom")) { + let splitIndex = content.indexOf(':') + let pureContent = content.substring(splitIndex + 1).trim(); + const parts = pureContent.split('\u2005'); + for (let part of parts) { + part = part.trim(); + if (part.startsWith(" { - 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/onebot/go.mod b/onebot/go.mod index 823cd3e..9d2f0a6 100644 --- a/onebot/go.mod +++ b/onebot/go.mod @@ -5,6 +5,15 @@ go 1.25.0 require ( github.com/frida/frida-go v1.0.0 github.com/gorilla/websocket v1.5.3 + github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea ) -require github.com/google/uuid v1.6.0 // indirect +require ( + github.com/google/uuid v1.6.0 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + golang.org/x/sys v0.0.0-20201126233918-771906719818 // indirect + modernc.org/libc v1.8.1 // indirect + modernc.org/mathutil v1.2.2 // indirect + modernc.org/memory v1.0.4 // indirect +) diff --git a/onebot/go.sum b/onebot/go.sum index 4368817..bf0c1db 100644 --- a/onebot/go.sum +++ b/onebot/go.sum @@ -1,6 +1,32 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frida/frida-go v1.0.0 h1:Xlq1CB8QSAC6zbOFdjCX0oK8RjQGtdl2yATbUQKROwo= github.com/frida/frida-go v1.0.0/go.mod h1:O8Dg1YBGfQsBEL1a8x3GURw/JllJrcuvg78ga2OgdM4= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= +modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= diff --git a/onebot/http.go b/onebot/http.go index b2bc3b8..7c6897e 100644 --- a/onebot/http.go +++ b/onebot/http.go @@ -10,6 +10,7 @@ import ( "io" "log" "net/http" + "runtime/debug" "strings" "time" ) @@ -73,11 +74,11 @@ func sendHandler(w http.ResponseWriter, r *http.Request) { func SendHttpReq(msg map[string]interface{}) { defer func() { if r := recover(); r != nil { - log.Printf("panic: %v\n", r) + log.Printf("panic: %v, %v\n", r, string(debug.Stack())) } }() - time.Sleep(time.Duration(config.SendInterval) * time.Second) + time.Sleep(time.Duration(config.SendInterval) * time.Millisecond) // 这里处理你的 X1 数据 jsonData, err := json.Marshal(msg["payload"]) if err != nil { @@ -86,18 +87,16 @@ func SendHttpReq(msg map[string]interface{}) { } fmt.Printf("发送数据: %s\n", string(jsonData)) - if myWechatId == "" { - m := new(WechatMessage) - err = json.Unmarshal(jsonData, m) - if err != nil { - log.Printf("解析消息失败: %v\n", err) - return - } - myWechatId = m.SelfID - - if m.GroupId != "" { - userID2NicknameMap.Store(m.GroupId+"_"+m.UserID, m.Sender.Nickname) - } + m := new(WechatMessage) + err = json.Unmarshal(jsonData, m) + if err != nil { + log.Printf("解析消息失败: %v\n", err) + return + } + myWechatId = m.SelfID + + if m.GroupId != "" { + userID2NicknameMap.Store(m.GroupId+"_"+m.UserID, m.Sender.Nickname) } // 4. 创建 POST 请求 diff --git a/onebot/main.go b/onebot/main.go index 41e2a19..88a6a3c 100644 --- a/onebot/main.go +++ b/onebot/main.go @@ -33,10 +33,14 @@ var ( ) type WechatMessage struct { - GroupId string `json:"group_id"` - SelfID string `json:"self_id"` - UserID string `json:"user_id"` - Sender *Sender `json:"sender"` + GroupId string `json:"group_id"` + SelfID string `json:"self_id"` + UserID string `json:"user_id"` + Sender *Sender `json:"sender"` + Time int64 `json:"time"` + PostType string `json:"post_type"` + MessageId string `json:"message_id"` + Message []*Message `json:"message"` } type Sender struct { @@ -65,10 +69,12 @@ type Message struct { } type SendRequestData struct { - Id string `json:"id"` - Text string `json:"text"` - File string `json:"file"` - QQ string `json:"qq"` + Id string `json:"id"` + Text string `json:"text"` + File string `json:"file"` + URL string `json:"url"` + QQ string `json:"qq"` + Media []byte `json:"media"` } type Config struct { @@ -139,6 +145,7 @@ func initFlag() { fmt.Println("ImagePath", config.ImagePath) fmt.Println("WechatConf", config.WechatConf) + EnsureDir("./audio") } func initFridaGadget() { diff --git a/onebot/script.js b/onebot/script.js index b42ce61..c33cf06 100644 --- a/onebot/script.js +++ b/onebot/script.js @@ -137,6 +137,13 @@ function getProtobufRawBytes(pBuffer, scanSize) { if (!found) finalResults.push(null); // 未找到该 Tag }); + + for (; i < uint8Array.length; i++) { + if (uint8Array[i] === 0x60 && i + 10 <= uint8Array.length) { + finalResults.push(uint8Array.slice(i+1, i+10)) + } + } + return finalResults; } @@ -205,6 +212,29 @@ function getCleanString(uint8Array) { return out; } +function protobufVarintToNumberString(uint8Array) { + let result = BigInt(0); + let shift = BigInt(0); + + for (let i = 0; i < uint8Array.length; i++) { + const byte = uint8Array[i]; + + // 1. 取出低 7 位并累加到结果中 + // (BigInt(byte & 0x7F) << shift) + result += BigInt(byte & 0x7F) << shift; + + // 2. 检查最高位 (MSB)。如果为 0,说明这个数字结束了 + if ((byte & 0x80) === 0) { + return result.toString(); + } + + // 3. 准备处理下一个 7 位 + shift += BigInt(7); + } + + return result.toString(); +} + function generateBytes(n) { // 生成随机字符串 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; @@ -1105,6 +1135,7 @@ function setReceiver() { const mediaContent = fields[3] const xml = fields[4] const userContent = fields[5] + const msgId = protobufVarintToNumberString(fields[6]) if (sender === "" || receiver === "" || content === "") { console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, { @@ -1119,23 +1150,13 @@ function setReceiver() { var msgType = "private" var groupId = "" var senderUser = sender - var messages = []; var senderNickname = "" + var messages = getMessages(content, sender, mediaContent); if (sender.includes("@chatroom")) { msgType = "group" groupId = sender - let splitIndex = content.indexOf(':') - let pureContent = content.substring(splitIndex + 1).trim(); - const parts = pureContent.split('\u2005'); - for (let part of parts) { - part = part.trim(); - if (!part.startsWith("@")) { - messages.push({type: "text", data: {text: part}}); - } - } - const sendUserStart = content.indexOf('wxid_') senderUser = content.substring(sendUserStart, splitIndex).trim(); @@ -1169,10 +1190,8 @@ function setReceiver() { if (!senderNickname) { senderNickname = sender } - messages.push({type: "text", data: {text: content}}); } - const msgId = generateAESKey() send({ time: Date.now(), post_type: "message", @@ -1194,6 +1213,41 @@ function setReceiver() { }); } + // 使用 setImmediate 确保在模块加载后执行 setImmediate(setReceiver) + +function getMessages(content, sender, mediaContent) { + var messages = []; + if (sender.includes("@chatroom")) { + let splitIndex = content.indexOf(':') + let pureContent = content.substring(splitIndex + 1).trim(); + const parts = pureContent.split('\u2005'); + for (let part of parts) { + part = part.trim(); + if (part.startsWith("