python 微信发送消息,Python发送微信

  python 微信发送消息,Python发送微信

  可以用Python实现微信消息发送功能。如何才能实现?你一定觉得很复杂,但是python的好处是很多人已经把接口打包好了,只需要调用就可以了。今天给大家分享一下用Python发微信的思想代码。让我们看一看。

  00-1010前言C语言发微信消息Python调用不用C编程dll怎么发消息调用我们写的机器码?第一次优化和第二次优化x86/x64调用Jmp指令的区别

  

目录

  原理:Windows反过来,调用通过内联汇编发送消息的函数。

  以下代码PC微信版本为:3.7.0.26,Python使用的32位版本3.8.13。

  如果你只是需要最后一段Python代码,请翻到最后一段看优化部分。

  

前言

  因为C语言本身支持内联汇编,所以编写和发送消息的代码非常简单。您需要找到发送消息的调用并构造参数。

  //微信通用结构

  结构WxBaseStruct

  {

  wchar _ t * buffer

  双字长度;

  DWORD maxLength

  DWORD fill1

  DWORD fill2

  WxBaseStruct(wchar_t* pStr) {

  缓冲区=pStr

  长度=wcs len(pStr);

  maxLength=wcs len(pStr)* 2;

  fill1=0x0

  fill2=0x0

  }

  };

  void send text(wchar _ t * wsTextMsg){

  //发送好友,filehelper是文件传输助手。

  wchar _ t wsw xid[0x 10]=L file helper ;

  wxbase struct wxw xid(wsw xid);

  //发送的消息的内容

  wxbase struct wxTextMsg(wsTextMsg);

  wchar _ t * * pwx msg=wxtextmsg . buffer;

  char buffer[0x3b 0]={ 0 };

  char wxNull[0x 100]={ 0 };

  DWORD dll base address=(DWORD)GetModuleHandleA( we chatwin . dll );

  //发送消息的函数的调用地址

  DWORD callAddress=dll base address0x 521d 30;

  _ _组件{

  lea eax,wxNull

  推0x1

  推eax

  mov edi,pWxmsg

  推送edi

  lea edx,wxWxid

  lea ecx,缓冲;

  调用callAddress

  添加esp,0xC

  }

  }

  如果你对这部分不了解,可以发消息电话到百度pc微信,相关文章很多。基本上可以找到call和call call。

  写好代码,然后打包成dll,注入微信。

  

c语言发微信消息

  原理:用C语言写的dll导出了发送消息的函数,这样就可以通过符号找到SendText的地址,然后通过CreateRemoteThread调用,这也是我在SendText中把wsWxId写死的原因。

  CreateRemoteThread调用的外部函数只能传递一个参数。要传递多个参数,有两种方法:通过在结构中传递;或者写一个汇编函数调用机器码,写入进程空间,然后调用写好的函数。

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

  导入类型

  dll=ctypes。WinDLL(kernel32.dll )

  WriteProcessMemory=dll。内存

  WriteProcessMemory.argtypes=[

  ctypes.c_void_p,

  ctypes.c_void_p,

  ctypes.c_void_p,

  ctypes.c_size_t,

  ctypes。指针(ctypes.c_size_t)

  ]

  WriteProcessM

  emory.restype = ctypes.c_long

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

  

import pymem

  import ctypes

  def start_thread(process_handle, address, params=None):

   调用CreateRemoteThread

   process_handle: 外部进程句柄

   address: 要调用的函数地址

   params: 参数地址

   params = params or 0

   NULL_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_h

  def main(wxpid, content):

   # 获取进程句柄

   process_handle = pymem.process.open(wxpid)

   # 这里通过加载dll(sendText.dll) ,获取到 SendText的地址偏移

   # 当然你也可以直接用查看pe的软件直接看SendText函数的偏移

   ctypes.CDLL(rF:\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 = 18196

   main(wxpid, "Python test!")

  

  

不用c编写dll如何发消息

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

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

  

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

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

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

  

import pymem

  import ctypes

  def 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_len

   pymem.ressources.kernel32.ReadProcessMemory(process_handle, source_addr, buffer, buffer_len, None)

   # 写入buffer.raw内容到src_addr,长度为buffer_len

   pymem.ressources.kernel32.WriteProcessMemory(process_handle, src_addr, buffer.raw, buffer_len, None)

   # 打印内容

   print(buffer.raw.hex())

  if __name__ == "__main__":

   wxpid = 18196

   source_addr = 0x508C1010

   buffer_len = 0x127

   src_addr = 0x508C1D80

   copy_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 time

  def calc_code(calladdr, codeaddr):

   code = calladdr - codeaddr - 5

   hex_code = hex(code & 0xFFFFFFFF)

   return hex_code

  def 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 + addr

   tmp = []

   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):

   调用CreateRemoteThread

   process_handle: 外部进程句柄

   address: 要调用的函数地址

   params: 参数地址

   params = params or 0

   NULL_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_h

  def 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_handle

   process_kernel32_handle = pymem.process.module_from_name(process_handle, "kernel32.dll")

   GetModuleHandleA = process_kernel32_handle.lpBaseOfDll + GetModuleHandleA_Offset

   GetModuleHandleA_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, rC:\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 + 0xDA10

   code_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 = 24600

   process_handle = pymem.process.open(wxpid)

   main(wxpid, "你好")

  

  

第二次优化

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

  见优化篇

  

  

x86/x64 Call Jmp指令区别

  

  • Call指令主要实现对一个函数的调用。Jmp指令主要实现地址的调转。
  • Call指令和Jmp指令的区别

  1:Call指令和Jmp指令的机器码不同。

  2:Call指令会对当前指令的下一条指令的地址进行压栈操作,来实现函数的返回。

   相当于

  

Push eip+5

   Jmp xxxxxxxx

  

  Call指令的二进制形态(机器码)

   1:X86

  Call --- e8/ ff15(但是其他比如 call eax 等是不相同的)

  E8 xxxxxxxx 其中xxxxxxxx是偏移地址

  计算方法:目标地址-当前地址-5 = 偏移地址

  Ff15 xxxxxxxx 其中xxxxxxxx是绝对地址(FF15会对当前的这个绝对地址解*号,也就是绝对地址[目标地址])

  Jmp ---e9 /ff25

  E9 xxxxxxxx其中xxxxxxxx是偏移地址

  计算方法:目标地址-当前地址-5 = 偏移地址

  Ff25 xxxxxxxx其中xxxxxxxx是绝对地址(FF15会对当前的这个绝对地址解*号,也就是绝对地址[目标地址])

  2:X64

  E8 xxxxxxxx 其中xxxxxxxx是偏移地址

  计算方法:目标地址-当前地址-5 = 偏移地址

  Ff15 xxxxxxxx 其中xxxxxxxx是相对地址(FF15会对当前的这个相对地址解*号,也就是相对地址[目标地址])

  Ff25 xxxxxxxx其中xxxxxxxx是相对地址(FF15会对当前的这个相对地址解*号,也就是相对地址[目标地址])

  到此这篇关于Python实现向好友发送微信消息 的文章就介绍到这了,更多相关Python微信消息内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: