写在前面
赶在二月底来更新。
WeChat版本:
3.5.0.46
编译环境:
Windows10 Pro
VS 2019 Community
实现思路
思路,之前的文章基本讲过:
- 编写32位DLL,添加内联汇编调用微信发消息CALL
- 开辟远程进程,在远程进程中开辟远程线程和写入DLL绝对路径
- 调用LoadLibrary让微信加载自己的DLL
- 将DLL加载进自己创建的exe进程中,计算导出函数的偏移
- 以同样的方式,在远程进程中写入数据,创建远程线程调用目标函数
- 创建32位ATL简单对象,编写注入、调用、卸载等一系列接口
- 注册COM组件
- 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(¶ms, 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, ¶ms, 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(¶ms, 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, ¶ms, 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
声明
本文所有代码和资源仅供学习交流使用,请勿用于非法和商业用途,如因个人行为产生任何法律纠纷,与本人无关。