Merge pull request #317 from lich0821/refactor

Refactoring
This commit is contained in:
Changhua 2025-02-06 21:12:28 +08:00 committed by GitHub
commit a166aedf4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 2827 additions and 3222 deletions

View File

@ -168,7 +168,7 @@ sdk.WxDestroySDK()
### 调试日志
```c
DbgMsg("ListenMessage"); // 封装的 OutputDebugString
util::dbg_msg("ListenMessage"); // 封装的 OutputDebugString
OutputDebugString(L"ListenMessage\n");
MessageBox(NULL, L"ListenMessage", L"ListenMessage", 0);
```

View File

@ -1,15 +1,18 @@
#pragma once
#ifdef ENABLE_DEBUG_LOG
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#endif
#include <filesystem>
#include <iomanip>
#include <memory>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <sstream>
#include <string>
#include "util.h"
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__)
#define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__)
@ -48,7 +51,7 @@ inline void InitLogger(const std::string &path)
logger = spdlog::rotating_logger_mt(DEFAULT_LOGGER_NAME, filename.string(), DEFAULT_LOGGER_MAX_SIZE,
DEFAULT_LOGGER_MAX_FILES);
} catch (const spdlog::spdlog_ex &ex) {
MessageBox(NULL, String2Wstring(ex.what()).c_str(), L"Init LOGGER ERROR", MB_ICONERROR);
MessageBoxA(NULL, ex.what(), "Init LOGGER ERROR", MB_ICONERROR);
return;
}
@ -59,11 +62,10 @@ inline void InitLogger(const std::string &path)
spdlog::set_level(spdlog::level::debug);
logger->flush_on(spdlog::level::debug);
#else
spdlog::set_level(spdlog::level::info);
logger->flush_on(spdlog::level::info);
#endif
SPDLOG_DEBUG("Logger initialized with default settings.");
LOG_DEBUG("InitLogger with debug level");
}
#ifdef ENABLE_DEBUG_LOG
@ -80,7 +82,7 @@ inline void log_buffer(uint8_t *buffer, size_t len)
}
}
SPDLOG_DEBUG(oss.str());
LOG_DEBUG(oss.str());
}
#endif

View File

@ -1,177 +1,72 @@
#include "Shlwapi.h"
#include "framework.h"
#include "util.h"
#include <codecvt>
#include <filesystem>
#include <locale>
#include <string.h>
#include <optional>
#include <strsafe.h>
#include <tlhelp32.h>
#include <vector>
#include <wchar.h>
#include "framework.h"
#include <Shlwapi.h>
#include <tlhelp32.h>
#include "log.hpp"
#include "util.h"
#pragma comment(lib, "shlwapi")
#pragma comment(lib, "Version.lib")
using namespace std;
namespace fs = std::filesystem;
wstring String2Wstring(string s)
namespace util
{
if (s.empty())
return wstring();
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &s[0], (int)s.size(), NULL, 0);
wstring ws(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, &s[0], (int)s.size(), &ws[0], size_needed);
constexpr char WECHATEXE[] = "WeChat.exe";
constexpr char WECHATWINDLL[] = "WeChatWin.dll";
std::wstring s2w(const std::string &s)
{
if (s.empty()) return std::wstring();
int size_needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast<int>(s.size()), nullptr, 0);
std::wstring ws(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast<int>(s.size()), &ws[0], size_needed);
return ws;
}
string Wstring2String(wstring ws)
std::string w2s(const std::wstring &ws)
{
if (ws.empty())
return string();
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), NULL, 0, NULL, NULL);
string s(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), &s[0], size_needed, NULL, NULL);
if (ws.empty()) return std::string();
int size_needed
= WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), static_cast<int>(ws.size()), nullptr, 0, nullptr, nullptr);
std::string s(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), static_cast<int>(ws.size()), &s[0], size_needed, nullptr, nullptr);
return s;
}
string GB2312ToUtf8(const char *gb2312)
std::string gb2312_to_utf8(const char *gb2312)
{
int size_needed = 0;
if (!gb2312) return "";
size_needed = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
wstring ws(size_needed, 0);
int size_needed = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, nullptr, 0);
std::wstring ws(size_needed, 0);
MultiByteToWideChar(CP_ACP, 0, gb2312, -1, &ws[0], size_needed);
size_needed = WideCharToMultiByte(CP_UTF8, 0, &ws[0], -1, NULL, 0, NULL, NULL);
string s(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &ws[0], -1, &s[0], size_needed, NULL, NULL);
size_needed = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1, nullptr, 0, nullptr, nullptr);
std::string s(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1, &s[0], size_needed, nullptr, nullptr);
return s;
}
static int GetWeChatPath(wchar_t *path)
static DWORD get_wechat_pid()
{
int ret = -1;
HKEY hKey = NULL;
// HKEY_CURRENT_USER\Software\Tencent\WeChat InstallPath = xx
if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, L"Software\\Tencent\\WeChat", &hKey)) {
ret = GetLastError();
return ret;
}
DWORD pid = 0;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) return 0;
DWORD Type = REG_SZ;
DWORD cbData = MAX_PATH * sizeof(WCHAR);
if (ERROR_SUCCESS != RegQueryValueEx(hKey, L"InstallPath", 0, &Type, (LPBYTE)path, &cbData)) {
ret = GetLastError();
goto __exit;
}
if (path != NULL) {
PathAppend(path, WECHAREXE);
}
__exit:
if (hKey) {
RegCloseKey(hKey);
}
return ERROR_SUCCESS;
}
static int GetWeChatWinDLLPath(wchar_t *path)
{
int ret = GetWeChatPath(path);
if (ret != ERROR_SUCCESS) {
return ret;
}
PathRemoveFileSpecW(path);
PathAppendW(path, WECHATWINDLL);
if (!PathFileExists(path)) {
// 微信从大约3.7开始,增加了一层版本目录: [3.7.0.29]
PathRemoveFileSpec(path);
_wfinddata_t findData;
wstring dir = wstring(path) + L"\\[*.*";
intptr_t handle = _wfindfirst(dir.c_str(), &findData);
if (handle == -1) { // 检查是否成功
return -1;
}
wstring dllPath = wstring(path) + L"\\" + findData.name;
wcscpy_s(path, MAX_PATH, dllPath.c_str());
PathAppend(path, WECHATWINDLL);
}
return ret;
}
static bool GetFileVersion(const wchar_t *filePath, wchar_t *version)
{
if (wcslen(filePath) > 0 && PathFileExists(filePath)) {
VS_FIXEDFILEINFO *pVerInfo = NULL;
DWORD dwTemp, dwSize;
BYTE *pData = NULL;
UINT uLen;
dwSize = GetFileVersionInfoSize(filePath, &dwTemp);
if (dwSize == 0) {
return false;
}
pData = new BYTE[dwSize + 1];
if (pData == NULL) {
return false;
}
if (!GetFileVersionInfo(filePath, 0, dwSize, pData)) {
delete[] pData;
return false;
}
if (!VerQueryValue(pData, TEXT("\\"), (void **)&pVerInfo, &uLen)) {
delete[] pData;
return false;
}
UINT64 verMS = pVerInfo->dwFileVersionMS;
UINT64 verLS = pVerInfo->dwFileVersionLS;
UINT64 major = HIWORD(verMS);
UINT64 minor = LOWORD(verMS);
UINT64 build = HIWORD(verLS);
UINT64 revision = LOWORD(verLS);
delete[] pData;
StringCbPrintf(version, 0x20, TEXT("%d.%d.%d.%d"), major, minor, build, revision);
return true;
}
return false;
}
int GetWeChatVersion(wchar_t *version)
{
WCHAR Path[MAX_PATH] = { 0 };
int ret = GetWeChatWinDLLPath(Path);
if (ret != ERROR_SUCCESS) {
return ret;
}
ret = GetFileVersion(Path, version);
return ret;
}
DWORD GetWeChatPid()
{
DWORD pid = 0;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
while (Process32Next(hSnapshot, &pe32)) {
wstring strProcess = pe32.szExeFile;
if (strProcess == WECHAREXE) {
if (pe32.szExeFile == s2w(WECHATEXE)) {
pid = pe32.th32ProcessID;
break;
}
@ -180,168 +75,208 @@ DWORD GetWeChatPid()
return pid;
}
enum class WindowsArchiture { x32, x64 };
static WindowsArchiture GetWindowsArchitecture()
static std::optional<std::string> get_wechat_path()
{
#ifdef _WIN64
return WindowsArchiture::x64;
#else
return WindowsArchiture::x32;
#endif
HKEY hKey;
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Tencent\\WeChat", 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
LOG_ERROR("无法打开注册表项");
return std::nullopt;
}
char path[MAX_PATH] = { 0 };
DWORD type = REG_SZ;
DWORD size = sizeof(path);
if (RegQueryValueExA(hKey, "InstallPath", nullptr, &type, reinterpret_cast<LPBYTE>(path), &size) != ERROR_SUCCESS) {
RegCloseKey(hKey);
LOG_ERROR("无法读取注册表中的 InstallPath");
return std::nullopt;
}
RegCloseKey(hKey);
PathAppendA(path, WECHATEXE);
return std::string(path);
}
BOOL IsProcessX64(DWORD pid)
static std::optional<std::string> get_wechat_win_dll_path()
{
BOOL isWow64 = false;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid);
if (!hProcess)
return false;
BOOL result = IsWow64Process(hProcess, &isWow64);
CloseHandle(hProcess);
if (!result)
return false;
if (isWow64)
return false;
else if (GetWindowsArchitecture() == WindowsArchiture::x32)
return false;
else
return true;
auto wechat_path = get_wechat_path();
if (!wechat_path) {
return std::nullopt;
}
fs::path dll_path = *wechat_path;
dll_path = dll_path.parent_path();
fs::path wechat_dll_path = dll_path / WECHATWINDLL;
if (fs::exists(wechat_dll_path)) { // 尝试直接查找 WeChatWin.dll
return wechat_dll_path.string();
}
// 微信从大约3.7开始,增加了一层版本目录: [3.7.0.29]
std::optional<std::string> found_path;
for (const auto &entry : fs::directory_iterator(dll_path)) {
if (entry.is_directory()) {
fs::path possible_dll = entry.path() / WECHATWINDLL;
if (fs::exists(possible_dll)) {
found_path = possible_dll.string();
break; // 取第一个找到的版本号文件夹
}
}
}
if (!found_path) {
LOG_ERROR("未找到 WeChatWin.dll");
}
return found_path;
}
int OpenWeChat(DWORD *pid)
static std::optional<std::string> get_file_version(const std::string &path)
{
*pid = GetWeChatPid();
if (*pid) {
if (!PathFileExistsA(path.c_str())) {
LOG_ERROR("文件不存在: {}", path);
return std::nullopt;
}
DWORD dummy = 0;
DWORD size = GetFileVersionInfoSizeA(path.c_str(), &dummy);
if (size == 0) {
LOG_ERROR("无法获取文件版本信息大小: {}", path);
return std::nullopt;
}
std::vector<BYTE> buffer(size);
if (!GetFileVersionInfoA(path.c_str(), 0, size, buffer.data())) {
LOG_ERROR("无法获取文件版本信息: {}", path);
return std::nullopt;
}
VS_FIXEDFILEINFO *ver_info = nullptr;
UINT ver_size = 0;
if (!VerQueryValueA(buffer.data(), "\\", reinterpret_cast<LPVOID *>(&ver_info), &ver_size)) {
LOG_ERROR("无法获取文件版本信息: {}", path);
return std::nullopt;
}
return fmt::format("{}.{}.{}.{}", HIWORD(ver_info->dwFileVersionMS), LOWORD(ver_info->dwFileVersionMS),
HIWORD(ver_info->dwFileVersionLS), LOWORD(ver_info->dwFileVersionLS));
}
std::string get_wechat_version()
{
auto dll_path = get_wechat_win_dll_path();
if (!dll_path) {
LOG_ERROR("无法获取 WeChatWin.dll 路径");
return "";
}
auto version = get_file_version(*dll_path);
if (!version) {
LOG_ERROR("无法获取 WeChat 版本信息");
return "";
}
return *version;
}
int open_wechat(DWORD &pid)
{
pid = get_wechat_pid();
if (pid != 0) {
return ERROR_SUCCESS;
}
int ret = -1;
STARTUPINFO si = { sizeof(si) };
WCHAR Path[MAX_PATH] = { 0 };
PROCESS_INFORMATION pi = { 0 };
ret = GetWeChatPath(Path);
if (ERROR_SUCCESS != ret) {
return ret;
auto wechat_path = util::get_wechat_path();
if (!wechat_path) {
LOG_ERROR("获取 WeChat 安装路径失败");
return ERROR_FILE_NOT_FOUND;
}
if (!CreateProcess(NULL, Path, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = {};
std::string command_line = *wechat_path;
if (!CreateProcessA(nullptr, command_line.data(), nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr,
&si, &pi)) {
return GetLastError();
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
*pid = pi.dwProcessId;
pid = pi.dwProcessId;
return ERROR_SUCCESS;
}
size_t GetWstringByAddress(UINT64 addr, wchar_t *buffer, UINT64 buffer_size)
uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr)
{
size_t strLength = GET_DWORD(addr + 8);
if (strLength == 0) {
return 0;
} else if (strLength > buffer_size) {
strLength = buffer_size - 1;
}
uint32_t value = 0;
if (!addr || !hProcess) return value;
wmemcpy_s(buffer, strLength + 1, GET_WSTRING(addr), strLength + 1);
return strLength;
}
string GetStringByAddress(UINT64 addr)
{
size_t strLength = GET_DWORD(addr + 8);
return Wstring2String(wstring(GET_WSTRING(addr), strLength));
}
string GetStringByStrAddr(UINT64 addr)
{
size_t strLength = GET_DWORD(addr + 8);
return strLength ? string(GET_STRING(addr), strLength) : string();
}
string GetStringByWstrAddr(UINT64 addr)
{
size_t strLength = GET_DWORD(addr + 8);
return strLength ? Wstring2String(wstring(GET_WSTRING(addr), strLength)) : string();
}
UINT32 GetMemoryIntByAddress(HANDLE hProcess, UINT64 addr)
{
UINT32 value = 0;
unsigned char data[4] = { 0 };
if (ReadProcessMemory(hProcess, (LPVOID)addr, data, 4, 0)) {
value = data[0] & 0xFF;
value |= ((data[1] << 8) & 0xFF00);
value |= ((data[2] << 16) & 0xFF0000);
value |= ((data[3] << 24) & 0xFF000000);
}
ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(addr), &value, sizeof(value), nullptr);
return value;
}
wstring GetUnicodeInfoByAddress(HANDLE hProcess, UINT64 address)
std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t address)
{
wstring value = L"";
if (!hProcess || !address) return L"";
UINT64 strAddress = GetMemoryIntByAddress(hProcess, address);
UINT64 strLen = GetMemoryIntByAddress(hProcess, address + 0x4);
if (strLen > 500)
return value;
uint64_t str_address = get_memory_int_by_address(hProcess, address);
uint64_t str_len = get_memory_int_by_address(hProcess, address + 0x4);
if (str_len > 500) return L"";
wchar_t cValue[500] = { 0 };
memset(cValue, 0, sizeof(cValue) / sizeof(wchar_t));
if (ReadProcessMemory(hProcess, (LPVOID)strAddress, cValue, (strLen + 1) * 2, 0)) {
value = wstring(cValue);
if (ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(str_address), cValue, (str_len + 1) * sizeof(wchar_t),
nullptr)) {
return std::wstring(cValue);
}
return value;
return L"";
}
void DbgMsg(const char *zcFormat, ...)
void dbg_msg(const char *format, ...)
{
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
if (!format) return;
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
va_list args;
va_start(args, format);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
std::string strText(zc.data(), iLen);
va_list args_copy;
va_copy(args_copy, args);
int len = vsnprintf(nullptr, 0, format, args_copy);
va_end(args_copy);
OutputDebugStringA(strText.c_str());
std::vector<char> buffer(len + 1);
vsnprintf(buffer.data(), buffer.size(), format, args);
va_end(args);
OutputDebugStringA(buffer.data());
}
WxString *NewWxStringFromStr(const string &str) { return NewWxStringFromWstr(String2Wstring(str)); }
WxString *NewWxStringFromWstr(const wstring &ws)
std::unique_ptr<WxString> new_wx_string(const char *str)
{
WxString *p = (WxString *)HeapAlloc(GetProcessHeap(), 0, sizeof(WxString));
wchar_t *pWstring = (wchar_t *)HeapAlloc(GetProcessHeap(), 0, (ws.size() + 1) * 2);
if (p == NULL || pWstring == NULL) {
LOG_ERROR("Out of Memory...");
return NULL;
return new_wx_string(str ? std::string(str) : std::string());
}
std::unique_ptr<WxString> new_wx_string(const std::string &str) { return std::make_unique<WxString>(s2w(str)); }
std::unique_ptr<WxString> new_wx_string(const wchar_t *wstr)
{
return new_wx_string(wstr ? std::wstring(wstr) : std::wstring());
}
std::unique_ptr<WxString> new_wx_string(const std::wstring &wstr) { return std::make_unique<WxString>(wstr); }
std::vector<WxString> parse_wxids(const std::string &wxids)
{
std::vector<WxString> wx_members;
std::wstringstream wss(s2w(wxids));
std::wstring wstr;
while (getline(wss, wstr, L',')) {
wx_members.emplace_back(wstr);
}
wmemcpy(pWstring, ws.c_str(), ws.size() + 1);
p->wptr = pWstring;
p->size = (DWORD)ws.size();
p->capacity = (DWORD)ws.size();
p->ptr = 0;
p->clen = 0;
return p;
return wx_members;
}
} // namespace util

View File

@ -1,41 +1,76 @@
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "spy_types.h"
#define WECHAREXE L"WeChat.exe"
#define WECHATWINDLL L"WeChatWin.dll"
#define WCFSDKDLL L"sdk.dll"
#define WCFSPYDLL L"spy.dll"
#define WCFSPYDLL_DEBUG L"spy_debug.dll"
#define GET_UINT64(addr) ((UINT64) * (UINT64 *)(addr))
#define GET_DWORD(addr) ((DWORD) * (UINT64 *)(addr))
#define GET_QWORD(addr) ((UINT64) * (UINT64 *)(addr))
#define GET_STRING(addr) ((CHAR *)(*(UINT64 *)(addr)))
#define GET_WSTRING(addr) ((WCHAR *)(*(UINT64 *)(addr)))
#define GET_STRING_FROM_P(addr) ((CHAR *)(addr))
#define GET_WSTRING_FROM_P(addr) ((WCHAR *)(addr))
typedef struct PortPath {
namespace util
{
struct PortPath {
int port;
char path[MAX_PATH];
} PortPath_t;
};
DWORD GetWeChatPid();
BOOL IsProcessX64(DWORD pid);
int OpenWeChat(DWORD *pid);
int GetWeChatVersion(wchar_t *version);
size_t GetWstringByAddress(UINT64 address, wchar_t *buffer, UINT64 buffer_size);
UINT32 GetMemoryIntByAddress(HANDLE hProcess, UINT64 address);
std::wstring GetUnicodeInfoByAddress(HANDLE hProcess, UINT64 address);
std::wstring String2Wstring(std::string s);
std::string Wstring2String(std::wstring ws);
std::string GB2312ToUtf8(const char *gb2312);
std::string GetStringByAddress(UINT64 address);
std::string GetStringByStrAddr(UINT64 addr);
std::string GetStringByWstrAddr(UINT64 addr);
void DbgMsg(const char *zcFormat, ...);
WxString *NewWxStringFromStr(const std::string &str);
WxString *NewWxStringFromWstr(const std::wstring &ws);
DWORD get_wechat_pid();
int open_wechat(DWORD &pid);
std::string get_wechat_version();
uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr);
std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t addr);
std::wstring s2w(const std::string &s);
std::string w2s(const std::wstring &ws);
std::string gb2312_to_utf8(const char *gb2312);
void dbg_msg(const char *format, ...);
inline DWORD get_dword(uint64_t addr) { return addr ? *reinterpret_cast<DWORD *>(addr) : 0; }
inline QWORD get_qword(uint64_t addr) { return addr ? *reinterpret_cast<QWORD *>(addr) : 0; }
inline uint64_t get_uint64(uint64_t addr) { return addr ? *reinterpret_cast<uint64_t *>(addr) : 0; }
inline std::string get_p_string(uint64_t addr) { return addr ? std::string(reinterpret_cast<const char *>(addr)) : ""; }
inline std::string get_p_string(uint64_t addr, size_t len)
{
return addr ? std::string(reinterpret_cast<const char *>(addr), len) : "";
}
inline std::wstring get_p_wstring(uint64_t addr)
{
return addr ? std::wstring(reinterpret_cast<const wchar_t *>(addr)) : L"";
}
inline std::wstring get_p_wstring(uint64_t addr, size_t len)
{
return addr ? std::wstring(reinterpret_cast<const wchar_t *>(addr), len) : L"";
}
inline std::string get_pp_string(uint64_t addr)
{
if (!addr) return "";
const char *ptr = *reinterpret_cast<const char **>(addr);
return (ptr && *ptr) ? std::string(ptr) : "";
}
inline std::wstring get_pp_wstring(uint64_t addr)
{
if (!addr) return L"";
const wchar_t *ptr = *reinterpret_cast<const wchar_t **>(addr);
return (ptr && *ptr) ? std::wstring(ptr) : L"";
}
inline std::string get_pp_len_string(uint64_t addr)
{
size_t len = get_dword(addr + 8);
return (addr && len) ? std::string(*reinterpret_cast<const char **>(addr), len) : "";
}
inline std::wstring get_pp_len_wstring(uint64_t addr)
{
size_t len = get_dword(addr + 8);
return (addr && len) ? std::wstring(*reinterpret_cast<const wchar_t **>(addr), len) : L"";
}
inline std::string get_str_by_wstr_addr(uint64_t addr) { return w2s(get_pp_len_wstring(addr)); }
std::unique_ptr<WxString> new_wx_string(const char *str);
std::unique_ptr<WxString> new_wx_string(const wchar_t *wstr);
std::unique_ptr<WxString> new_wx_string(const std::string &str);
std::unique_ptr<WxString> new_wx_string(const std::wstring &wstr);
std::vector<WxString> parse_wxids(const std::string &wxids);
} // namespace util

View File

@ -141,7 +141,6 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry</Com
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\com\log.hpp" />
<ClInclude Include="..\com\util.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="injector.h" />

View File

@ -27,9 +27,6 @@
<ClInclude Include="..\com\util.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\com\log.hpp">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">

View File

@ -1,112 +1,108 @@
#include "framework.h"
#include "psapi.h"
#include <filesystem>
#include <string>
#include "injector.h"
#include "injector.h"
#include "util.h"
#include <filesystem>
#include "psapi.h"
using namespace std;
HMODULE GetTargetModuleBase(HANDLE process, string dll)
static void handle_injection_error(HANDLE process, LPVOID remote_address, const std::string &error_msg)
{
DWORD cbNeeded;
HMODULE moduleHandleList[512];
BOOL ret = EnumProcessModulesEx(process, moduleHandleList, sizeof(moduleHandleList), &cbNeeded, LIST_MODULES_64BIT);
if (!ret) {
MessageBox(NULL, L"获取模块失败", L"GetTargetModuleBase", 0);
MessageBoxA(NULL, error_msg.c_str(), "Error", MB_ICONERROR);
if (remote_address) {
VirtualFreeEx(process, remote_address, 0, MEM_RELEASE);
}
if (process) {
CloseHandle(process);
}
}
HMODULE get_target_module_base(HANDLE process, const string &dll)
{
DWORD needed;
HMODULE modules[512];
if (!EnumProcessModulesEx(process, modules, sizeof(modules), &needed, LIST_MODULES_64BIT)) {
MessageBoxA(NULL, "获取模块失败", "get_target_module_base", 0);
return NULL;
}
if (cbNeeded > sizeof(moduleHandleList)) {
MessageBox(NULL, L"模块数量过多", L"GetTargetModuleBase", 0);
return NULL;
}
DWORD processCount = cbNeeded / sizeof(HMODULE);
char moduleName[32];
for (DWORD i = 0; i < processCount; i++) {
GetModuleBaseNameA(process, moduleHandleList[i], moduleName, 32);
if (!strncmp(dll.c_str(), moduleName, dll.size())) {
return moduleHandleList[i];
DWORD count = needed / sizeof(HMODULE);
char module_name[MAX_PATH];
for (DWORD i = 0; i < count; i++) {
GetModuleBaseNameA(process, modules[i], module_name, sizeof(module_name));
if (!strncmp(dll.c_str(), module_name, dll.size())) {
return modules[i];
}
}
return NULL;
}
HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase)
HANDLE inject_dll(DWORD pid, const string &dll_path, HMODULE *injected_base)
{
HANDLE hThread;
SIZE_T cszDLL = (wcslen(dllPath) + 1) * sizeof(WCHAR);
SIZE_T path_size = dll_path.size() + 1;
// 1. 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL) {
MessageBox(NULL, L"打开进程失败", L"InjectDll", 0);
if (!hProcess) {
MessageBoxA(NULL, "打开进程失败", "inject_dll", 0);
return NULL;
}
// 2. 在目标进程的内存里开辟空间
LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, cszDLL, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteAddress == NULL) {
MessageBox(NULL, L"DLL 路径写入失败", L"InjectDll", 0);
LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, path_size, MEM_COMMIT, PAGE_READWRITE);
if (!pRemoteAddress) {
handle_injection_error(hProcess, NULL, "DLL 路径写入失败");
return NULL;
}
// 3. 把 dll 的路径写入到目标进程的内存空间中
WriteProcessMemory(hProcess, pRemoteAddress, dllPath, cszDLL, NULL);
WriteProcessMemory(hProcess, pRemoteAddress, dll_path.c_str(), path_size, NULL);
// 3. 创建一个远程线程,让目标进程调用 LoadLibrary
HMODULE k32 = GetModuleHandle(L"kernel32.dll");
if (k32 == NULL) {
MessageBox(NULL, L"获取 kernel32 失败", L"InjectDll", 0);
// 4. 创建一个远程线程,让目标进程调用 LoadLibrary
HMODULE k32 = GetModuleHandleA("kernel32.dll");
if (!k32) {
handle_injection_error(hProcess, pRemoteAddress, "获取 kernel32 失败");
return NULL;
}
FARPROC libAddr = GetProcAddress(k32, "LoadLibraryW");
FARPROC libAddr = GetProcAddress(k32, "LoadLibraryA");
if (!libAddr) {
MessageBox(NULL, L"获取 LoadLibrary 失败", L"InjectDll", 0);
handle_injection_error(hProcess, pRemoteAddress, "获取 LoadLibrary 失败");
return NULL;
}
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, pRemoteAddress, 0, NULL);
if (hThread == NULL) {
VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);
MessageBox(NULL, L"CreateRemoteThread 失败", L"InjectDll", 0);
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, pRemoteAddress, 0, NULL);
if (!hThread) {
handle_injection_error(hProcess, pRemoteAddress, "CreateRemoteThread 失败");
return NULL;
}
WaitForSingleObject(hThread, -1);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
*injectedBase = GetTargetModuleBase(hProcess, filesystem::path(Wstring2String(dllPath)).filename().string());
*injected_base = get_target_module_base(hProcess, filesystem::path(dll_path).filename().string());
VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
// CloseHandle(hProcess); // Close when exit
return hProcess;
}
bool EjectDll(HANDLE process, HMODULE dllBase)
bool eject_dll(HANDLE process, HMODULE dll_base)
{
HANDLE hThread = NULL;
// 使目标进程调用 FreeLibrary卸载 DLL
HMODULE k32 = GetModuleHandle(L"kernel32.dll");
if (k32 == NULL) {
MessageBox(NULL, L"获取 kernel32 失败", L"InjectDll", 0);
return NULL;
HMODULE k32 = GetModuleHandleA("kernel32.dll");
if (!k32) {
MessageBoxA(NULL, "获取 kernel32 失败", "eject_dll", 0);
return false;
}
FARPROC libAddr = GetProcAddress(k32, "FreeLibraryAndExitThread");
if (!libAddr) {
MessageBox(NULL, L"获取 FreeLibrary 失败", L"InjectDll", 0);
return NULL;
MessageBoxA(NULL, "获取 FreeLibrary 失败", "eject_dll", 0);
return false;
}
hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, (LPVOID)dllBase, 0, NULL);
if (hThread == NULL) {
MessageBox(NULL, L"FreeLibrary 调用失败!", L"EjectDll", 0);
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, (LPVOID)dll_base, 0, NULL);
if (!hThread) {
MessageBoxA(NULL, "FreeLibrary 调用失败!", "eject_dll", 0);
return false;
}
@ -116,38 +112,34 @@ bool EjectDll(HANDLE process, HMODULE dllBase)
return true;
}
static UINT64 GetFuncOffset(LPCWSTR dllPath, LPCSTR funcName)
static uint64_t get_func_offset(const string &dll_path, const string &func_name)
{
HMODULE dll = LoadLibrary(dllPath);
if (dll == NULL) {
MessageBox(NULL, L"获取 DLL 失败", L"GetFuncOffset", 0);
HMODULE dll = LoadLibraryA(dll_path.c_str());
if (!dll) {
MessageBoxA(NULL, "获取 DLL 失败", "get_func_offset", 0);
return 0;
}
LPVOID absAddr = GetProcAddress(dll, funcName);
UINT64 offset = (UINT64)absAddr - (UINT64)dll;
LPVOID absAddr = GetProcAddress(dll, func_name.c_str());
uint64_t offset = reinterpret_cast<uint64_t>(absAddr) - reinterpret_cast<uint64_t>(dll);
FreeLibrary(dll);
return offset;
}
bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPDWORD ret)
bool call_dll_func(HANDLE process, const string &dll_path, HMODULE dll_base, const string &func_name, DWORD *ret)
{
UINT64 offset = GetFuncOffset(dllPath, funcName);
if (offset == 0) {
return false;
uint64_t offset = get_func_offset(dll_path, func_name);
if (offset == 0 || offset > (UINT64_MAX - reinterpret_cast<uint64_t>(dll_base))) {
return false; // 避免溢出
}
UINT64 pFunc = (UINT64)dllBase + GetFuncOffset(dllPath, funcName);
if (pFunc <= (UINT64)dllBase) {
return false;
}
uint64_t pFunc = reinterpret_cast<uint64_t>(dll_base) + offset;
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, NULL, 0, NULL);
if (hThread == NULL) {
if (!hThread) {
return false;
}
WaitForSingleObject(hThread, INFINITE);
if (ret != NULL) {
if (ret) {
GetExitCodeThread(hThread, ret);
}
@ -155,35 +147,32 @@ bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcNa
return true;
}
bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz,
LPDWORD ret)
bool call_dll_func_ex(HANDLE process, const string &dll_path, HMODULE dll_base, const string &func_name,
LPVOID parameter, size_t size, DWORD *ret)
{
UINT64 offset = GetFuncOffset(dllPath, funcName);
if (offset == 0) {
return false;
uint64_t offset = get_func_offset(dll_path, func_name);
if (offset == 0 || offset > (UINT64_MAX - reinterpret_cast<uint64_t>(dll_base))) {
return false; // 避免溢出
}
UINT64 pFunc = (UINT64)dllBase + GetFuncOffset(dllPath, funcName);
if (pFunc <= (UINT64)dllBase) {
uint64_t pFunc = reinterpret_cast<uint64_t>(dll_base) + offset;
LPVOID pRemoteAddress = VirtualAllocEx(process, NULL, size, MEM_COMMIT, PAGE_READWRITE);
if (!pRemoteAddress) {
MessageBoxA(NULL, "申请内存失败", "call_dll_func_ex", 0);
return false;
}
LPVOID pRemoteAddress = VirtualAllocEx(process, NULL, sz, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteAddress == NULL) {
MessageBox(NULL, L"申请内存失败", L"CallDllFuncEx", 0);
return NULL;
}
WriteProcessMemory(process, pRemoteAddress, parameter, sz, NULL);
WriteProcessMemory(process, pRemoteAddress, parameter, size, NULL);
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pRemoteAddress, 0, NULL);
if (hThread == NULL) {
VirtualFree(pRemoteAddress, 0, MEM_RELEASE);
MessageBox(NULL, L"远程调用失败", L"CallDllFuncEx", 0);
if (!hThread) {
VirtualFreeEx(process, pRemoteAddress, 0, MEM_RELEASE);
MessageBoxA(NULL, "远程调用失败", "call_dll_func_ex", 0);
return false;
}
WaitForSingleObject(hThread, INFINITE);
VirtualFree(pRemoteAddress, 0, MEM_RELEASE);
if (ret != NULL) {
VirtualFreeEx(process, pRemoteAddress, 0, MEM_RELEASE);
if (ret) {
GetExitCodeThread(hThread, ret);
}

View File

@ -1,9 +1,11 @@
#pragma once
#include <string>
#include "framework.h"
HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase);
bool EjectDll(HANDLE process, HMODULE dllBase);
bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, DWORD *ret);
bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz,
DWORD *ret);
HANDLE inject_dll(DWORD pid, const std::string &dll_path, HMODULE *injected_base);
bool eject_dll(HANDLE process, HMODULE dll_base);
bool call_dll_func(HANDLE process, const std::string &dll_path, HMODULE dll_base, const std::string &func, DWORD *ret);
bool call_dll_func_ex(HANDLE process, const std::string &dll_path, HMODULE dll_base, const std::string &func,
LPVOID parameter, size_t size, DWORD *ret);

View File

@ -1,4 +1,5 @@
#include "framework.h"
#include "sdk.h"
#include <chrono>
#include <filesystem>
#include <fstream>
@ -6,55 +7,57 @@
#include <process.h>
#include <sstream>
#include <thread>
#include "framework.h"
#include <tlhelp32.h>
#include "injector.h"
#include "sdk.h"
#include "util.h"
static BOOL injected = false;
static bool injected = false;
static HANDLE wcProcess = NULL;
static HMODULE spyBase = NULL;
static std::wstring spyDllPath;
static std::string spyDllPath;
constexpr char DISCLAIMER_FILE[] = ".license_accepted.flag";
constexpr char DISCLAIMER_TEXT_FILE[] = "DISCLAIMER.md";
constexpr char WCFSDKDLL[] = "sdk.dll";
constexpr char WCFSPYDLL[] = "spy.dll";
constexpr char WCFSPYDLL_DEBUG[] = "spy_debug.dll";
static std::optional<std::wstring> ReadDisclaimerText(const char *filePath)
constexpr std::string_view DISCLAIMER_FLAG = ".license_accepted.flag";
constexpr std::string_view DISCLAIMER_TEXT_FILE = "DISCLAIMER.md";
static std::optional<std::string> read_disclaimer_text(const std::string &path)
{
std::ifstream file(filePath, std::ios::binary);
std::ifstream file(path, std::ios::binary);
if (!file.is_open()) {
return std::nullopt; // 文件打开失败
}
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return String2Wstring(content);
return std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
}
static bool ShowDisclaimer()
static bool show_disclaimer()
{
if (std::filesystem::exists(DISCLAIMER_FILE)) {
if (std::filesystem::exists(DISCLAIMER_FLAG)) {
return true;
}
std::optional<std::wstring> disclaimerTextOpt = ReadDisclaimerText(DISCLAIMER_TEXT_FILE);
if (!disclaimerTextOpt.has_value() || disclaimerTextOpt->empty()) {
MessageBox(NULL, L"免责声明文件为空或读取失败。", L"错误", MB_ICONERROR);
auto disclaimerTextOpt = read_disclaimer_text(std::string(DISCLAIMER_TEXT_FILE));
if (!disclaimerTextOpt || disclaimerTextOpt->empty()) {
MessageBoxA(NULL, "免责声明文件为空或读取失败。", "错误", MB_ICONERROR);
return false;
}
std::wstring disclaimerText = *disclaimerTextOpt;
int result = MessageBox(NULL, disclaimerText.c_str(), L"免责声明", MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2);
int result
= MessageBoxA(NULL, disclaimerTextOpt->c_str(), "免责声明", MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2);
if (result == IDCANCEL) {
MessageBox(NULL, L"您拒绝了免责声明,程序将退出。", L"提示", MB_ICONINFORMATION);
MessageBoxA(NULL, "您拒绝了免责声明,程序将退出。", "提示", MB_ICONINFORMATION);
return false;
}
std::ofstream flagFile(DISCLAIMER_FILE, std::ios::out | std::ios::trunc);
std::ofstream flagFile(std::string(DISCLAIMER_FLAG), std::ios::out | std::ios::trunc);
if (!flagFile) {
MessageBox(NULL, L"无法创建协议标志文件。", L"错误", MB_ICONERROR);
MessageBoxA(NULL, "无法创建协议标志文件。", "错误", MB_ICONERROR);
return false;
}
flagFile << "User accepted the license agreement.";
@ -62,82 +65,79 @@ static bool ShowDisclaimer()
return true;
}
static std::wstring GetDllPath(bool debug)
static std::string get_dll_path(bool debug)
{
WCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName(GetModuleHandle(WCFSDKDLL), buffer, MAX_PATH);
char buffer[MAX_PATH] = { 0 };
GetModuleFileNameA(GetModuleHandleA(WCFSDKDLL), buffer, MAX_PATH);
std::filesystem::path path(buffer);
path.remove_filename(); // 移除文件名,保留目录路径
path.remove_filename(); // 只保留目录路径
path /= debug ? WCFSPYDLL_DEBUG : WCFSPYDLL;
if (!std::filesystem::exists(path)) {
MessageBox(NULL, path.c_str(), L"文件不存在", MB_ICONERROR);
return L"";
MessageBoxA(NULL, path.string().c_str(), "文件不存在", MB_ICONERROR);
return "";
}
return path.wstring();
return path.string();
}
int WxInitSDK(bool debug, int port)
{
if (!ShowDisclaimer()) {
if (!show_disclaimer()) {
exit(-1); // 用户拒绝协议,退出程序
}
int status = 0;
DWORD wcPid = 0;
spyDllPath = GetDllPath(debug);
spyDllPath = get_dll_path(debug);
if (spyDllPath.empty()) {
return ERROR_FILE_NOT_FOUND; // DLL 文件路径不存在
}
status = OpenWeChat(&wcPid);
status = util::open_wechat(wcPid);
if (status != 0) {
MessageBox(NULL, L"打开微信失败", L"WxInitSDK", 0);
MessageBoxA(NULL, "打开微信失败", "WxInitSDK", 0);
return status;
}
if (!IsProcessX64(wcPid)) {
MessageBox(NULL, L"只支持 64 位微信", L"WxInitSDK", 0);
return -1;
}
std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待微信打开
wcProcess = InjectDll(wcPid, spyDllPath.c_str(), &spyBase);
wcProcess = inject_dll(wcPid, spyDllPath, &spyBase);
if (wcProcess == NULL) {
MessageBox(NULL, L"注入失败", L"WxInitSDK", 0);
MessageBoxA(NULL, "注入失败", "WxInitSDK", 0);
return -1;
}
PortPath_t pp = { 0 };
pp.port = port;
sprintf_s(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str());
if (!CallDllFuncEx(wcProcess, spyDllPath.c_str(), spyBase, "InitSpy", (LPVOID)&pp, sizeof(PortPath_t), NULL)) {
MessageBox(NULL, L"初始化失败", L"WxInitSDK", 0);
return -1;
}
injected = true;
return 0;
util::PortPath pp = { 0 };
pp.port = port;
snprintf(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str());
status = -3; // TODO: 统一错误码
bool success = call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath),
(DWORD *)&status);
if (!success || status != 0) {
WxDestroySDK();
}
return status;
}
int WxDestroySDK()
{
if (!injected) {
return 1; // 未注入
}
if (!call_dll_func(wcProcess, spyDllPath, spyBase, "CleanupSpy", NULL)) {
return -1;
}
if (!CallDllFunc(wcProcess, spyDllPath.c_str(), spyBase, "CleanupSpy", NULL)) {
if (!eject_dll(wcProcess, spyBase)) {
return -2;
}
if (!EjectDll(wcProcess, spyBase)) {
return -3; // TODO: Unify error codes
}
injected = false;
return 0;
}

View File

@ -247,19 +247,20 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry</Co
<ClInclude Include="..\rpc\pb_util.h" />
<ClInclude Include="..\rpc\proto\wcf.pb.h" />
<ClInclude Include="..\smc\codec.h" />
<ClInclude Include="chatroom_mgmt.h" />
<ClInclude Include="funcs.h" />
<ClInclude Include="exec_sql.h" />
<ClInclude Include="chatroom_manager.h" />
<ClInclude Include="misc_manager.h" />
<ClInclude Include="database_executor.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="contact_mgmt.h" />
<ClInclude Include="receive_msg.h" />
<ClInclude Include="contact_manager.h" />
<ClInclude Include="message_handler.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rpc_helper.h" />
<ClInclude Include="rpc_server.h" />
<ClInclude Include="send_msg.h" />
<ClInclude Include="message_sender.h" />
<ClInclude Include="spy.h" />
<ClInclude Include="spy_types.h" />
<ClInclude Include="sqlite3.h" />
<ClInclude Include="user_info.h" />
<ClInclude Include="userinfo_manager.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\com\util.cpp" />
@ -268,16 +269,16 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry</Co
<ClCompile Include="..\rpc\nanopb\pb_encode.c" />
<ClCompile Include="..\rpc\pb_util.cpp" />
<ClCompile Include="..\rpc\proto\wcf.pb.c" />
<ClCompile Include="chatroom_mgmt.cpp" />
<ClCompile Include="funcs.cpp" />
<ClCompile Include="chatroom_manager.cpp" />
<ClCompile Include="misc_manager.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="exec_sql.cpp" />
<ClCompile Include="contact_mgmt.cpp" />
<ClCompile Include="receive_msg.cpp" />
<ClCompile Include="database_executor.cpp" />
<ClCompile Include="contact_manager.cpp" />
<ClCompile Include="message_handler.cpp" />
<ClCompile Include="rpc_server.cpp" />
<ClCompile Include="send_msg.cpp" />
<ClCompile Include="message_sender.cpp" />
<ClCompile Include="spy.cpp" />
<ClCompile Include="user_info.cpp" />
<ClCompile Include="userinfo_manager.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\rpc\proto\wcf.proto" />

View File

@ -24,16 +24,16 @@
<ClInclude Include="rpc_server.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="exec_sql.h">
<ClInclude Include="database_executor.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="contact_mgmt.h">
<ClInclude Include="contact_manager.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="receive_msg.h">
<ClInclude Include="message_handler.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="send_msg.h">
<ClInclude Include="message_sender.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="spy.h">
@ -63,16 +63,16 @@
<ClInclude Include="..\rpc\pb_types.h">
<Filter>nnrpc</Filter>
</ClInclude>
<ClInclude Include="chatroom_mgmt.h">
<ClInclude Include="chatroom_manager.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="user_info.h">
<ClInclude Include="userinfo_manager.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="funcs.h">
<ClInclude Include="misc_manager.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="sqlite3.h">
@ -87,6 +87,9 @@
<ClInclude Include="..\com\log.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="rpc_helper.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@ -95,16 +98,16 @@
<ClCompile Include="rpc_server.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="exec_sql.cpp">
<ClCompile Include="database_executor.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="contact_mgmt.cpp">
<ClCompile Include="contact_manager.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="receive_msg.cpp">
<ClCompile Include="message_handler.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="send_msg.cpp">
<ClCompile Include="message_sender.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="spy.cpp">
@ -125,13 +128,13 @@
<ClCompile Include="..\rpc\pb_util.cpp">
<Filter>nnrpc</Filter>
</ClCompile>
<ClCompile Include="chatroom_mgmt.cpp">
<ClCompile Include="chatroom_manager.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="user_info.cpp">
<ClCompile Include="userinfo_manager.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="funcs.cpp">
<ClCompile Include="misc_manager.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="..\com\util.cpp">

View File

@ -0,0 +1,115 @@
#pragma execution_character_set("utf-8")
#include "chatroom_manager.h"
#include "log.hpp"
#include "pb_util.h"
#include "rpc_helper.h"
#include "util.h"
using namespace std;
extern QWORD g_WeChatWinDllAddr;
namespace chatroom
{
#define OS_GET_CHATROOM_MGR 0x1B83BD0
#define OS_ADD_MEMBERS 0x2155100
#define OS_DELETE_MEMBERS 0x2155740
#define OS_INVITE_MEMBERS 0x2154AE0
using get_chatroom_mgr_t = QWORD (*)();
using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD);
using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD);
using invite_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD);
static vector<WxString> parse_wxids(const string &wxids)
{
vector<WxString> wx_members;
wstringstream wss(util::s2w(wxids));
wstring wstr;
while (getline(wss, wstr, L',')) {
wx_members.emplace_back(wstr);
}
return wx_members;
}
int add_chatroom_member(const string &roomid, const string &wxids)
{
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return -1;
}
get_chatroom_mgr_t get_chatroom_mgr
= reinterpret_cast<get_chatroom_mgr_t>(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR);
add_member_to_chatroom_t add_members
= reinterpret_cast<add_member_to_chatroom_t>(g_WeChatWinDllAddr + OS_ADD_MEMBERS);
vector<WxString> wx_members = parse_wxids(wxids);
auto wx_roomid = util::new_wx_string(roomid);
QWORD p_members = reinterpret_cast<QWORD>(&wx_members.front());
return static_cast<int>(add_members(get_chatroom_mgr(), p_members, reinterpret_cast<QWORD>(wx_roomid.get()), 0));
}
int del_chatroom_member(const string &roomid, const string &wxids)
{
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return -1;
}
get_chatroom_mgr_t get_chatroom_mgr
= reinterpret_cast<get_chatroom_mgr_t>(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR);
del_member_from_chatroom_t del_members
= reinterpret_cast<del_member_from_chatroom_t>(g_WeChatWinDllAddr + OS_DELETE_MEMBERS);
vector<WxString> wx_members = parse_wxids(wxids);
auto wx_roomid = util::new_wx_string(roomid);
QWORD p_members = reinterpret_cast<QWORD>(&wx_members.front());
return static_cast<int>(del_members(get_chatroom_mgr(), p_members, reinterpret_cast<QWORD>(wx_roomid.get())));
}
int invite_chatroom_member(const string &roomid, const string &wxids)
{
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return -1;
}
invite_member_to_chatroom_t invite_members
= reinterpret_cast<invite_member_to_chatroom_t>(g_WeChatWinDllAddr + OS_INVITE_MEMBERS);
vector<WxString> wx_members = parse_wxids(wxids);
auto wx_roomid = util::new_wx_string(roomid);
QWORD p_members = reinterpret_cast<QWORD>(&wx_members.front());
return static_cast<int>(invite_members(reinterpret_cast<QWORD>(wx_roomid.get()->wptr), p_members,
reinterpret_cast<QWORD>(wx_roomid.get()), 0));
}
bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len)
{
const std::string wxids = m.wxids;
const std::string roomid = m.roomid;
return fill_response<Functions_FUNC_ADD_ROOM_MEMBERS>(
out, len, [&](Response &rsp) { rsp.msg.status = add_chatroom_member(roomid, wxids); });
}
bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len)
{
const std::string wxids = m.wxids;
const std::string roomid = m.roomid;
return fill_response<Functions_FUNC_DEL_ROOM_MEMBERS>(
out, len, [&](Response &rsp) { rsp.msg.status = del_chatroom_member(roomid, wxids); });
}
bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len)
{
const std::string wxids = m.wxids;
const std::string roomid = m.roomid;
return fill_response<Functions_FUNC_INV_ROOM_MEMBERS>(
out, len, [&](Response &rsp) { rsp.msg.status = invite_chatroom_member(roomid, wxids); });
}
} // namespace chatroom

View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
#include "wcf.pb.h"
namespace chatroom
{
// 添加成员到群聊
int add_chatroom_member(const std::string &roomid, const std::string &wxids);
// 从群聊中移除成员
int del_chatroom_member(const std::string &roomid, const std::string &wxids);
// 邀请成员加入群聊
int invite_chatroom_member(const std::string &roomid, const std::string &wxids);
// RPC 方法
bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len);
bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len);
bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len);
} // namespace chatroom

View File

@ -1,113 +0,0 @@
#include "framework.h"
#include <sstream>
#include <vector>
#include "chatroom_mgmt.h"
#include "log.hpp"
#include "util.h"
using namespace std;
extern QWORD g_WeChatWinDllAddr;
#define OS_GET_CHATROOM_MGR 0x1B83BD0
#define OS_ADD_MEMBERS 0x2155100
#define OS_DELETE_MEMBERS 0x2155740
#define OS_INVITE_MEMBERS 0x2154AE0
typedef QWORD (*GetChatRoomMgr_t)();
typedef QWORD (*AddMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*DelMemberFromChatRoom_t)(QWORD, QWORD, QWORD);
typedef QWORD (*InviteMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD);
int AddChatroomMember(string roomid, string wxids)
{
int status = -1;
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return status;
}
GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR);
AddMemberToChatRoom_t AddMembers = (AddMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_ADD_MEMBERS);
vector<wstring> vMembers;
vector<WxString> vWxMembers;
wstringstream wss(String2Wstring(wxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vMembers.push_back(wstr);
WxString wxMember(vMembers.back());
vWxMembers.push_back(wxMember);
}
QWORD temp[2] = { 0 };
WxString *pWxRoomid = NewWxStringFromStr(roomid);
QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start;
QWORD mgr = GetChatRoomMgr();
status = (int)AddMembers(mgr, pMembers, (QWORD)pWxRoomid, (QWORD)temp);
return status;
}
int DelChatroomMember(string roomid, string wxids)
{
int status = -1;
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return status;
}
GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR);
DelMemberFromChatRoom_t DelMembers = (DelMemberFromChatRoom_t)(g_WeChatWinDllAddr + OS_DELETE_MEMBERS);
vector<wstring> vMembers;
vector<WxString> vWxMembers;
wstringstream wss(String2Wstring(wxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vMembers.push_back(wstr);
WxString wxMember(vMembers.back());
vWxMembers.push_back(wxMember);
}
WxString *pWxRoomid = NewWxStringFromStr(roomid);
QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start;
QWORD mgr = GetChatRoomMgr();
status = (int)DelMembers(mgr, pMembers, (QWORD)pWxRoomid);
return status;
}
int InviteChatroomMember(string roomid, string wxids)
{
int status = -1;
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return status;
}
InviteMemberToChatRoom_t InviteMembers = (InviteMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_INVITE_MEMBERS);
vector<wstring> vMembers;
vector<WxString> vWxMembers;
wstringstream wss(String2Wstring(wxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vMembers.push_back(wstr);
WxString wxMember(vMembers.back());
vWxMembers.push_back(wxMember);
}
QWORD temp[2] = { 0 };
wstring wsRoomid = String2Wstring(roomid);
WxString *pWxRoomid = NewWxStringFromWstr(wsRoomid);
QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start;
status = (int)InviteMembers((QWORD)wsRoomid.c_str(), pMembers, (QWORD)pWxRoomid, (QWORD)temp);
return status;
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <string>
int AddChatroomMember(std::string roomid, std::string wxids);
int DelChatroomMember(std::string roomid, std::string wxids);
int InviteChatroomMember(std::string roomid, std::string wxids);

View File

@ -0,0 +1,224 @@
#pragma execution_character_set("utf-8")
#include "contact_manager.h"
#include "log.hpp"
#include "pb_util.h"
#include "rpc_helper.h"
#include "util.h"
using namespace std;
extern QWORD g_WeChatWinDllAddr;
namespace contact
{
#define OS_GET_CONTACT_MGR 0x1B417A0
#define OS_GET_CONTACT_LIST 0x219ED10
#define OS_CONTACT_BIN 0x200
#define OS_CONTACT_BIN_LEN 0x208
#define OS_CONTACT_WXID 0x10
#define OS_CONTACT_CODE 0x30
#define OS_CONTACT_REMARK 0x80
#define OS_CONTACT_NAME 0xA0
#define OS_CONTACT_GENDER 0x0E
#define OS_CONTACT_STEP 0x6A8
using get_contact_mgr_t = QWORD (*)();
using get_contact_list_t = QWORD (*)(QWORD, QWORD);
#define FEAT_LEN 5
static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 };
static const uint8_t FEAT_PROVINCE[FEAT_LEN] = { 0xE2, 0xEA, 0xA8, 0xD1, 0x18 };
static const uint8_t FEAT_CITY[FEAT_LEN] = { 0x1D, 0x02, 0x5B, 0xBF, 0x18 };
static QWORD find_mem(QWORD start, QWORD end, const void *target, size_t len)
{
uint8_t *p = reinterpret_cast<uint8_t *>(start);
while (reinterpret_cast<QWORD>(p) < end) {
if (memcmp(p, target, len) == 0) {
return reinterpret_cast<QWORD>(p);
}
p++;
}
return 0;
}
static string get_cnt_string(QWORD start, QWORD end, const uint8_t *feat, size_t len)
{
QWORD pfeat = find_mem(start, end, feat, len);
if (pfeat == 0) {
return "";
}
DWORD lfeat = util::get_dword(pfeat + len);
if (lfeat <= 2) {
return "";
}
return util::w2s(util::get_p_wstring(pfeat + FEAT_LEN + 4, lfeat));
}
vector<RpcContact_t> get_contacts()
{
vector<RpcContact_t> contacts;
get_contact_mgr_t func_get_contact_mgr
= reinterpret_cast<get_contact_mgr_t>(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR);
get_contact_list_t func_get_contact_list
= reinterpret_cast<get_contact_list_t>(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST);
QWORD mgr = func_get_contact_mgr();
QWORD addr[3] = { 0 };
if (func_get_contact_list(mgr, reinterpret_cast<QWORD>(addr)) != 1) {
LOG_ERROR("get_contacts failed");
return contacts;
}
QWORD pstart = addr[0];
QWORD pend = addr[2];
while (pstart < pend) {
RpcContact_t cnt;
QWORD pbin = util::get_qword(pstart + OS_CONTACT_BIN);
QWORD lenbin = util::get_dword(pstart + OS_CONTACT_BIN_LEN);
cnt.wxid = util::get_str_by_wstr_addr(pstart + OS_CONTACT_WXID);
cnt.code = util::get_str_by_wstr_addr(pstart + OS_CONTACT_CODE);
cnt.remark = util::get_str_by_wstr_addr(pstart + OS_CONTACT_REMARK);
cnt.name = util::get_str_by_wstr_addr(pstart + OS_CONTACT_NAME);
cnt.country = get_cnt_string(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN);
cnt.province = get_cnt_string(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN);
cnt.city = get_cnt_string(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN);
cnt.gender = (pbin == 0) ? 0 : static_cast<DWORD>(*(uint8_t *)(pbin + OS_CONTACT_GENDER));
contacts.push_back(cnt);
pstart += OS_CONTACT_STEP;
}
return contacts;
}
int accept_new_friend(const std::string &v3, const std::string &v4, int scene)
{
int success = -1;
#if 0
DWORD accept_new_friend_call1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1;
DWORD accept_new_friend_call2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2;
DWORD accept_new_friend_call3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3;
DWORD accept_new_friend_call4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4;
char buffer[0x40] = { 0 };
char nullbuffer[0x3CC] = { 0 };
LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene);
wstring ws_v3 = util::s2w(v3);
wstring ws_v4 = util::s2w(v4);
WxString wx_v3(ws_v3);
WxString wx_v4(ws_v4);
__asm {
pushad;
pushfd;
lea ecx, buffer;
call accept_new_friend_call1;
mov esi, 0x0;
mov edi, scene;
push esi;
push edi;
sub esp, 0x14;
mov ecx, esp;
lea eax, wx_v4;
push eax;
call accept_new_friend_call2;
sub esp, 0x8;
push 0x0;
lea eax, nullbuffer;
push eax;
lea eax, wx_v3;
push eax;
lea ecx, buffer;
call accept_new_friend_call3;
mov success, eax;
lea ecx, buffer;
call accept_new_friend_call4;
popfd;
popad;
}
#endif
return success; // 成功返回 1
}
RpcContact_t get_contact_by_wxid(const string &wxid)
{
RpcContact_t contact;
#if 0
char buff[0x440] = { 0 };
wstring ws_wxid = util::s2w(wxid);
WxString pri(ws_wxid);
DWORD contact_mgr_addr = g_WeChatWinDllAddr + 0x75A4A0;
DWORD get_contact_addr = g_WeChatWinDllAddr + 0xC04E00;
DWORD free_contact_addr = g_WeChatWinDllAddr + 0xEA7880;
__asm {
PUSHAD
PUSHFD
CALL contact_mgr_addr
LEA ECX,buff
PUSH ECX
LEA ECX,pri
PUSH ECX
MOV ECX,EAX
CALL get_contact_addr
POPFD
POPAD
}
contact.wxid = wxid;
contact.code = util::get_str_by_wstr_addr(reinterpret_cast<DWORD>(buff) + g_WxCalls.contact.wxCode);
contact.remark = util::get_str_by_wstr_addr(reinterpret_cast<DWORD>(buff) + g_WxCalls.contact.wxRemark);
contact.name = util::get_str_by_wstr_addr(reinterpret_cast<DWORD>(buff) + g_WxCalls.contact.wxName);
contact.gender = util::get_dword(reinterpret_cast<DWORD>(buff) + 0x148);
__asm {
PUSHAD
PUSHFD
LEA ECX,buff
CALL free_contact_addr
POPFD
POPAD
}
#endif
return contact;
}
bool rpc_get_contacts(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_CONTACTS>(out, len, [](Response &rsp) {
vector<RpcContact_t> contacts = get_contacts();
rsp.msg.contacts.contacts.funcs.encode = encode_contacts;
rsp.msg.contacts.contacts.arg = &contacts;
});
}
bool rpc_get_contact_info(const string &wxid, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_CONTACT_INFO>(out, len, [&](Response &rsp) {
vector<RpcContact_t> contacts = { get_contact_by_wxid(wxid) };
rsp.msg.contacts.contacts.funcs.encode = encode_contacts;
rsp.msg.contacts.contacts.arg = &contacts;
});
}
bool rpc_accept_friend(const Verification &v, uint8_t *out, size_t *len)
{
const string v3 = v.v3;
const string v4 = v.v4;
int scene = v.scene;
return fill_response<Functions_FUNC_ACCEPT_FRIEND>(
out, len, [&](Response &rsp) { rsp.msg.status = accept_new_friend(v3, v4, scene); });
}
} // namespace contact

View File

@ -0,0 +1,30 @@
#pragma once
#include <string>
#include <vector>
#include "wcf.pb.h"
#include "pb_types.h"
namespace contact
{
// 获取所有联系人
std::vector<RpcContact_t> get_contacts();
// 根据 wxid 获取联系人信息
RpcContact_t get_contact_by_wxid(const std::string &wxid);
// 接受好友请求
int accept_new_friend(const std::string &v3, const std::string &v4, int scene);
// 发送好友请求
// int add_friend_by_wxid(const std::string &wxid, const std::string &msg);
// RPC 方法
bool rpc_get_contacts(uint8_t *out, size_t *len);
bool rpc_get_contact_info(const std::string &wxid, uint8_t *out, size_t *len);
bool rpc_accept_friend(const Verification &v, uint8_t *out, size_t *len);
} // namespace contact

View File

@ -1,192 +0,0 @@
#pragma execution_character_set("utf-8")
#include "contact_mgmt.h"
#include "log.hpp"
#include "util.h"
using namespace std;
extern QWORD g_WeChatWinDllAddr;
#define OS_GET_CONTACT_MGR 0x1B417A0
#define OS_GET_CONTACT_LIST 0x219ED10
#define OS_CONTACT_BIN 0x200
#define OS_CONTACT_BIN_LEN 0x208
#define OS_CONTACT_WXID 0x10
#define OS_CONTACT_CODE 0x30
#define OS_CONTACT_REMARK 0x80
#define OS_CONTACT_NAME 0xA0
#define OS_CONTACT_GENDER 0x0E
#define OS_CONTACT_STEP 0x6A8
typedef QWORD (*GetContactMgr_t)();
typedef QWORD (*GetContactList_t)(QWORD, QWORD);
#define FEAT_LEN 5
static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 };
static const uint8_t FEAT_PROVINCE[FEAT_LEN] = { 0xE2, 0xEA, 0xA8, 0xD1, 0x18 };
static const uint8_t FEAT_CITY[FEAT_LEN] = { 0x1D, 0x02, 0x5B, 0xBF, 0x18 };
static QWORD FindMem(QWORD start, QWORD end, const void *target, size_t len)
{
uint8_t *p = (uint8_t *)start;
while ((QWORD)p < end) {
if (memcmp((void *)p, target, len) == 0) {
return (QWORD)p;
}
p++;
}
return 0;
}
static string GetCntString(QWORD start, QWORD end, const uint8_t *feat, size_t len)
{
QWORD pfeat = FindMem(start, end, feat, len);
if (pfeat == 0) {
return "";
}
DWORD lfeat = GET_DWORD(pfeat + len);
if (lfeat <= 2) {
return "";
}
return Wstring2String(wstring(GET_WSTRING_FROM_P(pfeat + FEAT_LEN + 4), lfeat));
}
vector<RpcContact_t> GetContacts()
{
vector<RpcContact_t> contacts;
GetContactMgr_t funcGetContactMgr = (GetContactMgr_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR);
GetContactList_t funcGetContactList = (GetContactList_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST);
QWORD mgr = funcGetContactMgr();
QWORD addr[3] = { 0 };
if (funcGetContactList(mgr, (QWORD)addr) != 1) {
LOG_ERROR("GetContacts failed");
return contacts;
}
QWORD pstart = (QWORD)addr[0];
QWORD pend = (QWORD)addr[2];
while (pstart < pend) {
RpcContact_t cnt;
QWORD pbin = GET_QWORD(pstart + OS_CONTACT_BIN);
QWORD lenbin = GET_DWORD(pstart + OS_CONTACT_BIN_LEN);
cnt.wxid = GetStringByWstrAddr(pstart + OS_CONTACT_WXID);
cnt.code = GetStringByWstrAddr(pstart + OS_CONTACT_CODE);
cnt.remark = GetStringByWstrAddr(pstart + OS_CONTACT_REMARK);
cnt.name = GetStringByWstrAddr(pstart + OS_CONTACT_NAME);
cnt.country = GetCntString(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN);
cnt.province = GetCntString(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN);
cnt.city = GetCntString(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN);
if (pbin == 0) {
cnt.gender = 0;
} else {
cnt.gender = (DWORD) * (uint8_t *)(pbin + OS_CONTACT_GENDER);
}
contacts.push_back(cnt);
pstart += OS_CONTACT_STEP;
}
return contacts;
}
#if 0
int AcceptNewFriend(string v3, string v4, int scene)
{
int success = 0;
DWORD acceptNewFriendCall1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1;
DWORD acceptNewFriendCall2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2;
DWORD acceptNewFriendCall3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3;
DWORD acceptNewFriendCall4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4;
char buffer[0x40] = { 0 };
char nullbuffer[0x3CC] = { 0 };
LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene);
wstring wsV3 = String2Wstring(v3);
wstring wsV4 = String2Wstring(v4);
WxString wxV3(wsV3);
WxString wxV4(wsV4);
__asm {
pushad;
pushfd;
lea ecx, buffer;
call acceptNewFriendCall1;
mov esi, 0x0;
mov edi, scene;
push esi;
push edi;
sub esp, 0x14;
mov ecx, esp;
lea eax, wxV4;
push eax;
call acceptNewFriendCall2;
sub esp, 0x8;
push 0x0;
lea eax, nullbuffer;
push eax;
lea eax, wxV3;
push eax;
lea ecx, buffer;
call acceptNewFriendCall3;
mov success, eax;
lea ecx, buffer;
call acceptNewFriendCall4;
popfd;
popad;
}
return success; // 成功返回 1
}
/*没啥用,非好友获取不到*/
RpcContact_t GetContactByWxid(string wxid)
{
RpcContact_t contact;
char buff[0x440] = { 0 };
wstring wsWxid = String2Wstring(wxid);
WxString pri(wsWxid);
DWORD contact_mgr_addr = g_WeChatWinDllAddr + 0x75A4A0;
DWORD get_contact_addr = g_WeChatWinDllAddr + 0xC04E00;
DWORD free_contact_addr = g_WeChatWinDllAddr + 0xEA7880;
__asm {
PUSHAD
PUSHFD
CALL contact_mgr_addr
LEA ECX,buff
PUSH ECX
LEA ECX,pri
PUSH ECX
MOV ECX,EAX
CALL get_contact_addr
POPFD
POPAD
}
contact.wxid = wxid;
contact.code = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxCode);
contact.remark = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxRemark);
contact.name = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxName);
contact.gender = GET_DWORD((DWORD)buff + 0x148);
__asm {
PUSHAD
PUSHFD
LEA ECX,buff
CALL free_contact_addr
POPFD
POPAD
}
return contact;
}
#endif

View File

@ -1,11 +0,0 @@
#pragma once
#include "string"
#include <vector>
#include "pb_types.h"
vector<RpcContact_t> GetContacts();
int AcceptNewFriend(std::string v3, std::string v4, int scene);
int AddFriendByWxid(std::string wxid, std::string msg);
RpcContact_t GetContactByWxid(std::string wxid);

View File

@ -0,0 +1,292 @@
#include "database_executor.h"
#include <algorithm>
#include <iterator>
#include "log.hpp"
#include "pb_util.h"
#include "rpc_helper.h"
#include "sqlite3.h"
#include "util.h"
extern UINT64 g_WeChatWinDllAddr;
namespace db
{
#define OFFSET_DB_INSTANCE 0x5902000
#define OFFSET_DB_MICROMSG 0xB8
#define OFFSET_DB_CHAT_MSG 0x2C8
#define OFFSET_DB_MISC 0x5F0
#define OFFSET_DB_EMOTION 0x15F0
#define OFFSET_DB_MEDIA 0xF48
#define OFFSET_DB_BIZCHAT_MSG 0x1A70
#define OFFSET_DB_FUNCTION_MSG 0x1B98
#define OFFSET_DB_NAME 0x28
#define OFFSET_DB_MSG_MGR 0x595F900
using db_map_t = std::map<std::string, QWORD>;
static db_map_t db_map;
static void get_db_handle(QWORD base, QWORD offset)
{
auto *wsp = reinterpret_cast<wchar_t *>(*(QWORD *)(base + offset + OFFSET_DB_NAME));
std::string dbname = util::w2s(std::wstring(wsp));
db_map[dbname] = util::get_qword(base + offset);
}
static void get_msg_db_handle(QWORD msg_mgr_addr)
{
QWORD db_index = util::get_qword(msg_mgr_addr + 0x68);
QWORD p_start = util::get_qword(msg_mgr_addr + 0x50);
for (uint32_t i = 0; i < db_index; i++) {
QWORD db_addr = util::get_qword(p_start + i * 0x08);
if (db_addr) {
// MSGi.db
std::string dbname = util::w2s(util::get_pp_wstring(db_addr));
db_map[dbname] = util::get_qword(db_addr + 0x78);
// MediaMsgi.db
QWORD mmdb_addr = util::get_qword(db_addr + 0x20);
std::string mmdbname = util::w2s(util::get_pp_wstring(mmdb_addr + 0x78));
db_map[mmdbname] = util::get_qword(mmdb_addr + 0x50);
}
}
}
db_map_t get_db_handles()
{
db_map.clear();
QWORD db_instance_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE);
get_db_handle(db_instance_addr, OFFSET_DB_MICROMSG); // MicroMsg.db
get_db_handle(db_instance_addr, OFFSET_DB_CHAT_MSG); // ChatMsg.db
get_db_handle(db_instance_addr, OFFSET_DB_MISC); // Misc.db
get_db_handle(db_instance_addr, OFFSET_DB_EMOTION); // Emotion.db
get_db_handle(db_instance_addr, OFFSET_DB_MEDIA); // Media.db
get_db_handle(db_instance_addr, OFFSET_DB_FUNCTION_MSG); // Function.db
get_msg_db_handle(util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db
return db_map;
}
DbNames_t get_db_names()
{
if (db_map.empty()) {
db_map = get_db_handles();
}
DbNames_t names;
for (const auto &[k, _] : db_map) {
names.push_back(k);
}
return names;
}
static int cb_get_tables(void *ret, int argc, char **argv, char **azColName)
{
auto *tables = static_cast<DbTables_t *>(ret);
DbTable_t tbl;
for (int i = 0; i < argc; i++) {
if (strcmp(azColName[i], "name") == 0) {
tbl.name = argv[i] ? argv[i] : "";
} else if (strcmp(azColName[i], "sql") == 0) {
std::string sql(argv[i]);
sql.erase(std::remove(sql.begin(), sql.end(), '\t'), sql.end());
tbl.sql = sql;
}
}
tables->push_back(tbl);
return 0;
}
DbTables_t get_db_tables(const std::string &db)
{
DbTables_t tables;
if (db_map.empty()) {
db_map = get_db_handles();
}
auto it = db_map.find(db);
if (it == db_map.end()) {
return tables;
}
constexpr const char *sql = "SELECT name FROM sqlite_master WHERE type='table';";
Sqlite3_exec p_sqlite3_exec = reinterpret_cast<Sqlite3_exec>(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET);
p_sqlite3_exec(it->second, sql, (Sqlite3_callback)cb_get_tables, (void *)&tables, nullptr);
return tables;
}
DbRows_t exec_db_query(const std::string &db, const std::string &sql)
{
DbRows_t rows;
Sqlite3_prepare func_prepare = reinterpret_cast<Sqlite3_prepare>(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET);
Sqlite3_step func_step = reinterpret_cast<Sqlite3_step>(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET);
Sqlite3_column_count func_column_count
= reinterpret_cast<Sqlite3_column_count>(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET);
Sqlite3_column_name func_column_name
= reinterpret_cast<Sqlite3_column_name>(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET);
Sqlite3_column_type func_column_type
= reinterpret_cast<Sqlite3_column_type>(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET);
Sqlite3_column_blob func_column_blob
= reinterpret_cast<Sqlite3_column_blob>(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET);
Sqlite3_column_bytes func_column_bytes
= reinterpret_cast<Sqlite3_column_bytes>(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET);
Sqlite3_finalize func_finalize = reinterpret_cast<Sqlite3_finalize>(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET);
if (db_map.empty()) {
db_map = get_db_handles();
}
auto it = db_map.find(db);
if (it == db_map.end() || it->second == 0) {
LOG_WARN("Empty handle for database '{}', retrying...", db);
db_map = get_db_handles();
it = db_map.find(db);
if (it == db_map.end() || it->second == 0) {
LOG_ERROR("Failed to get handle for database '{}'", db);
return rows;
}
}
QWORD *stmt;
int rc = func_prepare(it->second, sql.c_str(), -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
LOG_ERROR("SQL prepare failed for '{}': error code {}", db, rc);
return rows;
}
while (func_step(stmt) == SQLITE_ROW) {
DbRow_t row;
int col_count = func_column_count(stmt);
for (int i = 0; i < col_count; i++) {
DbField_t field;
field.type = func_column_type(stmt, i);
field.column = func_column_name(stmt, i);
int length = func_column_bytes(stmt, i);
const void *blob = func_column_blob(stmt, i);
if (length > 0 && field.type != SQLITE_NULL) {
field.content.resize(length);
std::memcpy(field.content.data(), blob, length);
}
row.push_back(field);
}
rows.push_back(row);
}
func_finalize(stmt);
return rows;
}
int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx)
{
if (!local_id || !db_idx) {
LOG_ERROR("Invalid pointer arguments!");
return -1;
}
QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
int db_index = static_cast<int>(util::get_qword(msg_mgr_addr + 0x68)); // 总不能 int 还不够吧?
QWORD p_start = util::get_qword(msg_mgr_addr + 0x50);
*db_idx = 0;
for (int i = db_index - 1; i >= 0; i--) { // 从后往前遍历
QWORD db_addr = util::get_qword(p_start + i * 0x08);
if (!db_addr) {
continue;
}
std::string dbname = util::w2s(util::get_pp_wstring(db_addr));
db_map[dbname] = util::get_qword(db_addr + 0x78);
std::string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + std::to_string(id) + ";";
DbRows_t rows = exec_db_query(dbname, sql);
if (rows.empty() || rows.front().empty()) {
continue;
}
const DbField_t &field = rows.front().front();
if (field.column != "localId" || field.type != SQLITE_INTEGER) {
continue;
}
std::string id_str(field.content.begin(), field.content.end());
try {
*local_id = std::stoull(id_str);
} catch (const std::exception &e) {
LOG_ERROR("Failed to parse localId: {}", e.what());
continue;
}
*db_idx = static_cast<uint32_t>(util::get_qword(util::get_qword(db_addr + 0x28) + 0x1E8) >> 32);
return 0;
}
return -1;
}
std::vector<uint8_t> get_audio_data(uint64_t id)
{
QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
int db_index = static_cast<int>(util::get_qword(msg_mgr_addr + 0x68));
std::string sql = "SELECT Buf FROM Media WHERE Reserved0=" + std::to_string(id) + ";";
for (int i = db_index - 1; i >= 0; i--) {
std::string dbname = "MediaMSG" + std::to_string(i) + ".db";
DbRows_t rows = exec_db_query(dbname, sql);
if (rows.empty() || rows.front().empty()) {
continue;
}
const DbField_t &field = rows.front().front();
if (field.column != "Buf" || field.content.empty()) {
continue;
}
// 首字节为 0x02估计是混淆用的去掉。
if (field.content.front() == 0x02) {
return std::vector<uint8_t>(field.content.begin() + 1, field.content.end());
}
return field.content;
}
return {};
}
bool rpc_get_db_names(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_DB_NAMES>(out, len, [&](Response &rsp) {
DbNames_t names = get_db_names();
rsp.msg.dbs.names.funcs.encode = encode_dbnames;
rsp.msg.dbs.names.arg = &names;
});
}
bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_DB_TABLES>(out, len, [&](Response &rsp) {
DbTables_t tables = get_db_tables(db);
rsp.msg.tables.tables.funcs.encode = encode_tables;
rsp.msg.tables.tables.arg = &tables;
});
}
bool rpc_exec_db_query(const DbQuery query, uint8_t *out, size_t *len)
{
const std::string db(query.db);
const std::string sql(query.sql);
return fill_response<Functions_FUNC_EXEC_DB_QUERY>(out, len, [&](Response &rsp) {
DbRows_t rows = exec_db_query(db, sql);
rsp.msg.rows.rows.funcs.encode = encode_rows;
rsp.msg.rows.rows.arg = &rows;
});
}
} // namespace db

View File

@ -0,0 +1,34 @@
#pragma once
#include <optional>
#include <string>
#include <vector>
#include "wcf.pb.h"
#include "pb_types.h"
namespace db
{
// 获取数据库名称列表
DbNames_t get_db_names();
// 获取指定数据库的表列表
DbTables_t get_db_tables(const std::string &db);
// 执行 SQL 查询
DbRows_t exec_db_query(const std::string &db, const std::string &sql);
// 获取本地消息 ID 和数据库索引
int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx);
// 获取音频数据
std::vector<uint8_t> get_audio_data(uint64_t msg_id);
// RPC 方法
bool rpc_get_db_names(uint8_t *out, size_t *len);
bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len);
bool rpc_exec_db_query(const DbQuery query, uint8_t *out, size_t *len);
} // namespace db

View File

@ -1,232 +0,0 @@
#include <iterator>
#include "exec_sql.h"
#include "log.hpp"
#include "sqlite3.h"
#include "util.h"
#define OFFSET_DB_INSTANCE 0x5902000
#define OFFSET_DB_MICROMSG 0xB8
#define OFFSET_DB_CHAT_MSG 0x2C8
#define OFFSET_DB_MISC 0x5F0
#define OFFSET_DB_EMOTION 0x15F0
#define OFFSET_DB_MEDIA 0xF48
#define OFFSET_DB_BIZCHAT_MSG 0x1A70
#define OFFSET_DB_FUNCTION_MSG 0x1B98
#define OFFSET_DB_NAME 0x28
#define OFFSET_DB_MSG_MGR 0x595F900
extern UINT64 g_WeChatWinDllAddr;
typedef map<string, QWORD> dbMap_t;
static dbMap_t dbMap;
static void GetDbHandle(QWORD base, QWORD offset)
{
wchar_t *wsp = (wchar_t *)(*(QWORD *)(base + offset + OFFSET_DB_NAME));
string dbname = Wstring2String(wstring(wsp));
dbMap[dbname] = GET_QWORD(base + offset);
}
static void GetMsgDbHandle(QWORD msgMgrAddr)
{
QWORD dbIndex = GET_QWORD(msgMgrAddr + 0x68);
QWORD pStart = GET_QWORD(msgMgrAddr + 0x50);
for (uint32_t i = 0; i < dbIndex; i++) {
QWORD dbAddr = GET_QWORD(pStart + i * 0x08);
if (dbAddr) {
// MSGi.db
string dbname = Wstring2String(GET_WSTRING(dbAddr));
dbMap[dbname] = GET_QWORD(dbAddr + 0x78);
// MediaMsgi.db
QWORD mmdbAddr = GET_QWORD(dbAddr + 0x20);
string mmdbname = Wstring2String(GET_WSTRING(mmdbAddr + 0x78));
dbMap[mmdbname] = GET_QWORD(mmdbAddr + 0x50);
}
}
}
dbMap_t GetDbHandles()
{
dbMap.clear();
QWORD dbInstanceAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE);
GetDbHandle(dbInstanceAddr, OFFSET_DB_MICROMSG); // MicroMsg.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_CHAT_MSG); // ChatMsg.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_MISC); // Misc.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_EMOTION); // Emotion.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_MEDIA); // Media.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_FUNCTION_MSG); // Function.db
GetMsgDbHandle(GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db
return dbMap;
}
DbNames_t GetDbNames()
{
DbNames_t names;
if (dbMap.empty()) {
dbMap = GetDbHandles();
}
for (auto &[k, v] : dbMap) {
names.push_back(k);
}
return names;
}
static int cbGetTables(void *ret, int argc, char **argv, char **azColName)
{
DbTables_t *tbls = (DbTables_t *)ret;
DbTable_t tbl;
for (int i = 0; i < argc; i++) {
if (strcmp(azColName[i], "name") == 0) {
tbl.name = argv[i] ? argv[i] : "";
} else if (strcmp(azColName[i], "sql") == 0) {
string sql(argv[i]);
sql.erase(std::remove(sql.begin(), sql.end(), '\t'), sql.end());
tbl.sql = sql.c_str();
}
}
tbls->push_back(tbl);
return 0;
}
DbTables_t GetDbTables(const string db)
{
DbTables_t tables;
if (dbMap.empty()) {
dbMap = GetDbHandles();
}
auto it = dbMap.find(db);
if (it == dbMap.end()) {
return tables; // DB not found
}
const char *sql = "select name, sql from sqlite_master where type=\"table\";";
Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET);
p_Sqlite3_exec(it->second, sql, (Sqlite3_callback)cbGetTables, (void *)&tables, 0);
return tables;
}
DbRows_t ExecDbQuery(const string db, const string sql)
{
DbRows_t rows;
Sqlite3_prepare func_prepare = (Sqlite3_prepare)(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET);
Sqlite3_step func_step = (Sqlite3_step)(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET);
Sqlite3_column_count func_column_count = (Sqlite3_column_count)(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET);
Sqlite3_column_name func_column_name = (Sqlite3_column_name)(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET);
Sqlite3_column_type func_column_type = (Sqlite3_column_type)(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET);
Sqlite3_column_blob func_column_blob = (Sqlite3_column_blob)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET);
Sqlite3_column_bytes func_column_bytes = (Sqlite3_column_bytes)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET);
Sqlite3_finalize func_finalize = (Sqlite3_finalize)(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET);
if (dbMap.empty()) {
dbMap = GetDbHandles();
}
QWORD *stmt;
QWORD handle = dbMap[db];
if (handle == 0) {
LOG_WARN("Empty handle, retrying...");
dbMap = GetDbHandles();
}
int rc = func_prepare(dbMap[db], sql.c_str(), -1, &stmt, 0);
if (rc != SQLITE_OK) {
return rows;
}
while (func_step(stmt) == SQLITE_ROW) {
DbRow_t row;
int col_count = func_column_count(stmt);
for (int i = 0; i < col_count; i++) {
DbField_t field;
field.type = func_column_type(stmt, i);
field.column = func_column_name(stmt, i);
int length = func_column_bytes(stmt, i);
const void *blob = func_column_blob(stmt, i);
if (length && (field.type != 5)) {
field.content.reserve(length);
copy((uint8_t *)blob, (uint8_t *)blob + length, back_inserter(field.content));
}
row.push_back(field);
}
rows.push_back(row);
}
return rows;
}
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
{
QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68); // 总不能 int 还不够吧?
QWORD pStart = GET_QWORD(msgMgrAddr + 0x50);
*dbIdx = 0;
for (int i = dbIndex - 1; i >= 0; i--) { // 从后往前遍历
QWORD dbAddr = GET_QWORD(pStart + i * 0x08);
if (dbAddr) {
string dbname = Wstring2String(GET_WSTRING(dbAddr));
dbMap[dbname] = GET_QWORD(dbAddr + 0x78);
string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + to_string(id) + ";";
DbRows_t rows = ExecDbQuery(dbname, sql);
if (rows.empty()) {
continue;
}
DbRow_t row = rows.front();
if (row.empty()) {
continue;
}
DbField_t field = row.front();
if ((field.column.compare("localId") != 0) && (field.type != 1)) {
continue;
}
*localId = strtoull((const char *)(field.content.data()), NULL, 10);
*dbIdx = (uint32_t)(GET_QWORD(GET_QWORD(dbAddr + 0x28) + 0x1E8) >> 32);
return 0;
}
}
return -1;
}
vector<uint8_t> GetAudioData(uint64_t id)
{
QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68);
string sql = "SELECT Buf FROM Media WHERE Reserved0=" + to_string(id) + ";";
for (int i = dbIndex - 1; i >= 0; i--) {
string dbname = "MediaMSG" + to_string(i) + ".db";
DbRows_t rows = ExecDbQuery(dbname, sql);
if (rows.empty()) {
continue;
}
DbRow_t row = rows.front();
if (row.empty()) {
continue;
}
DbField_t field = row.front();
if (field.column.compare("Buf") != 0) {
continue;
}
// 首字节为 0x02估计是混淆用的去掉。
vector<uint8_t> rv(field.content.begin() + 1, field.content.end());
return rv;
}
return vector<uint8_t>();
}

View File

@ -1,11 +0,0 @@
#pragma once
#include <vector>
#include "pb_types.h"
DbNames_t GetDbNames();
DbTables_t GetDbTables(const string db);
DbRows_t ExecDbQuery(const string db, const string sql);
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx);
vector<uint8_t> GetAudioData(uint64_t msgid);

View File

@ -1,410 +0,0 @@
#pragma warning(disable : 4244)
#include "framework.h"
#include <filesystem>
#include <fstream>
#include "codec.h"
#include "exec_sql.h"
#include "funcs.h"
#include "log.hpp"
#include "spy_types.h"
#include "util.h"
using namespace std;
namespace fs = std::filesystem;
extern bool gIsListeningPyq;
extern QWORD g_WeChatWinDllAddr;
#define HEADER_PNG1 0x89
#define HEADER_PNG2 0x50
#define HEADER_JPG1 0xFF
#define HEADER_JPG2 0xD8
#define HEADER_GIF1 0x47
#define HEADER_GIF2 0x49
#define OS_LOGIN_STATUS 0x595C9E8
#define OS_GET_SNS_DATA_MGR 0x21E2200
#define OS_GET_SNS_FIRST_PAGE 0x2E212D0
#define OS_GET_SNS_TIMELINE_MGR 0x2DB3390
#define OS_GET_SNS_NEXT_PAGE 0x2EC8970
#define OS_NEW_CHAT_MSG 0x1B5E140
#define OS_FREE_CHAT_MSG 0x1B55850
#define OS_GET_CHAT_MGR 0x1B876C0
#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x213FB00
#define OS_GET_PRE_DOWNLOAD_MGR 0x1C0EE70
#define OS_PUSH_ATTACH_TASK 0x1CDF4E0
#define OS_LOGIN_QR_CODE 0x59620D8
typedef QWORD (*GetSNSDataMgr_t)();
typedef QWORD (*GetSnsTimeLineMgr_t)();
typedef QWORD (*GetSNSFirstPage_t)(QWORD, QWORD, QWORD);
typedef QWORD (*GetSNSNextPageScene_t)(QWORD, QWORD);
typedef QWORD (*GetChatMgr_t)();
typedef QWORD (*NewChatMsg_t)(QWORD);
typedef QWORD (*FreeChatMsg_t)(QWORD);
typedef QWORD (*GetPreDownLoadMgr_t)();
typedef QWORD (*GetMgrByPrefixLocalId_t)(QWORD, QWORD);
typedef QWORD (*PushAttachTask_t)(QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*GetOCRManager_t)();
typedef QWORD (*DoOCRTask_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
int IsLogin(void) { return (int)GET_QWORD(g_WeChatWinDllAddr + OS_LOGIN_STATUS); }
static string get_key(uint8_t header1, uint8_t header2, uint8_t *key)
{
// PNG?
*key = HEADER_PNG1 ^ header1;
if ((HEADER_PNG2 ^ *key) == header2) {
return ".png";
}
// JPG?
*key = HEADER_JPG1 ^ header1;
if ((HEADER_JPG2 ^ *key) == header2) {
return ".jpg";
}
// GIF?
*key = HEADER_GIF1 ^ header1;
if ((HEADER_GIF2 ^ *key) == header2) {
return ".gif";
}
return ""; // 错误
}
string DecryptImage(string src, string dir)
{
if (!fs::exists(src)) {
LOG_ERROR("File not exists: {}", src);
return "";
}
ifstream in(src.c_str(), ios::binary);
if (!in.is_open()) {
LOG_ERROR("Failed to read file {}", src);
return "";
}
filebuf *pfb = in.rdbuf();
size_t size = pfb->pubseekoff(0, ios::end, ios::in);
pfb->pubseekpos(0, ios::in);
vector<char> buff;
buff.reserve(size);
char *pBuf = buff.data();
pfb->sgetn(pBuf, size);
in.close();
uint8_t key = 0x00;
string ext = get_key(pBuf[0], pBuf[1], &key);
if (ext.empty()) {
LOG_ERROR("Failed to get key.");
return "";
}
for (size_t i = 0; i < size; i++) {
pBuf[i] ^= key;
}
string dst = "";
try {
if (dir.empty()) {
dst = fs::path(src).replace_extension(ext).string();
} else {
dst = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/");
// 判断dir文件夹是否存在若不存在则创建否则将无法创建出文件
if (!fs::exists(dst)) {//判断该文件夹是否存在
bool success = fs::create_directories(dst); //Windows创建文件夹
if (!success) { //创建失败
LOG_ERROR("Failed to mkdir:{}", dst);
return "";
}
}
dst += fs::path(src).stem().string() + ext;
}
replace(dst.begin(), dst.end(), '\\', '/');
} catch (const std::exception &e) {
LOG_ERROR(GB2312ToUtf8(e.what()));
} catch (...) {
LOG_ERROR("Unknow exception.");
return "";
}
ofstream out(dst.c_str(), ios::binary);
if (!out.is_open()) {
LOG_ERROR("Failed to write file {}", dst);
return "";
}
out.write(pBuf, size);
out.close();
return dst;
}
static int GetFirstPage()
{
int status = -1;
GetSNSDataMgr_t GetSNSDataMgr = (GetSNSDataMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR);
GetSNSFirstPage_t GetSNSFirstPage = (GetSNSFirstPage_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE);
QWORD buff[16] = { 0 };
QWORD mgr = GetSNSDataMgr();
status = (int)GetSNSFirstPage(mgr, (QWORD)buff, 1);
return status;
}
static int GetNextPage(QWORD id)
{
int status = -1;
GetSnsTimeLineMgr_t GetSnsTimeLineMgr = (GetSnsTimeLineMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR);
GetSNSNextPageScene_t GetSNSNextPageScene = (GetSNSNextPageScene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE);
QWORD mgr = GetSnsTimeLineMgr();
status = (int)GetSNSNextPageScene(mgr, id);
return status;
}
int RefreshPyq(QWORD id)
{
if (!gIsListeningPyq) {
LOG_ERROR("没有启动朋友圈消息接收参考enable_receiving_msg");
return -1;
}
if (id == 0) {
return GetFirstPage();
}
return GetNextPage(id);
}
/*******************************************************************************
*
*
*
* id id
* thumb mp4
* extra
*******************************************************************************/
int DownloadAttach(QWORD id, string thumb, string extra)
{
int status = -1;
QWORD localId;
uint32_t dbIdx;
if (fs::exists(extra)) { // 第一道不重复下载。TODO: 通过文件大小来判断
return 0;
}
if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) {
LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id));
return status;
}
NewChatMsg_t NewChatMsg = (NewChatMsg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG);
FreeChatMsg_t FreeChatMsg = (FreeChatMsg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG);
GetChatMgr_t GetChatMgr = (GetChatMgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR);
GetPreDownLoadMgr_t GetPreDownLoadMgr = (GetPreDownLoadMgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR);
PushAttachTask_t PushAttachTask = (PushAttachTask_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK);
GetMgrByPrefixLocalId_t GetMgrByPrefixLocalId
= (GetMgrByPrefixLocalId_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID);
LARGE_INTEGER l;
l.HighPart = dbIdx;
l.LowPart = (DWORD)localId;
char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, 0x460);
if (buff == nullptr) {
LOG_ERROR("Failed to allocate memory.");
return status;
}
QWORD pChatMsg = NewChatMsg((QWORD)buff);
GetChatMgr();
GetMgrByPrefixLocalId(l.QuadPart, pChatMsg);
QWORD type = GET_QWORD(buff + 0x38);
string save_path = "";
string thumb_path = "";
switch (type) {
case 0x03: { // Image: extra
save_path = extra;
break;
}
case 0x3E:
case 0x2B: { // Video: thumb
thumb_path = thumb;
save_path = fs::path(thumb).replace_extension("mp4").string();
break;
}
case 0x31: { // File: extra
save_path = extra;
break;
}
default:
break;
}
if (fs::exists(save_path)) { // 不重复下载。TODO: 通过文件大小来判断
return 0;
}
LOG_DEBUG("path: {}", save_path);
// 创建父目录,由于路径来源于微信,不做检查
fs::create_directory(fs::path(save_path).parent_path().string());
int temp = 1;
WxString *pSavePath = NewWxStringFromStr(save_path);
WxString *pThumbPath = NewWxStringFromStr(thumb_path);
memcpy(&buff[0x280], pThumbPath, sizeof(WxString));
memcpy(&buff[0x2A0], pSavePath, sizeof(WxString));
memcpy(&buff[0x40C], &temp, sizeof(temp));
QWORD mgr = GetPreDownLoadMgr();
status = (int)PushAttachTask(mgr, pChatMsg, 0, 1);
FreeChatMsg(pChatMsg);
return status;
}
string GetAudio(QWORD id, string dir)
{
string mp3path = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/");
mp3path += to_string(id) + ".mp3";
replace(mp3path.begin(), mp3path.end(), '\\', '/');
if (fs::exists(mp3path)) { // 不重复下载
return mp3path;
}
vector<uint8_t> silk = GetAudioData(id);
if (silk.size() == 0) {
LOG_ERROR("Empty audio data.");
return "";
}
Silk2Mp3(silk, mp3path, 24000);
return mp3path;
}
string GetPCMAudio(uint64_t id, string dir, int32_t sr)
{
string pcmpath = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/");
pcmpath += to_string(id) + ".pcm";
replace(pcmpath.begin(), pcmpath.end(), '\\', '/');
if (fs::exists(pcmpath)) { // 不重复下载
return pcmpath;
}
vector<uint8_t> pcm;
vector<uint8_t> silk = GetAudioData(id);
if (silk.size() == 0) {
LOG_ERROR("Empty audio data.");
return "";
}
SilkDecode(silk, pcm, sr);
errno_t err;
FILE* fPCM;
err = fopen_s(&fPCM, pcmpath.c_str(), "wb");
if (err != 0) {
printf("Error: could not open input file %s\n", pcmpath.c_str());
exit(0);
}
fwrite(pcm.data(), sizeof(uint8_t), pcm.size(), fPCM);
fclose(fPCM);
return pcmpath;
}
OcrResult_t GetOcrResult(string path)
{
OcrResult_t ret = { -1, "" };
#if 0 // 参数没调好,会抛异常,看看有没有好心人来修复
if (!fs::exists(path)) {
LOG_ERROR("Can not find: {}", path);
return ret;
}
GetOCRManager_t GetOCRManager = (GetOCRManager_t)(g_WeChatWinDllAddr + 0x1D6C3C0);
DoOCRTask_t DoOCRTask = (DoOCRTask_t)(g_WeChatWinDllAddr + 0x2D10BC0);
QWORD unk1 = 0, unk2 = 0, unused = 0;
QWORD *pUnk1 = &unk1;
QWORD *pUnk2 = &unk2;
// 路径分隔符有要求,必须为 `\`
wstring wsPath = String2Wstring(fs::path(path).make_preferred().string());
WxString wxPath(wsPath);
vector<QWORD> *pv = (vector<QWORD> *)HeapAlloc(GetProcessHeap(), 0, 0x20);
RawVector_t *pRv = (RawVector_t *)pv;
pRv->finish = pRv->start;
char buff[0x98] = { 0 };
memcpy(buff, &pRv->start, sizeof(QWORD));
QWORD mgr = GetOCRManager();
ret.status = (int)DoOCRTask(mgr, (QWORD)&wxPath, unused, (QWORD)buff, (QWORD)&pUnk1, (QWORD)&pUnk2);
QWORD count = GET_QWORD(buff + 0x8);
if (count > 0) {
QWORD header = GET_QWORD(buff);
for (QWORD i = 0; i < count; i++) {
QWORD content = GET_QWORD(header);
ret.result += Wstring2String(GET_WSTRING(content + 0x28));
ret.result += "\n";
header = content;
}
}
#endif
return ret;
}
int RevokeMsg(QWORD id)
{
int status = -1;
#if 0 // 这个挺鸡肋的,因为自己发的消息没法直接获得 msgid就这样吧
QWORD localId;
uint32_t dbIdx;
if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) {
LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id));
return status;
}
#endif
return status;
}
string GetLoginUrl()
{
LPVOID targetAddress = reinterpret_cast<LPBYTE>(g_WeChatWinDllAddr) + OS_LOGIN_QR_CODE;
char *dataPtr = *reinterpret_cast<char **>(targetAddress); // 读取指针内容
if (!dataPtr) {
LOG_ERROR("Failed to get login url");
return "error";
}
// 读取字符串内容
std::string data(dataPtr, 22);
return "http://weixin.qq.com/x/" + data;
}
int ReceiveTransfer(string wxid, string transferid, string transactionid)
{
// 别想了,这个不实现了
return -1;
}

View File

@ -1,15 +0,0 @@
#pragma once
#include "stdint.h"
#include <string>
int IsLogin(void);
std::string GetAudio(uint64_t id, std::string dir);
std::string GetPCMAudio(uint64_t id, std::string dir, int32_t sr);
std::string DecryptImage(std::string src, std::string dst);
int RefreshPyq(uint64_t id);
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
int RevokeMsg(uint64_t id);
OcrResult_t GetOcrResult(std::string path);
string GetLoginUrl();
int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid);

View File

@ -0,0 +1,280 @@
#include "message_handler.h"
#include <condition_variable>
#include <mutex>
#include <queue>
#include "framework.h"
#include "log.hpp"
#include "pb_util.h"
#include "rpc_helper.h"
#include "userinfo_manager.h"
#include "util.h"
extern QWORD g_WeChatWinDllAddr;
#define OS_RECV_MSG_ID 0x30
#define OS_RECV_MSG_TYPE 0x38
#define OS_RECV_MSG_SELF 0x3C
#define OS_RECV_MSG_TS 0x44
#define OS_RECV_MSG_ROOMID 0x48
#define OS_RECV_MSG_CONTENT 0x88
#define OS_RECV_MSG_WXID 0x240
#define OS_RECV_MSG_SIGN 0x260
#define OS_RECV_MSG_THUMB 0x280
#define OS_RECV_MSG_EXTRA 0x2A0
#define OS_RECV_MSG_XML 0x308
#define OS_RECV_MSG_CALL 0x213ED90
#define OS_PYQ_MSG_START 0x30
#define OS_PYQ_MSG_END 0x38
#define OS_PYQ_MSG_TS 0x38
#define OS_PYQ_MSG_XML 0x9B8
#define OS_PYQ_MSG_SENDER 0x18
#define OS_PYQ_MSG_CONTENT 0x48
#define OS_PYQ_MSG_CALL 0x2E42C90
#define OS_WXLOG 0x2613D20
namespace message
{
QWORD Handler::DispatchMsg(QWORD arg1, QWORD arg2)
{
auto &handler = getInstance();
WxMsg_t wxMsg = {};
try {
wxMsg.id = util::get_qword(arg2 + OS_RECV_MSG_ID);
wxMsg.type = util::get_dword(arg2 + OS_RECV_MSG_TYPE);
wxMsg.is_self = util::get_dword(arg2 + OS_RECV_MSG_SELF);
wxMsg.ts = util::get_dword(arg2 + OS_RECV_MSG_TS);
wxMsg.content = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_CONTENT);
wxMsg.sign = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_SIGN);
wxMsg.xml = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_XML);
wxMsg.roomid = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_ROOMID);
if (wxMsg.roomid.find("@chatroom") != std::string::npos) {
wxMsg.is_group = true;
wxMsg.sender
= wxMsg.is_self ? userinfo::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID);
} else {
wxMsg.is_group = false;
wxMsg.sender = wxMsg.is_self ? userinfo::get_self_wxid() : wxMsg.roomid;
}
} catch (const std::exception &e) {
LOG_ERROR(util::gb2312_to_utf8(e.what()));
}
{
std::unique_lock<std::mutex> lock(handler.mutex_);
handler.msgQueue_.push(wxMsg);
}
handler.cv_.notify_all();
return handler.realRecvMsg(arg1, arg2);
}
QWORD Handler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9,
QWORD a10, QWORD a11, QWORD a12)
{
auto &handler = getInstance();
QWORD p = handler.realWxLog(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
if (p == 0 || p == 1) {
return p;
}
LOG_INFO("【WX】\n{}", util::gb2312_to_utf8((char *)p));
return p;
}
void Handler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3)
{
auto &handler = getInstance();
QWORD startAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_START);
QWORD endAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_END);
if (startAddr == 0) {
return;
}
while (startAddr < endAddr) {
WxMsg_t wxMsg;
wxMsg.type = 0x00;
wxMsg.is_self = false;
wxMsg.is_group = false;
wxMsg.id = util::get_qword(startAddr);
wxMsg.ts = util::get_dword(startAddr + OS_PYQ_MSG_TS);
wxMsg.xml = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_XML);
wxMsg.sender = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_SENDER);
wxMsg.content = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_CONTENT);
{
std::unique_lock<std::mutex> lock(handler.mutex_);
handler.msgQueue_.push(wxMsg);
}
handler.cv_.notify_all();
startAddr += 0x1618;
}
}
Handler &Handler::getInstance()
{
static Handler instance;
return instance;
}
Handler::Handler()
{
isLogging = false;
isListeningMsg = false;
isListeningPyq = false;
}
Handler::~Handler()
{
DisableLog();
UnListenMsg();
UnListenPyq();
}
MsgTypes_t Handler::GetMsgTypes()
{
return { { 0x00, "朋友圈消息" },
{ 0x01, "文字" },
{ 0x03, "图片" },
{ 0x22, "语音" },
{ 0x25, "好友确认" },
{ 0x28, "POSSIBLEFRIEND_MSG" },
{ 0x2A, "名片" },
{ 0x2B, "视频" },
{ 0x2F, "石头剪刀布 | 表情图片" },
{ 0x30, "位置" },
{ 0x31, "共享实时位置、文件、转账、链接" },
{ 0x32, "VOIPMSG" },
{ 0x33, "微信初始化" },
{ 0x34, "VOIPNOTIFY" },
{ 0x35, "VOIPINVITE" },
{ 0x3E, "小视频" },
{ 0x42, "微信红包" },
{ 0x270F, "SYSNOTICE" },
{ 0x2710, "红包、系统消息" },
{ 0x2712, "撤回消息" },
{ 0x100031, "搜狗表情" },
{ 0x1000031, "链接" },
{ 0x1A000031, "微信红包" },
{ 0x20010031, "红包封面" },
{ 0x2D000031, "视频号视频" },
{ 0x2E000031, "视频号名片" },
{ 0x31000031, "引用消息" },
{ 0x37000031, "拍一拍" },
{ 0x3A000031, "视频号直播" },
{ 0x3A100031, "商品链接" },
{ 0x3A200031, "视频号直播" },
{ 0x3E000031, "音乐链接" },
{ 0x41000031, "文件" } };
}
std::optional<WxMsg_t> Handler::popMessage()
{
std::lock_guard<std::mutex> lock(mutex_);
if (msgQueue_.empty()) {
return std::nullopt;
}
WxMsg_t msg = std::move(msgQueue_.front());
msgQueue_.pop();
return msg;
}
int Handler::EnableLog()
{
if (isLogging) return 1;
funcWxLog = reinterpret_cast<funcWxLog_t>(g_WeChatWinDllAddr + OS_WXLOG);
if (InitializeHook() != MH_OK) return -1;
if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast<LPVOID *>(&realWxLog)) != MH_OK) return -1;
if (MH_EnableHook(funcWxLog) != MH_OK) return -1;
isLogging = true;
return 0;
}
int Handler::DisableLog()
{
if (!isLogging) return 1;
if (MH_DisableHook(funcWxLog) != MH_OK) return -1;
if (UninitializeHook() != MH_OK) return -1;
isLogging = false;
return 0;
}
int Handler::ListenMsg()
{
if (isListeningMsg) return 1;
funcRecvMsg = reinterpret_cast<funcRecvMsg_t>(g_WeChatWinDllAddr + OS_RECV_MSG_CALL);
if (InitializeHook() != MH_OK) return -1;
if (MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast<LPVOID *>(&realRecvMsg)) != MH_OK) return -1;
if (MH_EnableHook(funcRecvMsg) != MH_OK) return -1;
isListeningMsg = true;
return 0;
}
int Handler::UnListenMsg()
{
if (!isListeningMsg) return 1;
if (MH_DisableHook(funcRecvMsg) != MH_OK) return -1;
if (UninitializeHook() != MH_OK) return -1;
isListeningMsg = false;
return 0;
}
int Handler::ListenPyq()
{
if (isListeningPyq) return 1;
funcRecvPyq = reinterpret_cast<funcRecvPyq_t>(g_WeChatWinDllAddr + OS_PYQ_MSG_CALL);
if (InitializeHook() != MH_OK) return -1;
if (MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast<LPVOID *>(&realRecvPyq)) != MH_OK) return -1;
if (MH_EnableHook(funcRecvPyq) != MH_OK) return -1;
isListeningPyq = true;
return 0;
}
int Handler::UnListenPyq()
{
if (!isListeningPyq) return 1;
if (MH_DisableHook(funcRecvPyq) != MH_OK) return -1;
if (UninitializeHook() != MH_OK) return -1;
isListeningPyq = false;
return 0;
}
MH_STATUS Handler::InitializeHook()
{
if (isMH_Initialized) return MH_OK;
MH_STATUS status = MH_Initialize();
if (status == MH_OK) isMH_Initialized = true;
return status;
}
MH_STATUS Handler::UninitializeHook()
{
if (!isMH_Initialized || isLogging || isListeningMsg || isListeningPyq) return MH_OK;
MH_STATUS status = MH_Uninitialize();
if (status == MH_OK) isMH_Initialized = false;
return status;
}
bool Handler::rpc_get_msg_types(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_MSG_TYPES>(out, len, [&](Response &rsp) {
MsgTypes_t types = GetMsgTypes();
rsp.msg.types.types.funcs.encode = encode_types;
rsp.msg.types.types.arg = &types;
});
}
}

View File

@ -0,0 +1,73 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <queue>
#include "MinHook.h"
#include "pb_types.h"
#include "spy_types.h"
namespace message
{
class Handler
{
public:
static Handler &getInstance();
// 0: 成功, -1: 失败, 1: 已经开启
int EnableLog();
int DisableLog();
int ListenPyq();
int UnListenPyq();
int ListenMsg();
int UnListenMsg();
MsgTypes_t GetMsgTypes();
bool isLoggingEnabled() const { return isLogging.load(); }
bool isMessageListening() const { return isListeningMsg.load(); }
bool isPyqListening() const { return isListeningPyq.load(); }
std::optional<WxMsg_t> popMessage();
std::condition_variable &getConditionVariable() { return cv_; };
std::mutex &getMutex() { return mutex_; };
bool rpc_get_msg_types(uint8_t *out, size_t *len);
private:
Handler();
~Handler();
mutable std::mutex mutex_;
std::condition_variable cv_;
std::queue<WxMsg_t> msgQueue_;
std::atomic<bool> isLogging { false };
std::atomic<bool> isListeningMsg { false };
std::atomic<bool> isListeningPyq { false };
using funcRecvMsg_t = QWORD (*)(QWORD, QWORD);
using funcWxLog_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
using funcRecvPyq_t = QWORD (*)(QWORD, QWORD, QWORD);
funcWxLog_t funcWxLog, realWxLog;
funcRecvMsg_t funcRecvMsg, realRecvMsg;
funcRecvPyq_t funcRecvPyq, realRecvPyq;
bool isMH_Initialized { false };
MH_STATUS InitializeHook();
MH_STATUS UninitializeHook();
static QWORD DispatchMsg(QWORD arg1, QWORD arg2);
static QWORD PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9,
QWORD a10, QWORD a11, QWORD a12);
static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3);
};
} // namespace message

View File

@ -0,0 +1,363 @@
#include "message_sender.h"
#include <sstream>
#include <vector>
#include "database_executor.h"
#include "log.hpp"
#include "rpc_helper.h"
#include "spy_types.h"
#include "userinfo_manager.h"
#include "util.h"
extern QWORD g_WeChatWinDllAddr;
#define OS_NEW 0x1B5E140
#define OS_FREE 0x1B55850
#define OS_SEND_MSG_MGR 0x1B53FD0
#define OS_SEND_TEXT 0x22C6B60
#define OS_SEND_IMAGE 0x22BC2F0
#define OS_GET_APP_MSG_MGR 0x1B58F70
#define OS_SEND_FILE 0x20D0230
#define OS_RTM_NEW 0x1B5D690
#define OS_RTM_FREE 0x1B5CA60
#define OS_SEND_RICH_TEXT 0x20DA210
#define OS_SEND_PAT_MSG 0x2CAEC00
#define OS_FORWARD_MSG 0x22C60E0
#define OS_GET_EMOTION_MGR 0x1BCEF10
#define OS_SEND_EMOTION 0x21B52D5
#define OS_XML_BUFSIGN 0x24F0D70
#define OS_SEND_XML 0x20CF360
namespace message
{
Sender &Sender::get_instance()
{
static Sender instance;
return instance;
}
Sender::Sender()
{
func_new = reinterpret_cast<New_t>(g_WeChatWinDllAddr + OS_NEW);
func_free = reinterpret_cast<Free_t>(g_WeChatWinDllAddr + OS_FREE);
func_send_msg_mgr = reinterpret_cast<SendMsgMgr_t>(g_WeChatWinDllAddr + OS_SEND_MSG_MGR);
func_send_text = reinterpret_cast<SendText_t>(g_WeChatWinDllAddr + OS_SEND_TEXT);
func_send_image = reinterpret_cast<SendImage_t>(g_WeChatWinDllAddr + OS_SEND_IMAGE);
func_send_file = reinterpret_cast<SendFile_t>(g_WeChatWinDllAddr + OS_SEND_FILE);
func_send_rich_text = reinterpret_cast<SendRichText_t>(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT);
func_send_pat = reinterpret_cast<SendPat_t>(g_WeChatWinDllAddr + OS_SEND_PAT_MSG);
func_forward = reinterpret_cast<Forward_t>(g_WeChatWinDllAddr + OS_FORWARD_MSG);
func_send_emotion = reinterpret_cast<SendEmotion_t>(g_WeChatWinDllAddr + OS_SEND_EMOTION);
func_send_xml = reinterpret_cast<SendXml_t>(g_WeChatWinDllAddr + OS_SEND_XML);
}
std::unique_ptr<WxString> Sender::new_wx_string(const char *str)
{
return new_wx_string(str ? std::string(str) : std::string());
}
std::unique_ptr<WxString> Sender::new_wx_string(const std::string &str)
{
return std::make_unique<WxString>(util::s2w(str));
}
std::vector<WxString> Sender::parse_wxids(const string &wxids)
{
vector<WxString> wx_members;
wstringstream wss(util::s2w(wxids));
wstring wstr;
while (getline(wss, wstr, L',')) {
wx_members.emplace_back(wstr);
}
return wx_members;
}
void Sender::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids)
{
auto wxWxid = new_wx_string(wxid);
auto wxMsg = new_wx_string(msg);
std::vector<WxString> wx_at_wxids;
if (!at_wxids.empty()) {
wx_at_wxids = parse_wxids(at_wxids);
} else {
wx_at_wxids.emplace_back();
}
QWORD wx_ater_list = reinterpret_cast<QWORD>(&(wx_at_wxids.front()));
char buffer[0x460] = { 0 };
func_send_msg_mgr();
func_send_text(reinterpret_cast<QWORD>(&buffer), reinterpret_cast<QWORD>(wxWxid.get()),
reinterpret_cast<QWORD>(wxMsg.get()), wx_ater_list, 1, 1, 0, 0);
func_free(reinterpret_cast<QWORD>(&buffer));
}
void Sender::send_image(const std::string &wxid, const std::string &path)
{
auto wxWxid = new_wx_string(wxid);
auto wxPath = new_wx_string(path);
char msg[0x460] = { 0 };
char msgTmp[0x460] = { 0 };
QWORD *flag[10] = { nullptr };
QWORD tmp1 = 0, tmp2 = 0;
QWORD pMsgTmp = func_new(reinterpret_cast<QWORD>(&msgTmp));
flag[8] = &tmp1;
flag[9] = &tmp2;
flag[1] = reinterpret_cast<QWORD *>(pMsgTmp);
QWORD pMsg = func_new(reinterpret_cast<QWORD>(&msg));
QWORD sendMgr = func_send_msg_mgr();
func_send_image(sendMgr, pMsg, reinterpret_cast<QWORD>(wxWxid.get()), reinterpret_cast<QWORD>(wxPath.get()),
reinterpret_cast<QWORD>(&flag));
func_free(pMsg);
func_free(pMsgTmp);
}
void Sender::send_file(const std::string &wxid, const std::string &path)
{
auto wxWxid = new_wx_string(wxid);
auto wxPath = new_wx_string(path);
char msg[0x460] = { 0 };
QWORD tmp1[4] = { 0 };
QWORD tmp2[4] = { 0 };
QWORD tmp3[4] = { 0 };
QWORD pMsg = func_new(reinterpret_cast<QWORD>(&msg));
QWORD appMgr = func_get_app_mgr();
func_send_file(appMgr, pMsg, reinterpret_cast<QWORD>(wxWxid.get()), reinterpret_cast<QWORD>(wxPath.get()), 1, tmp1,
0, tmp2, 0, tmp3, 0, 0);
func_free(pMsg);
}
void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type)
{
std::unique_ptr<char[]> buff(new char[0x500]());
std::unique_ptr<char[]> buff2(new char[0x500]());
char nullBuf[0x1C] = { 0 };
func_new(reinterpret_cast<QWORD>(buff.get()));
func_new(reinterpret_cast<QWORD>(buff2.get()));
QWORD sbuf[4] = { 0, 0, 0, 0 };
QWORD sign = func_xml_buf_sign(reinterpret_cast<QWORD>(buff2.get()), reinterpret_cast<QWORD>(sbuf), 0x1);
auto wxReceiver = new_wx_string(receiver);
auto wxXml = new_wx_string(xml);
auto wxPath = new_wx_string(path);
auto wxSender = new_wx_string(userinfo::get_self_wxid());
func_send_xml(reinterpret_cast<QWORD>(buff.get()), reinterpret_cast<QWORD>(wxSender.get()),
reinterpret_cast<QWORD>(wxReceiver.get()), reinterpret_cast<QWORD>(wxXml.get()),
reinterpret_cast<QWORD>(wxPath.get()), reinterpret_cast<QWORD>(nullBuf), type, 0x4, sign,
reinterpret_cast<QWORD>(buff2.get()));
func_free(reinterpret_cast<QWORD>(buff.get()));
func_free(reinterpret_cast<QWORD>(buff2.get()));
}
void Sender::send_emotion(const std::string &wxid, const std::string &path)
{
auto wxWxid = new_wx_string(wxid);
auto wxPath = new_wx_string(path);
std::unique_ptr<QWORD[]> buff(new QWORD[8]()); // 0x20 bytes = 8 * QWORD
if (!buff) {
LOG_ERROR("Out of Memory...");
return;
}
QWORD mgr = func_get_emotion_mgr();
func_send_emotion(mgr, reinterpret_cast<QWORD>(wxPath.get()), reinterpret_cast<QWORD>(buff.get()),
reinterpret_cast<QWORD>(wxWxid.get()), 2, reinterpret_cast<QWORD>(buff.get()), 0,
reinterpret_cast<QWORD>(buff.get()));
}
int Sender::send_rich_text(const RichText &rt)
{
#define SRTM_SIZE 0x3F0
QWORD status = -1;
char *buff = static_cast<char *>(HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE));
if (!buff) {
LOG_ERROR("Out of Memory...");
return -1;
}
memset(buff, 0, SRTM_SIZE);
func_new(reinterpret_cast<QWORD>(buff));
auto pReceiver = new_wx_string(rt.receiver);
auto pTitle = new_wx_string(rt.title);
auto pUrl = new_wx_string(rt.url);
auto pThumburl = new_wx_string(rt.thumburl);
auto pDigest = new_wx_string(rt.digest);
auto pAccount = new_wx_string(rt.account);
auto pName = new_wx_string(rt.name);
memcpy(buff + 0x8, pTitle.get(), sizeof(WxString));
memcpy(buff + 0x48, pUrl.get(), sizeof(WxString));
memcpy(buff + 0xB0, pThumburl.get(), sizeof(WxString));
memcpy(buff + 0xF0, pDigest.get(), sizeof(WxString));
memcpy(buff + 0x2C0, pAccount.get(), sizeof(WxString));
memcpy(buff + 0x2E0, pName.get(), sizeof(WxString));
QWORD mgr = func_get_app_mgr();
status = func_send_rich_text(mgr, reinterpret_cast<QWORD>(pReceiver.get()), reinterpret_cast<QWORD>(buff));
func_free(reinterpret_cast<QWORD>(buff));
return static_cast<int>(status);
}
int Sender::send_pat(const std::string &roomid, const std::string &wxid)
{
QWORD status = -1;
auto wxRoomid = new_wx_string(roomid);
auto wxWxid = new_wx_string(wxid);
status = func_send_pat(reinterpret_cast<QWORD>(wxRoomid.get()), reinterpret_cast<QWORD>(wxWxid.get()));
return static_cast<int>(status);
}
int Sender::forward(QWORD msgid, const std::string &receiver)
{
uint32_t dbIdx = 0;
QWORD localId = 0;
if (db::get_local_id_and_dbidx(msgid, &localId, &dbIdx) != 0) {
LOG_ERROR("Failed to get localId, Please check id: {}", msgid);
return -1;
}
LARGE_INTEGER l;
l.HighPart = dbIdx;
l.LowPart = static_cast<DWORD>(localId);
auto wxReceiver = new_wx_string(receiver);
return static_cast<int>(func_forward(reinterpret_cast<QWORD>(wxReceiver.get()), l.QuadPart, 0x4, 0x0));
}
// RPC 方法
bool Sender::rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_SEND_TXT>(out, len, [&](Response &rsp) {
if (text.msg == nullptr || text.receiver == nullptr || strlen(text.msg) == 0 || strlen(text.receiver) == 0) {
LOG_ERROR("Empty message or receiver.");
rsp.msg.status = -1;
} else {
send_text(text.receiver, text.msg, text.aters ? text.aters : "");
rsp.msg.status = 0;
}
});
}
bool Sender::rpc_send_image(const PathMsg &file, uint8_t *out, size_t *len)
{
std::string path(file.path);
std::string receiver(file.receiver);
return fill_response<Functions_FUNC_SEND_IMG>(out, len, [&](Response &rsp) {
if (path.empty() || receiver.empty()) {
LOG_ERROR("Empty path or receiver.");
rsp.msg.status = -1;
} else {
send_image(receiver, path);
rsp.msg.status = 0;
}
});
}
bool Sender::rpc_send_file(const PathMsg &file, uint8_t *out, size_t *len)
{
std::string path(file.path);
std::string receiver(file.receiver);
return fill_response<Functions_FUNC_SEND_FILE>(out, len, [&](Response &rsp) {
if (path.empty() || receiver.empty()) {
LOG_ERROR("Empty path or receiver.");
rsp.msg.status = -1;
} else {
send_file(receiver, path);
rsp.msg.status = 0;
}
});
}
bool Sender::rpc_send_emotion(const PathMsg &file, uint8_t *out, size_t *len)
{
std::string path(file.path);
std::string receiver(file.receiver);
return fill_response<Functions_FUNC_SEND_EMOTION>(out, len, [&](Response &rsp) {
if (path.empty() || receiver.empty()) {
LOG_ERROR("Empty path or receiver.");
rsp.msg.status = -1;
} else {
send_emotion(receiver, path);
rsp.msg.status = 0;
}
});
}
bool Sender::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_SEND_XML>(out, len, [&](Response &rsp) {
if (xml.content == nullptr || xml.receiver == nullptr) {
LOG_ERROR("Empty content or receiver.");
rsp.msg.status = -1;
} else {
send_xml(xml.receiver, xml.content, xml.path, xml.type);
rsp.msg.status = 0;
}
});
}
bool Sender::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_SEND_RICH_TXT>(out, len, [&](Response &rsp) {
if (rt.receiver == nullptr) {
LOG_ERROR("Empty receiver.");
rsp.msg.status = -1;
} else {
rsp.msg.status = send_rich_text(rt);
}
});
}
bool Sender::rpc_send_pat(const PatMsg &pat, uint8_t *out, size_t *len)
{
std::string wxid(pat.wxid);
std::string roomid(pat.roomid);
return fill_response<Functions_FUNC_SEND_PAT_MSG>(out, len, [&](Response &rsp) {
if (roomid.empty() || wxid.empty()) {
LOG_ERROR("Empty roomid or wxid.");
rsp.msg.status = -1;
} else {
rsp.msg.status = send_pat(roomid, wxid);
}
});
}
bool Sender::rpc_forward(const ForwardMsg &fm, uint8_t *out, size_t *len)
{
uint64_t msgid = fm.id;
std::string receiver(fm.receiver);
return fill_response<Functions_FUNC_FORWARD_MSG>(out, len, [&](Response &rsp) {
if (receiver.empty()) {
LOG_ERROR("Empty receiver.");
rsp.msg.status = -1;
} else {
rsp.msg.status = forward(msgid, receiver);
}
});
}
}

View File

@ -0,0 +1,80 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "wcf.pb.h"
#include "spy_types.h"
namespace message
{
class Sender
{
public:
static Sender &get_instance();
void send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids = "");
void send_image(const std::string &wxid, const std::string &path);
void send_file(const std::string &wxid, const std::string &path);
void send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type);
void send_emotion(const std::string &wxid, const std::string &path);
int send_rich_text(const RichText &rt);
int send_pat(const std::string &roomid, const std::string &wxid);
int forward(uint64_t msgid, const std::string &receiver);
// RPC 方法
bool rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len);
bool rpc_send_image(const PathMsg &file, uint8_t *out, size_t *len);
bool rpc_send_file(const PathMsg &file, uint8_t *out, size_t *len);
bool rpc_send_emotion(const PathMsg &file, uint8_t *out, size_t *len);
bool rpc_send_xml(const XmlMsg &rt, uint8_t *out, size_t *len);
bool rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len);
bool rpc_send_pat(const PatMsg &pat, uint8_t *out, size_t *len);
bool rpc_forward(const ForwardMsg &fm, uint8_t *out, size_t *len);
private:
Sender();
~Sender() = default;
Sender(const Sender &) = delete;
Sender &operator=(const Sender &) = delete;
using New_t = QWORD (*)(QWORD);
using Free_t = QWORD (*)(QWORD);
using SendMsgMgr_t = QWORD (*)();
using GetAppMgr_t = QWORD (*)();
using SendText_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
using SendImage_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD);
using SendFile_t
= QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD);
using SendRichText_t = QWORD (*)(QWORD, QWORD, QWORD);
using SendPat_t = QWORD (*)(QWORD, QWORD);
using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD);
using GetEmotionMgr_t = QWORD (*)();
using SendEmotion_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
using XmlBufSign_t = QWORD (*)(QWORD, QWORD, QWORD);
using SendXml_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
New_t func_new;
Free_t func_free;
SendMsgMgr_t func_send_msg_mgr;
GetAppMgr_t func_get_app_mgr;
SendText_t func_send_text;
SendImage_t func_send_image;
SendFile_t func_send_file;
SendRichText_t func_send_rich_text;
SendPat_t func_send_pat;
Forward_t func_forward;
GetEmotionMgr_t func_get_emotion_mgr;
SendEmotion_t func_send_emotion;
XmlBufSign_t func_xml_buf_sign;
SendXml_t func_send_xml;
std::unique_ptr<WxString> new_wx_string(const char *str);
std::unique_ptr<WxString> new_wx_string(const std::string &str);
std::vector<WxString> parse_wxids(const std::string &wxids);
};
}

View File

@ -0,0 +1,415 @@
#pragma warning(disable : 4244)
#include "misc_manager.h"
#include <filesystem>
#include <fstream>
#include "framework.h"
#include "codec.h"
#include "database_executor.h"
#include "log.hpp"
#include "message_handler.h"
#include "rpc_helper.h"
#include "spy_types.h"
#include "util.h"
using namespace std;
namespace fs = std::filesystem;
extern QWORD g_WeChatWinDllAddr;
namespace misc
{
#define HEADER_PNG1 0x89
#define HEADER_PNG2 0x50
#define HEADER_JPG1 0xFF
#define HEADER_JPG2 0xD8
#define HEADER_GIF1 0x47
#define HEADER_GIF2 0x49
#define OS_LOGIN_STATUS 0x595C9E8
#define OS_GET_SNS_DATA_MGR 0x21E2200
#define OS_GET_SNS_FIRST_PAGE 0x2E212D0
#define OS_GET_SNS_TIMELINE_MGR 0x2DB3390
#define OS_GET_SNS_NEXT_PAGE 0x2EC8970
#define OS_NEW_CHAT_MSG 0x1B5E140
#define OS_FREE_CHAT_MSG 0x1B55850
#define OS_GET_CHAT_MGR 0x1B876C0
#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x213FB00
#define OS_GET_PRE_DOWNLOAD_MGR 0x1C0EE70
#define OS_PUSH_ATTACH_TASK 0x1CDF4E0
#define OS_LOGIN_QR_CODE 0x59620D8
using get_sns_data_mgr_t = QWORD (*)();
using get_sns_timeline_mgr_t = QWORD (*)();
using get_sns_first_page_t = QWORD (*)(QWORD, QWORD, QWORD);
using get_sns_next_page_scene_t = QWORD (*)(QWORD, QWORD);
using get_chat_mgr_t = QWORD (*)();
using new_chat_msg_t = QWORD (*)(QWORD);
using free_chat_msg_t = QWORD (*)(QWORD);
using get_pre_download_mgr_t = QWORD (*)();
using get_mgr_by_prefix_localid_t = QWORD (*)(QWORD, QWORD);
using push_attach_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD);
using get_ocr_manager_t = QWORD (*)();
using do_ocr_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
bool is_logged_in() { return util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS) != 0; }
static std::string detect_image_extension(uint8_t header1, uint8_t header2, uint8_t &key)
{
if ((key = HEADER_PNG1 ^ header1) && (HEADER_PNG2 ^ key) == header2) return ".png";
if ((key = HEADER_JPG1 ^ header1) && (HEADER_JPG2 ^ key) == header2) return ".jpg";
if ((key = HEADER_GIF1 ^ header1) && (HEADER_GIF2 ^ key) == header2) return ".gif";
return "";
}
std::string decrypt_image(const fs::path &src, const fs::path &dst_dir)
{
if (!fs::exists(src)) {
LOG_ERROR("文件不存在: {}", src.string());
return "";
}
std::ifstream in(src, std::ios::binary);
if (!in) {
LOG_ERROR("无法打开文件: {}", src.string());
return "";
}
std::vector<char> buffer(std::istreambuf_iterator<char>(in), {});
if (buffer.size() < 2) return "";
uint8_t key = 0x00;
auto ext = detect_image_extension(buffer[0], buffer[1], key);
if (!ext.empty()) {
LOG_ERROR("无法检测文件类型.");
return "";
}
std::for_each(buffer.begin(), buffer.end(), [key](char &c) { c ^= key; });
fs::path dst_path = dst_dir / (src.stem().string() + ext);
if (!fs::exists(dst_dir)) fs::create_directories(dst_dir);
std::ofstream out(dst_path, std::ios::binary);
if (!out) {
LOG_ERROR("写入文件失败: {}", dst_path.string());
return "";
}
out.write(buffer.data(), buffer.size());
return dst_path.string();
}
static int get_first_page()
{
int status = -1;
get_sns_data_mgr_t GetSNSDataMgr = (get_sns_data_mgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR);
get_sns_first_page_t GetSNSFirstPage = (get_sns_first_page_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE);
QWORD buff[16] = { 0 };
QWORD mgr = GetSNSDataMgr();
status = (int)GetSNSFirstPage(mgr, (QWORD)buff, 1);
return status;
}
static int get_next_page(QWORD id)
{
int status = -1;
get_sns_timeline_mgr_t GetSnsTimeLineMgr = (get_sns_timeline_mgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR);
get_sns_next_page_scene_t GetSNSNextPageScene
= (get_sns_next_page_scene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE);
QWORD mgr = GetSnsTimeLineMgr();
status = (int)GetSNSNextPageScene(mgr, id);
return status;
}
int refresh_pyq(uint64_t id)
{
auto &msgHandler = message::Handler::getInstance();
if (!msgHandler.isPyqListening()) {
LOG_ERROR("没有启动朋友圈消息接收参考enable_receiving_msg");
return -1;
}
if (id == 0) {
return get_first_page();
}
return get_next_page(id);
}
/*******************************************************************************
*
*
*
* id id
* thumb mp4
* extra
*******************************************************************************/
int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extra)
{
int status = -1;
QWORD localId;
uint32_t dbIdx;
if (fs::exists(extra)) { // 第一道不重复下载。TODO: 通过文件大小来判断
return 0;
}
if (db::get_local_id_and_dbidx(id, &localId, &dbIdx) != 0) {
LOG_ERROR("获取 localId 失败, 请检查消息 id: {} 是否正确", to_string(id));
return status;
}
new_chat_msg_t NewChatMsg = (new_chat_msg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG);
free_chat_msg_t FreeChatMsg = (free_chat_msg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG);
get_chat_mgr_t GetChatMgr = (get_chat_mgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR);
get_pre_download_mgr_t GetPreDownLoadMgr = (get_pre_download_mgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR);
push_attach_task_t PushAttachTask = (push_attach_task_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK);
get_mgr_by_prefix_localid_t GetMgrByPrefixLocalId
= (get_mgr_by_prefix_localid_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID);
LARGE_INTEGER l;
l.HighPart = dbIdx;
l.LowPart = (DWORD)localId;
char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, 0x460);
if (buff == nullptr) {
LOG_ERROR("申请内存失败.");
return status;
}
QWORD pChatMsg = NewChatMsg((QWORD)buff);
GetChatMgr();
GetMgrByPrefixLocalId(l.QuadPart, pChatMsg);
QWORD type = util::get_qword(reinterpret_cast<QWORD>(buff) + 0x38);
fs::path save_path, thumb_path;
switch (type) {
case 0x03: { // Image: extra
save_path = extra;
break;
}
case 0x3E:
case 0x2B: { // Video: thumb
thumb_path = thumb;
save_path = fs::path(thumb).replace_extension("mp4").string();
break;
}
case 0x31: { // File: extra
save_path = extra;
break;
}
default:
LOG_ERROR("不支持的文件类型: {}", type);
return -2;
}
if (fs::exists(save_path)) { // 不重复下载。TODO: 通过文件大小来判断
return 0;
}
LOG_DEBUG("保存路径: {}", save_path.string());
// 创建父目录,由于路径来源于微信,不做检查
fs::create_directory(save_path.parent_path());
int temp = 1;
auto wx_save_path = util::new_wx_string(save_path.string());
auto wx_thumb_path = util::new_wx_string(thumb_path.string());
memcpy(&buff[0x280], wx_thumb_path.get(), sizeof(WxString));
memcpy(&buff[0x2A0], wx_save_path.get(), sizeof(WxString));
memcpy(&buff[0x40C], &temp, sizeof(temp));
QWORD mgr = GetPreDownLoadMgr();
status = (int)PushAttachTask(mgr, pChatMsg, 0, 1);
FreeChatMsg(pChatMsg);
return status;
}
std::string get_audio(uint64_t id, const fs::path &dir)
{
if (!fs::exists(dir)) fs::create_directories(dir);
fs::path mp3path = dir / (std::to_string(id) + ".mp3");
if (fs::exists(mp3path)) return mp3path.string();
auto silk = db::get_audio_data(id);
if (silk.empty()) {
LOG_ERROR("没有获取到语音数据.");
return "";
}
Silk2Mp3(silk, mp3path.string(), 24000);
return mp3path.string();
}
std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr)
{
if (!fs::exists(dir)) fs::create_directories(dir);
fs::path pcmpath = dir / (std::to_string(id) + ".pcm");
if (fs::exists(pcmpath)) return pcmpath.string();
auto silk = db::get_audio_data(id);
if (silk.empty()) {
LOG_ERROR("没有获取到语音数据.");
return "";
}
std::vector<uint8_t> pcm;
SilkDecode(silk, pcm, sr);
std::ofstream out(pcmpath, std::ios::binary);
if (!out) {
LOG_ERROR("创建文件失败: {}", pcmpath.string());
return "";
}
out.write(reinterpret_cast<char *>(pcm.data()), pcm.size());
return pcmpath.string();
}
OcrResult_t get_ocr_result(const std::filesystem::path &path)
{
OcrResult_t ret = { -1, "" };
#if 0 // 参数没调好,会抛异常,看看有没有好心人来修复
if (!fs::exists(path)) {
LOG_ERROR("Can not find: {}", path);
return ret;
}
get_ocr_manager_t GetOCRManager = (get_ocr_manager_t)(g_WeChatWinDllAddr + 0x1D6C3C0);
do_ocr_task_t DoOCRTask = (do_ocr_task_t)(g_WeChatWinDllAddr + 0x2D10BC0);
QWORD unk1 = 0, unk2 = 0, unused = 0;
QWORD *pUnk1 = &unk1;
QWORD *pUnk2 = &unk2;
// 路径分隔符有要求,必须为 `\`
wstring wsPath = util::s2w(fs::path(path).make_preferred().string());
WxString wxPath(wsPath);
vector<QWORD> *pv = (vector<QWORD> *)HeapAlloc(GetProcessHeap(), 0, 0x20);
RawVector_t *pRv = (RawVector_t *)pv;
pRv->finish = pRv->start;
char buff[0x98] = { 0 };
memcpy(buff, &pRv->start, sizeof(QWORD));
QWORD mgr = GetOCRManager();
ret.status = (int)DoOCRTask(mgr, (QWORD)&wxPath, unused, (QWORD)buff, (QWORD)&pUnk1, (QWORD)&pUnk2);
QWORD count = util::get_qword(buff + 0x8);
if (count > 0) {
QWORD header = util::get_qword(buff);
for (QWORD i = 0; i < count; i++) {
QWORD content = util::get_qword(header);
ret.result += util::w2s(get_pp_wstring(content + 0x28));
ret.result += "\n";
header = content;
}
}
#endif
return ret;
}
int revoke_message(uint64_t id)
{
int status = -1;
#if 0 // 这个挺鸡肋的,因为自己发的消息没法直接获得 msgid就这样吧
QWORD localId;
uint32_t dbIdx;
if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) {
LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id));
return status;
}
#endif
return status;
}
std::string get_login_url()
{
LPVOID targetAddress = reinterpret_cast<LPBYTE>(g_WeChatWinDllAddr) + OS_LOGIN_QR_CODE;
char *dataPtr = *reinterpret_cast<char **>(targetAddress);
if (!dataPtr) {
LOG_ERROR("获取二维码失败.");
return "";
}
return "http://weixin.qq.com/x/" + std::string(dataPtr, 22);
}
int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid)
{
// 别想了,这个不实现了
return -1;
}
bool rpc_is_logged_in(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_IS_LOGIN>(out, len, [](Response &rsp) { rsp.msg.status = is_logged_in(); });
}
bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_AUDIO_MSG>(
out, len, [&](Response &rsp) { rsp.msg.str = (char *)get_audio(am.id, am.dir).c_str(); });
}
bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len)
{
return false;
}
bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_DECRYPT_IMAGE>(
out, len, [&](Response &rsp) { rsp.msg.str = (char *)decrypt_image(dec.src, dec.dst).c_str(); });
}
bool rpc_get_login_url(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_REFRESH_QRCODE>(
out, len, [&](Response &rsp) { rsp.msg.str = (char *)get_login_url().c_str(); });
}
bool rpc_refresh_pyq(uint64_t id, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_REFRESH_PYQ>(out, len,
[&](Response &rsp) { rsp.msg.status = refresh_pyq(id); });
}
bool rpc_download_attachment(const AttachMsg &att, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_DOWNLOAD_ATTACH>(
out, len, [&](Response &rsp) { rsp.msg.status = download_attachment(att.id, att.thumb, att.extra); });
}
bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_REVOKE_MSG>(out, len,
[&](Response &rsp) { rsp.msg.status = revoke_message(id); });
}
bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_EXEC_OCR>(out, len, [&](Response &rsp) {
OcrResult_t ret = { -1, "" };
ret = get_ocr_result(path);
rsp.msg.ocr.status = ret.status;
rsp.msg.ocr.result = (char *)ret.result.c_str();
});
}
bool rpc_receive_transfer(const Transfer &tf, uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_RECV_TRANSFER>(
out, len, [&](Response &rsp) { rsp.msg.status = receive_transfer(tf.wxid, tf.tfid, tf.taid); });
}
} // namespace misc

View File

@ -0,0 +1,43 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
#include "wcf.pb.h"
#include "pb_types.h"
namespace misc
{
bool is_logged_in();
std::string get_audio(uint64_t id, const std::filesystem::path &dir);
std::string get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr);
std::string decrypt_image(const std::filesystem::path &src, const std::filesystem::path &dst);
std::string get_login_url();
int refresh_pyq(uint64_t id);
int download_attachment(uint64_t id, const std::filesystem::path &thumb, const std::filesystem::path &extra);
int revoke_message(uint64_t id);
OcrResult_t get_ocr_result(const std::filesystem::path &path);
int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid);
// RPC
// clang-format off
bool rpc_is_logged_in(uint8_t *out, size_t *len);
bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len);
bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len);
bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len);
bool rpc_get_login_url(uint8_t *out, size_t *len);
bool rpc_refresh_pyq(uint64_t id, uint8_t *out, size_t *len);
bool rpc_download_attachment(const AttachMsg &att, uint8_t *out, size_t *len);
bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len);
bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len);
bool rpc_receive_transfer(const Transfer &tf, uint8_t *out, size_t *len);
// clang-format on
} // namespace misc

View File

@ -1,378 +0,0 @@
#pragma execution_character_set("utf-8")
#include "MinHook.h"
#include "framework.h"
#include <condition_variable>
#include <mutex>
#include <queue>
#include "log.hpp"
#include "receive_msg.h"
#include "user_info.h"
#include "util.h"
// Defined in rpc_server.cpp
extern bool gIsLogging, gIsListening, gIsListeningPyq;
extern mutex gMutex;
extern condition_variable gCV;
extern queue<WxMsg_t> gMsgQueue;
// Defined in spy.cpp
extern QWORD g_WeChatWinDllAddr;
#define OS_RECV_MSG_ID 0x30
#define OS_RECV_MSG_TYPE 0x38
#define OS_RECV_MSG_SELF 0x3C
#define OS_RECV_MSG_TS 0x44
#define OS_RECV_MSG_ROOMID 0x48
#define OS_RECV_MSG_CONTENT 0x88
#define OS_RECV_MSG_WXID 0x240
#define OS_RECV_MSG_SIGN 0x260
#define OS_RECV_MSG_THUMB 0x280
#define OS_RECV_MSG_EXTRA 0x2A0
#define OS_RECV_MSG_XML 0x308
#define OS_RECV_MSG_CALL 0x213ED90
#define OS_PYQ_MSG_START 0x30
#define OS_PYQ_MSG_END 0x38
#define OS_PYQ_MSG_TS 0x38
#define OS_PYQ_MSG_XML 0x9B8
#define OS_PYQ_MSG_SENDER 0x18
#define OS_PYQ_MSG_CONTENT 0x48
#define OS_PYQ_MSG_CALL 0x2E42C90
#define OS_WXLOG 0x2613D20
typedef QWORD (*RecvMsg_t)(QWORD, QWORD);
typedef QWORD (*WxLog_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*RecvPyq_t)(QWORD, QWORD, QWORD);
static RecvMsg_t funcRecvMsg = nullptr;
static RecvMsg_t realRecvMsg = nullptr;
static WxLog_t funcWxLog = nullptr;
static WxLog_t realWxLog = nullptr;
static RecvPyq_t funcRecvPyq = nullptr;
static RecvPyq_t realRecvPyq = nullptr;
static bool isMH_Initialized = false;
MsgTypes_t GetMsgTypes()
{
const MsgTypes_t m = {
{ 0x00, "朋友圈消息" },
{ 0x01, "文字" },
{ 0x03, "图片" },
{ 0x22, "语音" },
{ 0x25, "好友确认" },
{ 0x28, "POSSIBLEFRIEND_MSG" },
{ 0x2A, "名片" },
{ 0x2B, "视频" },
{ 0x2F, "石头剪刀布 | 表情图片" },
{ 0x30, "位置" },
{ 0x31, "共享实时位置、文件、转账、链接" },
{ 0x32, "VOIPMSG" },
{ 0x33, "微信初始化" },
{ 0x34, "VOIPNOTIFY" },
{ 0x35, "VOIPINVITE" },
{ 0x3E, "小视频" },
{ 0x42, "微信红包" },
{ 0x270F, "SYSNOTICE" },
{ 0x2710, "红包、系统消息" },
{ 0x2712, "撤回消息" },
{ 0x100031, "搜狗表情" },
{ 0x1000031, "链接" },
{ 0x1A000031, "微信红包" },
{ 0x20010031, "红包封面" },
{ 0x2D000031, "视频号视频" },
{ 0x2E000031, "视频号名片" },
{ 0x31000031, "引用消息" },
{ 0x37000031, "拍一拍" },
{ 0x3A000031, "视频号直播" },
{ 0x3A100031, "商品链接" },
{ 0x3A200031, "视频号直播" },
{ 0x3E000031, "音乐链接" },
{ 0x41000031, "文件" },
};
return m;
}
static QWORD DispatchMsg(QWORD arg1, QWORD arg2)
{
WxMsg_t wxMsg = { 0 };
try {
wxMsg.id = GET_QWORD(arg2 + OS_RECV_MSG_ID);
wxMsg.type = GET_DWORD(arg2 + OS_RECV_MSG_TYPE);
wxMsg.is_self = GET_DWORD(arg2 + OS_RECV_MSG_SELF);
wxMsg.ts = GET_DWORD(arg2 + OS_RECV_MSG_TS);
wxMsg.content = GetStringByWstrAddr(arg2 + OS_RECV_MSG_CONTENT);
wxMsg.sign = GetStringByWstrAddr(arg2 + OS_RECV_MSG_SIGN);
wxMsg.xml = GetStringByWstrAddr(arg2 + OS_RECV_MSG_XML);
string roomid = GetStringByWstrAddr(arg2 + OS_RECV_MSG_ROOMID);
wxMsg.roomid = roomid;
if (roomid.find("@chatroom") != string::npos) { // 群 ID 的格式为 xxxxxxxxxxx@chatroom
wxMsg.is_group = true;
if (wxMsg.is_self) {
wxMsg.sender = GetSelfWxid();
} else {
wxMsg.sender = GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID);
}
} else {
wxMsg.is_group = false;
if (wxMsg.is_self) {
wxMsg.sender = GetSelfWxid();
} else {
wxMsg.sender = roomid;
}
}
wxMsg.thumb = GetStringByWstrAddr(arg2 + OS_RECV_MSG_THUMB);
if (!wxMsg.thumb.empty()) {
wxMsg.thumb = GetHomePath() + wxMsg.thumb;
replace(wxMsg.thumb.begin(), wxMsg.thumb.end(), '\\', '/');
}
wxMsg.extra = GetStringByWstrAddr(arg2 + OS_RECV_MSG_EXTRA);
if (!wxMsg.extra.empty()) {
wxMsg.extra = GetHomePath() + wxMsg.extra;
replace(wxMsg.extra.begin(), wxMsg.extra.end(), '\\', '/');
}
} catch (const std::exception &e) {
LOG_ERROR(GB2312ToUtf8(e.what()));
} catch (...) {
LOG_ERROR("Unknow exception.");
}
{
unique_lock<mutex> lock(gMutex);
gMsgQueue.push(wxMsg); // 推送到队列
}
gCV.notify_all(); // 通知各方消息就绪
return realRecvMsg(arg1, arg2);
}
static QWORD PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9,
QWORD a10, QWORD a11, QWORD a12)
{
QWORD p = realWxLog(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
if (p == 0 || p == 1) {
return p;
}
LOG_INFO("【WX】\n{}", GB2312ToUtf8((char *)p));
return p;
}
static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3)
{
QWORD startAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_START);
QWORD endAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_END);
if (startAddr == 0) {
return;
}
while (startAddr < endAddr) {
WxMsg_t wxMsg;
wxMsg.type = 0x00; // 朋友圈消息
wxMsg.is_self = false;
wxMsg.is_group = false;
wxMsg.id = GET_QWORD(startAddr);
wxMsg.ts = GET_DWORD(startAddr + OS_PYQ_MSG_TS);
wxMsg.xml = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_XML);
wxMsg.sender = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_SENDER);
wxMsg.content = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_CONTENT);
{
unique_lock<mutex> lock(gMutex);
gMsgQueue.push(wxMsg); // 推送到队列
}
gCV.notify_all(); // 通知各方消息就绪
startAddr += 0x1618;
}
}
static MH_STATUS InitializeHook()
{
if (isMH_Initialized) {
return MH_OK;
}
MH_STATUS status = MH_Initialize();
if (status == MH_OK) {
isMH_Initialized = true;
}
return status;
}
static MH_STATUS UninitializeHook()
{
if (!isMH_Initialized) {
return MH_OK;
}
if (gIsLogging || gIsListening || gIsListeningPyq) {
return MH_OK;
}
MH_STATUS status = MH_Uninitialize();
if (status == MH_OK) {
isMH_Initialized = false;
}
return status;
}
void EnableLog()
{
MH_STATUS status = MH_UNKNOWN;
if (gIsLogging) {
LOG_WARN("gIsLogging");
return;
}
WxLog_t funcWxLog = (WxLog_t)(g_WeChatWinDllAddr + OS_WXLOG);
status = InitializeHook();
if (status != MH_OK) {
LOG_ERROR("MH_Initialize failed: {}", to_string(status));
return;
}
status = MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast<LPVOID *>(&realWxLog));
if (status != MH_OK) {
LOG_ERROR("MH_CreateHook failed: {}", to_string(status));
return;
}
status = MH_EnableHook(funcWxLog);
if (status != MH_OK) {
LOG_ERROR("MH_EnableHook failed: {}", to_string(status));
return;
}
gIsLogging = true;
}
void DisableLog()
{
MH_STATUS status = MH_UNKNOWN;
if (!gIsLogging) {
return;
}
status = MH_DisableHook(funcWxLog);
if (status != MH_OK) {
LOG_ERROR("MH_DisableHook failed: {}", to_string(status));
return;
}
gIsLogging = false;
status = UninitializeHook();
if (status != MH_OK) {
LOG_ERROR("MH_Uninitialize failed: {}", to_string(status));
return;
}
}
void ListenMessage()
{
MH_STATUS status = MH_UNKNOWN;
if (gIsListening) {
LOG_WARN("gIsListening");
return;
}
funcRecvMsg = (RecvMsg_t)(g_WeChatWinDllAddr + OS_RECV_MSG_CALL);
status = InitializeHook();
if (status != MH_OK) {
LOG_ERROR("MH_Initialize failed: {}", to_string(status));
return;
}
status = MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast<LPVOID *>(&realRecvMsg));
if (status != MH_OK) {
LOG_ERROR("MH_CreateHook failed: {}", to_string(status));
return;
}
status = MH_EnableHook(funcRecvMsg);
if (status != MH_OK) {
LOG_ERROR("MH_EnableHook failed: {}", to_string(status));
return;
}
gIsListening = true;
}
void UnListenMessage()
{
MH_STATUS status = MH_UNKNOWN;
if (!gIsListening) {
return;
}
status = MH_DisableHook(funcRecvMsg);
if (status != MH_OK) {
LOG_ERROR("MH_DisableHook failed: {}", to_string(status));
return;
}
gIsListening = false;
status = UninitializeHook();
if (status != MH_OK) {
LOG_ERROR("MH_Uninitialize failed: {}", to_string(status));
return;
}
}
void ListenPyq()
{
MH_STATUS status = MH_UNKNOWN;
if (gIsListeningPyq) {
LOG_WARN("gIsListeningPyq");
return;
}
funcRecvPyq = (RecvPyq_t)(g_WeChatWinDllAddr + OS_PYQ_MSG_CALL);
status = InitializeHook();
if (status != MH_OK) {
LOG_ERROR("MH_Initialize failed: {}", to_string(status));
return;
}
status = MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast<LPVOID *>(&realRecvPyq));
if (status != MH_OK) {
LOG_ERROR("MH_CreateHook failed: {}", to_string(status));
return;
}
status = MH_EnableHook(funcRecvPyq);
if (status != MH_OK) {
LOG_ERROR("MH_EnableHook failed: {}", to_string(status));
return;
}
gIsListeningPyq = true;
}
void UnListenPyq()
{
MH_STATUS status = MH_UNKNOWN;
if (!gIsListeningPyq) {
return;
}
status = MH_DisableHook(funcRecvPyq);
if (status != MH_OK) {
LOG_ERROR("MH_DisableHook failed: {}", to_string(status));
return;
}
gIsListeningPyq = false;
status = UninitializeHook();
if (status != MH_OK) {
LOG_ERROR("MH_Uninitialize failed: {}", to_string(status));
return;
}
}

View File

@ -1,11 +0,0 @@
#pragma once
#include "pb_types.h"
void EnableLog();
void DisableLog();
void ListenPyq();
void UnListenPyq();
void ListenMessage();
void UnListenMessage();
MsgTypes_t GetMsgTypes();

View File

@ -0,0 +1,67 @@
#pragma once
#include <unordered_map>
#include <magic_enum/magic_enum.hpp>
#include "wcf.pb.h"
#include "log.hpp"
#include "pb_encode.h"
#include "pb_types.h"
static const std::unordered_map<Functions, int> rpc_tag_map
= { { Functions_FUNC_IS_LOGIN, Response_status_tag },
{ Functions_FUNC_GET_SELF_WXID, Response_str_tag },
{ Functions_FUNC_GET_USER_INFO, Response_ui_tag },
{ Functions_FUNC_GET_MSG_TYPES, Response_types_tag },
{ Functions_FUNC_GET_CONTACTS, Response_contacts_tag },
{ Functions_FUNC_GET_DB_NAMES, Response_dbs_tag },
{ Functions_FUNC_GET_DB_TABLES, Response_tables_tag },
{ Functions_FUNC_GET_AUDIO_MSG, Response_str_tag },
{ Functions_FUNC_SEND_TXT, Response_status_tag },
{ Functions_FUNC_SEND_IMG, Response_status_tag },
{ Functions_FUNC_SEND_FILE, Response_status_tag },
{ Functions_FUNC_SEND_XML, Response_status_tag },
{ Functions_FUNC_SEND_RICH_TXT, Response_status_tag },
{ Functions_FUNC_SEND_PAT_MSG, Response_status_tag },
{ Functions_FUNC_FORWARD_MSG, Response_status_tag },
{ Functions_FUNC_SEND_EMOTION, Response_status_tag },
{ Functions_FUNC_ENABLE_RECV_TXT, Response_status_tag },
{ Functions_FUNC_DISABLE_RECV_TXT, Response_status_tag },
{ Functions_FUNC_EXEC_DB_QUERY, Response_rows_tag },
{ Functions_FUNC_REFRESH_PYQ, Response_status_tag },
{ Functions_FUNC_DOWNLOAD_ATTACH, Response_status_tag },
{ Functions_FUNC_GET_CONTACT_INFO, Response_contacts_tag },
{ Functions_FUNC_ACCEPT_FRIEND, Response_status_tag },
{ Functions_FUNC_RECV_TRANSFER, Response_status_tag },
{ Functions_FUNC_REVOKE_MSG, Response_status_tag },
{ Functions_FUNC_REFRESH_QRCODE, Response_str_tag },
{ Functions_FUNC_DECRYPT_IMAGE, Response_str_tag },
{ Functions_FUNC_EXEC_OCR, Response_ocr_tag },
{ Functions_FUNC_ADD_ROOM_MEMBERS, Response_status_tag },
{ Functions_FUNC_DEL_ROOM_MEMBERS, Response_status_tag },
{ Functions_FUNC_INV_ROOM_MEMBERS, Response_status_tag } };
template <Functions FuncType, typename AssignFunc> bool fill_response(uint8_t *out, size_t *len, AssignFunc assign)
{
Response rsp = Response_init_default;
rsp.func = FuncType;
auto it = rpc_tag_map.find(FuncType);
if (it == rpc_tag_map.end()) {
LOG_ERROR("Unknown function type: {}", magic_enum::enum_name(rsp.func));
return false;
}
rsp.which_msg = it->second;
assign(rsp);
pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
if (!pb_encode(&stream, Response_fields, &rsp)) {
LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
return false;
}
*len = stream.bytes_written;
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,273 +0,0 @@

#include "framework.h"
#include <sstream>
#include <vector>
#include "exec_sql.h"
#include "log.hpp"
#include "send_msg.h"
#include "spy_types.h"
#include "util.h"
extern HANDLE g_hEvent;
extern QWORD g_WeChatWinDllAddr;
extern string GetSelfWxid(); // Defined in spy.cpp
#define SRTM_SIZE 0x3F0
#define OS_NEW 0x1B5E140
#define OS_FREE 0x1B55850
#define OS_SEND_MSG_MGR 0x1B53FD0
#define OS_SEND_TEXT 0x22C6B60
#define OS_SEND_IMAGE 0x22BC2F0
#define OS_GET_APP_MSG_MGR 0x1B58F70
#define OS_SEND_FILE 0x20D0230
#define OS_RTM_NEW 0x1B5D690
#define OS_RTM_FREE 0x1B5CA60
#define OS_SEND_RICH_TEXT 0x20DA210
#define OS_SEND_PAT_MSG 0x2CAEC00
#define OS_FORWARD_MSG 0x22C60E0
#define OS_GET_EMOTION_MGR 0x1BCEF10
#define OS_SEND_EMOTION 0x21B52D5
#define OS_XML_BUFSIGN 0x24F0D70
#define OS_SEND_XML 0x20CF360
typedef QWORD (*New_t)(QWORD);
typedef QWORD (*Free_t)(QWORD);
typedef QWORD (*SendMsgMgr_t)();
typedef QWORD (*GetAppMsgMgr_t)();
typedef QWORD (*SendTextMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*SendImageMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*SendFileMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD,
QWORD);
typedef QWORD (*SendRichTextMsg_t)(QWORD, QWORD, QWORD);
typedef QWORD (*SendPatMsg_t)(QWORD, QWORD);
typedef QWORD (*ForwardMsg_t)(QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*GetEmotionMgr_t)();
typedef QWORD (*SendEmotion_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
typedef QWORD (*XmlBufSign_t)(QWORD, QWORD, QWORD);
typedef QWORD (*SendXmlMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
void SendTextMessage(string wxid, string msg, string atWxids)
{
QWORD success = 0;
wstring wsWxid = String2Wstring(wxid);
wstring wsMsg = String2Wstring(msg);
WxString wxMsg(wsMsg);
WxString wxWxid(wsWxid);
vector<wstring> vAtWxids;
vector<WxString> vWxAtWxids;
if (!atWxids.empty()) {
wstringstream wss(String2Wstring(atWxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vAtWxids.push_back(wstr);
WxString wxAtWxid(vAtWxids.back());
vWxAtWxids.push_back(wxAtWxid);
}
} else {
WxString wxEmpty = WxString();
vWxAtWxids.push_back(wxEmpty);
}
QWORD wxAters = (QWORD) & ((RawVector_t *)&vWxAtWxids)->start;
char buffer[0x460] = { 0 };
SendMsgMgr_t funcSendMsgMgr = (SendMsgMgr_t)(g_WeChatWinDllAddr + OS_SEND_MSG_MGR);
SendTextMsg_t funcSendTextMsg = (SendTextMsg_t)(g_WeChatWinDllAddr + OS_SEND_TEXT);
Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE);
funcSendMsgMgr();
success = funcSendTextMsg((QWORD)(&buffer), (QWORD)(&wxWxid), (QWORD)(&wxMsg), wxAters, 1, 1, 0, 0);
funcFree((QWORD)(&buffer));
}
void SendImageMessage(string wxid, string path)
{
wstring wsWxid = String2Wstring(wxid);
wstring wsPath = String2Wstring(path);
WxString wxWxid(wsWxid);
WxString wxPath(wsPath);
New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW);
Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE);
SendMsgMgr_t funcSendMsgMgr = (SendMsgMgr_t)(g_WeChatWinDllAddr + OS_SEND_MSG_MGR);
SendImageMsg_t funcSendImage = (SendImageMsg_t)(g_WeChatWinDllAddr + OS_SEND_IMAGE);
char msg[0x460] = { 0 };
char msgTmp[0x460] = { 0 };
QWORD *flag[10] = { 0 };
QWORD tmp1 = 0, tmp2 = 0;
QWORD pMsgTmp = funcNew((QWORD)(&msgTmp));
flag[8] = &tmp1;
flag[9] = &tmp2;
flag[1] = (QWORD *)(pMsgTmp);
QWORD pMsg = funcNew((QWORD)(&msg));
QWORD sendMgr = funcSendMsgMgr();
funcSendImage(sendMgr, pMsg, (QWORD)(&wxWxid), (QWORD)(&wxPath), (QWORD)(&flag));
funcFree(pMsg);
funcFree(pMsgTmp);
}
void SendFileMessage(string wxid, string path)
{
wstring wsWxid = String2Wstring(wxid);
wstring wsPath = String2Wstring(path);
WxString wxWxid(wsWxid);
WxString wxPath(wsPath);
New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW);
Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE);
GetAppMsgMgr_t funcGetAppMsgMgr = (GetAppMsgMgr_t)(g_WeChatWinDllAddr + OS_GET_APP_MSG_MGR);
SendFileMsg_t funcSendFile = (SendFileMsg_t)(g_WeChatWinDllAddr + OS_SEND_FILE);
char msg[0x460] = { 0 };
QWORD tmp1[4] = { 0 };
QWORD tmp2[4] = { 0 };
QWORD tmp3[4] = { 0 };
QWORD pMsg = funcNew((QWORD)(&msg));
QWORD appMgr = funcGetAppMsgMgr();
funcSendFile(appMgr, pMsg, (QWORD)(&wxWxid), (QWORD)(&wxPath), 1, tmp1, 0, tmp2, 0, tmp3, 0, 0);
funcFree(pMsg);
}
int SendRichTextMessage(RichText_t &rt)
{ // TODO: Fix memory leak
QWORD status = -1;
New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_RTM_NEW);
Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_RTM_FREE);
GetAppMsgMgr_t funcGetAppMsgMgr = (GetAppMsgMgr_t)(g_WeChatWinDllAddr + OS_GET_APP_MSG_MGR);
SendRichTextMsg_t funcForwordPublicMsg = (SendRichTextMsg_t)(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT);
char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE);
if (buff == NULL) {
LOG_ERROR("Out of Memory...");
return -1;
}
memset(buff, 0, SRTM_SIZE);
funcNew((QWORD)buff);
WxString *pReceiver = NewWxStringFromStr(rt.receiver);
WxString *pTitle = NewWxStringFromStr(rt.title);
WxString *pUrl = NewWxStringFromStr(rt.url);
WxString *pThumburl = NewWxStringFromStr(rt.thumburl);
WxString *pDigest = NewWxStringFromStr(rt.digest);
WxString *pAccount = NewWxStringFromStr(rt.account);
WxString *pName = NewWxStringFromStr(rt.name);
memcpy(buff + 0x8, pTitle, sizeof(WxString));
memcpy(buff + 0x48, pUrl, sizeof(WxString));
memcpy(buff + 0xB0, pThumburl, sizeof(WxString));
memcpy(buff + 0xF0, pDigest, sizeof(WxString));
memcpy(buff + 0x2C0, pAccount, sizeof(WxString));
memcpy(buff + 0x2E0, pName, sizeof(WxString));
QWORD mgr = funcGetAppMsgMgr();
status = funcForwordPublicMsg(mgr, (QWORD)(pReceiver), (QWORD)(buff));
funcFree((QWORD)buff);
return (int)status;
}
int SendPatMessage(string roomid, string wxid)
{
QWORD status = -1;
wstring wsRoomid = String2Wstring(roomid);
wstring wsWxid = String2Wstring(wxid);
WxString wxRoomid(wsRoomid);
WxString wxWxid(wsWxid);
SendPatMsg_t funcSendPatMsg = (SendPatMsg_t)(g_WeChatWinDllAddr + OS_SEND_PAT_MSG);
status = funcSendPatMsg((QWORD)(&wxRoomid), (QWORD)(&wxWxid));
return (int)status;
}
int ForwardMessage(QWORD msgid, string receiver)
{
int status = -1;
uint32_t dbIdx = 0;
QWORD localId = 0;
ForwardMsg_t funcForwardMsg = (ForwardMsg_t)(g_WeChatWinDllAddr + OS_FORWARD_MSG);
if (GetLocalIdandDbidx(msgid, &localId, &dbIdx) != 0) {
LOG_ERROR("Failed to get localId, Please check id: {}", to_string(msgid));
return status;
}
WxString *pReceiver = NewWxStringFromStr(receiver);
LARGE_INTEGER l;
l.HighPart = dbIdx;
l.LowPart = (DWORD)localId;
status = (int)funcForwardMsg((QWORD)pReceiver, l.QuadPart, 0x4, 0x0);
return status;
}
void SendEmotionMessage(string wxid, string path)
{
GetEmotionMgr_t GetEmotionMgr = (GetEmotionMgr_t)(g_WeChatWinDllAddr + OS_GET_EMOTION_MGR);
SendEmotion_t SendEmotion = (SendEmotion_t)(g_WeChatWinDllAddr + OS_SEND_EMOTION);
WxString *pWxPath = NewWxStringFromStr(path);
WxString *pWxWxid = NewWxStringFromStr(wxid);
QWORD *buff = (QWORD *)HeapAlloc(GetProcessHeap(), 0, 0x20);
if (buff == NULL) {
LOG_ERROR("Out of Memory...");
return;
}
memset(buff, 0, 0x20);
QWORD mgr = GetEmotionMgr();
SendEmotion(mgr, (QWORD)pWxPath, (QWORD)buff, (QWORD)pWxWxid, 2, (QWORD)buff, 0, (QWORD)buff);
}
void SendXmlMessage(string receiver, string xml, string path, QWORD type)
{
if (g_WeChatWinDllAddr == 0) {
return;
}
New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW);
Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE);
XmlBufSign_t xmlBufSign = (XmlBufSign_t)(g_WeChatWinDllAddr + OS_XML_BUFSIGN);
SendXmlMsg_t sendXmlMsg = (SendXmlMsg_t)(g_WeChatWinDllAddr + OS_SEND_XML);
char buff[0x500] = { 0 };
char buff2[0x500] = { 0 };
char nullBuf[0x1C] = { 0 };
QWORD pBuf = (QWORD)(&buff);
QWORD pBuf2 = (QWORD)(&buff2);
funcNew(pBuf);
funcNew(pBuf2);
QWORD sbuf[4] = { 0, 0, 0, 0 };
QWORD sign = xmlBufSign(pBuf2, (QWORD)(&sbuf), 0x1);
WxString *pReceiver = NewWxStringFromStr(receiver);
WxString *pXml = NewWxStringFromStr(xml);
WxString *pPath = NewWxStringFromStr(path);
WxString *pSender = NewWxStringFromStr(GetSelfWxid());
sendXmlMsg(pBuf, (QWORD)pSender, (QWORD)pReceiver, (QWORD)pXml, (QWORD)pPath, (QWORD)(&nullBuf), type, 0x4, sign,
pBuf2);
funcFree((QWORD)&buff);
funcFree((QWORD)&buff2);
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <string>
using namespace std;
typedef struct {
string name;
string account;
string title;
string digest;
string url;
string thumburl;
string receiver;
} RichText_t;
void SendTextMessage(string wxid, string msg, string atWxids);
void SendImageMessage(string wxid, string path);
void SendFileMessage(string wxid, string path);
void SendXmlMessage(string receiver, string xml, string path, uint64_t type);
void SendEmotionMessage(string wxid, string path);
int SendRichTextMessage(RichText_t &rt);
int SendPatMessage(string roomid, string wxid);
int ForwardMessage(uint64_t msgid, string receiver);

View File

@ -1,45 +1,39 @@
#include <filesystem>
#include "spy.h"
#include <filesystem>
#include <string_view>
#include "log.hpp"
#include "rpc_server.h"
#include "spy.h"
#include "util.h"
constexpr std::string_view SUPPORT_VERSION = "3.9.11.25";
UINT64 g_WeChatWinDllAddr = 0;
static bool IsWxVersionMatched(const wchar_t *version)
int InitSpy(LPVOID args)
{
if (wcscmp(version, SUPPORT_VERSION) != 0) {
return false;
}
return true;
}
void InitSpy(LPVOID args)
{
wchar_t version[16] = { 0 };
PortPath_t *pp = (PortPath_t *)args;
auto *pp = static_cast<util::PortPath *>(args);
Log::InitLogger(pp->path);
g_WeChatWinDllAddr = (UINT64)GetModuleHandle(L"WeChatWin.dll"); // 获取wechatWin模块地址
if (g_WeChatWinDllAddr == 0) {
LOG_ERROR("获取 wechatWin.dll 模块地址失败");
return; // TODO: 退出进程,避免后面操作失败
if (auto dll_addr = GetModuleHandle(L"WeChatWin.dll")) {
g_WeChatWinDllAddr = reinterpret_cast<UINT64>(dll_addr);
} else {
LOG_ERROR("获取 WeChatWin.dll 模块地址失败");
return -1;
}
if (!GetWeChatVersion(version)) { // 获取微信版本
LOG_ERROR("获取微信版本失败");
return;
}
LOG_INFO("WeChat version: {}", Wstring2String(version).c_str());
if (!IsWxVersionMatched(version)) {
LOG_ERROR("不支持当前版本");
MessageBox(NULL, L"不支持当前版本", L"错误", 0);
return;
std::string version = util::get_wechat_version();
std::string msg = fmt::format("WCF 支持版本: {},当前版本: {}", SUPPORT_VERSION, version);
if (version != SUPPORT_VERSION) {
LOG_ERROR(msg);
MessageBoxA(NULL, msg.c_str(), "微信版本错误", MB_ICONERROR);
return -2;
}
LOG_INFO(msg);
RpcStartServer(pp->port);
return 0;
}
void CleanupSpy()

View File

@ -2,7 +2,5 @@
#include "framework.h"
#define SUPPORT_VERSION L"3.9.11.25"
void InitSpy(int port);
int InitSpy(int port);
void CleanupSpy();

View File

@ -5,29 +5,42 @@
typedef uint64_t QWORD;
struct WxString {
class WxString
{
public:
const wchar_t *wptr;
DWORD size;
DWORD capacity;
const char *ptr;
DWORD clen;
WxString()
{
wptr = NULL;
wptr = nullptr;
size = 0;
capacity = 0;
ptr = NULL;
ptr = nullptr;
clen = 0;
}
WxString(std::wstring &ws)
WxString(std::wstring ws) : internal_ws(std::move(ws))
{
wptr = ws.c_str();
size = (DWORD)ws.size();
capacity = (DWORD)ws.capacity();
ptr = NULL;
wptr = internal_ws.c_str();
size = static_cast<DWORD>(internal_ws.size());
capacity = static_cast<DWORD>(internal_ws.capacity());
ptr = nullptr;
clen = 0;
}
WxString(const WxString &) = delete;
WxString &operator=(const WxString &) = delete;
WxString(WxString &&) noexcept = default;
WxString &operator=(WxString &&) noexcept = default;
private:
std::wstring internal_ws;
};
typedef struct RawVector {

View File

@ -1,58 +0,0 @@
#include "user_info.h"
#include "log.hpp"
#include "util.h"
extern UINT64 g_WeChatWinDllAddr;
#define OS_USER_HOME 0x5932770
#define OS_USER_WXID 0x595C270
#define OS_USER_NAME 0x595C3D8
#define OS_USER_MOBILE 0x595C318
static char home[MAX_PATH] = { 0 };
string GetHomePath()
{
if (home[0] == 0) {
string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\";
strncpy_s(home, path.c_str(), path.size());
}
return string(home);
}
string GetSelfWxid()
{
UINT64 wxidType = 0;
try {
wxidType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18);
if (wxidType == 0xF) {
return GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID);
} else {
return GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID);
}
} catch (...) {
LOG_ERROR("wxid type: {:#x}", wxidType);
LOG_BUFFER((uint8_t *)(g_WeChatWinDllAddr + OS_USER_WXID), 20);
return "empty_wxid";
}
}
UserInfo_t GetUserInfo()
{
UserInfo_t ui;
ui.wxid = GetSelfWxid();
UINT64 nameType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18);
if (nameType == 0xF) {
ui.name = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME);
} else { // 0x1F
ui.name = GET_STRING(g_WeChatWinDllAddr + OS_USER_NAME);
}
ui.mobile = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_MOBILE);
ui.home = GetHomePath();
return ui;
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <string>
#include "pb_types.h"
using namespace std;
string GetHomePath();
string GetSelfWxid();
UserInfo_t GetUserInfo();

View File

@ -0,0 +1,88 @@
#include "userinfo_manager.h"
#include <filesystem>
#include <mutex>
#include "log.hpp"
#include "rpc_helper.h"
#include "util.h"
extern UINT64 g_WeChatWinDllAddr;
namespace userinfo
{
#define OS_USER_HOME 0x5932770
#define OS_USER_WXID 0x595C270
#define OS_USER_NAME 0x595C3D8
#define OS_USER_MOBILE 0x595C318
std::string get_home_path()
{
static std::once_flag flag;
static std::string home_path;
std::call_once(flag, [] {
std::string path = util::w2s(util::get_pp_wstring(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\";
home_path = std::filesystem::absolute(path).string();
});
return home_path;
}
std::string get_self_wxid()
{
static std::once_flag flag;
static std::string wxid;
std::call_once(flag, [] {
UINT64 wxid_type = 0;
try {
wxid_type = util::get_qword(g_WeChatWinDllAddr + OS_USER_WXID + 0x18);
if (wxid_type == 0xF) {
wxid = util::get_p_string(g_WeChatWinDllAddr + OS_USER_WXID);
} else {
wxid = util::get_pp_string(g_WeChatWinDllAddr + OS_USER_WXID);
}
} catch (...) {
LOG_ERROR("Failed to get wxid, type: {:#x}", wxid_type);
LOG_BUFFER(reinterpret_cast<uint8_t *>(g_WeChatWinDllAddr + OS_USER_WXID), 20);
wxid = "获取wxid失败";
}
});
return wxid;
}
UserInfo_t get_user_info()
{
UserInfo_t ui;
ui.wxid = get_self_wxid();
UINT64 name_type = util::get_qword(g_WeChatWinDllAddr + OS_USER_NAME + 0x18);
ui.name = (name_type == 0xF) ? util::get_p_string(g_WeChatWinDllAddr + OS_USER_NAME)
: util::get_pp_string(g_WeChatWinDllAddr + OS_USER_NAME);
ui.mobile = util::get_p_string(g_WeChatWinDllAddr + OS_USER_MOBILE);
ui.home = get_home_path();
return ui;
}
bool rpc_get_self_wxid(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_SELF_WXID>(
out, len, [](Response &rsp) { rsp.msg.str = (char *)get_self_wxid().c_str(); });
}
bool rpc_get_user_info(uint8_t *out, size_t *len)
{
return fill_response<Functions_FUNC_GET_USER_INFO>(out, len, [](Response &rsp) {
UserInfo_t ui = get_user_info();
rsp.msg.ui.wxid = (char *)ui.wxid.c_str();
rsp.msg.ui.name = (char *)ui.name.c_str();
rsp.msg.ui.mobile = (char *)ui.mobile.c_str();
rsp.msg.ui.home = (char *)ui.home.c_str();
});
}
} // namespace userinfo

View File

@ -0,0 +1,24 @@
#pragma once
#include <optional>
#include <string>
#include "pb_types.h"
namespace userinfo
{
// 获取 WeChat 数据存储路径
std::string get_home_path();
// 获取自身 wxid
std::string get_self_wxid();
// 获取用户信息
UserInfo_t get_user_info();
// RPC 方法
bool rpc_get_self_wxid(uint8_t *out, size_t *len);
bool rpc_get_user_info(uint8_t *out, size_t *len);
} // namespace userinfo