朋友圈消息hook和朋友圈消息获取

This commit is contained in:
hugy 2023-02-08 13:35:04 +08:00
parent a0f83b5a99
commit 254a9eeb30
6 changed files with 277 additions and 2 deletions

107
README.md
View File

@ -93,7 +93,9 @@ vcpkg
2023-02-04 新增群消息置顶和取消置顶。 2023-02-04 新增群消息置顶和取消置顶。
2023-02-04 新增确认收款。 2023-02-06 新增确认收款。
2023-02-08 新增朋友圈消息。
#### 功能预览: #### 功能预览:
0.检查是否登录 0.检查是否登录
@ -122,6 +124,8 @@ vcpkg
50.拍一拍 50.拍一拍
51.群消息置顶消息 51.群消息置顶消息
52.群消息取消置顶 52.群消息取消置顶
53.朋友圈首页
54.朋友圈下一页
### 接口文档: ### 接口文档:
@ -1129,6 +1133,107 @@ vcpkg
{"code":0,"result":"OK"} {"code":0,"result":"OK"}
``` ```
#### 53.朋友圈首页消息**
###### 接口功能
> 获取朋友圈最新消息调用之后会在tcpserver服务中收到朋友圈的消息。格式如下
``` javascript
{
'data': [
{
'content': '朋友圈[玫瑰][玫瑰]',
'createTime': 1675827480,
'senderId': 'wxid_12333',
'snsId': 14057859804711563695,
'xml': '<TimelineObject><id><![CDATA[1405712322563695]]></id><username><![CDATA[wxid_12333]]></username><createTime><![CDATA[1675827480]]></createTime><contentDescShowType>0</contentDescShowType><contentDescScene>0</contentDescScene><private><![CDATA[0]]></private><contentDesc><![CDATA[朋友圈[玫瑰][玫瑰]]]></contentDesc><contentattr><![CDATA[0]]></contentattr><sourceUserName></sourceUserName><sourceNickName></sourceNickName><statisticsData></statisticsData><weappInfo><appUserName></appUserName><pagePath></pagePath><version><![CDATA[0]]></version><debugMode><![CDATA[0]]></debugMode><shareActionId></shareActionId><isGame><![CDATA[0]]></isGame><messageExtraData></messageExtraData><subType><![CDATA[0]]></subType><preloadResources></preloadResources></weappInfo><canvasInfoXml></canvasInfoXml><ContentObject><contentStyle><![CDATA[2]]></contentStyle><contentSubStyle><![CDATA[0]]></contentSubStyle><title></title><description></description><contentUrl></contentUrl></ContentObject><actionInfo><appMsg><mediaTagName></mediaTagName><messageExt></messageExt><messageAction></messageAction></appMsg></actionInfo><appInfo><id></id></appInfo><location poiClassifyId="" poiName="" poiAddress="" poiClassifyType="0" city=""></location><publicUserName></publicUserName><streamvideo><streamvideourl></streamvideourl><streamvideothumburl></streamvideothumburl><streamvideoweburl></streamvideoweburl></streamvideo></TimelineObject>'
}]
}
```
###### 接口地址
> [/api/?type=53](/api/?type=53)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,1成功, -1失败|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
```
响应:
``` javascript
{"code":1,"result":"OK"}
```
#### 54.朋友圈下一页**
###### 接口功能
> 朋友圈下一页会在tcpserver服务中收到朋友圈的消息。格式如下
``` javascript
{
'data': [
{
'content': '朋友圈[玫瑰][玫瑰]',
'createTime': 1675827480,
'senderId': 'wxid_12333',
'snsId': 14057859804711563695,
'xml': '<TimelineObject><id><![CDATA[1405712322563695]]></id><username><![CDATA[wxid_12333]]></username><createTime><![CDATA[1675827480]]></createTime><contentDescShowType>0</contentDescShowType><contentDescScene>0</contentDescScene><private><![CDATA[0]]></private><contentDesc><![CDATA[朋友圈[玫瑰][玫瑰]]]></contentDesc><contentattr><![CDATA[0]]></contentattr><sourceUserName></sourceUserName><sourceNickName></sourceNickName><statisticsData></statisticsData><weappInfo><appUserName></appUserName><pagePath></pagePath><version><![CDATA[0]]></version><debugMode><![CDATA[0]]></debugMode><shareActionId></shareActionId><isGame><![CDATA[0]]></isGame><messageExtraData></messageExtraData><subType><![CDATA[0]]></subType><preloadResources></preloadResources></weappInfo><canvasInfoXml></canvasInfoXml><ContentObject><contentStyle><![CDATA[2]]></contentStyle><contentSubStyle><![CDATA[0]]></contentSubStyle><title></title><description></description><contentUrl></contentUrl></ContentObject><actionInfo><appMsg><mediaTagName></mediaTagName><messageExt></messageExt><messageAction></messageAction></appMsg></actionInfo><appInfo><id></id></appInfo><location poiClassifyId="" poiName="" poiAddress="" poiClassifyType="0" city=""></location><publicUserName></publicUserName><streamvideo><streamvideourl></streamvideourl><streamvideothumburl></streamvideothumburl><streamvideoweburl></streamvideoweburl></streamvideo></TimelineObject>'
}]
}
```
###### 接口地址
> [/api/?type=54](/api/?type=54)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
|snsId |ture |string| 朋友圈的snsId |
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,1成功, -1失败|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
```
响应:
``` javascript
{"code":1,"result":"OK"}
```
#### 感谢 #### 感谢
https://github.com/ljc545w/ComWeChatRobot https://github.com/ljc545w/ComWeChatRobot

View File

@ -21,6 +21,7 @@
#include "ocr.h" #include "ocr.h"
#include "pat.h" #include "pat.h"
#include "confirm_receipt.h" #include "confirm_receipt.h"
#include "sns.h"
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
using namespace std; using namespace std;
@ -591,6 +592,19 @@ void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) {
ret = ret_data.dump(); ret = ret_data.dump();
break; break;
} }
case WECHAT_SNS_GET_FIRST_PAGE:{
int success = GetFirstPage();
json ret_data = {{"code", success}, {"result", "OK"}};
ret = ret_data.dump();
break;
}
case WECHAT_SNS_GET_NEXT_PAGE:{
ULONG64 snsid = get_http_param_ulong64(hm, j_param, "snsId", is_post);
int success = GetNextPage(snsid);
json ret_data = {{"code", success}, {"result", "OK"}};
ret = ret_data.dump();
break;
}
default: default:
break; break;
} }

View File

@ -67,6 +67,8 @@ typedef enum WECHAT_HTTP_APISTag
WECHAT_SEND_PAT_MSG, WECHAT_SEND_PAT_MSG,
WECHAT_SET_TOP_MSG, WECHAT_SET_TOP_MSG,
WECHAT_REMOVE_TOP_MSG, WECHAT_REMOVE_TOP_MSG,
WECHAT_SNS_GET_FIRST_PAGE,
WECHAT_SNS_GET_NEXT_PAGE,
} WECHAT_HTTP_APIS, } WECHAT_HTTP_APIS,
*PWECHAT_HTTP_APIS; *PWECHAT_HTTP_APIS;

View File

@ -12,6 +12,8 @@ using namespace nlohmann;
using namespace std; using namespace std;
#define WX_RECV_MSG_HOOK_OFFSET 0xb97126 #define WX_RECV_MSG_HOOK_OFFSET 0xb97126
#define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x6fc850 #define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x6fc850
#define WX_SNS_HOOK_OFFSET 0x12fb9a5
#define WX_SNS_HOOK_NEXT_OFFSET 0x12fbc30
// SyncMgr::addMsgListToDB // SyncMgr::addMsgListToDB
// #define WX_RECV_MSG_HOOK_OFFSET 0xB9C919 // #define WX_RECV_MSG_HOOK_OFFSET 0xB9C919
@ -29,6 +31,13 @@ static DWORD kReceMsgJmpBackAddress =
static DWORD kReceMsgNextAddress = static DWORD kReceMsgNextAddress =
kWeChatWinBase + WX_RECV_MSG_HOOK_NEXT_OFFSET; kWeChatWinBase + WX_RECV_MSG_HOOK_NEXT_OFFSET;
static char kOriginSnsMsgAsmCode[5] = {0};
static DWORD kSnsMsgJmpBackAddress =
kWeChatWinBase + WX_SNS_HOOK_OFFSET + 0x5;
static DWORD kSnsMsgNextAddress =
kWeChatWinBase + WX_SNS_HOOK_NEXT_OFFSET;
struct InnerMessageStruct { struct InnerMessageStruct {
char *buffer; char *buffer;
int length; int length;
@ -112,7 +121,7 @@ void SendSocketMessage(InnerMessageStruct *msg) {
} }
/// @brief msg handle /// @brief msg handle
/// @param msg_addr msg address in memory /// @param msg_addr msg address in memory
void OnRecvMsg(DWORD msg_addr) { void __cdecl OnRecvMsg(DWORD msg_addr) {
json j_msg; json j_msg;
unsigned long long msgid = *(unsigned long long *)(msg_addr + 0x30); unsigned long long msgid = *(unsigned long long *)(msg_addr + 0x30);
j_msg["msgId"] = msgid; j_msg["msgId"] = msgid;
@ -175,6 +184,50 @@ void OnRecvMsg(DWORD msg_addr) {
CloseHandle(thread); CloseHandle(thread);
} }
} }
void __cdecl OnSnsTimeLineMsg(DWORD msg_addr) {
json j_sns;
DWORD begin_addr = *(DWORD *)(msg_addr + 0x20);
DWORD end_addr = *(DWORD *)(msg_addr + 0x24);
#ifdef _DEBUG
cout << "begin" <<begin_addr<<endl;
cout << "end" <<end_addr<<endl;
#endif
if (begin_addr == 0) {
j_sns = {{"data", json::array()}};
} else {
while (begin_addr < end_addr) {
json j_item;
j_item["snsId"] = *(unsigned long long *)(begin_addr);
j_item["createTime"] = *(DWORD *)(begin_addr + 0x2C);
j_item["senderId"] =
unicode_to_utf8((wchar_t *)READ_WSTRING(begin_addr, 0x18).c_str());
j_item["content"] =
unicode_to_utf8((wchar_t *)READ_WSTRING(begin_addr, 0x3c).c_str());
j_item["xml"] =
unicode_to_utf8((wchar_t *)READ_WSTRING(begin_addr, 0x384).c_str());
j_sns["data"].push_back(j_item);
begin_addr += 0xB48;
}
}
string jstr = j_sns.dump() + '\n';
InnerMessageStruct *inner_msg = new InnerMessageStruct;
inner_msg->buffer = new char[jstr.size() + 1];
memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1);
inner_msg->length = jstr.size();
HANDLE thread = CreateThread(
NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessage, inner_msg, NULL, 0);
if (thread) {
CloseHandle(thread);
}
}
/// @brief hook implement /// @brief hook implement
_declspec(naked) void handle_sync_msg() { _declspec(naked) void handle_sync_msg() {
__asm { __asm {
@ -190,6 +243,23 @@ _declspec(naked) void handle_sync_msg() {
} }
} }
/// @brief hook sns msg implement
_declspec(naked) void handle_sns_msg() {
__asm {
PUSHAD
PUSHFD
PUSH [ESP + 0x24]
CALL OnSnsTimeLineMsg
ADD ESP, 0x4
POPFD
POPAD
CALL kSnsMsgNextAddress
JMP kSnsMsgJmpBackAddress
}
}
/// @brief hook any address address+0x5 /// @brief hook any address address+0x5
/// @param port 端口 /// @param port 端口
/// @return 成功返回1,已经hook返回2,失败返回-1 /// @return 成功返回1,已经hook返回2,失败返回-1
@ -210,6 +280,14 @@ int HookRecvMsg(char* client_ip,int port) {
kReceMsgJmpBackAddress = hook_recv_msg_addr + 0x5; kReceMsgJmpBackAddress = hook_recv_msg_addr + 0x5;
HookAnyAddress(hook_recv_msg_addr, (LPVOID)handle_sync_msg, HookAnyAddress(hook_recv_msg_addr, (LPVOID)handle_sync_msg,
kOriginReceMsgAsmCode); kOriginReceMsgAsmCode);
DWORD hook_sns_msg_addr = kWeChatWinBase + WX_SNS_HOOK_OFFSET;
kSnsMsgNextAddress = kWeChatWinBase + WX_SNS_HOOK_NEXT_OFFSET;
kSnsMsgJmpBackAddress = hook_sns_msg_addr + 0x5;
HookAnyAddress(hook_sns_msg_addr, (LPVOID)handle_sns_msg,
kOriginSnsMsgAsmCode);
kMessageHooked = TRUE; kMessageHooked = TRUE;
return 1; return 1;
} }
@ -218,7 +296,9 @@ int UnHookRecvMsg() {
kServerPort = 0; kServerPort = 0;
if (!kMessageHooked) return 2; if (!kMessageHooked) return 2;
DWORD hook_recv_msg_addr = kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET; DWORD hook_recv_msg_addr = kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET;
DWORD hook_sns_addr = kWeChatWinBase + WX_SNS_HOOK_OFFSET;
UnHookAnyAddress(hook_recv_msg_addr, kOriginReceMsgAsmCode); UnHookAnyAddress(hook_recv_msg_addr, kOriginReceMsgAsmCode);
UnHookAnyAddress(hook_sns_addr, kOriginSnsMsgAsmCode);
kMessageHooked = FALSE; kMessageHooked = FALSE;
return 1; return 1;
} }

68
src/sns.cc Normal file
View File

@ -0,0 +1,68 @@
#include "pch.h"
#include "sns.h"
#include "common.h"
#include "wechat_data.h"
using namespace std;
#define WX_SNS_DATA_MGR_OFFSET 0xac66a0
#define WX_SNS_GET_FIRST_PAGE_OFFSET 0x12e46c0
#define WX_SNS_TIME_LINE_MGR_OFFSET 0x128e6a0
#define WX_SNS_TRY_GET_FIRST_PAGE_SCENE_OFFSET 0x12ff300
#define WX_SNS_GET_NEXT_PAGE_OFFSET 0x12e4760
int GetFirstPage() {
int success = -1;
DWORD base = GetWeChatWinBase();
DWORD sns_data_mgr_addr = base + WX_SNS_DATA_MGR_OFFSET;
DWORD get_first_page_addr = base + WX_SNS_GET_FIRST_PAGE_OFFSET;
DWORD time_line_mgr_addr = base + WX_SNS_TIME_LINE_MGR_OFFSET;
DWORD get_first_page_scene_addr = base + WX_SNS_TRY_GET_FIRST_PAGE_SCENE_OFFSET;
char buff[0xB44] = {};
__asm {
PUSHAD
CALL sns_data_mgr_addr
PUSH 0x1
LEA ECX,buff
PUSH ECX
MOV ECX,EAX
CALL get_first_page_addr
MOV success,EAX
POPAD
}
// __asm {
// PUSHAD
// CALL time_line_mgr_addr
// PUSH 0x1
// MOV ECX,EAX
// CALL get_first_page_scene_addr
// MOV success, EAX
// POPAD
// }
return success;
}
int GetNextPage(ULONG64 sns_id) {
int success = -1;
DWORD base = GetWeChatWinBase();
DWORD sns_data_mgr_addr = base + WX_SNS_DATA_MGR_OFFSET;
DWORD get_next_page_addr = base + WX_SNS_GET_NEXT_PAGE_OFFSET;
VectorInner temp = {};
__asm{
PUSHAD
CALL sns_data_mgr_addr
LEA ECX,temp
PUSH ECX
MOV EBX,dword ptr [sns_id + 0x4]
PUSH EBX
MOV EDI,dword ptr [sns_id ]
PUSH EDI
MOV ECX,EAX
CALL get_next_page_addr
MOV success,EAX
POPAD
}
return success;
}

6
src/sns.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef SNS_H_
#define SNS_H_
int GetFirstPage();
int GetNextPage(ULONG64 sns_id);
#endif