目录

  • 前言
    • c语言发微信消息
    • Python调用
    • 不用c编写dll如何发消息
    • 调用我们写入的机器码
    • 第一次优化
    • 第二次优化

前言

原理:Windows逆向,通过内联汇编的形式调用发消息的函数

下面的代码PC微信版本是:3.7.0.26 , python使用的32位的3.8.13版本。

微信一定要和这个版本一样(其他版本需要自己找偏移量),python只需要32位,版本随意。

如果只是需要最后的Python代码请直接翻到最后看 优化篇

c语言发微信消息

因为c语言本身支持内联汇编,所以写发消息的代码很简单,需要找到发消息的call,构造一下参数即可

// 微信通用结构体
struct WxBaseStruct
{wchar_t* buffer;DWORD length;DWORD maxLength;DWORD fill1;DWORD fill2;WxBaseStruct(wchar_t* pStr) {buffer = pStr;length = wcslen(pStr);maxLength = wcslen(pStr) * 2;fill1 = 0x0;fill2 = 0x0;}
};void SendText( wchar_t* wsTextMsg) {// 发送的好友,filehelper是文件传输助手wchar_t wsWxId[0x10] = L"filehelper";WxBaseStruct wxWxid(wsWxId);// 发送的消息内容WxBaseStruct wxTextMsg(wsTextMsg);wchar_t** pWxmsg = &wxTextMsg.buffer;char buffer[0x3B0] = { 0 };char wxNull[0x100] = { 0 };DWORD dllBaseAddress = (DWORD)GetModuleHandleA("WeChatWin.dll");// 发消息的函数call地址DWORD callAddress = dllBaseAddress + 0x521D30;__asm {lea eax, wxNull;push 0x1;push eax;mov edi, pWxmsg;push edi;lea edx, wxWxid;lea ecx, buffer;call callAddress;add esp, 0xC;}
}

这部分不懂的可以百度pc微信发消息call,相关文章很多,基本从找call,调用call都有了。

写好代码然后封装成dll注入到微信即可

Python调用

原理:c语言写的dll将发消息的函数导出,这样就能通过符号找到SendText的地址,然后通过CreateRemoteThread来调用,这也就是为什么我SendText里将wsWxId写死的原因

CreateRemoteThread调用的外部函数只能传递一个参数。要想传递多个参数,两种方式:通过传入结构体的方式;或者写一个汇编函数调用call转机器码后写入进程空间,然后调用这个写入的函数。

另外,为了方便我直接使用pymem库,pip install pymem,也可以自己使用ctypes封装,参考pymem的源码,以WriteProcessMemory 为例

import ctypesdll = ctypes.WinDLL('kernel32.dll')
WriteProcessMemory = dll.WriteProcessMemory
WriteProcessMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_void_p,ctypes.c_size_t,ctypes.POINTER(ctypes.c_size_t)
]
WriteProcessMemory.restype = ctypes.c_long

这个定义的WriteProcessMemory就是pymem.ressources.kernel32.WriteProcessMemory

import pymem
import ctypesdef start_thread(process_handle, address, params=None):'''调用CreateRemoteThreadprocess_handle: 外部进程句柄address: 要调用的函数地址params: 参数地址'''params = params or 0NULL_SECURITY_ATTRIBUTES = ctypes.cast(0, pymem.ressources.structure.LPSECURITY_ATTRIBUTES)thread_h = pymem.ressources.kernel32.CreateRemoteThread(process_handle,NULL_SECURITY_ATTRIBUTES,0,address,params,0,ctypes.byref(ctypes.c_ulong(0)))last_error = ctypes.windll.kernel32.GetLastError()if last_error:pymem.logger.warning('Got an error in start thread, code: %s' % last_error)pymem.ressources.kernel32.WaitForSingleObject(thread_h, -1)return thread_hdef main(wxpid, content):# 获取进程句柄process_handle = pymem.process.open(wxpid)# 这里通过加载dll(sendText.dll) ,获取到 SendText的地址偏移# 当然你也可以直接用查看pe的软件直接看SendText函数的偏移ctypes.CDLL(r'F:\Code\汇编\sendText\Release\sendText.dll')local_sendText_handle = pymem.ressources.kernel32.GetModuleHandleW("sendText.dll")SendText_address = pymem.ressources.kernel32.GetProcAddress(local_sendText_handle, b"SendText")sendText_Offset = SendText_address - local_sendText_handle# 获取到微信进程中sendText.dll的句柄process_sendText_handle = pymem.process.module_from_name(process_handle, "sendText.dll")# 微信进程内SendText的函数地址就等于 sendText.dll的基址加上偏移SendText = process_sendText_handle.lpBaseOfDll + sendText_Offset# 开始构造消息,因为是参数是wchar_t类型的,所以需要编码为utf-16的msg = content.encode('utf-16')# 在微信进程申请一块内存空间,大小为1000个字节address = pymem.memory.allocate_memory(process_handle, 1000)# 输出SendText地址和申请的内存地址print(SendText, "address:", hex(address))# 往申请的内存地址写入我们要发送的消息内容pymem.ressources.kernel32.WriteProcessMemory(process_handle, address, msg, len(msg), None)# 调用CreateRemoteThread发送消息thread_h = start_thread(process_handle, SendText, address)print(thread_h)if __name__ == "__main__":wxpid = 18196main(wxpid, "Python test!")

不用c编写dll如何发消息

用c写dll再用Python来调用dll里的函数太麻烦了,能不能只用Python就实现发消息的功能。

回答这个问题前先看看写的dll里SendText函数的汇编形式

一共有四部分内容,第一部分是地址段,第二部分是机器语言,第三部分是汇编语言,第四部分是注释。

猜测:既然函数就是第二部分的机器语言组成的,那么我直接将第二部分的内容拷贝到另一个地址,是否能正常执行。为了方便和准确,我直接使用代码拷贝了。

首先在sendtext.dll最下面选一块没有被使用的地址(508C1D80)

import pymem
import ctypesdef copy_code(wxpid, source_addr, buffer_len, src_addr):# 获取进程句柄process_handle = pymem.process.open(wxpid)# 申请一块临时内存空间存放读取的机器码buffer = ctypes.create_string_buffer(buffer_len)# 读出source_addr地址里的内容到buffer,长度为buffer_lenpymem.ressources.kernel32.ReadProcessMemory(process_handle, source_addr, buffer, buffer_len, None)# 写入buffer.raw内容到src_addr,长度为buffer_lenpymem.ressources.kernel32.WriteProcessMemory(process_handle, src_addr, buffer.raw, buffer_len, None)# 打印内容print(buffer.raw.hex())if __name__ == "__main__":wxpid = 18196source_addr = 0x508C1010buffer_len = 0x127src_addr = 0x508C1D80copy_code(wxpid, source_addr, buffer_len, src_addr )

0x508C1010就是上面那种汇编图第一段的首地址,buffer_len 就是图的最后一个地址减第一个地址+1的值,0x508C1D80就是我选择的一块空白的空间

复制完成后先不直接调用,先用x64dbg看看翻译的汇编指令是不是一样的。我对比了一下,发现有一点出入,也就是说相同的机器码放在不同的内存地址解释出来的汇编指定是不一样的,主要不同之处在于call指定后面的地址。图中三个地方:508C10DD、508C10F0和508C112E

要想调用成功,首先得清楚为什么这三个call后面的地址不一样。可以看看这个:call、jmp指令地址计算,也就是说E8后面跟的地址是通过call的地址减去E8这条指令所在的地址再减5得出来的

以508C10DD为例,图中显示的汇编是call <sendtext._memset>,后面的其实就是一个地址,在x64dbg里点击这条汇编按空格就可以编辑,看到真实的地址是call 0x508C1D2C

计算表达式:0x508C1D2C-0x508C10DD-5 = 00000C4A,而机器码是 E8 4A0C0000 基本对上了,顺序应该只是大端和小端的问题,这个就不去研究了。这里的5是指E8 4A0C0000的字节数,其实真正的计算公式是call的地址减去当前指令的下一条指令的地址,而下一条指令的地址就是当前指令的地址+当前指令所占字节。就像图中的jne指令则是减2

调用我们写入的机器码

上面说我在0x508C1D80写入的机器码,call后面的地址不对。先手动用x64dbg按空格键将地址修改成和dll中调用原函数的汇编一样,然后再使用Python调用这个地址发消息。只需要把SendText = process_sendText_handle.lpBaseOfDll + sendText_Offset改成SendText = 0x508C1D80

测试是调用成功的,也就是说知道函数机器码的情况下,我们完全可以自己在内存自己构造一个发消息的函数,而不用c语言写dll。

要想实现上面的机器码能完全脱离dll还需要一些操作,因为用x64dbg看到的一些地址前面都带了sendtext.508C1960,说明这个地址是dll里的地址,如果dll不存在了,则这些地址里的内容也没有意义了

还是先处理那三个call, call <sendtext._memset>,回车进到该函数发现是vcruntime140.dll里的memset,那么我把这条指令改成 call vcruntime14.memset应该也是能调用成功的吧,看了下vcruntime14.memset的地址是553DDA10,就改成call 0x553DDA10,试了下,确实是成功的。

508C112E的汇编是call <sendtext.@__security_check_cookie@4> ,回车进入该函数就三条指令

cmp ecx,dword ptr ds:[<___security_cookie>]
bnd jne <sendtext.failure>
bnd ret

是一个判断,成功就ret,失败就jne。所以这就第一条指令是有效的,将508C112E的call指令直接改成cmp ecx,dword ptr ds:[<___security_cookie>] 试试,字节数不一样,所以我就不去改sendtext函数的原地址,我改0x508C1D80的代码,也就是我自己写入的代码,也是成功调用的

图中的jne指令并不需要改,这是相对地址,剩下需要改的就是一些内存地址了。比如508C1023出现的0x508C20AC,这个代码没太看懂。0x508C209C显示是filehelper,也就是我们写死的。用ce搜索utf-16的字符串filehelper, 然后替换这两个地址(508C1023、508C1028)。假设搜索到的地址是010EE490,则把508C209C改成010EE490,508C20AC改成010EE4A0。再试着发下消息,也是成功的,其他几个地址也做类似处理,如果ce没有搜到,就用Python申请,然后写入相应的内容即可

处理完后的机器码写入到WechatWin.dll调用会崩溃,原因可能是<___security_cookie>的地址也是dll中,我百度了下,这是微软的GS编译,用于验证堆栈平衡的,可以在vs2017中项目-属性->C/C+±?代码生成->安全检查->禁用安全检查,就可以关闭掉了。接着生成的汇编代码就不会包含cookie这种东西了

另外写入到WechatWin.dll的vcruntime14.memset的机器码也要改下,做完这些就可以调用成功了。

上面代码有个小错误, msg = content.encode('utf-16')这里应该改成msg = content.encode('utf-16le') 两个的区别请看:https://blog.csdn.net/QQxiaoqiang1573/article/details/84937863

第一次优化

上面折腾了那么多,写入汇编之后还是要手动改vcruntime14.memset的机器码,能不能在写入汇编的时候自动计算机器码呢?上面已经知道了计算公式,那么自动计算应该也不难

期间又遇到两个小问题,GetModuleHandleA的地址也是变化的,需要动态获取。vcruntime140.dll没有被加载,是在注入sendtext.dll后才被加载。

完整代码如下,如果你想在自己电脑上运行,其中有个地方需要改。C:\Software\WeChat\3.7.0.26\vcruntime140.dll这个路径得改成你自己微信3.7.0.26下的vcruntime140.dll,必须要是微信下的vcruntime140.dll,系统的不行,因为偏移不一样。用系统的则需要改0xDA10这个偏移量

import os
import pymem
import ctypes
import timedef calc_code(calladdr, codeaddr):code = calladdr - codeaddr - 5hex_code = hex(code & 0xFFFFFFFF)return hex_codedef convert_addr(addr):if isinstance(addr, int):addr = hex(addr)if addr.startswith("0x") or addr.startswith("0X"):addr = addr[2:]if len(addr) < 8:addr = (8-len(addr))*'0' + addrtmp = []for i in range(0, 8, 2):tmp.append(addr[i:i+2])tmp.reverse()return ''.join(tmp)def start_thread(process_handle, address, params=None):'''调用CreateRemoteThreadprocess_handle: 外部进程句柄address: 要调用的函数地址params: 参数地址'''params = params or 0NULL_SECURITY_ATTRIBUTES = ctypes.cast(0, pymem.ressources.structure.LPSECURITY_ATTRIBUTES)thread_h = pymem.ressources.kernel32.CreateRemoteThread(process_handle,NULL_SECURITY_ATTRIBUTES,0,address,params,0,ctypes.byref(ctypes.c_ulong(0)))last_error = ctypes.windll.kernel32.GetLastError()if last_error:pymem.logger.warning('Got an error in start thread, code: %s' % last_error)pymem.ressources.kernel32.WaitForSingleObject(thread_h, -1)return thread_hdef main(wxpid, content):format_code = '558bec81ecfc040000a1{filehelper10}0f1005{filehelper}8945c466a1{buffer}668945c88d45b48bc866c745d200000f1145b4560f57c08945d457660fd645ca8d5102668b0183c1026685c075f52bcac745e000000000d1f9894dd8c745e4000000008d04098b4d088bd18945dc894de88d7202668b0283c2026685c075f52bd6d1fa8955ec8d5102668b0183c1026685c075f52bcac745f400000000d1f968b00300006a00c745f8000000008d04098945f08d45e88945088d8504fbffff50e8{memset1}68000100008d85b4feffff6a0050e8{memset2}83c41868{wechatwin}ff15{GetModuleHandleA}05301d52008945fc8d85b4feffff6a01508b7d08578d55d48d8d04fbffffff55fc83c40c5f5e8be55dc3'process_handle = pymem.process.open(wxpid)filehelper_address = pymem.memory.allocate_memory(process_handle, 50)text = "filehelper".encode("utf-16le")pymem.ressources.kernel32.WriteProcessMemory(process_handle, filehelper_address, text, len(text), None)filehelper_hex_code = convert_addr(filehelper_address)filehelper10_hex_code = convert_addr(filehelper_address+0x10)buffer_address = pymem.memory.allocate_memory(process_handle, 16)buffer_hex_code = convert_addr(buffer_address)WeChatWin_address = pymem.memory.allocate_memory(process_handle, 100)msg = "WeChatWin.dll".encode("ascii")pymem.ressources.kernel32.WriteProcessMemory(process_handle, WeChatWin_address, msg, len(msg), None)wechatwin_hex_code = convert_addr(WeChatWin_address)ctypes.CDLL('kernel32.dll')local_kernel32_handle = pymem.ressources.kernel32.GetModuleHandleW("kernel32.dll")GetModuleHandleA_address = pymem.ressources.kernel32.GetProcAddress(local_kernel32_handle, b"GetModuleHandleA")GetModuleHandleA_Offset = GetModuleHandleA_address - local_kernel32_handleprocess_kernel32_handle = pymem.process.module_from_name(process_handle, "kernel32.dll")GetModuleHandleA = process_kernel32_handle.lpBaseOfDll + GetModuleHandleA_OffsetGetModuleHandleA_address = pymem.memory.allocate_memory(process_handle, 4)pymem.memory.write_int(process_handle, GetModuleHandleA_address, GetModuleHandleA)GetModuleHandleA_hex_code = convert_addr(GetModuleHandleA_address)process_vcruntime140_handle = pymem.process.module_from_name(process_handle, "vcruntime140.dll")if not process_vcruntime140_handle:pymem.process.inject_dll(process_handle, r'C:\Software\WeChat\3.7.0.26\vcruntime140.dll'.encode("ascii"))process_vcruntime140_handle = pymem.process.module_from_name(process_handle, "vcruntime140.dll")memset = process_vcruntime140_handle.lpBaseOfDll + 0xDA10code_address = pymem.memory.allocate_memory(process_handle, 500)memset1 = convert_addr(calc_code(memset, code_address+0xBE))memset2 = convert_addr(calc_code(memset, code_address+0xD1))hex_code = format_code.format(filehelper10=filehelper10_hex_code, filehelper=filehelper_hex_code, buffer=buffer_hex_code, wechatwin=wechatwin_hex_code, memset1=memset1, memset2=memset2,GetModuleHandleA=GetModuleHandleA_hex_code)hex_code = bytes.fromhex(hex_code)pymem.ressources.kernel32.WriteProcessMemory(process_handle, code_address, hex_code, len(hex_code), None)msg = content.encode('utf-16le')address = pymem.memory.allocate_memory(process_handle, 1000)pymem.ressources.kernel32.WriteProcessMemory(process_handle, address, msg, len(msg), None)print(hex(code_address))# 调用CreateRemoteThread发送消息thread_h = start_thread(process_handle, code_address, address)time.sleep(0.5)pymem.memory.free_memory(process_handle, filehelper_address)pymem.memory.free_memory(process_handle, buffer_address)pymem.memory.free_memory(process_handle, WeChatWin_address)pymem.memory.free_memory(process_handle, code_address)pymem.memory.free_memory(process_handle, GetModuleHandleA_address)if __name__ == "__main__":wxpid = 24600process_handle = pymem.process.open(wxpid)main(wxpid, "你好")

第二次优化

优化主要还有两个点:发送的人不要写死,也可以通过参数传入,这个实现很简单,写死的那个filehelper也是在内存构造的,当然也可以构造任意一个好友的wxid;上面的代码也很麻烦,还要自己拼装十六进程的机器码,能不能只写汇编,然后自动转成机器码呢?先说结果:是可以的,Python就有很多汇编转机器码的库,比如keystone和unicorn等,待我在研究研究

见下片优化篇

用Python发送微信消息给好友相关推荐

  1. 全网最全的Windows下Anaconda2 / Anaconda3里Python语言实现定时发送微信消息给好友或群里(图文详解)...

    不多说,直接上干货! 缘由: (1)最近看到情侣零点送祝福,感觉还是很浪漫的事情,相信有很多人熬夜为了给爱的人送上零点祝福,但是有时等着等着就睡着了或者时间并不是卡的那么准就有点强迫症了,这是也许程序 ...

  2. Python发送微信消息(文字、图片、文件)给指定好友和微信群,零基础可看懂(附源码和教程)

    前言 本示例是调用Windows API模拟发送,用Python调用win32api这个库来调用Windows API模拟人的手动操作来发送消息. 在使用前,请将你微信的窗口设置为在最前面,这样就便于 ...

  3. python 发送微信语音消息_全网最全的Windows下Anaconda2 / Anaconda3里Python语言实现定时发送微信消息给好友或群里(图文详解)...

    不多说,直接上干货! 缘由: (1)最近看到情侣零点送祝福,感觉还是很浪漫的事情,相信有很多人熬夜为了给爱的人送上零点祝福,但是有时等着等着就睡着了或者时间并不是卡的那么准就有点强迫症了,这是也许程序 ...

  4. Python发送微信消息(文字、图片、文件)给指定好友和微信群(调用Win32 API模拟人的手动操作来发送消息)

    本示例是调用Windows API模拟发送,用Python调用win32api这个库来调用Windows API模拟人的手动操作来发送消息. 在使用前,请将你微信的窗口设置为在最前面,这样就便于程序找 ...

  5. 利用Python发送微信消息的方法

    微信是支持用快捷键打开主界面的,这个可以在微信的设置里看到,如下图所示 我们就可以利用这一特性,用Python模拟按快捷键的操作,用来打开微信主界面. 打开主界面之后,使用Ctrl+F,定位到微信的搜 ...

  6. 使用frida发送微信消息给好友

    前言 之前说过怎么用python来发送微信文本消息,原理大概就是构造内存机器码.其实frida也可以做类似操作,构造数据和机器码,然后调用.我就不重复操作了,这里说下另一种方法. 想使用frida来发 ...

  7. python发送微信消息_python 发送QQ或者微信消息

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 废话少说,先上代码: # coding = utf-8 import win32gui import win32api import win32con i ...

  8. python发送微信消息_用python批量发送微信消息

    原博文 2018-09-12 14:09 − 根据用户的备注名来给用户发送特定的消息,在itchat获取的friends列表中,username每次登陆之后都会出现变化. 1 #-*- coding: ...

  9. 用python实现自动化办公------定时发送微信消息

    python自动化办公------定时发送微信消息 身边的一位朋友的真实事迹分享给大家,就暂称他C吧.C是一个英俊潇洒的健身男,经常沾花惹草,桃花运不断,听C说他的微信好友就有5000+,平时回消息都 ...

  10. python自动发送微信-Python自动定时发送微信消息

    如今微信已成为我们日常生活的主要交流工具,利用itchat库,我们可以发送微信消息,如果需要定时自动发送,则需要借助apscheduler库,关于apscheduler库,前一篇转载文章有专门的介绍. ...

最新文章

  1. linux 一些简记
  2. php怎么判断文件在下载,php文件下载显示找不到文件怎么办
  3. 存储过程与自定义函数的区别
  4. 为什么鼠标光标不是一条竖线而是一个小方块
  5. CGLIB代理使用与原理详解
  6. 视频怎么加水印?这里有你想要的答案
  7. 不是“饭饭之交”! 李彦宏丁磊CP乌镇神同步
  8. 沟通书籍排行榜前十名 提高沟通能力的十大书籍推荐
  9. hdu 1705[皮克定理]
  10. Galaxy数据统计平台(二)
  11. 01赵玉荣-03安海莹-04郝玥-实训一
  12. 触屏笔和电容笔哪个好?非常值得入手的电容笔推荐
  13. 【机器学习】深度学习框架是什么?有哪些?如何选择?
  14. 连夜干出来一个自动处理【支付宝交易支付投诉管理系统】,支持多商户
  15. 电商详情页设计小程序开发分享,支持微信转发,可用于增加微信转发,提高转化率
  16. 国产三维gis软件的行业赋能情况
  17. 7z文件格式及其源码的分析
  18. 程序员用学位证吗_没有学位如何成为一名优秀的Java程序员
  19. Happy Birthday To Myself
  20. 沁恒CH583 USB 自定义HID调试记录

热门文章

  1. android9原生体验,原生安卓必备软件 优化类原生ROM体验
  2. 自动关机win10_如何设置电脑自动关机
  3. 【逆向】【Binary Bomb Lab】二进制拆弹的解说
  4. 误删阿里云mysql恢复数据恢复_阿里云数据库表数据误删恢复
  5. Lab: Username enumeration via subtly different responses 通过细微的差别的响应来甄别用户名靶场复盘
  6. ss客户端以及tcp,udp,dns代理ss-tproxy本地安装版--centos7.3 x64以上(7.3-7.6x64测试通过)...
  7. structs2框架学习一(启动过程和各种配置)
  8. 机器学习期末考试满分试卷答案
  9. 畅想物联网未来 | 百度云天工智能物联网沙龙圆满落幕
  10. 技术问答-18 设计模式