diff --git a/frida/file.js b/frida/file.js index 7f3c68c..6aace1e 100644 --- a/frida/file.js +++ b/frida/file.js @@ -154,9 +154,10 @@ var uploadVideoX1 = ptr(0); var videoIdAddr = ptr(0); var videoPathAddr1 = ptr(0) -var globalImageCdnKey = "3057020100044b30490201000204d0a7095d02032dd27902042dd56c71020469b3aa24042430313733373839312d323038382d343537392d393563382d6339353035663364643861300204051838010201000405004c54a100"; -var globalAesKey1 = "d65861f0926b34cd0439d44bced55843"; -var globalMd5Key = "8dd4755e12e052fa5647a883e6bf0783"; +var globalImageCdnKey = ""; +var globalAesKey1 = ""; +var globalMd5Key = ""; +var videoIdentity = "" // 发送消息的全局变量 var taskIdGlobal = 0x0 // 最好比较大,不和原始的微信消息重复 @@ -751,10 +752,10 @@ function attachProto() { const senderHeader = [0x1A, 0x13]; // sender 和 receiver 互换了,sender 是 wxid_ldftuhe36izg19 - const sender = stringToHexArray("wxid_ldftuhe36izg19"); + const sender = stringToHexArray(senderGlobal); const receiverHeader = [0x22, 0x13] // receiver 是 wxid_7wd1ece99f7i21 - const receiver = stringToHexArray("wxid_7wd1ece99f7i21") + const receiver = stringToHexArray(receiverGlobal) const randomId1 = [0x28, 0xac, 0x73, 0x30, 0xac, 0x73, 0x3a, 0x04, 0x08, 0x00, 0x12, 0x00] const type1 = [0x40, 0xe8, 0xf2, 0x6f] @@ -781,25 +782,28 @@ function attachProto() { const cdn2 = stringToHexArray(globalImageCdnKey) - const randomId6 = [0xA0, 0x01, 0xAC, 0x73, 0xA8, 0x01, 0xE8, 0x02, 0xB0, 0x01, 0xCB, 0x01, 0xBA, 0x01] + const randomId6 = [0xA0, 0x01, 0xAC, 0x73, 0xA8, 0x01, 0xE8, 0x02, 0xB0, 0x01, 0xCB, 0x01] - const aesKey1Header = [0x20] - const aesKey1 = stringToHexArray("af74113cef556244423ec1bb2c7d4027") + const aesKey1Header = [0xBA, 0x01, 0x20] + const aesKey1 = stringToHexArray(globalAesKey1) - const md5Header = [0xAA, 0x02, 0x20] - const me5Key = stringToHexArray("1f56e8e2af9fe27efe6bd531ebf9c1b3") + const md5Header = [0xd2, 0x01, 0x20] + const md5Key = stringToHexArray(globalMd5Key) + + const md5Header1 = [0xAA, 0x02, 0x20] + const md5Key1 = stringToHexArray(videoIdentity) const randomId7 = [0xB0, 0x02, 0x00] const md5Key2Header = [0x82, 0x03, 0x20] - const md5Key2 = stringToHexArray("76d3cfca722c979b75215c4201fb9da2") + const md5Key2 = stringToHexArray(globalMd5Key) const cdn3Header = [0x8A, 0x03, 0xB2, 0x01] const cdn3 = stringToHexArray(globalImageCdnKey) const randomId8 = [0x92, 0x03, 0x20] - const md5Key3 = stringToHexArray("e7b7b9879746dc2db52369d00317b542") + const md5Key3 = stringToHexArray(globalAesKey1) var left0 = [ 0x98, 0x03, 0xe8, 0xf2, 0x6f @@ -807,8 +811,8 @@ function attachProto() { const finalPayload = type.concat(msgId, cpHeader, imageCp, randomId, sysHeader, sys, msgIdHeader, receiverMsgId, senderHeader, sender, receiverHeader, receiver, randomId1, type1, randomId2, randomId3, randomId4, htmlHeader, html, - cdnHeader, cdn, aesKeyHeader, aesKey, randomId5, cdn2, randomId6, aesKey1Header, aesKey1, md5Header, me5Key, randomId7, - md5Key2Header, md5Key2, cdn3Header, cdn3, randomId8, md5Key3, left0) + cdnHeader, cdn, aesKeyHeader, aesKey, randomId5, cdn2, randomId6, aesKey1Header, aesKey1, md5Header, md5Key, md5Header1, + md5Key1, randomId7, md5Key2Header, md5Key2, cdn3Header, cdn3, randomId8, md5Key3, left0) videoProtoX1PayloadAddr.writeByteArray(finalPayload); console.log("[+] 视频Payload 已写入,长度: " + finalPayload.length); @@ -841,7 +845,7 @@ function triggerUploadImg(receiver, md5, imagePath) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40 0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 图片id // 0x48 - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 发送人 0x68 @@ -967,7 +971,7 @@ function triggerUploadVideo(receiver, md5, videoPath) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40 0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 图片id // 0x48 - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 发送人 0x68 @@ -1058,20 +1062,20 @@ function triggerUploadVideo(receiver, md5, videoPath) { patchString(uploadAesKeyAddr, generateAESKey()) patchString(videoPathAddr1, videoPath); - uploadImageX1.writeByteArray(payload); - uploadImageX1.writePointer(uploadFunc1Addr); - uploadImageX1.add(0x08).writePointer(uploadFunc2Addr); - uploadImageX1.add(0x48).writePointer(videoIdAddr); - uploadImageX1.add(0x68).writeUtf8String(receiver); - uploadImageX1.add(0xa8).writePointer(md5Addr); - uploadImageX1.add(0xe0).writePointer(videoPathAddr1); - uploadImageX1.add(0x110).writePointer(videoPathAddr1); - uploadImageX1.add(0x140).writePointer(videoPathAddr1); - uploadImageX1.add(0x1f8).writePointer(uploadAesKeyAddr); + uploadVideoX1.writeByteArray(payload); + uploadVideoX1.writePointer(uploadFunc1Addr); + uploadVideoX1.add(0x08).writePointer(uploadFunc2Addr); + uploadVideoX1.add(0x48).writePointer(videoIdAddr); + uploadVideoX1.add(0x68).writeUtf8String(receiver); + uploadVideoX1.add(0xa8).writePointer(md5Addr); + uploadVideoX1.add(0xe0).writePointer(videoPathAddr1); + uploadVideoX1.add(0x110).writePointer(videoPathAddr1); + uploadVideoX1.add(0x140).writePointer(videoPathAddr1); + uploadVideoX1.add(0x1f8).writePointer(uploadAesKeyAddr); const startUploadMedia = new NativeFunction(uploadImageAddr, 'int64', ['pointer', 'pointer']); - const result = startUploadMedia(uploadGlobalX0, uploadImageX1); + const result = startUploadMedia(uploadGlobalX0, uploadVideoX1); console.log("调用结果: " + result); } @@ -1099,8 +1103,9 @@ function patchCdnOnComplete() { try { const x2 = this.context.x2; const currentFileId = x2.add(0x20).readPointer().readUtf8String(); - const fileId = imageIdAddr.readUtf8String(); - if (currentFileId !== fileId) { + const imageFileId = imageIdAddr.readUtf8String(); + const videoFileId = videoIdAddr.readUtf8String() + if (currentFileId !== imageFileId && currentFileId !== videoFileId ) { console.log("[-] CndOnComplete x2: " + x2 + " currentFileId: " + currentFileId + " fileId: " + fileId); return } @@ -1108,9 +1113,10 @@ function patchCdnOnComplete() { globalImageCdnKey = x2.add(0x60).readPointer().readUtf8String(); globalAesKey1 = x2.add(0x78).readPointer().readUtf8String(); globalMd5Key = x2.add(0x90).readPointer().readUtf8String(); + videoIdentity = x2.add(0xf0).readPointer().readUtf8String(); const targetId = x2.add(0x40).readUtf8String(); console.log("X2" + x2 + "[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + - " globalMd5Key: " + globalMd5Key); + " globalMd5Key: " + globalMd5Key + " videoIdentity:" + videoIdentity); send({ type: "finish", }) @@ -1121,6 +1127,8 @@ function patchCdnOnComplete() { type: "upload_finish", target_id: targetId, }) + } else { + console.error("cdnKey or aesKey or md5key 为空") } } catch (e) { console.log("[-] Memory access error at onEnter: " + e); @@ -1135,9 +1143,10 @@ function attachGetCallbackFromWrapper() { Interceptor.attach(baseAddr.add(0x491348C), { onEnter: function (args) { const tmpFileId = this.context.x1.readPointer().readUtf8String(); - const fileId = imageIdAddr.readUtf8String(); - if (tmpFileId !== fileId) { - console.log("[+] GetCallbackFromWrapper tmpFileId: " + tmpFileId + " fileId: " + fileId); + const imageFileId = imageIdAddr.readUtf8String(); + const videoFileId = videoIdAddr.readUtf8String() + if (tmpFileId !== imageFileId && tmpFileId !== videoFileId ) { + console.log("[+] GetCallbackFromWrapper tmpFileId: " + tmpFileId + " imageFileId: " + imageFileId + " videoFileId:" + videoFileId); return } @@ -1150,17 +1159,19 @@ function attachGetCallbackFromWrapper() { Interceptor.attach(baseAddr.add(0x4913A88), { onEnter: function (args) { const tmpFileId = this.context.x1.readPointer().readUtf8String(); - const fileId = imageIdAddr.readUtf8String(); - if (tmpFileId !== fileId) { - console.log("[+] OnComplete tmpFileId: " + tmpFileId + " fileId: " + fileId); + const imageFileId = imageIdAddr.readUtf8String(); + const videoFileId = videoIdAddr.readUtf8String() + if (tmpFileId !== imageFileId && tmpFileId !== videoFileId ) { + console.log("[+] OnComplete tmpFileId: " + tmpFileId + " imageFileId: " + imageFileId + " videoFileId:" + videoFileId); return } - console.log("[+] OnComplete x8: " + this.context.x8); uploadCallback.add(0x30).writePointer(baseAddr.add(0x36BB9C0)); this.context.x8 = uploadCallback; + console.log("[+] OnComplete x8: " + this.context.x8); } }) + } setImmediate(attachGetCallbackFromWrapper); diff --git a/frida/receiver.js b/frida/receiver.js index a89119a..603e8b0 100644 --- a/frida/receiver.js +++ b/frida/receiver.js @@ -38,16 +38,6 @@ function setReceiver() { return } - let start = 0x1e; - let senderLen = currentPtr.add(start).readU8(); - if (senderLen !== 0x14 && senderLen !== 0x13) { - start = 0x1d; - let senderLen = currentPtr.add(start).readU8(); - if (senderLen !== 0x14 && senderLen !== 0x13) { - return - } - } - const x2 = this.context.x2.toInt32(); console.log(" [+] currentPtr: ", hexdump(currentPtr, { offset: 0, @@ -65,7 +55,8 @@ function setReceiver() { const userContent = fields[5] const msgId = protobufVarintToNumberString(fields[6]) - if (sender === "" || receiver === "" || content === "" || msgId === "") { + if (typeof sender !== "string" || sender === "" || typeof receiver !== "string" || receiver === "" || + typeof content !== "string" || content === "" || typeof msgId !== "string" || msgId === "") { console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, { length: x2, header: true, diff --git a/frida/video.js b/frida/video.js new file mode 100644 index 0000000..201e708 --- /dev/null +++ b/frida/video.js @@ -0,0 +1,48 @@ +// 1. 获取微信主模块的基地址 +var baseAddr = Process.getModuleByName("WeChat").base; +if (!baseAddr) { + console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); +} +console.log("[+] WeChat base address: " + baseAddr); + +var CndOnCompleteAddr = baseAddr.add(0x36BAFC0); +var videoCallbackFuncAddr = baseAddr.add(0x24C76C8); +var videoProtobufAddr = videoCallbackFuncAddr.add(0x54); + +function videoInit() { + Interceptor.attach(videoProtobufAddr, { + onEnter: function (args) { + + console.log("[+] 视频寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(this.context.x1, { + offset: 0, + length: 1028, + header: true, + ansi: true + })); + } + }) + + Interceptor.attach(CndOnCompleteAddr, { + onEnter: function (args) { + + try { + const x2 = this.context.x2; + const currentFileId = x2.add(0x20).readPointer().readUtf8String(); + const targetId = x2.add(0x40).readUtf8String(); + + const globalImageCdnKey = x2.add(0x60).readPointer().readUtf8String(); + const globalAesKey1 = x2.add(0x78).readPointer().readUtf8String(); + const globalMd5Key = x2.add(0x90).readPointer().readUtf8String(); + + const mp4Identity = x2.add(0xf0).readPointer().readUtf8String(); + + console.log("X2" + x2 + "[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + + " globalMd5Key: " + globalMd5Key + " currentFileId: " + currentFileId, " targetId:" + targetId + " mp4Identity:" + mp4Identity ); + } catch (e) { + console.log("[-] Memory access error at onEnter: " + e); + } + } + }) +} + +setImmediate(videoInit); diff --git a/onebot/script.js b/onebot/script.js index 288e9d0..897e774 100644 --- a/onebot/script.js +++ b/onebot/script.js @@ -146,7 +146,7 @@ function getProtobufRawBytes(pBuffer, scanSize) { for (; i < uint8Array.length; i++) { if (uint8Array[i] === 0x60 && i + 10 <= uint8Array.length) { - finalResults.push(uint8Array.slice(i+1, i+10)) + finalResults.push(uint8Array.slice(i + 1, i + 10)) } } @@ -258,7 +258,12 @@ function generateBytes(n) { // -------------------------全局变量分区------------------------- // 文本消息全局变量 -var textCallbackFuncAddr = baseAddr.add({{.textCallbackFuncAddr}}); +var textCallbackFuncAddr = baseAddr.add({ +{. + textCallbackFuncAddr +} +}) +; var protobufAddr = textCallbackFuncAddr.add(0x44); var patchTextProtobufAddr = textCallbackFuncAddr.add(0x20); var patchTextProtobufByte @@ -268,21 +273,51 @@ var textCgiAddr = ptr(0); var sendTextMessageAddr = ptr(0); var textMessageAddr = ptr(0); var textProtoX1PayloadAddr = ptr(0); -var sendMessageCallbackFunc = baseAddr.add({{.sendMessageCallbackFunc}}); +var sendMessageCallbackFunc = baseAddr.add({ +{. + sendMessageCallbackFunc +} +}) +; // 双方公共使用的地址 var triggerX1Payload; var triggerX0; -var req2bufEnterAddr = baseAddr.add({{.req2bufEnterAddr}}); -var req2bufExitAddr = baseAddr.add({{.req2bufExitAddr}}); -var sendFuncAddr = baseAddr.add({{.sendFuncAddr}}); +var req2bufEnterAddr = baseAddr.add({ +{. + req2bufEnterAddr +} +}) +; +var req2bufExitAddr = baseAddr.add({ +{. + req2bufExitAddr +} +}) +; +var sendFuncAddr = baseAddr.add({ +{. + sendFuncAddr +} +}) +; var insertMsgAddr = ptr(0); var sendMsgType = ""; -var buf2RespAddr = baseAddr.add({{.buf2RespAddr}}); +var buf2RespAddr = baseAddr.add({ +{. + buf2RespAddr +} +}) +; // 图片消息全局变量 -var imageCallbackFuncAddr = baseAddr.add({{.imageCallbackFuncAddr}}); +var imageCallbackFuncAddr = baseAddr.add({ +{. + imageCallbackFuncAddr +} +}) +; var imgProtobufAddr = imageCallbackFuncAddr.add(0x54); var patchImgProtobufFunc1 = imageCallbackFuncAddr.add(0x10); var patchImgProtobufFunc1Byte; @@ -291,17 +326,69 @@ var patchImgProtobufFunc2Byte; var imgProtobufDeleteAddr = imageCallbackFuncAddr.add(0x6c); var imgProtobufDeleteAddrByte; -var uploadImageAddr = baseAddr.add({{.uploadImageAddr}}); -var CndOnCompleteAddr = baseAddr.add({{.CndOnCompleteAddr}}); -var imgMessageCallbackFunc1 = baseAddr.add({{.imgMessageCallbackFunc1}}); -var uploadGetCallbackWrapperAddr = baseAddr.add({{.uploadGetCallbackWrapperAddr}}); -var uploadGetCallbackWrapperFuncAddr = baseAddr.add({{.uploadGetCallbackWrapperFuncAddr}}); -var uploadOnCompleteAddr = baseAddr.add({{.uploadOnCompleteAddr}}); -var uploadOnCompleteFuncAddr = baseAddr.add({{.uploadOnCompleteFuncAddr}}); -var downloadImagAddr = baseAddr.add({{.downloadImagAddr}}); -var startDownloadMedia = baseAddr.add({{.startDownloadMedia}}) -var downloadFileAddr = baseAddr.add({{.downloadFileAddr}}) -var downloadVideoAddr = baseAddr.add({{.downloadVideoAddr}}) +var uploadImageAddr = baseAddr.add({ +{. + uploadImageAddr +} +}) +; +var CndOnCompleteAddr = baseAddr.add({ +{. + CndOnCompleteAddr +} +}) +; +var imgMessageCallbackFunc1 = baseAddr.add({ +{. + imgMessageCallbackFunc1 +} +}) +; +var uploadGetCallbackWrapperAddr = baseAddr.add({ +{. + uploadGetCallbackWrapperAddr +} +}) +; +var uploadGetCallbackWrapperFuncAddr = baseAddr.add({ +{. + uploadGetCallbackWrapperFuncAddr +} +}) +; +var uploadOnCompleteAddr = baseAddr.add({ +{. + uploadOnCompleteAddr +} +}) +; +var uploadOnCompleteFuncAddr = baseAddr.add({ +{. + uploadOnCompleteFuncAddr +} +}) +; +var downloadImagAddr = baseAddr.add({ +{. + downloadImagAddr +} +}) +; +var startDownloadMedia = baseAddr.add({ +{. + startDownloadMedia +} +}) +var downloadFileAddr = baseAddr.add({ +{. + downloadFileAddr +} +}) +var downloadVideoAddr = baseAddr.add({ +{. + downloadVideoAddr +} +}) var downloadGlobalX0; var downloadFileX1 = ptr(0) @@ -574,8 +661,7 @@ function attachSendTextProto() { const htmlUpperPart = [0x3C, 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x3E] let atUserHeader = [] if (atUserGlobal) { - atUserHeader = atUserHeader.concat([0x3C, 0x61, 0x74, 0x75, 0x73, 0x65, 0x72, 0x6c, 0x69, 0x73, 0x74, 0x3e]). - concat(stringToHexArray(atUserGlobal)).concat([0x3C, 0x2F, 0x61, 0x74, 0x75, 0x73, 0x65, 0x72, 0x6C, 0x69, 0x73, 0x74, 0x3E]) + atUserHeader = atUserHeader.concat([0x3C, 0x61, 0x74, 0x75, 0x73, 0x65, 0x72, 0x6c, 0x69, 0x73, 0x74, 0x3e]).concat(stringToHexArray(atUserGlobal)).concat([0x3C, 0x2F, 0x61, 0x74, 0x75, 0x73, 0x65, 0x72, 0x6C, 0x69, 0x73, 0x74, 0x3E]) } const htmlLowerPart = [0x3C, 0x61, 0x6C, 0x6E, 0x6F, 0x64, 0x65, 0x3E, 0x3C, 0x66, 0x72, 0x3E, 0x31, @@ -592,9 +678,7 @@ function attachSendTextProto() { htmlUpperPart.length + atUserHeader.length + htmlLowerPart.length) // 合并数组 - const finalPayload = type.concat(valueLen).concat(receiverHeader).concat(receiverProto).concat(contentHeader). - concat(contentProto).concat(tsHeader).concat(tsBytes).concat(msgIdHeader).concat(msgId).concat(htmlHeader).concat(htmlUpperPart). - concat(atUserHeader).concat(htmlLowerPart); + const finalPayload = type.concat(valueLen).concat(receiverHeader).concat(receiverProto).concat(contentHeader).concat(contentProto).concat(tsHeader).concat(tsBytes).concat(msgIdHeader).concat(msgId).concat(htmlHeader).concat(htmlUpperPart).concat(atUserHeader).concat(htmlLowerPart); textProtoX1PayloadAddr.writeByteArray(finalPayload); this.context.x1 = textProtoX1PayloadAddr; @@ -990,7 +1074,7 @@ function triggerUploadImg(receiver, md5, imagePath) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40 0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 图片id // 0x48 - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 发送人 0x68 @@ -1207,16 +1291,6 @@ function setReceiver() { return } - let start = 0x1e; - let senderLen = currentPtr.add(start).readU8(); - if (senderLen !== 0x14 && senderLen !== 0x13) { - start = 0x1d; - let senderLen = currentPtr.add(start).readU8(); - if (senderLen !== 0x14 && senderLen !== 0x13) { - return - } - } - const x2 = this.context.x2.toInt32(); // console.log(" [+] currentPtr: ", hexdump(currentPtr, { // offset: 0, @@ -1234,12 +1308,9 @@ function setReceiver() { const userContent = fields[5] const msgId = protobufVarintToNumberString(fields[6]) - if (sender === "" || receiver === "" || content === "" || msgId === "") { - console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, { - length: x2, - header: true, - ansi: true, - })) + if (typeof sender !== "string" || sender === "" || typeof receiver !== "string" || receiver === "" || + typeof content !== "string" || content === "" || typeof msgId !== "string" || msgId === "") { + console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + " content:" + content, " msgId:" + msgId) return; }