Compare commits

..
Author SHA1 Message Date
hugy b0e73002ff 新增群消息置顶,修改问题 close #9 2023-02-04 12:04:28 +08:00
hugy a2cfb13217 修改hook消息接口问题 2023-02-01 17:05:12 +08:00
hugy 911f87211c 修改hook消息接口 close #5 2023-02-01 17:01:53 +08:00
hugy 2f1793b2b1 拍一拍实现 2023-02-01 11:57:32 +08:00
hugy 6b36287c4d 修改文档 2023-02-01 10:39:41 +08:00
hugy d86d4c0bf6 新增拍一拍 2023-02-01 10:36:32 +08:00
hugy f82f85b2b6 修改文档错误描述 2023-01-31 16:50:18 +08:00
11 changed files with 391 additions and 19 deletions
+193 -14
View File
@@ -13,13 +13,14 @@ tool:简单的注入工具,一个是控制台,一个是图形界面。
python: 简单的服务器,用以接收消息内容。
release:编译好的dll。
0.首先安装对应的微信版本,主分支是3.8.0.41版本,3.8.1.26分支对应3.8.1.26版本。
1.通过cmake构建成功后,将wxhelper.dll注入到微信,本地启动tcp server,监听19088端口。
2.通过http协议与dll通信,方便客户端操作。
3.接口的url为http://127.0.0.1:19088,注入成功后,直接进行调用即可。
4.特别注意数据库查询接口需要先调用获取到句柄之后,才能进行查询。
5.相关功能只在win11环境下进行简单测试,其他环境无法保证。
6.注意个别接口在3.8.0.41版本没有实现,具体参考源码。
0.首先安装对应的微信版本,主分支是3.8.0.41版本,3.8.1.26分支对应3.8.1.26版本。
1.通过cmake构建成功后,将wxhelper.dll注入到微信,本地启动tcp server,监听19088端口。
2.通过http协议与dll通信,方便客户端操作。
3.接口的url为http://127.0.0.1:19088,注入成功后,直接进行调用即可。
4.特别注意数据库查询接口需要先调用获取到句柄之后,才能进行查询。
5.相关功能只在win11环境下进行简单测试,其他环境无法保证。
6.注意个别接口在3.8.0.41版本没有实现,具体参考源码。
7.对应分支接口文档都是支持指定版本的,其他版本不支持,请特别注意版本。
#### 编译环境
@@ -67,16 +68,30 @@ vcpkg
}
```
4.vscode中右键configure all projects,在Terminal中点击Run Task,如没有先配置build任务,然后运行即可
4.vscode中右键configure all projects,在Terminal中点击Run Task,如没有先配置build任务,然后运行即可
5.命令行注入工具,注入命令
``` javascript
//-i 注入程序名 -p 注入dll路径
// -u 卸载程序名 -d 卸载dll名称
//注入
ConsoleInject.exe -i demo.exe -p E:\testInject.dll
//卸载
ConsoleInject.exe -u demo.exe -d testInject.dll
```
#### 更新说明
2022-12-26 : 增加3.8.1.26版本支持。
2022-12-29 : 新增提取文字功能。
2022-01-02 : 退出微信登录。
2023-01-02 : 退出微信登录。
2022-01-31 : 新增修改群昵称(仅支持3.8.1.26)。
2023-01-31 : 新增修改群昵称(仅支持3.8.1.26)。
2023-02-01 : 新增拍一拍(仅支持3.8.1.26)。
2023-02-04 : 新增群消息置顶和取消置顶。
### 接口文档:
#### 0.检查微信登录**
@@ -287,7 +302,7 @@ vcpkg
|参数|必选|类型|说明|
|---|---|---|---|
|port |ture |string| 本地服务端端口,用来接收消息内容 |
|ip |ture |string| 服务端ip地址,用来接收消息内容,可以是任意ip,即tcp客户端连接的服务端的ip (3.8.1.26版本)|
###### 返回字段
|返回字段|字段类型|说明 |
@@ -301,6 +316,7 @@ vcpkg
``` javascript
{
"port": "19099"
"ip":"127.0.0.1"
}
```
响应:
@@ -309,6 +325,38 @@ vcpkg
```
#### 10.取消hook消息**
###### 接口功能
> 取消hook消息
###### 接口地址
> [/api/?type=10](/api/?type=10)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,1成功, 0失败|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
```
响应:
``` javascript
{"code":1,"result":"OK"}
```
#### 11.hook图片**
###### 接口功能
@@ -346,6 +394,38 @@ vcpkg
```
#### 12.取消hook图片**
###### 接口功能
> 取消hook图片
###### 接口地址
> [/api/?type=12](/api/?type=12)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,1成功, 0失败|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
```
响应:
``` javascript
{"code":1,"result":"OK"}
```
#### 17.删除好友**
###### 接口功能
@@ -504,7 +584,7 @@ vcpkg
#### 31.修改自身群昵称**
###### 接口功能
> 增加群成员
> 修改群名片
###### 接口地址
> [/api/?type=31](/api/?type=31)
@@ -829,20 +909,119 @@ vcpkg
|text|string|提取的相应文字|
#### 50.拍一拍**
###### 接口功能
> 群里拍一拍用户
###### 接口地址
> [/api/?type=50](/api/?type=50)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
|chatRoomId |ture |string| 微信群聊id |
|wxid |ture |string| 要拍的用户wxid,如果使用用户自定义的微信号,则不会显示群内昵称 |
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,1成功, -1失败|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
{
"imagePath":"C:\\3a610d7bc1cf5a15d12225a64b8962.dat"
"chatRoomId":"123331@chatroom",
"wxid":"wxid_123456"
}
```
响应:
``` javascript
{"code":0,"result":"OK","text":"搜索商品新闻简报小程序上线啦!!!我的收藏地址管理促销单品多买多省热门榜单热门榜单首发新品精品推荐精品推荐首发新品FRONTI警微安奈儿 童装冬季款男¥39900¥59.90帕拉丁品牌 时尚双背包409 00¥49.90我是有底线的首页分类购物车我的"}
{"code":1,"result":"OK"}
```
#### 51.群内消息置顶**
###### 接口功能
> 在群聊里置顶某条消息,可以置顶文字和图片消息,其他消息未测试,部分低版本移动端置顶消息点击后会直接取消,高版本会一直置顶,其他未测试。
###### 接口地址
> [/api/?type=51](/api/?type=51)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
|wxid |ture |string| 置顶消息的发送人wxid |
|msgid |ture |string| 消息id |
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,0成功, -1失败,-2 未查到该消息|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
{
"wxid":"wxid_oy11111p4422",
"msgid":3334956046278903121
}
```
响应:
``` javascript
{"code":0,"result":"OK"}
```
#### 52.取消群内消息置顶**
###### 接口功能
> 取消置顶的消息。部分低版本移动端会不显示移除消息,但是会正常移除。
###### 接口地址
> [/api/?type=52](/api/?type=52)
###### HTTP请求方式
> POST JSON
###### 请求参数
|参数|必选|类型|说明|
|---|---|---|---|
|chatRoomId |ture |string| 微信群聊id |
|msgid |ture |string| 消息id |
###### 返回字段
|返回字段|字段类型|说明 |
|---|---|---|
|code|int|返回状态,0成功, -1失败|
|result|string|成功提示|
###### 接口示例
入参:
``` javascript
{
"chatRoomId":"2136311004@chatroom",
"msgid":3374951233278903120
}
```
响应:
``` javascript
{"code":0,"result":"OK"}
```
#### 感谢
https://github.com/ljc545w/ComWeChatRobot
+30 -1
View File
@@ -19,6 +19,7 @@
#include "self_info.h"
#include "hook_img.h"
#include "ocr.h"
#include "pat.h"
#pragma comment(lib, "ws2_32.lib")
using namespace std;
@@ -266,7 +267,11 @@ void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) {
}
case WECHAT_MSG_START_HOOK: {
int port = get_http_req_param_int(hm, j_param, "port", is_post);
int success = HookRecvMsg(port);
wstring ip = get_http_req_param(hm, j_param, "ip", is_post);
string client_ip = Wstring2String(ip);
char ip_cstr[16];
strcpy_s(ip_cstr,client_ip.c_str());
int success = HookRecvMsg(ip_cstr,port);
json ret_data = {{"code", success}, {"result", "OK"}};
ret = ret_data.dump();
break;
@@ -555,6 +560,30 @@ void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) {
ret = ret_data.dump();
break;
}
case WECHAT_SEND_PAT_MSG:{
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
int success = SendPatMsg(WS2LW(room_id),WS2LW(wxid));
json ret_data = {{"code", success}, {"result", "OK"}};
ret = ret_data.dump();
break;
}
case WECHAT_SET_TOP_MSG:{
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post);
int success = SetTopMsg(WS2LW(wxid),msgid);
json ret_data = {{"code", success}, {"result", "OK"}};
ret = ret_data.dump();
break;
}
case WECHAT_REMOVE_TOP_MSG:{
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post);
int success = RemoveTopMsg(WS2LW(room_id),msgid);
json ret_data = {{"code", success}, {"result", "OK"}};
ret = ret_data.dump();
break;
}
default:
break;
}
+3
View File
@@ -64,6 +64,9 @@ typedef enum WECHAT_HTTP_APISTag
WECHAT_GET_CHATROOM_INFO,
WECHAT_GET_IMG_BY_NAME,
WECHAT_DO_OCR,
WECHAT_SEND_PAT_MSG,
WECHAT_SET_TOP_MSG,
WECHAT_REMOVE_TOP_MSG,
} WECHAT_HTTP_APIS,
*PWECHAT_HTTP_APIS;
+96 -1
View File
@@ -2,8 +2,9 @@
#include "chat_room.h"
#include "common.h"
#include "get_db_handle.h"
#include "wechat_data.h"
#include "base64.h"
#define WX_CHAT_ROOM_MGR_OFFSET 0x67ee70
#define WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET 0xa73a80
#define WX_NEW_CHAT_ROOM_INFO_OFFSET 0xd07010
@@ -16,6 +17,9 @@
#define WX_INIT_CHAT_ROOM_OFFSET 0xd04d80
#define WX_FREE_CHAT_ROOM_OFFSET 0xa7c620
#define WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET 0xa6f8f0
#define WX_NEW_CHAT_MSG_OFFSET 0x64adc0
#define WX_TOP_MSG_OFFSET 0xa76e60
#define WX_REMOVE_TOP_MSG_OFFSET 0xa76c50
int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) {
int success = 0;
@@ -216,5 +220,96 @@ int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick
MOV success,EAX
POPAD
}
return success;
}
int SetTopMsg(wchar_t* wxid,ULONG64 msg_id){
int success = -1;
char chat_msg[0x2A8] ={0};
DWORD base = GetWeChatWinBase();
DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET;
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
DWORD handle_top_msg_addr = base + WX_TOP_MSG_OFFSET;
vector<string> local_msg = GetChatMsgByMsgId(msg_id);
if(local_msg.empty()){
return -2;
}
string type = local_msg[3];
string status = local_msg[10];
string talker = local_msg[13];
string content = local_msg[14];
wstring w_talker = String2Wstring(talker);
wstring w_content = String2Wstring(content);
int msg_type =stoi(type);
int msg_status =stoi(status);
#ifdef _DEBUG
wcout << "w_talker:" <<w_talker <<endl;
wcout << "w_content:" <<w_content <<endl;
#endif
WeChatString chat_room(w_talker);
WeChatString msg_content(w_content);
WeChatString user_id(wxid);
__asm{
PUSHAD
LEA ECX,chat_msg
CALL new_chat_msg_addr
POPAD
}
memcpy(&chat_msg[0x30],&msg_id,sizeof(msg_id));
memcpy(&chat_msg[0x38],&msg_type,sizeof(msg_type));
memcpy(&chat_msg[0x40],&msg_status,sizeof(msg_status));
memcpy(&chat_msg[0x48],&chat_room,sizeof(chat_room));
memcpy(&chat_msg[0x70],&msg_content,sizeof(msg_content));
memcpy(&chat_msg[0x174],&user_id,sizeof(user_id));
__asm{
PUSHAD
CALL get_chat_room_mgr_addr
PUSH 0x1
LEA EAX,chat_msg
PUSH EAX
CALL handle_top_msg_addr
MOV success,EAX
POPAD
}
return success;
}
int RemoveTopMsg(wchar_t* chat_room_id,ULONG64 msg_id){
int success = -1;
DWORD left = (DWORD)(&msg_id);
DWORD right = (DWORD)(&msg_id+4);
WeChatString chat_room(chat_room_id);
DWORD base = GetWeChatWinBase();
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET;
DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET;
DWORD remove_top_msg_addr = base + WX_REMOVE_TOP_MSG_OFFSET;
__asm{
PUSHAD
CALL get_chat_room_mgr_addr
MOV EDI,dword ptr [msg_id]
LEA EAX,chat_room
MOV ESI,dword ptr [msg_id + 0x4]
SUB ESP,0x14
MOV ECX,ESP
PUSH EAX
CALL init_chat_msg_addr
PUSH ESI
PUSH EDI
CALL remove_top_msg_addr
MOV success,EAX
POPAD
}
return success;
}
+3
View File
@@ -8,4 +8,7 @@ int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len);
int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out);
int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick);
int SetTopMsg(wchar_t* wxid,ULONG64 msg_id);
int RemoveTopMsg(wchar_t* chat_room_id,ULONG64 msg_id);
#endif
+19
View File
@@ -296,4 +296,23 @@ unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex) {
return stoi(result[1][0]);
}
return 0;
}
vector<string> GetChatMsgByMsgId(ULONG64 msgid){
char sql[260] = {0};
sprintf_s(sql, "select localId,TalkerId,MsgSvrID,Type,SubType,IsSender,CreateTime,Sequence,StatusEx,FlagEx,Status,MsgServerSeq,MsgSequence,StrTalker,StrContent,BytesExtra from MSG where MsgSvrID=%llu;", msgid);
wchar_t dbname[20] = {0};
for (int i = 0;; i++) {
swprintf_s(dbname, L"MSG%d.db", i);
DWORD handle = GetDbHandleByDbName(dbname);
if (handle == 0) return {};
vector<vector<string>> result;
int ret = Select(handle, (const char *)sql, result);
#ifdef _DEBUG
cout <<" size =" <<result.size()<<endl;
#endif
if (result.size() == 0) continue;
return result[1];
}
return {};
}
+2
View File
@@ -2,8 +2,10 @@
#define GET_DB_HANDLE_H_
#include "windows.h"
#include <vector>
#include <string>
std::vector<void *> GetDbHandles();
DWORD GetDbHandleByDbName(wchar_t *dbname);
unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex);
std::vector<std::string> GetChatMsgByMsgId(ULONG64 msgid);
#endif
+6 -2
View File
@@ -20,6 +20,7 @@ using namespace std;
#define CLIENT_IP "127.0.0.1"
static int kServerPort = 0;
static int kMessageHooked = FALSE;
static char kServerIp[16]= "127.0.0.1";
static DWORD kWeChatWinBase = GetWeChatWinBase();
static char kOriginReceMsgAsmCode[5] = {0};
@@ -59,10 +60,12 @@ BOOL SendBySocket(const char *buffer, size_t len) {
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons((u_short)kServerPort);
InetPtonA(AF_INET, CLIENT_IP, &client_addr.sin_addr.s_addr);
InetPtonA(AF_INET, kServerIp, &client_addr.sin_addr.s_addr);
if (connect(client_socket, reinterpret_cast<sockaddr *>(&client_addr),
sizeof(sockaddr)) < 0) {
#ifdef _DEBUG
cout << "kServerIp:" << kServerIp <<endl;
cout << "port:" << kServerPort <<endl;
cout << "connect error,"
<< " errno:" << errno << endl;
#endif
@@ -190,8 +193,9 @@ _declspec(naked) void handle_sync_msg() {
/// @brief hook any address address+0x5
/// @param port 端口
/// @return 成功返回1,已经hook返回2,失败返回-1
int HookRecvMsg(int port) {
int HookRecvMsg(char* client_ip,int port) {
kServerPort = port;
strcpy_s(kServerIp,client_ip);
kWeChatWinBase = GetWeChatWinBase();
if (!kWeChatWinBase) {
return -1;
+1 -1
View File
@@ -4,7 +4,7 @@
/// @brief hook any address address+0x5
/// @param port 端口
/// @return 成功返回1,已经hook返回2
int HookRecvMsg(int port);
int HookRecvMsg(char* client_ip,int port);
int UnHookRecvMsg();
#endif
+34
View File
@@ -0,0 +1,34 @@
#include "pch.h"
#include "pat.h"
#include "common.h"
#include "wechat_data.h"
#define WX_PAT_MGR_OFFSET 0x7e91c0
#define WX_SEND_PAT_MSG_OFFSET 0x1228510
#define WX_RET_OFFSET 0x1AE4A45
int SendPatMsg(wchar_t* chat_room_id, wchar_t* wxid) {
int success = -1;
WeChatString chat_room(chat_room_id);
WeChatString self_wxid(wxid);
DWORD base = GetWeChatWinBase();
DWORD get_pat_mgr_addr = base + WX_PAT_MGR_OFFSET;
DWORD send_pat_msg_addr = base + WX_SEND_PAT_MSG_OFFSET;
DWORD ret_addr = base + WX_RET_OFFSET;
__asm {
PUSHAD
CALL get_pat_mgr_addr
PUSH ret_addr
PUSH 0x0
PUSH EAX
LEA ECX,chat_room
LEA EDX,self_wxid
CALL send_pat_msg_addr
ADD ESP,0xc
MOVZX EAX,AL
MOV success,EAX
POPAD
}
return success;
}
+4
View File
@@ -0,0 +1,4 @@
#ifndef PAT_H_
#define PAT_H_
int SendPatMsg(wchar_t* chat_room_id,wchar_t* wxid);
#endif