From e492836c57ff3bc8edf54c31f00559ba7116c15e Mon Sep 17 00:00:00 2001 From: Gy Hu Date: Wed, 21 Dec 2022 20:30:44 +0800 Subject: [PATCH] init commit --- CMakeLists.txt | 36 +++ README.md | 677 +++++++++++++++++++++++++++++++++++++++++++ python/tcpserver.py | 57 ++++ src/api.cc | 599 ++++++++++++++++++++++++++++++++++++++ src/api.h | 71 +++++ src/base64.cpp | 312 ++++++++++++++++++++ src/base64.h | 35 +++ src/chat_room.cc | 184 ++++++++++++ src/chat_room.h | 10 + src/common.cc | 138 +++++++++ src/common.h | 83 ++++++ src/contact.cc | 106 +++++++ src/contact.h | 11 + src/db_operation.cc | 155 ++++++++++ src/db_operation.h | 23 ++ src/dllMain.cc | 23 ++ src/forward.cc | 41 +++ src/forward.h | 5 + src/framework.h | 7 + src/get_db_handle.cc | 299 +++++++++++++++++++ src/get_db_handle.h | 9 + src/hook_img.cc | 231 +++++++++++++++ src/hook_img.h | 10 + src/hook_recv_msg.cc | 220 ++++++++++++++ src/hook_recv_msg.h | 10 + src/new_sqlite3.h | 195 +++++++++++++ src/pch.h | 18 ++ src/self_info.cc | 139 +++++++++ src/self_info.h | 7 + src/send_file.cc | 67 +++++ src/send_file.h | 5 + src/send_image.cc | 47 +++ src/send_image.h | 5 + src/send_text.cc | 45 +++ src/send_text.h | 6 + src/wechat_data.h | 140 +++++++++ 36 files changed, 4026 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 python/tcpserver.py create mode 100644 src/api.cc create mode 100644 src/api.h create mode 100644 src/base64.cpp create mode 100644 src/base64.h create mode 100644 src/chat_room.cc create mode 100644 src/chat_room.h create mode 100644 src/common.cc create mode 100644 src/common.h create mode 100644 src/contact.cc create mode 100644 src/contact.h create mode 100644 src/db_operation.cc create mode 100644 src/db_operation.h create mode 100644 src/dllMain.cc create mode 100644 src/forward.cc create mode 100644 src/forward.h create mode 100644 src/framework.h create mode 100644 src/get_db_handle.cc create mode 100644 src/get_db_handle.h create mode 100644 src/hook_img.cc create mode 100644 src/hook_img.h create mode 100644 src/hook_recv_msg.cc create mode 100644 src/hook_recv_msg.h create mode 100644 src/new_sqlite3.h create mode 100644 src/pch.h create mode 100644 src/self_info.cc create mode 100644 src/self_info.h create mode 100644 src/send_file.cc create mode 100644 src/send_file.h create mode 100644 src/send_image.cc create mode 100644 src/send_image.h create mode 100644 src/send_text.cc create mode 100644 src/send_text.h create mode 100644 src/wechat_data.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..372c064 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.0.0) +project(wxhelper VERSION 1.0.0) + + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D '0_UNICODE' /D 'UNICODE'") + +file(GLOB CPP_FILES ${PROJECT_SOURCE_DIR}/src/*.cc ${PROJECT_SOURCE_DIR}/src/*.cpp) + + + +include_directories(c:/soft/vcpkg/installed/x86-windows/include) + +# add_subdirectory(3rd) + + +find_package(nlohmann_json CONFIG REQUIRED) +find_package(unofficial-mongoose CONFIG REQUIRED) + +add_library(wxhelper SHARED ${CPP_FILES} ) + + +#target_include_directories(wxhelper PUBLIC 3rd/mongoose ) + +target_link_libraries(wxhelper PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(wxhelper PRIVATE unofficial::mongoose::mongoose) + +SET_TARGET_PROPERTIES(wxhelper PROPERTIES LINKER_LANGUAGE C + ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib + LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib + RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib + OUTPUT_NAME "wxhelper" + PREFIX "") + \ No newline at end of file diff --git a/README.md b/README.md index 2517bce..48c88a8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,679 @@ # wxhelper wechat hook . +#### 免责声明: +本仓库发布的内容,仅用于学习研究,请勿用于非法用途和商业用途!如因此产生任何法律纠纷,均与作者无关! + +#### 项目说明: +本项目是个人学习学习逆向的项目,主要参考https://github.com/ttttupup/ComWeChatRobot,在此基础上实现了wechat 3.8.0.41的版本的部分内容。 + +#### 使用说明: +支持的版本3.8.0.41,目前是最新版本。 +src:主要的dll代码 +tool:简单的注入工具,一个是控制台,一个是图形界面。 +python: 简单的服务器,用以接收消息内容。 +release:编译好的dll。 + +1.通过cmake构建成功后,将wxhelper.dll注入到微信,本地启动tcp server,监听19088端口。 +2.通过http协议与dll通信,方便客户端操作。 +3.接口的url为http://127.0.0.1:19088,注入成功后,直接进行调用即可。 +4.特别注意数据库查询接口需要先调用获取到句柄之后,才能进行查询。 + + + + +### 接口文档: + +#### 0.检查微信登录** +###### 接口功能 +> 检查微信是否登录 + +###### 接口地址 +> [/api/?type=0](/api/?type=0) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1 成功, 0失败| +|result|string|成功提示| +|data|string|响应内容| + +###### 接口示例 +地址:https://www.bincoding.cn/test +入参: +``` javascript +``` +响应: +``` javascript +{ + "code": 1, + "result": "ok" +} +``` + +#### 1.获取登录用户信息** +###### 接口功能 +> 获取登录用户信息 + +###### 接口地址 +> [/api/?type=1](/api/?type=1) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1 成功, 0失败| +|result|string|成功提示| +|data|object|响应内容| +|account|string|账号| +|bigImage|string|头像大图| +|city|string|城市| +|country|string|国家| +|currentDataPath|string|当前数据目录| +|dataRootPath|string|根目录| +|dataSavePath|string|保存目录| +|mobile|string|手机| +|name|string|昵称| +|province|string|省| +|smallImage|string|小头像| +|wxid|string|wxid| + +###### 接口示例 +地址:https://www.bincoding.cn/test +入参: +``` javascript +``` +响应: +``` javascript +{"code":1,"data":{"account":"xx","bigImage":"https://wx.qlogo.cn/mmhead/ver_1xx","city":"xx","country":"CN","currentDataPath":"xxx","dataRootPath":"C:\\xx","dataSavePath":"C:\\xx","mobie":"13949175447","name":"xx","province":"xx","smallImage":"xx","wxid":"xx"},"result":"OK"} +``` + + + + +#### 2.发送文本消息** +###### 接口功能 +> 发送文本消息 + +###### 接口地址 +> [/api/?type=2](/api/?type=2) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid |ture |string| 接收人wxid | +|msg|true |string|消息文本内容| + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,不为0成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +地址:https://www.bincoding.cn/test +入参: +``` javascript +{ + "wxid": "filehelper", + "msg": "1112222" +} +``` +响应: +``` javascript +{"code":345686720,"result":"OK"} +``` + + + +#### 5.发送图片消息** +###### 接口功能 +> 发送图片消息 + +###### 接口地址 +> [/api/?type=5](/api/?type=5) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid |ture |string| 接收人wxid | +|imagePath|true |string|图片路径| + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,不为0成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "wxid": "filehelper", + "imagePath": "C:/Users/123.png" +} +``` +响应: +``` javascript +{"code":345686724,"result":"OK"} +``` + + + +#### 6.发送文件消息** +###### 接口功能 +> 发送文件 + +###### 接口地址 +> [/api/?type=6](/api/?type=6) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid |ture |string| 接收人wxid | +|filePath|true |string|文件路径| + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "wxid": "filehelper", + "filePath": "C:/Users/123.txt" +} +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + +#### 9.hook消息** +###### 接口功能 +> hook接收文本消息,图片消息,群消息.该接口将hook的消息通过tcp回传给本地的端口 + +###### 接口地址 +> [/api/?type=9](/api/?type=9) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|port |ture |string| 本地服务端端口,用来接收消息内容 | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "port": "19099" +} +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + + +#### 11.hook图片** +###### 接口功能 +> hook图片原始内容,不推荐该接口,可以使用图片查询接口 + +###### 接口地址 +> [/api/?type=11](/api/?type=11) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|imgDir |ture |string| 图片保存的目录 | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "imgDir":"C:\\other" +} +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + + +#### 17.删除好友** +###### 接口功能 +> 删除好友 + +###### 接口地址 +> [/api/?type=17](/api/?type=17) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid |ture |string| 好友wxid | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "wxid":"wxid_o" +} +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + + +#### 25.获取群成员** +###### 接口功能 +> 获取群成员 + +###### 接口地址 +> [/api/?type=25](/api/?type=25) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId |ture |string| 群id | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| +|data|object|返回内容| +|admin|string|群主id| +|chatRoomId|string|群id| +|members|string|群成员id以^分隔| + + +###### 接口示例 +入参: +``` javascript +{ + "chatRoomId":"123@chatroom" +} +``` +响应: +``` javascript +{"code":1,"data":{"admin":"wxid","chatRoomId":"123@chatroom","members":"wxid_123^Gwxid_456^Gwxid_45677"},"result":"OK"} +``` + + + +#### 27.删除群成员** +###### 接口功能 +> 删除群成员 + +###### 接口地址 +> [/api/?type=27](/api/?type=27) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId |ture |string| 群id | +|memberIds |ture |string| 成员id,以,分割 | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "chatRoomId":"34932563384@chatroom", + "memberIds":"wxid_oyb662qhop4422" +} +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + + +#### 28.增加群成员** +###### 接口功能 +> 增加群成员 + +###### 接口地址 +> [/api/?type=28](/api/?type=28) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId |ture |string| 群id | +|memberIds |ture |string| 成员id,以,分割 | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + +###### 接口示例 +入参: +``` javascript +{ + "chatRoomId":"34932563384@chatroom", + "memberIds":"wxid_oyb662qhop4422" +} +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + + + + +#### 32.获取数据库句柄** +###### 接口功能 +> 获取sqlite3数据库句柄 + +###### 接口地址 +> [/api/?type=32](/api/?type=32) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| +|data|array|返回数据| +|databaseName|string|数据库名称| +|handle|int|数据库句柄| +|tables|array|表信息| +|name|string|表名| +|rootpage|string|rootpage| +|sql|string|sql| +|tableName|string|tableName| + + +###### 接口示例 +入参: +``` javascript + +``` +响应: +``` javascript +{ + "data": [ + { + "databaseName": "MicroMsg.db", + "handle": 119561688, + "tables": [ + { + "name": "Contact", + "rootpage": "2", + "sql": "CREATE TABLE Contact(UserName TEXT PRIMARY KEY ,Alias TEXT,EncryptUserName TEXT,DelFlag INTEGER DEFAULT 0,Type INTEGER DEFAULT 0,VerifyFlag INTEGER DEFAULT 0,Reserved1 INTEGER DEFAULT 0,Reserved2 INTEGER DEFAULT 0,Reserved3 TEXT,Reserved4 TEXT,Remark TEXT,NickName TEXT,LabelIDList TEXT,DomainList TEXT,ChatRoomType int,PYInitial TEXT,QuanPin TEXT,RemarkPYInitial TEXT,RemarkQuanPin TEXT,BigHeadImgUrl TEXT,SmallHeadImgUrl TEXT,HeadImgMd5 TEXT,ChatRoomNotify INTEGER DEFAULT 0,Reserved5 INTEGER DEFAULT 0,Reserved6 TEXT,Reserved7 TEXT,ExtraBuf BLOB,Reserved8 INTEGER DEFAULT 0,Reserved9 INTEGER DEFAULT 0,Reserved10 TEXT,Reserved11 TEXT)", + "tableName": "Contact" + } + ] + } + ], + "result": "OK" +} +``` + + + +#### 34.查询数据库** +###### 接口功能 +> 查询数据库 + +###### 接口地址 +> [/api/?type=34](/api/?type=34) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|dbHandle |ture |int| 句柄 | +|sql |ture |string| sql语句 | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| +|data|array|返回数据| + + + +###### 接口示例 +入参: +``` javascript +{ + "dbHandle": 219277920, + "sql":"select * from MSG where MsgSvrID=8985035417589024392" +} +``` +响应: +``` javascript +{"code":1,"data":[["localId","TalkerId","MsgSvrID","Type","SubType","IsSender","CreateTime","Sequence","StatusEx","FlagEx","Status","MsgServerSeq","MsgSequence","StrTalker","StrContent","DisplayContent","Reserved0","Reserved1","Reserved2","Reserved3","Reserved4","Reserved5","Reserved6","CompressContent","BytesExtra","BytesTrans"],["6346","24","8985035417589024392","1","0","0","1670897832","1670897832000","0","0","2","1","778715089","wxid_1222","112","","0","2","","","","","","","CgQIEBAAGkEIBxI9PG1zZ3NvdXJjZT4KCTxzaWduYXR1cmU+djFfSFFyeVAwZTE8L3NpZ25hdHVyZT4KPC9tc2dzb3VyY2U+ChokCAISIDU5NjI1NjUxNWE0YzU2ZDQxZDJlOWMyYmIxMjFhNmZl",""]],"result":"OK"} +``` + + + +#### 46.联系人列表** +###### 接口功能 +> 联系人列表 + +###### 接口地址 +> [/api/?type=46](/api/?type=46) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| +|data|array|返回内容| +|customAccount|string|自定义账号| +|delFlag|int|删除标志| +|type|int|好友类型| +|userName|string|用户名称| +|verifyFlag|int|验证| +|wxid|string|wxid| + + + +###### 接口示例 +入参: +``` javascript +{ + "wxid": "filehelper", + "msgid":7215505498606506901 +} +``` +响应: +``` javascript +{"code":1,"data":[{"customAccount":"custom","delFlag":0,"type":8388611,"userName":"昵称","verifyFlag":0,"wxid":"wxid_123pcqm22"}]} +``` + + +#### 47.群详情** +###### 接口功能 +> 获取群详情 + +###### 接口地址 +> [/api/?type=47](/api/?type=47) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId |ture |string| 群id | + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| +|data|object|返回内容| +|admin|string|群主id| +|chatRoomId|int|群id| +|notice|int|通知| +|xml|string|xml| + + + +###### 接口示例 +入参: +``` javascript +{ + "wxid": "filehelper", + "msgid":7215505498606506901 +} +``` +响应: +``` javascript +{"code":1,"data":{"admin":"123","chatRoomId":"123@chatroom","notice":"1222","xml":""},"result":"OK"} +``` + + + +#### 48.获取解密图片** +###### 接口功能 +> 获取解密图片 + +###### 接口地址 +> [/api/?type=48](/api/?type=48) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|imagePath |ture |string| 图片路径 | +|savePath |ture |string| 保存路径 | + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, 0失败| +|result|string|成功提示| + + + +###### 接口示例 +入参: +``` javascript +{ + "imagePath":"C:\\3a610d7bc1cf5a15d12225a64b8962.dat", + "savePath":"C:\\other" +} + +``` +响应: +``` javascript +{"code":1,"result":"OK"} +``` + + diff --git a/python/tcpserver.py b/python/tcpserver.py new file mode 100644 index 0000000..4d52c7e --- /dev/null +++ b/python/tcpserver.py @@ -0,0 +1,57 @@ +import json +import threading +import socketserver + + +class ReceiveMsgSocketServer(socketserver.BaseRequestHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def handle(self): + conn = self.request + while True: + try: + ptr_data = b"" + while True: + data = conn.recv(1024) + ptr_data += data + if len(data) == 0 or data[-1] == 0xA: + break + + msg = json.loads(ptr_data) + ReceiveMsgSocketServer.msg_callback(msg) + + except OSError: + break + except json.JSONDecodeError: + pass + conn.sendall("200 OK".encode()) + conn.close() + + @staticmethod + def msg_callback(msg): + print(msg) + + +def start_socket_server(port: int = 19099, + request_handler=ReceiveMsgSocketServer, + main_thread: bool = True) -> int or None: + ip_port = ("127.0.0.1", port) + try: + s = socketserver.ThreadingTCPServer(ip_port, request_handler) + if main_thread: + s.serve_forever() + else: + socket_server = threading.Thread(target=s.serve_forever) + socket_server.setDaemon(True) + socket_server.start() + return socket_server.ident + except KeyboardInterrupt: + pass + except Exception as e: + print(e) + return None + + +if __name__ == '__main__': + start_socket_server() diff --git a/src/api.cc b/src/api.cc new file mode 100644 index 0000000..10f5d5b --- /dev/null +++ b/src/api.cc @@ -0,0 +1,599 @@ +#include "pch.h" +#include "api.h" +#include + +#include + + +#include "send_text.h" +#include "common.h" +#include "send_image.h" +#include "send_file.h" +#include "hook_recv_msg.h" +#include "get_db_handle.h" +#include "wechat_data.h" +#include "forward.h" +#include "db_operation.h" +#include "contact.h" +#include "chat_room.h" +#include "self_info.h" +#include "hook_img.h" + +#pragma comment(lib, "ws2_32.lib") +using namespace std; +using namespace nlohmann; + +#define STR2INT(str) (is_digit(str) ? stoi(str) : 0) +#define WS2LW(wstr) (LPWSTR) wstr.c_str() + +static bool kHttpRuning = false; +static HANDLE kHttpThread = NULL; + +bool is_digit(string str) { + if (str.length() == 0) { + return false; + } + + for (auto it : str) { + if (it < '0' || it > '9') { + return false; + } + } + return true; +} + +string get_var(mg_http_message *hm, string name) { + string ret; + char *buffer = new char[hm->query.len + 1]; + ZeroMemory(buffer, hm->query.len + 1); + int len = mg_http_get_var(&hm->query, name.c_str(), buffer, hm->query.len); + if (len > 0) ret = string(buffer, len); + delete[] buffer; + buffer = NULL; + return ret; +} +/// @brief 获取request中的请求参数int类型 +/// @param hm 消息 +/// @param data json数据 +/// @param key key +/// @param method 是否是post,暂时全部用post +/// @return int +static int get_http_req_param_int(mg_http_message *hm, json data, string key, int method){ + int result; + switch (method) { + case 0: { + result = STR2INT(get_var(hm,key).c_str()); + break; + } + case 1: { + try { + result = data[key].get(); + } catch (json::exception) { + result = STR2INT(data[key].get()); + } + break; + } + default: + break; + } + return result; +} + +/// @brief 获取request中的请求参数 +/// @param hm 消息 +/// @param data json数据 +/// @param key key +/// @param method 是否是post,暂时全部用post +/// @return +static wstring get_http_req_param(mg_http_message *hm, json data, string key, int method){ + wstring result; + switch (method) { + case 0: { + result = utf8_to_unicode(get_var(hm,key).c_str()); + break; + } + case 1: { + result = utf8_to_unicode(data[key].get().c_str()); + break; + } + default: + break; + } + return result; +} + +static unsigned long long get_http_param_ulong64(mg_http_message *hm, + json j_data, string key, + int method) { + unsigned long long result = 0; + switch (method) { + case 0: { + string value = get_var(hm, key); + istringstream is(value); + is >> result; + break; + } + case 1: { + try { + result = j_data[key].get(); + } catch (json::exception) { + string value = j_data[key].get(); + istringstream is(value); + is >> result; + } + break; + } + default: + break; + } + return result; +} + +static int get_http_param_int(mg_http_message *hm, json j_data, string key, + int method) { + int result = 0; + switch (method) { + case 0: { + result = STR2INT(get_var(hm, key)); + break; + } + case 1: { + try { + result = j_data[key].get(); + } catch (json::exception) { + result = STR2INT(j_data[key].get()); + } + break; + } + default: + break; + } + return result; +} + +static vector get_http_param_array(mg_http_message *hm, json j_data, + string key, int method) { + vector result; + switch (method) { + case 0: { + result = split(utf8_to_unicode(get_var(hm, key).c_str()), L','); + break; + } + case 1: { + result = split(utf8_to_unicode(j_data[key].get().c_str()), L','); + break; + } + default: + break; + } + return result; +} + +/// @brief api接口入口解析 +/// @param hm mg_http_message +/// @param c connection +/// @param ret json数据 +void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) { + int is_post = 0; + + if (mg_vcasecmp(&hm->method, "POST") == 0) { + is_post = 1; + } + #ifdef _DEBUG + printf("method:%s body: %s", hm->method.ptr,hm->body.ptr); + #endif + if (is_post == 0){ + json ret_data = {{"result", "ERROR"}, {"msg", "not support method"}}; + ret = ret_data.dump(); + return; + } + + json j_param = + json::parse(hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false); + if (hm->body.len != 0 && j_param.is_discarded() == true) { + json ret_data = {{"result", "ERROR"}, {"msg", "json string is invalid."}}; + ret = ret_data.dump(); + return; + } + int api_number = STR2INT(get_var(hm, "type")); + switch (api_number) { + case WECHAT_IS_LOGIN: { + int success = CheckLogin(); + json ret_data = {{"result", "OK"}, {"code", success}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_SELF_INFO: { + SelfInfoInner self_info; + int success = GetSelfInfo(self_info); + json ret_data = {{"result", "OK"}, {"code", success}}; + if (success) { + json j_info = { + {"name", self_info.name}, + {"city", self_info.city}, + {"province", self_info.province}, + {"country", self_info.country}, + {"account", self_info.account}, + {"wxid", self_info.wxid}, + {"mobile", self_info.mobile}, + {"smallImage", self_info.small_img}, + {"bigImage", self_info.big_img}, + {"dataRootPath",self_info.data_root_path}, + {"dataSavePath",self_info.data_save_path}, + {"currentDataPath",self_info.current_data_path}, + }; + ret_data["data"] = j_info; + } + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_TEXT: { + wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); + wstring msg = get_http_req_param(hm, j_param, "msg", is_post); + int success = SendText(WS2LW(wxid), WS2LW(msg)); + json ret_data = {{"result", "OK"}, {"code", success}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_AT: { + break; + } + case WECHAT_MSG_SEND_CARD: { + break; + } + case WECHAT_MSG_SEND_IMAGE: { + wstring receiver = get_http_req_param(hm, j_param, "wxid", is_post); + wstring img_path = get_http_req_param(hm, j_param, "imagePath", is_post); + int success = SendImage(WS2LW(receiver), WS2LW(img_path)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_FILE: { + wstring receiver = get_http_req_param(hm, j_param, "wxid", is_post); + wstring file_path = get_http_req_param(hm, j_param, "filePath", is_post); + int success = SendFile(WS2LW(receiver), WS2LW(file_path)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_ARTICLE: { + break; + } + case WECHAT_MSG_SEND_APP: { + break; + } + case WECHAT_MSG_START_HOOK: { + int port = get_http_req_param_int(hm, j_param, "port", is_post); + int success = HookRecvMsg(port); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_STOP_HOOK: { + int success = UnHookRecvMsg(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_START_IMAGE_HOOK: { + wstring img_dir = get_http_req_param(hm, j_param, "imgDir", is_post); + int success = HookImg(img_dir); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_STOP_IMAGE_HOOK: { + int success = UnHookImg(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_START_VOICE_HOOK: { + break; + } + case WECHAT_MSG_STOP_VOICE_HOOK: { + break; + } + case WECHAT_CONTACT_GET_LIST: { + + break; + } + case WECHAT_CONTACT_CHECK_STATUS: { + break; + } + case WECHAT_CONTACT_DEL: { + wstring user_id = get_http_req_param(hm, j_param, "wxid", is_post); + int success = DelContact(WS2LW(user_id)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CONTACT_SEARCH_BY_CACHE: { + break; + } + case WECHAT_CONTACT_SEARCH_BY_NET: { + break; + } + case WECHAT_CONTACT_ADD_BY_WXID: { + break; + } + case WECHAT_CONTACT_ADD_BY_V3: { + break; + } + case WECHAT_CONTACT_ADD_BY_PUBLIC_ID: { + break; + } + case WECHAT_CONTACT_VERIFY_APPLY: { + break; + } + case WECHAT_CONTACT_EDIT_REMARK: { + break; + } + case WECHAT_CHATROOM_GET_MEMBER_LIST: { + wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); + ChatRoomInner out{0}; + int success = GetMemberFromChatRoom(WS2LW(room_id),out); + json ret_data = {{"code", success}, {"result", "OK"}}; + if (success){ + json member_info ={ + {"admin",unicode_to_utf8(out.admin)}, + {"chatRoomId",unicode_to_utf8(out.chat_room)}, + {"members",out.members}, + }; + ret_data["data"] = member_info; + } + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_GET_MEMBER_NICKNAME: { + break; + } + case WECHAT_CHATROOM_DEL_MEMBER: { + wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); + vector wxids = get_http_param_array(hm, j_param, "memberIds", is_post); + vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++){ + wxid_list.push_back(WS2LW(wxids[i])); + } + int success = DelMemberFromChatRoom(WS2LW(room_id), wxid_list.data(),wxid_list.size()); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_ADD_MEMBER: { + wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); + vector wxids = get_http_param_array(hm, j_param, "memberIds", is_post); + vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++){ + wxid_list.push_back(WS2LW(wxids[i])); + } + int success = AddMemberToChatRoom(WS2LW(room_id), wxid_list.data(),wxid_list.size()); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_SET_ANNOUNCEMENT: { + break; + } + case WECHAT_CHATROOM_SET_CHATROOM_NAME: { + break; + } + case WECHAT_CHATROOM_SET_SELF_NICKNAME: { + break; + } + case WECHAT_DATABASE_GET_HANDLES: { + vector v_ptr = GetDbHandles(); + json ret_data = {{"data", json::array()}, {"result", "OK"}}; + for (unsigned int i = 0; i < v_ptr.size(); i++) { + json db_info; + db_info["tables"] = json::array(); + DatabaseInfo *db = reinterpret_cast(v_ptr[i]); + db_info["handle"] = db->handle; + db_info["databaseName"] = unicode_to_utf8(db->db_name); + for (auto table : db->tables) { + json table_info = {{"name", table.name}, + {"tableName", table.table_name}, + {"sql", table.sql}, + {"rootpage", table.rootpage}}; + db_info["tables"].push_back(table_info); + } + ret_data["data"].push_back(db_info); + } + ret = ret_data.dump(); + break; + } + case WECHAT_DATABASE_BACKUP: { + break; + } + case WECHAT_DATABASE_QUERY: { + DWORD db_handle = get_http_param_int(hm, j_param, "dbHandle", is_post); + wstring sql = get_http_req_param(hm, j_param, "sql", is_post); + string sql_str = unicode_to_utf8(WS2LW(sql)); + vector> items; + int success = Select(db_handle, sql_str.c_str(),items); + json ret_data = {{"data", json::array()}, {"code",success},{"result", "OK"}}; + if(success == 0){ + ret = ret_data.dump(); + break; + } + for (auto it : items) { + json temp_arr = json::array(); + for (size_t i = 0; i < it.size(); i++) { + temp_arr.push_back(it[i]); + } + ret_data["data"].push_back(temp_arr); + } + ret = ret_data.dump(); + break; + } + case WECHAT_SET_VERSION: { + break; + } + case WECHAT_LOG_START_HOOK: { + break; + } + case WECHAT_LOG_STOP_HOOK: { + break; + } + case WECHAT_BROWSER_OPEN_WITH_URL: { + break; + } + case WECHAT_GET_PUBLIC_MSG: { + break; + } + case WECHAT_MSG_FORWARD_MESSAGE: { + 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 = ForwardMsg(WS2LW(wxid), msgid); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_QRCODE_IMAGE: { + break; + } + case WECHAT_GET_A8KEY: { + break; + } + case WECHAT_MSG_SEND_XML: { + break; + } + case WECHAT_LOGOUT: { + break; + } + case WECHAT_GET_TRANSFER: { + break; + } + case WECHAT_GET_CONTACT_ALL: { + vector vec; + int success = GetAllContact(vec); + json ret_data = { + {"data", json::array()}, {"code", success}, {"result", "OK"}}; + + for (unsigned int i = 0; i < vec.size(); i++) { + #ifdef _DEBUG + cout << "vector :" < 0 + ? vec[i].custom_account.ptr != nullptr + ? unicode_to_utf8(vec[i].custom_account.ptr) + : string() + : string()}, + {"delFlag", vec[i].del_flag}, + {"userName", vec[i].encrypt_name.length > 0 + ? vec[i].encrypt_name.ptr != nullptr + ? unicode_to_utf8(vec[i].encrypt_name.ptr) + : string() + : string()}, + {"type", vec[i].type}, + {"verifyFlag", vec[i].verify_flag}, + {"verifyFlag", vec[i].verify_flag}, + {"wxid", vec[i].wxid.length > 0 ? unicode_to_utf8(vec[i].wxid.ptr) + : string()}, + }; + ret_data["data"].push_back(item); + } + ret = ret_data.dump(); + break; + } + case WECHAT_GET_CHATROOM_INFO: { + wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); + ChatRoomInfoInner chat_room_detail{0}; + int success = GetChatRoomDetailInfo(WS2LW(room_id), chat_room_detail); + json ret_data = {{"code", success}, {"result", "OK"}}; + if(!success){ + break; + } + json detail = { + {"chatRoomId", + chat_room_detail.chat_room_id.length > 0 + ? unicode_to_utf8(chat_room_detail.chat_room_id.ptr) + : string()}, + {"notice", chat_room_detail.notice.length > 0 + ? unicode_to_utf8(chat_room_detail.notice.ptr) + : string()}, + {"admin", chat_room_detail.admin.length > 0 + ? unicode_to_utf8(chat_room_detail.admin.ptr) + : string()}, + {"xml", chat_room_detail.xml.length > 0 + ? unicode_to_utf8(chat_room_detail.xml.ptr) + : string()}, + }; + ret_data["data"]=detail; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_IMG_BY_NAME: { + wstring image_path = get_http_req_param(hm, j_param, "imagePath", is_post); + wstring save_path = get_http_req_param(hm, j_param, "savePath", is_post); + int success = GetImgByName(WS2LW(image_path),WS2LW(save_path)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + } + default: + break; + } +} + +/// @brief 事件回调函数 +/// @param c 链接 +/// @param ev 事件 +/// @param ev_data 事件数据 +/// @param fn_data 回调数据 +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + struct mg_http_serve_opts opts = {0}; + if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *)ev_data; + string ret = R"({"result":"OK"})"; + if (mg_http_match_uri(hm, "/api/")) { + try { + api_handle(hm, c, ret); + } catch (json::exception &e) { + json res = {{"result", "ERROR"}, {"msg", e.what()}}; + ret = res.dump(); + } + if (ret != "") { + mg_http_reply(c, 200, "", ret.c_str(), 0, 0); + } + } else { + mg_http_reply(c, 500, NULL, "%s", "Invalid URI"); + } + } + (void)fn_data; +} +/// @brief http server +/// @param port 端口 +void http_server(int port) { + string lsten_addr = "http://0.0.0.0:" + to_string(port); + struct mg_mgr mgr; + // Init manager + mg_mgr_init(&mgr); + // Setup listener + mg_http_listen(&mgr, lsten_addr.c_str(), fn, &mgr); + // Event loop + for (;;) mg_mgr_poll(&mgr, 1000); + // Cleanup + mg_mgr_free(&mgr); +} + +/// @brief 启动http服务 +/// @param port 端口 +/// @return 成功返回0 +int http_start(int port) { + if (kHttpRuning) { + return 1; + } + #ifdef _DEBUG + CreateConsole(); + #endif + kHttpRuning = true; + kHttpThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)http_server, + (LPVOID)port, NULL, 0); + return 0; +} \ No newline at end of file diff --git a/src/api.h b/src/api.h new file mode 100644 index 0000000..4ecd6e3 --- /dev/null +++ b/src/api.h @@ -0,0 +1,71 @@ +#ifndef API_H_ +#define API_H_ + + +typedef enum WECHAT_HTTP_APISTag +{ + // login check + WECHAT_IS_LOGIN = 0, + // self info + WECHAT_GET_SELF_INFO, + // send message + WECHAT_MSG_SEND_TEXT, + WECHAT_MSG_SEND_AT, + WECHAT_MSG_SEND_CARD, + WECHAT_MSG_SEND_IMAGE, + WECHAT_MSG_SEND_FILE, + WECHAT_MSG_SEND_ARTICLE, + WECHAT_MSG_SEND_APP, + // receive message + WECHAT_MSG_START_HOOK, + WECHAT_MSG_STOP_HOOK, + WECHAT_MSG_START_IMAGE_HOOK, + WECHAT_MSG_STOP_IMAGE_HOOK, + WECHAT_MSG_START_VOICE_HOOK, + WECHAT_MSG_STOP_VOICE_HOOK, + // contact + WECHAT_CONTACT_GET_LIST, + WECHAT_CONTACT_CHECK_STATUS, + WECHAT_CONTACT_DEL, + WECHAT_CONTACT_SEARCH_BY_CACHE, + WECHAT_CONTACT_SEARCH_BY_NET, + WECHAT_CONTACT_ADD_BY_WXID, + WECHAT_CONTACT_ADD_BY_V3, + WECHAT_CONTACT_ADD_BY_PUBLIC_ID, + WECHAT_CONTACT_VERIFY_APPLY, + WECHAT_CONTACT_EDIT_REMARK, + // chatroom + WECHAT_CHATROOM_GET_MEMBER_LIST, + WECHAT_CHATROOM_GET_MEMBER_NICKNAME, + WECHAT_CHATROOM_DEL_MEMBER, + WECHAT_CHATROOM_ADD_MEMBER, + WECHAT_CHATROOM_SET_ANNOUNCEMENT, + WECHAT_CHATROOM_SET_CHATROOM_NAME, + WECHAT_CHATROOM_SET_SELF_NICKNAME, + // database + WECHAT_DATABASE_GET_HANDLES, + WECHAT_DATABASE_BACKUP, + WECHAT_DATABASE_QUERY, + // version + WECHAT_SET_VERSION, + // log + WECHAT_LOG_START_HOOK, + WECHAT_LOG_STOP_HOOK, + // browser + WECHAT_BROWSER_OPEN_WITH_URL, + WECHAT_GET_PUBLIC_MSG, + WECHAT_MSG_FORWARD_MESSAGE, + WECHAT_GET_QRCODE_IMAGE, + WECHAT_GET_A8KEY, + WECHAT_MSG_SEND_XML, + WECHAT_LOGOUT, + WECHAT_GET_TRANSFER, + WECHAT_GET_CONTACT_ALL, + WECHAT_GET_CHATROOM_INFO, + WECHAT_GET_IMG_BY_NAME, +} WECHAT_HTTP_APIS, + *PWECHAT_HTTP_APIS; + + +int http_start(int port); +#endif \ No newline at end of file diff --git a/src/base64.cpp b/src/base64.cpp new file mode 100644 index 0000000..f81394e --- /dev/null +++ b/src/base64.cpp @@ -0,0 +1,312 @@ +#include "pch.h" +/* + base64.cpp and base64.h + + base64 encoding and decoding with C++. + More information at + https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp + + Version: 2.rc.08 (release candidate) + + Copyright (C) 2004-2017, 2020, 2021 Ren Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Ren Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" + +#include +#include + +// +// Depending on the url parameter in base64_chars, one of +// two sets of base64 characters needs to be chosen. +// They differ in their last two characters. +// +static const char *base64_chars[2] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/", + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-_"}; + +static unsigned int pos_of_char(const unsigned char chr) +{ + // + // Return the position of chr within base64_encode() + // + + if (chr >= 'A' && chr <= 'Z') + return chr - 'A'; + else if (chr >= 'a' && chr <= 'z') + return chr - 'a' + ('Z' - 'A') + 1; + else if (chr >= '0' && chr <= '9') + return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; + else if (chr == '+' || chr == '-') + return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( + else if (chr == '/' || chr == '_') + return 63; // Ditto for '/' and '_' + else + // + // 2020-10-23: Throw std::exception rather than const char* + //(Pablo Martin-Gomez, https://github.com/Bouska) + // + throw std::runtime_error("Input is not valid base64-encoded data."); +} + +static std::string insert_linebreaks(std::string str, size_t distance) +{ + // + // Provided by https://github.com/JomaCorpFX, adapted by me. + // + if (!str.length()) + { + return ""; + } + + size_t pos = distance; + + while (pos < str.size()) + { + str.insert(pos, "\n"); + pos += distance + 1; + } + + return str; +} + +template +static std::string encode_with_line_breaks(String s) +{ + return insert_linebreaks(base64_encode(s, false), line_length); +} + +template +static std::string encode_pem(String s) +{ + return encode_with_line_breaks(s); +} + +template +static std::string encode_mime(String s) +{ + return encode_with_line_breaks(s); +} + +template +static std::string encode(String s, bool url) +{ + return base64_encode(reinterpret_cast(s.data()), s.length(), url); +} + +std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len, bool url) +{ + + size_t len_encoded = (in_len + 2) / 3 * 4; + + unsigned char trailing_char = url ? '.' : '='; + + // + // Choose set of base64 characters. They differ + // for the last two positions, depending on the url + // parameter. + // A bool (as is the parameter url) is guaranteed + // to evaluate to either 0 or 1 in C++ therefore, + // the correct character set is chosen by subscripting + // base64_chars with url. + // + const char *base64_chars_ = base64_chars[url]; + + std::string ret; + ret.reserve(len_encoded); + + unsigned int pos = 0; + + while (pos < in_len) + { + ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]); + + if (pos + 1 < in_len) + { + ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]); + + if (pos + 2 < in_len) + { + ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]); + ret.push_back(base64_chars_[bytes_to_encode[pos + 2] & 0x3f]); + } + else + { + ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]); + ret.push_back(trailing_char); + } + } + else + { + + ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]); + ret.push_back(trailing_char); + ret.push_back(trailing_char); + } + + pos += 3; + } + + return ret; +} + +template +static std::string decode(String encoded_string, bool remove_linebreaks) +{ + // + // decode() is templated so that it can be used with String = const std::string& + // or std::string_view (requires at least C++17) + // + + if (encoded_string.empty()) + return std::string(); + + if (remove_linebreaks) + { + + std::string copy(encoded_string); + + copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end()); + + return base64_decode(copy, false); + } + + size_t length_of_string = encoded_string.length(); + size_t pos = 0; + + // + // The approximate length (bytes) of the decoded string might be one or + // two bytes smaller, depending on the amount of trailing equal signs + // in the encoded string. This approximation is needed to reserve + // enough space in the string to be returned. + // + size_t approx_length_of_decoded_string = length_of_string / 4 * 3; + std::string ret; + ret.reserve(approx_length_of_decoded_string); + + while (pos < length_of_string) + { + // + // Iterate over encoded input string in chunks. The size of all + // chunks except the last one is 4 bytes. + // + // The last chunk might be padded with equal signs or dots + // in order to make it 4 bytes in size as well, but this + // is not required as per RFC 2045. + // + // All chunks except the last one produce three output bytes. + // + // The last chunk produces at least one and up to three bytes. + // + + size_t pos_of_char_1 = pos_of_char(encoded_string[pos + 1]); + + // + // Emit the first output byte that is produced in each chunk: + // + ret.push_back(static_cast(((pos_of_char(encoded_string[pos + 0])) << 2) + ((pos_of_char_1 & 0x30) >> 4))); + + if ((pos + 2 < length_of_string) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045) + encoded_string[pos + 2] != '=' && + encoded_string[pos + 2] != '.' // accept URL-safe base 64 strings, too, so check for '.' also. + ) + { + // + // Emit a chunk's second byte (which might not be produced in the last chunk). + // + unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos + 2]); + ret.push_back(static_cast(((pos_of_char_1 & 0x0f) << 4) + ((pos_of_char_2 & 0x3c) >> 2))); + + if ((pos + 3 < length_of_string) && + encoded_string[pos + 3] != '=' && + encoded_string[pos + 3] != '.') + { + // + // Emit a chunk's third byte (which might not be produced in the last chunk). + // + ret.push_back(static_cast(((pos_of_char_2 & 0x03) << 6) + pos_of_char(encoded_string[pos + 3]))); + } + } + + pos += 4; + } + + return ret; +} + +std::string base64_decode(std::string const &s, bool remove_linebreaks) +{ + return decode(s, remove_linebreaks); +} + +std::string base64_encode(std::string const &s, bool url) +{ + return encode(s, url); +} + +std::string base64_encode_pem(std::string const &s) +{ + return encode_pem(s); +} + +std::string base64_encode_mime(std::string const &s) +{ + return encode_mime(s); +} + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// + +std::string base64_encode(std::string_view s, bool url) +{ + return encode(s, url); +} + +std::string base64_encode_pem(std::string_view s) +{ + return encode_pem(s); +} + +std::string base64_encode_mime(std::string_view s) +{ + return encode_mime(s); +} + +std::string base64_decode(std::string_view s, bool remove_linebreaks) +{ + return decode(s, remove_linebreaks); +} + +#endif // __cplusplus >= 201703L diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..3d2bab7 --- /dev/null +++ b/src/base64.h @@ -0,0 +1,35 @@ +// +// base64 encoding and decoding with C++. +// Version: 2.rc.08 (release candidate) +// +#pragma once +#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A +#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A + +#include + +#if __cplusplus >= 201703L +#include +#endif // __cplusplus >= 201703L + +std::string base64_encode(std::string const &s, bool url = false); +std::string base64_encode_pem(std::string const &s); +std::string base64_encode_mime(std::string const &s); + +std::string base64_decode(std::string const &s, bool remove_linebreaks = false); +std::string base64_encode(unsigned char const *, size_t len, bool url = false); + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// +std::string base64_encode(std::string_view s, bool url = false); +std::string base64_encode_pem(std::string_view s); +std::string base64_encode_mime(std::string_view s); + +std::string base64_decode(std::string_view s, bool remove_linebreaks = false); +#endif // __cplusplus >= 201703L + +#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ diff --git a/src/chat_room.cc b/src/chat_room.cc new file mode 100644 index 0000000..b07e2f8 --- /dev/null +++ b/src/chat_room.cc @@ -0,0 +1,184 @@ +#include "pch.h" +#include "chat_room.h" + +#include "common.h" + +#include "wechat_data.h" +#define WX_CHAT_ROOM_MGR_OFFSET 0x686e40 +#define WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET 0xa70920 +#define WX_NEW_CHAT_ROOM_INFO_OFFSET 0xd03ec0 +#define WX_FREE_CHAT_ROOM_INFO_OFFSET 0x7226e0 +#define WX_DEL_CHAT_ROOM_MEMBER_OFFSET 0xa668f0 +#define WX_INIT_CHAT_MSG_OFFSET 0xdbcc40 +#define WX_FREE_CHAT_MSG_OFFSET 0x651c40 +#define WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET 0xa66400 +#define WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET 0xa71650 +#define WX_INIT_CHAT_ROOM_OFFSET 0xd01c30 +#define WX_FREE_CHAT_ROOM_OFFSET 0xa79310 + +int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) { + int success = 0; + WeChatString chat_room(chat_room_id); + DWORD base = GetWeChatWinBase(); + DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; + DWORD get_chat_room_detail_addr = base + WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET; + DWORD create_chat_room_info_addr = base + WX_NEW_CHAT_ROOM_INFO_OFFSET; + DWORD free_chat_room_info_addr = base + WX_FREE_CHAT_ROOM_INFO_OFFSET; + char chat_room_info[0xA4] = {0}; + __asm { + PUSHAD + LEA ECX,chat_room_info + CALL create_chat_room_info_addr + CALL get_chat_room_mgr_addr + PUSH 0x0 + LEA ECX,chat_room_info + PUSH ECX + LEA ECX,chat_room + PUSH ECX + MOV ECX,EAX + CALL get_chat_room_detail_addr + MOV success,EAX + POPAD + } + room_info.chat_room_id.ptr = *(wchar_t**)(chat_room_info + 0x4); + room_info.chat_room_id.length = *(DWORD*)(chat_room_info + 0x8); + room_info.chat_room_id.max_length = *(DWORD*)(chat_room_info + 0xC); + + room_info.notice.ptr = *(wchar_t**)(chat_room_info + 0x18); + room_info.notice.length = *(DWORD*)(chat_room_info + 0x1C); + room_info.notice.max_length = *(DWORD*)(chat_room_info + 0x20); + + room_info.admin.ptr = *(wchar_t**)(chat_room_info + 0x2C); + room_info.admin.length = *(DWORD*)(chat_room_info + 0x30); + room_info.admin.max_length = *(DWORD*)(chat_room_info + 0x34); + + room_info.xml.ptr = *(wchar_t**)(chat_room_info + 0x50); + room_info.xml.length = *(DWORD*)(chat_room_info + 0x54); + room_info.xml.max_length = *(DWORD*)(chat_room_info + 0x58); + __asm { + PUSHAD + LEA ECX,chat_room_info + CALL free_chat_room_info_addr + POPAD + } + return success; +} + +int DelMemberFromChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len) { + int success = 0; + WeChatString chat_room(chat_room_id); + vector members; + VectorInner *list = (VectorInner *)&members; + DWORD members_ptr = (DWORD)&list->start; + for (int i = 0; i < len; i++) { + WeChatString pwxid(wxids[i]); + members.push_back(pwxid); + } + DWORD base = GetWeChatWinBase(); + DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; + DWORD del_member_addr = base + WX_DEL_CHAT_ROOM_MEMBER_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + __asm { + PUSHAD + CALL get_chat_room_mgr_addr + SUB ESP,0x14 + MOV ESI,EAX + MOV ECX,ESP + LEA EDI,chat_room + PUSH EDI + CALL init_chat_msg_addr + MOV ECX,ESI + MOV EAX,dword ptr[members_ptr] + PUSH EAX + CALL del_member_addr + MOV success,EAX + POPAD + } + return success; +} + + +int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len){ + int success = 0; + WeChatString chat_room(chat_room_id); + vector members; + VectorInner *list = (VectorInner *)&members; + DWORD members_ptr = (DWORD)&list->start; + for (int i = 0; i < len; i++) { + WeChatString pwxid(wxids[i]); + members.push_back(pwxid); + } + DWORD base = GetWeChatWinBase(); + DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; + DWORD add_member_addr = base + WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD temp=0; + __asm { + PUSHAD + PUSHFD + CALL get_chat_room_mgr_addr + SUB ESP,0x8 + MOV temp,EAX + MOV ECX,ESP + MOV dword ptr [ECX],0x0 + MOV dword ptr [ECX + 4],0x0 + TEST ESI,ESI + SUB ESP,0x14 + MOV ECX,ESP + LEA EAX,chat_room + PUSH EAX + CALL init_chat_msg_addr + MOV ECX,temp + MOV EAX,dword ptr[members_ptr] + PUSH EAX + CALL add_member_addr + MOV success,EAX + POPFD + POPAD + } + return success; +} + + +int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out){ + int success = 0; + WeChatString chat_room(chat_room_id); + DWORD chat_room_ptr = (DWORD) &chat_room; + char buffer[0x1A0] = {0}; + DWORD base = GetWeChatWinBase(); + DWORD get_member_addr = base + WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET; + DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; + DWORD create_chat_room_addr = base + WX_INIT_CHAT_ROOM_OFFSET; + DWORD free_chat_room_addr = base + WX_FREE_CHAT_ROOM_OFFSET; + __asm { + PUSHAD + LEA ECX,buffer + CALL create_chat_room_addr + CALL get_chat_room_mgr_addr + LEA EAX, buffer + PUSH EAX + PUSH chat_room_ptr + CALL get_member_addr + MOVZX EAX,AL + MOV success,EAX + POPAD + } + char* members = *(char **)(buffer +0x1c); + wchar_t* room = *(wchar_t **)(buffer +0x8); + wchar_t* admin = *(wchar_t **)(buffer +0x4c); + + out.members = new char[strlen(members) + 1]; + memcpy(out.members, members, strlen(members) + 1); + + out.chat_room = new wchar_t[wcslen(room)+1]; + wmemcpy(out.chat_room ,room,wcslen(room)+1); + + out.admin = new wchar_t[wcslen(admin)+1]; + wmemcpy(out.admin ,admin,wcslen(admin)+1); + + __asm{ + LEA ECX,buffer + CALL free_chat_room_addr + } + return success; +} diff --git a/src/chat_room.h b/src/chat_room.h new file mode 100644 index 0000000..9ac930a --- /dev/null +++ b/src/chat_room.h @@ -0,0 +1,10 @@ +#ifndef CHAT_ROOM_H_ +#define CHAT_ROOM_H_ +#include "wechat_data.h" + +int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info); +int DelMemberFromChatRoom(wchar_t* chat_room_id,wchar_t** wxids,int len); +int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len); + +int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out); +#endif \ No newline at end of file diff --git a/src/common.cc b/src/common.cc new file mode 100644 index 0000000..264188e --- /dev/null +++ b/src/common.cc @@ -0,0 +1,138 @@ +#include "pch.h" +#include "common.h" + + +using namespace std; + +/// @brief utf8 转换成unicode +/// @param buffer utf8 +/// @return unicode +wstring utf8_to_unicode(const char *buffer) { + int c_size = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, NULL, 0); + if (c_size > 0) { + wchar_t *temp = new wchar_t[c_size + 1]; + MultiByteToWideChar(CP_UTF8, 0, buffer, -1, temp, c_size); + temp[c_size] = L'\0'; + wstring ret(temp); + delete[] temp; + temp = NULL; + return ret; + } + return wstring(); +} + +/// @brief unicode转换utf8 +/// @param wstr unicode +/// @return string utf8 +string unicode_to_utf8(wchar_t *wstr) { + int c_size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, FALSE); + if (c_size > 0) { + char *buffer = new char[c_size + 1]; + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, c_size, NULL, FALSE); + buffer[c_size] = '\0'; + string str(buffer); + delete[] buffer; + buffer = NULL; + return str; + } + return string(); +} + +/// @brief 获取WeChatWin.dll基址 +/// @return 基址 +DWORD GetWeChatWinBase() { return (DWORD)GetModuleHandleA("WeChatWin.dll"); } + +/// @brief 创建窗口 +/// @param void +/// @return 创建结果 +BOOL CreateConsole(void) { + if (AllocConsole()) { + AttachConsole(GetCurrentProcessId()); + FILE *retStream; + freopen_s(&retStream, "CONOUT$", "w", stdout); + if (!retStream) throw std::runtime_error("Stdout redirection failed."); + freopen_s(&retStream, "CONOUT$", "w", stderr); + if (!retStream) throw std::runtime_error("Stderr redirection failed."); + return 0; + } + return 1; +} + +/// @brief hook any addr +/// @param hook_addr need hook of addr +/// @param jmp_addr hook function addr +/// @param origin origin code +void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin) { + BYTE jmp_code[5] = {0}; + jmp_code[0] = 0xE9; + *(DWORD *)&jmp_code[1] = (DWORD)jmp_addr - hook_addr - 5; + DWORD old_protext = 0; + VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext); + ReadProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0); + memcpy((void *)hook_addr, jmp_code, 5); + VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext); +} + +/// @brief unhook +/// @param hook_addr hook addr +/// @param origin origin addr code +void UnHookAnyAddress(DWORD hook_addr, char *origin) { + DWORD old_protext = 0; + VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext); + WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0); + VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext); +} + +/// @brief get timeW +/// @param timestamp timestamp +/// @return str +wstring GetTimeW(long long timestamp) { + wchar_t *wstr = new wchar_t[20]; + memset(wstr, 0, 20 * 2); + tm tm_out; + localtime_s(&tm_out, ×tamp); + swprintf_s(wstr, 20, L"%04d-%02d-%02d %02d:%02d:%02d", 1900 + tm_out.tm_year, + tm_out.tm_mon + 1, tm_out.tm_mday, tm_out.tm_hour, tm_out.tm_min, + tm_out.tm_sec); + wstring strTimeW(wstr); + delete[] wstr; + return strTimeW; +} + +wstring String2Wstring(string str) { + wstring result; + int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0); + wchar_t *buffer = new wchar_t[len + 1]; + MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len); + buffer[len] = '\0'; + result.append(buffer); + delete[] buffer; + return result; +} + +string Wstring2String(wstring wstr) { + string result; + int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, + NULL, NULL); + char *buffer = new char[len + 1]; + WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, + NULL); + buffer[len] = '\0'; + result.append(buffer); + delete[] buffer; + return result; +} + +BOOL FindOrCreateDirectoryW(const wchar_t *path) { + WIN32_FIND_DATAW fd; + HANDLE hFind = ::FindFirstFileW(path, &fd); + if (hFind != INVALID_HANDLE_VALUE) { + FindClose(hFind); + return true; + } + + if (!::CreateDirectoryW(path, NULL)) { + return false; + } + return true; +} \ No newline at end of file diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..af9bb7e --- /dev/null +++ b/src/common.h @@ -0,0 +1,83 @@ +#ifndef COMMON_H_ +#define COMMON_H_ +#include +using namespace std; +#define READ_WSTRING(addr, offset) ((*(DWORD *)(addr + offset + 0x4) == 0) ? wstring(L"") : wstring((wchar_t *)(*(DWORD *)(addr + offset)), *(DWORD *)(addr + offset + 0x4))) + +/// @brief utf8 转换成unicode +/// @param buffer utf8 +/// @return unicode +wstring utf8_to_unicode(const char *buffer); + +/// @brief unicode转换utf8 +/// @param wstr unicode +/// @return utf8 +string unicode_to_utf8(wchar_t *wstr); + +/// @brief 获取WeChatWin.dll基址 +/// @return 基址 +DWORD GetWeChatWinBase(); +/// @brief 创建窗口 +/// @param void +/// @return 创建结果 +BOOL CreateConsole(void); +/// @brief hook任意地址 +/// @param hook_addr 被hook的地址 +/// @param jmp_addr 被hook的函数的地址 +/// @param origin 原始code +void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin); + +/// @brief 取消hook +/// @param hook_addr 被hook的地址 +/// @param origin 原始code +void UnHookAnyAddress(DWORD hook_addr, char *origin); + +/// @brief get timeW +/// @param timestamp timestamp +/// @return str +wstring GetTimeW(long long timestamp); +/// @brief unicode trans utf8 +/// @param str unicode str +/// @return utf8 str +std::string UnicodeToUtf8(const wchar_t *str); +/// @brief string convert wstring +/// @param str +/// @return +wstring String2Wstring(string str); +/// @brief wstring convert string +/// @param str +/// @return +string Wstring2String(wstring wstr); + +/// @brief create dir +/// @param path +/// @return +BOOL FindOrCreateDirectoryW(const wchar_t *path); + + +template +vector split(T1 str, T2 letter) { + vector arr; + size_t pos; + while ((pos = str.find_first_of(letter)) != T1::npos) { + T1 str1 = str.substr(0, pos); + arr.push_back(str1); + str = str.substr(pos + 1, str.length() - pos - 1); + } + arr.push_back(str); + return arr; +} + +template +T1 replace(T1 source, T2 replaced, T1 replaceto) { + vector v_arr = split(source, replaced); + if (v_arr.size() < 2) return source; + T1 temp; + for (unsigned int i = 0; i < v_arr.size() - 1; i++) { + temp += v_arr[i]; + temp += replaceto; + } + temp += v_arr[v_arr.size() - 1]; + return temp; +} +#endif \ No newline at end of file diff --git a/src/contact.cc b/src/contact.cc new file mode 100644 index 0000000..608cae9 --- /dev/null +++ b/src/contact.cc @@ -0,0 +1,106 @@ +#include "pch.h" +#include "contact.h" + +#include "common.h" +#include "wechat_data.h" + +#define WX_CONTACT_MGR_INSTANCE_OFFSET 0x655d60 +#define WX_CONTACT_GET_LIST_OFFSET 0xa97da0 +#define WX_CONTACT_DEL_OFFSET 0xa9bd10 +#define WX_INIT_CHAT_MSG_OFFSET 0xdbcc40 +#define WX_DB_QUERY_OFFSET 0xa9ba20 +#define WX_SYNC_MGR_OFFSET 0x993fa0 +#define WX_SYNC_MGR_OFFSET 0x993fa0 +#define WX_DO_DEL_CONTACT_OFFSET 0xb9a750 +#define WX_DEL_CONTACT_VTABLE_OFFSET 0x2886990 +int GetAllContact(vector &vec) { + DWORD base = GetWeChatWinBase(); + DWORD get_instance = base + WX_CONTACT_MGR_INSTANCE_OFFSET; + DWORD contact_get_list = base + WX_CONTACT_GET_LIST_OFFSET; + // char contact[0xc] = {0}; + DWORD* contact[3] = {0, 0, 0}; + int success = 0; + __asm { + PUSHAD + CALL get_instance + LEA ECX,contact + PUSH ECX + MOV ECX,EAX + CALL contact_get_list + MOVZX EAX,AL + MOV success,EAX + POPAD + } + DWORD start = (DWORD)contact[0]; + DWORD end = (DWORD)contact[2]; +#ifdef _DEBUG + cout << "address = " << &contact << endl; + cout << "refresh contact = " << success << endl; + cout << "start = " << start << endl; + cout << "end = " << end << endl; +#endif + while (start < end) { + Contact temp{0}; + + temp.wxid.ptr = *(wchar_t **)(start + 0x10); + temp.wxid.length = *(DWORD *)(start + 0x14); + temp.wxid.max_length = *(DWORD *)(start + 0x18); + + temp.custom_account.ptr = *(wchar_t **)(start + 0x24); + temp.custom_account.length = *(DWORD *)(start + 0x28); + temp.custom_account.max_length = *(DWORD *)(start + 0x2C); + + temp.encrypt_name.ptr = *(wchar_t **)(start + 0x6c); + temp.encrypt_name.length = *(DWORD *)(start + 0x70); + temp.encrypt_name.max_length = *(DWORD *)(start + 0x74); + + temp.pinyin.ptr = *(wchar_t **)(start + 0xAC); + temp.pinyin.length = *(DWORD *)(start + 0xB0); + temp.pinyin.max_length = *(DWORD *)(start + 0xB4); + + temp.pinyin_all.ptr = *(wchar_t **)(start + 0xC0); + temp.pinyin_all.length = *(DWORD *)(start + 0xC4); + temp.pinyin_all.max_length = *(DWORD *)(start + 0xC8); + + temp.del_flag = *(DWORD *)(start + 0x4c); + temp.type = *(DWORD *)(start + 0x50); + temp.verify_flag = *(DWORD *)(start + 0x54); + vec.push_back(temp); + start += 0x3E8; + } + return success; +} +// todo +int DelContact(wchar_t *wxid) { + int success = 0; + WeChatString user_id(wxid); + DWORD id_ptr = (DWORD) &user_id; + DWORD base = GetWeChatWinBase(); + DWORD get_instance_addr = base + WX_CONTACT_MGR_INSTANCE_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD del_addr = base + WX_CONTACT_DEL_OFFSET; + DWORD db_op_addr = base + WX_DB_QUERY_OFFSET; + __asm{ + PUSHAD + PUSHFD + CALL get_instance_addr + MOV ECX,dword ptr[id_ptr] + PUSH ECX + MOV ECX,EAX + MOV ESI,EAX + CALL db_op_addr + SUB ESP,0x14 + MOV EAX,dword ptr[id_ptr] + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + MOV ECX,ESI + CALL del_addr + MOV success,EAX + POPFD + POPAD + } + return success; +} + + diff --git a/src/contact.h b/src/contact.h new file mode 100644 index 0000000..8f643f4 --- /dev/null +++ b/src/contact.h @@ -0,0 +1,11 @@ +#ifndef CONTACT_H_ +#define CONTACT_H_ +#include +#include "wechat_data.h" + +int GetAllContact(vector &vec); + + + +int DelContact(wchar_t* wxid); +#endif \ No newline at end of file diff --git a/src/db_operation.cc b/src/db_operation.cc new file mode 100644 index 0000000..3b43624 --- /dev/null +++ b/src/db_operation.cc @@ -0,0 +1,155 @@ +#include "pch.h" +#include "db_operation.h" + +#include "base64.h" +#include "common.h" +#include "new_sqlite3.h" + + +/// @brief free data +void FreeResult(vector> &data) { + if (data.size() == 0) { + return; + } + for (unsigned int i = 0; i < data.size(); i++) { + for (unsigned j = 0; j < data[i].size(); j++) { + SqlResult *sr = (SqlResult *)&data[i][j]; + if (sr->column_name) { + delete sr->column_name; + sr->column_name = NULL; + } + if (sr->content) { + delete sr->content; + sr->content = NULL; + } + } + data[i].clear(); + } + data.clear(); +} + +int SelectDataInner(DWORD db, const char *sql, + vector> &data) { + DWORD wxBaseAddress = GetWeChatWinBase(); + Sqlite3_prepare p_Sqlite3_prepare = + (Sqlite3_prepare)(wxBaseAddress + SQLITE3_PREPARE_OFFSET); + Sqlite3_step p_Sqlite3_step = + (Sqlite3_step)(wxBaseAddress + SQLITE3_STEP_OFFSET); + Sqlite3_column_count p_Sqlite3_column_count = + (Sqlite3_column_count)(wxBaseAddress + SQLITE3_COLUMN_COUNT_OFFSET); + Sqlite3_column_name p_Sqlite3_column_name = + (Sqlite3_column_name)(wxBaseAddress + SQLITE3_COLUMN_NAME_OFFSET); + Sqlite3_column_type p_Sqlite3_column_type = + (Sqlite3_column_type)(wxBaseAddress + SQLITE3_COLUMN_TYPE_OFFSET); + Sqlite3_column_blob p_Sqlite3_column_blob = + (Sqlite3_column_blob)(wxBaseAddress + SQLITE3_COLUMN_BLOB_OFFSET); + Sqlite3_column_bytes p_Sqlite3_column_bytes = + (Sqlite3_column_bytes)(wxBaseAddress + SQLITE3_COLUMN_BYTES_OFFSET); + Sqlite3_finalize p_Sqlite3_finalize = + (Sqlite3_finalize)(wxBaseAddress + SQLITE3_FINALIZE_OFFSET); + DWORD *stmt; + int rc = p_Sqlite3_prepare(db, sql, -1, &stmt, 0); + if (rc != SQLITE_OK) return NULL; + while (p_Sqlite3_step(stmt) == SQLITE_ROW) { + int col_count = p_Sqlite3_column_count(stmt); + vector tempStruct; + for (int i = 0; i < col_count; i++) { + SqlResult temp = {0}; + const char *ColName = p_Sqlite3_column_name(stmt, i); + int nType = p_Sqlite3_column_type(stmt, i); + const void *pReadBlobData = p_Sqlite3_column_blob(stmt, i); + int nLength = p_Sqlite3_column_bytes(stmt, i); + temp.column_name = new char[strlen(ColName) + 1]; + memcpy(temp.column_name, ColName, strlen(ColName) + 1); + temp.column_name_len = strlen(ColName); + temp.content_len = nLength; + switch (nType) { + case SQLITE_BLOB: { + temp.content = new char[nLength]; + memcpy(temp.content, pReadBlobData, nLength); + temp.is_blob = true; + break; + } + default: { + if (nLength != 0) { + temp.content = new char[nLength + 1]; + memcpy(temp.content, pReadBlobData, nLength + 1); + } else { + temp.content = new char[2]; + ZeroMemory(temp.content, 2); + } + temp.is_blob = false; + break; + } + } + tempStruct.push_back(temp); + } + data.push_back(tempStruct); + } + p_Sqlite3_finalize(stmt); + return 1; +} + +int Select(DWORD db_hanle, const char *sql, + vector> &query_result) { + vector> data; + int status = SelectDataInner(db_hanle, sql, data); + if (status == 0) { + return 0; + } + vector index; + for (size_t i = 0; i < data[0].size(); i++) + index.push_back(data[0][i].column_name); + query_result.push_back(index); + for (auto it : data) { + vector item; + for (size_t i = 0; i < it.size(); i++) { + if (!it[i].is_blob) { + string content(it[i].content); + item.push_back(content); + } else { + string b64_str = + base64_encode((BYTE *)it[i].content, it[i].content_len); + item.push_back(b64_str); + } + } + query_result.push_back(item); + } + FreeResult(data); + return 1; +} + +int SelectDbInfo(void *data, int argc, char **argv, char **name) { + vector result; + for (int i = 0; i < argc; i++) { + SqlResult temp = {0}; + temp.column_name = new char[strlen(name[i]) + 1]; + memcpy(temp.column_name, name[i], strlen(name[i]) + 1); + temp.column_name_len = strlen(name[i]); + if (argv[i]) { + temp.content = new char[strlen(argv[i]) + 1]; + memcpy(temp.content, argv[i], strlen(argv[i]) + 1); + temp.content_len = strlen(argv[i]); + } else { + temp.content = new char[2]; + ZeroMemory(temp.content, 2); + temp.content_len = 0; + } + result.push_back(temp); + } + return 1; +} + +/// @brief sqlite3_exec +/// @param db +/// @param sql +/// @param callback +/// @param data +/// @return +int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data) { + DWORD base = GetWeChatWinBase(); + DWORD sqlite3_exec_addr = base + SQLITE3_EXEC_OFFSET; + Sqlite3_exec fn_sqlite3_exec = (Sqlite3_exec)sqlite3_exec_addr; + int status = fn_sqlite3_exec(db, sql, (Sqlite3_callback)callback, data, 0); + return status; +} \ No newline at end of file diff --git a/src/db_operation.h b/src/db_operation.h new file mode 100644 index 0000000..ad987e8 --- /dev/null +++ b/src/db_operation.h @@ -0,0 +1,23 @@ +#ifndef DB_OPERATION_H_ +#define DB_OPERATION_H_ +#include +#include +using namespace std; +struct SqlResult { + char *column_name; + DWORD column_name_len; + char *content; + DWORD content_len; + BOOL is_blob; +}; +/// @brief exec sql +/// @param db opened db +/// @param sql sql +/// @param callback callback func +/// @param data data +/// @return +int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data); + +int Select(DWORD db_hanle, const char *sql,vector> &query_result); + +#endif \ No newline at end of file diff --git a/src/dllMain.cc b/src/dllMain.cc new file mode 100644 index 0000000..42cbf5e --- /dev/null +++ b/src/dllMain.cc @@ -0,0 +1,23 @@ +#include "pch.h" +#include "api.h" +#include "common.h" + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, + LPVOID lpReserved) { + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: { + http_start(19088); + break; + } + case DLL_THREAD_ATTACH: { + break; + } + case DLL_THREAD_DETACH: { + break; + } + case DLL_PROCESS_DETACH: { + break; + } + } + return TRUE; +} diff --git a/src/forward.cc b/src/forward.cc new file mode 100644 index 0000000..62a421d --- /dev/null +++ b/src/forward.cc @@ -0,0 +1,41 @@ +#include "pch.h" +#include "forward.h" + +#include "common.h" +#include "get_db_handle.h" +#include "wechat_data.h" +#define WX_FORWARD_MSG_OFFSET 0xb68c80 +#define WX_INIT_CHAT_MSG_OFFSET 0xdbcc40 + +int ForwardMsg(wchar_t *wxid, unsigned long long msgid) { + int success = 0; + + int db_index = 0; + int localid = GetLocalIdByMsgId(msgid, db_index); + + if (localid == 0) return 0; + WeChatString to_user(wxid); + DWORD base = GetWeChatWinBase(); + DWORD forward_msg_addr = base + WX_FORWARD_MSG_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + __asm { + PUSHAD + PUSHFD + MOV EDX, DWORD PTR [db_index] + PUSH EDX + MOV EAX, DWORD PTR [localid] + PUSH EAX + SUB ESP,0x14 + MOV ECX,ESP + LEA ESI, to_user; + PUSH ESI + CALL init_chat_msg_addr + CALL forward_msg_addr + MOVZX EAX,AL; + MOV success,EAX + ADD ESP,0x1c + POPFD + POPAD + } + return success; +} \ No newline at end of file diff --git a/src/forward.h b/src/forward.h new file mode 100644 index 0000000..cce23c2 --- /dev/null +++ b/src/forward.h @@ -0,0 +1,5 @@ +#ifndef FORWARD_H_ +#define FORWARD_H_ + +int ForwardMsg(wchar_t *wxid, unsigned long long msgid); +#endif \ No newline at end of file diff --git a/src/framework.h b/src/framework.h new file mode 100644 index 0000000..6c09fe4 --- /dev/null +++ b/src/framework.h @@ -0,0 +1,7 @@ +#ifndef FRAMEWORK_H_ +#define FRAMEWORK_H_ +#define WIN32_LEAN_AND_MEAN + +#include + +#endif \ No newline at end of file diff --git a/src/get_db_handle.cc b/src/get_db_handle.cc new file mode 100644 index 0000000..015a395 --- /dev/null +++ b/src/get_db_handle.cc @@ -0,0 +1,299 @@ +#include "get_db_handle.h" + +#include "common.h" +#include "db_operation.h" +#include "new_sqlite3.h" +#include "pch.h" +#include "wechat_data.h" +#define CONTACT_G_PINSTANCE 0x2bee928 +#define DB_MICRO_MSG_OFFSET 0x68 +#define DB_CHAT_MSG_OFFSET 0x1C0 +#define DB_MISC_OFFSET 0x3D8 +#define DB_EMOTION_OFFSET 0x558 +#define DB_MEDIA_OFFSET 0x9B8 +#define DB_BIZCHAT_MSG_OFFSET 0x1120 +#define DB_FUNCTION_MSG_OFFSET 0x11B0 +#define DB_NAME_OFFSET 0x14 + +#define PUBLIC_MSG_MGR_OFFSET 0x2c294c0 +#define MULTI_DB_MSG_MGR_OFFSET 0x2c2aff4 +#define FAVORITE_STORAGE_MGR_OFFSET 0x2c2aa14 +#define FTS_FAVORITE_MGR_OFFSET 0x2bef468 + +using namespace std; +map dbmap; +std::vector dbs; + +int GetDbInfo(void *data, int argc, char **argv, char **name) { + DatabaseInfo *pdata = (DatabaseInfo *)data; + TableInfo tb = {0}; + if (argv[1]) { + tb.name = new char[strlen(argv[1]) + 1]; + memcpy(tb.name, argv[1], strlen(argv[1]) + 1); + } else { + tb.name = (char *)"NULL"; + } + if (argv[2]) { + tb.table_name = new char[strlen(argv[2]) + 1]; + memcpy(tb.table_name, argv[2], strlen(argv[2]) + 1); + } else { + tb.table_name = (char *)"NULL"; + } + if (argv[3]) { + tb.rootpage = new char[strlen(argv[3]) + 1]; + memcpy(tb.rootpage, argv[3], strlen(argv[3]) + 1); + } else { + tb.rootpage = (char *)"NULL"; + } + if (argv[4]) { + tb.sql = new char[strlen(argv[4]) + 1]; + memcpy(tb.sql, argv[4], strlen(argv[4]) + 1); + } else { + tb.sql = (char *)"NULL"; + } + tb.name_len = strlen(tb.name); + tb.table_name_len = strlen(tb.table_name); + tb.sql_len = strlen(tb.sql); + tb.rootpage_len = strlen(tb.rootpage); + pdata->tables.push_back(tb); + pdata->count = pdata->tables.size(); + return 0; +} + +std::vector GetDbHandles() { + dbs.clear(); + dbmap.clear(); + DWORD base = GetWeChatWinBase(); + DWORD p_contact_addr = *(DWORD *)(base + CONTACT_G_PINSTANCE); + DWORD micro_msg_db_addr = *(DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET); + DWORD chat_msg_db_addr = *(DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET); + DWORD misc_db_addr = *(DWORD *)(p_contact_addr + DB_MISC_OFFSET); + DWORD emotion_db_addr = *(DWORD *)(p_contact_addr + DB_EMOTION_OFFSET); + DWORD media_db_addr = *(DWORD *)(p_contact_addr + DB_MEDIA_OFFSET); + DWORD bizchat_msg_db_addr = + *(DWORD *)(p_contact_addr + DB_BIZCHAT_MSG_OFFSET); + DWORD function_msg_db_addr = + *(DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET); + + // microMsg.db + DatabaseInfo micro_msg_db{0}; + micro_msg_db.db_name = (wchar_t *)(*( + DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET)); + micro_msg_db.db_name_len = + *(DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET + 0x4); + micro_msg_db.handle = micro_msg_db_addr; + ExecuteSQL(micro_msg_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, µ_msg_db); + dbs.push_back(micro_msg_db); + wstring micro_msg_name = wstring((wchar_t *)(*( + DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET))); + dbmap[micro_msg_name] = micro_msg_db; + + // chatMsg.db + DatabaseInfo chat_msg_db{0}; + chat_msg_db.db_name = (wchar_t *)(*( + DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET)); + chat_msg_db.db_name_len = + *(DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET + 0x4); + chat_msg_db.handle = chat_msg_db_addr; + ExecuteSQL(chat_msg_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &chat_msg_db); + dbs.push_back(chat_msg_db); + wstring chat_msg_name = wstring((wchar_t *)(*( + DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET))); + dbmap[chat_msg_name] = chat_msg_db; + + // misc.db + DatabaseInfo misc_db{0}; + misc_db.db_name = + (wchar_t *)(*(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET)); + misc_db.db_name_len = + *(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET + 0x4); + misc_db.handle = misc_db_addr; + ExecuteSQL(misc_db_addr, "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &misc_db); + dbs.push_back(misc_db); + wstring misc_name = wstring(( + wchar_t *)(*(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET))); + dbmap[misc_name] = misc_db; + + // emotion.db + DatabaseInfo emotion_db{0}; + emotion_db.db_name = (wchar_t *)(*( + DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET)); + emotion_db.db_name_len = + *(DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET + 0x4); + emotion_db.handle = emotion_db_addr; + ExecuteSQL(emotion_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &emotion_db); + dbs.push_back(emotion_db); + wstring emotion_name = wstring((wchar_t *)(*( + DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET))); + dbmap[emotion_name] = emotion_db; + + // media.db + DatabaseInfo media_db{0}; + media_db.db_name = (wchar_t *)(*(DWORD *)(p_contact_addr + DB_MEDIA_OFFSET + + DB_NAME_OFFSET)); + media_db.db_name_len = + *(DWORD *)(p_contact_addr + DB_MEDIA_OFFSET + DB_NAME_OFFSET + 0x4); + media_db.handle = media_db_addr; + ExecuteSQL(media_db_addr, "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &media_db); + dbs.push_back(media_db); + wstring media_name = wstring((wchar_t *)(*( + DWORD *)(p_contact_addr + DB_MEDIA_OFFSET + DB_NAME_OFFSET))); + dbmap[media_name] = media_db; + + // functionMsg.db + DatabaseInfo function_msg_db{0}; + function_msg_db.db_name = (wchar_t *)(*( + DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET)); + function_msg_db.db_name_len = *( + DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET + 0x4); + function_msg_db.handle = function_msg_db_addr; + ExecuteSQL(function_msg_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &function_msg_db); + dbs.push_back(function_msg_db); + wstring function_msg_name = wstring((wchar_t *)(*( + DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET))); + dbmap[function_msg_name] = function_msg_db; + + if (bizchat_msg_db_addr) { + // functionMsg.db maybe null + DatabaseInfo bizchat_msg_db{0}; + bizchat_msg_db.db_name = (wchar_t *)(*( + DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET)); + bizchat_msg_db.db_name_len = + *(DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET + + 0x4); + bizchat_msg_db.handle = bizchat_msg_db_addr; + ExecuteSQL(bizchat_msg_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &bizchat_msg_db); + dbs.push_back(bizchat_msg_db); + wstring bizchat_msg_name = wstring((wchar_t *)(*( + DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET))); + dbmap[bizchat_msg_name] = bizchat_msg_db; + } + DWORD multi_db_mgr_addr = base + MULTI_DB_MSG_MGR_OFFSET; + DWORD public_msg_mgr_addr = base + PUBLIC_MSG_MGR_OFFSET; + DWORD favorite_storage_mgr_addr = base + FAVORITE_STORAGE_MGR_OFFSET; + DWORD fts_favorite_mgr_addr = base + FTS_FAVORITE_MGR_OFFSET; + + // MsgX.db + DWORD wrap_ptr = *(DWORD *)(multi_db_mgr_addr); + DWORD db_num = *(DWORD *)(wrap_ptr+0x30); + DWORD current_db_num = *(DWORD *)(wrap_ptr+0x38); + DWORD begin_ptr = *(DWORD *)(wrap_ptr + 0x2c); + + for (unsigned int i = 0; i < current_db_num; i++) { + DWORD next_addr = begin_ptr + i * 0x4; + DWORD db_addr = *(DWORD *) next_addr; + if (db_addr) { + DWORD msg0_db_addr = *(DWORD *)(db_addr + 0x60); + DatabaseInfo msg0_db{0}; + msg0_db.db_name = (wchar_t *)(*(DWORD *)(db_addr)); + msg0_db.db_name_len = *(DWORD *)(db_addr + 0x4); + msg0_db.handle = msg0_db_addr; + msg0_db.extrainfo = *(DWORD *) (*(DWORD *)(db_addr + 0x18) +0x144); + ExecuteSQL( + msg0_db_addr, "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &msg0_db); + dbs.push_back(msg0_db); + wstring msg_db_name = wstring((wchar_t *)(*(DWORD *)(db_addr))); + dbmap[msg_db_name] = msg0_db; + } + } + + // publicMsg.db + DWORD public_msg_ptr = *(DWORD *) (*(DWORD *)(public_msg_mgr_addr) + 0x8); + if (public_msg_ptr) { + DWORD public_msg_db_addr = *(DWORD *)(public_msg_ptr + 0x38); + DatabaseInfo public_msg_db{0}; + public_msg_db.db_name = (wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C)); + public_msg_db.db_name_len =*(DWORD *)(public_msg_ptr + 0x50); + public_msg_db.handle = public_msg_db_addr; + ExecuteSQL(public_msg_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &public_msg_db); + dbs.push_back(public_msg_db); + wstring public_msg_db_name =wstring((wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C))); + dbmap[public_msg_db_name] = public_msg_db; + } + + // Favorite.db + DWORD favItems_ptr = *(DWORD *)(*(DWORD *)(favorite_storage_mgr_addr) + 0x8); + if (favItems_ptr) { + DWORD favorite_db_addr = *(DWORD *)(favItems_ptr + 0x38); + DatabaseInfo favorite_db{0}; + favorite_db.db_name =(wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C)); + favorite_db.db_name_len = *(DWORD *)(favItems_ptr + 0x50); + favorite_db.handle = favorite_db_addr; + ExecuteSQL(favorite_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &favorite_db); + dbs.push_back(favorite_db); + wstring public_msg_db_name =wstring((wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C))); + dbmap[public_msg_db_name] = favorite_db; + } + + DatabaseInfo db_end = {0}; + dbs.push_back(db_end); +#ifdef _DEBUG + for (unsigned int i = 0; i < dbs.size() - 1; i++) { + printf("dbname = %ws,handle = 0x%08X,table_count:%d\n", dbs[i].db_name, + dbs[i].handle, dbs[i].tables.size()); + for (unsigned int j = 0; j < dbs[i].tables.size(); j++) { + cout << "name = " << dbs[i].tables[j].name << endl; + cout << "tbl_name = " << dbs[i].tables[j].table_name << endl; + cout << "rootpage = " << dbs[i].tables[j].rootpage << endl; + cout << "sql = " << dbs[i].tables[j].sql << endl; + cout << endl; + } + cout << endl; + } +#endif + vector ret_array; + for (unsigned int i = 0; i < dbs.size() - 1; i++) + ret_array.push_back(&dbs[i]); + return ret_array; +} + +DWORD GetDbHandleByDbName(wchar_t *dbname) { + if (dbmap.size() == 0) { + GetDbHandles(); + } + if (dbmap.find(dbname) != dbmap.end()) { + return dbmap[dbname].handle; + } + + return 0; +} + +unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex) { + char sql[260] = {0}; + sprintf_s(sql, "select localId 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); + #ifdef _DEBUG + cout <<" handle =" <> result; + int ret = Select(handle, (const char *)sql, result); + #ifdef _DEBUG + cout <<" size =" < + +std::vector GetDbHandles(); +DWORD GetDbHandleByDbName(wchar_t *dbname); +unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex); +#endif \ No newline at end of file diff --git a/src/hook_img.cc b/src/hook_img.cc new file mode 100644 index 0000000..656f915 --- /dev/null +++ b/src/hook_img.cc @@ -0,0 +1,231 @@ +#include "pch.h" +#include "hook_img.h" + +#include "common.h" + + +// #define WX_HOOK_IMG_OFFSET 0xd7eaa5 +// #define WX_HOOK_IMG_NEXT_OFFSET 0xda56e0 +#define WX_HOOK_IMG_OFFSET 0xc63ebc +#define WX_HOOK_IMG_NEXT_OFFSET 0xd7e9e0 +#define WX_SELF_ID_OFFSET 0x2BEE08C +#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 + + + +static wstring kImgStorePath = L""; +static int kImgHooked = FALSE; +static DWORD kWeChatWinBase = GetWeChatWinBase(); +static char kOriginImgAsmCode[5] = {0}; + +static DWORD kHookImgNextAddress = kWeChatWinBase + WX_HOOK_IMG_NEXT_OFFSET; +static DWORD kHookImgJmpBackAddress = kWeChatWinBase + WX_HOOK_IMG_OFFSET + 0x5; + +void OnHookImg(DWORD obj_addr) { + DWORD wxid_addr = GetWeChatWinBase() + WX_SELF_ID_OFFSET; + string wxid = string(*(char **)wxid_addr, *(DWORD *)(wxid_addr + 0x10)); + wstring self_id = String2Wstring(wxid); + wstring save_path = kImgStorePath + self_id; + if (!FindOrCreateDirectoryW(save_path.c_str())) { + return; + } + wchar_t *origin_file_path = *(wchar_t **)obj_addr; + wstring img_path(origin_file_path); + if (img_path.find(L"_t.dat") != wstring::npos) { + return; + } + + int pos_begin = img_path.find_last_of(L"\\") + 1; + int pos_end = img_path.find_last_of(L"."); + wstring file_name = img_path.substr(pos_begin, pos_end - pos_begin); + char buffer[BUFSIZE] = {0}; + DWORD bytes_read = 0; + DWORD bytes_write = 0; + unsigned char magic_head[4] = {0}; + wchar_t suffix[5] = {0}; + + HANDLE h_origin_file = + CreateFileW(origin_file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h_origin_file == INVALID_HANDLE_VALUE) { + return; + } + + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + memcpy(magic_head, buffer, 3); + } + + if (magic_head[0] == PNG0 && magic_head[1] == PNG1 && magic_head[2] == PNG2) { + lstrcpyW(suffix, L".png"); + } else if (magic_head[0] == GIF0 && magic_head[1] == GIF1 && + magic_head[2] == GIF2) { + lstrcpyW(suffix, L".gif"); + } else if (magic_head[0] == JPEG0 && magic_head[1] == JPEG1 && + magic_head[2] == JPEG2) { + lstrcpyW(suffix, L".jpg"); + } else { + lstrcpyW(suffix, L""); + } + + wstring save_img_path = save_path + L"\\" + file_name + suffix; + HANDLE save_file = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (save_file == INVALID_HANDLE_VALUE) { + return; + } + if (!WriteFile(save_file, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_file); + return; + } + do { + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + if (!WriteFile(save_file, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_file); + return; + } + } + } while (bytes_read == BUFSIZE); + CloseHandle(h_origin_file); + CloseHandle(save_file); +} + +/// @brief hook img implement +_declspec(naked) void handle_img() { + __asm { + PUSHAD + PUSHFD + PUSH ECX + CALL OnHookImg + ADD ESP, 0x4 + POPFD + POPAD + CALL kHookImgNextAddress + JMP kHookImgJmpBackAddress + } +} + +/// @brief hook image +/// @param save_path image save dir +/// @return +int HookImg(wstring save_path) { + kWeChatWinBase = GetWeChatWinBase(); + if (!kWeChatWinBase) { + return -1; + } + if (kImgHooked) { + return 2; + } + kImgStorePath = save_path; + if (kImgStorePath.back() != '\\') { + kImgStorePath += L"\\"; + } + wstring createpath = kImgStorePath.substr(0, kImgStorePath.length() - 1); + if (!FindOrCreateDirectoryW(createpath.c_str())) { + return -2; + } + DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_IMG_OFFSET; + kHookImgNextAddress = kWeChatWinBase + WX_HOOK_IMG_NEXT_OFFSET; + static DWORD kHookImgJmpBackAddress = hook_img_addr + 0x5; + HookAnyAddress(hook_img_addr, (LPVOID)handle_img, kOriginImgAsmCode); + kImgHooked = TRUE; + return 1; +} + +int UnHookImg() { + if (!kImgHooked) return 1; + DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_IMG_OFFSET; + UnHookAnyAddress(hook_img_addr, kOriginImgAsmCode); + kImgHooked = FALSE; + return 1; +} + +int GetImgByName(wchar_t* file_path,wchar_t* save_dir) { + wstring save_path(save_dir); + wstring orign_file_path(file_path); + if (!FindOrCreateDirectoryW(save_path.c_str())) { + return 0; + } + + int pos_begin = orign_file_path.find_last_of(L"\\") + 1; + int pos_end = orign_file_path.find_last_of(L"."); + wstring file_name = orign_file_path.substr(pos_begin, pos_end - pos_begin); + HANDLE h_origin_file = + CreateFileW(file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + char buffer[BUFSIZE] = {0}; + DWORD bytes_read = 0; + DWORD bytes_write = 0; + unsigned char magic_head[4] = {0}; + wstring suffix; + short key = 0; + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + memcpy(magic_head, buffer, 3); + } else { + CloseHandle(h_origin_file); + return 0; + } + if ((magic_head[0] ^ JPEG0) == (magic_head[1] ^ JPEG1)) { + key = magic_head[0] ^ JPEG0; + suffix = L".jpg"; + } else if ((magic_head[0] ^ PNG1) == (magic_head[1] ^ PNG2)) { + key = magic_head[0] ^ PNG1; + suffix = L".png"; + } else if ((magic_head[0] ^ GIF0) == (magic_head[1] ^ GIF1)) { + key = magic_head[0] ^ GIF0; + suffix = L".gif"; + } else if ((magic_head[0] ^ BMP0) == (magic_head[1] ^ BMP1)) { + key = magic_head[0] ^ BMP0; + suffix = L".bmp"; + } else { + key = -1; + suffix = L".dat"; + } + wstring save_img_path = save_path + L"\\" + file_name + suffix; + HANDLE save_img = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (save_img == INVALID_HANDLE_VALUE) { + return 0; + } + if (key > 0) { + for (unsigned int i = 0; i < bytes_read; i++) { + buffer[i]^=key; + } + } + if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 0; + } + + do { + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + if (key > 0) { + for (unsigned int i = 0; i < bytes_read; i++) { + buffer[i] ^= key; + } + } + if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 0; + } + } + } while (bytes_read == BUFSIZE); + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 1; +} \ No newline at end of file diff --git a/src/hook_img.h b/src/hook_img.h new file mode 100644 index 0000000..68c458e --- /dev/null +++ b/src/hook_img.h @@ -0,0 +1,10 @@ +#ifndef HOOK_IMG_H_ +#define HOOK_IMG_H_ +#include "windows.h" +using namespace std; + +int HookImg(wstring save_path); +int UnHookImg(); + +int GetImgByName(wchar_t* file_path,wchar_t* save_dir); +#endif \ No newline at end of file diff --git a/src/hook_recv_msg.cc b/src/hook_recv_msg.cc new file mode 100644 index 0000000..7043a4b --- /dev/null +++ b/src/hook_recv_msg.cc @@ -0,0 +1,220 @@ +#include "hook_recv_msg.h" + +#include +#include + +#include + +#include "common.h" +#include "pch.h" +using namespace nlohmann; + +using namespace std; +#define WX_RECV_MSG_HOOK_OFFSET 0xb94796 +#define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x6fe2c0 + +// SyncMgr::addMsgListToDB +// #define WX_RECV_MSG_HOOK_OFFSET 0xB9C919 +// #define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x722FF0 + +#define CLIENT_IP "127.0.0.1" +static int kServerPort = 0; +static int kMessageHooked = FALSE; +static DWORD kWeChatWinBase = GetWeChatWinBase(); + +static char kOriginReceMsgAsmCode[5] = {0}; +static DWORD kReceMsgJmpBackAddress = + kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET + 0x5; +static DWORD kReceMsgNextAddress = + kWeChatWinBase + WX_RECV_MSG_HOOK_NEXT_OFFSET; + +struct InnerMessageStruct { + char *buffer; + int length; + ~InnerMessageStruct() { + if (this->buffer != NULL) { + delete[] this->buffer; + this->buffer = NULL; + } + } +}; +/// @brief send message by socket +/// @param buffer content +/// @param len len +/// @return true/false +BOOL SendBySocket(const char *buffer, size_t len) { + if (kServerPort == 0) { + return false; + } + SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client_socket < 0) { +#ifdef _DEBUG + cout << "create socket error," + << " errno:" << errno << endl; +#endif + return false; + } + 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, CLIENT_IP, &client_addr.sin_addr.s_addr); + if (connect(client_socket, reinterpret_cast(&client_addr), + sizeof(sockaddr)) < 0) { +#ifdef _DEBUG + cout << "connect error," + << " errno:" << errno << endl; +#endif + return false; + } + char recv_buf[1024] = {0}; + int ret = send(client_socket, buffer, len, 0); + if (ret == -1 || ret == 0) { +#ifdef _DEBUG + cout << "send fail," + << " errno:" << errno << endl; +#endif + closesocket(client_socket); + return false; + } + memset(recv_buf, 0, sizeof(recv_buf)); + ret = recv(client_socket, recv_buf, sizeof(recv_buf), 0); + closesocket(client_socket); + if (ret == -1 || ret == 0) { +#ifdef _DEBUG + cout << "the server close" << endl; +#endif + return false; + } + return true; +} +/// @brief send wrap +/// @param msg msg +void SendSocketMessage(InnerMessageStruct *msg) { + if (msg == NULL) { + return; + } + unique_ptr sms(msg); + json j_msg = + json::parse(msg->buffer, msg->buffer + msg->length, nullptr, false); + if (j_msg.is_discarded() == true) { + return; + } + string jstr = j_msg.dump() + "\n"; +#ifdef _DEBUG + cout << "json:" + jstr << endl; +#endif + SendBySocket(jstr.c_str(), jstr.size()); +} +/// @brief msg handle +/// @param msg_addr msg address in memory +void OnRecvMsg(DWORD msg_addr) { + json j_msg; + unsigned long long msgid = *(unsigned long long *)(msg_addr + 0x30); + j_msg["msgId"] = msgid; + j_msg["pid"] = GetCurrentProcessId(); + j_msg["type"] = *(DWORD *)(msg_addr + 0x38); + j_msg["isSendMsg"] = *(BOOL *)(msg_addr + 0x3C); + if (j_msg["isSendMsg"].get()) { + j_msg["isSendByPhone"] = (int)(*(BYTE *)(msg_addr + 0xD8)); + } + j_msg["time"] = + unicode_to_utf8((wchar_t *)GetTimeW(*(DWORD *)(msg_addr + 0x44)).c_str()); + j_msg["timestamp"] = *(DWORD *)(msg_addr + 0x44); + j_msg["fromGroup"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x48).c_str()); + int length = *(DWORD *)(msg_addr + 0x178); + if (length == 0) { + j_msg["fromUser"] = j_msg["fromGroup"].get(); + } else { + j_msg["fromUser"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x174).c_str()); + } + int content_len = *(DWORD *)(msg_addr + 0x74); + if (content_len > 0) { + j_msg["content"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x70).c_str()); +#ifdef _DEBUG + printf("%s", j_msg["content"].get().c_str()); +#endif + } + int sign_len = *(DWORD *)(msg_addr + 0x18C); + if (sign_len > 0) { + j_msg["sign"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x188).c_str()); + } + int thumb_len = *(DWORD *)(msg_addr + 0x1A0); + if (thumb_len > 0) { + j_msg["thumbPath"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x19C).c_str()); + } + int path_len = *(DWORD *)(msg_addr + 0x1B4); + if (path_len > 0) { + j_msg["path"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x1B0).c_str()); + } + + int signature_len = *(DWORD *)(msg_addr + 0x1F4); + if (signature_len > 0) { + j_msg["signature"] = + unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x1F0).c_str()); + } + + string jstr = j_msg.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 +_declspec(naked) void handle_sync_msg() { + __asm { + PUSHAD + PUSHFD + PUSH ECX + CALL OnRecvMsg + ADD ESP, 0x4 + POPFD + POPAD + CALL kReceMsgNextAddress + JMP kReceMsgJmpBackAddress + } +} + +/// @brief hook any address address+0x5 +/// @param port 端口 +/// @return 成功返回1,已经hook返回2,失败返回-1 +int HookRecvMsg(int port) { + kServerPort = port; + kWeChatWinBase = GetWeChatWinBase(); + if (!kWeChatWinBase) { + return -1; + } + + if (kMessageHooked) { + return 2; + } + kWeChatWinBase = GetWeChatWinBase(); + DWORD hook_recv_msg_addr = kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET; + kReceMsgNextAddress = kWeChatWinBase + WX_RECV_MSG_HOOK_NEXT_OFFSET; + kReceMsgJmpBackAddress = hook_recv_msg_addr + 0x5; + HookAnyAddress(hook_recv_msg_addr, (LPVOID)handle_sync_msg, + kOriginReceMsgAsmCode); + kMessageHooked = TRUE; + return 1; +} + +int UnHookRecvMsg() { + kServerPort = 0; + if (!kMessageHooked) return 2; + DWORD hook_recv_msg_addr = kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET; + UnHookAnyAddress(hook_recv_msg_addr, kOriginReceMsgAsmCode); + kMessageHooked = FALSE; + return 1; +} \ No newline at end of file diff --git a/src/hook_recv_msg.h b/src/hook_recv_msg.h new file mode 100644 index 0000000..c6927c6 --- /dev/null +++ b/src/hook_recv_msg.h @@ -0,0 +1,10 @@ +#ifndef HOOK_RECV_MSG_H_ +#define HOOK_RECV_MSG_H_ + +/// @brief hook any address address+0x5 +/// @param port 端口 +/// @return 成功返回1,已经hook返回2 +int HookRecvMsg(int port); + +int UnHookRecvMsg(); +#endif \ No newline at end of file diff --git a/src/new_sqlite3.h b/src/new_sqlite3.h new file mode 100644 index 0000000..4ea593c --- /dev/null +++ b/src/new_sqlite3.h @@ -0,0 +1,195 @@ +#ifndef NEW_SQLITE3_H_ +#define NEW_SQLITE3_H_ +#include "Windows.h" +#define SQLITE_OK 0 /* Successful result */ +/* beginning-of-error-codes */ +#define SQLITE_ERROR 1 /* Generic error */ +#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* Internal use only */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ +#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Not used */ +#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ +#define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ +#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ +#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ +/* end-of-error-codes */ + +/* +** CAPI3REF: Extended Result Codes +** KEYWORDS: {extended result code definitions} +** +** In its default configuration, SQLite API routines return one of 30 integer +** [result codes]. However, experience has shown that many of +** these result codes are too coarse-grained. They do not provide as +** much information about problems as programmers might like. In an effort to +** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8] +** and later) include +** support for additional result codes that provide more detailed information +** about errors. These [extended result codes] are enabled or disabled +** on a per database connection basis using the +** [sqlite3_extended_result_codes()] API. Or, the extended code for +** the most recent error can be obtained using +** [sqlite3_extended_errcode()]. +*/ +#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1 << 8)) +#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2 << 8)) +#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3 << 8)) +#define SQLITE_IOERR_READ (SQLITE_IOERR | (1 << 8)) +#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2 << 8)) +#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3 << 8)) +#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4 << 8)) +#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5 << 8)) +#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6 << 8)) +#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7 << 8)) +#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8 << 8)) +#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9 << 8)) +#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10 << 8)) +#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11 << 8)) +#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12 << 8)) +#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13 << 8)) +#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14 << 8)) +#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15 << 8)) +#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16 << 8)) +#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17 << 8)) +#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18 << 8)) +#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19 << 8)) +#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20 << 8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21 << 8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22 << 8)) +#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23 << 8)) +#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24 << 8)) +#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25 << 8)) +#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26 << 8)) +#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27 << 8)) +#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28 << 8)) +#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29 << 8)) +#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30 << 8)) +#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31 << 8)) +#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32 << 8)) +#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33 << 8)) +#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1 << 8)) +#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2 << 8)) +#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1 << 8)) +#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2 << 8)) +#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3 << 8)) +#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1 << 8)) +#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2 << 8)) +#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3 << 8)) +#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4 << 8)) +#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5 << 8)) /* Not Used */ +#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6 << 8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1 << 8)) +#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2 << 8)) +#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3 << 8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1 << 8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2 << 8)) +#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3 << 8)) +#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4 << 8)) +#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5 << 8)) +#define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6 << 8)) +#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2 << 8)) +#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1 << 8)) +#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2 << 8)) +#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3 << 8)) +#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4 << 8)) +#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5 << 8)) +#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6 << 8)) +#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7 << 8)) +#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8 << 8)) +#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9 << 8)) +#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT | (10 << 8)) +#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT | (11 << 8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT | (12 << 8)) +#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1 << 8)) +#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2 << 8)) +#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1 << 8)) +#define SQLITE_AUTH_USER (SQLITE_AUTH | (1 << 8)) +#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1 << 8)) +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2 << 8)) /* internal use only */ + + +#define SQLITE_INTEGER 1 +#define SQLITE_FLOAT 2 +#define SQLITE_BLOB 4 +#define SQLITE_NULL 5 +#define SQLITE_TEXT 3 + +#define SQLITE3_EXEC_OFFSET 0x1b623b0 +#define SQLITE3_BACKUP_INIT_OFFSET 0x1b27d50 +#define SQLITE3_PREPARE_OFFSET 0x1b68d00 +#define SQLITE3_OPEN_OFFSET 0x1b96cf0 +#define SQLITE3_BACKUP_STEP_OFFSET 0x1b28150 +#define SQLITE3_BACKUP_REMAINING_OFFSET 0x1b28890 +#define SQLITE3_BACKUP_PAGECOUNT_OFFSET 0x1b288a0 +#define SQLITE3_BACKUP_FINISH_OFFSET 0x1b28790 +#define SQLITE3_SLEEP_OFFSET 0x1b97530 +#define SQLITE3_ERRCODE_OFFSET 0x1b95990 +#define SQLITE3_CLOSE_OFFSET 0x1b94110 +#define SQLITE3_STEP_OFFSET 0x1b30bc0 +#define SQLITE3_COLUMN_COUNT_OFFSET 0x1b310d0 +#define SQLITE3_COLUMN_NAME_OFFSET 0x1b319c0 +#define SQLITE3_COLUMN_TYPE_OFFSET 0x1b31860 +#define SQLITE3_COLUMN_BLOB_OFFSET 0x1b31110 +#define SQLITE3_COLUMN_BYTES_OFFSET 0x1b311f0 +#define SQLITE3_FINALIZE_OFFSET 0x1b2fb90 + +typedef int (*Sqlite3_callback)(void*, int, char**, char**); + +typedef int(__cdecl* Sqlite3_exec)(DWORD, /* An open database */ + const char* sql, /* SQL to be evaluated */ + Sqlite3_callback, /* Callback function */ + void*, /* 1st argument to callback */ + char** errmsg /* Error msg written here */ +); +typedef DWORD(__cdecl* Sqlite3_backup_init)( + DWORD* pDest, /* Destination database handle */ + const char* zDestName, /* Destination database name */ + DWORD* pSource, /* Source database handle */ + const char* zSourceName /* Source database name */ +); +typedef int(__cdecl* Sqlite3_prepare)( + DWORD db, /* Database handle */ + const char* zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + DWORD** ppStmt, /* OUT: Statement handle */ + const char** pzTail /* OUT: Pointer to unused portion of zSql */ +); +typedef int(__cdecl* Sqlite3_open)(const char* filename, DWORD** ppDb); +typedef int(__cdecl* Sqlite3_backup_step)(DWORD* p, int nPage); +typedef int(__cdecl* Sqlite3_backup_remaining)(DWORD* p); +typedef int(__cdecl* Sqlite3_backup_pagecount)(DWORD* p); +typedef int(__cdecl* Sqlite3_backup_finish)(DWORD* p); +typedef int(__cdecl* Sqlite3_sleep)(int); +typedef int(__cdecl* Sqlite3_errcode)(DWORD* db); +typedef int(__cdecl* Sqlite3_close)(DWORD*); + +typedef int(__cdecl* Sqlite3_step)(DWORD*); +typedef int(__cdecl* Sqlite3_column_count)(DWORD* pStmt); +typedef const char*(__cdecl* Sqlite3_column_name)(DWORD*, int N); +typedef int(__cdecl* Sqlite3_column_type)(DWORD*, int iCol); +typedef const void*(__cdecl* Sqlite3_column_blob)(DWORD*, int iCol); +typedef int(__cdecl* Sqlite3_column_bytes)(DWORD*, int iCol); +typedef int(__cdecl* Sqlite3_finalize)(DWORD* pStmt); + +#endif \ No newline at end of file diff --git a/src/pch.h b/src/pch.h new file mode 100644 index 0000000..6e2a672 --- /dev/null +++ b/src/pch.h @@ -0,0 +1,18 @@ +#ifndef PCH_H +#define PCH_H + + +#define GLOG_NO_ABBREVIATED_SEVERITIES +#include "framework.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_H + + diff --git a/src/self_info.cc b/src/self_info.cc new file mode 100644 index 0000000..561da22 --- /dev/null +++ b/src/self_info.cc @@ -0,0 +1,139 @@ +#include "pch.h" +#include "self_info.h" + +#include "common.h" + +#include "wechat_data.h" + +#define WX_SELF_NAME_OFFSET 0x2bee198 +#define WX_SELF_MOBILE_OFFSET 0x2BEE108 +#define WX_SELF_CITY_OFFSET 0x2BEE168 +#define WX_SELF_PROVINCE_OFFSET 0x2BEE150 +#define WX_SELF_COUNTRY_OFFSET 0x2BEE138 +#define WX_SELF_ACCOUNT_OFFSET 0x2BEE0F0 +#define WX_SELF_ID_OFFSET 0x2BEE08C +#define WX_SELF_SMALL_IMG_OFFSET 0x2BEE34C +#define WX_SELF_BIG_IMG_OFFSET 0x2BEE364 +#define WX_LOGIN_STATUS_OFFSET 0x2BEE4C0 +#define WX_APP_DATA_ROOT_PATH_OFFSET 0x2c2f478 +#define WX_APP_DATA_SAVE_PATH_OFFSET 0x2C10D04 +#define WX_CURRENT_DATA_PATH_OFFSET 0x2C0EC38 + + + +int GetSelfInfo(SelfInfoInner &out) { + DWORD base = GetWeChatWinBase(); +#ifdef _DEBUG + cout << "mobile:" << (char *)(base + WX_SELF_MOBILE_OFFSET) << endl; + cout << "name:" << (char *)(base + WX_SELF_NAME_OFFSET) << endl; + cout << "city:" << (char *)(base + WX_SELF_CITY_OFFSET) << endl; + cout << "city:" << (char *)(base + WX_SELF_CITY_OFFSET) << endl; + cout << "province:" << (char *)(base + WX_SELF_PROVINCE_OFFSET) << endl; + cout << "country:" << (char *)(base + WX_SELF_COUNTRY_OFFSET) << endl; + cout << "account:" << (char *)(base + WX_SELF_ACCOUNT_OFFSET) << endl; + cout << "wxid:" << (char *)(base + WX_SELF_ID_OFFSET) << endl; + cout << "small_img:" << (char *)(base + WX_SELF_SMALL_IMG_OFFSET) << endl; + cout << "big_img:" << (char *)(base + WX_SELF_BIG_IMG_OFFSET) << endl; +#endif + if (*(DWORD *)(base + WX_SELF_NAME_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_NAME_OFFSET + 0x10) == 0) { + out.name = string(); + } else { + out.name = string((char *)(base + WX_SELF_NAME_OFFSET), + *(DWORD *)(base + WX_SELF_NAME_OFFSET + 0x10)); + } + if (*(DWORD *)(base + WX_SELF_MOBILE_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_MOBILE_OFFSET + 0x10) == 0) { + out.mobile = string(); + } else { + out.mobile = string((char *)(base + WX_SELF_MOBILE_OFFSET), + *(DWORD *)(base + WX_SELF_MOBILE_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_CITY_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_CITY_OFFSET + 0x10) == 0) { + out.city = string(); + } else { + out.city = string((char *)(base + WX_SELF_CITY_OFFSET), + *(DWORD *)(base + WX_SELF_CITY_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_PROVINCE_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_PROVINCE_OFFSET + 0x10) == 0) { + out.province = string(); + } else { + out.province = string((char *)(base + WX_SELF_PROVINCE_OFFSET), + *(DWORD *)(base + WX_SELF_PROVINCE_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_COUNTRY_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_COUNTRY_OFFSET + 0x10) == 0) { + out.country = string(); + } else { + out.country = string((char *)(base + WX_SELF_COUNTRY_OFFSET), + *(DWORD *)(base + WX_SELF_COUNTRY_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_ACCOUNT_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_ACCOUNT_OFFSET + 0x10) == 0) { + out.account = string(); + } else { + out.account = string(*(char **)(base + WX_SELF_ACCOUNT_OFFSET), + *(DWORD *)(base + WX_SELF_ACCOUNT_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_ID_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_ID_OFFSET + 0x10) == 0) { + out.wxid = string(); + } else { + out.wxid = string(*(char **)(base + WX_SELF_ID_OFFSET), + *(DWORD *)(base + WX_SELF_ID_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_SMALL_IMG_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_SMALL_IMG_OFFSET + 0x10) == 0) { + out.small_img = string(); + } else { + out.small_img = string(*(char **)(base + WX_SELF_SMALL_IMG_OFFSET), + *(DWORD *)(base + WX_SELF_SMALL_IMG_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_SELF_BIG_IMG_OFFSET) == 0 || + *(DWORD *)(base + WX_SELF_BIG_IMG_OFFSET + 0x10) == 0) { + out.big_img = string(); + } else { + out.big_img = string(*(char **)(base + WX_SELF_BIG_IMG_OFFSET), + *(DWORD *)(base + WX_SELF_BIG_IMG_OFFSET + 0x10)); + } + + if (*(DWORD *)(base + WX_APP_DATA_ROOT_PATH_OFFSET) == 0 || + *(DWORD *)(base + WX_APP_DATA_ROOT_PATH_OFFSET + 0x4) == 0) { + out.data_root_path = string(); + } else { + out.data_root_path = Wstring2String(wstring(*(wchar_t **)(base + WX_APP_DATA_ROOT_PATH_OFFSET), + *(DWORD *)(base + WX_APP_DATA_ROOT_PATH_OFFSET + 0x4))); + } + + if (*(DWORD *)(base + WX_APP_DATA_SAVE_PATH_OFFSET) == 0 || + *(DWORD *)(base + WX_APP_DATA_SAVE_PATH_OFFSET + 0x4) == 0) { + out.data_save_path = string(); + } else { + out.data_save_path = Wstring2String(wstring(*(wchar_t **)(base + WX_APP_DATA_SAVE_PATH_OFFSET), + *(DWORD *)(base + WX_APP_DATA_SAVE_PATH_OFFSET + 0x4))); + } + if (*(DWORD *)(base + WX_CURRENT_DATA_PATH_OFFSET) == 0 || + *(DWORD *)(base + WX_CURRENT_DATA_PATH_OFFSET + 0x4) == 0) { + out.current_data_path = string(); + } else { + out.current_data_path = Wstring2String(wstring(*(wchar_t **)(base + WX_CURRENT_DATA_PATH_OFFSET), + *(DWORD *)(base + WX_CURRENT_DATA_PATH_OFFSET + 0x4))); + } + + return 1; +} + + +int CheckLogin(){ + DWORD base = GetWeChatWinBase(); + return *(DWORD*) (base + WX_LOGIN_STATUS_OFFSET); +} \ No newline at end of file diff --git a/src/self_info.h b/src/self_info.h new file mode 100644 index 0000000..50ddac3 --- /dev/null +++ b/src/self_info.h @@ -0,0 +1,7 @@ +#ifndef SELF_INFO_H_ +#define SELF_INFO_H_ +#include "wechat_data.h" +int GetSelfInfo(SelfInfoInner& out); + +int CheckLogin(); +#endif \ No newline at end of file diff --git a/src/send_file.cc b/src/send_file.cc new file mode 100644 index 0000000..a33727c --- /dev/null +++ b/src/send_file.cc @@ -0,0 +1,67 @@ +#include "pch.h" +#include "send_file.h" +#include "common.h" +#include "wechat_data.h" + +#define WX_APP_MSG_MGR_OFFSET 0x665f60 +#define WX_SEND_FILE_OFFSET 0xa0ce20 +#define WX_INIT_CHAT_MSG_OFFSET 0xdbcc40 +#define WX_FREE_CHAT_MSG_OFFSET 0x651c40 + +int SendFile(wchar_t *wxid, wchar_t *file_path){ + int success = 0; + WeChatString to_user(wxid); + WeChatString path(file_path); + char chat_msg[0x2A8] = {0}; + DWORD base = GetWeChatWinBase(); + DWORD app_msg_mgr_addr = base + WX_APP_MSG_MGR_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD send_file_addr = base + WX_SEND_FILE_OFFSET; + DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD temp = 0; + WeChatString null_obj = {0}; + __asm{ + PUSHAD + PUSHFD + CALL app_msg_mgr_addr + SUB ESP,0x14 + MOV temp,EAX + LEA EAX,null_obj + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + PUSH 0x0 + SUB ESP,0x14 + MOV EDI,ESP + MOV dword ptr [EDI],0 + MOV dword ptr [EDI + 0x4],0 + MOV dword ptr [EDI + 0x8],0 + MOV dword ptr [EDI + 0xc],0 + MOV dword ptr [EDI + 0x10],0 + SUB ESP,0x14 + LEA EAX,path + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + SUB ESP,0x14 + LEA EAX,to_user + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + MOV ECX,dword ptr [temp] + LEA EAX,chat_msg + PUSH EAX + CALL send_file_addr + MOV AL,byte ptr [eax + 0x38] + MOVZX EAX,AL + MOV success,EAX + LEA ECX,chat_msg + CALL free_msg_addr + POPFD + POPAD + } + if (success == 0x31){ + return 1; + } + return 0; +} \ No newline at end of file diff --git a/src/send_file.h b/src/send_file.h new file mode 100644 index 0000000..7436b4d --- /dev/null +++ b/src/send_file.h @@ -0,0 +1,5 @@ +#ifndef SEND_FILE_H_ +#define SEND_FILE_H_ + +int SendFile(wchar_t *wxid, wchar_t *file_path); +#endif \ No newline at end of file diff --git a/src/send_image.cc b/src/send_image.cc new file mode 100644 index 0000000..29a358a --- /dev/null +++ b/src/send_image.cc @@ -0,0 +1,47 @@ +#include "pch.h" +#include "send_image.h" +#include "common.h" +#include "wechat_data.h" + +#define WX_SEND_IMAGE_OFFSET 0xb68b90 +#define WX_SEND_MESSAGE_MGR_OFFSET 0x663320 +#define WX_INIT_CHAT_MSG_OFFSET 0xdbcc40 +#define WX_FREE_CHAT_MSG_OFFSET 0x651c40 + +int SendImage(wchar_t *wxid, wchar_t *image_path){ + + int success = 0; + WeChatString to_user(wxid); + WeChatString path(image_path); + char chat_msg[0x2A8] ={0}; + DWORD base = GetWeChatWinBase(); + DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD send_image_msg_addr = base + WX_SEND_IMAGE_OFFSET; + DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD temp = 0; + WeChatString null_obj = {0}; + __asm{ + PUSHAD + CALL send_message_mgr_addr + SUB ESP,0x14 + MOV temp,EAX + LEA EAX,null_obj + MOV ECX,ESP + LEA EDI,path + PUSH EAX + CALL init_chat_msg_addr + MOV ECX,dword ptr [temp] + LEA EAX,to_user + PUSH EDI + PUSH EAX + LEA EAX,chat_msg + PUSH EAX + CALL send_image_msg_addr + MOV success,EAX + LEA ECX,chat_msg + CALL free_msg_addr + POPAD + } + return success; +} \ No newline at end of file diff --git a/src/send_image.h b/src/send_image.h new file mode 100644 index 0000000..0be9085 --- /dev/null +++ b/src/send_image.h @@ -0,0 +1,5 @@ +#ifndef SEND_IMAGE_H_ +#define SEND_IMAGE_H_ + +int SendImage(wchar_t *wxid, wchar_t *image_path); +#endif \ No newline at end of file diff --git a/src/send_text.cc b/src/send_text.cc new file mode 100644 index 0000000..6ba20cc --- /dev/null +++ b/src/send_text.cc @@ -0,0 +1,45 @@ +#include "pch.h" +#include "send_text.h" + + +#include "common.h" +#include "wechat_data.h" + +#define WX_SEND_TEXT_OFFSET 0xb690a0 + +#define WX_SEND_MESSAGE_MGR_OFFSET 0x663320 + +#define WX_FREE_CHAT_MSG_OFFSET 0x651c40 +/// @brief 发生文本消息 +/// @param wxid wxid +/// @param msg 文本消息 +/// @return 成功返回1 +int SendText(wchar_t* wxid, wchar_t* msg) { + int success = 0; + WeChatString to_user(wxid); + WeChatString text_msg(msg); + wchar_t **msg_pptr = &text_msg.ptr; + char chat_msg[0x2A8] ={0}; + DWORD base = GetWeChatWinBase(); + DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD send_text_msg_addr = base + WX_SEND_TEXT_OFFSET; + DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + __asm{ + PUSHAD + PUSH 0x0 + PUSH 0x0 + PUSH 0x1 + PUSH 0x0 + MOV EDI,msg_pptr + PUSH EDI + LEA EDX,to_user + LEA ECX,chat_msg + CALL send_text_msg_addr + ADD ESP,0x14 + MOV success,EAX + LEA ECX,chat_msg + CALL free_msg_addr + POPAD + } + return success; +} \ No newline at end of file diff --git a/src/send_text.h b/src/send_text.h new file mode 100644 index 0000000..57005dd --- /dev/null +++ b/src/send_text.h @@ -0,0 +1,6 @@ +#ifndef SEND_TEXT_H_ +#define SEND_TEXT_H_ + +int SendText(wchar_t* wxid, wchar_t* msg); + +#endif \ No newline at end of file diff --git a/src/wechat_data.h b/src/wechat_data.h new file mode 100644 index 0000000..d871ea5 --- /dev/null +++ b/src/wechat_data.h @@ -0,0 +1,140 @@ +#ifndef WECHAT_DATA_H_ +#define WECHAT_DATA_H_ + +// #include + +#include +using namespace std; +struct WeChatString { + wchar_t *ptr; + DWORD length; + DWORD max_length; + DWORD c_ptr = 0; + DWORD c_len = 0; + WeChatString() { WeChatString(NULL); } + + WeChatString(wstring &s) { + ptr = (wchar_t *)(s.c_str()); + length = s.length(); + max_length = s.length() * 2; + } + WeChatString(const wchar_t *pStr) { WeChatString((wchar_t *)pStr); } + WeChatString(int tmp) { + ptr = NULL; + length = 0x0; + max_length = 0x0; + } + WeChatString(wchar_t *pStr) { + ptr = pStr; + length = wcslen(pStr); + max_length = wcslen(pStr) * 2; + } + void set_value(const wchar_t *pStr) { + ptr = (wchar_t *)pStr; + length = wcslen(pStr); + max_length = wcslen(pStr) * 2; + } +}; + +struct TableInfo { + char *name; + DWORD name_len; + char *table_name; + DWORD table_name_len; + char *sql; + DWORD sql_len; + char *rootpage; + DWORD rootpage_len; +}; + +struct DatabaseInfo { + DWORD handle = 0; + wchar_t *db_name = NULL; + DWORD db_name_len = 0; + vector tables; + DWORD count = 0; + DWORD extrainfo = 0; +}; + +struct Contact { + WeChatString wxid; + WeChatString custom_account; + WeChatString encrypt_name; + WeChatString nick_name; + WeChatString pinyin; + WeChatString pinyin_all; + int del_flag; + int type; + int verify_flag; +}; + +struct ChatRoomInfo { + DWORD vftable; + WeChatString chat_room_id; + WeChatString notice; + WeChatString admin; + DWORD filed_40; + DWORD filed_44; + DWORD filed_48; + DWORD filed_4C; + WeChatString xml; + DWORD filed_64; + DWORD filed_68; + DWORD filed_6C; + DWORD filed_70; + DWORD filed_74; + DWORD filed_78; + DWORD filed_7C; + DWORD filed_80; + DWORD filed_84; + DWORD filed_88; + DWORD filed_8c; + DWORD filed_90; + DWORD filed_94; + DWORD filed_98; + DWORD filed_9C; + DWORD filed_A0; +}; + +struct ChatRoomInfoInner { + WeChatString chat_room_id; + WeChatString notice; + WeChatString admin; + WeChatString xml; +}; + +struct VectorInner { +#ifdef _DEBUG + DWORD head; +#endif + DWORD start; + DWORD finsh; + DWORD end; +}; + +struct ChatRoomInner{ + char* members; + wchar_t* chat_room; + wchar_t* admin; + ~ChatRoomInner(){ + delete []members; + delete []chat_room; + delete []admin; + } +}; + +struct SelfInfoInner{ + string name; + string city; + string province; + string country; + string account; + string wxid; + string mobile; + string small_img; + string big_img; + string data_root_path; + string data_save_path; + string current_data_path; +}; +#endif