From 0863b886ab7250c129b5ceeabd3f4779f9749a5d Mon Sep 17 00:00:00 2001 From: choogoo <104893934+choogoo@users.noreply.github.com> Date: Tue, 11 Jul 2023 13:51:58 +0800 Subject: [PATCH 01/38] 3.9.2.23 init (#180) * 3.9.2.23 initt * Update init-agent-script.js * 3.9.2.23 adapter --- src/agents/agent-script-3.6.0.18.js | 472 ++-- .../agent-script-3.9.2.23-laozhang-raw.js | 801 +++++++ src/agents/agent-script-3.9.2.23-new.js | 1964 +++++++++++++++++ src/init-agent-script.js | 1631 +++++++------- src/wechat-sidecar.ts | 16 +- 5 files changed, 3791 insertions(+), 1093 deletions(-) create mode 100644 src/agents/agent-script-3.9.2.23-laozhang-raw.js create mode 100644 src/agents/agent-script-3.9.2.23-new.js diff --git a/src/agents/agent-script-3.6.0.18.js b/src/agents/agent-script-3.6.0.18.js index b0dc6b1..6ae2c78 100644 --- a/src/agents/agent-script-3.6.0.18.js +++ b/src/agents/agent-script-3.6.0.18.js @@ -17,8 +17,8 @@ const offset = { /**---nick call */ - chatroom_member_nick_call_offset_v6:0x3E47B0,//3.6.0.18 - chatroom_member_nick_esi_offset_v6:0x22553D4, + chatroom_member_nick_call_offset_v6: 0x3E47B0,//3.6.0.18 + chatroom_member_nick_esi_offset_v6: 0x22553D4, /**-- nick call */ node_offset: 0x222f3bc,//0x1db9728 -- 3.3.0.155 handle_offset: 0x4c, @@ -38,13 +38,13 @@ const offset = { get_qr_login_data_offset: 0x282160, get_qr_login_call_offset: 0x286930, //-------3.6.0.18 send pic - send_picmsg_call_offset0:0x9A1C0,//assign value to ecx + send_picmsg_call_offset0: 0x9A1C0,//assign value to ecx send_picmsg_call_offset1: 0x4BE160,//0x5ccb50, - send_picmsg_call_ecx:0x222F0F0, + send_picmsg_call_ecx: 0x222F0F0, //-------3.6.0.18 send pic /*send_picmsg_call_offset2: 0x6f5c0, send_picmsg_call_offset3: 0x3e3490,*/ - send_attatch_ecx_offset:0x1D8FA8C, + send_attatch_ecx_offset: 0x1D8FA8C, send_attatch_call_offset0: 0x9A1C0, send_attatch_call_offset1: 0x701DC0,//0x701CD0,//701CD0 send_attatch_call_offset2: 0x4BA5F0,//4B A5F0 @@ -63,13 +63,13 @@ const offset = { /*------------------global-------------------------------------------*/ -const availableVersion = 1661337618////3.3.0.115 ==1661141107 +const availableVersion = 1661337618////3.3.0.115 ==1661141107 const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') const moduleLoad = Module.load('WeChatWin.dll') //1575CF98 -const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// -const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() +const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// +const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() let currentVersion = 0 let nodeList = [] //for contact @@ -88,46 +88,46 @@ let loggedIn = false let g_initTestAsm = null let g_BufferEbp2C = null -let g_initECXU32 = null -let g_initECXPtr = null -let g_initEBXPtr = null -let g_initEBX = null +let g_initECXU32 = null +let g_initECXPtr = null +let g_initEBXPtr = null +let g_initEBX = null let g_attatchEBP210Buffer = null -let g_attatchPathPtr = null -let g_attatchPath = null -let g_attatchEBPAc = null -let g_attatchEBPAcBufPtr = null +let g_attatchPathPtr = null +let g_attatchPath = null +let g_attatchEBPAc = null +let g_attatchEBPAcBufPtr = null -let g_attatchContactIdPtr = null +let g_attatchContactIdPtr = null //let g_attatchECXBuffer = null -let g_attatchESIU32 = null +let g_attatchESIU32 = null -let g_initECXTempPtr = null -const initGlobal = ( (contactId,attatchFile)=> { +let g_initECXTempPtr = null +const initGlobal = ((contactId, attatchFile) => { //const base = moduleBaseAddress.add(0x222f38c).readPointer() //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 - console.log('------------g_attatchEBPAc',g_attatchEBPAc) - console.log('------------g_EDIU32',g_EDIU32) + console.log('------------g_attatchEBPAc', g_attatchEBPAc) + console.log('------------g_EDIU32', g_EDIU32) g_initTestAsm = Memory.alloc(Process.pageSize) - console.log('------------address',g_initTestAsm) - + console.log('------------address', g_initTestAsm) + g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() g_initECXU32 = g_initECXPtr.toInt32() - g_attatchESIU32 = g_EDIU32 + g_attatchESIU32 = g_EDIU32 + + console.log('------------g_initECXU32', g_initECXU32) + console.log('------------g_initESIU32', g_attatchESIU32) - console.log('------------g_initECXU32',g_initECXU32) - console.log('------------g_initESIU32',g_attatchESIU32) - //console.log('==========g_initECXPtr',g_initECXPtr) //console.log('==========g_EDIU32',g_EDIU32) - + //g_attatchECXBuffer = Memory.alloc(0x1024) //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) - + g_BufferEbp2C = Memory.alloc(0x48) //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() @@ -150,7 +150,7 @@ const initGlobal = ( (contactId,attatchFile)=> { g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) /*---------------------------------ebp-210----------------*/ - + //g_attatchContactIdPtr = Memory.alloc(0x4) //g_attatchContactIdPtr.writeUtf16String(contactId) //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) @@ -160,17 +160,17 @@ const initGlobal = ( (contactId,attatchFile)=> { //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) - console.log('------------g_attatchEBPAc',g_attatchEBPAc) - + console.log('------------g_attatchEBPAc', g_attatchEBPAc) + /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) .add(0x04).writeU32(contactId.length*2)*/ - + //g_attatchESIU32 = g_EDI.toInt32() - + //console.log('------------g_attatchESIU32',g_attatchESIU32) //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) @@ -188,7 +188,7 @@ const initGlobal = ( (contactId,attatchFile)=> { cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ //cw.putMovRegOffsetPtrU32('ebp', -20, 0) - + /*cw.putPushU32(0) cw.putMovRegAddress('eax', g_attatchPathPtr) cw.putPushReg('eax') @@ -241,24 +241,24 @@ const initGlobal = ( (contactId,attatchFile)=> { }) -let g_personal_detail_ebx =null -let g_personal_detail_asm =null +let g_personal_detail_ebx = null +let g_personal_detail_asm = null let g_personal_wxid = null let g_personal_wxid_ptr = null -const getOldTest = ( (wxid) => {//personal detail +const getOldTest = ((wxid) => {//personal detail g_personal_detail_asm = Memory.alloc(Process.pageSize) g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() - - g_personal_wxid_ptr=Memory.alloc(wxid.length * 2 + 2) + + g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) g_personal_wxid_ptr.writeUtf16String(wxid) - g_personal_wxid=Memory.alloc(0x14) + g_personal_wxid = Memory.alloc(0x14) g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) - .writeU32(wxid.length*2).add(0x04) - .writeU32(wxid.length*2).add(0x08) + .writeU32(wxid.length * 2).add(0x04) + .writeU32(wxid.length * 2).add(0x08) - console.log('-----------address----------',g_personal_detail_asm) + console.log('-----------address----------', g_personal_detail_asm) Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { var cw = new X86Writer(code, { pc: g_personal_detail_asm }) @@ -269,14 +269,14 @@ const getOldTest = ( (wxid) => {//personal detail //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | cw.putMovRegU32('ebx', g_personal_detail_ebx) - cw.putMovRegReg('esi','eax') + cw.putMovRegReg('esi', 'eax') cw.putPushReg('ebx') - cw.putSubRegImm('esp',0x14) + cw.putSubRegImm('esp', 0x14) cw.putMovRegAddress('eax', g_personal_wxid) - cw.putMovRegReg('ecx','esp') + cw.putMovRegReg('ecx', 'esp') cw.putPushReg('eax') cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - cw.putMovRegReg('ecx','esi') + cw.putMovRegReg('ecx', 'esi') cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) cw.putPopax() @@ -313,33 +313,33 @@ const getOldTest = ( (wxid) => {//personal detail * test call */ let attatchTestAsm = null -let attatchTestEbp2C = null -let attatchGlobalEDI = null -let attatchGlobalEDIB88 = null +let attatchTestEbp2C = null +let attatchGlobalEDI = null +let attatchGlobalEDIB88 = null let attatchTestEBX = null let g_tempEcx = null let attatchFirstECX = null //let attatchFirstECX = null -let gattatchFilePtr=null +let gattatchFilePtr = null let gattatchFile = null -let gattatchReceiveIdPtr=null -let gattatchReceiveId=null -let attatchEAX3B0Buf=null +let gattatchReceiveIdPtr = null +let gattatchReceiveId = null +let attatchEAX3B0Buf = null -let attatchESIbuf = null -const getWxTest = ( (contactId,filePath)=>{ +let attatchESIbuf = null +const getWxTest = ((contactId, filePath) => { //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) //nativeativeFunction() attatchTestAsm = Memory.alloc(Process.pageSize) - console.log('----------------address',attatchTestAsm) + console.log('----------------address', attatchTestAsm) attatchTestEbp2C = Memory.alloc(0xC) attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() - .add(0x938).add(0x438).readPointer() + .add(0x938).add(0x438).readPointer() attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() - attatchTestEBX = Memory.alloc(0x4) + attatchTestEBX = Memory.alloc(0x4) attatchTestEBX.writePointer(attatchGlobalEDI) - console.log('----------------attatchGlobalEDI',attatchGlobalEDI) - console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) + console.log('----------------attatchGlobalEDI', attatchGlobalEDI) + console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) attatchFirstECX = Memory.alloc(0x28) //const attatchSecondEcx = Memory.alloc(0x14) @@ -348,13 +348,13 @@ const getWxTest = ( (contactId,filePath)=>{ const contractIdActLength = contactId.length - gattatchReceiveIdPtr=Memory.alloc(contactId.length * 2 + 2) + gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) gattatchReceiveIdPtr.writeUtf16String(contactId) - gattatchReceiveId=Memory.alloc(0x14) + gattatchReceiveId = Memory.alloc(0x14) gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) - .writeU32(contactId.length*2).add(0x04) - .writeU32(contactId.length*2).add(0x08) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x08) //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) //return /*console.log(hexdump(attatchTestEBX, { @@ -365,56 +365,56 @@ const getWxTest = ( (contactId,filePath)=>{ })) return*/ - gattatchFilePtr=Memory.alloc(filePath.length * 2 + 2) + gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) gattatchFilePtr.writeUtf16String(filePath) - gattatchFile=Memory.alloc(0x14) + gattatchFile = Memory.alloc(0x14) gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) - .writeU32(filePath.length*2).add(0x04) - .writeU32(filePath.length*2).add(0x08) + .writeU32(filePath.length * 2).add(0x04) + .writeU32(filePath.length * 2).add(0x08) const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() - attatchEAX3B0Buf = Memory.alloc(0x3B0) + attatchEAX3B0Buf = Memory.alloc(0x3B0) - g_tempEcx = Memory.alloc(0x4) + g_tempEcx = Memory.alloc(0x4) //g_tempEcx1 = Memory.alloc(0x4) attatchESIbuf = Memory.alloc(0x100) attatchESIbuf.add(0x0).writeU32(3) attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) - attatchESIbuf.add(0x8).writeU32(filePath.length*2) - attatchESIbuf.add(0xc).writeU32(filePath.length*2) - + attatchESIbuf.add(0x8).writeU32(filePath.length * 2) + attatchESIbuf.add(0xc).writeU32(filePath.length * 2) + Memory.patchCode(attatchTestAsm, Process.pageSize, code => { var cw = new X86Writer(code, { pc: attatchTestAsm }) cw.putPushfx() cw.putPushax() //cw.putMovRegU32('edi',attatchGlobalEDI) - - cw.putMovRegAddress('esi',attatchESIbuf) - + + cw.putMovRegAddress('esi', attatchESIbuf) + cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) cw.putMovRegAddress('eax', gattatchFile) cw.putPushReg('eax') cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - + cw.putMovRegAddress('ecx', attatchFirstECX) cw.putMovRegU32('eax', contactIdLength) cw.putPushReg('eax') cw.putPushU32(0) cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) cw.putAddRegImm('esp', 0x8) - - cw.putMovRegReg('edx','eax') - cw.putMovNearPtrReg(attatchFirstECX,'edx') + + cw.putMovRegReg('edx', 'eax') + cw.putMovNearPtrReg(attatchFirstECX, 'edx') cw.putMovRegU32('edi', contactIdLength) cw.putPushReg('edi') cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 cw.putPushReg('eax') - cw.putMovRegNearPtr('eax',attatchFirstECX) + cw.putMovRegNearPtr('eax', attatchFirstECX) cw.putPushReg('eax') cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) cw.putAddRegImm('esp', 0x0c) @@ -424,7 +424,7 @@ const getWxTest = ( (contactId,filePath)=>{ //cw.putMovRegU32('edx', 0) //cw.putMovRegRegPtr('eax', 'ecx') //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') - cw.putMovRegU32('edi',contactId.length * 2) + cw.putMovRegU32('edi', contactId.length * 2) cw.putMovRegU32('ecx', attatchLastECX) cw.putMovRegAddress('eax', attatchEAX3B0Buf) cw.putPushReg('eax') @@ -451,8 +451,8 @@ const getTestInfoFunction = ((addr) => { nativeativeFunction() //00CFE484 - - + + /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { onAccess(details){ console.log('============') @@ -492,7 +492,7 @@ const getHeaderNodeAddress = (() => { // 005 const getChatroomNodeAddress = (() => { - const baseAddress= moduleBaseAddress.add(0x222f3fc).readPointer() + const baseAddress = moduleBaseAddress.add(0x222f3fc).readPointer() if (baseAddress.isNull()) { return baseAddress } @@ -530,7 +530,7 @@ const getMyselfInfoFunction = (() => { const getMyselfIdFunction = (() => { let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) - + return wx_id }) @@ -668,9 +668,9 @@ const recurseNew = ((node) => { //const gender = node.add(0x18C).readU32() const contactJson = { - id1:id, + id1: id, id: wxid, - name:name, + name: name, /*code: wx_code, name: name, alias: alias, @@ -812,7 +812,7 @@ if (!isSupported) { const getContactNativeFunction = (() => { const headerNodeAddress = getHeaderNodeAddress() //console.log('headerNodeAddress',headerNodeAddress) - + if (headerNodeAddress.isNull()) { return '[]' } const node = headerNodeAddress.add(0x0).readPointer() @@ -820,7 +820,7 @@ const getContactNativeFunction = (() => { //console.log(ret) - console.log('getContactNativeFunction:',ret.length) + // console.log('getContactNativeFunction:',ret.length) /*for (let item of ret){ console.log(JSON.stringify(item)) }*/ @@ -998,7 +998,7 @@ const getQrcodeLoginData = () => { // 019 const recvMsgNativeCallback = (() => { - + const nativeCallback = new NativeCallback(() => { }, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) @@ -1006,79 +1006,85 @@ const recvMsgNativeCallback = (() => { moduleBaseAddress.add(offset.hook_point), { onEnter() { - const addr = this.context.eax//0xc30-0x08 - const msgType = addr.add(0x38).readU32() - const isMyMsg = addr.add(0x3C).readU32()//add isMyMsg - - if (msgType > 0) { - - const talkerIdPtr = addr.add(0x48).readPointer() - //console.log('txt msg',talkerIdPtr.readUtf16String()) - const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 - - const myTalkerIdPtr = Memory.alloc(talkerIdLen) - Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) - - - let contentPtr = null - let contentLen = 0 - let myContentPtr = null - if (msgType == 3) {// pic path - let thumbPtr = addr.add(0x198).readPointer(); - let hdPtr = addr.add(0x1ac).readPointer(); - let thumbPath = thumbPtr.readUtf16String(); - let hdPath = hdPtr.readUtf16String(); - let picData = [ - thumbPath,// PUPPET.types.Image.Unknown - thumbPath,// PUPPET.types.Image.Thumbnail - hdPath,// PUPPET.types.Image.HD - hdPath// PUPPET.types.Image.Artwork - ] - let content = JSON.stringify(picData); - myContentPtr = Memory.allocUtf16String(content); - } else { - contentPtr = addr.add(0x70).readPointer() - contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 - myContentPtr = Memory.alloc(contentLen) - Memory.copy(myContentPtr, contentPtr, contentLen) - } - - // console.log('----------------------------------------') - // console.log(msgType) - // console.log(contentPtr.readUtf16String()) - // console.log('----------------------------------------') - const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 - let myGroupMsgSenderIdPtr = null - if (groupMsgAddr == 0) {//weChatPublic is zero,type is 49 - - myGroupMsgSenderIdPtr = Memory.alloc(0x10) - myGroupMsgSenderIdPtr.writeUtf16String("null") - - } else { - - const groupMsgSenderIdPtr = addr.add(0x170).readPointer() - const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 - myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) - Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) - - } - - const xmlNullPtr = addr.add(0x1ec).readU32() - let myXmlContentPtr = null - if (xmlNullPtr == 0) { - - myXmlContentPtr = Memory.alloc(0x10) - myXmlContentPtr.writeUtf16String("null") - - } else { - const xmlContentPtr = addr.add(0x1ec).readPointer() - - const xmlContentLen = addr.add(0x1ec + 0x04).readU32() * 2 + 2 - myXmlContentPtr = Memory.alloc(xmlContentLen) - Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + let addr + let msgType = 0 + let isMyMsg = 0 + let curTime = new Date() + try { + addr = this.context.eax//0xc30-0x08 + msgType = addr.add(0x38).readU32() + isMyMsg = addr.add(0x3C).readU32()//add isMyMsg + if (msgType > 0) { + const talkerIdPtr = addr.add(0x48).readPointer() + //console.log('txt msg',talkerIdPtr.readUtf16String()) + const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 + + const myTalkerIdPtr = Memory.alloc(talkerIdLen) + Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) + + + let contentPtr = null + let contentLen = 0 + let myContentPtr = null + if (msgType == 3) {// pic path + let thumbPtr = addr.add(0x198).readPointer(); + let hdPtr = addr.add(0x1ac).readPointer(); + let thumbPath = thumbPtr.readUtf16String(); + let hdPath = hdPtr.readUtf16String(); + let picData = [ + thumbPath,// PUPPET.types.Image.Unknown + thumbPath,// PUPPET.types.Image.Thumbnail + hdPath,// PUPPET.types.Image.HD + hdPath// PUPPET.types.Image.Artwork + ] + let content = JSON.stringify(picData); + myContentPtr = Memory.allocUtf16String(content); + } else { + contentPtr = addr.add(0x70).readPointer() + contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 + myContentPtr = Memory.alloc(contentLen) + Memory.copy(myContentPtr, contentPtr, contentLen) + } + + // console.log('----------------------------------------') + // console.log(msgType) + // console.log(contentPtr.readUtf16String()) + // console.log('----------------------------------------') + const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 + let myGroupMsgSenderIdPtr = null + if (groupMsgAddr == 0) {//weChatPublic is zero,type is 49 + + myGroupMsgSenderIdPtr = Memory.alloc(0x10) + myGroupMsgSenderIdPtr.writeUtf16String("null") + + } else { + + const groupMsgSenderIdPtr = addr.add(0x170).readPointer() + const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 + myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) + Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) + + } + + const xmlNullPtr = addr.add(0x1ec).readU32() + let myXmlContentPtr = null + if (xmlNullPtr == 0) { + + myXmlContentPtr = Memory.alloc(0x10) + myXmlContentPtr.writeUtf16String("null") + + } else { + const xmlContentPtr = addr.add(0x1ec).readPointer() + + const xmlContentLen = addr.add(0x1ec + 0x04).readU32() * 2 + 2 + myXmlContentPtr = Memory.alloc(xmlContentLen) + Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + } + + setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) } - - setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) + } catch (err) { + console.error(curTime,'recvMsgNativeCallback at onEnter err:', err) } } }) @@ -1182,17 +1188,17 @@ let nickMemberIdStructV6 = null let memberNickBuffAsmV6 = null let nickResultEdiV6 = null -const getChatroomMemberNickInfoV1Function=((memberId, roomId)=>{ +const getChatroomMemberNickInfoV1Function = ((memberId, roomId) => { nickRoomIdV6 = initidStruct(roomId) nullEdiWxidStructV6 = initNullIdStruct('') nickMemberIdStructV6 = initStruct(memberId) memberNickBuffAsmV6 = Memory.alloc(Process.pageSize) - console.log('-----',memberNickBuffAsmV6) + console.log('-----', memberNickBuffAsmV6) const tmp = (moduleBaseAddress.add( offset.chatroom_member_nick_esi_offset_v6 )).readU32() - console.log('=======tmp',tmp) + console.log('=======tmp', tmp) Memory.patchCode(memberNickBuffAsmV6, Process.pageSize, code => { var cw = new X86Writer(code, { pc: memberNickBuffAsmV6 }) cw.putPushfx(); @@ -1202,19 +1208,19 @@ const getChatroomMemberNickInfoV1Function=((memberId, roomId)=>{ cw.putMovRegAddress('eax', nickMemberIdStructV6) cw.putMovRegAddress('ebx', nickRoomIdV6) - + cw.putMovRegAddress('esi', ptr(tmp)) cw.putPushReg('edi') cw.putPushReg('eax') cw.putPushReg('ebx') - + cw.putMovRegAddress('ecx', ptr(tmp)) cw.putCallAddress(moduleBaseAddress.add( offset.chatroom_member_nick_call_offset_v6 )) - + //cw.putMovNearPtrReg(nickResultEdiV6, 'edi') cw.putPopax() cw.putPopfx() @@ -1226,7 +1232,7 @@ const getChatroomMemberNickInfoV1Function=((memberId, roomId)=>{ const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsmV6), 'void', []) nativeativeFunction() - console.log('---------nullEdiWxidStructV6',nullEdiWxidStructV6) + console.log('---------nullEdiWxidStructV6', nullEdiWxidStructV6) const nickha = readWideString(nullEdiWxidStructV6) console.log('-----------------') @@ -1297,7 +1303,7 @@ const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { cw.putAddRegImm('esp', 0x04) //cw.putMovNearPtrReg(nickRetAddr, 'ebx') //lea eax, buf - + cw.putPopax() cw.putPopfx() cw.putRet() @@ -1324,7 +1330,7 @@ let attatchPathPtr = null let attatchAsm = null let attatchBuf = null -let attatchReceiveIdPtr=null +let attatchReceiveIdPtr = null let attatchReceiveId = null let attatchSendId = null @@ -1340,15 +1346,15 @@ let attatchEbp210 = null let attatchEbpAc = null*/ let attatchEbp11E8 = null -let attatchEbpCC = null -let attatchEbp368 = null -let attatchEAX = null +let attatchEbpCC = null +let attatchEbp368 = null +let attatchEAX = null let sFileName = null let fileNamePtr = null let attatchEbp84 = null -let attatchECX = null +let attatchECX = null /** @@ -1358,37 +1364,37 @@ param {78EDBC86 | 8B80 38040000 | mov eax,dword ptr ds:[eax+438] */ // 021 -const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) => { +const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size) => { attatchAsm = Memory.alloc(Process.pageSize) - console.log('--------------address',attatchAsm) + console.log('--------------address', attatchAsm) - fileNamePtr=Memory.alloc(filename.length * 2 + 2) + fileNamePtr = Memory.alloc(filename.length * 2 + 2) fileNamePtr.writeUtf16String(filename) - sFileName=Memory.alloc(0x14) + sFileName = Memory.alloc(0x14) sFileName.writePointer(ptr(fileNamePtr)).add(0x04) - .writeU32(fileNamePtr.length*2).add(0x04) - .writeU32(fileNamePtr.length*2).add(0x08) + .writeU32(fileNamePtr.length * 2).add(0x04) + .writeU32(fileNamePtr.length * 2).add(0x08) //const fileSize = size.toInt32() - attatchReceiveIdPtr=Memory.alloc(contactId.length * 2 + 2) + attatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) attatchReceiveIdPtr.writeUtf16String(contactId) - attatchReceiveId=Memory.alloc(0x14) + attatchReceiveId = Memory.alloc(0x14) attatchReceiveId.writePointer(ptr(attatchReceiveIdPtr)).add(0x04) - .writeU32(contactId.length*2).add(0x04) - .writeU32(contactId.length*2).add(0x08) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x08) - attatchSendIdPtr=Memory.alloc(senderId.length * 2 + 2) + attatchSendIdPtr = Memory.alloc(senderId.length * 2 + 2) attatchSendIdPtr.writeUtf16String(senderId) - attatchSendId=Memory.alloc(0x14) + attatchSendId = Memory.alloc(0x14) attatchSendId.writePointer(ptr(attatchSendIdPtr)).add(0x04) - .writeU32(senderId.length*2).add(0x04) - .writeU32(senderId.length*2).add(0x08) + .writeU32(senderId.length * 2).add(0x04) + .writeU32(senderId.length * 2).add(0x08) attatchPathPtr = Memory.alloc(path.length * 2 + 2) attatchPathPtr.writeUtf16String(path) @@ -1399,16 +1405,16 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) = .writeU32(path.length * 2).add(0x04) attatchEbp11E8 = Memory.alloc(0xBE4) - attatchEbpCC = Memory.alloc(0x14) + attatchEbpCC = Memory.alloc(0x14) attatchEbp368 = Memory.alloc(0x290) - attatchEbp84 = Memory.alloc(0x18) - attatchEAX = Memory.alloc(0x18) + attatchEbp84 = Memory.alloc(0x18) + attatchEAX = Memory.alloc(0x18) attatchECX = moduleBaseAddress.add(0x222f178).toInt32() //console.log('basename',path.basename(path)) //return - + /** * -------------buffer------------------------------- */ @@ -1425,23 +1431,23 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) = cw.putPushU32(moduleBaseAddress.add(0x1E1B3C0).toInt32()) cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x4))//11e4 cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid - // cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid - - /** - * 78482B8D | 6A FF | push FFFFFFFF | - 78482B8F | 68 B895E979 | push wechatwin.79E995B8 | 79E995B8:L"0" - 78482B94 | 8D8D 48EEFFFF | lea ecx,dword ptr ss:[ebp-11B8] | - 78482B9A | E8 71F83600 | call wechatwin.787F2410 | 此处继续写ebp-11b8 - */ + // cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid + + /** + * 78482B8D | 6A FF | push FFFFFFFF | + 78482B8F | 68 B895E979 | push wechatwin.79E995B8 | 79E995B8:L"0" + 78482B94 | 8D8D 48EEFFFF | lea ecx,dword ptr ss:[ebp-11B8] | + 78482B9A | E8 71F83600 | call wechatwin.787F2410 | 此处继续写ebp-11b8 + */ cw.putPushU32(-1) cw.putPushU32(moduleBaseAddress.add(0x1DA95B8).toInt32()) cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x30))//11B8 cw.putCallAddress(moduleBaseAddress.add(0x702410)) - cw.putMovRegU32('eax',0x6) - cw.putMovNearPtrReg(attatchEbp11E8.add(0x80), 'eax')//1168 - cw.putMovRegU32('eax',size)//file size - cw.putMovNearPtrReg(attatchEbp11E8.add(0x108), 'eax')//10e0 + cw.putMovRegU32('eax', 0x6) + cw.putMovNearPtrReg(attatchEbp11E8.add(0x80), 'eax')//1168 + cw.putMovRegU32('eax', size)//file size + cw.putMovNearPtrReg(attatchEbp11E8.add(0x108), 'eax')//10e0 cw.putMovRegAddress('eax', sFileName) @@ -1453,8 +1459,8 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) = cw.putPushReg('eax') cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x160))//1088=0x11e8-0x160 cw.putCallAddress(moduleBaseAddress.add(0x702980)) - - + + cw.putMovRegAddress('eax', attatchEbpCC) cw.putPushReg('eax') cw.putMovRegAddress('ecx', attatchEbp11E8) @@ -1475,10 +1481,10 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) = cw.putPushReg('ecx') cw.putPushReg('eax') - cw.putMovRegAddress('eax',attatchEbpCC) + cw.putMovRegAddress('eax', attatchEbpCC) cw.putPushReg('eax') - cw.putMovRegAddress('eax',attatchReceiveId) + cw.putMovRegAddress('eax', attatchReceiveId) cw.putPushReg('eax') cw.putMovRegAddress('ecx', attatchEbp368) @@ -1503,7 +1509,7 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) = //cw.putMovNearPtrReg(attatchEbpCC.add(0x64), 'eax')//ebp-68 //cw.putAddRegImm('ecx', 0x8) //cw.putMovRegAddress('eax', attatchEbp368.add(0x64))//ebp-68 - + //cw.putMovRegU32('ecx',attatchECX) //cw.putPushReg('eax') //cw.putMovRegAddress('eax', attatchEbpCC.add(0x40))//ebp-8c @@ -1518,8 +1524,8 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId,path,filename,size) = //cw.putMovRegAddress('ecx', attatchEbp368) //cw.putCallAddress(moduleBaseAddress.add(0x63B4F0)) - // 78F33099 | 8D8D 34FFFFFF | lea ecx,dword ptr ss:[ebp-CC] | -//78F3309F | E8 AC0FD0FF | call wechatwin.78C34050 | + // 78F33099 | 8D8D 34FFFFFF | lea ecx,dword ptr ss:[ebp-CC] | + //78F3309F | E8 AC0FD0FF | call wechatwin.78C34050 | //cw.putMovRegAddress('ecx',attatchEbpCC) //cw.putCallAddress(moduleBaseAddress.add(0x94050)) @@ -1607,7 +1613,7 @@ const sendPicMsgNativeFunction = ((contactId, path) => { offset.send_picmsg_call_offset0 )) cw.putMovRegReg('edx', 'eax')//缓存 - + cw.putSubRegImm('esp', 0x14) cw.putMovRegAddress('eax', buffwxid) cw.putMovRegReg('ecx', 'esp') @@ -1681,7 +1687,7 @@ const sendPicMsgNativeFunction = ((contactId, path) => { }) - console.log('----------picAsm',picAsm) + console.log('----------picAsm', picAsm) const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) nativeativeFunction() @@ -1950,18 +1956,18 @@ const agentReadyCallback = (() => { })() // 027 -const SendMiniProgramNativeFunction = ((bg_path_str,send_wxid_str,recv_wxid_str,xmlstr) => { +const SendMiniProgramNativeFunction = ((bg_path_str, send_wxid_str, recv_wxid_str, xmlstr) => { console.log("------------------------------------------------------"); - var asmCode=Memory.alloc(Process.pageSize); + var asmCode = Memory.alloc(Process.pageSize); - var ECX_buf=Memory.alloc(0x300); - var Buf_EAX=Memory.alloc(0x300); - var buf_1=Memory.alloc(0x300); - var ptr_to_buf_1=Memory.alloc(0x4).writePointer(buf_1); - var buf_2=Memory.alloc(0x300); + var ECX_buf = Memory.alloc(0x300); + var Buf_EAX = Memory.alloc(0x300); + var buf_1 = Memory.alloc(0x300); + var ptr_to_buf_1 = Memory.alloc(0x4).writePointer(buf_1); + var buf_2 = Memory.alloc(0x300); -// var bg_path_str="C:/aaaa.jpg"; - var bg_path_Ptr=Memory.alloc(bg_path_str.length * 2 + 1) + // var bg_path_str="C:/aaaa.jpg"; + var bg_path_Ptr = Memory.alloc(bg_path_str.length * 2 + 1) bg_path_Ptr.writeUtf16String(bg_path_str); var bg_path_Struct = Memory.alloc(0x14) // returns a NativePointer bg_path_Struct.writePointer(bg_path_Ptr).add(0x04) @@ -1970,8 +1976,8 @@ const SendMiniProgramNativeFunction = ((bg_path_str,send_wxid_str,recv_wxid_str, .writeU32(0).add(0x04) .writeU32(0); - // var send_wxid_str="wxid_4zr616ir6fi122"; - var send_wxid_Ptr=Memory.alloc(send_wxid_str.length * 2 + 1) + // var send_wxid_str="wxid_4zr616ir6fi122"; + var send_wxid_Ptr = Memory.alloc(send_wxid_str.length * 2 + 1) send_wxid_Ptr.writeUtf16String(send_wxid_str); var send_wxid_Struct = Memory.alloc(0x14) // returns a NativePointer send_wxid_Struct.writePointer(send_wxid_Ptr).add(0x04) @@ -1980,8 +1986,8 @@ const SendMiniProgramNativeFunction = ((bg_path_str,send_wxid_str,recv_wxid_str, .writeU32(0).add(0x04) .writeU32(0); - // var recv_wxid_str="filehelper"; - var recv_wxid_Ptr=Memory.alloc(recv_wxid_str.length * 2 + 1) + // var recv_wxid_str="filehelper"; + var recv_wxid_Ptr = Memory.alloc(recv_wxid_str.length * 2 + 1) recv_wxid_Ptr.writeUtf16String(recv_wxid_str); var recv_wxid_Struct = Memory.alloc(0x14) // returns a NativePointer recv_wxid_Struct.writePointer(recv_wxid_Ptr).add(0x04) @@ -1990,9 +1996,9 @@ const SendMiniProgramNativeFunction = ((bg_path_str,send_wxid_str,recv_wxid_str, .writeU32(0).add(0x04) .writeU32(0); - // vvar pXml=initidStruct('wxid_4zr616ir6fi1220腾讯出行服务|加油代驾公交view330https://mp.weixin.qq.com/mp/waerrpage?appid=wx65cc950f42e8fff1&amp;type=upgrade&amp;upgradetype=3#wechat_redirecthttp://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=200腾讯出行服务|加油代驾公交0gh_ad64296dc8bd@appwx65cc950f42e8fff11http://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=20002_wx65cc950f42e8fff1_875237370_1644979747_11Window wechat'); + // vvar pXml=initidStruct('wxid_4zr616ir6fi1220腾讯出行服务|加油代驾公交view330https://mp.weixin.qq.com/mp/waerrpage?appid=wx65cc950f42e8fff1&amp;type=upgrade&amp;upgradetype=3#wechat_redirecthttp://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=200腾讯出行服务|加油代驾公交0gh_ad64296dc8bd@appwx65cc950f42e8fff11http://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=20002_wx65cc950f42e8fff1_875237370_1644979747_11Window wechat'); - var pXml=initidStruct(xmlstr) + var pXml = initidStruct(xmlstr) console.log(send_wxid_Struct); console.log(recv_wxid_Struct); diff --git a/src/agents/agent-script-3.9.2.23-laozhang-raw.js b/src/agents/agent-script-3.9.2.23-laozhang-raw.js new file mode 100644 index 0000000..5d9cddc --- /dev/null +++ b/src/agents/agent-script-3.9.2.23-laozhang-raw.js @@ -0,0 +1,801 @@ +/** + * WeChat 3.2.1.121 + * > Special thanks to: @cixingguangming55555 老张学技术 + * + * Credit: https://github.com/cixingguangming55555/wechat-bot + * Source: https://pan.baidu.com/s/1OmX2lxNOYHyGsl_3ByhsoA + * 《源码3.2.1.121》提取码: 1rfa + * WeChat: https://pan.baidu.com/share/init?surl=IHRM2OMvrLyuCz5MRbigGg + * 微信:3.2.1.121 提取码: cscn + */ + +//https://blog.csdn.net/iloveitvm/article/details/109119687 frida学习 + +//const { isNullishCoalesce } = require("typescript") + +//3.9.2.23 + +const offset = { + + + hook_point: 0xd19a0b, //3.9.2.23 + + myselfinfo: { + offset: 0x2FFD484, //老版本微信号偏移,后面的地址,都要在这个偏移上增加 + //wxid_len:0x10, + head_img_url: 0x2D8, + head_img_url_len: 0x2E8, + wx_nick_name: 0x10C, + wxcode_new: 0x64, //新版本微信号 + //wxcode_len:0x74 + wxid_len_offset: 0x4D4 + }, + + contactInfo: { + nodeOffset: 0x2FFDD7C, + nodeRootOffset: 0x64 + }, + chatroomInfo: { + nodeOffset: 0x2FFDDC8, + nodeRootOffset: 0x8c8 + }, + sendTxtMsg: { + callOffset: 0xCE6C80 + }, + sendPicMsg: { + call1: 0x768140, + call2: 0xf59e40, + call3: 0xce6640 + } +}; + + + +/*------------------global-------------------------------------------*/ +const availableVersion = 1661534743 ////3.9.2.23 ==0x63090217 + +const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') +const moduleLoad = Module.load('WeChatWin.dll') +//1575CF98 + + +/*---------------base -------------------------*/ + +let retidPtr=null +let retidStruct=null +const initidStruct = ((str) => { + + retidPtr = Memory.alloc(str.length * 2 + 1) + retidPtr.writeUtf16String(str) + + retidStruct = Memory.alloc(0x14) // returns a NativePointer + + retidStruct + .writePointer(retidPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retidStruct +}) + +let retPtr = null +let retStruct = null +const initStruct = ((str) => { + + retPtr = Memory.alloc(str.length * 2 + 1) + retPtr.writeUtf16String(str) + + retStruct = Memory.alloc(0x14) // returns a NativePointer + + retStruct + .writePointer(retPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retStruct +}) + +let msgstrPtr=null +let msgStruct=null +const initmsgStruct = ((str) => { + msgstrPtr = Memory.alloc(str.length * 2 + 1) + msgstrPtr.writeUtf16String(str) + + msgStruct = Memory.alloc(0x14) // returns a NativePointer + + msgStruct + .writePointer(msgstrPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return msgStruct +}) + +let atStruct = null +const initAtMsgStruct = ((wxidStruct) => { + + atStruct = Memory.alloc(0x10) + + atStruct.writePointer(wxidStruct).add(0x04) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) + .writeU32(0) + return atStruct +}) +const readStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(16).readU32() + const capacity = addr.add(20).readU32() + addr.ptr = addr + addr.size = size + addr.capacity = capacity + if (capacity > 15 && !addr.readPointer().isNull()) { + addr.ptr = addr.readPointer() + } + addr.ptr._readCString = addr.ptr.readCString + addr.ptr._readAnsiString = addr.ptr.readAnsiString + addr.ptr._readUtf8String = addr.ptr.readUtf8String + addr.readCString = () => { + return addr.size ? addr.ptr._readCString(addr.size) : '' + } + addr.readAnsiString = () => { + return addr.size ? addr.ptr._readAnsiString(addr.size) : '' + } + addr.readUtf8String = () => { + return addr.size ? addr.ptr._readUtf8String(addr.size) : '' + } + + // console.log('readStringPtr() address:',address,' -> str ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readStringPtr() str:' , addr.readUtf8String()) + // console.log('readStringPtr() address:', addr,'dump:', addr.readByteArray(24)) + + return addr +} + +// std::wstring +// const wstr = readWStringPtr(ptr).readUtf16String() +const readWStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(4).readU32() + const capacity = addr.add(8).readU32() + addr.ptr = addr.readPointer() + addr.size = size + addr.capacity = capacity + addr.ptr._readUtf16String = addr.ptr.readUtf16String + addr.readUtf16String = () => { + return addr.size ? addr.ptr._readUtf16String(addr.size * 2) : '' + } + + // console.log('readWStringPtr() address:',address,' -> ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readWStringPtr() str:' , `"${addr.readUtf16String()}"`,'\n',addr.ptr.readByteArray(addr.size*2+2),'\n') + // console.log('readWStringPtr() address:', addr,'dump:', addr.readByteArray(16),'\n') + + return addr +} + +const readString = (address) => { + return readStringPtr(address).readUtf8String() +} + +const readWideString = (address) => { + return readWStringPtr(address).readUtf16String() +} + + +/*-----------------base-------------------------*/ + +let currentVersion = 0 + +let nodeList = [] //for contact +let contactList = [] //for contact + +let chatroomNodeList = [] //for chatroom +let chatroomMemberList = [] //for chatroom +let loggedIn = false + + + +const getWechatVersionFunction = (() => { + if (currentVersion) { + return currentVersion + } + const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' + const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) + if (results.length == 0) { + return 0 + } + const addr = results[0].address + const ret = addr.add(0x07).readPointer() + const ver = ret.add(0x0).readU32() + currentVersion = ver + return ver +}) + +// 011 +const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { + if (!ver) { + return '0.0.0.0' + } + const vers = [] + vers.push((ver >> 24) & 255 - 0x60) + vers.push((ver >> 16) & 255) + vers.push((ver >> 8) & 255) + vers.push(ver & 255) + return vers.join('.') +}) + +const checkSupportedFunction = (() => { + const ver = getWechatVersionFunction() + return ver == availableVersion +}) + +// 019 +const recvMsgNativeCallback = (() => { + + + const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + + Interceptor.attach( + moduleBaseAddress.add(offset.hook_point), { + onEnter() { + const addr = this.context.ecx //0xc30-0x08 + const msgType = addr.add(0x38).readU32() + const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg + + if (msgType > 0) { + + const talkerIdPtr = addr.add(0x48).readPointer() + //console.log('txt msg',talkerIdPtr.readUtf16String()) + const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 + + const myTalkerIdPtr = Memory.alloc(talkerIdLen) + Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) + + + let contentPtr = null + let contentLen = 0 + let myContentPtr = null + if (msgType == 3) { // pic path + let thumbPtr = addr.add(0x198).readPointer(); + let hdPtr = addr.add(0x1ac).readPointer(); + let thumbPath = thumbPtr.readUtf16String(); + let hdPath = hdPtr.readUtf16String(); + let picData = [ + thumbPath, // PUPPET.types.Image.Unknown + thumbPath, // PUPPET.types.Image.Thumbnail + hdPath, // PUPPET.types.Image.HD + hdPath // PUPPET.types.Image.Artwork + ] + let content = JSON.stringify(picData); + myContentPtr = Memory.allocUtf16String(content); + } else { + contentPtr = addr.add(0x70).readPointer() + contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 + myContentPtr = Memory.alloc(contentLen) + Memory.copy(myContentPtr, contentPtr, contentLen) + } + + // console.log('----------------------------------------') + // console.log(msgType) + // console.log(contentPtr.readUtf16String()) + // console.log('----------------------------------------') + const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 + let myGroupMsgSenderIdPtr = null + if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 + + myGroupMsgSenderIdPtr = Memory.alloc(0x10) + myGroupMsgSenderIdPtr.writeUtf16String("null") + + } else { + + const groupMsgSenderIdPtr = addr.add(0x170).readPointer() + const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 + myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) + Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) + + } + + const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 + let myXmlContentPtr = null + if (xmlNullPtr == 0) { + + myXmlContentPtr = Memory.alloc(0x10) + myXmlContentPtr.writeUtf16String("null") + + } else { + const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + + const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 + myXmlContentPtr = Memory.alloc(xmlContentLen) + Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + } + + setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) + } + } + }) + return nativeCallback +})() + + +const getBaseNodeAddress = (() => { + return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() +}) + +// 004 +const getHeaderNodeAddress = (() => { + const baseAddress = getBaseNodeAddress() + //console.log('baseAddress',baseAddress) + if (baseAddress.isNull()) { + return baseAddress + } + + //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) + return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() +}) + +const getMyselfInfoFunction = (() => { + + let ptr = 0 + let wx_code = '' + let wx_id = '' + let wx_name = '' + let head_img_url = '' + + const base = moduleBaseAddress.add(offset.myselfinfo.offset) + const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() + + if (wxid_len === 0x13) { // 新版本微信 + wx_id = base.readPointer().readAnsiString(wxid_len) + wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() + } else { + wx_id = readString(base) + wx_code = wx_id + } + + + wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) + const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() + const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() + + head_img_url = img_addr.readAnsiString(img_len) + + const myself = { + id: wx_id, + code: wx_code, + name: wx_name, + head_img_url: head_img_url, + }; + + return JSON.stringify(myself) + +}) + + +const recurseNew = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { + return + } + + if (node.equals(headerNodeAddress)) { + return + } + + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } + + + nodeList.push(node) + const id = readString(node.add(0x8)) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x30)) + //console.log('-----------',wxid) + + + //custom id, if not set return null, and use wxid which should be custom id + //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + + //custom Nickname + const name = readWideString(node.add(0x8c)) + + //alias aka 'remark' in wechat + //const alias = readWideString(node.add(0x80)) + + //avatarUrl + //const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + //const gender = node.add(0x18C).readU32() + + const contactJson = { + id1: id, + id: wxid, + name: name, + /*code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender,*/ + } + + contactList.push(contactJson) + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + //const rightNode = node.add(0x08).readPointer() + + recurseNew(leftNode) + recurseNew(centerNode) + //recurse(rightNode) + + const allContactJson = contactList + return allContactJson + +}) + + +const getContactNativeFunction = (() => { + const headerNodeAddress = getHeaderNodeAddress() + //console.log('headerNodeAddress',headerNodeAddress) + + if (headerNodeAddress.isNull()) { + return '[]' + } + + const node = headerNodeAddress.add(0x0).readPointer() + const ret = recurseNew(node) + + //console.log(ret) + + console.log('getContactNativeFunction:', ret.length) + /*for (let item of ret){ + console.log(JSON.stringify(item)) + }*/ + //console.log(ret.contact) + const cloneRet = JSON.stringify(ret) + nodeList.length = 0 + contactList.length = 0 + + return cloneRet +}) + + +const getChatroomNodeAddress = (() => { + const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() + if (baseAddress.isNull()) { + return baseAddress + } + return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() +}) + +const chatroomRecurse = ((node) => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return + } + + if (node.equals(chatroomNodeAddress)) { + return + } + + for (const item in chatroomNodeList) { + if (node.equals(chatroomNodeList[item])) { + return + } + } + + chatroomNodeList.push(node) + const roomid = readWideString(node.add(0x10)) + + const len = node.add(0x54).readU32() // + //const memberJson={} + if (len > 4) { // + const memberStr = readString(node.add(0x44)) + if (memberStr.length > 0) { + const memberList = memberStr.split(/[\\^][G]/) + const memberJson = { + roomid: roomid, + roomMember: memberList + } + + chatroomMemberList.push(memberJson) + } + + } + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() + + chatroomRecurse(leftNode) + chatroomRecurse(centerNode) + chatroomRecurse(rightNode) + + const allChatroomMemberJson = chatroomMemberList + return allChatroomMemberJson +}) + +const getChatroomMemberInfoFunction = (() => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return '[]' + } + + const node = chatroomNodeAddress.add(0x0).readPointer() + const ret = chatroomRecurse(node) + + const cloneRet = JSON.stringify(ret) + chatroomNodeList.length = 0 //empty + chatroomMemberList.length = 0 //empty + return cloneRet +}) + + + +/** + * sendMsgNativeFunction + * send text message + * @param {string} talkerId = wxid or roomid + * @param {string} content + */ +const sendMsgNativeFunction = ((talkerId, content) => { + + const txtAsm = Memory.alloc(Process.pageSize) + //const buffwxid = Memory.alloc(0x20) + + + let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) + wxidPtr.writeUtf16String(talkerId) + + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(wxidPtr)).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + + let contentPtr = Memory.alloc(content.length * 2 + 2) + contentPtr.writeUtf16String(content) + + const sizeOfStringStruct = Process.pointerSize * 5 + let contentStruct = Memory.alloc(sizeOfStringStruct) + + contentStruct + .writePointer(contentPtr).add(0x4) + .writeU32(content.length).add(0x4) + .writeU32(content.length * 2) + + + const ecxBuffer = Memory.alloc(0x2d8) + + Memory.patchCode(txtAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: txtAsm + }) + cw.putPushfx() + cw.putPushax() + + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + cw.putPushU32(0x0) + + //cw.putMovRegReg + + cw.putMovRegAddress('eax', contentStruct) + cw.putPushReg('eax') + + cw.putMovRegAddress('edx', picWxid) //room_id + + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) + + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + console.log('----------txtAsm', txtAsm) + const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + nativeativeFunction() + +}) + + +let asmAtMsg = null +let roomid_, msg_, wxid_, atid_ +let ecxBuffer +const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { + + asmAtMsg = Memory.alloc(Process.pageSize) + ecxBuffer = Memory.alloc(0x3b0) + + const atContent = '@'+nickname+' '+text + + roomid_ = initStruct(roomId) + wxid_ = initidStruct(contactId) + msg_ = initmsgStruct(atContent) + atid_ = initAtMsgStruct(wxid_) + + Memory.patchCode(asmAtMsg, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: asmAtMsg + }) + cw.putPushfx() + cw.putPushax() + + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + //cw.putPushU32(0x0) + cw.putMovRegAddress('eax', atid_) + cw.putPushReg('eax') + + //cw.putMovRegReg + + cw.putMovRegAddress('eax', msg_) + cw.putPushReg('eax') + + cw.putMovRegAddress('edx', roomid_) //room_id + + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) + + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + //console.log('----------txtAsm', asmAtMsg) + const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + nativeativeFunction() + +}) + +/** + * + * @param {*} contactId + * @param {*} path + */ +const sendPicMsgNativeFunction = ((contactId, path) => { + + const picAsm = Memory.alloc(Process.pageSize) + const buffwxid = Memory.alloc(0x20) + const picbuff = Memory.alloc(0x2D8) + + let pathPtr = Memory.alloc(path.length * 2 + 1) + pathPtr.writeUtf16String(path) + + let imagefilepath = Memory.alloc(0x24) + imagefilepath.writePointer(pathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) + + let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) + picWxidPtr.writeUtf16String(contactId) + + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(picWxidPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + + + //const test_offset1 = 0x701DC0; + Memory.patchCode(picAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: picAsm + }) + cw.putPushfx(); + cw.putPushax(); + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call1 + )) + cw.putMovRegReg('edx', 'eax') //缓存 + + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', buffwxid) + cw.putMovRegReg('ecx', 'esp') + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call2 + )) + + cw.putMovRegReg('ecx', 'edx') + cw.putMovRegAddress('eax', picWxid) //=lea + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('edi') + cw.putPushReg('eax') + cw.putMovRegAddress('eax', picbuff) + cw.putPushReg('eax') + + cw.putMovRegAddress('edi', picWxid) //edi + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call3 + )) + + + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + //console.log('----------picAsm',picAsm) + const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) + nativeativeFunction() + +}) + + + + +let memberNickBuffAsm = null +let nickRoomId = null +let nickMemberId = null +let nickBuff = null +const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { + + nickBuff = Memory.alloc(0x7e4) + //const nickRetAddr = Memory.alloc(0x04) + memberNickBuffAsm = Memory.alloc(Process.pageSize) + //console.log('asm address----------',memberNickBuffAsm) + nickRoomId = initidStruct(roomId) + //console.log('nick room id',nickRoomId) + nickMemberId = initStruct(memberId) + + //console.log('nick nickMemberId id',nickMemberId) + //const nickStructPtr = initmsgStruct('') + + Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: memberNickBuffAsm + }) + cw.putPushfx() + cw.putPushax() + cw.putMovRegAddress('edi', nickRoomId) + cw.putMovRegAddress('eax', nickBuff) + cw.putMovRegReg('edx', 'edi') + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', nickMemberId) + cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) + cw.putAddRegImm('esp', 0x04) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) + nativeativeFunction() + + const nickname = readWideString(nickBuff) + console.log('----nickname', nickname) + return readWideString(nickBuff) +}) + + diff --git a/src/agents/agent-script-3.9.2.23-new.js b/src/agents/agent-script-3.9.2.23-new.js new file mode 100644 index 0000000..f706c38 --- /dev/null +++ b/src/agents/agent-script-3.9.2.23-new.js @@ -0,0 +1,1964 @@ +/** + * > Special thanks to: @cixingguangming55555 老张学技术 + * + * Credit: https://github.com/cixingguangming55555/wechat-bot + */ + +//https://blog.csdn.net/iloveitvm/article/details/109119687 frida学习 + +//const { isNullishCoalesce } = require("typescript") + +//3.9.2.23 + +// 偏移地址集合 done +const offset = { + hook_point: 0xd19a0b, //3.9.2.23 + myselfinfo: { + offset: 0x2FFD484, //老版本微信号偏移,后面的地址,都要在这个偏移上增加 + //wxid_len:0x10, + head_img_url: 0x2D8, + head_img_url_len: 0x2E8, + wx_nick_name: 0x10C, + wxcode_new: 0x64, //新版本微信号 + //wxcode_len:0x74 + wxid_len_offset: 0x4D4 + }, + contactInfo: { + nodeOffset: 0x2FFDD7C, + nodeRootOffset: 0x64 + }, + chatroomInfo: { + nodeOffset: 0x2FFDDC8, + nodeRootOffset: 0x8c8 + }, + sendTxtMsg: { + callOffset: 0xCE6C80 + }, + sendPicMsg: { + call1: 0x768140, + call2: 0xf59e40, + call3: 0xce6640 + } +}; +//3.9.2.23 + +// 全局配置 done +/*------------------global-------------------------------------------*/ +const availableVersion = 1661534743 ////3.9.2.23 ==0x63090217 + +const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') +const moduleLoad = Module.load('WeChatWin.dll') +//1575CF98 + +// 全局变量 tbd +const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// +const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() + +/*------------------global-------------------------------------------*/ + +/*---------------base -------------------------*/ + +// done +let retidPtr=null +let retidStruct=null +const initidStruct = ((str) => { + + retidPtr = Memory.alloc(str.length * 2 + 1) + retidPtr.writeUtf16String(str) + + retidStruct = Memory.alloc(0x14) // returns a NativePointer + + retidStruct + .writePointer(retidPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retidStruct +}) + +// done +let retPtr = null +let retStruct = null +const initStruct = ((str) => { + + retPtr = Memory.alloc(str.length * 2 + 1) + retPtr.writeUtf16String(str) + + retStruct = Memory.alloc(0x14) // returns a NativePointer + + retStruct + .writePointer(retPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retStruct +}) + +// done +let msgstrPtr=null +let msgStruct=null +const initmsgStruct = ((str) => { + msgstrPtr = Memory.alloc(str.length * 2 + 1) + msgstrPtr.writeUtf16String(str) + + msgStruct = Memory.alloc(0x14) // returns a NativePointer + + msgStruct + .writePointer(msgstrPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return msgStruct +}) + +// tbd +let retidNullStruct = null +let retidNullPtr = null +const initNullIdStruct = ((str) => { + + retidNullPtr = Memory.alloc(str.length * 2 + 1) + retidNullPtr.writeUtf16String(str) + + retidNullStruct = Memory.alloc(0x14) // returns a NativePointer + + retidNullStruct + .writePointer(retidNullPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retidNullStruct +}) + +// done +/** +* at msg structure +*/ +let atStruct = null +const initAtMsgStruct = ((wxidStruct) => { + + atStruct = Memory.alloc(0x10) + + atStruct.writePointer(wxidStruct).add(0x04) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) + .writeU32(0) + return atStruct +}) + +// done +// std::string +// const str = readStringPtr(ptr).readUtf8String() +const readStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(16).readU32() + const capacity = addr.add(20).readU32() + addr.ptr = addr + addr.size = size + addr.capacity = capacity + if (capacity > 15 && !addr.readPointer().isNull()) { + addr.ptr = addr.readPointer() + } + addr.ptr._readCString = addr.ptr.readCString + addr.ptr._readAnsiString = addr.ptr.readAnsiString + addr.ptr._readUtf8String = addr.ptr.readUtf8String + addr.readCString = () => { return addr.size ? addr.ptr._readCString(addr.size) : '' } + addr.readAnsiString = () => { return addr.size ? addr.ptr._readAnsiString(addr.size) : '' } + addr.readUtf8String = () => { return addr.size ? addr.ptr._readUtf8String(addr.size) : '' } + + // console.log('readStringPtr() address:',address,' -> str ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readStringPtr() str:' , addr.readUtf8String()) + // console.log('readStringPtr() address:', addr,'dump:', addr.readByteArray(24)) + + return addr +} + +// done +const readString = (address) => { + return readStringPtr(address).readUtf8String() +} + +// done +const readWideString = (address) => { + return readWStringPtr(address).readUtf16String() +} + +/*-----------------base-------------------------*/ + +let currentVersion = 0 + +let nodeList = [] //for contact +let contactList = [] //for contact + +let chatroomNodeList = [] //for chatroom +let chatroomMemberList = []//for chatroom +let loggedIn = false + +//开启日志 3.6.0.18 +// //[0x221c330+wechatwin.dll]+0xf8 +// 20220504 + + +let g_initTestAsm = null +let g_BufferEbp2C = null +let g_initECXU32 = null +let g_initECXPtr = null +let g_initEBXPtr = null +let g_initEBX = null + +let g_attatchEBP210Buffer = null +let g_attatchPathPtr = null +let g_attatchPath = null +let g_attatchEBPAc = null +let g_attatchEBPAcBufPtr = null + +let g_attatchContactIdPtr = null +//let g_attatchECXBuffer = null +let g_attatchESIU32 = null + +let g_initECXTempPtr = null +const initGlobal = ((contactId, attatchFile) => { + + //const base = moduleBaseAddress.add(0x222f38c).readPointer() + //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 + console.log('------------g_attatchEBPAc', g_attatchEBPAc) + console.log('------------g_EDIU32', g_EDIU32) + g_initTestAsm = Memory.alloc(Process.pageSize) + console.log('------------address', g_initTestAsm) + + g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) + g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() + g_initECXU32 = g_initECXPtr.toInt32() + g_attatchESIU32 = g_EDIU32 + + console.log('------------g_initECXU32', g_initECXU32) + console.log('------------g_initESIU32', g_attatchESIU32) + + + //console.log('==========g_initECXPtr',g_initECXPtr) + //console.log('==========g_EDIU32',g_EDIU32) + + //g_attatchECXBuffer = Memory.alloc(0x1024) + //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) + + g_BufferEbp2C = Memory.alloc(0x48) + + //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() + //g_initEBXPtr = Memory.alloc(0x14).writePointer(g_initEBX) + //g_initEBXPtr.add(0x08).writePointer(g_BufferEbp2C) + + g_attatchPathPtr = Memory.alloc(attatchFile.length * 2 + 2) + g_attatchPathPtr.writeUtf16String(attatchFile) + + g_attatchPath = Memory.alloc(0x28) + g_attatchPath.writePointer(g_attatchPathPtr).add(0x04) + .writeU32(attatchFile.length * 2).add(0x04) + .writeU32(attatchFile.length * 2).add(0x04) + /*---------------------------------ebp-210----------------*/ + g_attatchEBP210Buffer = Memory.alloc(0x48) + g_attatchEBP210Buffer.writeU32(0x3) + g_attatchEBP210Buffer.add(0x4).writePointer(g_attatchPathPtr) + g_attatchEBP210Buffer.add(0x8).writeU32(attatchFile.length * 2) + g_attatchEBP210Buffer.add(0xC).writeU32(attatchFile.length * 2) + g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) + //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) + /*---------------------------------ebp-210----------------*/ + + //g_attatchContactIdPtr = Memory.alloc(0x4) + //g_attatchContactIdPtr.writeUtf16String(contactId) + //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) + g_attatchEBPAc = Memory.alloc(0x140) + //g_attatchEBPAcBufPtr = Memory.alloc(0x100) + //g_attatchEBPAc.writePointer(g_attatchEBP210Buffer) + //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) + //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) + g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) + console.log('------------g_attatchEBPAc', g_attatchEBPAc) + + /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) + g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) + .add(0x04).writeU32(contactId.length*2)*/ + + + + //g_attatchESIU32 = g_EDI.toInt32() + + + //console.log('------------g_attatchESIU32',g_attatchESIU32) + //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) + + Memory.patchCode(g_initTestAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: g_initTestAsm }) + cw.putPushfx() + cw.putPushax() + //cw.putMovRegAddress('eax', g_attatchEBP210Buffer) + + /*cw.putMovRegAddress('edi',g_EDIPtr) + cw.putMovRegReg('esi','edi') + cw.putMovRegAddress('eax',g_BufferEbp2C) + cw.putMovRegAddress('ecx',g_initECXTempPtr) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ + + //cw.putMovRegOffsetPtrU32('ebp', -20, 0) + + /*cw.putPushU32(0) + cw.putMovRegAddress('eax', g_attatchPathPtr) + cw.putPushReg('eax') + cw.putPushU32(3) + cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) + cw.putCallAddress(moduleBaseAddress.add(0x130220))*/ + + + /*cw.putMovRegAddress('edi', g_attatchEBPAc)//后面要用的的ebp-2c + cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) + cw.putPushReg('ecx') + cw.putMovRegU32('eax',0) + cw.putPushReg('eax')//push eax + cw.putMovRegReg('ecx', 'edi') + cw.putMovRegAddress('esi',g_attatchPathPtr) + cw.putCallAddress(moduleBaseAddress.add(0x138880))*/ + + + /*cw.putSubRegImm('esp',0x14) + cw.putMovRegU32('ecx',g_initECXU32) + cw.putMovRegU32('esi',g_attatchESIU32) + cw.putMovRegAddress('eax', g_attatchEBPAc) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x173620))*/ + //cw.putCallAddress(moduleBaseAddress.add(0x522590)) + + /** g_attatchEBPAc*/ + //cw.putMovRegNearPtr('eax', g_attatchEBPAc) + //cw.putMovNearPtrReg(g_attatchEBPAc.add(0xc), 'eax') + /** g_attatchEBPAc*/ + + + //cw.putMovRegAddress('ebx', g_initEBXPtr) + //cw.putMovRegU32('edi', g_EDI.toInt32()) + //cw.putMovRegU32('esi', g_EDI.toInt32()) + /*cw.putMovRegU32('ecx', g_initECX) + cw.putMovRegAddress('eax', g_BufferEbp2C) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x131BB0))*/ + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(g_initTestAsm), 'void', []) + nativeativeFunction() +}) + + +let g_personal_detail_ebx = null +let g_personal_detail_asm = null +let g_personal_wxid = null +let g_personal_wxid_ptr = null +const getOldTest = ((wxid) => {//personal detail + + g_personal_detail_asm = Memory.alloc(Process.pageSize) + g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() + + g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) + g_personal_wxid_ptr.writeUtf16String(wxid) + + g_personal_wxid = Memory.alloc(0x14) + g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) + .writeU32(wxid.length * 2).add(0x04) + .writeU32(wxid.length * 2).add(0x08) + + console.log('-----------address----------', g_personal_detail_asm) + + Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: g_personal_detail_asm }) + cw.putPushfx() + cw.putPushax() + + cw.putCallAddress(moduleBaseAddress.add(0x9A000)) + + //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | + cw.putMovRegU32('ebx', g_personal_detail_ebx) + cw.putMovRegReg('esi', 'eax') + cw.putPushReg('ebx') + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', g_personal_wxid) + cw.putMovRegReg('ecx', 'esp') + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) + cw.putMovRegReg('ecx', 'esi') + cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(g_personal_detail_asm), 'void', []) + nativeativeFunction() + + +}) + +// const writeLogNativeCallback = (() => { +// const nativeCallback = new NativeCallback(() => { }, 'void', []) +// const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) + +// Interceptor.attach( +// moduleBaseAddress.add(0x7008A4), +// { +// onEnter() { +// const addr = this.context.eax//.sub(0x114)//0xc30-0x08 +// if(addr >0){ +// const log = ptr(addr).readAnsiString() +// } +// } +// }) +// return nativeCallback +// })() + +/** + * test call + */ +let attatchTestAsm = null +let attatchTestEbp2C = null +let attatchGlobalEDI = null +let attatchGlobalEDIB88 = null +let attatchTestEBX = null +let g_tempEcx = null +let attatchFirstECX = null +//let attatchFirstECX = null +let gattatchFilePtr = null +let gattatchFile = null +let gattatchReceiveIdPtr = null +let gattatchReceiveId = null +let attatchEAX3B0Buf = null + +let attatchESIbuf = null +const getWxTest = ((contactId, filePath) => { + //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) + //nativeativeFunction() + attatchTestAsm = Memory.alloc(Process.pageSize) + console.log('----------------address', attatchTestAsm) + attatchTestEbp2C = Memory.alloc(0xC) + attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() + .add(0x938).add(0x438).readPointer() + attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() + attatchTestEBX = Memory.alloc(0x4) + attatchTestEBX.writePointer(attatchGlobalEDI) + console.log('----------------attatchGlobalEDI', attatchGlobalEDI) + console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) + + attatchFirstECX = Memory.alloc(0x28) + //const attatchSecondEcx = Memory.alloc(0x14) + + const contactIdLength = contactId.length * 2 + 2//edx + const contractIdActLength = contactId.length + + + gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) + gattatchReceiveIdPtr.writeUtf16String(contactId) + + gattatchReceiveId = Memory.alloc(0x14) + gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x08) + //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) + //return + /*console.log(hexdump(attatchTestEBX, { + offset: 0, + length: 0x40, + header: true, + ansi: true + })) + return*/ + + gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) + gattatchFilePtr.writeUtf16String(filePath) + + gattatchFile = Memory.alloc(0x14) + gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) + .writeU32(filePath.length * 2).add(0x04) + .writeU32(filePath.length * 2).add(0x08) + + const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() + + attatchEAX3B0Buf = Memory.alloc(0x3B0) + + g_tempEcx = Memory.alloc(0x4) + //g_tempEcx1 = Memory.alloc(0x4) + + attatchESIbuf = Memory.alloc(0x100) + attatchESIbuf.add(0x0).writeU32(3) + attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) + attatchESIbuf.add(0x8).writeU32(filePath.length * 2) + attatchESIbuf.add(0xc).writeU32(filePath.length * 2) + + Memory.patchCode(attatchTestAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: attatchTestAsm }) + cw.putPushfx() + cw.putPushax() + //cw.putMovRegU32('edi',attatchGlobalEDI) + + + cw.putMovRegAddress('esi', attatchESIbuf) + + cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) + cw.putMovRegAddress('eax', gattatchFile) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) + + + cw.putMovRegAddress('ecx', attatchFirstECX) + cw.putMovRegU32('eax', contactIdLength) + cw.putPushReg('eax') + cw.putPushU32(0) + cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) + cw.putAddRegImm('esp', 0x8) + + cw.putMovRegReg('edx', 'eax') + cw.putMovNearPtrReg(attatchFirstECX, 'edx') + cw.putMovRegU32('edi', contactIdLength) + cw.putPushReg('edi') + cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 + cw.putPushReg('eax') + cw.putMovRegNearPtr('eax', attatchFirstECX) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) + cw.putAddRegImm('esp', 0x0c) + + //cw.putMovRegNearPtr('ecx',attatchFirstECX) + //cw.putAddRegImm('esp', 0x0c) + //cw.putMovRegU32('edx', 0) + //cw.putMovRegRegPtr('eax', 'ecx') + //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') + cw.putMovRegU32('edi', contactId.length * 2) + cw.putMovRegU32('ecx', attatchLastECX) + cw.putMovRegAddress('eax', attatchEAX3B0Buf) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x392260)) + + cw.putMovRegAddress('ecx', attatchEAX3B0Buf) + cw.putCallAddress(moduleBaseAddress.add(0x94200)) + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(attatchTestAsm), 'void', []) + nativeativeFunction() + + +}) +// 001 +const getTestInfoFunction = ((addr) => { + const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) + nativeativeFunction() + + //00CFE484 + + + /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { + onAccess(details){ + console.log('============') + console.log(details.operation) + console.log(details.from) + console.log(details.address) + console.log('============') + } + })*/ + +}) + +// 002get global data + +const isLoggedInFunction = (() => { + loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() + return !!loggedIn +}) + +// 007 缺失,请标注已废弃或者其他原因 +const getMyselfIdFunction = (() => { + + let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) + + return wx_id + +}) + +// std::wstring +// const wstr = readWStringPtr(ptr).readUtf16String() +const readWStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(4).readU32() + const capacity = addr.add(8).readU32() + addr.ptr = addr.readPointer() + addr.size = size + addr.capacity = capacity + addr.ptr._readUtf16String = addr.ptr.readUtf16String + addr.readUtf16String = () => { return addr.size ? addr.ptr._readUtf16String(addr.size * 2) : '' } + + // console.log('readWStringPtr() address:',address,' -> ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readWStringPtr() str:' , `"${addr.readUtf16String()}"`,'\n',addr.ptr.readByteArray(addr.size*2+2),'\n') + // console.log('readWStringPtr() address:', addr,'dump:', addr.readByteArray(16),'\n') + + return addr +} + +//contact +const recurse = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { return } + + if (node.equals(headerNodeAddress)) { return } + + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } + + + nodeList.push(node) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x38)) + + //custom id, if not set return null, and use wxid which should be custom id + const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + + //custom Nickname + const name = readWideString(node.add(0x94)) + + //alias aka 'remark' in wechat + const alias = readWideString(node.add(0x80)) + + //avatarUrl + const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + const gender = node.add(0x18C).readU32() + + const contactJson = { + id: wxid, + code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender, + } + + contactList.push(contactJson) + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() + + recurse(leftNode) + recurse(centerNode) + recurse(rightNode) + + const allContactJson = contactList + return allContactJson + +}) + +// 010 done +const getWechatVersionFunction = (() => { + if (currentVersion) { + return currentVersion + } + const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' + const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) + if (results.length == 0) { + return 0 + } + const addr = results[0].address + const ret = addr.add(0x07).readPointer() + const ver = ret.add(0x0).readU32() + currentVersion = ver + return ver +}) + +// 011 done +const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { + if (!ver) { + return '0.0.0.0' + } + const vers = [] + vers.push((ver >> 24) & 255 - 0x60) + vers.push((ver >> 16) & 255) + vers.push((ver >> 8) & 255) + vers.push(ver & 255) + return vers.join('.') +}) + +// 012 done +const checkSupportedFunction = (() => { + const ver = getWechatVersionFunction() + return ver == availableVersion +}) + +/** + * @Hook: recvMsg -> recvMsgNativeCallback + */ + +// 019 done +const recvMsgNativeCallback = (() => { + + + const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + + Interceptor.attach( + moduleBaseAddress.add(offset.hook_point), { + onEnter() { + const addr = this.context.ecx //0xc30-0x08 + const msgType = addr.add(0x38).readU32() + const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg + + if (msgType > 0) { + + const talkerIdPtr = addr.add(0x48).readPointer() + //console.log('txt msg',talkerIdPtr.readUtf16String()) + const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 + + const myTalkerIdPtr = Memory.alloc(talkerIdLen) + Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) + + + let contentPtr = null + let contentLen = 0 + let myContentPtr = null + if (msgType == 3) { // pic path + let thumbPtr = addr.add(0x198).readPointer(); + let hdPtr = addr.add(0x1ac).readPointer(); + let thumbPath = thumbPtr.readUtf16String(); + let hdPath = hdPtr.readUtf16String(); + let picData = [ + thumbPath, // PUPPET.types.Image.Unknown + thumbPath, // PUPPET.types.Image.Thumbnail + hdPath, // PUPPET.types.Image.HD + hdPath // PUPPET.types.Image.Artwork + ] + let content = JSON.stringify(picData); + myContentPtr = Memory.allocUtf16String(content); + } else { + contentPtr = addr.add(0x70).readPointer() + contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 + myContentPtr = Memory.alloc(contentLen) + Memory.copy(myContentPtr, contentPtr, contentLen) + } + + // console.log('----------------------------------------') + // console.log(msgType) + // console.log(contentPtr.readUtf16String()) + // console.log('----------------------------------------') + const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 + let myGroupMsgSenderIdPtr = null + if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 + + myGroupMsgSenderIdPtr = Memory.alloc(0x10) + myGroupMsgSenderIdPtr.writeUtf16String("null") + + } else { + + const groupMsgSenderIdPtr = addr.add(0x170).readPointer() + const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 + myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) + Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) + + } + + const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 + let myXmlContentPtr = null + if (xmlNullPtr == 0) { + + myXmlContentPtr = Memory.alloc(0x10) + myXmlContentPtr.writeUtf16String("null") + + } else { + const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + + const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 + myXmlContentPtr = Memory.alloc(xmlContentLen) + Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + } + + setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) + } + } + }) + return nativeCallback +})() + +// 003get myself info done +const getBaseNodeAddress = (() => { + return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() +}) + +// 004 done +const getHeaderNodeAddress = (() => { + const baseAddress = getBaseNodeAddress() + //console.log('baseAddress',baseAddress) + if (baseAddress.isNull()) { + return baseAddress + } + + //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) + return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() +}) + +// 006 done +const getMyselfInfoFunction = (() => { + + let ptr = 0 + let wx_code = '' + let wx_id = '' + let wx_name = '' + let head_img_url = '' + + const base = moduleBaseAddress.add(offset.myselfinfo.offset) + const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() + + if (wxid_len === 0x13) { // 新版本微信 + wx_id = base.readPointer().readAnsiString(wxid_len) + wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() + } else { + wx_id = readString(base) + wx_code = wx_id + } + + + wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) + const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() + const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() + + head_img_url = img_addr.readAnsiString(img_len) + + const myself = { + id: wx_id, + code: wx_code, + name: wx_name, + head_img_url: head_img_url, + }; + + return JSON.stringify(myself) + +}) + +// done +const recurseNew = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { + return + } + + if (node.equals(headerNodeAddress)) { + return + } + + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } + + + nodeList.push(node) + const id = readString(node.add(0x8)) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x30)) + //console.log('-----------',wxid) + + + //custom id, if not set return null, and use wxid which should be custom id + //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + + //custom Nickname + const name = readWideString(node.add(0x8c)) + + //alias aka 'remark' in wechat + //const alias = readWideString(node.add(0x80)) + + //avatarUrl + //const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + //const gender = node.add(0x18C).readU32() + + const contactJson = { + id1: id, + id: wxid, + name: name, + /*code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender,*/ + } + + contactList.push(contactJson) + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + //const rightNode = node.add(0x08).readPointer() + + recurseNew(leftNode) + recurseNew(centerNode) + //recurse(rightNode) + + const allContactJson = contactList + return allContactJson + +}) + +// done ? +const getContactNativeFunction = (() => { + const headerNodeAddress = getHeaderNodeAddress() + //console.log('headerNodeAddress',headerNodeAddress) + + if (headerNodeAddress.isNull()) { + return '[]' + } + + const node = headerNodeAddress.add(0x0).readPointer() + const ret = recurseNew(node) + + //console.log(ret) + + console.log('getContactNativeFunction:', ret.length) + /*for (let item of ret){ + console.log(JSON.stringify(item)) + }*/ + //console.log(ret.contact) + const cloneRet = JSON.stringify(ret) + nodeList.length = 0 + contactList.length = 0 + + return cloneRet +}) + +// 005 done +const getChatroomNodeAddress = (() => { + const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() + if (baseAddress.isNull()) { + return baseAddress + } + return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() +}) + +// 008chatroom member done +const chatroomRecurse = ((node) => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return + } + + if (node.equals(chatroomNodeAddress)) { + return + } + + for (const item in chatroomNodeList) { + if (node.equals(chatroomNodeList[item])) { + return + } + } + + chatroomNodeList.push(node) + const roomid = readWideString(node.add(0x10)) + + const len = node.add(0x54).readU32() // + //const memberJson={} + if (len > 4) { // + const memberStr = readString(node.add(0x44)) + if (memberStr.length > 0) { + const memberList = memberStr.split(/[\\^][G]/) + const memberJson = { + roomid: roomid, + roomMember: memberList + } + + chatroomMemberList.push(memberJson) + } + + } + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() + + chatroomRecurse(leftNode) + chatroomRecurse(centerNode) + chatroomRecurse(rightNode) + + const allChatroomMemberJson = chatroomMemberList + return allChatroomMemberJson +}) + +// 009 done +const getChatroomMemberInfoFunction = (() => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return '[]' + } + + const node = chatroomNodeAddress.add(0x0).readPointer() + const ret = chatroomRecurse(node) + + const cloneRet = JSON.stringify(ret) + chatroomNodeList.length = 0 //empty + chatroomMemberList.length = 0 //empty + return cloneRet +}) + +// 024 done +/** + * sendMsgNativeFunction + * send text message + * @param {string} talkerId = wxid or roomid + * @param {string} content + */ + const sendMsgNativeFunction = ((talkerId, content) => { + + const txtAsm = Memory.alloc(Process.pageSize) + //const buffwxid = Memory.alloc(0x20) + + + let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) + wxidPtr.writeUtf16String(talkerId) + + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(wxidPtr)).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + + let contentPtr = Memory.alloc(content.length * 2 + 2) + contentPtr.writeUtf16String(content) + + const sizeOfStringStruct = Process.pointerSize * 5 + let contentStruct = Memory.alloc(sizeOfStringStruct) + + contentStruct + .writePointer(contentPtr).add(0x4) + .writeU32(content.length).add(0x4) + .writeU32(content.length * 2) + + + const ecxBuffer = Memory.alloc(0x2d8) + + Memory.patchCode(txtAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: txtAsm + }) + cw.putPushfx() + cw.putPushax() + + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + cw.putPushU32(0x0) + + //cw.putMovRegReg + + cw.putMovRegAddress('eax', contentStruct) + cw.putPushReg('eax') + + cw.putMovRegAddress('edx', picWxid) //room_id + + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) + + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + console.log('----------txtAsm', txtAsm) + const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + nativeativeFunction() + +}) + +// 023 done +/** +* send at msg +*/ +let asmAtMsg = null +let roomid_, msg_, wxid_, atid_ +let ecxBuffer +const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { + + asmAtMsg = Memory.alloc(Process.pageSize) + ecxBuffer = Memory.alloc(0x3b0) + + const atContent = '@'+nickname+' '+text + + roomid_ = initStruct(roomId) + wxid_ = initidStruct(contactId) + msg_ = initmsgStruct(atContent) + atid_ = initAtMsgStruct(wxid_) + + Memory.patchCode(asmAtMsg, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: asmAtMsg + }) + cw.putPushfx() + cw.putPushax() + + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + //cw.putPushU32(0x0) + cw.putMovRegAddress('eax', atid_) + cw.putPushReg('eax') + + //cw.putMovRegReg + + cw.putMovRegAddress('eax', msg_) + cw.putPushReg('eax') + + cw.putMovRegAddress('edx', roomid_) //room_id + + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) + + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + //console.log('----------txtAsm', asmAtMsg) + const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + nativeativeFunction() + +}) + +// 022 done +/** + * + * @param {*} contactId + * @param {*} path + */ + const sendPicMsgNativeFunction = ((contactId, path) => { + + const picAsm = Memory.alloc(Process.pageSize) + const buffwxid = Memory.alloc(0x20) + const picbuff = Memory.alloc(0x2D8) + + let pathPtr = Memory.alloc(path.length * 2 + 1) + pathPtr.writeUtf16String(path) + + let imagefilepath = Memory.alloc(0x24) + imagefilepath.writePointer(pathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) + + let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) + picWxidPtr.writeUtf16String(contactId) + + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(picWxidPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + + + //const test_offset1 = 0x701DC0; + Memory.patchCode(picAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: picAsm + }) + cw.putPushfx(); + cw.putPushax(); + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call1 + )) + cw.putMovRegReg('edx', 'eax') //缓存 + + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', buffwxid) + cw.putMovRegReg('ecx', 'esp') + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call2 + )) + + cw.putMovRegReg('ecx', 'edx') + cw.putMovRegAddress('eax', picWxid) //=lea + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('edi') + cw.putPushReg('eax') + cw.putMovRegAddress('eax', picbuff) + cw.putPushReg('eax') + + cw.putMovRegAddress('edi', picWxid) //edi + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call3 + )) + + + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + //console.log('----------picAsm',picAsm) + const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) + nativeativeFunction() + +}) + +// 020 done +let memberNickBuffAsm = null +let nickRoomId = null +let nickMemberId = null +let nickBuff = null +const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { + + nickBuff = Memory.alloc(0x7e4) + //const nickRetAddr = Memory.alloc(0x04) + memberNickBuffAsm = Memory.alloc(Process.pageSize) + //console.log('asm address----------',memberNickBuffAsm) + nickRoomId = initidStruct(roomId) + //console.log('nick room id',nickRoomId) + nickMemberId = initStruct(memberId) + + //console.log('nick nickMemberId id',nickMemberId) + //const nickStructPtr = initmsgStruct('') + + Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: memberNickBuffAsm + }) + cw.putPushfx() + cw.putPushax() + cw.putMovRegAddress('edi', nickRoomId) + cw.putMovRegAddress('eax', nickBuff) + cw.putMovRegReg('edx', 'edi') + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', nickMemberId) + cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) + cw.putAddRegImm('esp', 0x04) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) + nativeativeFunction() + + const nickname = readWideString(nickBuff) + console.log('----nickname', nickname) + return readWideString(nickBuff) +}) + +// 013 +const isSupported = checkSupportedFunction() + +if (!isSupported) { + throw new Error(`Wechat version not supported. \nWechat version: ${getWechatVersionStringFunction()}, supported version: ${getWechatVersionStringFunction(availableVersion)}`) +} + +// 015 +const hookLogoutEventCallback = (() => { + const nativeCallback = new NativeCallback(() => { }, 'void', ['int32']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32']) + Interceptor.attach(moduleBaseAddress.add(offset.hook_on_logout_offset), { + onEnter: function (args) { + const bySrv = args[0].toInt32() + setImmediate(() => nativeativeFunction(bySrv)) + } + }) + return nativeCallback +})() + +// 016 +const hookLoginEventCallback = (() => { + const nativeCallback = new NativeCallback(() => { }, 'void', []) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', []) + Interceptor.attach(moduleBaseAddress.add(offset.hook_on_login_offset), { + onLeave: function (retval) { + isLoggedInFunction() + setImmediate(() => nativeativeFunction()) + return retval + } + }) + + setTimeout(() => { + if (isLoggedInFunction()) { + setImmediate(() => nativeativeFunction()) + } + }, 500); + + return nativeCallback +})() + +// 017 +const checkQRLoginNativeCallback = (() => { + + const nativeCallback = new NativeCallback(() => { }, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'pointer', 'int32', 'pointer']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'pointer', 'int32', 'pointer']) + // const json = { + // status, + // uuid, + // wxid, + // avatarUrl, + // nickname, + // phoneType, + // phoneClientVer, + // pairWaitTip, + // } + + const callback = { + onLeave: function (retval) { + const json = getQrcodeLoginData() + if (json.status == 0) { + // 当状态为 0 时,即未扫码。而其他状态会触发另一个方法,拥有更多数据。 + ret(json) + } + return retval + }, + } + + const ret = (json) => { + const arr = [ + json.status || 0, + Memory.allocUtf8String(json.uuid ? `http://weixin.qq.com/x/${json.uuid}` : ''), + Memory.allocUtf8String(json.wxid || ''), + Memory.allocUtf8String(json.avatarUrl || ''), + Memory.allocUtf8String(json.nickname || ''), + Memory.allocUtf8String(json.phoneType || ''), + json.phoneClientVer || 0, + Memory.allocUtf8String(json.pairWaitTip || ''), + ] + setImmediate(() => nativeativeFunction(...arr)) + } + + Interceptor.attach(moduleBaseAddress.add(offset.hook_get_login_qr_offset), callback) + Interceptor.attach(moduleBaseAddress.add(offset.hook_check_login_qr_offset), callback) + Interceptor.attach(moduleBaseAddress.add(offset.hook_save_login_qr_info_offset), { + onEnter: function () { + const qrNotify = this.context['ebp'].sub(72) + const uuid = readString(qrNotify.add(4).readPointer()) + const wxid = readString(qrNotify.add(8).readPointer()) + const status = qrNotify.add(16).readUInt() + const avatarUrl = readString(qrNotify.add(24).readPointer()) + const nickname = readString(qrNotify.add(28).readPointer()) + const pairWaitTip = readString(qrNotify.add(32).readPointer()) + const phoneClientVer = qrNotify.add(40).readUInt() + const phoneType = readString(qrNotify.add(44).readPointer()) + + const json = { + status, + uuid, + wxid, + avatarUrl, + nickname, + phoneType, + phoneClientVer, + pairWaitTip, + } + ret(json) + }, + onLeave: function (retval) { + return retval + }, + }) + + if (!isLoggedInFunction()) { + setTimeout(() => { + const json = getQrcodeLoginData() + ret(json) + }, 100); + } + + return nativeCallback +})() + +// 018 +const getQrcodeLoginData = () => { + const getQRCodeLoginMgr = new NativeFunction(moduleBaseAddress.add(offset.get_qr_login_data_offset), 'pointer', []) + const qlMgr = getQRCodeLoginMgr() + + const json = { + status: 0, + uuid: '', + wxid: '', + avatarUrl: '', + } + + if (!qlMgr.isNull()) { + json.uuid = readString(qlMgr.add(8)) + json.status = qlMgr.add(40).readUInt() + json.wxid = readString(qlMgr.add(44)) + json.avatarUrl = readString(qlMgr.add(92)) + } + return json +} + + +/** + * 20220504 writelog + * 7A566D72 | FFB5 ECFEFFFF | push dword ptr ss:[ebp-114] | 【3.6.0.18】写日志,这个里面就是日志内容 + 7A566D78 | FFB5 E8FEFFFF | push dword ptr ss:[ebp-118] | + */ +/*const writeLogNativeCallback = (() => { + const nativeCallback = new NativeCallback(() => { }, 'void', []) + const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) + + Interceptor.attach( + moduleBaseAddress.add(0x1576D7E), + { + onEnter() { + const addr = this.context.ebp.sub(0x114)//0xc30-0x08 + console.log('-------',addr) + + } + }) + return nativeCallback +})()*/ + +let nickRoomIdV6 = null +let nullEdiWxidStructV6 = null +let nickMemberIdStructV6 = null +let memberNickBuffAsmV6 = null +let nickResultEdiV6 = null + +const getChatroomMemberNickInfoV1Function = ((memberId, roomId) => { + nickRoomIdV6 = initidStruct(roomId) + nullEdiWxidStructV6 = initNullIdStruct('') + nickMemberIdStructV6 = initStruct(memberId) + memberNickBuffAsmV6 = Memory.alloc(Process.pageSize) + console.log('-----', memberNickBuffAsmV6) + + const tmp = (moduleBaseAddress.add( + offset.chatroom_member_nick_esi_offset_v6 + )).readU32() + console.log('=======tmp', tmp) + Memory.patchCode(memberNickBuffAsmV6, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: memberNickBuffAsmV6 }) + cw.putPushfx(); + cw.putPushax(); + + cw.putMovRegAddress('edi', nullEdiWxidStructV6) + cw.putMovRegAddress('eax', nickMemberIdStructV6) + cw.putMovRegAddress('ebx', nickRoomIdV6) + + + cw.putMovRegAddress('esi', ptr(tmp)) + cw.putPushReg('edi') + cw.putPushReg('eax') + cw.putPushReg('ebx') + + cw.putMovRegAddress('ecx', ptr(tmp)) + + cw.putCallAddress(moduleBaseAddress.add( + offset.chatroom_member_nick_call_offset_v6 + )) + + + //cw.putMovNearPtrReg(nickResultEdiV6, 'edi') + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsmV6), 'void', []) + nativeativeFunction() + + console.log('---------nullEdiWxidStructV6', nullEdiWxidStructV6) + const nickha = readWideString(nullEdiWxidStructV6) + + console.log('-----------------') + console.log(nickha) + console.log('-----------------') + return readWideString(nullEdiWxidStructV6) +}) + +/** +* send attatch +*/ +let attatchWxid = null +let attatchPath = null +let attatchPathPtr = null +let attatchAsm = null +let attatchBuf = null + +let attatchReceiveIdPtr = null +let attatchReceiveId = null + +let attatchSendId = null +let attatchSendIdPtr = null + +/* +let attatchEbp2C = null +let attatchEDIPtr = null +let attatchEDIU32 = null +let attatchECX = null + +let attatchEbp210 = null +let attatchEbpAc = null*/ + +let attatchEbp11E8 = null +let attatchEbpCC = null +let attatchEbp368 = null +let attatchEAX = null + +let sFileName = null +let fileNamePtr = null + +let attatchEbp84 = null +let attatchECX = null + + +/** + * +param {78EDBC86 | 8B80 38040000 | mov eax,dword ptr ds:[eax+438] | +78EDBC8C | 8BB0 800B0000 | mov esi,dword ptr ds:[eax+B80] |} sendWxid +*/ + +// 021 +const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size) => { + + attatchAsm = Memory.alloc(Process.pageSize) + console.log('--------------address', attatchAsm) + + fileNamePtr = Memory.alloc(filename.length * 2 + 2) + fileNamePtr.writeUtf16String(filename) + + sFileName = Memory.alloc(0x14) + sFileName.writePointer(ptr(fileNamePtr)).add(0x04) + .writeU32(fileNamePtr.length * 2).add(0x04) + .writeU32(fileNamePtr.length * 2).add(0x08) + + //const fileSize = size.toInt32() + + + attatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) + attatchReceiveIdPtr.writeUtf16String(contactId) + + attatchReceiveId = Memory.alloc(0x14) + attatchReceiveId.writePointer(ptr(attatchReceiveIdPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x08) + + attatchSendIdPtr = Memory.alloc(senderId.length * 2 + 2) + attatchSendIdPtr.writeUtf16String(senderId) + + attatchSendId = Memory.alloc(0x14) + attatchSendId.writePointer(ptr(attatchSendIdPtr)).add(0x04) + .writeU32(senderId.length * 2).add(0x04) + .writeU32(senderId.length * 2).add(0x08) + + attatchPathPtr = Memory.alloc(path.length * 2 + 2) + attatchPathPtr.writeUtf16String(path) + + attatchPath = Memory.alloc(0x28) + attatchPath.writePointer(attatchPathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) + + attatchEbp11E8 = Memory.alloc(0xBE4) + attatchEbpCC = Memory.alloc(0x14) + attatchEbp368 = Memory.alloc(0x290) + attatchEbp84 = Memory.alloc(0x18) + attatchEAX = Memory.alloc(0x18) + + attatchECX = moduleBaseAddress.add(0x222f178).toInt32() + + //console.log('basename',path.basename(path)) + //return + + /** + * -------------buffer------------------------------- + */ + + Memory.patchCode(attatchAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: attatchAsm }) + cw.putPushfx() + cw.putPushax() + + cw.putMovRegAddress('ecx', attatchEbp11E8) + cw.putCallAddress(moduleBaseAddress.add(0xE1590)) + + cw.putPushU32(-1) + cw.putPushU32(moduleBaseAddress.add(0x1E1B3C0).toInt32()) + cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x4))//11e4 + cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid + // cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid + + /** + * 78482B8D | 6A FF | push FFFFFFFF | + 78482B8F | 68 B895E979 | push wechatwin.79E995B8 | 79E995B8:L"0" + 78482B94 | 8D8D 48EEFFFF | lea ecx,dword ptr ss:[ebp-11B8] | + 78482B9A | E8 71F83600 | call wechatwin.787F2410 | 此处继续写ebp-11b8 + */ + cw.putPushU32(-1) + cw.putPushU32(moduleBaseAddress.add(0x1DA95B8).toInt32()) + cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x30))//11B8 + cw.putCallAddress(moduleBaseAddress.add(0x702410)) + + cw.putMovRegU32('eax', 0x6) + cw.putMovNearPtrReg(attatchEbp11E8.add(0x80), 'eax')//1168 + cw.putMovRegU32('eax', size)//file size + cw.putMovNearPtrReg(attatchEbp11E8.add(0x108), 'eax')//10e0 + + + cw.putMovRegAddress('eax', sFileName) + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x44))//11a4=0x11e8-0x160 + cw.putCallAddress(moduleBaseAddress.add(0x702980))//write filename + + cw.putMovRegAddress('eax', attatchSendId) + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x160))//1088=0x11e8-0x160 + cw.putCallAddress(moduleBaseAddress.add(0x702980)) + + + cw.putMovRegAddress('eax', attatchEbpCC) + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', attatchEbp11E8) + cw.putCallAddress(moduleBaseAddress.add(0x617C30)) + + cw.putMovRegAddress('ecx', attatchEbp368) + cw.putCallAddress(moduleBaseAddress.add(0x954F0)) + + cw.putPushU32(-1) + cw.putPushU32((moduleBaseAddress.add(0x1D8F248)).toInt32()) + cw.putMovRegAddress('ecx', attatchEbp84) + cw.putCallAddress(moduleBaseAddress.add(0x701CD0)) + + cw.putMovRegAddress('ecx', attatchPath) + cw.putPushU32(0x6) + cw.putMovRegAddress('edx', attatchEbp11E8.add(0x160))//1088 + //cw.putMovRegAddress('eax',attatchEAX) + cw.putPushReg('ecx') + cw.putPushReg('eax') + + cw.putMovRegAddress('eax', attatchEbpCC) + cw.putPushReg('eax') + + cw.putMovRegAddress('eax', attatchReceiveId) + cw.putPushReg('eax') + + cw.putMovRegAddress('ecx', attatchEbp368) + cw.putCallAddress(moduleBaseAddress.add(0x391F80)) + cw.putAddRegImm('esp', 0x14) + + + cw.putPushU32(moduleBaseAddress.add(0x223EC34).toInt32()) + cw.putPushU32(moduleBaseAddress.add(0x223EC34).toInt32()) + //cw.putMovRegU32('edx',0xAD0001) 两行代码都可以 + cw.putAddRegImm('edx', 0x1) + cw.putMovRegAddress('ecx', attatchEbp368) + cw.putCallAddress(moduleBaseAddress.add(0x392150)) + cw.putAddRegImm('esp', 0x8) + + + + //cw.putMovRegAddress('ecx', attatchEbp368) + //cw.putCallAddress(moduleBaseAddress.add(0x63B4F0)) + // 7B53F178 + //cw.putMovRegU32('ecx', attatchEbpCC.add(0x3c).toInt32())//ebp-90 + //cw.putMovNearPtrReg(attatchEbpCC.add(0x64), 'eax')//ebp-68 + //cw.putAddRegImm('ecx', 0x8) + //cw.putMovRegAddress('eax', attatchEbp368.add(0x64))//ebp-68 + + //cw.putMovRegU32('ecx',attatchECX) + //cw.putPushReg('eax') + //cw.putMovRegAddress('eax', attatchEbpCC.add(0x40))//ebp-8c + //cw.putPushReg('eax') + //cw.putCallAddress(moduleBaseAddress.add(0xC9D30)) + //cw.putCallAddress(moduleBaseAddress.add(0x522590)) + //78483063 | E8 28F51800 | call wechatwin.78612590 | + + + //78483039 | 8D8D 98FCFFFF | lea ecx,dword ptr ss:[ebp-368] | + //7848303F | E8 AC842A00 | call wechatwin.7872B4F0 | + //cw.putMovRegAddress('ecx', attatchEbp368) + //cw.putCallAddress(moduleBaseAddress.add(0x63B4F0)) + + // 78F33099 | 8D8D 34FFFFFF | lea ecx,dword ptr ss:[ebp-CC] | + //78F3309F | E8 AC0FD0FF | call wechatwin.78C34050 | + + //cw.putMovRegAddress('ecx',attatchEbpCC) + //cw.putCallAddress(moduleBaseAddress.add(0x94050)) + + /** + * 78F3307F | 8B4D AC | mov ecx,dword ptr ss:[ebp-54] | + 78F33082 | 8D85 98FCFFFF | lea eax,dword ptr ss:[ebp-368] | + 78F33088 | 50 | push eax | + 78F33089 | E8 82DACFFF | call wechatwin.78C30B10 | + + cw.putMovRegAddress('ecx', attatchEbp54) + cw.putMovRegAddress('eax', attatchEbp368) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x90B10))*/ + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(attatchAsm), 'void', []) + nativeativeFunction() + /*console.log(hexdump(attatchEbp11E8.add(0x80), { + offset: 0, + length: 0x40, + header: true, + ansi: true + }))*/ + //console.log('') + /*console.log(hexdump(attatchEbpCC.add(0x160), { + offset: 0, + length: 0x64, + header: true, + ansi: true + }))*/ + //console.log('-------',attatchEbp1C.readPointer()) + //console.log('-------',attatchEbp1C.add(0x4).readPointer()) + //console.log('-------',attatchEbp1C.add(0x8).readPointer()) +}) +/*------------------send pic -------------------------- +let buffwxid = null +let imagefilepath = null +let pathPtr = null +let picWxid = null +let picWxidPtr = null +let picAsm = null +let picbuff = null +const sendPicMsgNativeFunction = ((contactId, path) => { + + picAsm = Memory.alloc(Process.pageSize) + buffwxid = Memory.alloc(0x20) + picbuff = Memory.alloc(0x378) + + pathPtr = Memory.alloc(path.length * 2 + 1) + pathPtr.writeUtf16String(path) + + imagefilepath = Memory.alloc(0x24) + imagefilepath.writePointer(pathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) + + picWxidPtr = Memory.alloc(contactId.length * 2 + 1) + picWxidPtr.writeUtf16String(contactId) + + picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(picWxidPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + + Memory.patchCode(picAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: picAsm }) + cw.putPushfx(); + cw.putPushax(); + + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', buffwxid) + + cw.putMovRegReg('ecx', 'esp') + + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.send_picmsg_call_offset1 + )) + + cw.putMovRegAddress('ebx', imagefilepath) + cw.putPushReg('ebx') + + cw.putMovRegAddress('eax', picWxid) + cw.putPushReg('eax') + + cw.putMovRegAddress('eax', picbuff) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.send_picmsg_call_offset2 + )) + + cw.putMovRegReg('ecx', 'eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.send_picmsg_call_offset3 + )) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) + nativeativeFunction() + +})*/ + +// 025 +const callLoginQrcodeFunction = ((forceRefresh = false) => { + const json = getQrcodeLoginData() + if (!forceRefresh && json.uuid) { + return + } + + const callAsm = Memory.alloc(Process.pageSize) + const loginWnd = moduleBaseAddress.add(offset.get_login_wnd_offset).readPointer() + + Memory.patchCode(callAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: callAsm }) + cw.putPushfx(); + cw.putPushax(); + + cw.putMovRegAddress('ecx', loginWnd) + cw.putCallAddress(moduleBaseAddress.add(offset.get_qr_login_call_offset)) + + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + }) + + const nativeativeFunction = new NativeFunction(ptr(callAsm), 'void', []) + nativeativeFunction() +}) + + +// 026 +const agentReadyCallback = (() => { + const nativeCallback = new NativeCallback(() => { }, 'void', []) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', []) + + setTimeout(() => { + nativeativeFunction() + }, 500); + return nativeCallback +})() + +// 027 +const SendMiniProgramNativeFunction = ((bg_path_str, send_wxid_str, recv_wxid_str, xmlstr) => { + console.log("------------------------------------------------------"); + var asmCode = Memory.alloc(Process.pageSize); + + var ECX_buf = Memory.alloc(0x300); + var Buf_EAX = Memory.alloc(0x300); + var buf_1 = Memory.alloc(0x300); + var ptr_to_buf_1 = Memory.alloc(0x4).writePointer(buf_1); + var buf_2 = Memory.alloc(0x300); + + // var bg_path_str="C:/aaaa.jpg"; + var bg_path_Ptr = Memory.alloc(bg_path_str.length * 2 + 1) + bg_path_Ptr.writeUtf16String(bg_path_str); + var bg_path_Struct = Memory.alloc(0x14) // returns a NativePointer + bg_path_Struct.writePointer(bg_path_Ptr).add(0x04) + .writeU32(bg_path_str.length * 2).add(0x04) + .writeU32(bg_path_str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0); + + // var send_wxid_str="wxid_4zr616ir6fi122"; + var send_wxid_Ptr = Memory.alloc(send_wxid_str.length * 2 + 1) + send_wxid_Ptr.writeUtf16String(send_wxid_str); + var send_wxid_Struct = Memory.alloc(0x14) // returns a NativePointer + send_wxid_Struct.writePointer(send_wxid_Ptr).add(0x04) + .writeU32(send_wxid_str.length * 2).add(0x04) + .writeU32(send_wxid_str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0); + + // var recv_wxid_str="filehelper"; + var recv_wxid_Ptr = Memory.alloc(recv_wxid_str.length * 2 + 1) + recv_wxid_Ptr.writeUtf16String(recv_wxid_str); + var recv_wxid_Struct = Memory.alloc(0x14) // returns a NativePointer + recv_wxid_Struct.writePointer(recv_wxid_Ptr).add(0x04) + .writeU32(recv_wxid_str.length * 2).add(0x04) + .writeU32(recv_wxid_str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0); + + // vvar pXml=initidStruct('wxid_4zr616ir6fi1220腾讯出行服务|加油代驾公交view330https://mp.weixin.qq.com/mp/waerrpage?appid=wx65cc950f42e8fff1&amp;type=upgrade&amp;upgradetype=3#wechat_redirecthttp://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=200腾讯出行服务|加油代驾公交0gh_ad64296dc8bd@appwx65cc950f42e8fff11http://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=20002_wx65cc950f42e8fff1_875237370_1644979747_11Window wechat'); + + var pXml = initidStruct(xmlstr) + + console.log(send_wxid_Struct); + console.log(recv_wxid_Struct); + console.log(pXml); + console.log("okkk"); + + console.log("------------------------------------------------------"); + + Memory.patchCode(asmCode, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: asmCode }) + cw.putPushfx(); + cw.putPushax(); + cw.putMovRegReg('ecx', 'ecx'); + cw.putMovRegAddress('ecx', ECX_buf); + cw.putCallAddress(moduleBaseAddress.add(0x69BB0)); //init ecx + + cw.putPushU32(0x21); + + + cw.putPushNearPtr(ptr_to_buf_1); //ptr + cw.putPushU32(bg_path_Struct.toInt32()); + cw.putPushU32(pXml.toInt32()); + cw.putPushU32(recv_wxid_Struct.toInt32()); + + cw.putMovRegAddress('edx', send_wxid_Struct); + cw.putMovRegAddress('ecx', ECX_buf); + cw.putCallAddress(moduleBaseAddress.add(0x2E2420)); + cw.putAddRegImm('esp', 0x14) + + cw.putPushU32(Buf_EAX.toInt32()); + cw.putMovRegAddress('ecx', ECX_buf); + cw.putCallAddress(moduleBaseAddress.add(0x94C10)); + + cw.putPushU32(moduleBaseAddress.add(0x1DCB46C).toInt32()); + cw.putPushU32(moduleBaseAddress.add(0x1DCB46C).toInt32()); + cw.putMovRegAddress('ecx', ECX_buf); + cw.putCallAddress(moduleBaseAddress.add(0x2E2630)); + cw.putAddRegImm('esp', 0x8) + + cw.putPopax(); + cw.putPopfx(); + cw.putRet(); + cw.flush(); + }) + + const nativeativeFunction = new NativeFunction(ptr(asmCode), 'void', []) + nativeativeFunction() + + +}) \ No newline at end of file diff --git a/src/init-agent-script.js b/src/init-agent-script.js index 6ae2c78..f706c38 100644 --- a/src/init-agent-script.js +++ b/src/init-agent-script.js @@ -1,75 +1,197 @@ /** - * WeChat 3.2.1.121 * > Special thanks to: @cixingguangming55555 老张学技术 * * Credit: https://github.com/cixingguangming55555/wechat-bot - * Source: https://pan.baidu.com/s/1OmX2lxNOYHyGsl_3ByhsoA - * 《源码3.2.1.121》提取码: 1rfa - * WeChat: https://pan.baidu.com/share/init?surl=IHRM2OMvrLyuCz5MRbigGg - * 微信:3.2.1.121 提取码: cscn */ //https://blog.csdn.net/iloveitvm/article/details/109119687 frida学习 //const { isNullishCoalesce } = require("typescript") -//3.6.0.18 +//3.9.2.23 +// 偏移地址集合 done const offset = { - /**---nick call */ - chatroom_member_nick_call_offset_v6: 0x3E47B0,//3.6.0.18 - chatroom_member_nick_esi_offset_v6: 0x22553D4, - /**-- nick call */ - node_offset: 0x222f3bc,//0x1db9728 -- 3.3.0.155 - handle_offset: 0x4c, - send_txt_call_offset: 0x4BE7B0,//0x3e3b80 - hook_point: 0x4E94F2,//0x4E9464,//3.3.0.115 = 0x40d3b1 - chatroom_node_offset: 0xad8, - nickname_offset: 0x222EBB4, - wxid_offset: 0x222F020, - head_img_url_offset: 0x222EE94, - is_logged_in_offset: 0x1DDF9D4, - hook_on_login_offset: 0x51B790, - hook_on_logout_offset: 0x51C2C0, - hook_get_login_qr_offset: 0x4B6020, - hook_check_login_qr_offset: 0x478B90, - hook_save_login_qr_info_offset: 0x3DB2E0, - get_login_wnd_offset: 0x1DB96A4, - get_qr_login_data_offset: 0x282160, - get_qr_login_call_offset: 0x286930, - //-------3.6.0.18 send pic - send_picmsg_call_offset0: 0x9A1C0,//assign value to ecx - send_picmsg_call_offset1: 0x4BE160,//0x5ccb50, - send_picmsg_call_ecx: 0x222F0F0, - //-------3.6.0.18 send pic - /*send_picmsg_call_offset2: 0x6f5c0, - send_picmsg_call_offset3: 0x3e3490,*/ - send_attatch_ecx_offset: 0x1D8FA8C, - send_attatch_call_offset0: 0x9A1C0, - send_attatch_call_offset1: 0x701DC0,//0x701CD0,//701CD0 - send_attatch_call_offset2: 0x4BA5F0,//4B A5F0 - send_attatch_call_offset3: 0xC95A0, - send_attatch_call_offset4: 0x94200, - send_attatch_call_offset5: 0x3C4950, - send_attatch_call_offset6: 0x63B4F0, - send_attatch_call_para1: 0x1D8F248, - send_attatch_call_para2: 0x19a7350, - chatroom_member_nick_call_offset1: 0x558cb0, - chatroom_member_nick_call_offset2: 0x3b0fe0, - chatroom_member_nick_call_offset3: 0x55f6e0, - chatroom_member_nick_call_offset4: 0x34cb10, + hook_point: 0xd19a0b, //3.9.2.23 + myselfinfo: { + offset: 0x2FFD484, //老版本微信号偏移,后面的地址,都要在这个偏移上增加 + //wxid_len:0x10, + head_img_url: 0x2D8, + head_img_url_len: 0x2E8, + wx_nick_name: 0x10C, + wxcode_new: 0x64, //新版本微信号 + //wxcode_len:0x74 + wxid_len_offset: 0x4D4 + }, + contactInfo: { + nodeOffset: 0x2FFDD7C, + nodeRootOffset: 0x64 + }, + chatroomInfo: { + nodeOffset: 0x2FFDDC8, + nodeRootOffset: 0x8c8 + }, + sendTxtMsg: { + callOffset: 0xCE6C80 + }, + sendPicMsg: { + call1: 0x768140, + call2: 0xf59e40, + call3: 0xce6640 + } }; -//3.3.0.115 - +//3.9.2.23 +// 全局配置 done /*------------------global-------------------------------------------*/ -const availableVersion = 1661337618////3.3.0.115 ==1661141107 +const availableVersion = 1661534743 ////3.9.2.23 ==0x63090217 const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') const moduleLoad = Module.load('WeChatWin.dll') //1575CF98 + +// 全局变量 tbd const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() + +/*------------------global-------------------------------------------*/ + +/*---------------base -------------------------*/ + +// done +let retidPtr=null +let retidStruct=null +const initidStruct = ((str) => { + + retidPtr = Memory.alloc(str.length * 2 + 1) + retidPtr.writeUtf16String(str) + + retidStruct = Memory.alloc(0x14) // returns a NativePointer + + retidStruct + .writePointer(retidPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retidStruct +}) + +// done +let retPtr = null +let retStruct = null +const initStruct = ((str) => { + + retPtr = Memory.alloc(str.length * 2 + 1) + retPtr.writeUtf16String(str) + + retStruct = Memory.alloc(0x14) // returns a NativePointer + + retStruct + .writePointer(retPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retStruct +}) + +// done +let msgstrPtr=null +let msgStruct=null +const initmsgStruct = ((str) => { + msgstrPtr = Memory.alloc(str.length * 2 + 1) + msgstrPtr.writeUtf16String(str) + + msgStruct = Memory.alloc(0x14) // returns a NativePointer + + msgStruct + .writePointer(msgstrPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return msgStruct +}) + +// tbd +let retidNullStruct = null +let retidNullPtr = null +const initNullIdStruct = ((str) => { + + retidNullPtr = Memory.alloc(str.length * 2 + 1) + retidNullPtr.writeUtf16String(str) + + retidNullStruct = Memory.alloc(0x14) // returns a NativePointer + + retidNullStruct + .writePointer(retidNullPtr).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(str.length * 2).add(0x04) + .writeU32(0).add(0x04) + .writeU32(0) + + return retidNullStruct +}) + +// done +/** +* at msg structure +*/ +let atStruct = null +const initAtMsgStruct = ((wxidStruct) => { + + atStruct = Memory.alloc(0x10) + + atStruct.writePointer(wxidStruct).add(0x04) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) + .writeU32(0) + return atStruct +}) + +// done +// std::string +// const str = readStringPtr(ptr).readUtf8String() +const readStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(16).readU32() + const capacity = addr.add(20).readU32() + addr.ptr = addr + addr.size = size + addr.capacity = capacity + if (capacity > 15 && !addr.readPointer().isNull()) { + addr.ptr = addr.readPointer() + } + addr.ptr._readCString = addr.ptr.readCString + addr.ptr._readAnsiString = addr.ptr.readAnsiString + addr.ptr._readUtf8String = addr.ptr.readUtf8String + addr.readCString = () => { return addr.size ? addr.ptr._readCString(addr.size) : '' } + addr.readAnsiString = () => { return addr.size ? addr.ptr._readAnsiString(addr.size) : '' } + addr.readUtf8String = () => { return addr.size ? addr.ptr._readUtf8String(addr.size) : '' } + + // console.log('readStringPtr() address:',address,' -> str ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readStringPtr() str:' , addr.readUtf8String()) + // console.log('readStringPtr() address:', addr,'dump:', addr.readByteArray(24)) + + return addr +} + +// done +const readString = (address) => { + return readStringPtr(address).readUtf8String() +} + +// done +const readWideString = (address) => { + return readWStringPtr(address).readUtf16String() +} + +/*-----------------base-------------------------*/ + let currentVersion = 0 let nodeList = [] //for contact @@ -79,8 +201,6 @@ let chatroomNodeList = [] //for chatroom let chatroomMemberList = []//for chatroom let loggedIn = false -/*------------------global-------------------------------------------*/ - //开启日志 3.6.0.18 // //[0x221c330+wechatwin.dll]+0xf8 // 20220504 @@ -472,60 +592,6 @@ const isLoggedInFunction = (() => { return !!loggedIn }) -// 003get myself info - -const getBaseNodeAddress = (() => { - return moduleBaseAddress.add(offset.node_offset).readPointer() -}) - -// 004 -const getHeaderNodeAddress = (() => { - const baseAddress = getBaseNodeAddress() - //console.log('baseAddress',baseAddress) - if (baseAddress.isNull()) { - return baseAddress - } - - //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) - return baseAddress.add(offset.handle_offset).readPointer() -}) - -// 005 -const getChatroomNodeAddress = (() => { - const baseAddress = moduleBaseAddress.add(0x222f3fc).readPointer() - if (baseAddress.isNull()) { - return baseAddress - } - return baseAddress.add(offset.chatroom_node_offset).readPointer() -}) - -// 006 -const getMyselfInfoFunction = (() => { - - let ptr = 0 - let wx_code = '' - let wx_id = '' - let wx_name = '' - let head_img_url = '' - - wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) - wx_code = wx_id - - wx_name = readString(moduleBaseAddress.add(offset.nickname_offset)) - head_img_url = readString(moduleBaseAddress.add(offset.head_img_url_offset)) - - - const myself = { - id: wx_id, - code: wx_code, - name: wx_name, - head_img_url: head_img_url, - }; - - return JSON.stringify(myself) - -}) - // 007 缺失,请标注已废弃或者其他原因 const getMyselfIdFunction = (() => { @@ -535,76 +601,6 @@ const getMyselfIdFunction = (() => { }) -// 008chatroom member -const chatroomRecurse = ((node) => { - const chatroomNodeAddress = getChatroomNodeAddress() - if (chatroomNodeAddress.isNull()) { return } - - if (node.equals(chatroomNodeAddress)) { return } - - for (const item in chatroomNodeList) { - if (node.equals(chatroomNodeList[item])) { - return - } - } - - chatroomNodeList.push(node) - const roomid = readWideString(node.add(0x10)) - - const len = node.add(0x50).readU32() // - //const memberJson={} - if (len > 4) {// - const memberStr = readString(node.add(0x40)) - if (memberStr.length > 0) { - const memberList = memberStr.split(/[\\^][G]/) - const memberJson = { - roomid: roomid, - roomMember: memberList - } - - chatroomMemberList.push(memberJson) - } - - } - - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() - - chatroomRecurse(leftNode) - chatroomRecurse(centerNode) - chatroomRecurse(rightNode) - - const allChatroomMemberJson = chatroomMemberList - return allChatroomMemberJson -}) - -// std::string -// const str = readStringPtr(ptr).readUtf8String() -const readStringPtr = (address) => { - const addr = ptr(address) - const size = addr.add(16).readU32() - const capacity = addr.add(20).readU32() - addr.ptr = addr - addr.size = size - addr.capacity = capacity - if (capacity > 15 && !addr.readPointer().isNull()) { - addr.ptr = addr.readPointer() - } - addr.ptr._readCString = addr.ptr.readCString - addr.ptr._readAnsiString = addr.ptr.readAnsiString - addr.ptr._readUtf8String = addr.ptr.readUtf8String - addr.readCString = () => { return addr.size ? addr.ptr._readCString(addr.size) : '' } - addr.readAnsiString = () => { return addr.size ? addr.ptr._readAnsiString(addr.size) : '' } - addr.readUtf8String = () => { return addr.size ? addr.ptr._readUtf8String(addr.size) : '' } - - // console.log('readStringPtr() address:',address,' -> str ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) - // console.log('readStringPtr() str:' , addr.readUtf8String()) - // console.log('readStringPtr() address:', addr,'dump:', addr.readByteArray(24)) - - return addr -} - // std::wstring // const wstr = readWStringPtr(ptr).readUtf16String() const readWStringPtr = (address) => { @@ -624,15 +620,8 @@ const readWStringPtr = (address) => { return addr } -const readString = (address) => { - return readStringPtr(address).readUtf8String() -} - -const readWideString = (address) => { - return readWStringPtr(address).readUtf16String() -} - -const recurseNew = ((node) => { +//contact +const recurse = ((node) => { const headerNodeAddress = getHeaderNodeAddress() if (headerNodeAddress.isNull()) { return } @@ -646,17 +635,262 @@ const recurseNew = ((node) => { nodeList.push(node) - const id = readString(node.add(0x8)) //wxid, format relates to registration method - const wxid = readWideString(node.add(0x30)) - //console.log('-----------',wxid) - + const wxid = readWideString(node.add(0x38)) //custom id, if not set return null, and use wxid which should be custom id - //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) //custom Nickname - const name = readWideString(node.add(0x8c)) + const name = readWideString(node.add(0x94)) + + //alias aka 'remark' in wechat + const alias = readWideString(node.add(0x80)) + + //avatarUrl + const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + const gender = node.add(0x18C).readU32() + + const contactJson = { + id: wxid, + code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender, + } + + contactList.push(contactJson) + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() + + recurse(leftNode) + recurse(centerNode) + recurse(rightNode) + + const allContactJson = contactList + return allContactJson + +}) + +// 010 done +const getWechatVersionFunction = (() => { + if (currentVersion) { + return currentVersion + } + const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' + const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) + if (results.length == 0) { + return 0 + } + const addr = results[0].address + const ret = addr.add(0x07).readPointer() + const ver = ret.add(0x0).readU32() + currentVersion = ver + return ver +}) + +// 011 done +const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { + if (!ver) { + return '0.0.0.0' + } + const vers = [] + vers.push((ver >> 24) & 255 - 0x60) + vers.push((ver >> 16) & 255) + vers.push((ver >> 8) & 255) + vers.push(ver & 255) + return vers.join('.') +}) + +// 012 done +const checkSupportedFunction = (() => { + const ver = getWechatVersionFunction() + return ver == availableVersion +}) + +/** + * @Hook: recvMsg -> recvMsgNativeCallback + */ + +// 019 done +const recvMsgNativeCallback = (() => { + + + const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + + Interceptor.attach( + moduleBaseAddress.add(offset.hook_point), { + onEnter() { + const addr = this.context.ecx //0xc30-0x08 + const msgType = addr.add(0x38).readU32() + const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg + + if (msgType > 0) { + + const talkerIdPtr = addr.add(0x48).readPointer() + //console.log('txt msg',talkerIdPtr.readUtf16String()) + const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 + + const myTalkerIdPtr = Memory.alloc(talkerIdLen) + Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) + + + let contentPtr = null + let contentLen = 0 + let myContentPtr = null + if (msgType == 3) { // pic path + let thumbPtr = addr.add(0x198).readPointer(); + let hdPtr = addr.add(0x1ac).readPointer(); + let thumbPath = thumbPtr.readUtf16String(); + let hdPath = hdPtr.readUtf16String(); + let picData = [ + thumbPath, // PUPPET.types.Image.Unknown + thumbPath, // PUPPET.types.Image.Thumbnail + hdPath, // PUPPET.types.Image.HD + hdPath // PUPPET.types.Image.Artwork + ] + let content = JSON.stringify(picData); + myContentPtr = Memory.allocUtf16String(content); + } else { + contentPtr = addr.add(0x70).readPointer() + contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 + myContentPtr = Memory.alloc(contentLen) + Memory.copy(myContentPtr, contentPtr, contentLen) + } + + // console.log('----------------------------------------') + // console.log(msgType) + // console.log(contentPtr.readUtf16String()) + // console.log('----------------------------------------') + const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 + let myGroupMsgSenderIdPtr = null + if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 + + myGroupMsgSenderIdPtr = Memory.alloc(0x10) + myGroupMsgSenderIdPtr.writeUtf16String("null") + + } else { + + const groupMsgSenderIdPtr = addr.add(0x170).readPointer() + const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 + myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) + Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) + + } + + const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 + let myXmlContentPtr = null + if (xmlNullPtr == 0) { + + myXmlContentPtr = Memory.alloc(0x10) + myXmlContentPtr.writeUtf16String("null") + + } else { + const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + + const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 + myXmlContentPtr = Memory.alloc(xmlContentLen) + Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + } + + setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) + } + } + }) + return nativeCallback +})() + +// 003get myself info done +const getBaseNodeAddress = (() => { + return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() +}) + +// 004 done +const getHeaderNodeAddress = (() => { + const baseAddress = getBaseNodeAddress() + //console.log('baseAddress',baseAddress) + if (baseAddress.isNull()) { + return baseAddress + } + + //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) + return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() +}) + +// 006 done +const getMyselfInfoFunction = (() => { + + let ptr = 0 + let wx_code = '' + let wx_id = '' + let wx_name = '' + let head_img_url = '' + + const base = moduleBaseAddress.add(offset.myselfinfo.offset) + const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() + + if (wxid_len === 0x13) { // 新版本微信 + wx_id = base.readPointer().readAnsiString(wxid_len) + wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() + } else { + wx_id = readString(base) + wx_code = wx_id + } + + + wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) + const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() + const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() + + head_img_url = img_addr.readAnsiString(img_len) + + const myself = { + id: wx_id, + code: wx_code, + name: wx_name, + head_img_url: head_img_url, + }; + + return JSON.stringify(myself) + +}) + +// done +const recurseNew = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { + return + } + + if (node.equals(headerNodeAddress)) { + return + } + + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } + + + nodeList.push(node) + const id = readString(node.add(0x8)) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x30)) + //console.log('-----------',wxid) + + + //custom id, if not set return null, and use wxid which should be custom id + //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + + //custom Nickname + const name = readWideString(node.add(0x8c)) //alias aka 'remark' in wechat //const alias = readWideString(node.add(0x80)) @@ -693,112 +927,361 @@ const recurseNew = ((node) => { }) - -//contact -const recurse = ((node) => { +// done ? +const getContactNativeFunction = (() => { const headerNodeAddress = getHeaderNodeAddress() - if (headerNodeAddress.isNull()) { return } + //console.log('headerNodeAddress',headerNodeAddress) - if (node.equals(headerNodeAddress)) { return } + if (headerNodeAddress.isNull()) { + return '[]' + } - for (const item in nodeList) { - if (node.equals(nodeList[item])) { + const node = headerNodeAddress.add(0x0).readPointer() + const ret = recurseNew(node) + + //console.log(ret) + + console.log('getContactNativeFunction:', ret.length) + /*for (let item of ret){ + console.log(JSON.stringify(item)) + }*/ + //console.log(ret.contact) + const cloneRet = JSON.stringify(ret) + nodeList.length = 0 + contactList.length = 0 + + return cloneRet +}) + +// 005 done +const getChatroomNodeAddress = (() => { + const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() + if (baseAddress.isNull()) { + return baseAddress + } + return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() +}) + +// 008chatroom member done +const chatroomRecurse = ((node) => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return + } + + if (node.equals(chatroomNodeAddress)) { + return + } + + for (const item in chatroomNodeList) { + if (node.equals(chatroomNodeList[item])) { return } } + chatroomNodeList.push(node) + const roomid = readWideString(node.add(0x10)) - nodeList.push(node) - //wxid, format relates to registration method - const wxid = readWideString(node.add(0x38)) + const len = node.add(0x54).readU32() // + //const memberJson={} + if (len > 4) { // + const memberStr = readString(node.add(0x44)) + if (memberStr.length > 0) { + const memberList = memberStr.split(/[\\^][G]/) + const memberJson = { + roomid: roomid, + roomMember: memberList + } + + chatroomMemberList.push(memberJson) + } + + } + + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() + + chatroomRecurse(leftNode) + chatroomRecurse(centerNode) + chatroomRecurse(rightNode) + + const allChatroomMemberJson = chatroomMemberList + return allChatroomMemberJson +}) + +// 009 done +const getChatroomMemberInfoFunction = (() => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return '[]' + } + + const node = chatroomNodeAddress.add(0x0).readPointer() + const ret = chatroomRecurse(node) + + const cloneRet = JSON.stringify(ret) + chatroomNodeList.length = 0 //empty + chatroomMemberList.length = 0 //empty + return cloneRet +}) + +// 024 done +/** + * sendMsgNativeFunction + * send text message + * @param {string} talkerId = wxid or roomid + * @param {string} content + */ + const sendMsgNativeFunction = ((talkerId, content) => { + + const txtAsm = Memory.alloc(Process.pageSize) + //const buffwxid = Memory.alloc(0x20) + + + let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) + wxidPtr.writeUtf16String(talkerId) + + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(wxidPtr)).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + + let contentPtr = Memory.alloc(content.length * 2 + 2) + contentPtr.writeUtf16String(content) + + const sizeOfStringStruct = Process.pointerSize * 5 + let contentStruct = Memory.alloc(sizeOfStringStruct) + + contentStruct + .writePointer(contentPtr).add(0x4) + .writeU32(content.length).add(0x4) + .writeU32(content.length * 2) + + + const ecxBuffer = Memory.alloc(0x2d8) + + Memory.patchCode(txtAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: txtAsm + }) + cw.putPushfx() + cw.putPushax() + + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + cw.putPushU32(0x0) + + //cw.putMovRegReg + + cw.putMovRegAddress('eax', contentStruct) + cw.putPushReg('eax') + + cw.putMovRegAddress('edx', picWxid) //room_id + + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) + + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + console.log('----------txtAsm', txtAsm) + const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + nativeativeFunction() + +}) + +// 023 done +/** +* send at msg +*/ +let asmAtMsg = null +let roomid_, msg_, wxid_, atid_ +let ecxBuffer +const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { + + asmAtMsg = Memory.alloc(Process.pageSize) + ecxBuffer = Memory.alloc(0x3b0) + + const atContent = '@'+nickname+' '+text + + roomid_ = initStruct(roomId) + wxid_ = initidStruct(contactId) + msg_ = initmsgStruct(atContent) + atid_ = initAtMsgStruct(wxid_) + + Memory.patchCode(asmAtMsg, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: asmAtMsg + }) + cw.putPushfx() + cw.putPushax() + + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + //cw.putPushU32(0x0) + cw.putMovRegAddress('eax', atid_) + cw.putPushReg('eax') + + //cw.putMovRegReg + + cw.putMovRegAddress('eax', msg_) + cw.putPushReg('eax') + + cw.putMovRegAddress('edx', roomid_) //room_id + + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) + + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() + + }) + + //console.log('----------txtAsm', asmAtMsg) + const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + nativeativeFunction() + +}) + +// 022 done +/** + * + * @param {*} contactId + * @param {*} path + */ + const sendPicMsgNativeFunction = ((contactId, path) => { + + const picAsm = Memory.alloc(Process.pageSize) + const buffwxid = Memory.alloc(0x20) + const picbuff = Memory.alloc(0x2D8) + + let pathPtr = Memory.alloc(path.length * 2 + 1) + pathPtr.writeUtf16String(path) + + let imagefilepath = Memory.alloc(0x24) + imagefilepath.writePointer(pathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) + + let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) + picWxidPtr.writeUtf16String(contactId) + + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(picWxidPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x04) - //custom id, if not set return null, and use wxid which should be custom id - const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) - //custom Nickname - const name = readWideString(node.add(0x94)) + //const test_offset1 = 0x701DC0; + Memory.patchCode(picAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: picAsm + }) + cw.putPushfx(); + cw.putPushax(); + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call1 + )) + cw.putMovRegReg('edx', 'eax') //缓存 - //alias aka 'remark' in wechat - const alias = readWideString(node.add(0x80)) + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', buffwxid) + cw.putMovRegReg('ecx', 'esp') + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call2 + )) - //avatarUrl - const avatar = readWideString(node.add(0x138)) - //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) - //contact gender - const gender = node.add(0x18C).readU32() + cw.putMovRegReg('ecx', 'edx') + cw.putMovRegAddress('eax', picWxid) //=lea + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('edi') + cw.putPushReg('eax') + cw.putMovRegAddress('eax', picbuff) + cw.putPushReg('eax') - const contactJson = { - id: wxid, - code: wx_code, - name: name, - alias: alias, - avatarUrl: avatar, - gender: gender, - } + cw.putMovRegAddress('edi', picWxid) //edi + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call3 + )) - contactList.push(contactJson) - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() - recurse(leftNode) - recurse(centerNode) - recurse(rightNode) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - const allContactJson = contactList - return allContactJson + }) + + //console.log('----------picAsm',picAsm) + const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) + nativeativeFunction() }) -// 009 -const getChatroomMemberInfoFunction = (() => { - const chatroomNodeAddress = getChatroomNodeAddress() - if (chatroomNodeAddress.isNull()) { return '[]' } +// 020 done +let memberNickBuffAsm = null +let nickRoomId = null +let nickMemberId = null +let nickBuff = null +const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { - const node = chatroomNodeAddress.add(0x0).readPointer() - const ret = chatroomRecurse(node) + nickBuff = Memory.alloc(0x7e4) + //const nickRetAddr = Memory.alloc(0x04) + memberNickBuffAsm = Memory.alloc(Process.pageSize) + //console.log('asm address----------',memberNickBuffAsm) + nickRoomId = initidStruct(roomId) + //console.log('nick room id',nickRoomId) + nickMemberId = initStruct(memberId) - const cloneRet = JSON.stringify(ret) - chatroomNodeList.length = 0//empty - chatroomMemberList.length = 0 //empty - return cloneRet -}) + //console.log('nick nickMemberId id',nickMemberId) + //const nickStructPtr = initmsgStruct('') -// 010 -const getWechatVersionFunction = (() => { - if (currentVersion) { - return currentVersion - } - const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' - const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) - if (results.length == 0) { - return 0 - } - const addr = results[0].address - const ret = addr.add(0x07).readPointer() - const ver = ret.add(0x0).readU32() - currentVersion = ver - return ver -}) + Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: memberNickBuffAsm + }) + cw.putPushfx() + cw.putPushax() + cw.putMovRegAddress('edi', nickRoomId) + cw.putMovRegAddress('eax', nickBuff) + cw.putMovRegReg('edx', 'edi') + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', nickMemberId) + cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) + cw.putAddRegImm('esp', 0x04) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() -// 011 -const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { - if (!ver) { - return '0.0.0.0' - } - const vers = [] - vers.push((ver >> 24) & 255 - 0x60) - vers.push((ver >> 16) & 255) - vers.push((ver >> 8) & 255) - vers.push(ver & 255) - return vers.join('.') -}) + }) -// 012 -const checkSupportedFunction = (() => { - const ver = getWechatVersionFunction() - return ver == availableVersion + const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) + nativeativeFunction() + + const nickname = readWideString(nickBuff) + console.log('----nickname', nickname) + return readWideString(nickBuff) }) // 013 @@ -808,30 +1291,6 @@ if (!isSupported) { throw new Error(`Wechat version not supported. \nWechat version: ${getWechatVersionStringFunction()}, supported version: ${getWechatVersionStringFunction(availableVersion)}`) } -// 014 -const getContactNativeFunction = (() => { - const headerNodeAddress = getHeaderNodeAddress() - //console.log('headerNodeAddress',headerNodeAddress) - - if (headerNodeAddress.isNull()) { return '[]' } - - const node = headerNodeAddress.add(0x0).readPointer() - const ret = recurseNew(node) - - //console.log(ret) - - // console.log('getContactNativeFunction:',ret.length) - /*for (let item of ret){ - console.log(JSON.stringify(item)) - }*/ - //console.log(ret.contact) - const cloneRet = JSON.stringify(ret) - nodeList.length = 0 - contactList.length = 0 - - return cloneRet -}) - // 015 const hookLogoutEventCallback = (() => { const nativeCallback = new NativeCallback(() => { }, 'void', ['int32']) @@ -973,214 +1432,23 @@ const getQrcodeLoginData = () => { /** * 20220504 writelog * 7A566D72 | FFB5 ECFEFFFF | push dword ptr ss:[ebp-114] | 【3.6.0.18】写日志,这个里面就是日志内容 - 7A566D78 | FFB5 E8FEFFFF | push dword ptr ss:[ebp-118] | - */ -/*const writeLogNativeCallback = (() => { - const nativeCallback = new NativeCallback(() => { }, 'void', []) - const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) - - Interceptor.attach( - moduleBaseAddress.add(0x1576D7E), - { - onEnter() { - const addr = this.context.ebp.sub(0x114)//0xc30-0x08 - console.log('-------',addr) - - } - }) - return nativeCallback -})()*/ - -/** - * @Hook: recvMsg -> recvMsgNativeCallback - */ - -// 019 -const recvMsgNativeCallback = (() => { - - - const nativeCallback = new NativeCallback(() => { }, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) - - Interceptor.attach( - moduleBaseAddress.add(offset.hook_point), - { - onEnter() { - let addr - let msgType = 0 - let isMyMsg = 0 - let curTime = new Date() - try { - addr = this.context.eax//0xc30-0x08 - msgType = addr.add(0x38).readU32() - isMyMsg = addr.add(0x3C).readU32()//add isMyMsg - if (msgType > 0) { - const talkerIdPtr = addr.add(0x48).readPointer() - //console.log('txt msg',talkerIdPtr.readUtf16String()) - const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 - - const myTalkerIdPtr = Memory.alloc(talkerIdLen) - Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) - - - let contentPtr = null - let contentLen = 0 - let myContentPtr = null - if (msgType == 3) {// pic path - let thumbPtr = addr.add(0x198).readPointer(); - let hdPtr = addr.add(0x1ac).readPointer(); - let thumbPath = thumbPtr.readUtf16String(); - let hdPath = hdPtr.readUtf16String(); - let picData = [ - thumbPath,// PUPPET.types.Image.Unknown - thumbPath,// PUPPET.types.Image.Thumbnail - hdPath,// PUPPET.types.Image.HD - hdPath// PUPPET.types.Image.Artwork - ] - let content = JSON.stringify(picData); - myContentPtr = Memory.allocUtf16String(content); - } else { - contentPtr = addr.add(0x70).readPointer() - contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 - myContentPtr = Memory.alloc(contentLen) - Memory.copy(myContentPtr, contentPtr, contentLen) - } - - // console.log('----------------------------------------') - // console.log(msgType) - // console.log(contentPtr.readUtf16String()) - // console.log('----------------------------------------') - const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 - let myGroupMsgSenderIdPtr = null - if (groupMsgAddr == 0) {//weChatPublic is zero,type is 49 - - myGroupMsgSenderIdPtr = Memory.alloc(0x10) - myGroupMsgSenderIdPtr.writeUtf16String("null") - - } else { - - const groupMsgSenderIdPtr = addr.add(0x170).readPointer() - const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 - myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) - Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) - - } - - const xmlNullPtr = addr.add(0x1ec).readU32() - let myXmlContentPtr = null - if (xmlNullPtr == 0) { - - myXmlContentPtr = Memory.alloc(0x10) - myXmlContentPtr.writeUtf16String("null") - - } else { - const xmlContentPtr = addr.add(0x1ec).readPointer() - - const xmlContentLen = addr.add(0x1ec + 0x04).readU32() * 2 + 2 - myXmlContentPtr = Memory.alloc(xmlContentLen) - Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) - } - - setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) - } - } catch (err) { - console.error(curTime,'recvMsgNativeCallback at onEnter err:', err) - } - } - }) - return nativeCallback -})() - - -let msgStruct = null -let msgstrPtr = null -const initmsgStruct = ((str) => { - msgstrPtr = Memory.alloc(str.length * 2 + 1) - msgstrPtr.writeUtf16String(str) - - msgStruct = Memory.alloc(0x14) // returns a NativePointer - - msgStruct - .writePointer(msgstrPtr).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0) - - return msgStruct -}) - -let retidNullStruct = null -let retidNullPtr = null -const initNullIdStruct = ((str) => { - - retidNullPtr = Memory.alloc(str.length * 2 + 1) - retidNullPtr.writeUtf16String(str) - - retidNullStruct = Memory.alloc(0x14) // returns a NativePointer - - retidNullStruct - .writePointer(retidNullPtr).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0) - - return retidNullStruct -}) - -let retidStruct = null -let retidPtr = null -const initidStruct = ((str) => { - - retidPtr = Memory.alloc(str.length * 2 + 1) - retidPtr.writeUtf16String(str) - - retidStruct = Memory.alloc(0x14) // returns a NativePointer - - retidStruct - .writePointer(retidPtr).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0) - - return retidStruct -}) - -let retPtr = null -let retStruct = null -const initStruct = ((str) => { - - retPtr = Memory.alloc(str.length * 2 + 1) - retPtr.writeUtf16String(str) - - retStruct = Memory.alloc(0x14) // returns a NativePointer - - retStruct - .writePointer(retPtr).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0) - - return retStruct -}) - -/** -* at msg structure -*/ -let atStruct = null -const initAtMsgStruct = ((wxidStruct) => { - - atStruct = Memory.alloc(0x10) + 7A566D78 | FFB5 E8FEFFFF | push dword ptr ss:[ebp-118] | + */ +/*const writeLogNativeCallback = (() => { + const nativeCallback = new NativeCallback(() => { }, 'void', []) + const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) - atStruct.writePointer(wxidStruct).add(0x04) - .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) - .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) - .writeU32(0) - return atStruct -}) + Interceptor.attach( + moduleBaseAddress.add(0x1576D7E), + { + onEnter() { + const addr = this.context.ebp.sub(0x114)//0xc30-0x08 + console.log('-------',addr) + + } + }) + return nativeCallback +})()*/ let nickRoomIdV6 = null let nullEdiWxidStructV6 = null @@ -1241,86 +1509,6 @@ const getChatroomMemberNickInfoV1Function = ((memberId, roomId) => { return readWideString(nullEdiWxidStructV6) }) -//get nick from chatroom -let nickRoomId = null -let nickMemberId = null -let nickStructPtr = null -let nickBuff = null -let memberNickBuffAsm = null -let nickRetAddr = null - -// 020 -const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { - - nickBuff = Memory.alloc(0x7e4) - nickRetAddr = Memory.alloc(0x04) - memberNickBuffAsm = Memory.alloc(Process.pageSize) - nickRoomId = initidStruct(roomId) - nickMemberId = initStruct(memberId) - nickStructPtr = initmsgStruct('') - - Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: memberNickBuffAsm }) - cw.putPushfx(); - cw.putPushax(); - - /*cw.putMovRegAddress('ebx', nickStructPtr) - cw.putMovRegAddress('esi', nickMemberId) - cw.putMovRegAddress('edi', nickRoomId) - - cw.putMovRegAddress('ecx', nickBuff) - cw.putCallAddress(moduleBaseAddress.add( - offset.chatroom_member_nick_call_offset1 - )) - - cw.putMovRegAddress('eax', nickBuff) - cw.putPushReg('eax') - cw.putPushReg('esi') - cw.putCallAddress(moduleBaseAddress.add( - offset.chatroom_member_nick_call_offset2 - )) - - cw.putMovRegReg('ecx', 'eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.chatroom_member_nick_call_offset3 - )) - - cw.putPushU32(1) - cw.putPushReg('ebx') - cw.putMovRegReg('edx', 'edi') - cw.putMovRegAddress('ecx', nickBuff) - cw.putCallAddress(moduleBaseAddress.add( - offset.chatroom_member_nick_call_offset4 - )) - cw.putAddRegImm('esp', 0x08) - cw.putMovNearPtrReg(nickRetAddr, 'ebx')*/ - cw.putMovRegAddress('edi', nickRoomId) - cw.putMovRegAddress('eax', nickBuff) - cw.putMovRegReg('edx', 'edi') - cw.putPushReg('eax') - cw.putMovRegAddress('ecx', nickMemberId) - cw.putCallAddress(moduleBaseAddress.add(0x404500)) - cw.putAddRegImm('esp', 0x04) - //cw.putMovNearPtrReg(nickRetAddr, 'ebx') - //lea eax, buf - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) - nativeativeFunction() - - const nickname = readWideString(nickBuff); - // console.log('--------nickname',nickname) - return readWideString(nickBuff); - //return readWideString(nickRetAddr.readPointer()) - -}) - /** * send attatch */ @@ -1567,131 +1755,6 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size //console.log('-------',attatchEbp1C.add(0x4).readPointer()) //console.log('-------',attatchEbp1C.add(0x8).readPointer()) }) -/*-----------------send pic 3.6.0.18----------------*/ -/*------------------send pic --------------------------*/ -let buffwxid = null -let imagefilepath = null -let pathPtr = null -let picWxid = null -let picWxidPtr = null -let picAsm = null -let picbuff = null - -// 022 -const sendPicMsgNativeFunction = ((contactId, path) => { - - picAsm = Memory.alloc(Process.pageSize) - buffwxid = Memory.alloc(0x20) - picbuff = Memory.alloc(0x3B0) - - pathPtr = Memory.alloc(path.length * 2 + 1) - pathPtr.writeUtf16String(path) - - imagefilepath = Memory.alloc(0x24) - imagefilepath.writePointer(pathPtr).add(0x04) - .writeU32(path.length * 2).add(0x04) - .writeU32(path.length * 2).add(0x04) - - picWxidPtr = Memory.alloc(contactId.length * 2 + 1) - picWxidPtr.writeUtf16String(contactId) - - picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(picWxidPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - - /*const ecxValue = (moduleBaseAddress.add( - offset.send_picmsg_call_offset0 - )).toInt32() -*/ - const test_offset1 = 0x701DC0; - Memory.patchCode(picAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: picAsm }) - cw.putPushfx(); - cw.putPushax(); - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset0 - )) - cw.putMovRegReg('edx', 'eax')//缓存 - - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', buffwxid) - cw.putMovRegReg('ecx', 'esp') - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - test_offset1 - )) - - cw.putMovRegReg('ecx', 'edx') - cw.putMovRegAddress('eax', picWxid) //=lea - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('edi') - cw.putPushReg('eax') - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') - - cw.putMovRegAddress('edi', picWxid)//edi - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset1 - )) - - /*cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset0 - )) - cw.putMovRegReg('ecx', 'eax') - cw.putMovRegAddress('eax', picWxid) - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('edi') - cw.putPushReg('eax') - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') - - cw.putMovRegAddress('edi', picWxid)//edi - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset1 - ))-------ok but exception*/ - - /*3.3.0.115 - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', buffwxid) - - cw.putMovRegReg('ecx', 'esp') - - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset1 - )) - - cw.putMovRegAddress('ebx', imagefilepath) - cw.putPushReg('ebx') - - cw.putMovRegAddress('eax', picWxid) - cw.putPushReg('eax') - - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset2 - )) - - cw.putMovRegReg('ecx', 'eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset3 - ))*/ - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - console.log('----------picAsm', picAsm) - const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) - nativeativeFunction() - -}) /*------------------send pic -------------------------- let buffwxid = null let imagefilepath = null @@ -1764,156 +1827,6 @@ const sendPicMsgNativeFunction = ((contactId, path) => { nativeativeFunction() })*/ -/** -* send at msg -*/ -let asmAtMsg = null -let roomid_, msg_, wxid_, atid_ -let ecxBuffer - -// 023 -const sendAtMsgNativeFunction = ((roomId, text, contactId) => { - asmAtMsg = Memory.alloc(Process.pageSize) - ecxBuffer = Memory.alloc(0x3b0) - - - roomid_ = initStruct(roomId) - wxid_ = initidStruct(contactId) - msg_ = initmsgStruct(text) - atid_ = initAtMsgStruct(wxid_) - - Memory.patchCode(asmAtMsg, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: asmAtMsg }) - //cw.putMovRegAddress('eax',roomid) - - cw.putPushfx(); - cw.putPushax(); - - cw.putPushU32(1) // push - - cw.putMovRegAddress('edi', atid_) - cw.putMovRegAddress('ebx', msg_)//msg_ - - cw.putPushReg('edi') - cw.putPushReg('ebx') - - //cw.putMovRegRegOffsetPtr('edx', 'ebp', 0x10)//at wxid - cw.putMovRegAddress('edx', roomid_)//room_id - - cw.putMovRegAddress('ecx', ecxBuffer) - - cw.putCallAddress(moduleBaseAddress.add( - offset.send_txt_call_offset - )) - cw.putAddRegImm('esp', 0xc) - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - }) - - const atMsgNativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) - atMsgNativeFunction() -}) - -/** -* @Call: sendMsg -> agentSendMsg -*/ - -// 024 -const sendMsgNativeFunction = (() => { - //const asmBuffer = Memory.alloc(/*0x5a8*/0x5f0) // magic number from wechat-bot (laozhang) - const asmBuffer = Memory.alloc(0x5f0) - const asmSendMsg = Memory.alloc(Process.pageSize) - Memory.patchCode(asmSendMsg, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: asmSendMsg }) - - cw.putPushReg('ebp') - cw.putMovRegReg('ebp', 'esp') - cw.putPushax() - cw.putPushfx() - - cw.putPushU32(1) // push - cw.putPushU32(0) // push - - cw.putMovRegRegOffsetPtr('ebx', 'ebp', 0xc) // arg 1 - cw.putPushReg('ebx') // push - - cw.putMovRegRegOffsetPtr('edx', 'ebp', 0x8) // arg 0 - cw.putMovRegAddress('ecx', asmBuffer) - - //0x3b56a0 3.2.1.121 - cw.putCallAddress(moduleBaseAddress.add( - offset.send_txt_call_offset - )) - cw.putAddRegImm('esp', 0xc) - - cw.putPopfx() - cw.putPopax() - cw.putMovRegRegPtr('esp', 'ebp') // Huan(202107): why use RegRegPtr? (RegRet will fail) - cw.putPopReg('ebp') - cw.putRet() - - cw.flush() - }) - - /*let ins = Instruction.parse(asmSendMsg) - for (let i=0; i<20; i++) { - console.log(ins.address, '\t', ins.mnemonic, '\t', ins.opStr) - ins = Instruction.parse(ins.next) - }*/ - - const asmNativeFunction = new NativeFunction(asmSendMsg, 'void', ['pointer', 'pointer']) - - const sendMsg = ( - talkerId, - content, - ) => { - const talkerIdPtr = Memory.alloc(talkerId.length * 2 + 1) - const contentPtr = Memory.alloc(content.length * 2 + 1) - - talkerIdPtr.writeUtf16String(talkerId) - contentPtr.writeUtf16String(content) - - const sizeOfStringStruct = Process.pointerSize * 5 // + 0xd - - // allocate space for the struct - const talkerIdStruct = Memory.alloc(sizeOfStringStruct) // returns a NativePointer - const contentStruct = Memory.alloc(sizeOfStringStruct) // returns a NativePointer - - talkerIdStruct - .writePointer(talkerIdPtr).add(0x4) - .writeU32(talkerId.length).add(0x4) - .writeU32(talkerId.length * 2) - - contentStruct - .writePointer(contentPtr).add(0x4) - .writeU32(content.length).add(0x4) - .writeU32(content.length * 2) - - asmNativeFunction(talkerIdStruct, contentStruct) - } - - /** - * Best Practices - * https://frida.re/docs/best-practices/ - * - * There is however a pitfall: the value returned by Memory.allocUtf8String() must be kept alive - * – it gets freed as soon as the JavaScript value gets garbage-collected. - * - * This means it needs to be kept alive for at least the duration of the function-call, - * and in some cases even longer; the exact semantics depend on how the API was designed. - */ - const refHolder = { - asmBuffer, - asmSendMsg, - asmNativeFunction, - sendMsg, - } - - return (...args) => refHolder.sendMsg(...args) -})() // 025 const callLoginQrcodeFunction = ((forceRefresh = false) => { diff --git a/src/wechat-sidecar.ts b/src/wechat-sidecar.ts index 8e73d49..4b8ec6c 100644 --- a/src/wechat-sidecar.ts +++ b/src/wechat-sidecar.ts @@ -37,6 +37,7 @@ import { codeRoot } from './cjs.js' const supportedVersions = { v330115:'3.3.0.115', v360000:'3.6.0.18', + v39223:'3.9.2.23', } // let initAgentScript = fs.readFileSync(path.join( @@ -58,7 +59,7 @@ try { // await detach(wechatVersion) - const currentVersion = '3.6.0.18' + const currentVersion = '3.9.2.23' switch (currentVersion) { case supportedVersions.v330115: @@ -82,6 +83,19 @@ try { 'init-agent-script.js', ), 'utf-8') break + case supportedVersions.v39223: + // initAgentScript = fs.readFileSync(path.join( + // codeRoot, + // 'src', + // 'agents', + // 'agent-script-3.6.0.18.js', + // ), 'utf-8') + initAgentScript = fs.readFileSync(path.join( + codeRoot, + 'src', + 'init-agent-script.js', + ), 'utf-8') + break default: throw new Error(`Wechat version not supported. \nWechat version: ${currentVersion}, supported version: ${JSON.stringify(supportedVersions)}`) } From ad0e0e319e31e0f91ed48ac9dd7b95958ac183ef Mon Sep 17 00:00:00 2001 From: choogoo <104893934+choogoo@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:22:08 +0800 Subject: [PATCH 02/38] 3.9.2.23 init (#182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 3.9.2.23 initt * Update init-agent-script.js * 3.9.2.23 adapter * 适配3.9.2.23 * Delete agent-script-3.9.2.23-new.js --- ...9.2.23-new.js => agent-script-3.9.2.23.js} | 1845 ++++++++--------- src/init-agent-script.js | 1845 ++++++++--------- 2 files changed, 1780 insertions(+), 1910 deletions(-) rename src/agents/{agent-script-3.9.2.23-new.js => agent-script-3.9.2.23.js} (95%) diff --git a/src/agents/agent-script-3.9.2.23-new.js b/src/agents/agent-script-3.9.2.23.js similarity index 95% rename from src/agents/agent-script-3.9.2.23-new.js rename to src/agents/agent-script-3.9.2.23.js index f706c38..9c97f9a 100644 --- a/src/agents/agent-script-3.9.2.23-new.js +++ b/src/agents/agent-script-3.9.2.23.js @@ -8,11 +8,14 @@ //const { isNullishCoalesce } = require("typescript") -//3.9.2.23 +// wechat:3.9.2.23 -// 偏移地址集合 done +// 028 done const offset = { + + hook_point: 0xd19a0b, //3.9.2.23 + myselfinfo: { offset: 0x2FFD484, //老版本微信号偏移,后面的地址,都要在这个偏移上增加 //wxid_len:0x10, @@ -23,6 +26,7 @@ const offset = { //wxcode_len:0x74 wxid_len_offset: 0x4D4 }, + contactInfo: { nodeOffset: 0x2FFDD7C, nodeRootOffset: 0x64 @@ -40,25 +44,20 @@ const offset = { call3: 0xce6640 } }; -//3.9.2.23 -// 全局配置 done +// 029 done /*------------------global-------------------------------------------*/ const availableVersion = 1661534743 ////3.9.2.23 ==0x63090217 const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') const moduleLoad = Module.load('WeChatWin.dll') -//1575CF98 - -// 全局变量 tbd +// tbd const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() - /*------------------global-------------------------------------------*/ /*---------------base -------------------------*/ - -// done +// 030 done let retidPtr=null let retidStruct=null const initidStruct = ((str) => { @@ -78,7 +77,7 @@ const initidStruct = ((str) => { return retidStruct }) -// done +// 031 done let retPtr = null let retStruct = null const initStruct = ((str) => { @@ -98,7 +97,7 @@ const initStruct = ((str) => { return retStruct }) -// done +// 032 done let msgstrPtr=null let msgStruct=null const initmsgStruct = ((str) => { @@ -117,7 +116,23 @@ const initmsgStruct = ((str) => { return msgStruct }) -// tbd +// 034 done +/** +* at msg structure +*/ +let atStruct = null +const initAtMsgStruct = ((wxidStruct) => { + + atStruct = Memory.alloc(0x10) + + atStruct.writePointer(wxidStruct).add(0x04) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) + .writeU32(0) + return atStruct +}) + +// 033 tbd let retidNullStruct = null let retidNullPtr = null const initNullIdStruct = ((str) => { @@ -137,25 +152,7 @@ const initNullIdStruct = ((str) => { return retidNullStruct }) -// done -/** -* at msg structure -*/ -let atStruct = null -const initAtMsgStruct = ((wxidStruct) => { - - atStruct = Memory.alloc(0x10) - - atStruct.writePointer(wxidStruct).add(0x04) - .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) - .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) - .writeU32(0) - return atStruct -}) - -// done -// std::string -// const str = readStringPtr(ptr).readUtf8String() +// 035 done const readStringPtr = (address) => { const addr = ptr(address) const size = addr.add(16).readU32() @@ -169,9 +166,15 @@ const readStringPtr = (address) => { addr.ptr._readCString = addr.ptr.readCString addr.ptr._readAnsiString = addr.ptr.readAnsiString addr.ptr._readUtf8String = addr.ptr.readUtf8String - addr.readCString = () => { return addr.size ? addr.ptr._readCString(addr.size) : '' } - addr.readAnsiString = () => { return addr.size ? addr.ptr._readAnsiString(addr.size) : '' } - addr.readUtf8String = () => { return addr.size ? addr.ptr._readUtf8String(addr.size) : '' } + addr.readCString = () => { + return addr.size ? addr.ptr._readCString(addr.size) : '' + } + addr.readAnsiString = () => { + return addr.size ? addr.ptr._readAnsiString(addr.size) : '' + } + addr.readUtf8String = () => { + return addr.size ? addr.ptr._readUtf8String(addr.size) : '' + } // console.log('readStringPtr() address:',address,' -> str ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) // console.log('readStringPtr() str:' , addr.readUtf8String()) @@ -180,913 +183,853 @@ const readStringPtr = (address) => { return addr } -// done +// 036 done const readString = (address) => { return readStringPtr(address).readUtf8String() } -// done +// 037 done const readWideString = (address) => { return readWStringPtr(address).readUtf16String() } +/*-----------------base-------------------------*/ + +// 041 +// std::wstring +// const wstr = readWStringPtr(ptr).readUtf16String() +const readWStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(4).readU32() + const capacity = addr.add(8).readU32() + addr.ptr = addr.readPointer() + addr.size = size + addr.capacity = capacity + addr.ptr._readUtf16String = addr.ptr.readUtf16String + addr.readUtf16String = () => { + return addr.size ? addr.ptr._readUtf16String(addr.size * 2) : '' + } + + // console.log('readWStringPtr() address:',address,' -> ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readWStringPtr() str:' , `"${addr.readUtf16String()}"`,'\n',addr.ptr.readByteArray(addr.size*2+2),'\n') + // console.log('readWStringPtr() address:', addr,'dump:', addr.readByteArray(16),'\n') + + return addr +} /*-----------------base-------------------------*/ +// 010 done let currentVersion = 0 -let nodeList = [] //for contact +let nodeList = [] //for contact let contactList = [] //for contact let chatroomNodeList = [] //for chatroom -let chatroomMemberList = []//for chatroom +let chatroomMemberList = [] //for chatroom let loggedIn = false -//开启日志 3.6.0.18 -// //[0x221c330+wechatwin.dll]+0xf8 -// 20220504 +const getWechatVersionFunction = (() => { + if (currentVersion) { + return currentVersion + } + const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' + const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) + if (results.length == 0) { + return 0 + } + const addr = results[0].address + const ret = addr.add(0x07).readPointer() + const ver = ret.add(0x0).readU32() + currentVersion = ver + return ver +}) -let g_initTestAsm = null -let g_BufferEbp2C = null -let g_initECXU32 = null -let g_initECXPtr = null -let g_initEBXPtr = null -let g_initEBX = null +// 011 done +const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { + if (!ver) { + return '0.0.0.0' + } + const vers = [] + vers.push((ver >> 24) & 255 - 0x60) + vers.push((ver >> 16) & 255) + vers.push((ver >> 8) & 255) + vers.push(ver & 255) + return vers.join('.') +}) -let g_attatchEBP210Buffer = null -let g_attatchPathPtr = null -let g_attatchPath = null -let g_attatchEBPAc = null -let g_attatchEBPAcBufPtr = null +// 012 done +const checkSupportedFunction = (() => { + const ver = getWechatVersionFunction() + return ver == availableVersion +}) -let g_attatchContactIdPtr = null -//let g_attatchECXBuffer = null -let g_attatchESIU32 = null +// 019 done +/** + * @Hook: recvMsg -> recvMsgNativeCallback + */ + const recvMsgNativeCallback = (() => { -let g_initECXTempPtr = null -const initGlobal = ((contactId, attatchFile) => { - //const base = moduleBaseAddress.add(0x222f38c).readPointer() - //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 - console.log('------------g_attatchEBPAc', g_attatchEBPAc) - console.log('------------g_EDIU32', g_EDIU32) - g_initTestAsm = Memory.alloc(Process.pageSize) - console.log('------------address', g_initTestAsm) + const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) - g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) - g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() - g_initECXU32 = g_initECXPtr.toInt32() - g_attatchESIU32 = g_EDIU32 + Interceptor.attach( + moduleBaseAddress.add(offset.hook_point), { + onEnter() { + const addr = this.context.ecx //0xc30-0x08 + const msgType = addr.add(0x38).readU32() + const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg - console.log('------------g_initECXU32', g_initECXU32) - console.log('------------g_initESIU32', g_attatchESIU32) + if (msgType > 0) { + const talkerIdPtr = addr.add(0x48).readPointer() + //console.log('txt msg',talkerIdPtr.readUtf16String()) + const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 - //console.log('==========g_initECXPtr',g_initECXPtr) - //console.log('==========g_EDIU32',g_EDIU32) + const myTalkerIdPtr = Memory.alloc(talkerIdLen) + Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) - //g_attatchECXBuffer = Memory.alloc(0x1024) - //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) - g_BufferEbp2C = Memory.alloc(0x48) + let contentPtr = null + let contentLen = 0 + let myContentPtr = null + if (msgType == 3) { // pic path + let thumbPtr = addr.add(0x198).readPointer(); + let hdPtr = addr.add(0x1ac).readPointer(); + let thumbPath = thumbPtr.readUtf16String(); + let hdPath = hdPtr.readUtf16String(); + let picData = [ + thumbPath, // PUPPET.types.Image.Unknown + thumbPath, // PUPPET.types.Image.Thumbnail + hdPath, // PUPPET.types.Image.HD + hdPath // PUPPET.types.Image.Artwork + ] + let content = JSON.stringify(picData); + myContentPtr = Memory.allocUtf16String(content); + } else { + contentPtr = addr.add(0x70).readPointer() + contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 + myContentPtr = Memory.alloc(contentLen) + Memory.copy(myContentPtr, contentPtr, contentLen) + } - //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() - //g_initEBXPtr = Memory.alloc(0x14).writePointer(g_initEBX) - //g_initEBXPtr.add(0x08).writePointer(g_BufferEbp2C) + // console.log('----------------------------------------') + // console.log(msgType) + // console.log(contentPtr.readUtf16String()) + // console.log('----------------------------------------') + const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 + let myGroupMsgSenderIdPtr = null + if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 - g_attatchPathPtr = Memory.alloc(attatchFile.length * 2 + 2) - g_attatchPathPtr.writeUtf16String(attatchFile) + myGroupMsgSenderIdPtr = Memory.alloc(0x10) + myGroupMsgSenderIdPtr.writeUtf16String("null") - g_attatchPath = Memory.alloc(0x28) - g_attatchPath.writePointer(g_attatchPathPtr).add(0x04) - .writeU32(attatchFile.length * 2).add(0x04) - .writeU32(attatchFile.length * 2).add(0x04) - /*---------------------------------ebp-210----------------*/ - g_attatchEBP210Buffer = Memory.alloc(0x48) - g_attatchEBP210Buffer.writeU32(0x3) - g_attatchEBP210Buffer.add(0x4).writePointer(g_attatchPathPtr) - g_attatchEBP210Buffer.add(0x8).writeU32(attatchFile.length * 2) - g_attatchEBP210Buffer.add(0xC).writeU32(attatchFile.length * 2) - g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) - //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) - /*---------------------------------ebp-210----------------*/ + } else { - //g_attatchContactIdPtr = Memory.alloc(0x4) - //g_attatchContactIdPtr.writeUtf16String(contactId) - //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) - g_attatchEBPAc = Memory.alloc(0x140) - //g_attatchEBPAcBufPtr = Memory.alloc(0x100) - //g_attatchEBPAc.writePointer(g_attatchEBP210Buffer) - //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) - //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) - g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) - console.log('------------g_attatchEBPAc', g_attatchEBPAc) + const groupMsgSenderIdPtr = addr.add(0x170).readPointer() + const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 + myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) + Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) - /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) - g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) - .add(0x04).writeU32(contactId.length*2)*/ + } + const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 + let myXmlContentPtr = null + if (xmlNullPtr == 0) { + myXmlContentPtr = Memory.alloc(0x10) + myXmlContentPtr.writeUtf16String("null") - //g_attatchESIU32 = g_EDI.toInt32() + } else { + const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 + myXmlContentPtr = Memory.alloc(xmlContentLen) + Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + } - //console.log('------------g_attatchESIU32',g_attatchESIU32) - //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) + setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) + } + } + }) + return nativeCallback +})() - Memory.patchCode(g_initTestAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: g_initTestAsm }) - cw.putPushfx() - cw.putPushax() - //cw.putMovRegAddress('eax', g_attatchEBP210Buffer) +// 003 done +const getBaseNodeAddress = (() => { + return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() +}) - /*cw.putMovRegAddress('edi',g_EDIPtr) - cw.putMovRegReg('esi','edi') - cw.putMovRegAddress('eax',g_BufferEbp2C) - cw.putMovRegAddress('ecx',g_initECXTempPtr) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ +// 004 done +const getHeaderNodeAddress = (() => { + const baseAddress = getBaseNodeAddress() + //console.log('baseAddress',baseAddress) + if (baseAddress.isNull()) { + return baseAddress + } - //cw.putMovRegOffsetPtrU32('ebp', -20, 0) - - /*cw.putPushU32(0) - cw.putMovRegAddress('eax', g_attatchPathPtr) - cw.putPushReg('eax') - cw.putPushU32(3) - cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) - cw.putCallAddress(moduleBaseAddress.add(0x130220))*/ + //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) + return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() +}) +// 006 done +const getMyselfInfoFunction = (() => { - /*cw.putMovRegAddress('edi', g_attatchEBPAc)//后面要用的的ebp-2c - cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) - cw.putPushReg('ecx') - cw.putMovRegU32('eax',0) - cw.putPushReg('eax')//push eax - cw.putMovRegReg('ecx', 'edi') - cw.putMovRegAddress('esi',g_attatchPathPtr) - cw.putCallAddress(moduleBaseAddress.add(0x138880))*/ + let ptr = 0 + let wx_code = '' + let wx_id = '' + let wx_name = '' + let head_img_url = '' + const base = moduleBaseAddress.add(offset.myselfinfo.offset) + const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() - /*cw.putSubRegImm('esp',0x14) - cw.putMovRegU32('ecx',g_initECXU32) - cw.putMovRegU32('esi',g_attatchESIU32) - cw.putMovRegAddress('eax', g_attatchEBPAc) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x173620))*/ - //cw.putCallAddress(moduleBaseAddress.add(0x522590)) + if (wxid_len === 0x13) { // 新版本微信 + wx_id = base.readPointer().readAnsiString(wxid_len) + wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() + } else { + wx_id = readString(base) + wx_code = wx_id + } - /** g_attatchEBPAc*/ - //cw.putMovRegNearPtr('eax', g_attatchEBPAc) - //cw.putMovNearPtrReg(g_attatchEBPAc.add(0xc), 'eax') - /** g_attatchEBPAc*/ + wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) + const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() + const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() - //cw.putMovRegAddress('ebx', g_initEBXPtr) - //cw.putMovRegU32('edi', g_EDI.toInt32()) - //cw.putMovRegU32('esi', g_EDI.toInt32()) - /*cw.putMovRegU32('ecx', g_initECX) - cw.putMovRegAddress('eax', g_BufferEbp2C) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x131BB0))*/ + head_img_url = img_addr.readAnsiString(img_len) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() + const myself = { + id: wx_id, + code: wx_code, + name: wx_name, + head_img_url: head_img_url, + }; - }) + return JSON.stringify(myself) - const nativeativeFunction = new NativeFunction(ptr(g_initTestAsm), 'void', []) - nativeativeFunction() }) +// 043 done +const recurseNew = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { + return + } -let g_personal_detail_ebx = null -let g_personal_detail_asm = null -let g_personal_wxid = null -let g_personal_wxid_ptr = null -const getOldTest = ((wxid) => {//personal detail + if (node.equals(headerNodeAddress)) { + return + } - g_personal_detail_asm = Memory.alloc(Process.pageSize) - g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } - g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) - g_personal_wxid_ptr.writeUtf16String(wxid) - g_personal_wxid = Memory.alloc(0x14) - g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) - .writeU32(wxid.length * 2).add(0x04) - .writeU32(wxid.length * 2).add(0x08) + nodeList.push(node) + const id = readString(node.add(0x8)) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x30)) + //console.log('-----------',wxid) - console.log('-----------address----------', g_personal_detail_asm) - Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: g_personal_detail_asm }) - cw.putPushfx() - cw.putPushax() + //custom id, if not set return null, and use wxid which should be custom id + //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) - cw.putCallAddress(moduleBaseAddress.add(0x9A000)) + //custom Nickname + const name = readWideString(node.add(0x8c)) - //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | - cw.putMovRegU32('ebx', g_personal_detail_ebx) - cw.putMovRegReg('esi', 'eax') - cw.putPushReg('ebx') - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', g_personal_wxid) - cw.putMovRegReg('ecx', 'esp') - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - cw.putMovRegReg('ecx', 'esi') - cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) + //alias aka 'remark' in wechat + //const alias = readWideString(node.add(0x80)) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() + //avatarUrl + //const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + //const gender = node.add(0x18C).readU32() - }) + const contactJson = { + id1: id, + id: wxid, + name: name, + /*code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender,*/ + } - const nativeativeFunction = new NativeFunction(ptr(g_personal_detail_asm), 'void', []) - nativeativeFunction() + contactList.push(contactJson) + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + //const rightNode = node.add(0x08).readPointer() -}) + recurseNew(leftNode) + recurseNew(centerNode) + //recurse(rightNode) -// const writeLogNativeCallback = (() => { -// const nativeCallback = new NativeCallback(() => { }, 'void', []) -// const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) + const allContactJson = contactList + return allContactJson -// Interceptor.attach( -// moduleBaseAddress.add(0x7008A4), -// { -// onEnter() { -// const addr = this.context.eax//.sub(0x114)//0xc30-0x08 -// if(addr >0){ -// const log = ptr(addr).readAnsiString() -// } -// } -// }) -// return nativeCallback -// })() +}) -/** - * test call - */ -let attatchTestAsm = null -let attatchTestEbp2C = null -let attatchGlobalEDI = null -let attatchGlobalEDIB88 = null -let attatchTestEBX = null -let g_tempEcx = null -let attatchFirstECX = null -//let attatchFirstECX = null -let gattatchFilePtr = null -let gattatchFile = null -let gattatchReceiveIdPtr = null -let gattatchReceiveId = null -let attatchEAX3B0Buf = null +// 044 done +const getContactNativeFunction = (() => { + const headerNodeAddress = getHeaderNodeAddress() + //console.log('headerNodeAddress',headerNodeAddress) -let attatchESIbuf = null -const getWxTest = ((contactId, filePath) => { - //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) - //nativeativeFunction() - attatchTestAsm = Memory.alloc(Process.pageSize) - console.log('----------------address', attatchTestAsm) - attatchTestEbp2C = Memory.alloc(0xC) - attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() - .add(0x938).add(0x438).readPointer() - attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() - attatchTestEBX = Memory.alloc(0x4) - attatchTestEBX.writePointer(attatchGlobalEDI) - console.log('----------------attatchGlobalEDI', attatchGlobalEDI) - console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) + if (headerNodeAddress.isNull()) { + return '[]' + } - attatchFirstECX = Memory.alloc(0x28) - //const attatchSecondEcx = Memory.alloc(0x14) + const node = headerNodeAddress.add(0x0).readPointer() + const ret = recurseNew(node) - const contactIdLength = contactId.length * 2 + 2//edx - const contractIdActLength = contactId.length + //console.log(ret) + console.log('getContactNativeFunction:', ret.length) + /*for (let item of ret){ + console.log(JSON.stringify(item)) + }*/ + //console.log(ret.contact) + const cloneRet = JSON.stringify(ret) + nodeList.length = 0 + contactList.length = 0 - gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) - gattatchReceiveIdPtr.writeUtf16String(contactId) + return cloneRet +}) - gattatchReceiveId = Memory.alloc(0x14) - gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x08) - //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) - //return - /*console.log(hexdump(attatchTestEBX, { - offset: 0, - length: 0x40, - header: true, - ansi: true - })) - return*/ +// 0xx done +const getChatroomNodeAddress = (() => { + const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() + if (baseAddress.isNull()) { + return baseAddress + } + return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() +}) - gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) - gattatchFilePtr.writeUtf16String(filePath) - - gattatchFile = Memory.alloc(0x14) - gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) - .writeU32(filePath.length * 2).add(0x04) - .writeU32(filePath.length * 2).add(0x08) - - const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() - - attatchEAX3B0Buf = Memory.alloc(0x3B0) - - g_tempEcx = Memory.alloc(0x4) - //g_tempEcx1 = Memory.alloc(0x4) - - attatchESIbuf = Memory.alloc(0x100) - attatchESIbuf.add(0x0).writeU32(3) - attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) - attatchESIbuf.add(0x8).writeU32(filePath.length * 2) - attatchESIbuf.add(0xc).writeU32(filePath.length * 2) - - Memory.patchCode(attatchTestAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: attatchTestAsm }) - cw.putPushfx() - cw.putPushax() - //cw.putMovRegU32('edi',attatchGlobalEDI) - - - cw.putMovRegAddress('esi', attatchESIbuf) - - cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) - cw.putMovRegAddress('eax', gattatchFile) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - - - cw.putMovRegAddress('ecx', attatchFirstECX) - cw.putMovRegU32('eax', contactIdLength) - cw.putPushReg('eax') - cw.putPushU32(0) - cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) - cw.putAddRegImm('esp', 0x8) - - cw.putMovRegReg('edx', 'eax') - cw.putMovNearPtrReg(attatchFirstECX, 'edx') - cw.putMovRegU32('edi', contactIdLength) - cw.putPushReg('edi') - cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 - cw.putPushReg('eax') - cw.putMovRegNearPtr('eax', attatchFirstECX) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) - cw.putAddRegImm('esp', 0x0c) - - //cw.putMovRegNearPtr('ecx',attatchFirstECX) - //cw.putAddRegImm('esp', 0x0c) - //cw.putMovRegU32('edx', 0) - //cw.putMovRegRegPtr('eax', 'ecx') - //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') - cw.putMovRegU32('edi', contactId.length * 2) - cw.putMovRegU32('ecx', attatchLastECX) - cw.putMovRegAddress('eax', attatchEAX3B0Buf) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x392260)) - - cw.putMovRegAddress('ecx', attatchEAX3B0Buf) - cw.putCallAddress(moduleBaseAddress.add(0x94200)) - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(attatchTestAsm), 'void', []) - nativeativeFunction() +// 008chatroom member done +const chatroomRecurse = ((node) => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return + } + if (node.equals(chatroomNodeAddress)) { + return + } -}) -// 001 -const getTestInfoFunction = ((addr) => { - const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) - nativeativeFunction() + for (const item in chatroomNodeList) { + if (node.equals(chatroomNodeList[item])) { + return + } + } - //00CFE484 + chatroomNodeList.push(node) + const roomid = readWideString(node.add(0x10)) + const len = node.add(0x54).readU32() // + //const memberJson={} + if (len > 4) { // + const memberStr = readString(node.add(0x44)) + if (memberStr.length > 0) { + const memberList = memberStr.split(/[\\^][G]/) + const memberJson = { + roomid: roomid, + roomMember: memberList + } - /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { - onAccess(details){ - console.log('============') - console.log(details.operation) - console.log(details.from) - console.log(details.address) - console.log('============') + chatroomMemberList.push(memberJson) } - })*/ -}) + } -// 002get global data + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() -const isLoggedInFunction = (() => { - loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() - return !!loggedIn -}) + chatroomRecurse(leftNode) + chatroomRecurse(centerNode) + chatroomRecurse(rightNode) -// 007 缺失,请标注已废弃或者其他原因 -const getMyselfIdFunction = (() => { + const allChatroomMemberJson = chatroomMemberList + return allChatroomMemberJson +}) - let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) +// 009 done +const getChatroomMemberInfoFunction = (() => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return '[]' + } - return wx_id + const node = chatroomNodeAddress.add(0x0).readPointer() + const ret = chatroomRecurse(node) + const cloneRet = JSON.stringify(ret) + chatroomNodeList.length = 0 //empty + chatroomMemberList.length = 0 //empty + return cloneRet }) -// std::wstring -// const wstr = readWStringPtr(ptr).readUtf16String() -const readWStringPtr = (address) => { - const addr = ptr(address) - const size = addr.add(4).readU32() - const capacity = addr.add(8).readU32() - addr.ptr = addr.readPointer() - addr.size = size - addr.capacity = capacity - addr.ptr._readUtf16String = addr.ptr.readUtf16String - addr.readUtf16String = () => { return addr.size ? addr.ptr._readUtf16String(addr.size * 2) : '' } - - // console.log('readWStringPtr() address:',address,' -> ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) - // console.log('readWStringPtr() str:' , `"${addr.readUtf16String()}"`,'\n',addr.ptr.readByteArray(addr.size*2+2),'\n') - // console.log('readWStringPtr() address:', addr,'dump:', addr.readByteArray(16),'\n') +// 024 done +/** + * sendMsgNativeFunction + * send text message + * @param {string} talkerId = wxid or roomid + * @param {string} content + */ + const sendMsgNativeFunction = ((talkerId, content) => { - return addr -} + const txtAsm = Memory.alloc(Process.pageSize) + //const buffwxid = Memory.alloc(0x20) -//contact -const recurse = ((node) => { - const headerNodeAddress = getHeaderNodeAddress() - if (headerNodeAddress.isNull()) { return } - if (node.equals(headerNodeAddress)) { return } + let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) + wxidPtr.writeUtf16String(talkerId) - for (const item in nodeList) { - if (node.equals(nodeList[item])) { - return - } - } + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(wxidPtr)).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + let contentPtr = Memory.alloc(content.length * 2 + 2) + contentPtr.writeUtf16String(content) - nodeList.push(node) - //wxid, format relates to registration method - const wxid = readWideString(node.add(0x38)) + const sizeOfStringStruct = Process.pointerSize * 5 + let contentStruct = Memory.alloc(sizeOfStringStruct) - //custom id, if not set return null, and use wxid which should be custom id - const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + contentStruct + .writePointer(contentPtr).add(0x4) + .writeU32(content.length).add(0x4) + .writeU32(content.length * 2) - //custom Nickname - const name = readWideString(node.add(0x94)) - //alias aka 'remark' in wechat - const alias = readWideString(node.add(0x80)) + const ecxBuffer = Memory.alloc(0x2d8) - //avatarUrl - const avatar = readWideString(node.add(0x138)) - //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) - //contact gender - const gender = node.add(0x18C).readU32() + Memory.patchCode(txtAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: txtAsm + }) + cw.putPushfx() + cw.putPushax() - const contactJson = { - id: wxid, - code: wx_code, - name: name, - alias: alias, - avatarUrl: avatar, - gender: gender, - } + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + cw.putPushU32(0x0) - contactList.push(contactJson) + //cw.putMovRegReg - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() + cw.putMovRegAddress('eax', contentStruct) + cw.putPushReg('eax') - recurse(leftNode) - recurse(centerNode) - recurse(rightNode) + cw.putMovRegAddress('edx', picWxid) //room_id - const allContactJson = contactList - return allContactJson + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) -}) + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() -// 010 done -const getWechatVersionFunction = (() => { - if (currentVersion) { - return currentVersion - } - const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' - const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) - if (results.length == 0) { - return 0 - } - const addr = results[0].address - const ret = addr.add(0x07).readPointer() - const ver = ret.add(0x0).readU32() - currentVersion = ver - return ver -}) + }) -// 011 done -const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { - if (!ver) { - return '0.0.0.0' - } - const vers = [] - vers.push((ver >> 24) & 255 - 0x60) - vers.push((ver >> 16) & 255) - vers.push((ver >> 8) & 255) - vers.push(ver & 255) - return vers.join('.') -}) + console.log('----------txtAsm', txtAsm) + const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + nativeativeFunction() -// 012 done -const checkSupportedFunction = (() => { - const ver = getWechatVersionFunction() - return ver == availableVersion }) +// 023 done /** - * @Hook: recvMsg -> recvMsgNativeCallback - */ +* send at msg +*/ +let asmAtMsg = null +let roomid_, msg_, wxid_, atid_ +let ecxBuffer +const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { -// 019 done -const recvMsgNativeCallback = (() => { + asmAtMsg = Memory.alloc(Process.pageSize) + ecxBuffer = Memory.alloc(0x3b0) + const atContent = '@'+nickname+' '+text - const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + roomid_ = initStruct(roomId) + wxid_ = initidStruct(contactId) + msg_ = initmsgStruct(atContent) + atid_ = initAtMsgStruct(wxid_) - Interceptor.attach( - moduleBaseAddress.add(offset.hook_point), { - onEnter() { - const addr = this.context.ecx //0xc30-0x08 - const msgType = addr.add(0x38).readU32() - const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg + Memory.patchCode(asmAtMsg, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: asmAtMsg + }) + cw.putPushfx() + cw.putPushax() - if (msgType > 0) { + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + //cw.putPushU32(0x0) + cw.putMovRegAddress('eax', atid_) + cw.putPushReg('eax') - const talkerIdPtr = addr.add(0x48).readPointer() - //console.log('txt msg',talkerIdPtr.readUtf16String()) - const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 + //cw.putMovRegReg - const myTalkerIdPtr = Memory.alloc(talkerIdLen) - Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) + cw.putMovRegAddress('eax', msg_) + cw.putPushReg('eax') + cw.putMovRegAddress('edx', roomid_) //room_id - let contentPtr = null - let contentLen = 0 - let myContentPtr = null - if (msgType == 3) { // pic path - let thumbPtr = addr.add(0x198).readPointer(); - let hdPtr = addr.add(0x1ac).readPointer(); - let thumbPath = thumbPtr.readUtf16String(); - let hdPath = hdPtr.readUtf16String(); - let picData = [ - thumbPath, // PUPPET.types.Image.Unknown - thumbPath, // PUPPET.types.Image.Thumbnail - hdPath, // PUPPET.types.Image.HD - hdPath // PUPPET.types.Image.Artwork - ] - let content = JSON.stringify(picData); - myContentPtr = Memory.allocUtf16String(content); - } else { - contentPtr = addr.add(0x70).readPointer() - contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 - myContentPtr = Memory.alloc(contentLen) - Memory.copy(myContentPtr, contentPtr, contentLen) - } + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) - // console.log('----------------------------------------') - // console.log(msgType) - // console.log(contentPtr.readUtf16String()) - // console.log('----------------------------------------') - const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 - let myGroupMsgSenderIdPtr = null - if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - myGroupMsgSenderIdPtr = Memory.alloc(0x10) - myGroupMsgSenderIdPtr.writeUtf16String("null") + }) - } else { + //console.log('----------txtAsm', asmAtMsg) + const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + nativeativeFunction() - const groupMsgSenderIdPtr = addr.add(0x170).readPointer() - const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 - myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) - Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) +}) - } +// 022 done +/** + * + * @param {*} contactId + * @param {*} path + */ + const sendPicMsgNativeFunction = ((contactId, path) => { - const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 - let myXmlContentPtr = null - if (xmlNullPtr == 0) { + const picAsm = Memory.alloc(Process.pageSize) + const buffwxid = Memory.alloc(0x20) + const picbuff = Memory.alloc(0x2D8) - myXmlContentPtr = Memory.alloc(0x10) - myXmlContentPtr.writeUtf16String("null") + let pathPtr = Memory.alloc(path.length * 2 + 1) + pathPtr.writeUtf16String(path) - } else { - const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + let imagefilepath = Memory.alloc(0x24) + imagefilepath.writePointer(pathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) - const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 - myXmlContentPtr = Memory.alloc(xmlContentLen) - Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) - } + let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) + picWxidPtr.writeUtf16String(contactId) - setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) - } - } - }) - return nativeCallback -})() + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(picWxidPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x04) -// 003get myself info done -const getBaseNodeAddress = (() => { - return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() -}) -// 004 done -const getHeaderNodeAddress = (() => { - const baseAddress = getBaseNodeAddress() - //console.log('baseAddress',baseAddress) - if (baseAddress.isNull()) { - return baseAddress - } + //const test_offset1 = 0x701DC0; + Memory.patchCode(picAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: picAsm + }) + cw.putPushfx(); + cw.putPushax(); + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call1 + )) + cw.putMovRegReg('edx', 'eax') //缓存 - //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) - return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() -}) + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', buffwxid) + cw.putMovRegReg('ecx', 'esp') + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call2 + )) -// 006 done -const getMyselfInfoFunction = (() => { + cw.putMovRegReg('ecx', 'edx') + cw.putMovRegAddress('eax', picWxid) //=lea + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('edi') + cw.putPushReg('eax') + cw.putMovRegAddress('eax', picbuff) + cw.putPushReg('eax') - let ptr = 0 - let wx_code = '' - let wx_id = '' - let wx_name = '' - let head_img_url = '' + cw.putMovRegAddress('edi', picWxid) //edi + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call3 + )) - const base = moduleBaseAddress.add(offset.myselfinfo.offset) - const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() - if (wxid_len === 0x13) { // 新版本微信 - wx_id = base.readPointer().readAnsiString(wxid_len) - wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() - } else { - wx_id = readString(base) - wx_code = wx_id - } + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) - const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() - const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() + }) - head_img_url = img_addr.readAnsiString(img_len) + //console.log('----------picAsm',picAsm) + const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) + nativeativeFunction() - const myself = { - id: wx_id, - code: wx_code, - name: wx_name, - head_img_url: head_img_url, - }; +}) - return JSON.stringify(myself) +// 020 done +let memberNickBuffAsm = null +let nickRoomId = null +let nickMemberId = null +let nickBuff = null +const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { -}) + nickBuff = Memory.alloc(0x7e4) + //const nickRetAddr = Memory.alloc(0x04) + memberNickBuffAsm = Memory.alloc(Process.pageSize) + //console.log('asm address----------',memberNickBuffAsm) + nickRoomId = initidStruct(roomId) + //console.log('nick room id',nickRoomId) + nickMemberId = initStruct(memberId) -// done -const recurseNew = ((node) => { - const headerNodeAddress = getHeaderNodeAddress() - if (headerNodeAddress.isNull()) { - return - } + //console.log('nick nickMemberId id',nickMemberId) + //const nickStructPtr = initmsgStruct('') - if (node.equals(headerNodeAddress)) { - return - } + Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: memberNickBuffAsm + }) + cw.putPushfx() + cw.putPushax() + cw.putMovRegAddress('edi', nickRoomId) + cw.putMovRegAddress('eax', nickBuff) + cw.putMovRegReg('edx', 'edi') + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', nickMemberId) + cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) + cw.putAddRegImm('esp', 0x04) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - for (const item in nodeList) { - if (node.equals(nodeList[item])) { - return - } - } + }) + const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) + nativeativeFunction() - nodeList.push(node) - const id = readString(node.add(0x8)) - //wxid, format relates to registration method - const wxid = readWideString(node.add(0x30)) - //console.log('-----------',wxid) + const nickname = readWideString(nickBuff) + console.log('----nickname', nickname) + return readWideString(nickBuff) +}) +/* 由此之后是3.9.2.23中缺失的未实现方法 */ - //custom id, if not set return null, and use wxid which should be custom id - //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) +// 038 初始化全局变量 tbd +let g_initTestAsm = null +let g_BufferEbp2C = null +let g_initECXU32 = null +let g_initECXPtr = null +let g_initEBXPtr = null +let g_initEBX = null - //custom Nickname - const name = readWideString(node.add(0x8c)) +let g_attatchEBP210Buffer = null +let g_attatchPathPtr = null +let g_attatchPath = null +let g_attatchEBPAc = null +let g_attatchEBPAcBufPtr = null - //alias aka 'remark' in wechat - //const alias = readWideString(node.add(0x80)) +let g_attatchContactIdPtr = null +//let g_attatchECXBuffer = null +let g_attatchESIU32 = null - //avatarUrl - //const avatar = readWideString(node.add(0x138)) - //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) - //contact gender - //const gender = node.add(0x18C).readU32() +let g_initECXTempPtr = null +const initGlobal = ((contactId, attatchFile) => { - const contactJson = { - id1: id, - id: wxid, - name: name, - /*code: wx_code, - name: name, - alias: alias, - avatarUrl: avatar, - gender: gender,*/ - } + //const base = moduleBaseAddress.add(0x222f38c).readPointer() + //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 + console.log('------------g_attatchEBPAc', g_attatchEBPAc) + console.log('------------g_EDIU32', g_EDIU32) + g_initTestAsm = Memory.alloc(Process.pageSize) + console.log('------------address', g_initTestAsm) - contactList.push(contactJson) + g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) + g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() + g_initECXU32 = g_initECXPtr.toInt32() + g_attatchESIU32 = g_EDIU32 - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - //const rightNode = node.add(0x08).readPointer() + console.log('------------g_initECXU32', g_initECXU32) + console.log('------------g_initESIU32', g_attatchESIU32) - recurseNew(leftNode) - recurseNew(centerNode) - //recurse(rightNode) - const allContactJson = contactList - return allContactJson + //console.log('==========g_initECXPtr',g_initECXPtr) + //console.log('==========g_EDIU32',g_EDIU32) -}) + //g_attatchECXBuffer = Memory.alloc(0x1024) + //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) -// done ? -const getContactNativeFunction = (() => { - const headerNodeAddress = getHeaderNodeAddress() - //console.log('headerNodeAddress',headerNodeAddress) + g_BufferEbp2C = Memory.alloc(0x48) - if (headerNodeAddress.isNull()) { - return '[]' - } + //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() + //g_initEBXPtr = Memory.alloc(0x14).writePointer(g_initEBX) + //g_initEBXPtr.add(0x08).writePointer(g_BufferEbp2C) - const node = headerNodeAddress.add(0x0).readPointer() - const ret = recurseNew(node) + g_attatchPathPtr = Memory.alloc(attatchFile.length * 2 + 2) + g_attatchPathPtr.writeUtf16String(attatchFile) - //console.log(ret) + g_attatchPath = Memory.alloc(0x28) + g_attatchPath.writePointer(g_attatchPathPtr).add(0x04) + .writeU32(attatchFile.length * 2).add(0x04) + .writeU32(attatchFile.length * 2).add(0x04) + /*---------------------------------ebp-210----------------*/ + g_attatchEBP210Buffer = Memory.alloc(0x48) + g_attatchEBP210Buffer.writeU32(0x3) + g_attatchEBP210Buffer.add(0x4).writePointer(g_attatchPathPtr) + g_attatchEBP210Buffer.add(0x8).writeU32(attatchFile.length * 2) + g_attatchEBP210Buffer.add(0xC).writeU32(attatchFile.length * 2) + g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) + //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) + /*---------------------------------ebp-210----------------*/ - console.log('getContactNativeFunction:', ret.length) - /*for (let item of ret){ - console.log(JSON.stringify(item)) - }*/ - //console.log(ret.contact) - const cloneRet = JSON.stringify(ret) - nodeList.length = 0 - contactList.length = 0 + //g_attatchContactIdPtr = Memory.alloc(0x4) + //g_attatchContactIdPtr.writeUtf16String(contactId) + //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) + g_attatchEBPAc = Memory.alloc(0x140) + //g_attatchEBPAcBufPtr = Memory.alloc(0x100) + //g_attatchEBPAc.writePointer(g_attatchEBP210Buffer) + //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) + //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) + g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) + console.log('------------g_attatchEBPAc', g_attatchEBPAc) - return cloneRet -}) + /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) + g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) + .add(0x04).writeU32(contactId.length*2)*/ -// 005 done -const getChatroomNodeAddress = (() => { - const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() - if (baseAddress.isNull()) { - return baseAddress - } - return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() -}) -// 008chatroom member done -const chatroomRecurse = ((node) => { - const chatroomNodeAddress = getChatroomNodeAddress() - if (chatroomNodeAddress.isNull()) { - return - } - if (node.equals(chatroomNodeAddress)) { - return - } + //g_attatchESIU32 = g_EDI.toInt32() - for (const item in chatroomNodeList) { - if (node.equals(chatroomNodeList[item])) { - return - } - } - chatroomNodeList.push(node) - const roomid = readWideString(node.add(0x10)) + //console.log('------------g_attatchESIU32',g_attatchESIU32) + //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) - const len = node.add(0x54).readU32() // - //const memberJson={} - if (len > 4) { // - const memberStr = readString(node.add(0x44)) - if (memberStr.length > 0) { - const memberList = memberStr.split(/[\\^][G]/) - const memberJson = { - roomid: roomid, - roomMember: memberList - } + Memory.patchCode(g_initTestAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: g_initTestAsm }) + cw.putPushfx() + cw.putPushax() + //cw.putMovRegAddress('eax', g_attatchEBP210Buffer) - chatroomMemberList.push(memberJson) - } + /*cw.putMovRegAddress('edi',g_EDIPtr) + cw.putMovRegReg('esi','edi') + cw.putMovRegAddress('eax',g_BufferEbp2C) + cw.putMovRegAddress('ecx',g_initECXTempPtr) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ - } + //cw.putMovRegOffsetPtrU32('ebp', -20, 0) - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() + /*cw.putPushU32(0) + cw.putMovRegAddress('eax', g_attatchPathPtr) + cw.putPushReg('eax') + cw.putPushU32(3) + cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) + cw.putCallAddress(moduleBaseAddress.add(0x130220))*/ - chatroomRecurse(leftNode) - chatroomRecurse(centerNode) - chatroomRecurse(rightNode) - const allChatroomMemberJson = chatroomMemberList - return allChatroomMemberJson -}) + /*cw.putMovRegAddress('edi', g_attatchEBPAc)//后面要用的的ebp-2c + cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) + cw.putPushReg('ecx') + cw.putMovRegU32('eax',0) + cw.putPushReg('eax')//push eax + cw.putMovRegReg('ecx', 'edi') + cw.putMovRegAddress('esi',g_attatchPathPtr) + cw.putCallAddress(moduleBaseAddress.add(0x138880))*/ -// 009 done -const getChatroomMemberInfoFunction = (() => { - const chatroomNodeAddress = getChatroomNodeAddress() - if (chatroomNodeAddress.isNull()) { - return '[]' - } - const node = chatroomNodeAddress.add(0x0).readPointer() - const ret = chatroomRecurse(node) + /*cw.putSubRegImm('esp',0x14) + cw.putMovRegU32('ecx',g_initECXU32) + cw.putMovRegU32('esi',g_attatchESIU32) + cw.putMovRegAddress('eax', g_attatchEBPAc) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x173620))*/ + //cw.putCallAddress(moduleBaseAddress.add(0x522590)) - const cloneRet = JSON.stringify(ret) - chatroomNodeList.length = 0 //empty - chatroomMemberList.length = 0 //empty - return cloneRet -}) + /** g_attatchEBPAc*/ + //cw.putMovRegNearPtr('eax', g_attatchEBPAc) + //cw.putMovNearPtrReg(g_attatchEBPAc.add(0xc), 'eax') + /** g_attatchEBPAc*/ -// 024 done -/** - * sendMsgNativeFunction - * send text message - * @param {string} talkerId = wxid or roomid - * @param {string} content - */ - const sendMsgNativeFunction = ((talkerId, content) => { - const txtAsm = Memory.alloc(Process.pageSize) - //const buffwxid = Memory.alloc(0x20) + //cw.putMovRegAddress('ebx', g_initEBXPtr) + //cw.putMovRegU32('edi', g_EDI.toInt32()) + //cw.putMovRegU32('esi', g_EDI.toInt32()) + /*cw.putMovRegU32('ecx', g_initECX) + cw.putMovRegAddress('eax', g_BufferEbp2C) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x131BB0))*/ + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) - wxidPtr.writeUtf16String(talkerId) + }) - let picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(wxidPtr)).add(0x04) - .writeU32(talkerId.length * 2).add(0x04) - .writeU32(talkerId.length * 2).add(0x04) + const nativeativeFunction = new NativeFunction(ptr(g_initTestAsm), 'void', []) + nativeativeFunction() +}) - let contentPtr = Memory.alloc(content.length * 2 + 2) - contentPtr.writeUtf16String(content) +// 039 +let g_personal_detail_ebx = null +let g_personal_detail_asm = null +let g_personal_wxid = null +let g_personal_wxid_ptr = null +const getOldTest = ((wxid) => {//personal detail - const sizeOfStringStruct = Process.pointerSize * 5 - let contentStruct = Memory.alloc(sizeOfStringStruct) + g_personal_detail_asm = Memory.alloc(Process.pageSize) + g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() - contentStruct - .writePointer(contentPtr).add(0x4) - .writeU32(content.length).add(0x4) - .writeU32(content.length * 2) + g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) + g_personal_wxid_ptr.writeUtf16String(wxid) + g_personal_wxid = Memory.alloc(0x14) + g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) + .writeU32(wxid.length * 2).add(0x04) + .writeU32(wxid.length * 2).add(0x08) - const ecxBuffer = Memory.alloc(0x2d8) + console.log('-----------address----------', g_personal_detail_asm) - Memory.patchCode(txtAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: txtAsm - }) + Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: g_personal_detail_asm }) cw.putPushfx() cw.putPushax() - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x1) - cw.putPushU32(0x0) - - //cw.putMovRegReg + cw.putCallAddress(moduleBaseAddress.add(0x9A000)) - cw.putMovRegAddress('eax', contentStruct) + //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | + cw.putMovRegU32('ebx', g_personal_detail_ebx) + cw.putMovRegReg('esi', 'eax') + cw.putPushReg('ebx') + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', g_personal_wxid) + cw.putMovRegReg('ecx', 'esp') cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) + cw.putMovRegReg('ecx', 'esi') + cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) - cw.putMovRegAddress('edx', picWxid) //room_id - - cw.putMovRegAddress('ecx', ecxBuffer) - cw.putCallAddress(moduleBaseAddress.add( - offset.sendTxtMsg.callOffset - )) - - cw.putAddRegImm('esp', 0x18) cw.putPopax() cw.putPopfx() cw.putRet() @@ -1094,59 +1037,154 @@ const getChatroomMemberInfoFunction = (() => { }) - console.log('----------txtAsm', txtAsm) - const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + const nativeativeFunction = new NativeFunction(ptr(g_personal_detail_asm), 'void', []) nativeativeFunction() + }) -// 023 done +// const writeLogNativeCallback = (() => { +// const nativeCallback = new NativeCallback(() => { }, 'void', []) +// const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) + +// Interceptor.attach( +// moduleBaseAddress.add(0x7008A4), +// { +// onEnter() { +// const addr = this.context.eax//.sub(0x114)//0xc30-0x08 +// if(addr >0){ +// const log = ptr(addr).readAnsiString() +// } +// } +// }) +// return nativeCallback +// })() + +// 040 /** -* send at msg -*/ -let asmAtMsg = null -let roomid_, msg_, wxid_, atid_ -let ecxBuffer -const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { + * test call + */ +let attatchTestAsm = null +let attatchTestEbp2C = null +let attatchGlobalEDI = null +let attatchGlobalEDIB88 = null +let attatchTestEBX = null +let g_tempEcx = null +let attatchFirstECX = null +//let attatchFirstECX = null +let gattatchFilePtr = null +let gattatchFile = null +let gattatchReceiveIdPtr = null +let gattatchReceiveId = null +let attatchEAX3B0Buf = null - asmAtMsg = Memory.alloc(Process.pageSize) - ecxBuffer = Memory.alloc(0x3b0) +let attatchESIbuf = null +const getWxTest = ((contactId, filePath) => { + //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) + //nativeativeFunction() + attatchTestAsm = Memory.alloc(Process.pageSize) + console.log('----------------address', attatchTestAsm) + attatchTestEbp2C = Memory.alloc(0xC) + attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() + .add(0x938).add(0x438).readPointer() + attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() + attatchTestEBX = Memory.alloc(0x4) + attatchTestEBX.writePointer(attatchGlobalEDI) + console.log('----------------attatchGlobalEDI', attatchGlobalEDI) + console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) - const atContent = '@'+nickname+' '+text + attatchFirstECX = Memory.alloc(0x28) + //const attatchSecondEcx = Memory.alloc(0x14) - roomid_ = initStruct(roomId) - wxid_ = initidStruct(contactId) - msg_ = initmsgStruct(atContent) - atid_ = initAtMsgStruct(wxid_) + const contactIdLength = contactId.length * 2 + 2//edx + const contractIdActLength = contactId.length - Memory.patchCode(asmAtMsg, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: asmAtMsg - }) + + gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) + gattatchReceiveIdPtr.writeUtf16String(contactId) + + gattatchReceiveId = Memory.alloc(0x14) + gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x08) + //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) + //return + /*console.log(hexdump(attatchTestEBX, { + offset: 0, + length: 0x40, + header: true, + ansi: true + })) + return*/ + + gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) + gattatchFilePtr.writeUtf16String(filePath) + + gattatchFile = Memory.alloc(0x14) + gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) + .writeU32(filePath.length * 2).add(0x04) + .writeU32(filePath.length * 2).add(0x08) + + const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() + + attatchEAX3B0Buf = Memory.alloc(0x3B0) + + g_tempEcx = Memory.alloc(0x4) + //g_tempEcx1 = Memory.alloc(0x4) + + attatchESIbuf = Memory.alloc(0x100) + attatchESIbuf.add(0x0).writeU32(3) + attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) + attatchESIbuf.add(0x8).writeU32(filePath.length * 2) + attatchESIbuf.add(0xc).writeU32(filePath.length * 2) + + Memory.patchCode(attatchTestAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: attatchTestAsm }) cw.putPushfx() cw.putPushax() + //cw.putMovRegU32('edi',attatchGlobalEDI) - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x1) - //cw.putPushU32(0x0) - cw.putMovRegAddress('eax', atid_) + + cw.putMovRegAddress('esi', attatchESIbuf) + + cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) + cw.putMovRegAddress('eax', gattatchFile) cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - //cw.putMovRegReg - cw.putMovRegAddress('eax', msg_) + cw.putMovRegAddress('ecx', attatchFirstECX) + cw.putMovRegU32('eax', contactIdLength) cw.putPushReg('eax') + cw.putPushU32(0) + cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) + cw.putAddRegImm('esp', 0x8) - cw.putMovRegAddress('edx', roomid_) //room_id + cw.putMovRegReg('edx', 'eax') + cw.putMovNearPtrReg(attatchFirstECX, 'edx') + cw.putMovRegU32('edi', contactIdLength) + cw.putPushReg('edi') + cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 + cw.putPushReg('eax') + cw.putMovRegNearPtr('eax', attatchFirstECX) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) + cw.putAddRegImm('esp', 0x0c) - cw.putMovRegAddress('ecx', ecxBuffer) - cw.putCallAddress(moduleBaseAddress.add( - offset.sendTxtMsg.callOffset - )) + //cw.putMovRegNearPtr('ecx',attatchFirstECX) + //cw.putAddRegImm('esp', 0x0c) + //cw.putMovRegU32('edx', 0) + //cw.putMovRegRegPtr('eax', 'ecx') + //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') + cw.putMovRegU32('edi', contactId.length * 2) + cw.putMovRegU32('ecx', attatchLastECX) + cw.putMovRegAddress('eax', attatchEAX3B0Buf) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x392260)) + + cw.putMovRegAddress('ecx', attatchEAX3B0Buf) + cw.putCallAddress(moduleBaseAddress.add(0x94200)) - cw.putAddRegImm('esp', 0x18) cw.putPopax() cw.putPopfx() cw.putRet() @@ -1154,134 +1192,103 @@ const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { }) - //console.log('----------txtAsm', asmAtMsg) - const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + const nativeativeFunction = new NativeFunction(ptr(attatchTestAsm), 'void', []) nativeativeFunction() + }) -// 022 done -/** - * - * @param {*} contactId - * @param {*} path - */ - const sendPicMsgNativeFunction = ((contactId, path) => { +// 001 +const getTestInfoFunction = ((addr) => { + const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) + nativeativeFunction() - const picAsm = Memory.alloc(Process.pageSize) - const buffwxid = Memory.alloc(0x20) - const picbuff = Memory.alloc(0x2D8) + //00CFE484 - let pathPtr = Memory.alloc(path.length * 2 + 1) - pathPtr.writeUtf16String(path) - let imagefilepath = Memory.alloc(0x24) - imagefilepath.writePointer(pathPtr).add(0x04) - .writeU32(path.length * 2).add(0x04) - .writeU32(path.length * 2).add(0x04) + /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { + onAccess(details){ + console.log('============') + console.log(details.operation) + console.log(details.from) + console.log(details.address) + console.log('============') + } + })*/ - let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) - picWxidPtr.writeUtf16String(contactId) +}) - let picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(picWxidPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x04) +// 002 +const isLoggedInFunction = (() => { + loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() + return !!loggedIn +}) +// 007 获得当前账号信息 +const getMyselfIdFunction = (() => { - //const test_offset1 = 0x701DC0; - Memory.patchCode(picAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: picAsm - }) - cw.putPushfx(); - cw.putPushax(); - cw.putCallAddress(moduleBaseAddress.add( - offset.sendPicMsg.call1 - )) - cw.putMovRegReg('edx', 'eax') //缓存 + let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', buffwxid) - cw.putMovRegReg('ecx', 'esp') - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.sendPicMsg.call2 - )) + return wx_id - cw.putMovRegReg('ecx', 'edx') - cw.putMovRegAddress('eax', picWxid) //=lea - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('edi') - cw.putPushReg('eax') - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') +}) - cw.putMovRegAddress('edi', picWxid) //edi - cw.putCallAddress(moduleBaseAddress.add( - offset.sendPicMsg.call3 - )) +//contact +// 042 +const recurse = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { return } + if (node.equals(headerNodeAddress)) { return } + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - }) + nodeList.push(node) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x38)) - //console.log('----------picAsm',picAsm) - const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) - nativeativeFunction() + //custom id, if not set return null, and use wxid which should be custom id + const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) -}) + //custom Nickname + const name = readWideString(node.add(0x94)) -// 020 done -let memberNickBuffAsm = null -let nickRoomId = null -let nickMemberId = null -let nickBuff = null -const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { + //alias aka 'remark' in wechat + const alias = readWideString(node.add(0x80)) - nickBuff = Memory.alloc(0x7e4) - //const nickRetAddr = Memory.alloc(0x04) - memberNickBuffAsm = Memory.alloc(Process.pageSize) - //console.log('asm address----------',memberNickBuffAsm) - nickRoomId = initidStruct(roomId) - //console.log('nick room id',nickRoomId) - nickMemberId = initStruct(memberId) + //avatarUrl + const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + const gender = node.add(0x18C).readU32() - //console.log('nick nickMemberId id',nickMemberId) - //const nickStructPtr = initmsgStruct('') + const contactJson = { + id: wxid, + code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender, + } - Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: memberNickBuffAsm - }) - cw.putPushfx() - cw.putPushax() - cw.putMovRegAddress('edi', nickRoomId) - cw.putMovRegAddress('eax', nickBuff) - cw.putMovRegReg('edx', 'edi') - cw.putPushReg('eax') - cw.putMovRegAddress('ecx', nickMemberId) - cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) - cw.putAddRegImm('esp', 0x04) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() + contactList.push(contactJson) - }) + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() - const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) - nativeativeFunction() + recurse(leftNode) + recurse(centerNode) + recurse(rightNode) + + const allContactJson = contactList + return allContactJson - const nickname = readWideString(nickBuff) - console.log('----nickname', nickname) - return readWideString(nickBuff) }) // 013 @@ -1428,13 +1435,13 @@ const getQrcodeLoginData = () => { return json } - +// 045 /** * 20220504 writelog * 7A566D72 | FFB5 ECFEFFFF | push dword ptr ss:[ebp-114] | 【3.6.0.18】写日志,这个里面就是日志内容 7A566D78 | FFB5 E8FEFFFF | push dword ptr ss:[ebp-118] | */ -/*const writeLogNativeCallback = (() => { +const writeLogNativeCallback = (() => { const nativeCallback = new NativeCallback(() => { }, 'void', []) const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) @@ -1448,8 +1455,9 @@ const getQrcodeLoginData = () => { } }) return nativeCallback -})()*/ +})() +// 046 let nickRoomIdV6 = null let nullEdiWxidStructV6 = null let nickMemberIdStructV6 = null @@ -1509,6 +1517,7 @@ const getChatroomMemberNickInfoV1Function = ((memberId, roomId) => { return readWideString(nullEdiWxidStructV6) }) +// 021 /** * send attatch */ @@ -1550,8 +1559,6 @@ let attatchECX = null param {78EDBC86 | 8B80 38040000 | mov eax,dword ptr ds:[eax+438] | 78EDBC8C | 8BB0 800B0000 | mov esi,dword ptr ds:[eax+B80] |} sendWxid */ - -// 021 const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size) => { attatchAsm = Memory.alloc(Process.pageSize) @@ -1755,78 +1762,6 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size //console.log('-------',attatchEbp1C.add(0x4).readPointer()) //console.log('-------',attatchEbp1C.add(0x8).readPointer()) }) -/*------------------send pic -------------------------- -let buffwxid = null -let imagefilepath = null -let pathPtr = null -let picWxid = null -let picWxidPtr = null -let picAsm = null -let picbuff = null -const sendPicMsgNativeFunction = ((contactId, path) => { - - picAsm = Memory.alloc(Process.pageSize) - buffwxid = Memory.alloc(0x20) - picbuff = Memory.alloc(0x378) - - pathPtr = Memory.alloc(path.length * 2 + 1) - pathPtr.writeUtf16String(path) - - imagefilepath = Memory.alloc(0x24) - imagefilepath.writePointer(pathPtr).add(0x04) - .writeU32(path.length * 2).add(0x04) - .writeU32(path.length * 2).add(0x04) - - picWxidPtr = Memory.alloc(contactId.length * 2 + 1) - picWxidPtr.writeUtf16String(contactId) - - picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(picWxidPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - - Memory.patchCode(picAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: picAsm }) - cw.putPushfx(); - cw.putPushax(); - - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', buffwxid) - - cw.putMovRegReg('ecx', 'esp') - - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset1 - )) - - cw.putMovRegAddress('ebx', imagefilepath) - cw.putPushReg('ebx') - - cw.putMovRegAddress('eax', picWxid) - cw.putPushReg('eax') - - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset2 - )) - - cw.putMovRegReg('ecx', 'eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset3 - )) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) - nativeativeFunction() - -})*/ // 025 const callLoginQrcodeFunction = ((forceRefresh = false) => { diff --git a/src/init-agent-script.js b/src/init-agent-script.js index f706c38..9c97f9a 100644 --- a/src/init-agent-script.js +++ b/src/init-agent-script.js @@ -8,11 +8,14 @@ //const { isNullishCoalesce } = require("typescript") -//3.9.2.23 +// wechat:3.9.2.23 -// 偏移地址集合 done +// 028 done const offset = { + + hook_point: 0xd19a0b, //3.9.2.23 + myselfinfo: { offset: 0x2FFD484, //老版本微信号偏移,后面的地址,都要在这个偏移上增加 //wxid_len:0x10, @@ -23,6 +26,7 @@ const offset = { //wxcode_len:0x74 wxid_len_offset: 0x4D4 }, + contactInfo: { nodeOffset: 0x2FFDD7C, nodeRootOffset: 0x64 @@ -40,25 +44,20 @@ const offset = { call3: 0xce6640 } }; -//3.9.2.23 -// 全局配置 done +// 029 done /*------------------global-------------------------------------------*/ const availableVersion = 1661534743 ////3.9.2.23 ==0x63090217 const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') const moduleLoad = Module.load('WeChatWin.dll') -//1575CF98 - -// 全局变量 tbd +// tbd const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() - /*------------------global-------------------------------------------*/ /*---------------base -------------------------*/ - -// done +// 030 done let retidPtr=null let retidStruct=null const initidStruct = ((str) => { @@ -78,7 +77,7 @@ const initidStruct = ((str) => { return retidStruct }) -// done +// 031 done let retPtr = null let retStruct = null const initStruct = ((str) => { @@ -98,7 +97,7 @@ const initStruct = ((str) => { return retStruct }) -// done +// 032 done let msgstrPtr=null let msgStruct=null const initmsgStruct = ((str) => { @@ -117,7 +116,23 @@ const initmsgStruct = ((str) => { return msgStruct }) -// tbd +// 034 done +/** +* at msg structure +*/ +let atStruct = null +const initAtMsgStruct = ((wxidStruct) => { + + atStruct = Memory.alloc(0x10) + + atStruct.writePointer(wxidStruct).add(0x04) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) + .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) + .writeU32(0) + return atStruct +}) + +// 033 tbd let retidNullStruct = null let retidNullPtr = null const initNullIdStruct = ((str) => { @@ -137,25 +152,7 @@ const initNullIdStruct = ((str) => { return retidNullStruct }) -// done -/** -* at msg structure -*/ -let atStruct = null -const initAtMsgStruct = ((wxidStruct) => { - - atStruct = Memory.alloc(0x10) - - atStruct.writePointer(wxidStruct).add(0x04) - .writeU32(wxidStruct.toInt32() + 0x14).add(0x04)//0x14 = sizeof(wxid structure) - .writeU32(wxidStruct.toInt32() + 0x14).add(0x04) - .writeU32(0) - return atStruct -}) - -// done -// std::string -// const str = readStringPtr(ptr).readUtf8String() +// 035 done const readStringPtr = (address) => { const addr = ptr(address) const size = addr.add(16).readU32() @@ -169,9 +166,15 @@ const readStringPtr = (address) => { addr.ptr._readCString = addr.ptr.readCString addr.ptr._readAnsiString = addr.ptr.readAnsiString addr.ptr._readUtf8String = addr.ptr.readUtf8String - addr.readCString = () => { return addr.size ? addr.ptr._readCString(addr.size) : '' } - addr.readAnsiString = () => { return addr.size ? addr.ptr._readAnsiString(addr.size) : '' } - addr.readUtf8String = () => { return addr.size ? addr.ptr._readUtf8String(addr.size) : '' } + addr.readCString = () => { + return addr.size ? addr.ptr._readCString(addr.size) : '' + } + addr.readAnsiString = () => { + return addr.size ? addr.ptr._readAnsiString(addr.size) : '' + } + addr.readUtf8String = () => { + return addr.size ? addr.ptr._readUtf8String(addr.size) : '' + } // console.log('readStringPtr() address:',address,' -> str ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) // console.log('readStringPtr() str:' , addr.readUtf8String()) @@ -180,913 +183,853 @@ const readStringPtr = (address) => { return addr } -// done +// 036 done const readString = (address) => { return readStringPtr(address).readUtf8String() } -// done +// 037 done const readWideString = (address) => { return readWStringPtr(address).readUtf16String() } +/*-----------------base-------------------------*/ + +// 041 +// std::wstring +// const wstr = readWStringPtr(ptr).readUtf16String() +const readWStringPtr = (address) => { + const addr = ptr(address) + const size = addr.add(4).readU32() + const capacity = addr.add(8).readU32() + addr.ptr = addr.readPointer() + addr.size = size + addr.capacity = capacity + addr.ptr._readUtf16String = addr.ptr.readUtf16String + addr.readUtf16String = () => { + return addr.size ? addr.ptr._readUtf16String(addr.size * 2) : '' + } + + // console.log('readWStringPtr() address:',address,' -> ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) + // console.log('readWStringPtr() str:' , `"${addr.readUtf16String()}"`,'\n',addr.ptr.readByteArray(addr.size*2+2),'\n') + // console.log('readWStringPtr() address:', addr,'dump:', addr.readByteArray(16),'\n') + + return addr +} /*-----------------base-------------------------*/ +// 010 done let currentVersion = 0 -let nodeList = [] //for contact +let nodeList = [] //for contact let contactList = [] //for contact let chatroomNodeList = [] //for chatroom -let chatroomMemberList = []//for chatroom +let chatroomMemberList = [] //for chatroom let loggedIn = false -//开启日志 3.6.0.18 -// //[0x221c330+wechatwin.dll]+0xf8 -// 20220504 +const getWechatVersionFunction = (() => { + if (currentVersion) { + return currentVersion + } + const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' + const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) + if (results.length == 0) { + return 0 + } + const addr = results[0].address + const ret = addr.add(0x07).readPointer() + const ver = ret.add(0x0).readU32() + currentVersion = ver + return ver +}) -let g_initTestAsm = null -let g_BufferEbp2C = null -let g_initECXU32 = null -let g_initECXPtr = null -let g_initEBXPtr = null -let g_initEBX = null +// 011 done +const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { + if (!ver) { + return '0.0.0.0' + } + const vers = [] + vers.push((ver >> 24) & 255 - 0x60) + vers.push((ver >> 16) & 255) + vers.push((ver >> 8) & 255) + vers.push(ver & 255) + return vers.join('.') +}) -let g_attatchEBP210Buffer = null -let g_attatchPathPtr = null -let g_attatchPath = null -let g_attatchEBPAc = null -let g_attatchEBPAcBufPtr = null +// 012 done +const checkSupportedFunction = (() => { + const ver = getWechatVersionFunction() + return ver == availableVersion +}) -let g_attatchContactIdPtr = null -//let g_attatchECXBuffer = null -let g_attatchESIU32 = null +// 019 done +/** + * @Hook: recvMsg -> recvMsgNativeCallback + */ + const recvMsgNativeCallback = (() => { -let g_initECXTempPtr = null -const initGlobal = ((contactId, attatchFile) => { - //const base = moduleBaseAddress.add(0x222f38c).readPointer() - //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 - console.log('------------g_attatchEBPAc', g_attatchEBPAc) - console.log('------------g_EDIU32', g_EDIU32) - g_initTestAsm = Memory.alloc(Process.pageSize) - console.log('------------address', g_initTestAsm) + const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) - g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) - g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() - g_initECXU32 = g_initECXPtr.toInt32() - g_attatchESIU32 = g_EDIU32 + Interceptor.attach( + moduleBaseAddress.add(offset.hook_point), { + onEnter() { + const addr = this.context.ecx //0xc30-0x08 + const msgType = addr.add(0x38).readU32() + const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg - console.log('------------g_initECXU32', g_initECXU32) - console.log('------------g_initESIU32', g_attatchESIU32) + if (msgType > 0) { + const talkerIdPtr = addr.add(0x48).readPointer() + //console.log('txt msg',talkerIdPtr.readUtf16String()) + const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 - //console.log('==========g_initECXPtr',g_initECXPtr) - //console.log('==========g_EDIU32',g_EDIU32) + const myTalkerIdPtr = Memory.alloc(talkerIdLen) + Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) - //g_attatchECXBuffer = Memory.alloc(0x1024) - //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) - g_BufferEbp2C = Memory.alloc(0x48) + let contentPtr = null + let contentLen = 0 + let myContentPtr = null + if (msgType == 3) { // pic path + let thumbPtr = addr.add(0x198).readPointer(); + let hdPtr = addr.add(0x1ac).readPointer(); + let thumbPath = thumbPtr.readUtf16String(); + let hdPath = hdPtr.readUtf16String(); + let picData = [ + thumbPath, // PUPPET.types.Image.Unknown + thumbPath, // PUPPET.types.Image.Thumbnail + hdPath, // PUPPET.types.Image.HD + hdPath // PUPPET.types.Image.Artwork + ] + let content = JSON.stringify(picData); + myContentPtr = Memory.allocUtf16String(content); + } else { + contentPtr = addr.add(0x70).readPointer() + contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 + myContentPtr = Memory.alloc(contentLen) + Memory.copy(myContentPtr, contentPtr, contentLen) + } - //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() - //g_initEBXPtr = Memory.alloc(0x14).writePointer(g_initEBX) - //g_initEBXPtr.add(0x08).writePointer(g_BufferEbp2C) + // console.log('----------------------------------------') + // console.log(msgType) + // console.log(contentPtr.readUtf16String()) + // console.log('----------------------------------------') + const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 + let myGroupMsgSenderIdPtr = null + if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 - g_attatchPathPtr = Memory.alloc(attatchFile.length * 2 + 2) - g_attatchPathPtr.writeUtf16String(attatchFile) + myGroupMsgSenderIdPtr = Memory.alloc(0x10) + myGroupMsgSenderIdPtr.writeUtf16String("null") - g_attatchPath = Memory.alloc(0x28) - g_attatchPath.writePointer(g_attatchPathPtr).add(0x04) - .writeU32(attatchFile.length * 2).add(0x04) - .writeU32(attatchFile.length * 2).add(0x04) - /*---------------------------------ebp-210----------------*/ - g_attatchEBP210Buffer = Memory.alloc(0x48) - g_attatchEBP210Buffer.writeU32(0x3) - g_attatchEBP210Buffer.add(0x4).writePointer(g_attatchPathPtr) - g_attatchEBP210Buffer.add(0x8).writeU32(attatchFile.length * 2) - g_attatchEBP210Buffer.add(0xC).writeU32(attatchFile.length * 2) - g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) - //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) - /*---------------------------------ebp-210----------------*/ + } else { - //g_attatchContactIdPtr = Memory.alloc(0x4) - //g_attatchContactIdPtr.writeUtf16String(contactId) - //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) - g_attatchEBPAc = Memory.alloc(0x140) - //g_attatchEBPAcBufPtr = Memory.alloc(0x100) - //g_attatchEBPAc.writePointer(g_attatchEBP210Buffer) - //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) - //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) - g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) - console.log('------------g_attatchEBPAc', g_attatchEBPAc) + const groupMsgSenderIdPtr = addr.add(0x170).readPointer() + const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 + myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) + Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) - /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) - g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) - .add(0x04).writeU32(contactId.length*2)*/ + } + const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 + let myXmlContentPtr = null + if (xmlNullPtr == 0) { + myXmlContentPtr = Memory.alloc(0x10) + myXmlContentPtr.writeUtf16String("null") - //g_attatchESIU32 = g_EDI.toInt32() + } else { + const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 + myXmlContentPtr = Memory.alloc(xmlContentLen) + Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) + } - //console.log('------------g_attatchESIU32',g_attatchESIU32) - //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) + setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) + } + } + }) + return nativeCallback +})() - Memory.patchCode(g_initTestAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: g_initTestAsm }) - cw.putPushfx() - cw.putPushax() - //cw.putMovRegAddress('eax', g_attatchEBP210Buffer) +// 003 done +const getBaseNodeAddress = (() => { + return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() +}) - /*cw.putMovRegAddress('edi',g_EDIPtr) - cw.putMovRegReg('esi','edi') - cw.putMovRegAddress('eax',g_BufferEbp2C) - cw.putMovRegAddress('ecx',g_initECXTempPtr) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ +// 004 done +const getHeaderNodeAddress = (() => { + const baseAddress = getBaseNodeAddress() + //console.log('baseAddress',baseAddress) + if (baseAddress.isNull()) { + return baseAddress + } - //cw.putMovRegOffsetPtrU32('ebp', -20, 0) - - /*cw.putPushU32(0) - cw.putMovRegAddress('eax', g_attatchPathPtr) - cw.putPushReg('eax') - cw.putPushU32(3) - cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) - cw.putCallAddress(moduleBaseAddress.add(0x130220))*/ + //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) + return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() +}) +// 006 done +const getMyselfInfoFunction = (() => { - /*cw.putMovRegAddress('edi', g_attatchEBPAc)//后面要用的的ebp-2c - cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) - cw.putPushReg('ecx') - cw.putMovRegU32('eax',0) - cw.putPushReg('eax')//push eax - cw.putMovRegReg('ecx', 'edi') - cw.putMovRegAddress('esi',g_attatchPathPtr) - cw.putCallAddress(moduleBaseAddress.add(0x138880))*/ + let ptr = 0 + let wx_code = '' + let wx_id = '' + let wx_name = '' + let head_img_url = '' + const base = moduleBaseAddress.add(offset.myselfinfo.offset) + const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() - /*cw.putSubRegImm('esp',0x14) - cw.putMovRegU32('ecx',g_initECXU32) - cw.putMovRegU32('esi',g_attatchESIU32) - cw.putMovRegAddress('eax', g_attatchEBPAc) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x173620))*/ - //cw.putCallAddress(moduleBaseAddress.add(0x522590)) + if (wxid_len === 0x13) { // 新版本微信 + wx_id = base.readPointer().readAnsiString(wxid_len) + wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() + } else { + wx_id = readString(base) + wx_code = wx_id + } - /** g_attatchEBPAc*/ - //cw.putMovRegNearPtr('eax', g_attatchEBPAc) - //cw.putMovNearPtrReg(g_attatchEBPAc.add(0xc), 'eax') - /** g_attatchEBPAc*/ + wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) + const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() + const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() - //cw.putMovRegAddress('ebx', g_initEBXPtr) - //cw.putMovRegU32('edi', g_EDI.toInt32()) - //cw.putMovRegU32('esi', g_EDI.toInt32()) - /*cw.putMovRegU32('ecx', g_initECX) - cw.putMovRegAddress('eax', g_BufferEbp2C) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x131BB0))*/ + head_img_url = img_addr.readAnsiString(img_len) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() + const myself = { + id: wx_id, + code: wx_code, + name: wx_name, + head_img_url: head_img_url, + }; - }) + return JSON.stringify(myself) - const nativeativeFunction = new NativeFunction(ptr(g_initTestAsm), 'void', []) - nativeativeFunction() }) +// 043 done +const recurseNew = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { + return + } -let g_personal_detail_ebx = null -let g_personal_detail_asm = null -let g_personal_wxid = null -let g_personal_wxid_ptr = null -const getOldTest = ((wxid) => {//personal detail + if (node.equals(headerNodeAddress)) { + return + } - g_personal_detail_asm = Memory.alloc(Process.pageSize) - g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } - g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) - g_personal_wxid_ptr.writeUtf16String(wxid) - g_personal_wxid = Memory.alloc(0x14) - g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) - .writeU32(wxid.length * 2).add(0x04) - .writeU32(wxid.length * 2).add(0x08) + nodeList.push(node) + const id = readString(node.add(0x8)) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x30)) + //console.log('-----------',wxid) - console.log('-----------address----------', g_personal_detail_asm) - Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: g_personal_detail_asm }) - cw.putPushfx() - cw.putPushax() + //custom id, if not set return null, and use wxid which should be custom id + //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) - cw.putCallAddress(moduleBaseAddress.add(0x9A000)) + //custom Nickname + const name = readWideString(node.add(0x8c)) - //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | - cw.putMovRegU32('ebx', g_personal_detail_ebx) - cw.putMovRegReg('esi', 'eax') - cw.putPushReg('ebx') - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', g_personal_wxid) - cw.putMovRegReg('ecx', 'esp') - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - cw.putMovRegReg('ecx', 'esi') - cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) + //alias aka 'remark' in wechat + //const alias = readWideString(node.add(0x80)) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() + //avatarUrl + //const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + //const gender = node.add(0x18C).readU32() - }) + const contactJson = { + id1: id, + id: wxid, + name: name, + /*code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender,*/ + } - const nativeativeFunction = new NativeFunction(ptr(g_personal_detail_asm), 'void', []) - nativeativeFunction() + contactList.push(contactJson) + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + //const rightNode = node.add(0x08).readPointer() -}) + recurseNew(leftNode) + recurseNew(centerNode) + //recurse(rightNode) -// const writeLogNativeCallback = (() => { -// const nativeCallback = new NativeCallback(() => { }, 'void', []) -// const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) + const allContactJson = contactList + return allContactJson -// Interceptor.attach( -// moduleBaseAddress.add(0x7008A4), -// { -// onEnter() { -// const addr = this.context.eax//.sub(0x114)//0xc30-0x08 -// if(addr >0){ -// const log = ptr(addr).readAnsiString() -// } -// } -// }) -// return nativeCallback -// })() +}) -/** - * test call - */ -let attatchTestAsm = null -let attatchTestEbp2C = null -let attatchGlobalEDI = null -let attatchGlobalEDIB88 = null -let attatchTestEBX = null -let g_tempEcx = null -let attatchFirstECX = null -//let attatchFirstECX = null -let gattatchFilePtr = null -let gattatchFile = null -let gattatchReceiveIdPtr = null -let gattatchReceiveId = null -let attatchEAX3B0Buf = null +// 044 done +const getContactNativeFunction = (() => { + const headerNodeAddress = getHeaderNodeAddress() + //console.log('headerNodeAddress',headerNodeAddress) -let attatchESIbuf = null -const getWxTest = ((contactId, filePath) => { - //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) - //nativeativeFunction() - attatchTestAsm = Memory.alloc(Process.pageSize) - console.log('----------------address', attatchTestAsm) - attatchTestEbp2C = Memory.alloc(0xC) - attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() - .add(0x938).add(0x438).readPointer() - attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() - attatchTestEBX = Memory.alloc(0x4) - attatchTestEBX.writePointer(attatchGlobalEDI) - console.log('----------------attatchGlobalEDI', attatchGlobalEDI) - console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) + if (headerNodeAddress.isNull()) { + return '[]' + } - attatchFirstECX = Memory.alloc(0x28) - //const attatchSecondEcx = Memory.alloc(0x14) + const node = headerNodeAddress.add(0x0).readPointer() + const ret = recurseNew(node) - const contactIdLength = contactId.length * 2 + 2//edx - const contractIdActLength = contactId.length + //console.log(ret) + console.log('getContactNativeFunction:', ret.length) + /*for (let item of ret){ + console.log(JSON.stringify(item)) + }*/ + //console.log(ret.contact) + const cloneRet = JSON.stringify(ret) + nodeList.length = 0 + contactList.length = 0 - gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) - gattatchReceiveIdPtr.writeUtf16String(contactId) + return cloneRet +}) - gattatchReceiveId = Memory.alloc(0x14) - gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x08) - //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) - //return - /*console.log(hexdump(attatchTestEBX, { - offset: 0, - length: 0x40, - header: true, - ansi: true - })) - return*/ +// 0xx done +const getChatroomNodeAddress = (() => { + const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() + if (baseAddress.isNull()) { + return baseAddress + } + return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() +}) - gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) - gattatchFilePtr.writeUtf16String(filePath) - - gattatchFile = Memory.alloc(0x14) - gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) - .writeU32(filePath.length * 2).add(0x04) - .writeU32(filePath.length * 2).add(0x08) - - const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() - - attatchEAX3B0Buf = Memory.alloc(0x3B0) - - g_tempEcx = Memory.alloc(0x4) - //g_tempEcx1 = Memory.alloc(0x4) - - attatchESIbuf = Memory.alloc(0x100) - attatchESIbuf.add(0x0).writeU32(3) - attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) - attatchESIbuf.add(0x8).writeU32(filePath.length * 2) - attatchESIbuf.add(0xc).writeU32(filePath.length * 2) - - Memory.patchCode(attatchTestAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: attatchTestAsm }) - cw.putPushfx() - cw.putPushax() - //cw.putMovRegU32('edi',attatchGlobalEDI) - - - cw.putMovRegAddress('esi', attatchESIbuf) - - cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) - cw.putMovRegAddress('eax', gattatchFile) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - - - cw.putMovRegAddress('ecx', attatchFirstECX) - cw.putMovRegU32('eax', contactIdLength) - cw.putPushReg('eax') - cw.putPushU32(0) - cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) - cw.putAddRegImm('esp', 0x8) - - cw.putMovRegReg('edx', 'eax') - cw.putMovNearPtrReg(attatchFirstECX, 'edx') - cw.putMovRegU32('edi', contactIdLength) - cw.putPushReg('edi') - cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 - cw.putPushReg('eax') - cw.putMovRegNearPtr('eax', attatchFirstECX) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) - cw.putAddRegImm('esp', 0x0c) - - //cw.putMovRegNearPtr('ecx',attatchFirstECX) - //cw.putAddRegImm('esp', 0x0c) - //cw.putMovRegU32('edx', 0) - //cw.putMovRegRegPtr('eax', 'ecx') - //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') - cw.putMovRegU32('edi', contactId.length * 2) - cw.putMovRegU32('ecx', attatchLastECX) - cw.putMovRegAddress('eax', attatchEAX3B0Buf) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x392260)) - - cw.putMovRegAddress('ecx', attatchEAX3B0Buf) - cw.putCallAddress(moduleBaseAddress.add(0x94200)) - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(attatchTestAsm), 'void', []) - nativeativeFunction() +// 008chatroom member done +const chatroomRecurse = ((node) => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return + } + if (node.equals(chatroomNodeAddress)) { + return + } -}) -// 001 -const getTestInfoFunction = ((addr) => { - const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) - nativeativeFunction() + for (const item in chatroomNodeList) { + if (node.equals(chatroomNodeList[item])) { + return + } + } - //00CFE484 + chatroomNodeList.push(node) + const roomid = readWideString(node.add(0x10)) + const len = node.add(0x54).readU32() // + //const memberJson={} + if (len > 4) { // + const memberStr = readString(node.add(0x44)) + if (memberStr.length > 0) { + const memberList = memberStr.split(/[\\^][G]/) + const memberJson = { + roomid: roomid, + roomMember: memberList + } - /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { - onAccess(details){ - console.log('============') - console.log(details.operation) - console.log(details.from) - console.log(details.address) - console.log('============') + chatroomMemberList.push(memberJson) } - })*/ -}) + } -// 002get global data + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() -const isLoggedInFunction = (() => { - loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() - return !!loggedIn -}) + chatroomRecurse(leftNode) + chatroomRecurse(centerNode) + chatroomRecurse(rightNode) -// 007 缺失,请标注已废弃或者其他原因 -const getMyselfIdFunction = (() => { + const allChatroomMemberJson = chatroomMemberList + return allChatroomMemberJson +}) - let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) +// 009 done +const getChatroomMemberInfoFunction = (() => { + const chatroomNodeAddress = getChatroomNodeAddress() + if (chatroomNodeAddress.isNull()) { + return '[]' + } - return wx_id + const node = chatroomNodeAddress.add(0x0).readPointer() + const ret = chatroomRecurse(node) + const cloneRet = JSON.stringify(ret) + chatroomNodeList.length = 0 //empty + chatroomMemberList.length = 0 //empty + return cloneRet }) -// std::wstring -// const wstr = readWStringPtr(ptr).readUtf16String() -const readWStringPtr = (address) => { - const addr = ptr(address) - const size = addr.add(4).readU32() - const capacity = addr.add(8).readU32() - addr.ptr = addr.readPointer() - addr.size = size - addr.capacity = capacity - addr.ptr._readUtf16String = addr.ptr.readUtf16String - addr.readUtf16String = () => { return addr.size ? addr.ptr._readUtf16String(addr.size * 2) : '' } - - // console.log('readWStringPtr() address:',address,' -> ptr:', addr.ptr, 'size:', addr.size, 'capacity:', addr.capacity) - // console.log('readWStringPtr() str:' , `"${addr.readUtf16String()}"`,'\n',addr.ptr.readByteArray(addr.size*2+2),'\n') - // console.log('readWStringPtr() address:', addr,'dump:', addr.readByteArray(16),'\n') +// 024 done +/** + * sendMsgNativeFunction + * send text message + * @param {string} talkerId = wxid or roomid + * @param {string} content + */ + const sendMsgNativeFunction = ((talkerId, content) => { - return addr -} + const txtAsm = Memory.alloc(Process.pageSize) + //const buffwxid = Memory.alloc(0x20) -//contact -const recurse = ((node) => { - const headerNodeAddress = getHeaderNodeAddress() - if (headerNodeAddress.isNull()) { return } - if (node.equals(headerNodeAddress)) { return } + let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) + wxidPtr.writeUtf16String(talkerId) - for (const item in nodeList) { - if (node.equals(nodeList[item])) { - return - } - } + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(wxidPtr)).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + .writeU32(talkerId.length * 2).add(0x04) + let contentPtr = Memory.alloc(content.length * 2 + 2) + contentPtr.writeUtf16String(content) - nodeList.push(node) - //wxid, format relates to registration method - const wxid = readWideString(node.add(0x38)) + const sizeOfStringStruct = Process.pointerSize * 5 + let contentStruct = Memory.alloc(sizeOfStringStruct) - //custom id, if not set return null, and use wxid which should be custom id - const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) + contentStruct + .writePointer(contentPtr).add(0x4) + .writeU32(content.length).add(0x4) + .writeU32(content.length * 2) - //custom Nickname - const name = readWideString(node.add(0x94)) - //alias aka 'remark' in wechat - const alias = readWideString(node.add(0x80)) + const ecxBuffer = Memory.alloc(0x2d8) - //avatarUrl - const avatar = readWideString(node.add(0x138)) - //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) - //contact gender - const gender = node.add(0x18C).readU32() + Memory.patchCode(txtAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: txtAsm + }) + cw.putPushfx() + cw.putPushax() - const contactJson = { - id: wxid, - code: wx_code, - name: name, - alias: alias, - avatarUrl: avatar, - gender: gender, - } + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + cw.putPushU32(0x0) - contactList.push(contactJson) + //cw.putMovRegReg - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() + cw.putMovRegAddress('eax', contentStruct) + cw.putPushReg('eax') - recurse(leftNode) - recurse(centerNode) - recurse(rightNode) + cw.putMovRegAddress('edx', picWxid) //room_id - const allContactJson = contactList - return allContactJson + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) -}) + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() -// 010 done -const getWechatVersionFunction = (() => { - if (currentVersion) { - return currentVersion - } - const pattern = '55 8B ?? 83 ?? ?? A1 ?? ?? ?? ?? 83 ?? ?? 85 ?? 7F ?? 8D ?? ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 8B ?? ?? ?? 85 ?? 75 ?? E8 ?? ?? ?? ?? 0F ?? ?? 0D ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 8B ?? 5D C3' - const results = Memory.scanSync(moduleLoad.base, moduleLoad.size, pattern) - if (results.length == 0) { - return 0 - } - const addr = results[0].address - const ret = addr.add(0x07).readPointer() - const ver = ret.add(0x0).readU32() - currentVersion = ver - return ver -}) + }) -// 011 done -const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { - if (!ver) { - return '0.0.0.0' - } - const vers = [] - vers.push((ver >> 24) & 255 - 0x60) - vers.push((ver >> 16) & 255) - vers.push((ver >> 8) & 255) - vers.push(ver & 255) - return vers.join('.') -}) + console.log('----------txtAsm', txtAsm) + const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + nativeativeFunction() -// 012 done -const checkSupportedFunction = (() => { - const ver = getWechatVersionFunction() - return ver == availableVersion }) +// 023 done /** - * @Hook: recvMsg -> recvMsgNativeCallback - */ +* send at msg +*/ +let asmAtMsg = null +let roomid_, msg_, wxid_, atid_ +let ecxBuffer +const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { -// 019 done -const recvMsgNativeCallback = (() => { + asmAtMsg = Memory.alloc(Process.pageSize) + ecxBuffer = Memory.alloc(0x3b0) + const atContent = '@'+nickname+' '+text - const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) + roomid_ = initStruct(roomId) + wxid_ = initidStruct(contactId) + msg_ = initmsgStruct(atContent) + atid_ = initAtMsgStruct(wxid_) - Interceptor.attach( - moduleBaseAddress.add(offset.hook_point), { - onEnter() { - const addr = this.context.ecx //0xc30-0x08 - const msgType = addr.add(0x38).readU32() - const isMyMsg = addr.add(0x3C).readU32() //add isMyMsg + Memory.patchCode(asmAtMsg, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: asmAtMsg + }) + cw.putPushfx() + cw.putPushax() - if (msgType > 0) { + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x0) + cw.putPushU32(0x1) + //cw.putPushU32(0x0) + cw.putMovRegAddress('eax', atid_) + cw.putPushReg('eax') - const talkerIdPtr = addr.add(0x48).readPointer() - //console.log('txt msg',talkerIdPtr.readUtf16String()) - const talkerIdLen = addr.add(0x48 + 0x04).readU32() * 2 + 2 + //cw.putMovRegReg - const myTalkerIdPtr = Memory.alloc(talkerIdLen) - Memory.copy(myTalkerIdPtr, talkerIdPtr, talkerIdLen) + cw.putMovRegAddress('eax', msg_) + cw.putPushReg('eax') + cw.putMovRegAddress('edx', roomid_) //room_id - let contentPtr = null - let contentLen = 0 - let myContentPtr = null - if (msgType == 3) { // pic path - let thumbPtr = addr.add(0x198).readPointer(); - let hdPtr = addr.add(0x1ac).readPointer(); - let thumbPath = thumbPtr.readUtf16String(); - let hdPath = hdPtr.readUtf16String(); - let picData = [ - thumbPath, // PUPPET.types.Image.Unknown - thumbPath, // PUPPET.types.Image.Thumbnail - hdPath, // PUPPET.types.Image.HD - hdPath // PUPPET.types.Image.Artwork - ] - let content = JSON.stringify(picData); - myContentPtr = Memory.allocUtf16String(content); - } else { - contentPtr = addr.add(0x70).readPointer() - contentLen = addr.add(0x70 + 0x04).readU32() * 2 + 2 - myContentPtr = Memory.alloc(contentLen) - Memory.copy(myContentPtr, contentPtr, contentLen) - } + cw.putMovRegAddress('ecx', ecxBuffer) + cw.putCallAddress(moduleBaseAddress.add( + offset.sendTxtMsg.callOffset + )) - // console.log('----------------------------------------') - // console.log(msgType) - // console.log(contentPtr.readUtf16String()) - // console.log('----------------------------------------') - const groupMsgAddr = addr.add(0x170).readU32() //* 2 + 2 - let myGroupMsgSenderIdPtr = null - if (groupMsgAddr == 0) { //weChatPublic is zero,type is 49 + cw.putAddRegImm('esp', 0x18) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - myGroupMsgSenderIdPtr = Memory.alloc(0x10) - myGroupMsgSenderIdPtr.writeUtf16String("null") + }) - } else { + //console.log('----------txtAsm', asmAtMsg) + const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + nativeativeFunction() - const groupMsgSenderIdPtr = addr.add(0x170).readPointer() - const groupMsgSenderIdLen = addr.add(0x170 + 0x04).readU32() * 2 + 2 - myGroupMsgSenderIdPtr = Memory.alloc(groupMsgSenderIdLen) - Memory.copy(myGroupMsgSenderIdPtr, groupMsgSenderIdPtr, groupMsgSenderIdLen) +}) - } +// 022 done +/** + * + * @param {*} contactId + * @param {*} path + */ + const sendPicMsgNativeFunction = ((contactId, path) => { - const xmlNullPtr = addr.add(0x1f0).readU32() //3.9.2.23 - let myXmlContentPtr = null - if (xmlNullPtr == 0) { + const picAsm = Memory.alloc(Process.pageSize) + const buffwxid = Memory.alloc(0x20) + const picbuff = Memory.alloc(0x2D8) - myXmlContentPtr = Memory.alloc(0x10) - myXmlContentPtr.writeUtf16String("null") + let pathPtr = Memory.alloc(path.length * 2 + 1) + pathPtr.writeUtf16String(path) - } else { - const xmlContentPtr = addr.add(0x1f0).readPointer() //3.9.2.23 + let imagefilepath = Memory.alloc(0x24) + imagefilepath.writePointer(pathPtr).add(0x04) + .writeU32(path.length * 2).add(0x04) + .writeU32(path.length * 2).add(0x04) - const xmlContentLen = addr.add(0x1f0 + 0x04).readU32() * 2 + 2 - myXmlContentPtr = Memory.alloc(xmlContentLen) - Memory.copy(myXmlContentPtr, xmlContentPtr, xmlContentLen) - } + let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) + picWxidPtr.writeUtf16String(contactId) - setImmediate(() => nativeativeFunction(msgType, myTalkerIdPtr, myContentPtr, myGroupMsgSenderIdPtr, myXmlContentPtr, isMyMsg)) - } - } - }) - return nativeCallback -})() + let picWxid = Memory.alloc(0x0c) + picWxid.writePointer(ptr(picWxidPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x04) -// 003get myself info done -const getBaseNodeAddress = (() => { - return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() -}) -// 004 done -const getHeaderNodeAddress = (() => { - const baseAddress = getBaseNodeAddress() - //console.log('baseAddress',baseAddress) - if (baseAddress.isNull()) { - return baseAddress - } + //const test_offset1 = 0x701DC0; + Memory.patchCode(picAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: picAsm + }) + cw.putPushfx(); + cw.putPushax(); + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call1 + )) + cw.putMovRegReg('edx', 'eax') //缓存 - //console.log('HeaderNodeAddress',baseAddress.add(offset.handle_offset).readPointer()) - return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() -}) + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', buffwxid) + cw.putMovRegReg('ecx', 'esp') + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call2 + )) -// 006 done -const getMyselfInfoFunction = (() => { + cw.putMovRegReg('ecx', 'edx') + cw.putMovRegAddress('eax', picWxid) //=lea + cw.putMovRegAddress('edi', imagefilepath) + cw.putPushReg('edi') + cw.putPushReg('eax') + cw.putMovRegAddress('eax', picbuff) + cw.putPushReg('eax') - let ptr = 0 - let wx_code = '' - let wx_id = '' - let wx_name = '' - let head_img_url = '' + cw.putMovRegAddress('edi', picWxid) //edi + cw.putCallAddress(moduleBaseAddress.add( + offset.sendPicMsg.call3 + )) - const base = moduleBaseAddress.add(offset.myselfinfo.offset) - const wxid_len = base.add(offset.myselfinfo.wxid_len_offset).readU32() - if (wxid_len === 0x13) { // 新版本微信 - wx_id = base.readPointer().readAnsiString(wxid_len) - wx_code = base.add(offset.myselfinfo.wxcode_new).readAnsiString() - } else { - wx_id = readString(base) - wx_code = wx_id - } + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - wx_name = readString(base.add(offset.myselfinfo.wx_nick_name)) - const img_addr = base.add(offset.myselfinfo.head_img_url).readPointer() - const img_len = base.add(offset.myselfinfo.head_img_url_len).readU32() + }) - head_img_url = img_addr.readAnsiString(img_len) + //console.log('----------picAsm',picAsm) + const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) + nativeativeFunction() - const myself = { - id: wx_id, - code: wx_code, - name: wx_name, - head_img_url: head_img_url, - }; +}) - return JSON.stringify(myself) +// 020 done +let memberNickBuffAsm = null +let nickRoomId = null +let nickMemberId = null +let nickBuff = null +const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { -}) + nickBuff = Memory.alloc(0x7e4) + //const nickRetAddr = Memory.alloc(0x04) + memberNickBuffAsm = Memory.alloc(Process.pageSize) + //console.log('asm address----------',memberNickBuffAsm) + nickRoomId = initidStruct(roomId) + //console.log('nick room id',nickRoomId) + nickMemberId = initStruct(memberId) -// done -const recurseNew = ((node) => { - const headerNodeAddress = getHeaderNodeAddress() - if (headerNodeAddress.isNull()) { - return - } + //console.log('nick nickMemberId id',nickMemberId) + //const nickStructPtr = initmsgStruct('') - if (node.equals(headerNodeAddress)) { - return - } + Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { + pc: memberNickBuffAsm + }) + cw.putPushfx() + cw.putPushax() + cw.putMovRegAddress('edi', nickRoomId) + cw.putMovRegAddress('eax', nickBuff) + cw.putMovRegReg('edx', 'edi') + cw.putPushReg('eax') + cw.putMovRegAddress('ecx', nickMemberId) + cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) + cw.putAddRegImm('esp', 0x04) + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - for (const item in nodeList) { - if (node.equals(nodeList[item])) { - return - } - } + }) + const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) + nativeativeFunction() - nodeList.push(node) - const id = readString(node.add(0x8)) - //wxid, format relates to registration method - const wxid = readWideString(node.add(0x30)) - //console.log('-----------',wxid) + const nickname = readWideString(nickBuff) + console.log('----nickname', nickname) + return readWideString(nickBuff) +}) +/* 由此之后是3.9.2.23中缺失的未实现方法 */ - //custom id, if not set return null, and use wxid which should be custom id - //const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) +// 038 初始化全局变量 tbd +let g_initTestAsm = null +let g_BufferEbp2C = null +let g_initECXU32 = null +let g_initECXPtr = null +let g_initEBXPtr = null +let g_initEBX = null - //custom Nickname - const name = readWideString(node.add(0x8c)) +let g_attatchEBP210Buffer = null +let g_attatchPathPtr = null +let g_attatchPath = null +let g_attatchEBPAc = null +let g_attatchEBPAcBufPtr = null - //alias aka 'remark' in wechat - //const alias = readWideString(node.add(0x80)) +let g_attatchContactIdPtr = null +//let g_attatchECXBuffer = null +let g_attatchESIU32 = null - //avatarUrl - //const avatar = readWideString(node.add(0x138)) - //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) - //contact gender - //const gender = node.add(0x18C).readU32() +let g_initECXTempPtr = null +const initGlobal = ((contactId, attatchFile) => { - const contactJson = { - id1: id, - id: wxid, - name: name, - /*code: wx_code, - name: name, - alias: alias, - avatarUrl: avatar, - gender: gender,*/ - } + //const base = moduleBaseAddress.add(0x222f38c).readPointer() + //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 + console.log('------------g_attatchEBPAc', g_attatchEBPAc) + console.log('------------g_EDIU32', g_EDIU32) + g_initTestAsm = Memory.alloc(Process.pageSize) + console.log('------------address', g_initTestAsm) - contactList.push(contactJson) + g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) + g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() + g_initECXU32 = g_initECXPtr.toInt32() + g_attatchESIU32 = g_EDIU32 - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - //const rightNode = node.add(0x08).readPointer() + console.log('------------g_initECXU32', g_initECXU32) + console.log('------------g_initESIU32', g_attatchESIU32) - recurseNew(leftNode) - recurseNew(centerNode) - //recurse(rightNode) - const allContactJson = contactList - return allContactJson + //console.log('==========g_initECXPtr',g_initECXPtr) + //console.log('==========g_EDIU32',g_EDIU32) -}) + //g_attatchECXBuffer = Memory.alloc(0x1024) + //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) -// done ? -const getContactNativeFunction = (() => { - const headerNodeAddress = getHeaderNodeAddress() - //console.log('headerNodeAddress',headerNodeAddress) + g_BufferEbp2C = Memory.alloc(0x48) - if (headerNodeAddress.isNull()) { - return '[]' - } + //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() + //g_initEBXPtr = Memory.alloc(0x14).writePointer(g_initEBX) + //g_initEBXPtr.add(0x08).writePointer(g_BufferEbp2C) - const node = headerNodeAddress.add(0x0).readPointer() - const ret = recurseNew(node) + g_attatchPathPtr = Memory.alloc(attatchFile.length * 2 + 2) + g_attatchPathPtr.writeUtf16String(attatchFile) - //console.log(ret) + g_attatchPath = Memory.alloc(0x28) + g_attatchPath.writePointer(g_attatchPathPtr).add(0x04) + .writeU32(attatchFile.length * 2).add(0x04) + .writeU32(attatchFile.length * 2).add(0x04) + /*---------------------------------ebp-210----------------*/ + g_attatchEBP210Buffer = Memory.alloc(0x48) + g_attatchEBP210Buffer.writeU32(0x3) + g_attatchEBP210Buffer.add(0x4).writePointer(g_attatchPathPtr) + g_attatchEBP210Buffer.add(0x8).writeU32(attatchFile.length * 2) + g_attatchEBP210Buffer.add(0xC).writeU32(attatchFile.length * 2) + g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) + //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) + /*---------------------------------ebp-210----------------*/ - console.log('getContactNativeFunction:', ret.length) - /*for (let item of ret){ - console.log(JSON.stringify(item)) - }*/ - //console.log(ret.contact) - const cloneRet = JSON.stringify(ret) - nodeList.length = 0 - contactList.length = 0 + //g_attatchContactIdPtr = Memory.alloc(0x4) + //g_attatchContactIdPtr.writeUtf16String(contactId) + //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) + g_attatchEBPAc = Memory.alloc(0x140) + //g_attatchEBPAcBufPtr = Memory.alloc(0x100) + //g_attatchEBPAc.writePointer(g_attatchEBP210Buffer) + //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) + //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) + g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) + console.log('------------g_attatchEBPAc', g_attatchEBPAc) - return cloneRet -}) + /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) + g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) + .add(0x04).writeU32(contactId.length*2)*/ -// 005 done -const getChatroomNodeAddress = (() => { - const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() - if (baseAddress.isNull()) { - return baseAddress - } - return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() -}) -// 008chatroom member done -const chatroomRecurse = ((node) => { - const chatroomNodeAddress = getChatroomNodeAddress() - if (chatroomNodeAddress.isNull()) { - return - } - if (node.equals(chatroomNodeAddress)) { - return - } + //g_attatchESIU32 = g_EDI.toInt32() - for (const item in chatroomNodeList) { - if (node.equals(chatroomNodeList[item])) { - return - } - } - chatroomNodeList.push(node) - const roomid = readWideString(node.add(0x10)) + //console.log('------------g_attatchESIU32',g_attatchESIU32) + //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) - const len = node.add(0x54).readU32() // - //const memberJson={} - if (len > 4) { // - const memberStr = readString(node.add(0x44)) - if (memberStr.length > 0) { - const memberList = memberStr.split(/[\\^][G]/) - const memberJson = { - roomid: roomid, - roomMember: memberList - } + Memory.patchCode(g_initTestAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: g_initTestAsm }) + cw.putPushfx() + cw.putPushax() + //cw.putMovRegAddress('eax', g_attatchEBP210Buffer) - chatroomMemberList.push(memberJson) - } + /*cw.putMovRegAddress('edi',g_EDIPtr) + cw.putMovRegReg('esi','edi') + cw.putMovRegAddress('eax',g_BufferEbp2C) + cw.putMovRegAddress('ecx',g_initECXTempPtr) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ - } + //cw.putMovRegOffsetPtrU32('ebp', -20, 0) - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() + /*cw.putPushU32(0) + cw.putMovRegAddress('eax', g_attatchPathPtr) + cw.putPushReg('eax') + cw.putPushU32(3) + cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) + cw.putCallAddress(moduleBaseAddress.add(0x130220))*/ - chatroomRecurse(leftNode) - chatroomRecurse(centerNode) - chatroomRecurse(rightNode) - const allChatroomMemberJson = chatroomMemberList - return allChatroomMemberJson -}) + /*cw.putMovRegAddress('edi', g_attatchEBPAc)//后面要用的的ebp-2c + cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) + cw.putPushReg('ecx') + cw.putMovRegU32('eax',0) + cw.putPushReg('eax')//push eax + cw.putMovRegReg('ecx', 'edi') + cw.putMovRegAddress('esi',g_attatchPathPtr) + cw.putCallAddress(moduleBaseAddress.add(0x138880))*/ -// 009 done -const getChatroomMemberInfoFunction = (() => { - const chatroomNodeAddress = getChatroomNodeAddress() - if (chatroomNodeAddress.isNull()) { - return '[]' - } - const node = chatroomNodeAddress.add(0x0).readPointer() - const ret = chatroomRecurse(node) + /*cw.putSubRegImm('esp',0x14) + cw.putMovRegU32('ecx',g_initECXU32) + cw.putMovRegU32('esi',g_attatchESIU32) + cw.putMovRegAddress('eax', g_attatchEBPAc) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x173620))*/ + //cw.putCallAddress(moduleBaseAddress.add(0x522590)) - const cloneRet = JSON.stringify(ret) - chatroomNodeList.length = 0 //empty - chatroomMemberList.length = 0 //empty - return cloneRet -}) + /** g_attatchEBPAc*/ + //cw.putMovRegNearPtr('eax', g_attatchEBPAc) + //cw.putMovNearPtrReg(g_attatchEBPAc.add(0xc), 'eax') + /** g_attatchEBPAc*/ -// 024 done -/** - * sendMsgNativeFunction - * send text message - * @param {string} talkerId = wxid or roomid - * @param {string} content - */ - const sendMsgNativeFunction = ((talkerId, content) => { - const txtAsm = Memory.alloc(Process.pageSize) - //const buffwxid = Memory.alloc(0x20) + //cw.putMovRegAddress('ebx', g_initEBXPtr) + //cw.putMovRegU32('edi', g_EDI.toInt32()) + //cw.putMovRegU32('esi', g_EDI.toInt32()) + /*cw.putMovRegU32('ecx', g_initECX) + cw.putMovRegAddress('eax', g_BufferEbp2C) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x131BB0))*/ + cw.putPopax() + cw.putPopfx() + cw.putRet() + cw.flush() - let wxidPtr = Memory.alloc(talkerId.length * 2 + 2) - wxidPtr.writeUtf16String(talkerId) + }) - let picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(wxidPtr)).add(0x04) - .writeU32(talkerId.length * 2).add(0x04) - .writeU32(talkerId.length * 2).add(0x04) + const nativeativeFunction = new NativeFunction(ptr(g_initTestAsm), 'void', []) + nativeativeFunction() +}) - let contentPtr = Memory.alloc(content.length * 2 + 2) - contentPtr.writeUtf16String(content) +// 039 +let g_personal_detail_ebx = null +let g_personal_detail_asm = null +let g_personal_wxid = null +let g_personal_wxid_ptr = null +const getOldTest = ((wxid) => {//personal detail - const sizeOfStringStruct = Process.pointerSize * 5 - let contentStruct = Memory.alloc(sizeOfStringStruct) + g_personal_detail_asm = Memory.alloc(Process.pageSize) + g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() - contentStruct - .writePointer(contentPtr).add(0x4) - .writeU32(content.length).add(0x4) - .writeU32(content.length * 2) + g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) + g_personal_wxid_ptr.writeUtf16String(wxid) + g_personal_wxid = Memory.alloc(0x14) + g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) + .writeU32(wxid.length * 2).add(0x04) + .writeU32(wxid.length * 2).add(0x08) - const ecxBuffer = Memory.alloc(0x2d8) + console.log('-----------address----------', g_personal_detail_asm) - Memory.patchCode(txtAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: txtAsm - }) + Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: g_personal_detail_asm }) cw.putPushfx() cw.putPushax() - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x1) - cw.putPushU32(0x0) - - //cw.putMovRegReg + cw.putCallAddress(moduleBaseAddress.add(0x9A000)) - cw.putMovRegAddress('eax', contentStruct) + //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | + cw.putMovRegU32('ebx', g_personal_detail_ebx) + cw.putMovRegReg('esi', 'eax') + cw.putPushReg('ebx') + cw.putSubRegImm('esp', 0x14) + cw.putMovRegAddress('eax', g_personal_wxid) + cw.putMovRegReg('ecx', 'esp') cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) + cw.putMovRegReg('ecx', 'esi') + cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) - cw.putMovRegAddress('edx', picWxid) //room_id - - cw.putMovRegAddress('ecx', ecxBuffer) - cw.putCallAddress(moduleBaseAddress.add( - offset.sendTxtMsg.callOffset - )) - - cw.putAddRegImm('esp', 0x18) cw.putPopax() cw.putPopfx() cw.putRet() @@ -1094,59 +1037,154 @@ const getChatroomMemberInfoFunction = (() => { }) - console.log('----------txtAsm', txtAsm) - const nativeativeFunction = new NativeFunction(ptr(txtAsm), 'void', []) + const nativeativeFunction = new NativeFunction(ptr(g_personal_detail_asm), 'void', []) nativeativeFunction() + }) -// 023 done +// const writeLogNativeCallback = (() => { +// const nativeCallback = new NativeCallback(() => { }, 'void', []) +// const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) + +// Interceptor.attach( +// moduleBaseAddress.add(0x7008A4), +// { +// onEnter() { +// const addr = this.context.eax//.sub(0x114)//0xc30-0x08 +// if(addr >0){ +// const log = ptr(addr).readAnsiString() +// } +// } +// }) +// return nativeCallback +// })() + +// 040 /** -* send at msg -*/ -let asmAtMsg = null -let roomid_, msg_, wxid_, atid_ -let ecxBuffer -const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { + * test call + */ +let attatchTestAsm = null +let attatchTestEbp2C = null +let attatchGlobalEDI = null +let attatchGlobalEDIB88 = null +let attatchTestEBX = null +let g_tempEcx = null +let attatchFirstECX = null +//let attatchFirstECX = null +let gattatchFilePtr = null +let gattatchFile = null +let gattatchReceiveIdPtr = null +let gattatchReceiveId = null +let attatchEAX3B0Buf = null - asmAtMsg = Memory.alloc(Process.pageSize) - ecxBuffer = Memory.alloc(0x3b0) +let attatchESIbuf = null +const getWxTest = ((contactId, filePath) => { + //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) + //nativeativeFunction() + attatchTestAsm = Memory.alloc(Process.pageSize) + console.log('----------------address', attatchTestAsm) + attatchTestEbp2C = Memory.alloc(0xC) + attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() + .add(0x938).add(0x438).readPointer() + attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() + attatchTestEBX = Memory.alloc(0x4) + attatchTestEBX.writePointer(attatchGlobalEDI) + console.log('----------------attatchGlobalEDI', attatchGlobalEDI) + console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) - const atContent = '@'+nickname+' '+text + attatchFirstECX = Memory.alloc(0x28) + //const attatchSecondEcx = Memory.alloc(0x14) - roomid_ = initStruct(roomId) - wxid_ = initidStruct(contactId) - msg_ = initmsgStruct(atContent) - atid_ = initAtMsgStruct(wxid_) + const contactIdLength = contactId.length * 2 + 2//edx + const contractIdActLength = contactId.length - Memory.patchCode(asmAtMsg, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: asmAtMsg - }) + + gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) + gattatchReceiveIdPtr.writeUtf16String(contactId) + + gattatchReceiveId = Memory.alloc(0x14) + gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) + .writeU32(contactId.length * 2).add(0x04) + .writeU32(contactId.length * 2).add(0x08) + //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) + //return + /*console.log(hexdump(attatchTestEBX, { + offset: 0, + length: 0x40, + header: true, + ansi: true + })) + return*/ + + gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) + gattatchFilePtr.writeUtf16String(filePath) + + gattatchFile = Memory.alloc(0x14) + gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) + .writeU32(filePath.length * 2).add(0x04) + .writeU32(filePath.length * 2).add(0x08) + + const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() + + attatchEAX3B0Buf = Memory.alloc(0x3B0) + + g_tempEcx = Memory.alloc(0x4) + //g_tempEcx1 = Memory.alloc(0x4) + + attatchESIbuf = Memory.alloc(0x100) + attatchESIbuf.add(0x0).writeU32(3) + attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) + attatchESIbuf.add(0x8).writeU32(filePath.length * 2) + attatchESIbuf.add(0xc).writeU32(filePath.length * 2) + + Memory.patchCode(attatchTestAsm, Process.pageSize, code => { + var cw = new X86Writer(code, { pc: attatchTestAsm }) cw.putPushfx() cw.putPushax() + //cw.putMovRegU32('edi',attatchGlobalEDI) - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x0) - cw.putPushU32(0x1) - //cw.putPushU32(0x0) - cw.putMovRegAddress('eax', atid_) + + cw.putMovRegAddress('esi', attatchESIbuf) + + cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) + cw.putMovRegAddress('eax', gattatchFile) cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - //cw.putMovRegReg - cw.putMovRegAddress('eax', msg_) + cw.putMovRegAddress('ecx', attatchFirstECX) + cw.putMovRegU32('eax', contactIdLength) cw.putPushReg('eax') + cw.putPushU32(0) + cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) + cw.putAddRegImm('esp', 0x8) - cw.putMovRegAddress('edx', roomid_) //room_id + cw.putMovRegReg('edx', 'eax') + cw.putMovNearPtrReg(attatchFirstECX, 'edx') + cw.putMovRegU32('edi', contactIdLength) + cw.putPushReg('edi') + cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 + cw.putPushReg('eax') + cw.putMovRegNearPtr('eax', attatchFirstECX) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) + cw.putAddRegImm('esp', 0x0c) - cw.putMovRegAddress('ecx', ecxBuffer) - cw.putCallAddress(moduleBaseAddress.add( - offset.sendTxtMsg.callOffset - )) + //cw.putMovRegNearPtr('ecx',attatchFirstECX) + //cw.putAddRegImm('esp', 0x0c) + //cw.putMovRegU32('edx', 0) + //cw.putMovRegRegPtr('eax', 'ecx') + //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') + cw.putMovRegU32('edi', contactId.length * 2) + cw.putMovRegU32('ecx', attatchLastECX) + cw.putMovRegAddress('eax', attatchEAX3B0Buf) + cw.putPushReg('eax') + cw.putCallAddress(moduleBaseAddress.add(0x392260)) + + cw.putMovRegAddress('ecx', attatchEAX3B0Buf) + cw.putCallAddress(moduleBaseAddress.add(0x94200)) - cw.putAddRegImm('esp', 0x18) cw.putPopax() cw.putPopfx() cw.putRet() @@ -1154,134 +1192,103 @@ const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { }) - //console.log('----------txtAsm', asmAtMsg) - const nativeativeFunction = new NativeFunction(ptr(asmAtMsg), 'void', []) + const nativeativeFunction = new NativeFunction(ptr(attatchTestAsm), 'void', []) nativeativeFunction() + }) -// 022 done -/** - * - * @param {*} contactId - * @param {*} path - */ - const sendPicMsgNativeFunction = ((contactId, path) => { +// 001 +const getTestInfoFunction = ((addr) => { + const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) + nativeativeFunction() - const picAsm = Memory.alloc(Process.pageSize) - const buffwxid = Memory.alloc(0x20) - const picbuff = Memory.alloc(0x2D8) + //00CFE484 - let pathPtr = Memory.alloc(path.length * 2 + 1) - pathPtr.writeUtf16String(path) - let imagefilepath = Memory.alloc(0x24) - imagefilepath.writePointer(pathPtr).add(0x04) - .writeU32(path.length * 2).add(0x04) - .writeU32(path.length * 2).add(0x04) + /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { + onAccess(details){ + console.log('============') + console.log(details.operation) + console.log(details.from) + console.log(details.address) + console.log('============') + } + })*/ - let picWxidPtr = Memory.alloc(contactId.length * 2 + 1) - picWxidPtr.writeUtf16String(contactId) +}) - let picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(picWxidPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x04) +// 002 +const isLoggedInFunction = (() => { + loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() + return !!loggedIn +}) +// 007 获得当前账号信息 +const getMyselfIdFunction = (() => { - //const test_offset1 = 0x701DC0; - Memory.patchCode(picAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: picAsm - }) - cw.putPushfx(); - cw.putPushax(); - cw.putCallAddress(moduleBaseAddress.add( - offset.sendPicMsg.call1 - )) - cw.putMovRegReg('edx', 'eax') //缓存 + let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', buffwxid) - cw.putMovRegReg('ecx', 'esp') - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.sendPicMsg.call2 - )) + return wx_id - cw.putMovRegReg('ecx', 'edx') - cw.putMovRegAddress('eax', picWxid) //=lea - cw.putMovRegAddress('edi', imagefilepath) - cw.putPushReg('edi') - cw.putPushReg('eax') - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') +}) - cw.putMovRegAddress('edi', picWxid) //edi - cw.putCallAddress(moduleBaseAddress.add( - offset.sendPicMsg.call3 - )) +//contact +// 042 +const recurse = ((node) => { + const headerNodeAddress = getHeaderNodeAddress() + if (headerNodeAddress.isNull()) { return } + if (node.equals(headerNodeAddress)) { return } + for (const item in nodeList) { + if (node.equals(nodeList[item])) { + return + } + } - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - }) + nodeList.push(node) + //wxid, format relates to registration method + const wxid = readWideString(node.add(0x38)) - //console.log('----------picAsm',picAsm) - const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) - nativeativeFunction() + //custom id, if not set return null, and use wxid which should be custom id + const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) -}) + //custom Nickname + const name = readWideString(node.add(0x94)) -// 020 done -let memberNickBuffAsm = null -let nickRoomId = null -let nickMemberId = null -let nickBuff = null -const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { + //alias aka 'remark' in wechat + const alias = readWideString(node.add(0x80)) - nickBuff = Memory.alloc(0x7e4) - //const nickRetAddr = Memory.alloc(0x04) - memberNickBuffAsm = Memory.alloc(Process.pageSize) - //console.log('asm address----------',memberNickBuffAsm) - nickRoomId = initidStruct(roomId) - //console.log('nick room id',nickRoomId) - nickMemberId = initStruct(memberId) + //avatarUrl + const avatar = readWideString(node.add(0x138)) + //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) + //contact gender + const gender = node.add(0x18C).readU32() - //console.log('nick nickMemberId id',nickMemberId) - //const nickStructPtr = initmsgStruct('') + const contactJson = { + id: wxid, + code: wx_code, + name: name, + alias: alias, + avatarUrl: avatar, + gender: gender, + } - Memory.patchCode(memberNickBuffAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { - pc: memberNickBuffAsm - }) - cw.putPushfx() - cw.putPushax() - cw.putMovRegAddress('edi', nickRoomId) - cw.putMovRegAddress('eax', nickBuff) - cw.putMovRegReg('edx', 'edi') - cw.putPushReg('eax') - cw.putMovRegAddress('ecx', nickMemberId) - cw.putCallAddress(moduleBaseAddress.add(0xC06F10)) - cw.putAddRegImm('esp', 0x04) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() + contactList.push(contactJson) - }) + const leftNode = node.add(0x0).readPointer() + const centerNode = node.add(0x04).readPointer() + const rightNode = node.add(0x08).readPointer() - const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsm), 'void', []) - nativeativeFunction() + recurse(leftNode) + recurse(centerNode) + recurse(rightNode) + + const allContactJson = contactList + return allContactJson - const nickname = readWideString(nickBuff) - console.log('----nickname', nickname) - return readWideString(nickBuff) }) // 013 @@ -1428,13 +1435,13 @@ const getQrcodeLoginData = () => { return json } - +// 045 /** * 20220504 writelog * 7A566D72 | FFB5 ECFEFFFF | push dword ptr ss:[ebp-114] | 【3.6.0.18】写日志,这个里面就是日志内容 7A566D78 | FFB5 E8FEFFFF | push dword ptr ss:[ebp-118] | */ -/*const writeLogNativeCallback = (() => { +const writeLogNativeCallback = (() => { const nativeCallback = new NativeCallback(() => { }, 'void', []) const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) @@ -1448,8 +1455,9 @@ const getQrcodeLoginData = () => { } }) return nativeCallback -})()*/ +})() +// 046 let nickRoomIdV6 = null let nullEdiWxidStructV6 = null let nickMemberIdStructV6 = null @@ -1509,6 +1517,7 @@ const getChatroomMemberNickInfoV1Function = ((memberId, roomId) => { return readWideString(nullEdiWxidStructV6) }) +// 021 /** * send attatch */ @@ -1550,8 +1559,6 @@ let attatchECX = null param {78EDBC86 | 8B80 38040000 | mov eax,dword ptr ds:[eax+438] | 78EDBC8C | 8BB0 800B0000 | mov esi,dword ptr ds:[eax+B80] |} sendWxid */ - -// 021 const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size) => { attatchAsm = Memory.alloc(Process.pageSize) @@ -1755,78 +1762,6 @@ const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size //console.log('-------',attatchEbp1C.add(0x4).readPointer()) //console.log('-------',attatchEbp1C.add(0x8).readPointer()) }) -/*------------------send pic -------------------------- -let buffwxid = null -let imagefilepath = null -let pathPtr = null -let picWxid = null -let picWxidPtr = null -let picAsm = null -let picbuff = null -const sendPicMsgNativeFunction = ((contactId, path) => { - - picAsm = Memory.alloc(Process.pageSize) - buffwxid = Memory.alloc(0x20) - picbuff = Memory.alloc(0x378) - - pathPtr = Memory.alloc(path.length * 2 + 1) - pathPtr.writeUtf16String(path) - - imagefilepath = Memory.alloc(0x24) - imagefilepath.writePointer(pathPtr).add(0x04) - .writeU32(path.length * 2).add(0x04) - .writeU32(path.length * 2).add(0x04) - - picWxidPtr = Memory.alloc(contactId.length * 2 + 1) - picWxidPtr.writeUtf16String(contactId) - - picWxid = Memory.alloc(0x0c) - picWxid.writePointer(ptr(picWxidPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - - Memory.patchCode(picAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: picAsm }) - cw.putPushfx(); - cw.putPushax(); - - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', buffwxid) - - cw.putMovRegReg('ecx', 'esp') - - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset1 - )) - - cw.putMovRegAddress('ebx', imagefilepath) - cw.putPushReg('ebx') - - cw.putMovRegAddress('eax', picWxid) - cw.putPushReg('eax') - - cw.putMovRegAddress('eax', picbuff) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset2 - )) - - cw.putMovRegReg('ecx', 'eax') - cw.putCallAddress(moduleBaseAddress.add( - offset.send_picmsg_call_offset3 - )) - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(picAsm), 'void', []) - nativeativeFunction() - -})*/ // 025 const callLoginQrcodeFunction = ((forceRefresh = false) => { From 3faaa0b48b502a1941c32d350e57b6a78f5e82e1 Mon Sep 17 00:00:00 2001 From: choogoo <104893934+choogoo@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:54:14 +0800 Subject: [PATCH 03/38] =?UTF-8?q?=E9=80=82=E9=85=8D3.9.2.23=20(#185)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 3.9.2.23 initt * Update init-agent-script.js * 3.9.2.23 adapter * 适配3.9.2.23 * Delete agent-script-3.9.2.23-new.js * 适配3.9.2.23 --------- Co-authored-by: LuChao --- package.json | 2 +- src/init-agent-script.js | 1174 ++------------------------------------ src/puppet-xp.ts | 14 +- src/wechat-sidecar.ts | 66 ++- 4 files changed, 84 insertions(+), 1172 deletions(-) diff --git a/package.json b/package.json index 8e9f1dd..ffe22cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wechaty-puppet-xp", - "version": "1.12.7", + "version": "1.13.0", "description": "Puppet XP for Wechaty", "type": "module", "exports": { diff --git a/src/init-agent-script.js b/src/init-agent-script.js index 9c97f9a..802ce45 100644 --- a/src/init-agent-script.js +++ b/src/init-agent-script.js @@ -1,16 +1,20 @@ /** + * WeChat 3.2.1.121 * > Special thanks to: @cixingguangming55555 老张学技术 * * Credit: https://github.com/cixingguangming55555/wechat-bot + * Source: https://pan.baidu.com/s/1OmX2lxNOYHyGsl_3ByhsoA + * 《源码3.2.1.121》提取码: 1rfa + * WeChat: https://pan.baidu.com/share/init?surl=IHRM2OMvrLyuCz5MRbigGg + * 微信:3.2.1.121 提取码: cscn */ //https://blog.csdn.net/iloveitvm/article/details/109119687 frida学习 //const { isNullishCoalesce } = require("typescript") -// wechat:3.9.2.23 +//3.9.2.23 -// 028 done const offset = { @@ -45,19 +49,18 @@ const offset = { } }; -// 029 done + + /*------------------global-------------------------------------------*/ const availableVersion = 1661534743 ////3.9.2.23 ==0x63090217 const moduleBaseAddress = Module.getBaseAddress('WeChatWin.dll') const moduleLoad = Module.load('WeChatWin.dll') -// tbd -const g_EDIPtr = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readPointer()// -const g_EDIU32 = moduleBaseAddress.add(0x222f38c).readPointer().add(0xD70).readU32() -/*------------------global-------------------------------------------*/ +//1575CF98 + /*---------------base -------------------------*/ -// 030 done + let retidPtr=null let retidStruct=null const initidStruct = ((str) => { @@ -77,7 +80,6 @@ const initidStruct = ((str) => { return retidStruct }) -// 031 done let retPtr = null let retStruct = null const initStruct = ((str) => { @@ -97,7 +99,6 @@ const initStruct = ((str) => { return retStruct }) -// 032 done let msgstrPtr=null let msgStruct=null const initmsgStruct = ((str) => { @@ -116,10 +117,6 @@ const initmsgStruct = ((str) => { return msgStruct }) -// 034 done -/** -* at msg structure -*/ let atStruct = null const initAtMsgStruct = ((wxidStruct) => { @@ -131,28 +128,6 @@ const initAtMsgStruct = ((wxidStruct) => { .writeU32(0) return atStruct }) - -// 033 tbd -let retidNullStruct = null -let retidNullPtr = null -const initNullIdStruct = ((str) => { - - retidNullPtr = Memory.alloc(str.length * 2 + 1) - retidNullPtr.writeUtf16String(str) - - retidNullStruct = Memory.alloc(0x14) // returns a NativePointer - - retidNullStruct - .writePointer(retidNullPtr).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0) - - return retidNullStruct -}) - -// 035 done const readStringPtr = (address) => { const addr = ptr(address) const size = addr.add(16).readU32() @@ -183,18 +158,6 @@ const readStringPtr = (address) => { return addr } -// 036 done -const readString = (address) => { - return readStringPtr(address).readUtf8String() -} - -// 037 done -const readWideString = (address) => { - return readWStringPtr(address).readUtf16String() -} -/*-----------------base-------------------------*/ - -// 041 // std::wstring // const wstr = readWStringPtr(ptr).readUtf16String() const readWStringPtr = (address) => { @@ -216,9 +179,17 @@ const readWStringPtr = (address) => { return addr } +const readString = (address) => { + return readStringPtr(address).readUtf8String() +} + +const readWideString = (address) => { + return readWStringPtr(address).readUtf16String() +} + + /*-----------------base-------------------------*/ -// 010 done let currentVersion = 0 let nodeList = [] //for contact @@ -229,6 +200,7 @@ let chatroomMemberList = [] //for chatroom let loggedIn = false + const getWechatVersionFunction = (() => { if (currentVersion) { return currentVersion @@ -245,7 +217,7 @@ const getWechatVersionFunction = (() => { return ver }) -// 011 done +// 011 const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { if (!ver) { return '0.0.0.0' @@ -258,17 +230,13 @@ const getWechatVersionStringFunction = ((ver = getWechatVersionFunction()) => { return vers.join('.') }) -// 012 done const checkSupportedFunction = (() => { const ver = getWechatVersionFunction() return ver == availableVersion }) -// 019 done -/** - * @Hook: recvMsg -> recvMsgNativeCallback - */ - const recvMsgNativeCallback = (() => { +// 019 +const recvMsgNativeCallback = (() => { const nativeCallback = new NativeCallback(() => {}, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'int32']) @@ -356,12 +324,12 @@ const checkSupportedFunction = (() => { return nativeCallback })() -// 003 done + const getBaseNodeAddress = (() => { return moduleBaseAddress.add(offset.contactInfo.nodeOffset).readPointer() }) -// 004 done +// 004 const getHeaderNodeAddress = (() => { const baseAddress = getBaseNodeAddress() //console.log('baseAddress',baseAddress) @@ -373,7 +341,6 @@ const getHeaderNodeAddress = (() => { return baseAddress.add(offset.contactInfo.nodeRootOffset).readPointer() }) -// 006 done const getMyselfInfoFunction = (() => { let ptr = 0 @@ -411,7 +378,7 @@ const getMyselfInfoFunction = (() => { }) -// 043 done + const recurseNew = ((node) => { const headerNodeAddress = getHeaderNodeAddress() if (headerNodeAddress.isNull()) { @@ -477,7 +444,7 @@ const recurseNew = ((node) => { }) -// 044 done + const getContactNativeFunction = (() => { const headerNodeAddress = getHeaderNodeAddress() //console.log('headerNodeAddress',headerNodeAddress) @@ -503,7 +470,7 @@ const getContactNativeFunction = (() => { return cloneRet }) -// 0xx done + const getChatroomNodeAddress = (() => { const baseAddress = moduleBaseAddress.add(offset.chatroomInfo.nodeOffset).readPointer() if (baseAddress.isNull()) { @@ -512,7 +479,6 @@ const getChatroomNodeAddress = (() => { return baseAddress.add(offset.chatroomInfo.nodeRootOffset).readPointer() }) -// 008chatroom member done const chatroomRecurse = ((node) => { const chatroomNodeAddress = getChatroomNodeAddress() if (chatroomNodeAddress.isNull()) { @@ -560,7 +526,6 @@ const chatroomRecurse = ((node) => { return allChatroomMemberJson }) -// 009 done const getChatroomMemberInfoFunction = (() => { const chatroomNodeAddress = getChatroomNodeAddress() if (chatroomNodeAddress.isNull()) { @@ -576,14 +541,15 @@ const getChatroomMemberInfoFunction = (() => { return cloneRet }) -// 024 done + + /** * sendMsgNativeFunction * send text message * @param {string} talkerId = wxid or roomid * @param {string} content */ - const sendMsgNativeFunction = ((talkerId, content) => { +const sendMsgNativeFunction = ((talkerId, content) => { const txtAsm = Memory.alloc(Process.pageSize) //const buffwxid = Memory.alloc(0x20) @@ -650,10 +616,7 @@ const getChatroomMemberInfoFunction = (() => { }) -// 023 done -/** -* send at msg -*/ + let asmAtMsg = null let roomid_, msg_, wxid_, atid_ let ecxBuffer @@ -710,13 +673,12 @@ const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { }) -// 022 done /** * * @param {*} contactId * @param {*} path */ - const sendPicMsgNativeFunction = ((contactId, path) => { +const sendPicMsgNativeFunction = ((contactId, path) => { const picAsm = Memory.alloc(Process.pageSize) const buffwxid = Memory.alloc(0x20) @@ -788,7 +750,9 @@ const sendAtMsgNativeFunction = ((roomId, text, contactId,nickname) => { }) -// 020 done + + + let memberNickBuffAsm = null let nickRoomId = null let nickMemberId = null @@ -834,1066 +798,8 @@ const getChatroomMemberNickInfoFunction = ((memberId, roomId) => { return readWideString(nickBuff) }) -/* 由此之后是3.9.2.23中缺失的未实现方法 */ - -// 038 初始化全局变量 tbd -let g_initTestAsm = null -let g_BufferEbp2C = null -let g_initECXU32 = null -let g_initECXPtr = null -let g_initEBXPtr = null -let g_initEBX = null - -let g_attatchEBP210Buffer = null -let g_attatchPathPtr = null -let g_attatchPath = null -let g_attatchEBPAc = null -let g_attatchEBPAcBufPtr = null - -let g_attatchContactIdPtr = null -//let g_attatchECXBuffer = null -let g_attatchESIU32 = null - -let g_initECXTempPtr = null -const initGlobal = ((contactId, attatchFile) => { - - //const base = moduleBaseAddress.add(0x222f38c).readPointer() - //g_EDI = base.add(0xD70).readPointer()//0x438+0x938 - console.log('------------g_attatchEBPAc', g_attatchEBPAc) - console.log('------------g_EDIU32', g_EDIU32) - g_initTestAsm = Memory.alloc(Process.pageSize) - console.log('------------address', g_initTestAsm) - - g_initECXPtr = g_EDIPtr.add(0xB80).readPointer().add(0x1640) - g_initECXTempPtr = g_EDIPtr.add(0xB88).readPointer() - g_initECXU32 = g_initECXPtr.toInt32() - g_attatchESIU32 = g_EDIU32 - - console.log('------------g_initECXU32', g_initECXU32) - console.log('------------g_initESIU32', g_attatchESIU32) - - - //console.log('==========g_initECXPtr',g_initECXPtr) - //console.log('==========g_EDIU32',g_EDIU32) - - //g_attatchECXBuffer = Memory.alloc(0x1024) - //Memory.copy(g_attatchECXBuffer, g_initECXPtr, 0x1024) - - g_BufferEbp2C = Memory.alloc(0x48) - - //g_initEBX = moduleBaseAddress.add(0x2251724).readPointer().readPointer() - //g_initEBXPtr = Memory.alloc(0x14).writePointer(g_initEBX) - //g_initEBXPtr.add(0x08).writePointer(g_BufferEbp2C) - - g_attatchPathPtr = Memory.alloc(attatchFile.length * 2 + 2) - g_attatchPathPtr.writeUtf16String(attatchFile) - - g_attatchPath = Memory.alloc(0x28) - g_attatchPath.writePointer(g_attatchPathPtr).add(0x04) - .writeU32(attatchFile.length * 2).add(0x04) - .writeU32(attatchFile.length * 2).add(0x04) - /*---------------------------------ebp-210----------------*/ - g_attatchEBP210Buffer = Memory.alloc(0x48) - g_attatchEBP210Buffer.writeU32(0x3) - g_attatchEBP210Buffer.add(0x4).writePointer(g_attatchPathPtr) - g_attatchEBP210Buffer.add(0x8).writeU32(attatchFile.length * 2) - g_attatchEBP210Buffer.add(0xC).writeU32(attatchFile.length * 2) - g_attatchEBP210Buffer.add(0x2C).writeU32(moduleBaseAddress.add(0x2ECE87).toInt32()) - //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) - /*---------------------------------ebp-210----------------*/ - - //g_attatchContactIdPtr = Memory.alloc(0x4) - //g_attatchContactIdPtr.writeUtf16String(contactId) - //console.log('------------g_attatchEBP210Buffer',g_attatchEBP210Buffer) - g_attatchEBPAc = Memory.alloc(0x140) - //g_attatchEBPAcBufPtr = Memory.alloc(0x100) - //g_attatchEBPAc.writePointer(g_attatchEBP210Buffer) - //g_attatchEBPAc.add(0x4).writePointer(g_attatchEBPAcBufPtr) - //g_attatchEBPAc.add(0x8).writePointer(g_attatchEBPAcBufPtr) - g_attatchEBPAc.add(0x10).writeU32(g_EDIU32) - console.log('------------g_attatchEBPAc', g_attatchEBPAc) - - /*g_attatchECXBuffer.add(0x18).writePointer(g_attatchContactIdPtr) - g_attatchECXBuffer.add(0x1C).writeU32(contactId.length*2) - .add(0x04).writeU32(contactId.length*2)*/ - - - - //g_attatchESIU32 = g_EDI.toInt32() - - - //console.log('------------g_attatchESIU32',g_attatchESIU32) - //console.log('==========g_attatchECXBuffer',g_attatchECXBuffer) - - Memory.patchCode(g_initTestAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: g_initTestAsm }) - cw.putPushfx() - cw.putPushax() - //cw.putMovRegAddress('eax', g_attatchEBP210Buffer) - - /*cw.putMovRegAddress('edi',g_EDIPtr) - cw.putMovRegReg('esi','edi') - cw.putMovRegAddress('eax',g_BufferEbp2C) - cw.putMovRegAddress('ecx',g_initECXTempPtr) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x131bb0))*/ - - //cw.putMovRegOffsetPtrU32('ebp', -20, 0) - - /*cw.putPushU32(0) - cw.putMovRegAddress('eax', g_attatchPathPtr) - cw.putPushReg('eax') - cw.putPushU32(3) - cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) - cw.putCallAddress(moduleBaseAddress.add(0x130220))*/ - - - /*cw.putMovRegAddress('edi', g_attatchEBPAc)//后面要用的的ebp-2c - cw.putMovRegAddress('ecx', g_attatchEBP210Buffer) - cw.putPushReg('ecx') - cw.putMovRegU32('eax',0) - cw.putPushReg('eax')//push eax - cw.putMovRegReg('ecx', 'edi') - cw.putMovRegAddress('esi',g_attatchPathPtr) - cw.putCallAddress(moduleBaseAddress.add(0x138880))*/ - - - /*cw.putSubRegImm('esp',0x14) - cw.putMovRegU32('ecx',g_initECXU32) - cw.putMovRegU32('esi',g_attatchESIU32) - cw.putMovRegAddress('eax', g_attatchEBPAc) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x173620))*/ - //cw.putCallAddress(moduleBaseAddress.add(0x522590)) - - /** g_attatchEBPAc*/ - //cw.putMovRegNearPtr('eax', g_attatchEBPAc) - //cw.putMovNearPtrReg(g_attatchEBPAc.add(0xc), 'eax') - /** g_attatchEBPAc*/ - - - //cw.putMovRegAddress('ebx', g_initEBXPtr) - //cw.putMovRegU32('edi', g_EDI.toInt32()) - //cw.putMovRegU32('esi', g_EDI.toInt32()) - /*cw.putMovRegU32('ecx', g_initECX) - cw.putMovRegAddress('eax', g_BufferEbp2C) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x131BB0))*/ - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(g_initTestAsm), 'void', []) - nativeativeFunction() -}) - -// 039 -let g_personal_detail_ebx = null -let g_personal_detail_asm = null -let g_personal_wxid = null -let g_personal_wxid_ptr = null -const getOldTest = ((wxid) => {//personal detail - - g_personal_detail_asm = Memory.alloc(Process.pageSize) - g_personal_detail_ebx = moduleBaseAddress.add(0x222F38C).readPointer().add(0xFB8).toInt32() - - g_personal_wxid_ptr = Memory.alloc(wxid.length * 2 + 2) - g_personal_wxid_ptr.writeUtf16String(wxid) - - g_personal_wxid = Memory.alloc(0x14) - g_personal_wxid.writePointer(ptr(g_personal_wxid_ptr)).add(0x04) - .writeU32(wxid.length * 2).add(0x04) - .writeU32(wxid.length * 2).add(0x08) - - console.log('-----------address----------', g_personal_detail_asm) - - Memory.patchCode(g_personal_detail_asm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: g_personal_detail_asm }) - cw.putPushfx() - cw.putPushax() - - cw.putCallAddress(moduleBaseAddress.add(0x9A000)) - - //78BA9ACE | E8 2D05D6FF | call wechatwin.7890A000 | - cw.putMovRegU32('ebx', g_personal_detail_ebx) - cw.putMovRegReg('esi', 'eax') - cw.putPushReg('ebx') - cw.putSubRegImm('esp', 0x14) - cw.putMovRegAddress('eax', g_personal_wxid) - cw.putMovRegReg('ecx', 'esp') - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - cw.putMovRegReg('ecx', 'esi') - cw.putCallAddress(moduleBaseAddress.add(0x4024A0)) - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(g_personal_detail_asm), 'void', []) - nativeativeFunction() - - -}) - -// const writeLogNativeCallback = (() => { -// const nativeCallback = new NativeCallback(() => { }, 'void', []) -// const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) - -// Interceptor.attach( -// moduleBaseAddress.add(0x7008A4), -// { -// onEnter() { -// const addr = this.context.eax//.sub(0x114)//0xc30-0x08 -// if(addr >0){ -// const log = ptr(addr).readAnsiString() -// } -// } -// }) -// return nativeCallback -// })() - -// 040 -/** - * test call - */ -let attatchTestAsm = null -let attatchTestEbp2C = null -let attatchGlobalEDI = null -let attatchGlobalEDIB88 = null -let attatchTestEBX = null -let g_tempEcx = null -let attatchFirstECX = null -//let attatchFirstECX = null -let gattatchFilePtr = null -let gattatchFile = null -let gattatchReceiveIdPtr = null -let gattatchReceiveId = null -let attatchEAX3B0Buf = null - -let attatchESIbuf = null -const getWxTest = ((contactId, filePath) => { - //const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) - //nativeativeFunction() - attatchTestAsm = Memory.alloc(Process.pageSize) - console.log('----------------address', attatchTestAsm) - attatchTestEbp2C = Memory.alloc(0xC) - attatchGlobalEDI = moduleBaseAddress.add(0x222f38c).readPointer() - .add(0x938).add(0x438).readPointer() - attatchGlobalEDIB88 = attatchGlobalEDI.add(0xB88).readPointer() - attatchTestEBX = Memory.alloc(0x4) - attatchTestEBX.writePointer(attatchGlobalEDI) - console.log('----------------attatchGlobalEDI', attatchGlobalEDI) - console.log('----------------attatchGlobalEDIB88', attatchGlobalEDIB88) - - attatchFirstECX = Memory.alloc(0x28) - //const attatchSecondEcx = Memory.alloc(0x14) - - const contactIdLength = contactId.length * 2 + 2//edx - const contractIdActLength = contactId.length - - - gattatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) - gattatchReceiveIdPtr.writeUtf16String(contactId) - - gattatchReceiveId = Memory.alloc(0x14) - gattatchReceiveId.writePointer(ptr(gattatchReceiveIdPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x08) - //console.log('----------------attatchGlobalEDIB88',attatchGlobalEDIB88) - //return - /*console.log(hexdump(attatchTestEBX, { - offset: 0, - length: 0x40, - header: true, - ansi: true - })) - return*/ - - gattatchFilePtr = Memory.alloc(filePath.length * 2 + 2) - gattatchFilePtr.writeUtf16String(filePath) - - gattatchFile = Memory.alloc(0x14) - gattatchFile.writePointer(ptr(gattatchFilePtr)).add(0x04) - .writeU32(filePath.length * 2).add(0x04) - .writeU32(filePath.length * 2).add(0x08) - - const attatchLastECX = moduleBaseAddress.add(0x222f170).toInt32() - - attatchEAX3B0Buf = Memory.alloc(0x3B0) - - g_tempEcx = Memory.alloc(0x4) - //g_tempEcx1 = Memory.alloc(0x4) - - attatchESIbuf = Memory.alloc(0x100) - attatchESIbuf.add(0x0).writeU32(3) - attatchESIbuf.add(0x4).writePointer(gattatchFilePtr) - attatchESIbuf.add(0x8).writeU32(filePath.length * 2) - attatchESIbuf.add(0xc).writeU32(filePath.length * 2) - - Memory.patchCode(attatchTestAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: attatchTestAsm }) - cw.putPushfx() - cw.putPushax() - //cw.putMovRegU32('edi',attatchGlobalEDI) - - - cw.putMovRegAddress('esi', attatchESIbuf) - - cw.putMovRegAddress('ecx', attatchFirstECX.add(0x14)) - cw.putMovRegAddress('eax', gattatchFile) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x701DC0)) - - - cw.putMovRegAddress('ecx', attatchFirstECX) - cw.putMovRegU32('eax', contactIdLength) - cw.putPushReg('eax') - cw.putPushU32(0) - cw.putCallAddress(moduleBaseAddress.add(0x1a11c83)) - cw.putAddRegImm('esp', 0x8) - - cw.putMovRegReg('edx', 'eax') - cw.putMovNearPtrReg(attatchFirstECX, 'edx') - cw.putMovRegU32('edi', contactIdLength) - cw.putPushReg('edi') - cw.putMovRegAddress('eax', gattatchReceiveIdPtr)//ebp-58 - cw.putPushReg('eax') - cw.putMovRegNearPtr('eax', attatchFirstECX) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x1a1047a)) - cw.putAddRegImm('esp', 0x0c) - - //cw.putMovRegNearPtr('ecx',attatchFirstECX) - //cw.putAddRegImm('esp', 0x0c) - //cw.putMovRegU32('edx', 0) - //cw.putMovRegRegPtr('eax', 'ecx') - //cw.putMovNearPtrReg(attatchFirstECX.add(0x04),'edi') - cw.putMovRegU32('edi', contactId.length * 2) - cw.putMovRegU32('ecx', attatchLastECX) - cw.putMovRegAddress('eax', attatchEAX3B0Buf) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x392260)) - - cw.putMovRegAddress('ecx', attatchEAX3B0Buf) - cw.putCallAddress(moduleBaseAddress.add(0x94200)) - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(attatchTestAsm), 'void', []) - nativeativeFunction() - - -}) - -// 001 -const getTestInfoFunction = ((addr) => { - const nativeativeFunction = new NativeFunction(ptr(addr), 'void', []) - nativeativeFunction() - - //00CFE484 - - - /*MemoryAccessMonitor.enable({base:ptr(addr),size:0x01}, { - onAccess(details){ - console.log('============') - console.log(details.operation) - console.log(details.from) - console.log(details.address) - console.log('============') - } - })*/ - -}) - -// 002 const isLoggedInFunction = (() => { - loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() - return !!loggedIn -}) - -// 007 获得当前账号信息 -const getMyselfIdFunction = (() => { - - let wx_id = readString(moduleBaseAddress.add(offset.wxid_offset)) - - return wx_id - -}) - -//contact -// 042 -const recurse = ((node) => { - const headerNodeAddress = getHeaderNodeAddress() - if (headerNodeAddress.isNull()) { return } - - if (node.equals(headerNodeAddress)) { return } - - for (const item in nodeList) { - if (node.equals(nodeList[item])) { - return - } - } - - - nodeList.push(node) - //wxid, format relates to registration method - const wxid = readWideString(node.add(0x38)) - - //custom id, if not set return null, and use wxid which should be custom id - const wx_code = readWideString(node.add(0x4c)) || readWideString(node.add(0x38)) - - //custom Nickname - const name = readWideString(node.add(0x94)) - - //alias aka 'remark' in wechat - const alias = readWideString(node.add(0x80)) - - //avatarUrl - const avatar = readWideString(node.add(0x138)) - //const avatar = Memory.readUtf16String(node.add(0x138).readPointer()) - //contact gender - const gender = node.add(0x18C).readU32() - - const contactJson = { - id: wxid, - code: wx_code, - name: name, - alias: alias, - avatarUrl: avatar, - gender: gender, - } - - contactList.push(contactJson) - - const leftNode = node.add(0x0).readPointer() - const centerNode = node.add(0x04).readPointer() - const rightNode = node.add(0x08).readPointer() - - recurse(leftNode) - recurse(centerNode) - recurse(rightNode) - - const allContactJson = contactList - return allContactJson - -}) - -// 013 -const isSupported = checkSupportedFunction() - -if (!isSupported) { - throw new Error(`Wechat version not supported. \nWechat version: ${getWechatVersionStringFunction()}, supported version: ${getWechatVersionStringFunction(availableVersion)}`) -} - -// 015 -const hookLogoutEventCallback = (() => { - const nativeCallback = new NativeCallback(() => { }, 'void', ['int32']) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32']) - Interceptor.attach(moduleBaseAddress.add(offset.hook_on_logout_offset), { - onEnter: function (args) { - const bySrv = args[0].toInt32() - setImmediate(() => nativeativeFunction(bySrv)) - } - }) - return nativeCallback -})() - -// 016 -const hookLoginEventCallback = (() => { - const nativeCallback = new NativeCallback(() => { }, 'void', []) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', []) - Interceptor.attach(moduleBaseAddress.add(offset.hook_on_login_offset), { - onLeave: function (retval) { - isLoggedInFunction() - setImmediate(() => nativeativeFunction()) - return retval - } - }) - - setTimeout(() => { - if (isLoggedInFunction()) { - setImmediate(() => nativeativeFunction()) - } - }, 500); - - return nativeCallback -})() - -// 017 -const checkQRLoginNativeCallback = (() => { - - const nativeCallback = new NativeCallback(() => { }, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'pointer', 'int32', 'pointer']) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', ['int32', 'pointer', 'pointer', 'pointer', 'pointer', 'pointer', 'int32', 'pointer']) - // const json = { - // status, - // uuid, - // wxid, - // avatarUrl, - // nickname, - // phoneType, - // phoneClientVer, - // pairWaitTip, - // } - - const callback = { - onLeave: function (retval) { - const json = getQrcodeLoginData() - if (json.status == 0) { - // 当状态为 0 时,即未扫码。而其他状态会触发另一个方法,拥有更多数据。 - ret(json) - } - return retval - }, - } - - const ret = (json) => { - const arr = [ - json.status || 0, - Memory.allocUtf8String(json.uuid ? `http://weixin.qq.com/x/${json.uuid}` : ''), - Memory.allocUtf8String(json.wxid || ''), - Memory.allocUtf8String(json.avatarUrl || ''), - Memory.allocUtf8String(json.nickname || ''), - Memory.allocUtf8String(json.phoneType || ''), - json.phoneClientVer || 0, - Memory.allocUtf8String(json.pairWaitTip || ''), - ] - setImmediate(() => nativeativeFunction(...arr)) - } - - Interceptor.attach(moduleBaseAddress.add(offset.hook_get_login_qr_offset), callback) - Interceptor.attach(moduleBaseAddress.add(offset.hook_check_login_qr_offset), callback) - Interceptor.attach(moduleBaseAddress.add(offset.hook_save_login_qr_info_offset), { - onEnter: function () { - const qrNotify = this.context['ebp'].sub(72) - const uuid = readString(qrNotify.add(4).readPointer()) - const wxid = readString(qrNotify.add(8).readPointer()) - const status = qrNotify.add(16).readUInt() - const avatarUrl = readString(qrNotify.add(24).readPointer()) - const nickname = readString(qrNotify.add(28).readPointer()) - const pairWaitTip = readString(qrNotify.add(32).readPointer()) - const phoneClientVer = qrNotify.add(40).readUInt() - const phoneType = readString(qrNotify.add(44).readPointer()) - - const json = { - status, - uuid, - wxid, - avatarUrl, - nickname, - phoneType, - phoneClientVer, - pairWaitTip, - } - ret(json) - }, - onLeave: function (retval) { - return retval - }, - }) - - if (!isLoggedInFunction()) { - setTimeout(() => { - const json = getQrcodeLoginData() - ret(json) - }, 100); - } - - return nativeCallback -})() - -// 018 -const getQrcodeLoginData = () => { - const getQRCodeLoginMgr = new NativeFunction(moduleBaseAddress.add(offset.get_qr_login_data_offset), 'pointer', []) - const qlMgr = getQRCodeLoginMgr() - - const json = { - status: 0, - uuid: '', - wxid: '', - avatarUrl: '', - } - - if (!qlMgr.isNull()) { - json.uuid = readString(qlMgr.add(8)) - json.status = qlMgr.add(40).readUInt() - json.wxid = readString(qlMgr.add(44)) - json.avatarUrl = readString(qlMgr.add(92)) - } - return json -} - -// 045 -/** - * 20220504 writelog - * 7A566D72 | FFB5 ECFEFFFF | push dword ptr ss:[ebp-114] | 【3.6.0.18】写日志,这个里面就是日志内容 - 7A566D78 | FFB5 E8FEFFFF | push dword ptr ss:[ebp-118] | - */ -const writeLogNativeCallback = (() => { - const nativeCallback = new NativeCallback(() => { }, 'void', []) - const nativeCallFunction = new NativeFunction(nativeCallback, 'void', []) - - Interceptor.attach( - moduleBaseAddress.add(0x1576D7E), - { - onEnter() { - const addr = this.context.ebp.sub(0x114)//0xc30-0x08 - console.log('-------',addr) - - } - }) - return nativeCallback -})() - -// 046 -let nickRoomIdV6 = null -let nullEdiWxidStructV6 = null -let nickMemberIdStructV6 = null -let memberNickBuffAsmV6 = null -let nickResultEdiV6 = null - -const getChatroomMemberNickInfoV1Function = ((memberId, roomId) => { - nickRoomIdV6 = initidStruct(roomId) - nullEdiWxidStructV6 = initNullIdStruct('') - nickMemberIdStructV6 = initStruct(memberId) - memberNickBuffAsmV6 = Memory.alloc(Process.pageSize) - console.log('-----', memberNickBuffAsmV6) - - const tmp = (moduleBaseAddress.add( - offset.chatroom_member_nick_esi_offset_v6 - )).readU32() - console.log('=======tmp', tmp) - Memory.patchCode(memberNickBuffAsmV6, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: memberNickBuffAsmV6 }) - cw.putPushfx(); - cw.putPushax(); - - cw.putMovRegAddress('edi', nullEdiWxidStructV6) - cw.putMovRegAddress('eax', nickMemberIdStructV6) - cw.putMovRegAddress('ebx', nickRoomIdV6) - - - cw.putMovRegAddress('esi', ptr(tmp)) - cw.putPushReg('edi') - cw.putPushReg('eax') - cw.putPushReg('ebx') - - cw.putMovRegAddress('ecx', ptr(tmp)) - - cw.putCallAddress(moduleBaseAddress.add( - offset.chatroom_member_nick_call_offset_v6 - )) - - - //cw.putMovNearPtrReg(nickResultEdiV6, 'edi') - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(memberNickBuffAsmV6), 'void', []) - nativeativeFunction() - - console.log('---------nullEdiWxidStructV6', nullEdiWxidStructV6) - const nickha = readWideString(nullEdiWxidStructV6) - - console.log('-----------------') - console.log(nickha) - console.log('-----------------') - return readWideString(nullEdiWxidStructV6) -}) - -// 021 -/** -* send attatch -*/ -let attatchWxid = null -let attatchPath = null -let attatchPathPtr = null -let attatchAsm = null -let attatchBuf = null - -let attatchReceiveIdPtr = null -let attatchReceiveId = null - -let attatchSendId = null -let attatchSendIdPtr = null - -/* -let attatchEbp2C = null -let attatchEDIPtr = null -let attatchEDIU32 = null -let attatchECX = null - -let attatchEbp210 = null -let attatchEbpAc = null*/ - -let attatchEbp11E8 = null -let attatchEbpCC = null -let attatchEbp368 = null -let attatchEAX = null - -let sFileName = null -let fileNamePtr = null - -let attatchEbp84 = null -let attatchECX = null - - -/** - * -param {78EDBC86 | 8B80 38040000 | mov eax,dword ptr ds:[eax+438] | -78EDBC8C | 8BB0 800B0000 | mov esi,dword ptr ds:[eax+B80] |} sendWxid -*/ -const sendAttatchMsgNativeFunction = ((contactId, senderId, path, filename, size) => { - - attatchAsm = Memory.alloc(Process.pageSize) - console.log('--------------address', attatchAsm) - - fileNamePtr = Memory.alloc(filename.length * 2 + 2) - fileNamePtr.writeUtf16String(filename) - - sFileName = Memory.alloc(0x14) - sFileName.writePointer(ptr(fileNamePtr)).add(0x04) - .writeU32(fileNamePtr.length * 2).add(0x04) - .writeU32(fileNamePtr.length * 2).add(0x08) - - //const fileSize = size.toInt32() - - - attatchReceiveIdPtr = Memory.alloc(contactId.length * 2 + 2) - attatchReceiveIdPtr.writeUtf16String(contactId) - - attatchReceiveId = Memory.alloc(0x14) - attatchReceiveId.writePointer(ptr(attatchReceiveIdPtr)).add(0x04) - .writeU32(contactId.length * 2).add(0x04) - .writeU32(contactId.length * 2).add(0x08) - - attatchSendIdPtr = Memory.alloc(senderId.length * 2 + 2) - attatchSendIdPtr.writeUtf16String(senderId) - - attatchSendId = Memory.alloc(0x14) - attatchSendId.writePointer(ptr(attatchSendIdPtr)).add(0x04) - .writeU32(senderId.length * 2).add(0x04) - .writeU32(senderId.length * 2).add(0x08) - - attatchPathPtr = Memory.alloc(path.length * 2 + 2) - attatchPathPtr.writeUtf16String(path) - - attatchPath = Memory.alloc(0x28) - attatchPath.writePointer(attatchPathPtr).add(0x04) - .writeU32(path.length * 2).add(0x04) - .writeU32(path.length * 2).add(0x04) - - attatchEbp11E8 = Memory.alloc(0xBE4) - attatchEbpCC = Memory.alloc(0x14) - attatchEbp368 = Memory.alloc(0x290) - attatchEbp84 = Memory.alloc(0x18) - attatchEAX = Memory.alloc(0x18) - - attatchECX = moduleBaseAddress.add(0x222f178).toInt32() - - //console.log('basename',path.basename(path)) - //return - - /** - * -------------buffer------------------------------- - */ - - Memory.patchCode(attatchAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: attatchAsm }) - cw.putPushfx() - cw.putPushax() - - cw.putMovRegAddress('ecx', attatchEbp11E8) - cw.putCallAddress(moduleBaseAddress.add(0xE1590)) - - cw.putPushU32(-1) - cw.putPushU32(moduleBaseAddress.add(0x1E1B3C0).toInt32()) - cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x4))//11e4 - cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid - // cw.putCallAddress(moduleBaseAddress.add(0x702410))//write appid - - /** - * 78482B8D | 6A FF | push FFFFFFFF | - 78482B8F | 68 B895E979 | push wechatwin.79E995B8 | 79E995B8:L"0" - 78482B94 | 8D8D 48EEFFFF | lea ecx,dword ptr ss:[ebp-11B8] | - 78482B9A | E8 71F83600 | call wechatwin.787F2410 | 此处继续写ebp-11b8 - */ - cw.putPushU32(-1) - cw.putPushU32(moduleBaseAddress.add(0x1DA95B8).toInt32()) - cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x30))//11B8 - cw.putCallAddress(moduleBaseAddress.add(0x702410)) - - cw.putMovRegU32('eax', 0x6) - cw.putMovNearPtrReg(attatchEbp11E8.add(0x80), 'eax')//1168 - cw.putMovRegU32('eax', size)//file size - cw.putMovNearPtrReg(attatchEbp11E8.add(0x108), 'eax')//10e0 - - - cw.putMovRegAddress('eax', sFileName) - cw.putPushReg('eax') - cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x44))//11a4=0x11e8-0x160 - cw.putCallAddress(moduleBaseAddress.add(0x702980))//write filename - - cw.putMovRegAddress('eax', attatchSendId) - cw.putPushReg('eax') - cw.putMovRegAddress('ecx', attatchEbp11E8.add(0x160))//1088=0x11e8-0x160 - cw.putCallAddress(moduleBaseAddress.add(0x702980)) - - - cw.putMovRegAddress('eax', attatchEbpCC) - cw.putPushReg('eax') - cw.putMovRegAddress('ecx', attatchEbp11E8) - cw.putCallAddress(moduleBaseAddress.add(0x617C30)) - - cw.putMovRegAddress('ecx', attatchEbp368) - cw.putCallAddress(moduleBaseAddress.add(0x954F0)) - - cw.putPushU32(-1) - cw.putPushU32((moduleBaseAddress.add(0x1D8F248)).toInt32()) - cw.putMovRegAddress('ecx', attatchEbp84) - cw.putCallAddress(moduleBaseAddress.add(0x701CD0)) - - cw.putMovRegAddress('ecx', attatchPath) - cw.putPushU32(0x6) - cw.putMovRegAddress('edx', attatchEbp11E8.add(0x160))//1088 - //cw.putMovRegAddress('eax',attatchEAX) - cw.putPushReg('ecx') - cw.putPushReg('eax') - - cw.putMovRegAddress('eax', attatchEbpCC) - cw.putPushReg('eax') - - cw.putMovRegAddress('eax', attatchReceiveId) - cw.putPushReg('eax') - - cw.putMovRegAddress('ecx', attatchEbp368) - cw.putCallAddress(moduleBaseAddress.add(0x391F80)) - cw.putAddRegImm('esp', 0x14) - - - cw.putPushU32(moduleBaseAddress.add(0x223EC34).toInt32()) - cw.putPushU32(moduleBaseAddress.add(0x223EC34).toInt32()) - //cw.putMovRegU32('edx',0xAD0001) 两行代码都可以 - cw.putAddRegImm('edx', 0x1) - cw.putMovRegAddress('ecx', attatchEbp368) - cw.putCallAddress(moduleBaseAddress.add(0x392150)) - cw.putAddRegImm('esp', 0x8) - - - - //cw.putMovRegAddress('ecx', attatchEbp368) - //cw.putCallAddress(moduleBaseAddress.add(0x63B4F0)) - // 7B53F178 - //cw.putMovRegU32('ecx', attatchEbpCC.add(0x3c).toInt32())//ebp-90 - //cw.putMovNearPtrReg(attatchEbpCC.add(0x64), 'eax')//ebp-68 - //cw.putAddRegImm('ecx', 0x8) - //cw.putMovRegAddress('eax', attatchEbp368.add(0x64))//ebp-68 - - //cw.putMovRegU32('ecx',attatchECX) - //cw.putPushReg('eax') - //cw.putMovRegAddress('eax', attatchEbpCC.add(0x40))//ebp-8c - //cw.putPushReg('eax') - //cw.putCallAddress(moduleBaseAddress.add(0xC9D30)) - //cw.putCallAddress(moduleBaseAddress.add(0x522590)) - //78483063 | E8 28F51800 | call wechatwin.78612590 | - - - //78483039 | 8D8D 98FCFFFF | lea ecx,dword ptr ss:[ebp-368] | - //7848303F | E8 AC842A00 | call wechatwin.7872B4F0 | - //cw.putMovRegAddress('ecx', attatchEbp368) - //cw.putCallAddress(moduleBaseAddress.add(0x63B4F0)) - - // 78F33099 | 8D8D 34FFFFFF | lea ecx,dword ptr ss:[ebp-CC] | - //78F3309F | E8 AC0FD0FF | call wechatwin.78C34050 | - - //cw.putMovRegAddress('ecx',attatchEbpCC) - //cw.putCallAddress(moduleBaseAddress.add(0x94050)) - - /** - * 78F3307F | 8B4D AC | mov ecx,dword ptr ss:[ebp-54] | - 78F33082 | 8D85 98FCFFFF | lea eax,dword ptr ss:[ebp-368] | - 78F33088 | 50 | push eax | - 78F33089 | E8 82DACFFF | call wechatwin.78C30B10 | - - cw.putMovRegAddress('ecx', attatchEbp54) - cw.putMovRegAddress('eax', attatchEbp368) - cw.putPushReg('eax') - cw.putCallAddress(moduleBaseAddress.add(0x90B10))*/ - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - - }) - - const nativeativeFunction = new NativeFunction(ptr(attatchAsm), 'void', []) - nativeativeFunction() - /*console.log(hexdump(attatchEbp11E8.add(0x80), { - offset: 0, - length: 0x40, - header: true, - ansi: true - }))*/ - //console.log('') - /*console.log(hexdump(attatchEbpCC.add(0x160), { - offset: 0, - length: 0x64, - header: true, - ansi: true - }))*/ - //console.log('-------',attatchEbp1C.readPointer()) - //console.log('-------',attatchEbp1C.add(0x4).readPointer()) - //console.log('-------',attatchEbp1C.add(0x8).readPointer()) -}) - -// 025 -const callLoginQrcodeFunction = ((forceRefresh = false) => { - const json = getQrcodeLoginData() - if (!forceRefresh && json.uuid) { - return - } - - const callAsm = Memory.alloc(Process.pageSize) - const loginWnd = moduleBaseAddress.add(offset.get_login_wnd_offset).readPointer() - - Memory.patchCode(callAsm, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: callAsm }) - cw.putPushfx(); - cw.putPushax(); - - cw.putMovRegAddress('ecx', loginWnd) - cw.putCallAddress(moduleBaseAddress.add(offset.get_qr_login_call_offset)) - - cw.putPopax() - cw.putPopfx() - cw.putRet() - cw.flush() - }) - - const nativeativeFunction = new NativeFunction(ptr(callAsm), 'void', []) - nativeativeFunction() -}) - - -// 026 -const agentReadyCallback = (() => { - const nativeCallback = new NativeCallback(() => { }, 'void', []) - const nativeativeFunction = new NativeFunction(nativeCallback, 'void', []) - - setTimeout(() => { - nativeativeFunction() - }, 500); - return nativeCallback -})() - -// 027 -const SendMiniProgramNativeFunction = ((bg_path_str, send_wxid_str, recv_wxid_str, xmlstr) => { - console.log("------------------------------------------------------"); - var asmCode = Memory.alloc(Process.pageSize); - - var ECX_buf = Memory.alloc(0x300); - var Buf_EAX = Memory.alloc(0x300); - var buf_1 = Memory.alloc(0x300); - var ptr_to_buf_1 = Memory.alloc(0x4).writePointer(buf_1); - var buf_2 = Memory.alloc(0x300); - - // var bg_path_str="C:/aaaa.jpg"; - var bg_path_Ptr = Memory.alloc(bg_path_str.length * 2 + 1) - bg_path_Ptr.writeUtf16String(bg_path_str); - var bg_path_Struct = Memory.alloc(0x14) // returns a NativePointer - bg_path_Struct.writePointer(bg_path_Ptr).add(0x04) - .writeU32(bg_path_str.length * 2).add(0x04) - .writeU32(bg_path_str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0); - - // var send_wxid_str="wxid_4zr616ir6fi122"; - var send_wxid_Ptr = Memory.alloc(send_wxid_str.length * 2 + 1) - send_wxid_Ptr.writeUtf16String(send_wxid_str); - var send_wxid_Struct = Memory.alloc(0x14) // returns a NativePointer - send_wxid_Struct.writePointer(send_wxid_Ptr).add(0x04) - .writeU32(send_wxid_str.length * 2).add(0x04) - .writeU32(send_wxid_str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0); - - // var recv_wxid_str="filehelper"; - var recv_wxid_Ptr = Memory.alloc(recv_wxid_str.length * 2 + 1) - recv_wxid_Ptr.writeUtf16String(recv_wxid_str); - var recv_wxid_Struct = Memory.alloc(0x14) // returns a NativePointer - recv_wxid_Struct.writePointer(recv_wxid_Ptr).add(0x04) - .writeU32(recv_wxid_str.length * 2).add(0x04) - .writeU32(recv_wxid_str.length * 2).add(0x04) - .writeU32(0).add(0x04) - .writeU32(0); - - // vvar pXml=initidStruct('wxid_4zr616ir6fi1220腾讯出行服务|加油代驾公交view330https://mp.weixin.qq.com/mp/waerrpage?appid=wx65cc950f42e8fff1&amp;type=upgrade&amp;upgradetype=3#wechat_redirecthttp://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=200腾讯出行服务|加油代驾公交0gh_ad64296dc8bd@appwx65cc950f42e8fff11http://mmbiz.qpic.cn/mmbiz_png/NM1fK7leWGPaFnMAe95jbg4sZAI3fkEZWHq69CIk6zA00SGARbmsGTbgLnZUXFoRwjROelKicbSp9K34MaZBuuA/640?wx_fmt=png&wxfrom=20002_wx65cc950f42e8fff1_875237370_1644979747_11Window wechat'); - - var pXml = initidStruct(xmlstr) - - console.log(send_wxid_Struct); - console.log(recv_wxid_Struct); - console.log(pXml); - console.log("okkk"); - - console.log("------------------------------------------------------"); - - Memory.patchCode(asmCode, Process.pageSize, code => { - var cw = new X86Writer(code, { pc: asmCode }) - cw.putPushfx(); - cw.putPushax(); - cw.putMovRegReg('ecx', 'ecx'); - cw.putMovRegAddress('ecx', ECX_buf); - cw.putCallAddress(moduleBaseAddress.add(0x69BB0)); //init ecx - - cw.putPushU32(0x21); - - - cw.putPushNearPtr(ptr_to_buf_1); //ptr - cw.putPushU32(bg_path_Struct.toInt32()); - cw.putPushU32(pXml.toInt32()); - cw.putPushU32(recv_wxid_Struct.toInt32()); - - cw.putMovRegAddress('edx', send_wxid_Struct); - cw.putMovRegAddress('ecx', ECX_buf); - cw.putCallAddress(moduleBaseAddress.add(0x2E2420)); - cw.putAddRegImm('esp', 0x14) - - cw.putPushU32(Buf_EAX.toInt32()); - cw.putMovRegAddress('ecx', ECX_buf); - cw.putCallAddress(moduleBaseAddress.add(0x94C10)); - - cw.putPushU32(moduleBaseAddress.add(0x1DCB46C).toInt32()); - cw.putPushU32(moduleBaseAddress.add(0x1DCB46C).toInt32()); - cw.putMovRegAddress('ecx', ECX_buf); - cw.putCallAddress(moduleBaseAddress.add(0x2E2630)); - cw.putAddRegImm('esp', 0x8) - - cw.putPopax(); - cw.putPopfx(); - cw.putRet(); - cw.flush(); - }) - - const nativeativeFunction = new NativeFunction(ptr(asmCode), 'void', []) - nativeativeFunction() - - + // loggedIn = moduleBaseAddress.add(offset.is_logged_in_offset).readU32() + // return !!loggedIn + return true }) \ No newline at end of file diff --git a/src/puppet-xp.ts b/src/puppet-xp.ts index 446ffae..ce4278f 100644 --- a/src/puppet-xp.ts +++ b/src/puppet-xp.ts @@ -108,6 +108,7 @@ class PuppetXp extends PUPPET.Puppet { this.#sidecar = new WeChatSidecar() await attach(this.sidecar) + void this.onLogin() this.sidecar.on('hook', ({ method, args }) => { log.verbose('PuppetXp', 'onHook(%s, %s)', method, JSON.stringify(args)) @@ -141,15 +142,16 @@ class PuppetXp extends PUPPET.Puppet { private async onAgentReady () { log.verbose('PuppetXp', 'onAgentReady()') - const isLoggedIn = await this.sidecar.isLoggedIn() - if (!isLoggedIn) { - await this.sidecar.callLoginQrcode(false) - } + // const isLoggedIn = await this.sidecar.isLoggedIn() + // if (!isLoggedIn) { + // await this.sidecar.callLoginQrcode(false) + // } } private async onLogin () { const selfInfoRaw = JSON.parse(await this.sidecar.getMyselfInfo()) + // console.debug('selfInfoRaw:\n\n\n', selfInfoRaw) const selfInfo: PUPPET.payloads.Contact = { alias: '', avatar: selfInfoRaw.head_img_url, @@ -567,7 +569,7 @@ class PuppetXp extends PUPPET.Puppet { id: roomId, memberIdList: roomMember, ownerId: '', - topic: topic, + topic, } this.roomStore[roomId] = room delete this.contactStore[roomId] @@ -834,7 +836,7 @@ class PuppetXp extends PUPPET.Puppet { } } - if ([PUPPET.types.Message.Video, PUPPET.types.Message.Audio].includes(message?.type || PUPPET.types.Message.Unknown)) { + if ([ PUPPET.types.Message.Video, PUPPET.types.Message.Audio ].includes(message?.type || PUPPET.types.Message.Unknown)) { this.notSupported('Video/`Audio') } return FileBox.fromFile( diff --git a/src/wechat-sidecar.ts b/src/wechat-sidecar.ts index 4b8ec6c..ab02a70 100644 --- a/src/wechat-sidecar.ts +++ b/src/wechat-sidecar.ts @@ -47,7 +47,11 @@ const supportedVersions = { // 'agent-script-3.3.0.115.js', // ), 'utf-8') -let initAgentScript = '' +let initAgentScript = fs.readFileSync(path.join( + codeRoot, + 'src', + 'init-agent-script.js', +), 'utf-8') try { // const wechatVersion = new WeChatVersion() @@ -104,8 +108,8 @@ try { @Sidecar('WeChat.exe', initAgentScript) class WeChatSidecar extends SidecarBody { - @Call(agentTarget('getTestInfoFunction')) - getTestInfo ():Promise { return Ret() } + // @Call(agentTarget('getTestInfoFunction')) + // getTestInfo ():Promise { return Ret() } @Call(agentTarget('getChatroomMemberNickInfoFunction')) getChatroomMemberNickInfo ( @@ -131,10 +135,10 @@ class WeChatSidecar extends SidecarBody { @Call(agentTarget('checkSupportedFunction')) checkSupported ():Promise { return Ret() } - @Call(agentTarget('callLoginQrcodeFunction')) - callLoginQrcode ( - forceRefresh: boolean, - ):Promise { return Ret(forceRefresh) } + // @Call(agentTarget('callLoginQrcodeFunction')) + // callLoginQrcode ( + // forceRefresh: boolean, + // ):Promise { return Ret(forceRefresh) } @Call(agentTarget('getContactNativeFunction')) getContact ():Promise { return Ret() } @@ -181,30 +185,30 @@ class WeChatSidecar extends SidecarBody { @ParamType('int32', 'U32') isMyMsg: number, // add isMyMsg type ) { return Ret(msgType, contactId, text, groupMsgSenderId, xmlContent, isMyMsg) } - @Hook(agentTarget('checkQRLoginNativeCallback')) - checkQRLogin ( - @ParamType('int32', 'U32') status: number, - @ParamType('pointer', 'Utf8String') qrcodeUrl: string, - @ParamType('pointer', 'Utf8String') wxid: string, - @ParamType('pointer', 'Utf8String') avatarUrl: string, - @ParamType('pointer', 'Utf8String') nickname: string, - @ParamType('pointer', 'Utf8String') phoneType: string, - @ParamType('int32', 'U32') phoneClientVer: number, - @ParamType('pointer', 'Utf8String') pairWaitTip: string, - ) { return Ret(status, qrcodeUrl, wxid, avatarUrl, nickname, phoneType, phoneClientVer, pairWaitTip) } - - @Hook(agentTarget('hookLogoutEventCallback')) - logoutEvent ( - @ParamType('int32', 'U32') bySrv: number, - ) { return Ret(bySrv) } - - @Hook(agentTarget('hookLoginEventCallback')) - loginEvent ( - ) { return Ret() } - - @Hook(agentTarget('agentReadyCallback')) - agentReady ( - ) { return Ret() } + // @Hook(agentTarget('checkQRLoginNativeCallback')) + // checkQRLogin ( + // @ParamType('int32', 'U32') status: number, + // @ParamType('pointer', 'Utf8String') qrcodeUrl: string, + // @ParamType('pointer', 'Utf8String') wxid: string, + // @ParamType('pointer', 'Utf8String') avatarUrl: string, + // @ParamType('pointer', 'Utf8String') nickname: string, + // @ParamType('pointer', 'Utf8String') phoneType: string, + // @ParamType('int32', 'U32') phoneClientVer: number, + // @ParamType('pointer', 'Utf8String') pairWaitTip: string, + // ) { return Ret(status, qrcodeUrl, wxid, avatarUrl, nickname, phoneType, phoneClientVer, pairWaitTip) } + + // @Hook(agentTarget('hookLogoutEventCallback')) + // logoutEvent ( + // @ParamType('int32', 'U32') bySrv: number, + // ) { return Ret(bySrv) } + + // @Hook(agentTarget('hookLoginEventCallback')) + // loginEvent ( + // ) { return Ret() } + + // @Hook(agentTarget('agentReadyCallback')) + // agentReady ( + // ) { return Ret() } } From 865ef7fb912cf98e2df58a896ebe509a11836277 Mon Sep 17 00:00:00 2001 From: LuChao Date: Thu, 21 Sep 2023 15:14:07 +0800 Subject: [PATCH 04/38] add 1.3.0 illustrate --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66fdf73..cba69be 100644 --- a/README.md +++ b/README.md @@ -81,9 +81,22 @@ Puppet|xp👍| 登录事件|✅ 依赖协议|Windows +## VERSION SUPPORT + +puppet-xp|wechat| +:---|:---| +>1.11.14|[WeChat-v3.3.0.115](https://github.com/wechaty/wechaty-puppet-xp/releases/download/v0.5/WeChatSetup-v3.3.0.115.exe)| +1.12.x|[WeChat-v3.6.0.18](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.6.0.18/WeChatSetup-3.6.0.18.exe)| +1.3.x|[WeChat-v3.2.2.23](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.9.2.23/WeChatSetup-3.9.2.23.exe)| + ## HISTORY -### main v1.12.0 (November 22, 2022) +### next v1.13.0 (September 21, 2023) + +1. This version start to support WeChat v3.9.2.23,need to update WeChat on your pc to 3.9.2.23 +2. [WeChatSetup-v3.2.2.23.exe](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.9.2.23/WeChatSetup-3.9.2.23.exe) + +### main v1.12.7 (November 22, 2022) 1. This version start to support WeChat v3.6.0.18,need to update WeChat on your pc to 3.6.0.18 2. [WeChatSetup-v3.6.0.18.exe](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.6.0.18/WeChatSetup-3.6.0.18.exe) From 7bfb22c01a9bd5172f2a944af073fe6dee39530b Mon Sep 17 00:00:00 2001 From: LuChao Date: Thu, 21 Sep 2023 15:21:11 +0800 Subject: [PATCH 05/38] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cba69be..1a603cf 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,10 @@ Puppet|xp👍| ## VERSION SUPPORT puppet-xp|wechat| -:---|:---| ->1.11.14|[WeChat-v3.3.0.115](https://github.com/wechaty/wechaty-puppet-xp/releases/download/v0.5/WeChatSetup-v3.3.0.115.exe)| -1.12.x|[WeChat-v3.6.0.18](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.6.0.18/WeChatSetup-3.6.0.18.exe)| -1.3.x|[WeChat-v3.2.2.23](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.9.2.23/WeChatSetup-3.9.2.23.exe)| +|:---|:---| +|>1.11.14|[WeChat-v3.3.0.115](https://github.com/wechaty/wechaty-puppet-xp/releases/download/v0.5/WeChatSetup-v3.3.0.115.exe)| +|1.12.x|[WeChat-v3.6.0.18](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.6.0.18/WeChatSetup-3.6.0.18.exe)| +|1.3.x|[WeChat-v3.2.2.23](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.9.2.23/WeChatSetup-3.9.2.23.exe)| ## HISTORY From e2fe79119b9980c977489f4147d7be513633dcef Mon Sep 17 00:00:00 2001 From: LuChao Date: Thu, 21 Sep 2023 15:22:03 +0800 Subject: [PATCH 06/38] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a603cf..00dff81 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,8 @@ Puppet|xp👍| puppet-xp|wechat| |:---|:---| -|>1.11.14|[WeChat-v3.3.0.115](https://github.com/wechaty/wechaty-puppet-xp/releases/download/v0.5/WeChatSetup-v3.3.0.115.exe)| -|1.12.x|[WeChat-v3.6.0.18](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.6.0.18/WeChatSetup-3.6.0.18.exe)| +|1.11.14|[WeChat-v3.3.0.115](https://github.com/wechaty/wechaty-puppet-xp/releases/download/v0.5/WeChatSetup-v3.3.0.115.exe)| +|1.12.7|[WeChat-v3.6.0.18](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.6.0.18/WeChatSetup-3.6.0.18.exe)| |1.3.x|[WeChat-v3.2.2.23](https://github.com/tom-snow/wechat-windows-versions/releases/download/v3.9.2.23/WeChatSetup-3.9.2.23.exe)| ## HISTORY From eae4d9020d9bec924986df54d215d49f0ca58d27 Mon Sep 17 00:00:00 2001 From: LuChao Date: Thu, 21 Sep 2023 19:53:35 +0800 Subject: [PATCH 07/38] add demo --- bot-qr-code.png | 1387 +++++++++++++++++ .../file/34e2cc942535fadad18f3ec2c2bddb10.png | Bin 0 -> 174747 bytes examples/media/test.gif | Bin 0 -> 53392 bytes examples/media/test.mp4 | Bin 0 -> 353363 bytes examples/media/test.txt | 87 ++ examples/ripe-wechaty.ts | 96 +- package.json | 1 + src/init-agent-script.js | 4 +- src/puppet-xp.ts | 2 +- test.gif | Bin 0 -> 53392 bytes test.mp4 | Bin 0 -> 353363 bytes test.txt | 87 ++ 12 files changed, 1620 insertions(+), 44 deletions(-) create mode 100644 bot-qr-code.png create mode 100644 examples/file/34e2cc942535fadad18f3ec2c2bddb10.png create mode 100644 examples/media/test.gif create mode 100644 examples/media/test.mp4 create mode 100644 examples/media/test.txt create mode 100644 test.gif create mode 100644 test.mp4 create mode 100644 test.txt diff --git a/bot-qr-code.png b/bot-qr-code.png new file mode 100644 index 0000000..803961b --- /dev/null +++ b/bot-qr-code.png @@ -0,0 +1,1387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wechaty/docs/images/bot-qr-code.png at main · wechaty/wechaty · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ Skip to content + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+ + + + +
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + diff --git a/examples/file/34e2cc942535fadad18f3ec2c2bddb10.png b/examples/file/34e2cc942535fadad18f3ec2c2bddb10.png new file mode 100644 index 0000000000000000000000000000000000000000..dbc1175e94a027c070a31811d63201ef33278c71 GIT binary patch literal 174747 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
  • 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

    3sQaR%12cq1oY)*{btVG^J;<4jeJ=6WXx${4w zHdre4YT@a+?v~2{Vto9W@kn>nj;_sJ@Y$))MoFyMM2)fTp+(W@HFc8!^$qD27se564wSZX7_nSI&-y@W^*sYvBp+pmu+vBr8PssUIp?gnJeHZ8f&+2}g zK&P?(m?g5&m~4eL)^fku>?32VyqVH&Xom-?{!=_$XnT}h7e}t(pO2cpXN4>v=|>m; z3rz-aIQ;(K%cZkl=H@TB63(>zYbxn*Jqv;{Pf`D>%*Y;g*&+~j^*i-X;XtL()K@9K%eP9o{#2{ zEVthCLZ7!|FJbMiAAINR<*pzpKd7mJE?vB4<%bg%yDg3%9VoCk$|j+}5R~wUD9o7H z=y)2s#3ZrQG>Z&||5R+VcnS=PI0{M&xGE~jcsPoy^D-M7OJ!Tz+L=I|T^+J5RMoiH zLwH1^gF{%8ICurfv)H9nDZSOJ)%q)BWSWw@yM$w-)o3T^r>AF^7nj%P*SB}4)$~uI zFKdfSFCTwDmq1!VLCqpg!NMUUU*`qFkt68%Tt;GaWp`piN(gL>BbGK<6gb4*?q*)n z=n}EiOb%K+z*1Qx=F|yHv1O&%Y;IRdmy?3>`FtdR0=|w4c1_#Ty{C{e& z)L_K3qD`w5F%v9Tgwb!hcFod>h3FlLt;wVUrR{011IiqPs!F95CTd_7MVml);OkDz z(y6O5@>(RY*kekIEXMK=_qqxDS>|{?*1bLC6dD@yd*070J{oglNK#5tfmkH1sl*W} z7xiFrx1cC7GIZsvpj`Dw4II0n)3Yr5q)T&0aU>2BS8UK(XHpo;=t^Puj~BO7De|-q zX&UOfiVA8s8LJ^6ev0&QiN3ekZd{z65)s)BoJktQ>Y@9w3EG2cmXP>&=aWR+*o3{5 zhQ9R*-ONjc-O7rhO*T^~(Ga?Cbx z07gVNOTwOGGv>HjQ8a0KPH}<;Ia%qoPS$BYA4icjGF;3kexWFs)x6&BUHMg2O_qgX zmF$=ev$1awSd{2PYSjMl0Y5LXIbp$>h(KFQszHd;_v!0#Vm5-MW-$FHL-UNDjHFh2 zQ-ql?&fP4nLJCrux+2=(uwR;Kb=!4rDJxy*lG`9*O0nMgI{;H*w7n?KfD#6jJhGP5 z(N$6z^QBph?rUi)FI)(5a0$|Cv$o)`pEnGeq(vLewB+&y2@;#NJN%)}J| z8wHCeG^Cpu@P+Wk`0L$tpUg0?N9$TEp#ekxK61w(F<3+Aw zh_fyMNX>g#V<5#dxof3rjAjCPKozQf-9VYVsO}LuAwW{);I65Pg8nsWC=^(tl}dBb27v&7dH&2JW%H;u0R91+Gu(DnhN(!T~_E7uI4Q2wkYjr}T zOJv9x3zDjTS@1A7%dp)58&(`Tj2X^-!57`q{-1kc>`+wqZh* zloP_(wU~2W5`Z%KeN_hsxJr*50lZ4!vP+)u(#{cX!1mCxK@! z%8@_K%BoW{_O7+QB4)@2*GQp=yu2YSl|uEZYGZV7$rpvL!s8BHaPFQZJix39MG#>N zwb!CbNbSs~6Myl!>809OA82>ZtLs*mf>mDdi#))S<--N@=U^{sBen6=F)3H)dUuk# z|D)c8M7!QG!jcKdbvLil&hH~Mh6Z+^6FGp3WvC@i*taDcFKM3Fc$?Nob4I70ai-{~ zCJgECG`thOV(HkR2}e6VFdti*cgIKas zniBEhEmEM2k{z>(wKR*COmWm$5SYOSyfWZG6(7dD%F2X_UDyZ#DXS7=^Q|bkEku z8wwcTpx_5trLdY8iIdKPf6G|INa9S{W{;FdP*xcJ5Cp^!Fx4^*1gDghNcO* z#vM?fWn}00zLq(5gztgW!9$3LL)4*WHl6PrEyVHLO}qPSxA`YgoWB3zl{)T)n1Z+S zZb35J246};0){^EdQ7?J#Au>b9Tfj}G_h zKkzgQvY%?j+7GgEL}Zrc_dLMl_UeTJP4V{M~k&N5W7W_ci_;Qk?d9eKJxv!prqrRQ5# z#8?bpD}M3RT)g-9f`Qcsc^E=t;W+5EjBm z&9QD<%pN2O3!C{=JOapIv>_`{S204np_JM@+3hqG1UeK?%;i2{xMEA?RJeg^Z-I0P zKy+$(1MaZNC8MH4uM|#7PFOvtaQcW=(>$fHJymZ796}nfa0KS?o{eY(Vt`kK-r|`4 z4NOE3HQ^SQK4=l&Q%s3`ID}s-^6*9R>@`4NLg|m2cTy@%#~=o#CdcXsgwJAR&}$?T zEOGR?Yg?N<-e}zR6A+wJ;i+B4o&;e8!p?F)Lx%AGqG_vW+ylFK!x`=>+oJJ zklZpEH=sk$k2*Zyjbi))j!+8fY8fB+7PD|+|J+XH|3s64rSx}Qc9vR{5>FdDJW`E= zy~|V?WE{tgmt30)qH9<-Q*K_febG z8Er^B_?SA`gT@P5ELnnz?e+~Q0*9mSW(aRdb=wZdeI_4K#QP9Id@&K$M4ix;;jIXl z3X=)tw_zNCs_NsFYJ6b0-<~SzY;M@;)9|&>PDaS#h7jX9;6?`0Q&{|ZNgKcfktw$r z=6I!d&{(o-Cu2+`Pou|Dy{N(taotf{@dQG8djx%+>bM6Juf1jrDia|Y`J`AUsNj8r z0%Jm|bU#|l%;S-`gJH}?wJ4U(S}IB`^X6$c!Cto_`{_kgjn1P;ld_OUJ%%s znH|Cz#PfBN(JL(C2m(@CrZUmPz$hg3PD882(tTK0s@zR4NcLVzivl+bFbK?iDb)yRb@Di_Vi>N9qBmk@4ibDvj8kAywOk~}7pO9E(@}(-j+NmBZQ_a( z<9wt|jn#E^s7sZ@tQUuPrN@JcC;S397R9{VEDQVd2-jw99O& zj?;1hszo!6(w;+QcJMB5uUX(3=5AX&HasYJk;Z^=>Jm>6j;eVq zs97Jc8GWyDWj9-1s8Ij}S(nDyjp{1=ES!kYg|JL{PO%uEc5s_0WS9t`{Z)4dsG=vS zKf~mI0n~pz=Zu_)c{!52-r_N$X<*VxlNHT>mbNsSXy7pdc4$M$*$M%8qR?#}qq8#N zfU;#I1+Hz4qFFg{kWJnpl{z*o5{atZ5a3^a6-M}g=`Zy~Nmw5!XGVmgr=~i+Z{RF+ z%?~=woIcH$o$e-WJj9drJWoWv)e-8i-0`9 z0Cb*fj`b1rq#}dtJAWW8ZUX#marPR+9SL zC@b%3cGSFWV26HWR0?(is0hb$v!Fb)(0r%bD5?4ZN0YXCM24C@JoMwde41n?qgp zwr#e*+F0!*07Ga5feHwCi1vTSxUZ)oxyP%6pa{#-Ryjx?h28Twm=e*k=?+GUuj9CR z=AZQDAN2-9dOAfqgA!n@kbk<(p4-ZHRwm1VCiUgoOrr^o%7Y1iW+K}sEar@D*7$V# z7%QHP2SQdiE;0T#bvurZn*K#+?X`_A01t#vdBIUQz%`S4ePUSnQsVOpzC*QmuGycLpt_D*Z z8#Q$YH|uLR%ob(Hst<3fw8r>9?@OEBH9Ml^UhOEAnQ2S)FvbB)9KzC3Ku;x4WM@Vma+Ms*=vKyZozD{V?x=d&zifCuZ zZ=*QP85U7>O@&aUx12zKGmXJVcsr|uHpi)I2fsE$UT?nCZ>wZ?cSnA!fIvktXzS8> zL~{w7H!ViJs&bM=fj4&>4rTy)M|AgcftGt?hymsj^B9dgmU z+}*5N-BvvYsmAN-Z`s;#)7iGAOJ9DPIC+P-d28(YTu!rS^u3)xdeYYSMxDVy-5pyd zRN+N!b5|9Yu&{&NortBHHd@5?m)s@;ohgYk5}TbJXyiE|p8=KHX6Wl~-B1{=rbmcPcov0we9pPD^@(e@zt1k_C?pW`+{h~oU-;iB|GUuecdP42 zrt_caQ&?{wn9s;sUvS1u``NEeMh&*$6PDqVDRW8XXZnoG-oj+D5pR0t4ctpz0Mk6W(aOVx#4I=d<602|GqOod z+;5Y8T>hnVqzRyD9!a)$GvBKpK^Sm`k>FRS6BwV zeznUp--}?q{paw5V_%39F2R@m?>xLeb{*)?5s*Vwihte{zMjo#Y>_j1X#^#%6npf) zw(Y)VZP^B**e5W97Ku5h!{);g1K%S1t)l1OS{xq}P{wQOI9+_L1ODCND}h_m3ig;> zZ8+GfOme!#L-eUTn3{Bbvj=~+(cc$PW>5@R+Qfg5O!KDxd^lj|h zKW{zq`x^!I!yf$j$NWYGm($P%D(XggzfxSi@Sw=d$LaN-Zb!Vfh~Ofht`+TlfElW7 z$DLeQJkMMJb;75eEcqL;w*CjUGTZG6xQqYsm$dvJpW-{CaT$4W>2sFcMr#@OqL!<)X=D0fIcAv~9~M-zW;b(0#WSAq6I%m}AdvsDFsYsxo__^_ zF+4cG#k~qZ*>XG?Gm}!%;t2PhZnv|XRYX!B+a|Gv^Nx4<1W<7JH|1Cq9Lm)nWS&WX ze{@qgUl%cz3j5%w`G5(?GIl%=HSb7qR1z_RhWmo+4JPIJj_~O%kz(YCcXJ7h`ICcY zsj~aocOGzZPa+l1-Toc_8~r@l|NQC#Ou${m_qnj@JFA}1%y=B&R}FkY;GcVmIFl60 z^*%05jqyAj$d5L4KJ@u#(sR#Wxeq}5JBt4f>OZ>b4_Ywi!aja-;I#8 zUQquxdT#SDx0!9c`(6D$Lo;0d$8x)8s|9V$DBQVww})TMzp~f+@ASV!pt!;8KYq&6 znsjmk5G8OEV|(W|zaRYlA|RCRMR7w9klTI1O81E$+!%8q%@HVejW95>4iNFe96KchssklURQ09