parent
38db02437c
commit
4c13d86d41
@ -23,6 +23,7 @@ enum Functions {
|
|||||||
FUNC_ACCEPT_FRIEND = 0x51;
|
FUNC_ACCEPT_FRIEND = 0x51;
|
||||||
FUNC_RECV_TRANSFER = 0x52;
|
FUNC_RECV_TRANSFER = 0x52;
|
||||||
FUNC_REFRESH_PYQ = 0x53;
|
FUNC_REFRESH_PYQ = 0x53;
|
||||||
|
FUNC_DOWNLOAD_ATTACH = 0x54;
|
||||||
FUNC_DECRYPT_IMAGE = 0x60;
|
FUNC_DECRYPT_IMAGE = 0x60;
|
||||||
FUNC_ADD_ROOM_MEMBERS = 0x70;
|
FUNC_ADD_ROOM_MEMBERS = 0x70;
|
||||||
FUNC_DEL_ROOM_MEMBERS = 0x71;
|
FUNC_DEL_ROOM_MEMBERS = 0x71;
|
||||||
@ -45,6 +46,7 @@ message Request
|
|||||||
Transfer tf = 11;
|
Transfer tf = 11;
|
||||||
uint64 ui64 = 12; // 64 位整数,通用
|
uint64 ui64 = 12; // 64 位整数,通用
|
||||||
bool flag = 13;
|
bool flag = 13;
|
||||||
|
AttachMsg att = 14;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,3 +178,10 @@ message Transfer
|
|||||||
string tfid = 2; // 转账id transferid
|
string tfid = 2; // 转账id transferid
|
||||||
string taid = 3; // Transaction id
|
string taid = 3; // Transaction id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message AttachMsg
|
||||||
|
{
|
||||||
|
uint64 id = 1; // 消息 id
|
||||||
|
string thumb = 2; // 消息中的 thumb
|
||||||
|
string extra = 3; // 消息中的 extra
|
||||||
|
}
|
||||||
|
@ -159,3 +159,39 @@ DbRows_t ExecDbQuery(const string db, const string sql)
|
|||||||
}
|
}
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
|
||||||
|
{
|
||||||
|
DWORD msgMgrAddr = GET_DWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
|
||||||
|
DWORD dbIndex = GET_DWORD(msgMgrAddr + 0x38);
|
||||||
|
DWORD pStart = GET_DWORD(msgMgrAddr + 0x2C);
|
||||||
|
|
||||||
|
*dbIdx = 0;
|
||||||
|
for (int i = dbIndex - 1; i >= 0; i--) { // 从后往前遍历
|
||||||
|
DWORD dbAddr = GET_DWORD(pStart + i * 0x04);
|
||||||
|
if (dbAddr) {
|
||||||
|
string dbname = Wstring2String(GET_WSTRING(dbAddr));
|
||||||
|
dbMap[dbname] = GET_DWORD(dbAddr + 0x60);
|
||||||
|
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 = strtoul((const char *)(field.content.data()), NULL, 10);
|
||||||
|
*dbIdx = GET_DWORD(GET_DWORD(dbAddr + 0x18) + 0x144);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
@ -5,3 +5,4 @@
|
|||||||
DbNames_t GetDbNames();
|
DbNames_t GetDbNames();
|
||||||
DbTables_t GetDbTables(const string db);
|
DbTables_t GetDbTables(const string db);
|
||||||
DbRows_t ExecDbQuery(const string db, const string sql);
|
DbRows_t ExecDbQuery(const string db, const string sql);
|
||||||
|
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#pragma warning(disable : 4244)
|
#pragma warning(disable : 4244)
|
||||||
|
|
||||||
#include "framework.h"
|
#include "framework.h"
|
||||||
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "exec_sql.h"
|
||||||
#include "funcs.h"
|
#include "funcs.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "spy_types.h"
|
#include "spy_types.h"
|
||||||
@ -16,6 +18,7 @@
|
|||||||
#define HEADER_GIF2 0x49
|
#define HEADER_GIF2 0x49
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
extern bool gIsListeningPyq;
|
extern bool gIsListeningPyq;
|
||||||
extern WxCalls_t g_WxCalls;
|
extern WxCalls_t g_WxCalls;
|
||||||
@ -91,6 +94,47 @@ bool DecryptImage(string src, string dst)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string DecImage(string src)
|
||||||
|
{
|
||||||
|
ifstream in(src.c_str(), ios::binary);
|
||||||
|
if (!in.is_open()) {
|
||||||
|
LOG_ERROR("Failed to open file {}", src);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
filebuf *pfb = in.rdbuf();
|
||||||
|
size_t size = pfb->pubseekoff(0, ios::end, ios::in);
|
||||||
|
pfb->pubseekpos(0, ios::in);
|
||||||
|
|
||||||
|
char *pBuf = new char[size];
|
||||||
|
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 = fs::path(src).replace_extension(ext).string();
|
||||||
|
ofstream out(dst.c_str(), ios::binary);
|
||||||
|
if (!out.is_open()) {
|
||||||
|
LOG_ERROR("Failed to open file {}", dst);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write(pBuf, size);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
delete[] pBuf; // memory leak
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
static int GetFirstPage()
|
static int GetFirstPage()
|
||||||
{
|
{
|
||||||
int rv = -1;
|
int rv = -1;
|
||||||
@ -152,3 +196,121 @@ int RefreshPyq(uint64_t id)
|
|||||||
|
|
||||||
return GetNextPage(id);
|
return GetNextPage(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string DownloadAttach(uint64_t id, string thumb, string extra)
|
||||||
|
{
|
||||||
|
int status = -1;
|
||||||
|
uint64_t localId;
|
||||||
|
uint32_t dbIdx;
|
||||||
|
if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) {
|
||||||
|
LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
char buff[0x2D8] = { 0 };
|
||||||
|
DWORD dlCall1 = g_WeChatWinDllAddr + g_WxCalls.da.call1;
|
||||||
|
DWORD dlCall2 = g_WeChatWinDllAddr + g_WxCalls.da.call2;
|
||||||
|
DWORD dlCall3 = g_WeChatWinDllAddr + g_WxCalls.da.call3;
|
||||||
|
DWORD dlCall4 = g_WeChatWinDllAddr + g_WxCalls.da.call4;
|
||||||
|
DWORD dlCall5 = g_WeChatWinDllAddr + g_WxCalls.da.call5;
|
||||||
|
DWORD dlCall6 = g_WeChatWinDllAddr + g_WxCalls.da.call6;
|
||||||
|
|
||||||
|
__asm {
|
||||||
|
pushad;
|
||||||
|
pushfd;
|
||||||
|
lea ecx, buff;
|
||||||
|
call dlCall1;
|
||||||
|
call dlCall2;
|
||||||
|
push dword ptr [dbIdx];
|
||||||
|
lea ecx, buff;
|
||||||
|
push dword ptr [localId];
|
||||||
|
call dlCall3;
|
||||||
|
add esp, 0x8;
|
||||||
|
popfd;
|
||||||
|
popad;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD type = GET_DWORD(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建父目录,由于路径来源于微信,不做检查
|
||||||
|
fs::create_directory(fs::path(save_path).parent_path().string());
|
||||||
|
|
||||||
|
wstring wsSavePath = String2Wstring(save_path);
|
||||||
|
wstring wsThumbPath = String2Wstring(thumb_path);
|
||||||
|
|
||||||
|
WxString_t wxSavePath = { 0 };
|
||||||
|
WxString_t wxThumbPath = { 0 };
|
||||||
|
|
||||||
|
wxSavePath.text = (wchar_t *)wsSavePath.c_str();
|
||||||
|
wxSavePath.size = wsSavePath.size();
|
||||||
|
wxSavePath.capacity = wsSavePath.capacity();
|
||||||
|
|
||||||
|
wxThumbPath.text = (wchar_t *)wsThumbPath.c_str();
|
||||||
|
wxThumbPath.size = wsThumbPath.size();
|
||||||
|
wxThumbPath.capacity = wsThumbPath.capacity();
|
||||||
|
|
||||||
|
int temp = 1;
|
||||||
|
memcpy(&buff[0x19C], &wxThumbPath, sizeof(wxThumbPath));
|
||||||
|
memcpy(&buff[0x1B0], &wxSavePath, sizeof(wxSavePath));
|
||||||
|
memcpy(&buff[0x29C], &temp, sizeof(temp));
|
||||||
|
|
||||||
|
__asm {
|
||||||
|
pushad;
|
||||||
|
pushfd;
|
||||||
|
call dlCall4;
|
||||||
|
push 0x1;
|
||||||
|
push 0x0;
|
||||||
|
lea ecx, buff;
|
||||||
|
push ecx;
|
||||||
|
mov ecx, eax;
|
||||||
|
call dlCall5;
|
||||||
|
mov status, eax;
|
||||||
|
lea ecx, buff;
|
||||||
|
push 0x0;
|
||||||
|
call dlCall6;
|
||||||
|
popfd;
|
||||||
|
popad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存成功,如果是图片则需要解密。考虑异步?
|
||||||
|
if (type == 0x03) {
|
||||||
|
uint32_t cnt = 0;
|
||||||
|
while (cnt < 10) {
|
||||||
|
if (fs::exists(save_path)) {
|
||||||
|
return DecImage(save_path);
|
||||||
|
}
|
||||||
|
Sleep(500);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return save_path;
|
||||||
|
}
|
||||||
|
@ -5,3 +5,4 @@
|
|||||||
|
|
||||||
bool DecryptImage(std::string src, std::string dst);
|
bool DecryptImage(std::string src, std::string dst);
|
||||||
int RefreshPyq(uint64_t id);
|
int RefreshPyq(uint64_t id);
|
||||||
|
std::string DownloadAttach(uint64_t id, std::string thumb, std::string extra);
|
||||||
|
@ -27,7 +27,9 @@ WxCalls_t wxCalls = {
|
|||||||
{ 0x7B2E60, 0x15E2C20, 0x79C250 }, // Receive transfer
|
{ 0x7B2E60, 0x15E2C20, 0x79C250 }, // Receive transfer
|
||||||
/* Receive PYQ
|
/* Receive PYQ
|
||||||
hook, call, call1, call2, call3, start, end, ts, wxid, content, xml, step*/
|
hook, call, call1, call2, call3, start, end, ts, wxid, content, xml, step*/
|
||||||
{ 0x14F9E15, 0x14FA0A0, 0xC39680, 0x14E2140, 0x14E21E0, 0x20, 0x24, 0x2C, 0x18, 0x3C, 0x384, 0xB48 }
|
{ 0x14F9E15, 0x14FA0A0, 0xC39680, 0x14E2140, 0x14E21E0, 0x20, 0x24, 0x2C, 0x18, 0x3C, 0x384, 0xB48 },
|
||||||
|
/* call1, call2, call3, call4, call5, call6*/
|
||||||
|
{ 0x76F010, 0x792700, 0xBC0370, 0x80F110, 0x82BB40, 0x756E30}
|
||||||
};
|
};
|
||||||
|
|
||||||
int LoadCalls(const wchar_t *version, WxCalls_t *calls)
|
int LoadCalls(const wchar_t *version, WxCalls_t *calls)
|
||||||
|
@ -511,6 +511,29 @@ bool func_refresh_pyq(uint64_t id, uint8_t *out, size_t *len)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool func_download_attach(AttachMsg att, uint8_t *out, size_t *len)
|
||||||
|
{
|
||||||
|
Response rsp = Response_init_default;
|
||||||
|
rsp.func = Functions_FUNC_DOWNLOAD_ATTACH;
|
||||||
|
rsp.which_msg = Response_str_tag;
|
||||||
|
|
||||||
|
uint64_t id = att.id;
|
||||||
|
string thumb = string(att.thumb ? att.thumb : "");
|
||||||
|
string extra = string(att.extra ? att.extra : "");
|
||||||
|
|
||||||
|
string path = DownloadAttach(id, thumb, extra);
|
||||||
|
rsp.msg.str = (char *)path.c_str();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
bool func_decrypt_image(char *src, char *dst, uint8_t *out, size_t *len)
|
bool func_decrypt_image(char *src, char *dst, uint8_t *out, size_t *len)
|
||||||
{
|
{
|
||||||
Response rsp = Response_init_default;
|
Response rsp = Response_init_default;
|
||||||
@ -683,6 +706,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
|
|||||||
ret = func_refresh_pyq(req.msg.ui64, out, out_len);
|
ret = func_refresh_pyq(req.msg.ui64, out, out_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Functions_FUNC_DOWNLOAD_ATTACH: {
|
||||||
|
LOG_DEBUG("[Functions_FUNC_DOWNLOAD_ATTACH]");
|
||||||
|
ret = func_download_attach(req.msg.att, out, out_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Functions_FUNC_DECRYPT_IMAGE: {
|
case Functions_FUNC_DECRYPT_IMAGE: {
|
||||||
LOG_DEBUG("[FUNCTIONS_FUNC_DECRYPT_IMAGE]");
|
LOG_DEBUG("[FUNCTIONS_FUNC_DECRYPT_IMAGE]");
|
||||||
ret = func_decrypt_image(req.msg.dec.src, req.msg.dec.dst, out, out_len);
|
ret = func_decrypt_image(req.msg.dec.src, req.msg.dec.dst, out, out_len);
|
||||||
|
@ -102,6 +102,15 @@ typedef struct Pyq {
|
|||||||
DWORD step;
|
DWORD step;
|
||||||
} Pyq_t;
|
} Pyq_t;
|
||||||
|
|
||||||
|
typedef struct DlAttach{
|
||||||
|
DWORD call1;
|
||||||
|
DWORD call2;
|
||||||
|
DWORD call3;
|
||||||
|
DWORD call4;
|
||||||
|
DWORD call5;
|
||||||
|
DWORD call6;
|
||||||
|
} DlAttach_t;
|
||||||
|
|
||||||
typedef struct WxCalls {
|
typedef struct WxCalls {
|
||||||
DWORD login; // 登录状态
|
DWORD login; // 登录状态
|
||||||
UserInfoCall_t ui; // 用户信息
|
UserInfoCall_t ui; // 用户信息
|
||||||
@ -118,6 +127,7 @@ typedef struct WxCalls {
|
|||||||
RoomMember_t drm; // 删除群成员
|
RoomMember_t drm; // 删除群成员
|
||||||
TF_t tf; // 接收转账
|
TF_t tf; // 接收转账
|
||||||
Pyq_t pyq; // 接收朋友圈消息
|
Pyq_t pyq; // 接收朋友圈消息
|
||||||
|
DlAttach_t da; // 下载资源(图片、文件、视频)
|
||||||
} WxCalls_t;
|
} WxCalls_t;
|
||||||
|
|
||||||
typedef struct WxString {
|
typedef struct WxString {
|
||||||
|
Loading…
Reference in New Issue
Block a user