Python通过COM口调用微信发消息CALL

写在前面

赶在二月底来更新。
WeChat版本:
3.5.0.46
编译环境:
Windows10 Pro
VS 2019 Community

实现思路

思路,之前的文章基本讲过:

  1. 编写32位DLL,添加内联汇编调用微信发消息CALL
  2. 开辟远程进程,在远程进程中开辟远程线程和写入DLL绝对路径
  3. 调用LoadLibrary让微信加载自己的DLL
  4. 将DLL加载进自己创建的exe进程中,计算导出函数的偏移
  5. 以同样的方式,在远程进程中写入数据,创建远程线程调用目标函数
  6. 创建32位ATL简单对象,编写注入、调用、卸载等一系列接口
  7. 注册COM组件
  8. 64/32位Python调用COM组件提供的接口

注入的DLL

部分代码:
发送文本消息:SendText.cpp

#include "pch.h"

#define SendTextCallOffset 0x49BC80;

struct SendTextStruct
{
    DWORD wxid;
    DWORD wxmsg;
};

void SendTextRemote(LPVOID lpParameter) {
    SendTextStruct* rp = (SendTextStruct*)lpParameter;
    wchar_t* wsWxId = (WCHAR*)rp->wxid;
    wchar_t* wsTextMsg = (WCHAR*)rp->wxmsg;
    SendText(wsWxId, wsTextMsg);
}

void __stdcall SendText(wchar_t* wsWxId, wchar_t* wsTextMsg) {
    WxBaseStruct wxWxid(wsWxId);
    WxBaseStruct wxTextMsg(wsTextMsg);
    wchar_t** pWxmsg = &wxTextMsg.buffer;
    char buffer[0x3A8] = { 0 };

    WxString wxNull = { 0 };
    DWORD dllBaseAddress = GetWeChatWinBase();
    DWORD callAddress = dllBaseAddress + SendTextCallOffset;

    __asm {
        lea eax, wxNull;
        push 0x1;
        push eax;
        mov edi, pWxmsg;
        push edi;
        lea edx, wxWxid;
        lea ecx, buffer;
        call callAddress;
        add esp, 0xC;
    }
}

发送图片消息:SendImage.cpp

#include "pch.h"

#define WxSendImageCall1offset (0x02EDDB60 - 0x02E50000)
#define WxSendImageCall2offset (0x03528420 - 0x02E50000)
#define WxSendImageCall3offset (0x032EB690 - 0x02E50000)

struct ImageParamStruct {
	DWORD wxid;
	DWORD imagepath;
};

void SendImageRemote(LPVOID lpParamStruct) {
	ImageParamStruct* params = (ImageParamStruct*)lpParamStruct;
	SendImage((WCHAR*)params->wxid, (WCHAR*)params->imagepath);
}

void __stdcall SendImage(wchar_t* receiver, wchar_t* ImagePath) {
	DWORD WxSendImageCall1 = GetWeChatWinBase() + WxSendImageCall1offset;
	DWORD WxSendImageCall2 = GetWeChatWinBase() + WxSendImageCall2offset;
	DWORD WxSendImageCall3 = GetWeChatWinBase() + WxSendImageCall3offset;
	char nullbuffer[0x50] = { 0 };
	char buffer[0x3A8] = { 0 };
	WxBaseStruct pReceiver(receiver);
	WxBaseStruct pImagePath(ImagePath);
	
	__asm {
		pushad;
		call WxSendImageCall1;
		sub esp, 0x14;
		mov dword ptr[ebp - 0x50], eax;
		mov ecx, esp;
		lea edi, pImagePath;
		push eax;
		call WxSendImageCall2;
		mov ecx, dword ptr[ebp - 0x50];
		lea eax, pReceiver;
		push edi;
		push eax;
		lea eax, buffer;
		push eax;
		call WxSendImageCall3;
		popad;
	}
}

COM组件

部分代码:
注入DLL:InjertDll.cpp

bool InjectDll(DWORD dwId, WCHAR* szPath)//参数1:目标进程PID  参数2:DLL路径
{
    if (!hProcess)
        return 1;
    if (GetWeChatRobotBase() != 0) {
        return 1;
    }

    LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    DWORD dwWriteSize = 0;
    if (pRemoteAddress)
    {
        WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize);
    }
    else {
        return 1;
    }

    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL);
    if (hThread) {
        WaitForSingleObject(hThread, -1);
    }
    else {
        return 1;
    }
    CloseHandle(hThread);
    VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
    return 0;
}

bool Injert(DWORD dwPid,wchar_t* workPath) {
    wchar_t* dllpath = new wchar_t[MAX_PATH];
    swprintf_s(dllpath, MAX_PATH, L"%ws%ws%ws", workPath, L"\\", dllname);
    string name = _com_util::ConvertBSTRToString((BSTR)workPath); 
    if (!isFileExists_stat(name)) {
        MessageBoxA(NULL, name.c_str(), "文件不存在", MB_ICONWARNING);
        return 1;
    }
    bool status = InjectDll(dwPid, dllpath);
    delete[] dllpath;
    dllpath = NULL;
    return status;
}

发送文本消息:SendText.cpp

#include "pch.h"

struct SendTextStruct
{
    DWORD wxid;
    DWORD wxmsg;
};

int SendText(wchar_t* wxid, wchar_t* wxmsg) {
    DWORD WeChatRobotBase = GetWeChatRobotBase();
    DWORD dwId = 0;
    DWORD dwWriteSize = 0;
    SendTextStruct params;
    ZeroMemory(&params, sizeof(params));
    LPVOID wxidaddr = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    LPVOID wxmsgaddr = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    SendTextStruct* paramAndFunc = (SendTextStruct*)::VirtualAllocEx(hProcess, 0, sizeof(SendTextStruct), MEM_COMMIT, PAGE_READWRITE);
    if (!wxidaddr || !wxmsgaddr || !paramAndFunc || !WeChatRobotBase) {
        return 1;
    }
    DWORD dwTId = 0;

    if (wxidaddr)
        WriteProcessMemory(hProcess, wxidaddr, wxid, wcslen(wxid) * 2 + 2, &dwWriteSize);

    if (wxmsgaddr)
        WriteProcessMemory(hProcess, wxmsgaddr, wxmsg, wcslen(wxmsg) * 2 + 2, &dwWriteSize);

    params.wxid = (DWORD)wxidaddr;
    params.wxmsg = (DWORD)wxmsgaddr;

    if (paramAndFunc) {
        if (!::WriteProcessMemory(hProcess, paramAndFunc, &params, sizeof(params), &dwTId))
        {
            return 1;
        }
    }
    else {
        return 1;
    }

    DWORD SendTextRemoteAddr = WeChatRobotBase + SendTextOffset;
    HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)SendTextRemoteAddr, (LPVOID)paramAndFunc, 0, &dwId);
    if (hThread) {
        WaitForSingleObject(hThread, INFINITE);
    }
    else {
        return 1;
    }
    CloseHandle(hThread);
    VirtualFreeEx(hProcess, wxidaddr, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, wxmsgaddr, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, paramAndFunc, 0, MEM_RELEASE);
    return 0;
}

发送图片消息:SendImage.cpp

#include "pch.h"

struct ImageParamStruct {
    DWORD wxid;
    DWORD filepath;
};

int SendImage(wchar_t* wxid, wchar_t* filepath) {
    DWORD WeChatRobotBase = GetWeChatRobotBase();
    DWORD dwId = 0;
    DWORD dwWriteSize = 0;
    ImageParamStruct params;
    ZeroMemory(&params, sizeof(params));
    LPVOID wxidaddr = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    LPVOID filepathaddr = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    ImageParamStruct* paramAndFunc = (ImageParamStruct*)::VirtualAllocEx(hProcess, 0, sizeof(ImageParamStruct), MEM_COMMIT, PAGE_READWRITE);
    if (!wxidaddr || !filepathaddr || !paramAndFunc || !WeChatRobotBase) {
        return 1;
    }
    DWORD dwTId = 0;

    if (wxidaddr)
        WriteProcessMemory(hProcess, wxidaddr, wxid, wcslen(wxid) * 2 + 2, &dwWriteSize);

    if (filepathaddr)
        WriteProcessMemory(hProcess, filepathaddr, filepath, wcslen(filepath) * 2 + 2, &dwWriteSize);

    params.wxid = (DWORD)wxidaddr;
    params.filepath = (DWORD)filepathaddr;

    if (paramAndFunc) {
        if (!::WriteProcessMemory(hProcess, paramAndFunc, &params, sizeof(params), &dwTId))
        {
            return 1;
        }
    }
    else {
        return 1;
    }

    DWORD SendImageRemoteAddr = WeChatRobotBase + SendImageOffset;
    HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)SendImageRemoteAddr, (LPVOID)paramAndFunc, 0, &dwId);
    if (hThread) {
        WaitForSingleObject(hThread, INFINITE);
    }
    else {
        return 1;
    }
    CloseHandle(hThread);
    VirtualFreeEx(hProcess, wxidaddr, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, filepathaddr, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, paramAndFunc, 0, MEM_RELEASE);
    return 0;
}

定义的接口函数:WeChatRobot.cpp

// WeChatRobot.cpp: CWeChatRobot 的实现

#include "pch.h"
#include "WeChatRobot.h"
#include "SendImage.h"
#include "SendText.h"


// CWeChatRobot
/*
* 参数1:`MyWeChatRobot.dll`所在目录
* 参数2:预返回的值,调用时无需提供
*/
STDMETHODIMP CWeChatRobot::CStartRobotService(BSTR workPath,int* __result) {
    *__result = StartRobotService(workPath);
    return S_OK;
}

/*
* 参数1:预返回的值,调用时无需提供
*/
STDMETHODIMP CWeChatRobot::CStopRobotService(int* __result) {
    *__result = StopRobotService();
    return S_OK;
}

/*
* 参数1:接收人wxid
* 参数2:图片绝对路径
* 参数3:预返回的值,调用时无需提供
*/
STDMETHODIMP CWeChatRobot::CSendImage(BSTR wxid, BSTR filepath, int* __result) {
    *__result = SendImage(wxid, filepath);
    return S_OK;
}

/*
* 参数1:接收人wxid
* 参数2:文本消息内容
* 参数3:预返回的值,调用时无需提供
*/
STDMETHODIMP CWeChatRobot::CSendText(BSTR wxid, BSTR wxmsg, int* __result) {
    *__result = SendText(wxid, wxmsg);
    return S_OK;
}

此外,接口还需要在类中和idl中声明:

// 类中的声明
public:

	STDMETHODIMP CStartRobotService(BSTR workPath, int* __result);
	STDMETHODIMP CStopRobotService(int* __result);
	STDMETHODIMP CSendImage(BSTR wxid, BSTR filepath, int* __result);
	STDMETHODIMP CSendText(BSTR wxid, BSTR wxmsg, int* __result);
// idl中的声明
interface IWeChatRobot : IDispatch
{
	[id(1)] HRESULT CStartRobotService([in] BSTR workPath, [out, retval] int* __result);
	[id(2)] HRESULT CStopRobotService([out, retval] int* __result);
	[id(3)] HRESULT CSendImage([in] BSTR wxid, [in] BSTR filepath, [out, retval] int* __result);
	[id(4)] HRESULT CSendText([in] BSTR wxid, [in] BSTR wxmsg, [out, retval] int* __result);
};

注册COM服务

编译ATL项目,会生成一个exe文件,以管理员权限执行下面的命令:

# 注册
CWeChatRobot.exe /regserver
# 卸载
CWeChatRobot.exe /unregserver

Python中的调用

wxRobot.py

import win32com.client as client
import time

class WeChatRobot():
    
    def __init__(self,dllpath):
        # 参数为添加ATL简单对象时填写的progId
        self.robot = client.Dispatch('CWeChatRobot.WeChatRobot')
        self.dllpath = dllpath
        
    def StartService(self):
        return self.robot.CStartRobotService(self.dllpath)
        
    def SendText(self,receiver,msg):
        return self.robot.CSendText(receiver,msg)
        
    def SendImage(self,receiver,imgpath):
        return self.robot.CSendImage(receiver,imgpath)
        
    def StopService(self):
        return self.robot.CStopRobotService()

# MyWeChatRobot.dll path
dllpath = r'D:\CPP\MyWeChatRobot\Debug'
# image full path
imgpath = r"D:\Python\test.png"
# receiver
receiver = 'filehelper'

wx = WeChatRobot(dllpath)
wx.StartService()

for i in range(5):
    wx.SendText(receiver,'测试中文消息')
    wx.SendImage(receiver,imgpath)
    time.sleep(1)

wx.StopService()

资源下载

链接: https://pan.baidu.com/s/17nB6ZDyB0O5aQuLpGR7bzw
提取码: vkq9

声明

本文所有代码和资源仅供学习交流使用,请勿用于非法和商业用途,如因个人行为产生任何法律纠纷,与本人无关。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值