-
Motivation
-
Basic Principles
-
Key Technologies
-
Injection
-
Interception and Spoofing
-
RPC
-
Mixed Programming
-
Specific Implementation
-
Project Structure
-
SDK Logic
-
Applications
-
Weather Reporting Bot
-
Simple Response Bot
-
To Be Continued…
The title might be a bit misleading—this article primarily focuses on introducing a communication channel for WeChat bots, with less emphasis on the bot itself. However, even if it can only respond mechanically, it can still be considered a bot. This article intends to elaborate on five aspects: motivation, basic principles, key technologies, specific implementation, and application examples.
The code is open-source: WeChatFerry[1], and hands-on enthusiasts can directly download the SDK[2] to get started (supports Python!).
The WeChat installation package repository is also open-source, and you can download the corresponding version of the WeChat installation package: WeChatSetup[3].
ℹ️ WeChatFerry is a tool based on PC WeChat. If you don’t have a Windows computer, you can still play around with it using a virtual machine. Reply with WeChatFerry to join the group chat for discussions!
Motivation
Initially, a senior developer had a requirement to send notifications via WeChat group messages. I found ichat, a framework based on the WeChat web version. However, after a few days, my WeChat web version was banned.
This might be because the WeChat web protocol was almost cracked, and later, the WeChat web version became inaccessible. I had no choice but to look for alternatives.
Thanks to the great internet, I finally found another framework based on PC WeChat. However, this framework supported an older version, and when I debugged the project and published it on the server, it failed—my server was a new system, and the old version of WeChat couldn’t be used.
At this point, I had two options: one was to create an image of the development environment and install it on the server to continue using the old version of WeChat; the other was to create a new solution and adapt it to the latest version of WeChat.
Out of love for technology, I chose to create a new solution. The functionalities that have been implemented so far include:
- Getting login status
- Sending text messages (can @ in group chats)
- Sending image messages
- Receiving all types of messages
- Getting contacts (based on memory)
- Friend verification
- Querying the database to get libraries and tables
- Executing SQL
- Supporting Python3
Basic Principles
Essentially, I wrote a tool that “hijacks” WeChat:
- When WeChat receives a message, the tool processes it before WeChat does (before displaying it on the page); after processing, it hands it back to the original processing module;
- When sending a message, it simulates WeChat sending the message, assembles the message body, and calls the WeChat sending message module;
- To get contacts, it traverses a specific memory space;
- For friend verification, it assembles the verification information and calls the WeChat verification module;
- Database-related functions are executed by obtaining the database handle and referencing the sqlite3 interface.

To illustrate, we deploy a spy (Spy.DLL) into WeChat, exchanging messages through telegrams (RPC) and intelligence stations (SDK.DLL) and external agents (C++ applications, Python applications):
- When WeChat receives a message,
Spy.DLLpasses the message toSDK.DLLviaRPC, which then distributes it toC++ applicationsorPython applications; - When
C++ applicationsorPython applicationsneed to send a message,SDK.DLLpasses it toSpy.DLLto “falsely convey the order” to send it out.
So far, there is still a question: how did the spy get in? This requires the use of injection technology. Below, I will introduce several key technical points involved in this project.
Key Technologies
Based on the previous introduction:
Spy.DLLis responsible for interception and spoofing, which requires interception technology (Hook);RPCis responsible for message transmission, involving inter-process communication, and this project uses Remote Procedure Call;SDK.DLL(C++) andPython applications(Python) can communicate, involving mixed programming;- Finally, to inject
Spy.DLL(the spy) into WeChat, injection technology is involved.
This is also why I enjoy this project, as it may be small, but it involves many interesting technical points.
Injection
First, let’s introduce injection technology.
Injection technology is usually associated with malicious software, generally used to execute custom code in the target process. There are many types of injection technology, and this project selects the most classic one: writing the path of Spy.DLL into the virtual address space of the WeChat process, and then creating a remote thread in the WeChat process to load Spy.DLL: 
The reference implementation is as follows:
// 1. Get the target process and allocate space in the target process's memory
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
// 2. Write the dll path into the target process's memory space
if (pRemoteAddress) {
WriteProcessMemory(hProcess, pRemoteAddress, dllPath, wcslen(dllPath) * 2 + 2, &dwWriteSize);
} else {
MessageBox(NULL, L"DLL path write failed", L"InjectDll", 0);
return -1;
}
// 3. Create a remote thread to call LoadLibrary in the target process
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL);
if (hThread) {
WaitForSingleObject(hThread, -1);
} else {
MessageBox(NULL, L"LoadLibrary call failed", L"InjectDll", 0);
return -2;
}
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);
Interception and Spoofing
By using injection technology, we successfully injected Spy.DLL (the spy) into WeChat. The next step is to enable Spy.DLL (the spy) to “hijack” WeChat messages and “falsely convey orders”, which requires interception and spoofing technology.
Interception
Interception technology is commonly referred to as Hook.
To introduce interception technology, we first need to discuss the process from coding to execution. Taking C/C++ as an example, the program generally goes through the following stages:
- Coding
- Pre-compilation, compilation, assembly, linking
- Creating the program process, loading program code and data, creating and mapping virtual address space
- Creating the main thread and running the program
During the compilation stage, the compiler places the instructions in the code into the code segment. When the program is loaded into the virtual address space, the code segment is mapped over. Thus, the functions in our program can be replaced with an address (does this remind you of pointers?).
When WeChat receives a new message that needs to be displayed to the user, it is reasonable to assume that it will call a certain function to display the message. If we replace this function with our own, we can intercept WeChat’s messages. As mentioned earlier, during program execution, a function is merely an address pointer, so we just need to point this address to our own function to achieve interception.
Here’s an example:
# Research shows that when WeChat receives a message, it calls the following function
# Address Machine Code Disassembly
0F7F0F4C E8 FF535400 call WeChatWi.0FD36350
We just need to replace call WeChatWi.0FD36350 in 0F7F0F4C with call our own function to intercept the message. At the same time, to avoid affecting the original functionality, we also need to call WeChatWi.0FD36350 at the end of our own function.
We refer to 0F7F0F4C as the Hook address and WeChatWi.0FD36350 as the Call address. Here, both 0F7F0F4C and 0FD36350 are “relative” addresses—relative to the address of WeChatWin.dll; the address of WeChatWin.dll is called the Base address.
In this example, the base address of WeChatWin.dll is: 0F2A0000, so:
Hook = 0x0F7F0F4C - 0x0F2A0000 = 0x550F4C
Call = 0x0FD36350 - 0x0F2A0000 = 0xA96350
Assuming we wrote a function RecieveMsgHook to handle intercepted messages, the following code can achieve message interception:
// Calculate the addresses of Hook and Call
DWORD hookAddress = g_WeChatWinDllAddr + g_WxCalls.recvMsg.hook;
recvMsgCallAddr = g_WeChatWinDllAddr + g_WxCalls.recvMsg.call;
recvMsgJumpBackAddr = hookAddress + 5;
// Assemble machine code
BYTE jmpCode[5] = { 0 };
jmpCode[0] = 0xE9; // Original function call, machine code E8, now changed to jump E9
// Replace the original WeChatWi.0FD36350 with the address of RecieveMsgHook
*(DWORD *)&jmpCode[1] = (DWORD)RecieveMsgHook - hookAddress - 5;
// 0F7F0F4C E8 FF535400 call WeChatWi.0FD36350
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookAddress, jmpCode, 5, 0);
Spoofing
When we need to send a new message on WeChat, it is reasonable to assume that WeChat will call a certain function to send the message. If we find this function, assemble the sending content, and call it, we can send WeChat messages.
Here’s an example:
0F44FBF3 8D46 38 lea eax,dword ptr ds:[esi+0x38]
0F44FBF6 6A 01 push 0x1
0F44FBF8 50 push eax ; At members
0F44FBF9 57 push edi ; Message
0F44FBFA 8D55 90 lea edx,dword ptr ss:[ebp-0x70] ; Receiver wxid
0F44FBFD 8D8D 50FCFFFF lea ecx,dword ptr ss:[ebp-0x3B0] ; Buffer
# Research shows that when WeChat sends a message, it uses the following function
0F44FC03 E8 28213700 call WeChatWi.0F7C1D30 ; Send Msg
0F44FC08 83C4 0C add esp,0xC
0F44FC0B C645 FC 05 mov byte ptr ss:[ebp-0x4],0x5
0F44FC0F 8B85 70FCFFFF mov eax,dword ptr ss:[ebp-0x390]
0F44FC15 0B85 74FCFFFF or eax,dword ptr ss:[ebp-0x38C]
0F44FC1B 75 10 jnz short WeChatWi.0F44FC2D
Thus, when we need to send a message, we just need to call 0x521D30 (0x0F7C1D30 – 0x0F2A0000).
RPC
Having successfully infiltrated WeChat and being able to intercept messages and “falsely convey orders”, how do we send messages out or bring them in?
WeChat and our application are in different processes. If our application needs to communicate with WeChat, it involves inter-process communication (IPC).
Windows supports several IPC methods, including:
- Clipboard
- COM
- Data Copy
- DDE
- File Mapping
- Mailslots
- Pipes
- RPC
- Windows Sockets
RPC stands for Remote Procedure Call. Here, remote refers to not being in the same process; it can be different processes on the same computer or different computers. Using RPC, high-performance tightly coupled distributed applications can be created.
This project chose RPC, which caused some trouble. However, through RPC, inter-process communication becomes very simple. The RPC tool makes it appear as if the client is directly calling the procedure located in the remote server program. The client and server each have their own address space; that is, each resource has its own memory allocation for the data used by the procedure. The following diagram illustrates the RPC architecture: 
Yet Another Demo for Windows RPC[4] summarizes some uses of RPC on Windows.
Mixed Programming
Now that we can intercept messages and “falsely convey orders”—but only for our own (C++). How can we allow Python to also intercept messages and “falsely convey orders”? This involves mixed programming, specifically Python calling the C++ SDK.
The following is the implementation method described in Microsoft Documentation[5]:
| Approach | Vintage | Representative Users |
|---|---|---|
| C/C++ extension modules for CPython | 1991 | Standard Library |
| PyBind11 (recommended for C++) | 2015 | |
| Cython (recommended for C) | 2007 | gevent, kivy |
| HPy | 2019 | |
| mypyc | 2017 | |
| ctypes | 2003 | oscrypto |
| cffi | 2013 | cryptography, pypy |
| SWIG | 1996 | |
| Boost.Python | 2002 | |
| cppyy | 2017 |
This project initially chose ctypes for its simplicity. However, as the functionality became more complex, ctypes became difficult to manage, so I switched to PyBind11.
Specific Implementation
Having introduced the key technologies, the specific implementation is easier to understand.
Project Structure
WeChatFerry
├── App
├── Rpc
├── SDK
├── SDKpy
└── Spy
App
The specific application can be a chat bot, a scheduled message sender, or a mass messaging bot. This module should be the most interesting part.
Currently, this part only provides a C++ version and a Python version of the interface usage example to introduce how to use the interface:
App.cpp
App.py
Rpc
This mainly defines the RPC interface, using Microsoft’s IDL (Interface Definition Language):
rpc.idl
rpc_memory.cpp
SDK
The SDK layer provides interfaces for the App layer and encapsulates many details. Essentially, it is an RPC Client that transmits the App’s interface calls to the Spy module for execution:
dllmain.cpp
framework.h
injector.cpp
injector.h
rpc_client.cpp
rpc_client.h
sdk.cpp
sdk.h
util.cpp
util.h
SDKpy
This part is the mixed programming part mentioned earlier, implementing the Python client:
sdkpy.cpp
Spy
This part implements the interception of messages and “falsely conveying orders” as previously illustrated, currently implementing:
- Accepting friend requests (
accept_new_friend) - Executing SQL (
exec_sql) - Getting contacts (
get_contacts) - Receiving messages (
receive_msg) - Sending messages (
send_msg)
The source code files are as follows:
accept_new_friend.cpp
accept_new_friend.h
dllmain.cpp
exec_sql.cpp
exec_sql.h
framework.h
get_contacts.cpp
get_contacts.h
load_calls.cpp
load_calls.h
receive_msg.cpp
receive_msg.h
rpc_server.cpp
rpc_server.h
send_msg.cpp
send_msg.h
spy.cpp
spy.h
spy_types.h
SDK Logic
The logic of the SDK is mainly hidden in the initialization phase, while other interfaces can generally be called directly.
The SDK initialization completes:
- Open WeChat (if not already open)
- Inject
Spy.dll - Connect RPC
- Check login status
After initialization, the SDK can be used normally. When exiting the SDK, it only disconnects the RPC connection for convenient use of the SDK next time. The implementation code is as follows:
int WxInitSDK()
{
int status = 0;
unsigned long ulCode = 0;
GetModuleFileName(GetModuleHandle(WECHATSDKDLL), SpyDllPath, MAX_PATH);
PathRemoveFileSpec(SpyDllPath);
PathAppend(SpyDllPath, WECHATINJECTDLL);
if (!PathFileExists(SpyDllPath)) {
return ERROR_FILE_NOT_FOUND;
}
status = OpenWeChat(&WeChatPID);
if (status != 0) {
return status;
}
Sleep(2000); // Wait for WeChat to open
if (InjectDll(WeChatPID, SpyDllPath)) {
return -1;
}
Sleep(1000); // Wait for SPY to be ready
status = RpcConnectServer();
if (status != 0) {
printf("RpcConnectServer: %d\n", status);
return -1;
}
do {
status = RpcIsLogin();
if (status == -1) {
return status;
} else if (status == 1) {
break;
}
Sleep(1000);
} while (1);
return ERROR_SUCCESS;
}
Applications
Initially, the wheel was created out of necessity, but unexpectedly, the need disappeared after the wheel was created, so now I can only make some useless things.
Weather Reporting Bot
Every day at 7 AM, it publishes the weather forecast in the group:
To create the weather reporting bot, I wrote a weather crawler to fetch the weather data. The code is also open-source: WeatherScrapy[6].
Simple Response Bot
Can only answer weather inquiries:

This application uses RASA, and I created some training data for simple training.
To Be Continued…
- Statistics on friend distribution
- Calculating intimacy between friends (like ratio, number of common group chats, chat frequency…)
- Wool group bot
- Clearing group zombies
- ……
Reply with WeChatFerry to join the group chat for discussions!
Related Links
[1]
WeChatFerry: https://gitee.com/lch0821/WeChatFerry
[2]
SDK: https://gitee.com/lch0821/WeChatFerry/releases
[3]
WeChatSetup: https://gitee.com/lch0821/WeChatSetup
[4]
Yet Another Demo for Windows RPC: https://gitee.com/lch0821/RpcDemo
[5]
Microsoft Documentation: https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2022
[6]
WeatherScrapy: https://gitee.com/lch0821/weather-scrapy