feat: 消息同步

This commit is contained in:
hugy 2023-10-26 20:25:10 +08:00
parent 2767576351
commit 8ab646be96
13 changed files with 490 additions and 3 deletions

View File

@ -56,6 +56,8 @@ bool CreateConsole();
void CloseConsole();
void HideModule(HMODULE module);
bool IsDigit(const std::string &str);
} // namespace utils
} // namespace base
#endif

View File

@ -1,7 +1,10 @@
#include "include/utils.h"
#include <fstream>
#include <winternl.h>
#include <fstream>
#include "utils.h"
namespace base {
namespace utils {
const std::string hex_table = "0123456789abcdef";
@ -151,5 +154,18 @@ void HideModule(HMODULE module) {
}
#endif
}
bool IsDigit(const std::string &str) {
if (str.length() == 0) {
return false;
}
for (auto it : str) {
if (it < '0' || it > '9') {
return false;
}
}
return true;
}
} // namespace utils
} // namespace base

View File

@ -23,6 +23,7 @@ void GlobalManager::initialize(HMODULE module) {
http_server = std::unique_ptr<http::HttpServer>(
new http::HttpServer(config->GetPort()));
http_server->AddHttpApiUrl("/api/sendTextMsg", SendTextMsg);
http_server->AddHttpApiUrl("/api/hookSyncMsg", HookSyncMsg);
http_server->Start();
base::ThreadPool::GetInstance().Create(2, 8);

View File

@ -0,0 +1,73 @@
#include "http_client.h"
namespace http {
void HttpClient::SendRequest(const std::string &content) {
struct mg_mgr mgr;
Data data;
data.done = false;
data.post_data = content;
mg_mgr_init(&mgr);
mg_http_connect(&mgr, kUrl.c_str(), OnHttpEvent, &data);
while (!data.done) {
mg_mgr_poll(&mgr, 500);
}
mg_mgr_free(&mgr);
data.done = false;
}
void HttpClient::SetConfig(std::string url, uint64_t timeout) {
kUrl = url;
kTimeout = timeout;
}
void HttpClient::OnHttpEvent(struct mg_connection *c, int ev, void *ev_data,
void *fn_data) {
const char *s_url = kUrl.c_str();
Data *data = (Data *)fn_data;
if (ev == MG_EV_OPEN) {
// Connection created. Store connect expiration time in c->data
*(uint64_t *)c->data = mg_millis() + kTimeout;
} else if (ev == MG_EV_POLL) {
if (mg_millis() > *(uint64_t *)c->data &&
(c->is_connecting || c->is_resolving)) {
mg_error(c, "Connect timeout");
}
} else if (ev == MG_EV_CONNECT) {
struct mg_str host = mg_url_host(s_url);
if (mg_url_is_ssl(s_url)) {
// no implement
}
// Send request
size_t content_length = data->post_data.size();
mg_printf(c,
"POST %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"Content-Type: application/json\r\n"
"Content-Length: %d\r\n"
"\r\n",
mg_url_uri(s_url), (int)host.len, host.ptr, content_length);
mg_send(c, data->post_data.c_str(), content_length);
} else if (ev == MG_EV_HTTP_MSG) {
// Response is received. Print it
#ifdef _DEBUG
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
printf("%.*s", (int)hm->message.len, hm->message.ptr);
#endif
// c->is_closing = 1; // Tell mongoose to close this connection
c->is_draining = 1;
data->done = true; // Tell event loop to stops
} else if (ev == MG_EV_ERROR) {
data->done = true; // Error, tell event loop to stop
} else if (ev == MG_EV_CLOSE) {
if (!data->done) {
data->done = true;
}
} else if (ev == MG_EV_HTTP_CHUNK) {
mg_error(c, "http chunk no implement");
c->is_closing = 1;
data->done = true;
}
}
} // namespace http

View File

@ -0,0 +1,26 @@
#ifndef WXHELPER_HTTP_CLIENT_H_
#define WXHELPER_HTTP_CLIENT_H_
#include <string>
#include "mongoose.h"
#include "singleton.h"
namespace http {
static std::string kUrl = "http://127.0.0.1:8000";
static uint64_t kTimeout = 3000;
struct Data {
bool done;
std::string post_data;
};
class HttpClient {
public:
static void SendRequest(const std::string &content);
static void SetConfig(std::string url, uint64_t timeout);
static void OnHttpEvent(struct mg_connection *c, int ev, void *ev_data,
void *fn_data);
};
} // namespace http
#endif

View File

@ -4,12 +4,34 @@
#include "utils.h"
#include "wechat_service.h"
#include "windows.h"
#include "wechat_hook.h"
#define STR2ULL(str) (base::utils::IsDigit(str) ? stoull(str) : 0)
#define STR2LL(str) (base::utils::IsDigit(str) ? stoll(str) : 0)
#define STR2I(str) (base::utils::IsDigit(str) ? stoi(str) : 0)
namespace wxhelper {
std::wstring GetWStringParam(nlohmann::json data, std::string key) {
return base::utils::Utf8ToWstring(data[key].get<std::string>());
}
int GetIntParam(nlohmann::json data, std::string key) {
int result;
try {
result = data[key].get<int>();
} catch (nlohmann::json::exception) {
result = STR2I(data[key].get<std::string>());
}
return result;
}
bool GetBoolParam(nlohmann::json data, std::string key) {
return data[key].get<bool>();
}
std::string GetStringParam(nlohmann::json data, std::string key) {
return data[key].get<std::string>();
}
std::string SendTextMsg(mg_http_message* hm) {
nlohmann::json j_param = nlohmann::json::parse(
hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false);
@ -21,4 +43,27 @@ std::string SendTextMsg(mg_http_message* hm) {
std::string ret = ret_data.dump();
return ret;
}
std::string HookSyncMsg(mg_http_message* hm) {
nlohmann::json j_param = nlohmann::json::parse(
hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false);
int port = GetIntParam(j_param, "port");
std::string ip = GetStringParam(j_param, "ip");
bool enable = GetBoolParam(j_param, "enableHttp");
std::string url = "http:://127.0.0.1:19088";
uint64_t timeout = 3000;
if (enable) {
url = GetStringParam(j_param, "url");
timeout = GetIntParam(j_param, "timeout");
}
hook::WechatHookParam param = {
ip, url, port, enable, timeout,
};
hook::WechatHook::GetInstance().Init(param);
INT64 success = hook::WechatHook::GetInstance().HookSyncMsg();
nlohmann::json ret_data = {
{"code", success}, {"data", {}}, {"msg", "success"}};
std::string ret = ret_data.dump();
return ret;
}
} // namespace wxhelper

View File

@ -5,6 +5,7 @@
#include "mongoose.h"
namespace wxhelper {
std::string SendTextMsg(struct mg_http_message *hm);
std::string HookSyncMsg(struct mg_http_message *hm);
}
#endif

View File

@ -487,6 +487,7 @@ namespace offset {
const UINT64 kGetSendMessageMgr = 0x8fe740;
const UINT64 kFreeChatMsg = 0x8fffc0;
const UINT64 kSendTextMsg = 0x1024370;
const UINT64 kDoAddMsg = 0x106b810;
} // namespace offset
namespace function {
typedef UINT64 (*__GetSendMessageMgr)();

View File

@ -0,0 +1,187 @@
#include <WS2tcpip.h>
#include "wechat_hook.h"
#include <detours/detours.h>
#include <windows.h>
#include <nlohmann/json.hpp>
#include "base64.h"
#include "http_client.h"
#include "spdlog/spdlog.h"
#include "thread_pool.h"
#include "wxutils.h"
namespace offset = wxhelper::V3_9_7_29::offset;
namespace common = wxhelper::common;
namespace hook {
static bool kEnableHttp = false;
static bool kLogHookFlag = false;
static char kServerIp[20] = "127.0.0.1";
static int kServerPort = 19099;
UINT64(*RealDoAddMsg)
(UINT64, UINT64, UINT64) = (UINT64(*)(UINT64, UINT64, UINT64))(
wxhelper::wxutils::GetWeChatWinBase() + offset::kDoAddMsg);
VOID SendMsgCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
PTP_WORK Work) {
common::InnerMessageStruct *msg = (common::InnerMessageStruct *)context;
if (msg == NULL) {
SPDLOG_INFO("add work:msg is null");
return;
}
std::unique_ptr<common::InnerMessageStruct> sms(msg);
nlohmann::json j_msg = nlohmann::json::parse(
msg->buffer, msg->buffer + msg->length, nullptr, false);
if (j_msg.is_discarded() == true) {
return;
}
std::string jstr = j_msg.dump() + "\n";
WSADATA was_data = {0};
int ret = WSAStartup(MAKEWORD(2, 2), &was_data);
if (ret != 0) {
SPDLOG_ERROR("WSAStartup failed:{}", ret);
return;
}
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket < 0) {
SPDLOG_ERROR("socket init fail");
return;
}
BOOL status = false;
sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons((u_short)kServerPort);
InetPtonA(AF_INET, kServerIp, &client_addr.sin_addr.s_addr);
if (connect(client_socket, reinterpret_cast<sockaddr *>(&client_addr),
sizeof(sockaddr)) < 0) {
SPDLOG_ERROR("socket connect fail. host:{} , port:{},",kServerIp,kServerPort);
goto clean;
}
char recv_buf[1024] = {0};
ret = send(client_socket, jstr.c_str(), static_cast<int>(jstr.size()), 0);
if (ret < 0) {
SPDLOG_ERROR("socket send fail ,ret:{}", ret);
goto clean;
}
ret = shutdown(client_socket, SD_SEND);
if (ret == SOCKET_ERROR) {
SPDLOG_ERROR("shutdown failed with erro:{}", ret);
goto clean;
}
ret = recv(client_socket, recv_buf, sizeof(recv_buf), 0);
if (ret < 0) {
SPDLOG_ERROR("socket recv fail ,ret:{}", ret);
goto clean;
}
clean:
closesocket(client_socket);
WSACleanup();
return;
}
VOID SendHttpMsgCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
PTP_WORK Work) {
common::InnerMessageStruct *msg = (common::InnerMessageStruct *)context;
if (msg == NULL) {
SPDLOG_INFO("http msg is null");
return;
}
std::unique_ptr<common::InnerMessageStruct> sms(msg);
nlohmann::json j_msg = nlohmann::json::parse(
msg->buffer, msg->buffer + msg->length, nullptr, false);
if (j_msg.is_discarded() == true) {
return;
}
std::string jstr = j_msg.dump() + "\n";
http::HttpClient::SendRequest(jstr);
}
void HandleSyncMsg(INT64 param1, INT64 param2, INT64 param3) {
nlohmann::json msg;
msg["pid"] = GetCurrentProcessId();
msg["fromUser"] =
wxhelper::wxutils::ReadSKBuiltinString(*(INT64 *)(param2 + 0x18));
msg["toUser"] =
wxhelper::wxutils::ReadSKBuiltinString(*(INT64 *)(param2 + 0x28));
msg["content"] =
wxhelper::wxutils::ReadSKBuiltinString(*(INT64 *)(param2 + 0x30));
msg["signature"] =
wxhelper::wxutils::ReadWeChatStr(*(INT64 *)(param2 + 0x48));
msg["msgId"] = *(INT64 *)(param2 + 0x60);
msg["msgSequence"] = *(DWORD *)(param2 + 0x5C);
msg["createTime"] = *(DWORD *)(param2 + 0x58);
msg["displayFullContent"] =
wxhelper::wxutils::ReadWeChatStr(*(INT64 *)(param2 + 0x50));
DWORD type = *(DWORD *)(param2 + 0x24);
msg["type"] = type;
if (type == 3) {
int a = 1;
std::string img =
wxhelper::wxutils::ReadSKBuiltinBuffer(*(INT64 *)(param2 + 0x40));
SPDLOG_INFO("encode size:{}", img.size());
msg["base64Img"] = base64_encode(img);
a = 2;
}
std::string jstr = msg.dump() + '\n';
common::InnerMessageStruct *inner_msg = new common::InnerMessageStruct;
inner_msg->buffer = new char[jstr.size() + 1];
memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1);
inner_msg->length = jstr.size();
if (kEnableHttp) {
bool add =
base::ThreadPool::GetInstance().AddWork(SendHttpMsgCallback, inner_msg);
SPDLOG_INFO("add http msg work:{}", add);
} else {
bool add =
base::ThreadPool::GetInstance().AddWork(SendMsgCallback, inner_msg);
SPDLOG_INFO("add msg work:{}", add);
}
RealDoAddMsg(param1, param2, param3);
}
void WechatHook::Init(WechatHookParam param) {
if(!init_){
param_ = param;
kEnableHttp = param.enable_http;
}
}
int WechatHook::HookSyncMsg() {
if (sync_msg_flag_) {
SPDLOG_INFO("recv msg hook already called");
return 2;
}
if (param_.server_ip.size() < 1) {
return -2;
}
UINT64 base = wxhelper::wxutils::GetWeChatWinBase();
if (!base) {
SPDLOG_INFO("base addr is null");
return -1;
}
if (param_.enable_http) {
http::HttpClient::SetConfig(param_.http_url,param_.http_time_out);
}else{
const char* charPtr = param_.server_ip.c_str();
std::strcpy(kServerIp, charPtr);
kServerPort = param_.server_port;
}
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID &)RealDoAddMsg, &HandleSyncMsg);
LONG ret = DetourTransactionCommit();
if (ret == NO_ERROR) {
sync_msg_flag_ = true;
}
return ret;
}
} // namespace hook

View File

@ -0,0 +1,34 @@
#ifndef WXHELPER_WECHAT_HOOK_H_
#define WXHELPER_WECHAT_HOOK_H_
#include "wechat_function.h"
#include "singleton.h"
namespace hook {
struct WechatHookParam {
std::string server_ip = "127.0.0.1";
std::string http_url = "http:://127.0.0.1:19088";
int server_port = 19099;
bool enable_http = false;
uint64_t http_time_out = 3000;
};
class WechatHook :public base::Singleton<WechatHook>{
public:
void Init(WechatHookParam param);
int HookSyncMsg();
int UnHookSyncMsg();
int HookLog();
int UnHookLog();
private:
WechatHookParam param_;
bool sync_msg_flag_;
bool init_;
};
} // namespace hook
#endif

View File

@ -179,4 +179,5 @@ void WechatService::SetBaseAddr(UINT64 addr) { base_addr_ = addr; }
void WechatService::SetJsApiAddr(UINT64 addr) { js_api_addr_ = addr; }
} // namespace wxhelper

View File

@ -1,7 +1,96 @@
#include "wxutils.h"
#include "utils.h"
#define BUFSIZE 1024
#define JPEG0 0xFF
#define JPEG1 0xD8
#define JPEG2 0xFF
#define PNG0 0x89
#define PNG1 0x50
#define PNG2 0x4E
#define BMP0 0x42
#define BMP1 0x4D
#define GIF0 0x47
#define GIF1 0x49
#define GIF2 0x46
namespace wxhelper {
namespace wxutils {
UINT64 GetWeChatWinBase() { return (UINT64)GetModuleHandleA("WeChatWin.dll"); }
std::string ReadSKBuiltinString(INT64 addr) {
INT64 inner_string = *(INT64 *)(addr + 0x8);
if (inner_string == 0) {
return std::string();
}
return ReadWeChatStr(inner_string);
}
std::string ReadSKBuiltinBuffer(INT64 addr) {
INT64 len = *(INT64 *)(addr + 0x10);
if (len == 0) {
return std::string();
}
INT64 inner_string = *(INT64 *)(addr + 0x8);
if (inner_string == 0) {
return std::string();
}
return ReadWeChatStr(inner_string);
}
std::string ReadWeChatStr(INT64 addr) {
INT64 len = *(INT64 *)(addr + 0x10);
if (len == 0) {
return std::string();
}
INT64 max_len = *(INT64 *)(addr + 0x18);
if ((max_len | 0xF) == 0xF) {
return std::string((char *)addr, len);
}
char *char_from_user = *(char **)(addr);
return std::string(char_from_user, len);
}
std::string ImageXor(std::string buf) {
const char *origin = buf.c_str();
short key = 0;
if ((*origin ^ JPEG0) == (*(origin + 1) ^ JPEG1)) {
key = *origin ^ JPEG0;
} else if ((*origin ^ PNG1) == (*(origin + 1) ^ PNG2)) {
key = *origin ^ PNG1;
} else if ((*origin ^ GIF0) == (*(origin + 1) ^ GIF1)) {
key = *origin ^ GIF0;
} else if ((*origin ^ BMP0) == (*(origin + 1) ^ BMP1)) {
key = *origin ^ BMP0;
} else {
key = -1;
}
if (key > 0) {
char *img_buf = new char[buf.size()];
for (unsigned int i = 0; i < buf.size(); i++) {
img_buf[i] = *(origin + i) ^ key;
}
std::string str(img_buf);
delete[] img_buf;
img_buf = NULL;
return str;
}
return std::string();
}
std::wstring ReadWstring(INT64 addr) {
DWORD len = *(DWORD *)(addr + 0x8);
if (len == 0) {
return std::wstring();
}
wchar_t *str = *(wchar_t **)(addr);
if (str == NULL) {
return std::wstring();
}
return std::wstring(str, len);
}
std::string ReadWstringThenConvert(INT64 addr) {
std::wstring wstr = ReadWstring(addr);
return base::utils::WstringToUtf8(wstr);
}
} // namespace wxutils
} // namespace wxhelper

View File

@ -1,10 +1,21 @@
#ifndef WXHELPER_WXUTILS_H_
#define WXHELPER_WXUTILS_H_
#include <windows.h>
#include <string>
namespace wxhelper {
namespace wxutils {
UINT64 GetWeChatWinBase();
}
std::string ReadSKBuiltinString(INT64 addr);
std::string ReadSKBuiltinBuffer(INT64 addr);
std::string ReadWeChatStr(INT64 addr);
std::string ImageXor(std::string buf);
std::wstring ReadWstring(INT64 addr);
std::string ReadWstringThenConvert(INT64 addr);
INT64 DecodeImage(const wchar_t* file_path, const wchar_t* save_dir);
} // namespace wxutils
} // namespace wxhelper
#endif