Merge pull request #38 from lich0821/3.9.2.23

3.9.2.23
This commit is contained in:
Changhua 2023-07-10 19:57:25 +08:00 committed by GitHub
commit 703c2f62d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1042 additions and 528 deletions

View File

@ -1,6 +1,9 @@
# WeChatFerry
一个玩微信的工具。更多介绍见:[WeChatFerry: 一个玩微信的工具](https://mp.weixin.qq.com/s/CGLfSaNDy8MyuyPWGjGJ7w)。
|[📖 文档](https://wechatferry.readthedocs.io/)|[📺 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/XTJ9H-FsCPCscixAts8i_A)|
|:-:|:-:|:-:|
👉 [WeChatRobot🤖](https://github.com/lich0821/WeChatRobot),一个基于 WeChatFerry 的 Python 机器人框架。
<details><summary>点击查看功能清单</summary>

View File

@ -1,6 +1,8 @@
# WeChatFerry Python 客户端
[![PyPi](https://img.shields.io/pypi/v/wcferry.svg)](https://pypi.python.org/pypi/wcferry) [![Downloads](https://static.pepy.tech/badge/wcferry)](https://pypi.python.org/pypi/wcferry) [![Documentation Status](https://readthedocs.org/projects/wechatferry/badge/?version=latest)](https://wechatferry.readthedocs.io/zh/latest/?badge=latest)
|[📖 文档](https://wechatferry.readthedocs.io/)|[📺 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/XTJ9H-FsCPCscixAts8i_A)|
|:-:|:-:|:-:|
🤖示例机器人框架:[WeChatRobot](https://github.com/lich0821/WeChatRobot)。
## 快速开始
@ -37,7 +39,6 @@ def process_msg(wcf: Wcf):
def main():
LOG.info("Start demo...")
wcf = Wcf(debug=True) # 默认连接本地服务
# wcf = Wcf("tcp://127.0.0.1:10086") # 连接远端服务
sleep(5) # 等微信加载好,以免信息显示异常
LOG.info(f"已经登录: {True if wcf.is_login() else False}")
@ -110,10 +111,49 @@ pip install grpcio-tools pynng
### 重新生成 PB 文件
```sh
cd python\wcferry
# CMD
cd python\wcferry
python -m grpc_tools.protoc --python_out=. --proto_path=..\..\rpc\proto\ wcf.proto
# GitBash
cd python/wcferry
python -m grpc_tools.protoc --python_out=. --proto_path=../../rpc/proto/ wcf.proto
```
## 版本更新
版本号:`w.x.y.z`。
其中:
* `w` 是微信的大版本号,如 `37` (3.7.a.a), `38` (3.8.a.a), `39` (3.9.a.a)
* `x` 是适配的微信的小版本号,从 0 开始
* `y` 是 `WeChatFerry` 的版本,从 0 开始
* `z` 是各客户端的版本,从 0 开始
### 37.1.25.5 (2023.05.19)
支持 `3.7.0.30` 的最后一个版本。
功能:
* 检查登录状态
* 获取登录账号的 wxid
* 获取消息类型
* 获取所有联系人
* 获取所有好友
* 获取数据库
* 获取某数据库下的表
* 发送文本消息(可 @
* 发送图片Python 客户端支持网络路径)
* 发送文件
* 发送 XML
* 发送表情
* 允许接收消息
* 停止接收消息
* 执行 SQL 查询
* 接受好友申请
* 添加群成员
* 解密图片
* 某功能
<details><summary>历史更新</summary>
</details>

View File

@ -1,7 +1,7 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
__version__ = "37.1.25.5"
__version__ = "39.0.0.0"
import atexit
import base64
@ -180,6 +180,8 @@ class Wcf():
gender = ""
elif gender == 2:
gender = ""
else:
gender = ""
contact = {
"wxid": cnt.get("wxid", ""),
"code": cnt.get("code", ""),
@ -502,29 +504,13 @@ class Wcf():
return friends
def add_chatroom_members(self, roomid: str, wxids: str) -> int:
"""添加群成员
Args:
roomid (str): 待加群的 id
wxids (str): 要加到群里的 wxid多个用逗号分隔
Returns:
int: 1 为成功其他失败
"""
req = wcf_pb2.Request()
req.func = wcf_pb2.FUNC_ADD_ROOM_MEMBERS # FUNC_ADD_ROOM_MEMBERS
req.m.roomid = roomid
req.m.wxids = wxids
rsp = self._send_request(req)
return rsp.status
def receive_transfer(self, wxid: str, transferid: str) -> int:
def receive_transfer(self, wxid: str, transferid: str, transactionid: str) -> int:
"""接收转账
Args:
wxid (str): 转账消息里的发送人 wxid
transferid (str): 转账消息里的 transferid
transactionid (str): 转账消息里的 transactionid
Returns:
int: 1 为成功其他失败
@ -532,7 +518,8 @@ class Wcf():
req = wcf_pb2.Request()
req.func = wcf_pb2.FUNC_RECV_TRANSFER # FUNC_RECV_TRANSFER
req.tf.wxid = wxid
req.tf.tid = transferid
req.tf.tfid = transferid
req.tf.taid = transactionid
rsp = self._send_request(req)
return rsp.status
@ -552,3 +539,37 @@ class Wcf():
req.dec.dst = dst
rsp = self._send_request(req)
return rsp.status == 1
def add_chatroom_members(self, roomid: str, wxids: str) -> int:
"""添加群成员
Args:
roomid (str): 待加群的 id
wxids (str): 要加到群里的 wxid多个用逗号分隔
Returns:
int: 1 为成功其他失败
"""
req = wcf_pb2.Request()
req.func = wcf_pb2.FUNC_ADD_ROOM_MEMBERS # FUNC_ADD_ROOM_MEMBERS
req.m.roomid = roomid
req.m.wxids = wxids
rsp = self._send_request(req)
return rsp.status
def del_chatroom_members(self, roomid: str, wxids: str) -> int:
"""删除群成员
Args:
roomid (str): 群的 id
wxids (str): 要删除成员的 wxid多个用逗号分隔
Returns:
int: 1 为成功其他失败
"""
req = wcf_pb2.Request()
req.func = wcf_pb2.FUNC_DEL_ROOM_MEMBERS # FUNC_DEL_ROOM_MEMBERS
req.m.roomid = roomid
req.m.wxids = wxids.replace(" ", "")
rsp = self._send_request(req)
return rsp.status

View File

@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xc8\x02\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.AddMembersH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x42\x05\n\x03msg\"\xab\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xa0\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0c\n\x04type\x18\x03 \x01(\x05\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0b\n\x03xml\x18\x05 \x01(\t\x12\x0e\n\x06sender\x18\x06 \x01(\t\x12\x0e\n\x06roomid\x18\x07 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x08 \x01(\t\x12\r\n\x05thumb\x18\t \x01(\t\x12\r\n\x05\x65xtra\x18\n \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x05\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nAddMembers\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"%\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0b\n\x03tid\x18\x02 \x01(\t*\xd3\x03\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10R\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10S\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`B\r\n\x0b\x63om.iamteerb\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xc8\x02\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.AddMembersH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x42\x05\n\x03msg\"\xab\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xa0\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0c\n\x04type\x18\x03 \x01(\x05\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0b\n\x03xml\x18\x05 \x01(\t\x12\x0e\n\x06sender\x18\x06 \x01(\t\x12\x0e\n\x06roomid\x18\x07 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x08 \x01(\t\x12\r\n\x05thumb\x18\t \x01(\t\x12\r\n\x05\x65xtra\x18\n \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x05\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nAddMembers\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t*\xee\x03\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3')
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'wcf_pb2', globals())
@ -23,8 +23,8 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._serialized_options = b'\n\013com.iamteer'
_MSGTYPES_TYPESENTRY._options = None
_MSGTYPES_TYPESENTRY._serialized_options = b'8\001'
_FUNCTIONS._serialized_start=1805
_FUNCTIONS._serialized_end=2272
_FUNCTIONS._serialized_start=1820
_FUNCTIONS._serialized_end=2314
_REQUEST._serialized_start=19
_REQUEST._serialized_end=347
_RESPONSE._serialized_start=350
@ -70,5 +70,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
_DECPATH._serialized_start=1728
_DECPATH._serialized_end=1763
_TRANSFER._serialized_start=1765
_TRANSFER._serialized_end=1802
_TRANSFER._serialized_end=1817
# @@protoc_insertion_point(module_scope)

View File

@ -21,9 +21,10 @@ enum Functions {
FUNC_DISABLE_RECV_TXT = 0x40;
FUNC_EXEC_DB_QUERY = 0x50;
FUNC_ACCEPT_FRIEND = 0x51;
FUNC_ADD_ROOM_MEMBERS = 0x52;
FUNC_RECV_TRANSFER = 0x53;
FUNC_RECV_TRANSFER = 0x52;
FUNC_DECRYPT_IMAGE = 0x60;
FUNC_ADD_ROOM_MEMBERS = 0x70;
FUNC_DEL_ROOM_MEMBERS = 0x71;
}
message Request
@ -167,5 +168,6 @@ message DecPath
message Transfer
{
string wxid = 1; //
string tid = 2; // id transferid
string tfid = 2; // id transferid
string taid = 3; // Transaction id
}

View File

@ -88,3 +88,35 @@ bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcNa
CloseHandle(hThread);
return true;
}
bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz,
DWORD *ret)
{
void *pFunc = GetFuncAddr(dllPath, dllBase, funcName);
if (pFunc == NULL) {
return false;
}
LPVOID pRemoteAddress = VirtualAllocEx(process, NULL, sz, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteAddress == NULL) {
MessageBox(NULL, L"申请内存失败", L"CallDllFuncEx", 0);
return NULL;
}
WriteProcessMemory(process, pRemoteAddress, parameter, sz, NULL);
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pRemoteAddress, 0, NULL);
if (hThread == NULL) {
VirtualFree(pRemoteAddress, 0, MEM_RELEASE);
MessageBox(NULL, L"远程调用失败", L"CallDllFuncEx", 0);
return false;
}
WaitForSingleObject(hThread, INFINITE);
VirtualFree(pRemoteAddress, 0, MEM_RELEASE);
if (ret != NULL) {
GetExitCodeThread(hThread, ret);
}
CloseHandle(hThread);
return true;
}

View File

@ -5,3 +5,5 @@
HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase);
bool EjectDll(HANDLE process, HMODULE dllBase);
bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, DWORD *ret);
bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz,
DWORD *ret);

View File

@ -1,10 +1,10 @@
#include "Shlwapi.h"
#include "framework.h"
#include <filesystem>
#include <process.h>
#include <tlhelp32.h>
#include "injector.h"
#include "log.h"
#include "sdk.h"
#include "util.h"
@ -17,7 +17,6 @@ static WCHAR spyDllPath[MAX_PATH] = { 0 };
static int GetDllPath(bool debug, wchar_t *dllPath)
{
InitLogger();
GetModuleFileName(GetModuleHandle(WECHATSDKDLL), spyDllPath, MAX_PATH);
PathRemoveFileSpec(spyDllPath);
if (debug) {
@ -27,7 +26,7 @@ static int GetDllPath(bool debug, wchar_t *dllPath)
}
if (!PathFileExists(spyDllPath)) {
LOG_ERROR("DLL does not exists: {}.", Wstring2String(spyDllPath));
MessageBox(NULL, spyDllPath, L"文件不存在", 0);
return ERROR_FILE_NOT_FOUND;
}
@ -46,26 +45,30 @@ int WxInitSDK(bool debug, int port)
status = OpenWeChat(&wcPid);
if (status != 0) {
LOG_ERROR("OpenWeChat failed: {}.", status);
MessageBox(NULL, L"打开微信失败", L"WxInitSDK", 0);
return status;
}
Sleep(2000); // 等待微信打开
wcProcess = InjectDll(wcPid, spyDllPath, &spyBase);
if (wcProcess == NULL) {
LOG_ERROR("Failed to Inject DLL into WeChat.");
MessageBox(NULL, L"注入失败", L"WxInitSDK", 0);
return -1;
}
if (!CallDllFunc(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)port, NULL)) {
LOG_ERROR("Failed to InitSpy.");
PortPath_t pp = { 0 };
pp.port = port;
sprintf_s(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str());
if (!CallDllFuncEx(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(PortPath_t), NULL)) {
MessageBox(NULL, L"初始化失败", L"WxInitSDK", 0);
return -1;
}
#ifdef WCF
FILE *fd = fopen(WCF_LOCK, "wb");
if (fd == NULL) {
LOG_ERROR("Failed to open {}.", WCF_LOCK);
MessageBox(NULL, L"无法打开lock文件", L"WxInitSDK", 0);
return -2;
}
fwrite((uint8_t *)&debug, sizeof(debug), 1, fd);
@ -83,19 +86,19 @@ int WxDestroySDK()
bool debug;
DWORD pid = GetWeChatPid();
if (pid == 0) {
LOG_ERROR("WeChat is not running.");
MessageBox(NULL, L"微信未运行", L"WxDestroySDK", 0);
return status;
}
wcProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (wcProcess == NULL) {
LOG_ERROR("WeChat is not running.");
MessageBox(NULL, L"微信未运行", L"WxDestroySDK", 0);
return -1;
}
FILE *fd = fopen(WCF_LOCK, "rb");
if (fd == NULL) {
LOG_ERROR("Failed to open {}.", WCF_LOCK);
MessageBox(NULL, L"无法打开lock文件", L"WxDestroySDK", 0);
return -2;
}
fread((uint8_t *)&debug, sizeof(debug), 1, fd);
@ -111,14 +114,12 @@ int WxDestroySDK()
}
if (!CallDllFunc(wcProcess, spyDllPath, spyBase, "CleanupSpy", NULL, NULL)) {
LOG_ERROR("Failed to CleanupSpy.");
return -1;
}
if (!EjectDll(wcProcess, spyBase)) {
LOG_ERROR("Failed to Eject DLL.");
return -1; // TODO: Unify error codes
}
LOG_INFO("WxDestroySDK done.");
return 0;
}

View File

@ -107,7 +107,7 @@
</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile />
<DisableSpecificWarnings>4251;4819</DisableSpecificWarnings>
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/EHa %(AdditionalOptions)</AdditionalOptions>
@ -153,7 +153,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile />
<DisableSpecificWarnings>4251;4819</DisableSpecificWarnings>
<DisableSpecificWarnings>4251;4731;4819</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
@ -226,12 +226,11 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<ClInclude Include="..\rpc\pb_types.h" />
<ClInclude Include="..\rpc\pb_util.h" />
<ClInclude Include="..\rpc\proto\wcf.pb.h" />
<ClInclude Include="accept_new_friend.h" />
<ClInclude Include="add_chatroom_member.h" />
<ClInclude Include="chatroom_mgmt.h" />
<ClInclude Include="decrypt_image.h" />
<ClInclude Include="exec_sql.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="get_contacts.h" />
<ClInclude Include="contact_mgmt.h" />
<ClInclude Include="load_calls.h" />
<ClInclude Include="log.h" />
<ClInclude Include="receive_msg.h" />
@ -241,6 +240,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<ClInclude Include="send_msg.h" />
<ClInclude Include="spy.h" />
<ClInclude Include="spy_types.h" />
<ClInclude Include="sqlite3.h" />
<ClInclude Include="user_info.h" />
<ClInclude Include="util.h" />
</ItemGroup>
@ -250,12 +250,11 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto</Command>
<ClCompile Include="..\rpc\nanopb\pb_encode.c" />
<ClCompile Include="..\rpc\pb_util.cpp" />
<ClCompile Include="..\rpc\proto\wcf.pb.c" />
<ClCompile Include="accept_new_friend.cpp" />
<ClCompile Include="add_chatroom_member.cpp" />
<ClCompile Include="chatroom_mgmt.cpp" />
<ClCompile Include="decrypt_image.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="exec_sql.cpp" />
<ClCompile Include="get_contacts.cpp" />
<ClCompile Include="contact_mgmt.cpp" />
<ClCompile Include="load_calls.cpp" />
<ClCompile Include="log.cpp" />
<ClCompile Include="receive_msg.cpp" />

View File

@ -27,13 +27,10 @@
<ClInclude Include="log.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="accept_new_friend.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="exec_sql.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="get_contacts.h">
<ClInclude Include="contact_mgmt.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="load_calls.h">
@ -75,7 +72,7 @@
<ClInclude Include="..\rpc\pb_types.h">
<Filter>nnrpc</Filter>
</ClInclude>
<ClInclude Include="add_chatroom_member.h">
<ClInclude Include="chatroom_mgmt.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="user_info.h">
@ -90,6 +87,9 @@
<ClInclude Include="receive_transfer.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="sqlite3.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@ -101,13 +101,10 @@
<ClCompile Include="log.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="accept_new_friend.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="exec_sql.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="get_contacts.cpp">
<ClCompile Include="contact_mgmt.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="load_calls.cpp">
@ -140,7 +137,7 @@
<ClCompile Include="..\rpc\pb_util.cpp">
<Filter>nnrpc</Filter>
</ClCompile>
<ClCompile Include="add_chatroom_member.cpp">
<ClCompile Include="chatroom_mgmt.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="user_info.cpp">

View File

@ -1,76 +0,0 @@
#include "framework.h"
#include "accept_new_friend.h"
#include "load_calls.h"
#include "log.h"
#include "util.h"
typedef struct NewFriendParam {
DWORD handle;
DWORD *status;
DWORD statusEnd1;
DWORD statusEnd2;
char buffer[0x3C];
} NewFriendParam_t;
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
int AcceptNewFriend(std::string v3, std::string v4, int scene)
{
int isSucceeded = 0;
DWORD acceptNewFriendCall1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1;
DWORD acceptNewFriendCall2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2;
DWORD acceptNewFriendHandle = g_WeChatWinDllAddr + g_WxCalls.anf.handle;
char buffer[0x94] = { 0 };
NewFriendParam_t param = { 0 };
DWORD status[9] = { 0xB2, (DWORD)&param, 0xB5, (DWORD)&param, 0xB0, (DWORD)&param, 0xB1, (DWORD)&param, 0x00 };
param.handle = acceptNewFriendHandle;
param.status = status;
param.statusEnd1 = (DWORD)status + 0x20;
param.statusEnd2 = (DWORD)status + 0x20;
NewFriendParam_t *pParam = &param;
LOG_DEBUG("v3: {}\nv4: {}", v3, v4);
WxString_t wxV3 = { 0 };
WxString_t wxV4 = { 0 };
std::wstring wsV3 = String2Wstring(v3);
std::wstring wsV4 = String2Wstring(v4);
wxV3.text = (wchar_t *)wsV3.c_str();
wxV3.size = wsV3.size();
wxV3.capacity = wsV3.capacity();
wxV4.text = (wchar_t *)wsV4.c_str();
wxV4.size = wsV4.size();
wxV4.capacity = wsV4.capacity();
__asm {
pushad;
pushfd;
push 0x0;
mov eax, scene;
push eax;
sub esp, 0x14;
mov ecx, esp;
lea eax, wxV4;
push eax;
call acceptNewFriendCall1;
sub esp, 0x8;
push 0x0;
lea eax, buffer;
push eax;
lea eax, wxV3;
push eax;
mov ecx, pParam;
call acceptNewFriendCall2;
mov isSucceeded, eax;
popfd;
popad;
}
return isSucceeded; // 成功返回 1
}

View File

@ -1,67 +0,0 @@
#include "framework.h"
#include <sstream>
#include "add_chatroom_member.h"
#include "load_calls.h"
#include "log.h"
#include "util.h"
using namespace std;
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
int AddChatroomMember(string roomid, string wxids)
{
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return -1;
}
int rv = 0;
DWORD addRoomMemberCall1 = g_WeChatWinDllAddr + g_WxCalls.arm.call1;
DWORD addRoomMemberCall2 = g_WeChatWinDllAddr + g_WxCalls.arm.call2;
DWORD addRoomMemberCall3 = g_WeChatWinDllAddr + g_WxCalls.arm.call3;
WxString_t txtRoomid = { 0 };
wstring wsRoomid = String2Wstring(roomid);
txtRoomid.text = (wchar_t *)wsRoomid.c_str();
txtRoomid.size = wsRoomid.size();
txtRoomid.capacity = wsRoomid.capacity();
vector<wstring> vMembers;
vector<WxString_t> vTxtMembers;
wstringstream wss(String2Wstring(wxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vMembers.push_back(wstr);
WxString_t txtMember = { 0 };
txtMember.text = (wchar_t *)vMembers.back().c_str();
txtMember.size = vMembers.back().size();
txtMember.capacity = vMembers.back().capacity();
vTxtMembers.push_back(txtMember);
}
LOG_DEBUG("Adding {} members[{}] to {}", vTxtMembers.size(), wxids.c_str(), roomid.c_str());
__asm {
pushad;
pushfd;
call addRoomMemberCall1;
sub esp, 0x14;
mov esi, eax;
mov ecx, esp;
lea eax, txtRoomid;
push eax;
call addRoomMemberCall2;
lea edi, vTxtMembers
push edi;
mov ecx, esi;
call addRoomMemberCall3;
mov rv, eax;
popfd;
popad;
}
return rv;
}

128
spy/chatroom_mgmt.cpp Normal file
View File

@ -0,0 +1,128 @@
#include "framework.h"
#include <sstream>
#include <vector>
#include "chatroom_mgmt.h"
#include "load_calls.h"
#include "log.h"
#include "util.h"
using namespace std;
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
int AddChatroomMember(string roomid, string wxids)
{
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return -1;
}
int rv = 0;
DWORD addRoomMemberCall1 = g_WeChatWinDllAddr + g_WxCalls.arm.call1;
DWORD addRoomMemberCall2 = g_WeChatWinDllAddr + g_WxCalls.arm.call2;
DWORD addRoomMemberCall3 = g_WeChatWinDllAddr + g_WxCalls.arm.call3;
DWORD temp = 0;
WxString_t txtRoomid = { 0 };
wstring wsRoomid = String2Wstring(roomid);
txtRoomid.text = (wchar_t *)wsRoomid.c_str();
txtRoomid.size = wsRoomid.size();
txtRoomid.capacity = wsRoomid.capacity();
vector<wstring> vMembers;
vector<WxString_t> vTxtMembers;
wstringstream wss(String2Wstring(wxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vMembers.push_back(wstr);
WxString_t txtMember = { 0 };
txtMember.text = (wchar_t *)vMembers.back().c_str();
txtMember.size = vMembers.back().size();
txtMember.capacity = vMembers.back().capacity();
vTxtMembers.push_back(txtMember);
}
LOG_DEBUG("Adding {} members[{}] to {}", vTxtMembers.size(), wxids.c_str(), roomid.c_str());
__asm {
pushad;
pushfd;
call addRoomMemberCall1;
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, txtRoomid;
push eax;
call addRoomMemberCall2;
mov ecx, temp;
lea eax, vTxtMembers;
push eax;
call addRoomMemberCall3;
mov rv, eax;
popfd;
popad;
}
return rv;
}
int DelChatroomMember(string roomid, string wxids)
{
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
return -1;
}
int rv = 0;
DWORD delRoomMemberCall1 = g_WeChatWinDllAddr + g_WxCalls.drm.call1;
DWORD delRoomMemberCall2 = g_WeChatWinDllAddr + g_WxCalls.drm.call2;
DWORD delRoomMemberCall3 = g_WeChatWinDllAddr + g_WxCalls.drm.call3;
DWORD temp = 0;
WxString_t txtRoomid = { 0 };
wstring wsRoomid = String2Wstring(roomid);
txtRoomid.text = (wchar_t *)wsRoomid.c_str();
txtRoomid.size = wsRoomid.size();
txtRoomid.capacity = wsRoomid.capacity();
vector<wstring> vMembers;
vector<WxString_t> vTxtMembers;
wstringstream wss(String2Wstring(wxids));
while (wss.good()) {
wstring wstr;
getline(wss, wstr, L',');
vMembers.push_back(wstr);
WxString_t txtMember = { 0 };
txtMember.text = (wchar_t *)vMembers.back().c_str();
txtMember.size = vMembers.back().size();
txtMember.capacity = vMembers.back().capacity();
vTxtMembers.push_back(txtMember);
}
LOG_DEBUG("Adding {} members[{}] to {}", vTxtMembers.size(), wxids.c_str(), roomid.c_str());
__asm {
pushad;
pushfd;
call delRoomMemberCall1;
sub esp, 0x14;
mov esi, eax;
mov ecx, esp;
lea edi, txtRoomid;
push edi;
call delRoomMemberCall2;
mov ecx, esi;
lea eax, vTxtMembers;
push eax;
call delRoomMemberCall3;
mov rv, eax;
popfd;
popad;
}
return rv;
}

View File

@ -1,6 +1,6 @@
#pragma once
#include <string>
#include <vector>
int AddChatroomMember(std::string roomid, std::string wxids);
int DelChatroomMember(std::string roomid, std::string wxids);

149
spy/contact_mgmt.cpp Normal file
View File

@ -0,0 +1,149 @@
#pragma execution_character_set("utf-8")
#include "contact_mgmt.h"
#include "load_calls.h"
#include "log.h"
#include "util.h"
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
#define FEAT_LEN 5
static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 };
static const uint8_t FEAT_PROVINCE[FEAT_LEN] = { 0xE2, 0xEA, 0xA8, 0xD1, 0x18 };
static const uint8_t FEAT_CITY[FEAT_LEN] = { 0x1D, 0x02, 0x5B, 0xBF, 0x18 };
static DWORD FindMem(DWORD start, DWORD end, const void *target, size_t len)
{
uint8_t *p = (uint8_t *)start;
while ((DWORD)p < end) {
if (memcmp((void *)p, target, len) == 0) {
return (DWORD)p;
}
p++;
}
return 0;
}
static string GetCntString(DWORD start, DWORD end, const uint8_t *feat, size_t len)
{
DWORD pfeat = FindMem(start, end, feat, len);
if (pfeat == 0) {
return "";
}
DWORD lfeat = GET_DWORD(pfeat + len);
if (lfeat <= 2) {
return "";
}
return Wstring2String(wstring(GET_WSTRING_FROM_P(pfeat + FEAT_LEN + 4), lfeat));
}
vector<RpcContact_t> GetContacts()
{
vector<RpcContact_t> contacts;
DWORD call1 = g_WeChatWinDllAddr + g_WxCalls.contact.base;
DWORD call2 = g_WeChatWinDllAddr + g_WxCalls.contact.head;
int success = 0;
DWORD *addr[3] = { 0, 0, 0 };
__asm {
pushad
call call1
lea ecx,addr
push ecx
mov ecx,eax
call call2
mov success,eax
popad
}
DWORD pstart = (DWORD)addr[0];
DWORD pend = (DWORD)addr[2];
while (pstart < pend) {
RpcContact_t cnt;
DWORD pbin = GET_DWORD(pstart + 0x150);
DWORD lenbin = GET_DWORD(pstart + 0x154);
cnt.wxid = GetStringByAddress(pstart + g_WxCalls.contact.wxId);
cnt.code = GetStringByAddress(pstart + g_WxCalls.contact.wxCode);
cnt.remark = GetStringByAddress(pstart + g_WxCalls.contact.wxRemark);
cnt.name = GetStringByAddress(pstart + g_WxCalls.contact.wxName);
cnt.country = GetCntString(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN);
cnt.province = GetCntString(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN);
cnt.city = GetCntString(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN);
if (pbin == 0) {
cnt.gender = 0;
} else {
cnt.gender = (DWORD) * (uint8_t *)(pbin + g_WxCalls.contact.wxGender);
}
contacts.push_back(cnt);
pstart += 0x438;
}
return contacts;
}
int AcceptNewFriend(std::string v3, std::string v4, int scene)
{
int success = 0;
DWORD acceptNewFriendCall1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1;
DWORD acceptNewFriendCall2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2;
DWORD acceptNewFriendCall3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3;
DWORD acceptNewFriendCall4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4;
char buffer[0x40] = { 0 };
char nullbuffer[0x3CC] = { 0 };
LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene);
WxString_t wxV3 = { 0 };
WxString_t wxV4 = { 0 };
std::wstring wsV3 = String2Wstring(v3);
std::wstring wsV4 = String2Wstring(v4);
wxV3.text = (wchar_t *)wsV3.c_str();
wxV3.size = wsV3.size();
wxV3.capacity = wsV3.capacity();
wxV4.text = (wchar_t *)wsV4.c_str();
wxV4.size = wsV4.size();
wxV4.capacity = wsV4.capacity();
__asm {
pushad;
pushfd;
lea ecx, buffer;
call acceptNewFriendCall1;
mov esi, 0x0;
mov edi, scene;
push esi;
push edi;
sub esp, 0x14;
mov ecx, esp;
lea eax, wxV4;
push eax;
call acceptNewFriendCall2;
sub esp, 0x8;
push 0x0;
lea eax, nullbuffer;
push eax;
lea eax, wxV3;
push eax;
lea ecx, buffer;
call acceptNewFriendCall3;
mov success, eax;
lea ecx, buffer;
call acceptNewFriendCall4;
popfd;
popad;
}
return success; // 成功返回 1
}

View File

@ -1,5 +1,9 @@
#pragma once
#include "string"
#include <vector>
#include "pb_types.h"
vector<RpcContact_t> GetContacts();
int AcceptNewFriend(std::string v3, std::string v4, int scene);

View File

@ -1,3 +1,5 @@
#pragma warning( disable: 4244 )
#include <fstream>
#include "decrypt_image.h"

View File

@ -2,90 +2,45 @@
#include "exec_sql.h"
#include "load_calls.h"
#include "sqlite3.h"
#include "util.h"
#define SQLITE_OK 0 /* Successful result */
#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 */
#define OFFSET_DB_INSTANCE 0x2FFDDC8
#define OFFSET_DB_MICROMSG 0x68
#define OFFSET_DB_CHAT_MSG 0x1C0
#define OFFSET_DB_MISC 0x3D8
#define OFFSET_DB_EMOTION 0x558
#define OFFSET_DB_MEDIA 0x9B8
#define OFFSET_DB_BIZCHAT_MSG 0x1120
#define OFFSET_DB_FUNCTION_MSG 0x11B0
#define OFFSET_DB_NAME 0x14
#define SQLITE_INTEGER 1
#define SQLITE_FLOAT 2
#define SQLITE_TEXT 3
#define SQLITE_BLOB 4
#define SQLITE_NULL 5
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
typedef map<string, DWORD> dbMap_t;
static dbMap_t dbMap;
// 回调函数指针
typedef int (*sqlite3_callback)(void *, int, char **, char **);
// sqlite3_exec函数指针
typedef int(__cdecl *Sqlite3_exec)(DWORD, /* The database on which the SQL executes */
const char *, /* The SQL to be executed */
sqlite3_callback, /* Invoke this callback routine */
void *, /* First argument to xCallback() */
char ** /* Write error messages here */
);
typedef int(__cdecl *Sqlite3_prepare)(DWORD, const char *, int, DWORD **, int);
typedef int(__cdecl *Sqlite3_step)(DWORD *);
typedef int(__cdecl *Sqlite3_column_count)(DWORD *);
typedef const char *(__cdecl *Sqlite3_column_name)(DWORD *, int);
typedef int(__cdecl *Sqlite3_column_type)(DWORD *, int);
typedef const void *(__cdecl *Sqlite3_column_blob)(DWORD *, int);
typedef int(__cdecl *Sqlite3_column_bytes)(DWORD *, int);
typedef int(__cdecl *Sqlite3_finalize)(DWORD *);
static void GetDbHandle(DWORD base, DWORD offset)
{
wchar_t *wsp;
wsp = (wchar_t *)(*(DWORD *)(base + offset + OFFSET_DB_NAME));
string dbname = Wstring2String(wstring(wsp));
dbMap[dbname] = *(DWORD *)(base + offset);
}
dbMap_t GetDbHandles()
{
if (!dbMap.empty())
return dbMap;
dbMap.clear();
g_WeChatWinDllAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll");
DWORD sqlHandleBaseAddr = *(DWORD *)(g_WeChatWinDllAddr + g_WxCalls.sql.base);
DWORD sqlHandleBeginAddr = *(DWORD *)(sqlHandleBaseAddr + g_WxCalls.sql.start);
DWORD sqlHandleEndAddr = *(DWORD *)(sqlHandleBaseAddr + g_WxCalls.sql.end);
while (sqlHandleBeginAddr < sqlHandleEndAddr) {
DWORD dwHandle = *(DWORD *)sqlHandleBeginAddr;
string dbName = Wstring2String(wstring((wchar_t *)(*(DWORD *)(dwHandle + g_WxCalls.sql.name))));
DWORD handle = *(DWORD *)(dwHandle + g_WxCalls.sql.slot);
if (handle) {
dbMap[dbName] = handle;
}
DWORD dbInstanceAddr = *(DWORD *)(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE);
GetDbHandle(dbInstanceAddr, OFFSET_DB_MICROMSG); // MicroMsg.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_CHAT_MSG); // ChatMsg.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_MISC); // Misc.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_EMOTION); // Emotion.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_MEDIA); // Media.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_FUNCTION_MSG); // Function.db
sqlHandleBeginAddr += 0x04;
}
return dbMap;
}
@ -133,9 +88,9 @@ DbTables_t GetDbTables(const string db)
}
const char *sql = "select name, sql from sqlite_master where type=\"table\";";
Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(g_WeChatWinDllAddr + g_WxCalls.sql.exec);
Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET);
p_Sqlite3_exec(it->second, sql, (sqlite3_callback)cbGetTables, (void *)&tables, 0);
p_Sqlite3_exec(it->second, sql, (Sqlite3_callback)cbGetTables, (void *)&tables, 0);
return tables;
}
@ -143,14 +98,14 @@ DbTables_t GetDbTables(const string db)
DbRows_t ExecDbQuery(const string db, const string sql)
{
DbRows_t rows;
Sqlite3_prepare func_prepare = (Sqlite3_prepare)(g_WeChatWinDllAddr + 0x14227F0);
Sqlite3_step func_step = (Sqlite3_step)(g_WeChatWinDllAddr + 0x13EA780);
Sqlite3_column_count func_column_count = (Sqlite3_column_count)(g_WeChatWinDllAddr + 0x13EACD0);
Sqlite3_column_name func_column_name = (Sqlite3_column_name)(g_WeChatWinDllAddr + 0x13EB630);
Sqlite3_column_type func_column_type = (Sqlite3_column_type)(g_WeChatWinDllAddr + 0x13EB470);
Sqlite3_column_blob func_column_blob = (Sqlite3_column_blob)(g_WeChatWinDllAddr + 0x13EAD10);
Sqlite3_column_bytes func_column_bytes = (Sqlite3_column_bytes)(g_WeChatWinDllAddr + 0x13EADD0);
Sqlite3_finalize func_finalize = (Sqlite3_finalize)(g_WeChatWinDllAddr + 0x13E9730);
Sqlite3_prepare func_prepare = (Sqlite3_prepare)(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET);
Sqlite3_step func_step = (Sqlite3_step)(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET);
Sqlite3_column_count func_column_count = (Sqlite3_column_count)(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET);
Sqlite3_column_name func_column_name = (Sqlite3_column_name)(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET);
Sqlite3_column_type func_column_type = (Sqlite3_column_type)(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET);
Sqlite3_column_blob func_column_blob = (Sqlite3_column_blob)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET);
Sqlite3_column_bytes func_column_bytes = (Sqlite3_column_bytes)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET);
Sqlite3_finalize func_finalize = (Sqlite3_finalize)(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET);
if (dbMap.empty()) {
dbMap = GetDbHandles();

View File

@ -1,33 +0,0 @@
#pragma execution_character_set("utf-8")
#include "get_contacts.h"
#include "load_calls.h"
#include "util.h"
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
vector<RpcContact_t> GetContacts()
{
vector<RpcContact_t> contacts;
DWORD baseAddr = g_WeChatWinDllAddr + g_WxCalls.contact.base;
DWORD tempAddr = GET_DWORD(baseAddr);
DWORD head = GET_DWORD(tempAddr + g_WxCalls.contact.head);
DWORD node = GET_DWORD(head);
while (node != head) {
RpcContact_t cnt;
cnt.wxid = GetStringByAddress(node + g_WxCalls.contact.wxId);
cnt.code = GetStringByAddress(node + g_WxCalls.contact.wxCode);
cnt.remark = GetStringByAddress(node + g_WxCalls.contact.wxRemark);
cnt.name = GetStringByAddress(node + g_WxCalls.contact.wxName);
cnt.country = GetStringByAddress(node + g_WxCalls.contact.wxCountry);
cnt.province = GetStringByAddress(node + g_WxCalls.contact.wxProvince);
cnt.city = GetStringByAddress(node + g_WxCalls.contact.wxCity);
cnt.gender = GET_DWORD(node + g_WxCalls.contact.wxGender);
contacts.push_back(cnt);
node = GET_DWORD(node);
}
return contacts;
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <vector>
#include "pb_types.h"
vector<RpcContact_t> GetContacts();

View File

@ -3,27 +3,28 @@
#include "load_calls.h"
#define SUPPORT_VERSION L"3.7.0.30"
#define SUPPORT_VERSION L"3.9.2.23"
WxCalls_t wxCalls = {
0x2366538, // Login Status
{ 0x236607C, 0x23660F4, 0x2366128, 0x2386F7C }, // User Info: wxid, nickname, mobile, home
0x521D30, // Send Message
0x2FFD638, // Login Status
{ 0x2FFD4E8, 0x2FFD590, 0x2FFD500, 0x30238CC }, // User Info: wxid, nickname, mobile, home
{ 0x768140, 0xCE6C80, 0x756960 }, // Send Message
/* Receive Message:
Hook, call, type, self, id, msgXml, roomId, wxId, content, thumb, extra */
{ 0x550F4C, 0xA96350, 0x38, 0x3C, 0x184, 0x1EC, 0x48, 0x170, 0x70, 0x198, 0x1AC },
{ 0xBD780, 0x771980, 0x521640 }, // Send Image Message
{ 0xC3B70, 0x771980, 0x3ED8C0 }, // Send File Message
{ 0xD19A0B, 0x756960, 0x38, 0x3C, 0x194, 0x1FC, 0x48, 0x180, 0x70, 0x1A8, 0x1BC },
{ 0x768140, 0XF59E40, 0XCE6640, 0x756960 }, // Send Image Message
{ 0x76AE20, 0xF59E40, 0xB6D1F0, 0x756960 }, // Send File Message
{ 0xB8A70, 0x3ED5E0, 0x107F00, 0x3ED7B0, 0x2386FE4 }, // Send xml Message
{ 0x771980, 0x4777E0, 0x239E888 }, // Send Emotion Message
/* Get Contacts:
Base, head, wxId, Code, Remark,Name, Gender, Country, Province, City*/
{ 0x23668F4, 0x4C, 0x30, 0x44, 0x78, 0x8C, 0x184, 0x1D0, 0x1E4, 0x1F8 },
call1, call2, wxId, Code, Remark,Name, Gender, Country, Province, City*/
{ 0x75A4A0, 0xC089F0, 0x10, 0x24, 0x58, 0x6C, 0x0E, 0x00, 0x00, 0x00 },
/* Exec Sql:
Exec, base, start, end, slot, name*/
{ 0x141BDF0, 0x2366934, 0x1428, 0x142C, 0x3C, 0x50 },
{ 0x771980, 0x2AE8D0, 0x1EE40E0 }, // Accept New Friend application
{ 0xE29F0, 0x771980, 0x43D8D0 }, // Add chatroom members
{ 0x771980, 0xCD2A90 } // Receive transfer
{ 0xA17D50, 0xF59E40, 0xA18BD0, 0xA17E70 }, // Accept New Friend application
{ 0x78CF20, 0xF59E40, 0xBD1DC0 }, // Add chatroom members
{ 0x78CF20, 0xF59E40, 0xBD22A0 }, // Delete chatroom members
{ 0x7B2E60, 0x15E2C20, 0x79C250 } // Receive transfer
};
int LoadCalls(const wchar_t *version, WxCalls_t *calls)

View File

@ -1,27 +1,34 @@
#include <filesystem>
#include "log.h"
#include "util.h"
#define LOGGER_NAME "WCF"
#define LOGGER_FILE_NAME "logs/wcf.txt"
#define LOGGER_FILE_NAME "/logs/wcf.txt"
#define LOGGER_MAX_SIZE 1024 * 1024 * 10 // 10M
#define LOGGER_MAX_FILES 10 // 10 files
void InitLogger()
void InitLogger(std::string path)
{
static std::shared_ptr<spdlog::logger> gLogger = nullptr;
if (gLogger != nullptr) {
static std::shared_ptr<spdlog::logger> logger = nullptr;
if (logger != nullptr) {
return;
}
gLogger = spdlog::rotating_logger_mt(LOGGER_NAME, LOGGER_FILE_NAME, LOGGER_MAX_SIZE, LOGGER_MAX_FILES);
// gLogger = spdlog::stdout_color_mt("console");
auto filename = std::filesystem::path(path + LOGGER_FILE_NAME).make_preferred().string();
try {
logger = spdlog::rotating_logger_mt(LOGGER_NAME, filename, LOGGER_MAX_SIZE, LOGGER_MAX_FILES);
} catch (const spdlog::spdlog_ex &ex) {
MessageBox(NULL, String2Wstring(ex.what()).c_str(), L"Init LOGGER ERROR", 0);
}
spdlog::set_default_logger(gLogger);
gLogger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%s::%#::%!] %v");
spdlog::set_default_logger(logger);
logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%s::%#::%!] %v");
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
spdlog::set_level(spdlog::level::debug);
gLogger->flush_on(spdlog::level::debug);
logger->flush_on(spdlog::level::debug);
#else
gLogger->flush_on(spdlog::level::info);
logger->flush_on(spdlog::level::info);
#endif
LOG_DEBUG("InitLogger with debug level");
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <string>
#ifdef ENABLE_DEBUG_LOG
#include <stdint.h>
@ -19,4 +21,4 @@ void log_buffer(uint8_t *buffer, size_t len);
#define LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__);
#define LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__);
void InitLogger();
void InitLogger(std::string path);

View File

@ -28,24 +28,40 @@ static CHAR recvMsgBackupCode[5] = { 0 };
MsgTypes_t GetMsgTypes()
{
const MsgTypes_t m = { { 0x01, "文字" },
{ 0x03, "图片" },
{ 0x22, "语音" },
{ 0x25, "好友确认" },
{ 0x28, "POSSIBLEFRIEND_MSG" },
{ 0x2A, "名片" },
{ 0x2B, "视频" },
{ 0x2F, "石头剪刀布 | 表情图片" },
{ 0x30, "位置" },
{ 0x31, "共享实时位置、文件、转账、链接" },
{ 0x32, "VOIPMSG" },
{ 0x33, "微信初始化" },
{ 0x34, "VOIPNOTIFY" },
{ 0x35, "VOIPINVITE" },
{ 0x3E, "小视频" },
{ 0x270F, "SYSNOTICE" },
{ 0x2710, "红包、系统消息" },
{ 0x2712, "撤回消息" } };
const MsgTypes_t m = {
{ 0x01, "文字" },
{ 0x03, "图片" },
{ 0x22, "语音" },
{ 0x25, "好友确认" },
{ 0x28, "POSSIBLEFRIEND_MSG" },
{ 0x2A, "名片" },
{ 0x2B, "视频" },
{ 0x2F, "石头剪刀布 | 表情图片" },
{ 0x30, "位置" },
{ 0x31, "共享实时位置、文件、转账、链接" },
{ 0x32, "VOIPMSG" },
{ 0x33, "微信初始化" },
{ 0x34, "VOIPNOTIFY" },
{ 0x35, "VOIPINVITE" },
{ 0x3E, "小视频" },
{ 0x42, "微信红包" },
{ 0x270F, "SYSNOTICE" },
{ 0x2710, "红包、系统消息" },
{ 0x2712, "撤回消息" },
{ 0x100031, "搜狗表情" },
{ 0x1000031, "链接" },
{ 0x1A000031, "微信红包" },
{ 0x20010031, "红包封面" },
{ 0x2D000031, "视频号视频" },
{ 0x2E000031, "视频号名片" },
{ 0x31000031, "引用消息" },
{ 0x37000031, "拍一拍" },
{ 0x3A000031, "视频号直播" },
{ 0x3A100031, "商品链接" },
{ 0x3A200031, "视频号直播" },
{ 0x3E000031, "音乐链接" },
{ 0x41000031, "文件" },
};
return m;
}
@ -73,21 +89,20 @@ void UnHookAddress(DWORD hookAddr, CHAR restoreCode[5])
void DispatchMsg(DWORD reg)
{
WxMsg_t wxMsg;
DWORD *p = (DWORD *)reg; // 消息结构基址
wxMsg.type = GET_DWORD(*p + g_WxCalls.recvMsg.type);
wxMsg.is_self = GET_DWORD(*p + g_WxCalls.recvMsg.isSelf);
wxMsg.id = GetStringByAddress(*p + g_WxCalls.recvMsg.msgId);
wxMsg.xml = GetStringByAddress(*p + g_WxCalls.recvMsg.msgXml);
wxMsg.type = GET_DWORD(reg + g_WxCalls.recvMsg.type);
wxMsg.is_self = GET_DWORD(reg + g_WxCalls.recvMsg.isSelf);
wxMsg.id = GetStringByStrAddr(reg + g_WxCalls.recvMsg.msgId);
wxMsg.xml = GetStringByStrAddr(reg + g_WxCalls.recvMsg.msgXml);
string roomid = GetStringByAddress(*p + g_WxCalls.recvMsg.roomId);
string roomid = GetStringByWstrAddr(reg + g_WxCalls.recvMsg.roomId);
if (roomid.find("@chatroom") != string::npos) { // 群 ID 的格式为 xxxxxxxxxxx@chatroom
wxMsg.is_group = true;
wxMsg.roomid = roomid;
if (wxMsg.is_self) {
wxMsg.sender = GetSelfWxid();
} else {
wxMsg.sender = GetStringByAddress(*p + g_WxCalls.recvMsg.wxId);
wxMsg.sender = GetStringByStrAddr(reg + g_WxCalls.recvMsg.wxId);
}
} else {
wxMsg.is_group = false;
@ -98,15 +113,16 @@ void DispatchMsg(DWORD reg)
}
}
wxMsg.content = GetStringByAddress(*p + g_WxCalls.recvMsg.content);
wxMsg.thumb = GetStringByAddress(*p + g_WxCalls.recvMsg.thumb);
wxMsg.content = GetStringByWstrAddr(reg + g_WxCalls.recvMsg.content);
wxMsg.thumb = GetStringByStrAddr(reg + g_WxCalls.recvMsg.thumb);
if (!wxMsg.thumb.empty()) {
wxMsg.thumb = GetHomePath() + "\\WeChat Files\\" + wxMsg.thumb;
wxMsg.thumb = GetHomePath() + wxMsg.thumb;
}
wxMsg.extra = GetStringByAddress(*p + g_WxCalls.recvMsg.extra);
wxMsg.extra = GetStringByStrAddr(reg + g_WxCalls.recvMsg.extra);
if (!wxMsg.extra.empty()) {
wxMsg.extra = GetHomePath() + "\\WeChat Files\\" + wxMsg.extra;
wxMsg.extra = GetHomePath() + wxMsg.extra;
}
{
@ -120,13 +136,13 @@ void DispatchMsg(DWORD reg)
__declspec(naked) void RecieveMsgFunc()
{
__asm {
mov reg_buffer, edi // 把值复制出来
}
DispatchMsg(reg_buffer);
__asm
{
pushad
pushfd
push ecx
call DispatchMsg
add esp, 0x4
popfd
popad
call recvMsgCallAddr // 这个为被覆盖的call
jmp recvMsgJumpBackAddr // 跳回被HOOK指令的下一条指令
}
@ -134,6 +150,7 @@ __declspec(naked) void RecieveMsgFunc()
void ListenMessage()
{
// DbgMsg("ListenMessage");
// OutputDebugString(L"ListenMessage\n");
// MessageBox(NULL, L"ListenMessage", L"ListenMessage", 0);
if (gIsListening || (g_WeChatWinDllAddr == 0)) {

View File

@ -1,4 +1,4 @@
#include "receive_transfer.h"
#include "receive_transfer.h"
#include "load_calls.h"
#include "log.h"
#include "util.h"
@ -8,31 +8,57 @@ using namespace std;
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
int ReceiveTransfer(string wxid, string transferid)
int ReceiveTransfer(string wxid, string transferid, string transactionid)
{
int rv = 0;
DWORD recvTransferCall = g_WeChatWinDllAddr + g_WxCalls.tf.call1;
DWORD recvTransferCall1 = g_WeChatWinDllAddr + g_WxCalls.tf.call1;
DWORD recvTransferCall2 = g_WeChatWinDllAddr + g_WxCalls.tf.call2;
DWORD recvTransferCall3 = g_WeChatWinDllAddr + g_WxCalls.tf.call3;
wstring wsWxid = String2Wstring(wxid);
wstring wsTid = String2Wstring(transferid);
char payInfo[0x134] = { 0 };
wstring wsWxid = String2Wstring(wxid);
WxString_t wxWxid = { 0 };
wxWxid.text = (wchar_t *)wsWxid.c_str();
wxWxid.size = wsWxid.size();
wxWxid.capacity = wsWxid.capacity();
LOG_DEBUG("Receiving transfer, from: {}, transferid: {}", wxid, transferid);
wstring wsTfid = String2Wstring(transferid);
WxString_t wxTfid = { 0 };
wxTfid.text = (wchar_t *)wsTfid.c_str();
wxTfid.size = wsTfid.size();
wxTfid.capacity = wsTfid.capacity();
wstring wsTaid = String2Wstring(transactionid);
WxString_t wxTaid = { 0 };
wxTaid.text = (wchar_t *)wsTaid.c_str();
wxTaid.size = wsTaid.size();
wxTaid.capacity = wsTaid.capacity();
LOG_DEBUG("Receiving transfer, from: {}, transferid: {}, transactionid: {}", wxid, transferid, transactionid);
__asm {
pushad
sub esp, 0x30
mov ecx, esp
lea eax, wsTid
push eax
call recvTransferCall
lea ecx, dword ptr ds:[esp+0x14]
lea eax, wsWxid
push eax
call recvTransferCall
call recvTransferCall2
add esp, 0x30
mov rv, eax
popad
pushad;
lea ecx, payInfo;
call recvTransferCall1;
mov dword ptr[payInfo + 0x4], 0x1;
mov dword ptr[payInfo + 0x4C], 0x1;
popad;
}
memcpy(&payInfo[0x1C], &wxTaid, sizeof(wxTaid));
memcpy(&payInfo[0x38], &wxTfid, sizeof(wxTfid));
__asm {
pushad;
push 0x1;
sub esp, 0x8;
lea edx, wxWxid;
lea ecx, payInfo;
call recvTransferCall2;
mov rv, eax;
add esp, 0xC;
push 0x0;
lea ecx, payInfo;
call recvTransferCall3;
popad;
}
return rv;

View File

@ -2,4 +2,4 @@
#include <string>
int ReceiveTransfer(std::string wxid, std::string transferid);
int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid);

View File

@ -16,11 +16,10 @@
#include "wcf.pb.h"
#include "accept_new_friend.h"
#include "add_chatroom_member.h"
#include "chatroom_mgmt.h"
#include "contact_mgmt.h"
#include "decrypt_image.h"
#include "exec_sql.h"
#include "get_contacts.h"
#include "log.h"
#include "pb_types.h"
#include "pb_util.h"
@ -465,36 +464,14 @@ bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, size_t
return true;
}
bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
rsp.func = Functions_FUNC_ADD_ROOM_MEMBERS;
rsp.which_msg = Response_status_tag;
rsp.msg.status = 0;
rsp.msg.status = AddChatroomMember(roomid, wxids);
if (rsp.msg.status != 1) {
LOG_ERROR("AddChatroomMember failed: {}", rsp.msg.status);
}
pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
if (!pb_encode(&stream, Response_fields, &rsp)) {
LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
return false;
}
*len = stream.bytes_written;
return true;
}
bool func_receive_transfer(char *wxid, char *transferid, uint8_t *out, size_t *len)
bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
rsp.func = Functions_FUNC_RECV_TRANSFER;
rsp.which_msg = Response_status_tag;
rsp.msg.status = 0;
rsp.msg.status = ReceiveTransfer(wxid, transferid);
rsp.msg.status = ReceiveTransfer(wxid, tfid, taid);
if (rsp.msg.status != 1) {
LOG_ERROR("AddChatroomMember failed: {}", rsp.msg.status);
}
@ -531,6 +508,50 @@ bool func_decrypt_image(char *src, char *dst, uint8_t *out, size_t *len)
return true;
}
bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
rsp.func = Functions_FUNC_ADD_ROOM_MEMBERS;
rsp.which_msg = Response_status_tag;
rsp.msg.status = 0;
rsp.msg.status = AddChatroomMember(roomid, wxids);
if (rsp.msg.status != 1) {
LOG_ERROR("AddChatroomMember failed: {}", rsp.msg.status);
}
pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
if (!pb_encode(&stream, Response_fields, &rsp)) {
LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
return false;
}
*len = stream.bytes_written;
return true;
}
bool func_del_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
rsp.func = Functions_FUNC_DEL_ROOM_MEMBERS;
rsp.which_msg = Response_status_tag;
rsp.msg.status = 0;
rsp.msg.status = DelChatroomMember(roomid, wxids);
if (rsp.msg.status != 1) {
LOG_ERROR("DelChatroomMember failed: {}", rsp.msg.status);
}
pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
if (!pb_encode(&stream, Response_fields, &rsp)) {
LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
return false;
}
*len = stream.bytes_written;
return true;
}
static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len)
{
bool ret = false;
@ -594,6 +615,7 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_send_file(req.msg.file.path, req.msg.file.receiver, out, out_len);
break;
}
#if 0
case Functions_FUNC_SEND_XML: {
LOG_DEBUG("[Functions_FUNC_SEND_XML]");
ret = func_send_xml(req.msg.xml, out, out_len);
@ -604,6 +626,7 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_send_emotion(req.msg.file.path, req.msg.file.receiver, out, out_len);
break;
}
#endif
case Functions_FUNC_ENABLE_RECV_TXT: {
LOG_DEBUG("[Functions_FUNC_ENABLE_RECV_TXT]");
ret = func_enable_recv_txt(out, out_len);
@ -624,14 +647,9 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_accept_friend(req.msg.v.v3, req.msg.v.v4, req.msg.v.scene, out, out_len);
break;
}
case Functions_FUNC_ADD_ROOM_MEMBERS: {
LOG_DEBUG("[Functions_FUNC_ADD_ROOM_MEMBERS]");
ret = func_add_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len);
break;
}
case Functions_FUNC_RECV_TRANSFER: {
LOG_DEBUG("[Functions_FUNC_RECV_TRANSFER]");
ret = func_receive_transfer(req.msg.tf.wxid, req.msg.tf.tid, out, out_len);
ret = func_receive_transfer(req.msg.tf.wxid, req.msg.tf.tfid, req.msg.tf.taid, out, out_len);
break;
}
case Functions_FUNC_DECRYPT_IMAGE: {
@ -639,6 +657,16 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_decrypt_image(req.msg.dec.src, req.msg.dec.dst, out, out_len);
break;
}
case Functions_FUNC_ADD_ROOM_MEMBERS: {
LOG_DEBUG("[Functions_FUNC_ADD_ROOM_MEMBERS]");
ret = func_add_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len);
break;
}
case Functions_FUNC_DEL_ROOM_MEMBERS: {
LOG_DEBUG("[Functions_FUNC_DEL_ROOM_MEMBERS]");
ret = func_del_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len);
break;
}
default: {
LOG_ERROR("[UNKNOW FUNCTION]");
break;

View File

@ -13,12 +13,15 @@ extern string GetSelfWxid(); // Defined in spy.cpp
void SendTextMessage(string wxid, string msg, string atWxids)
{
char buffer[0x3B0] = { 0 };
int success = 0;
char buffer[0x2D8] = { 0 };
WxString_t wxMsg = { 0 };
WxString_t wxWxid = { 0 };
// 发送消息Call地址 = 微信基址 + 偏移
DWORD sendCallAddress = g_WeChatWinDllAddr + g_WxCalls.sendTextMsg;
DWORD sendCall1 = g_WeChatWinDllAddr + g_WxCalls.sendText.call1;
DWORD sendCall2 = g_WeChatWinDllAddr + g_WxCalls.sendText.call2;
DWORD sendCall3 = g_WeChatWinDllAddr + g_WxCalls.sendText.call3;
wstring wsWxid = String2Wstring(wxid);
wstring wsMsg = String2Wstring(msg);
@ -49,15 +52,24 @@ void SendTextMessage(string wxid, string msg, string atWxids)
__asm
{
pushad;
call sendCall1;
push 0x0;
push 0x0;
push 0x0;
push 0x1;
lea eax, vTxtAtWxids;
push 0x01;
push eax;
lea edi, wxMsg;
push edi;
lea eax, wxMsg;
push eax;
lea edx, wxWxid;
lea ecx, buffer;
call sendCallAddress;
add esp, 0xC;
call sendCall2;
mov success, eax;
add esp, 0x18;
lea ecx, buffer;
call sendCall3;
popad;
}
}
@ -66,11 +78,12 @@ void SendImageMessage(string wxid, string path)
if (g_WeChatWinDllAddr == 0) {
return;
}
DWORD tmpEAX = 0;
char buf1[0x48] = { 0 };
char buf2[0x3B0] = { 0 };
WxString_t imgWxid = { 0 };
WxString_t imgPath = { 0 };
int success = 0;
DWORD tmpEAX = 0;
char buf[0x2D8] = { 0 };
WxString_t imgWxid = { 0 };
WxString_t imgPath = { 0 };
WxString_t nullbuffer = { 0 };
wstring wsWxid = String2Wstring(wxid);
wstring wspath = String2Wstring(path);
@ -87,25 +100,29 @@ void SendImageMessage(string wxid, string path)
DWORD sendCall1 = g_WeChatWinDllAddr + g_WxCalls.sendImg.call1;
DWORD sendCall2 = g_WeChatWinDllAddr + g_WxCalls.sendImg.call2;
DWORD sendCall3 = g_WeChatWinDllAddr + g_WxCalls.sendImg.call3;
DWORD sendCall4 = g_WeChatWinDllAddr + g_WxCalls.sendImg.call4;
__asm {
pushad
call sendCall1
sub esp, 0x14
mov tmpEAX, eax
lea eax, buf1
mov ecx, esp
lea edi, imgPath
push eax
call sendCall2
mov ecx, dword ptr[tmpEAX]
lea eax, imgWxid
push edi
push eax
lea eax, buf2
push eax
call sendCall3
popad
pushad;
call sendCall1;
sub esp,0x14;
mov tmpEAX,eax;
lea eax,nullbuffer;
mov ecx,esp;
lea edi,imgPath;
push eax;
call sendCall2;
mov ecx,dword ptr [tmpEAX];
lea eax,imgWxid;
push edi;
push eax;
lea eax,buf;
push eax;
call sendCall3;
mov success,eax;
lea ecx,buf;
call sendCall4;
popad;
}
}
@ -114,8 +131,9 @@ void SendFileMessage(string wxid, string path)
if (g_WeChatWinDllAddr == 0) {
return;
}
int success = 0;
DWORD tmpEAX = 0;
char buffer[0x3B0] = { 0 };
char buffer[0x2D8] = { 0 };
WxString_t fileWxid = { 0 };
WxString_t filePath = { 0 };
WxString_t nullbuffer = { 0 };
@ -135,46 +153,49 @@ void SendFileMessage(string wxid, string path)
DWORD sendCall1 = g_WeChatWinDllAddr + g_WxCalls.sendFile.call1;
DWORD sendCall2 = g_WeChatWinDllAddr + g_WxCalls.sendFile.call2;
DWORD sendCall3 = g_WeChatWinDllAddr + g_WxCalls.sendFile.call3;
DWORD sendCall4 = g_WeChatWinDllAddr + g_WxCalls.sendFile.call4;
__asm {
pushad;
pushfd;
call sendCall1;
sub esp, 0x14;
mov tmpEAX, eax;
lea eax, nullbuffer;
mov ecx, esp;
push eax;
call sendCall2;
push 0x00DBE200;
sub esp, 0x14;
mov edi, esp;
mov dword ptr ds : [edi] , 0x0;
mov dword ptr ds : [edi + 0x4] , 0x0;
mov dword ptr ds : [edi + 0x8] , 0x0;
mov dword ptr ds : [edi + 0xC] , 0x0;
mov dword ptr ds : [edi + 0x10] , 0x0;
sub esp, 0x14;
lea eax, filePath;
mov ecx, esp;
push eax;
call sendCall2;
sub esp, 0x14;
lea eax, fileWxid;
mov ecx, esp;
push eax;
call sendCall2;
mov ecx, dword ptr [tmpEAX];
lea eax, buffer;
push eax;
call sendCall3;
mov al,byte ptr [eax + 0x38];
movzx eax,al;
popfd;
popad;
pushad;
pushfd;
call sendCall1;
sub esp, 0x14;
mov tmpEAX, eax;
lea eax, nullbuffer;
mov ecx, esp;
push eax;
call sendCall2;
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, filePath;
mov ecx, esp;
push eax;
call sendCall2;
sub esp, 0x14;
lea eax, fileWxid;
mov ecx, esp;
push eax;
call sendCall2;
mov ecx, dword ptr[tmpEAX];
lea eax, buffer;
push eax;
call sendCall3;
mov al, byte ptr[eax + 0x38];
movzx eax, al;
mov success, eax;
lea ecx, buffer;
call sendCall4;
popfd;
popad;
}
}
void SendXmlMessage(string receiver, string xml, string path, int type)
{
if (g_WeChatWinDllAddr == 0) {
@ -260,7 +281,7 @@ void SendEmotionMessage(string wxid, string path)
return;
}
char buffer[0x1C] = { 0 };
char buffer[0x1C] = { 0 };
WxString_t emoWxid = { 0 };
WxString_t emoPath = { 0 };
WxString_t nullbuffer = { 0 };

Binary file not shown.

View File

@ -1,16 +1,22 @@
#include "spy.h"
#include <filesystem>
#include "load_calls.h"
#include "log.h"
#include "rpc_server.h"
#include "spy.h"
#include "util.h"
WxCalls_t g_WxCalls = { 0 };
DWORD g_WeChatWinDllAddr = 0;
void InitSpy(int port)
void InitSpy(LPVOID args)
{
wchar_t version[16] = { 0 };
InitLogger();
PortPath_t *pp = (PortPath_t *)args;
int port = pp->port;
std::string path(pp->path);
InitLogger(path);
g_WeChatWinDllAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll"); // 获取wechatWin模块地址
if (g_WeChatWinDllAddr == 0) {
LOG_ERROR("获取wechatWin.dll模块地址失败");

View File

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 37,1,25,0
PRODUCTVERSION 3,7,0,30
FILEVERSION 39,0,0,0
PRODUCTVERSION 3,9,2,23
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "WeChatFerry"
VALUE "FileDescription", "WeChatFerry"
VALUE "FileVersion", "37.1.25.0"
VALUE "FileVersion", "39.0.0.0"
VALUE "InternalName", "spy.dll"
VALUE "LegalCopyright", "Copyright (C) 2023"
VALUE "OriginalFilename", "spy.dll"
VALUE "ProductName", "WeChatFerry"
VALUE "ProductVersion", "3.7.0.30"
VALUE "ProductVersion", "3.9.2.23"
END
END
BLOCK "VarFileInfo"

View File

@ -23,10 +23,17 @@ typedef struct RecvMsg {
DWORD extra; // 附加数据
} RecvMsg_t;
typedef struct SendText {
DWORD call1;
DWORD call2;
DWORD call3;
} SendText_t;
typedef struct Sendfile {
DWORD call1;
DWORD call2;
DWORD call3;
DWORD call4;
} Sendfile_t;
typedef struct Contact {
@ -54,7 +61,8 @@ typedef struct Sql {
typedef struct NewFriend {
DWORD call1;
DWORD call2;
DWORD handle;
DWORD call3;
DWORD call4;
} NewFriend_t;
typedef struct RoomMember {
@ -74,12 +82,13 @@ typedef struct Xml {
typedef struct TF {
DWORD call1;
DWORD call2;
DWORD call3;
} TF_t;
typedef struct WxCalls {
DWORD login; // 登录状态
UserInfoCall_t ui; // 用户信息
DWORD sendTextMsg; // 发送消息
SendText_t sendText; // 发送消息
RecvMsg_t recvMsg; // 接收消息
Sendfile_t sendImg; // 发送图片
Sendfile_t sendFile; // 发送文件
@ -89,6 +98,7 @@ typedef struct WxCalls {
Sql_t sql; // 执行 SQL
NewFriend_t anf; // 通过好友申请
RoomMember_t arm; // 添加群成员
RoomMember_t drm; // 删除群成员
TF_t tf; // 接收转账
} WxCalls_t;

192
spy/sqlite3.h Normal file
View File

@ -0,0 +1,192 @@
#pragma once
#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 0x1E24F70
#define SQLITE3_BACKUP_INIT_OFFSET 0x1DEA900
#define SQLITE3_PREPARE_OFFSET 0x1E2B8C0
#define SQLITE3_OPEN_OFFSET 0x1E598B0
#define SQLITE3_BACKUP_STEP_OFFSET 0x1DEAD00
#define SQLITE3_BACKUP_REMAINING_OFFSET 0x1DEB440
#define SQLITE3_BACKUP_PAGECOUNT_OFFSET 0x1DEB450
#define SQLITE3_BACKUP_FINISH_OFFSET 0x1DEB340
#define SQLITE3_SLEEP_OFFSET 0x1E5A0F0
#define SQLITE3_ERRCODE_OFFSET 0x1E58550
#define SQLITE3_CLOSE_OFFSET 0x1E56CD0
#define SQLITE3_STEP_OFFSET 0x1DF3770
#define SQLITE3_COLUMN_COUNT_OFFSET 0x1DF3C80
#define SQLITE3_COLUMN_NAME_OFFSET 0x1DF4570
#define SQLITE3_COLUMN_TYPE_OFFSET 0x1DF4410
#define SQLITE3_COLUMN_BLOB_OFFSET 0x1DF3CC0
#define SQLITE3_COLUMN_BYTES_OFFSET 0x1DF3DA0
#define SQLITE3_FINALIZE_OFFSET 0x1DF2740
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);

View File

@ -6,14 +6,23 @@
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
string GetHomePath() { return GET_STRING(g_WeChatWinDllAddr + g_WxCalls.ui.home); }
static char home[MAX_PATH] = { 0 };
string GetHomePath()
{
if (home[0] == 0) {
string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + g_WxCalls.ui.home)) + "\\WeChat Files\\";
strncpy_s(home, path.c_str(), path.size());
}
return string(home);
}
string GetSelfWxid()
{
DWORD wxidType = 0;
try {
wxidType = GET_DWORD(g_WeChatWinDllAddr + g_WxCalls.ui.wxid + 0x14);
LOG_DEBUG("WeChatWinDll: {:#x}, wxid type: {:#x}", g_WeChatWinDllAddr, wxidType);
if (wxidType == 0xF) {
return GET_STRING_FROM_P(g_WeChatWinDllAddr + g_WxCalls.ui.wxid);
} else {
@ -33,7 +42,7 @@ UserInfo_t GetUserInfo()
ui.wxid = GetSelfWxid();
ui.name = GET_STRING_FROM_P(g_WeChatWinDllAddr + g_WxCalls.ui.nickName);
ui.mobile = GET_STRING_FROM_P(g_WeChatWinDllAddr + g_WxCalls.ui.mobile);
ui.home = GET_STRING(g_WeChatWinDllAddr + g_WxCalls.ui.home);
ui.home = GetHomePath();
return ui;
}

View File

@ -5,6 +5,7 @@
#include <string.h>
#include <strsafe.h>
#include <tlhelp32.h>
#include <vector>
#include <wchar.h>
#include "util.h"
@ -212,6 +213,18 @@ string GetStringByAddress(DWORD address)
return Wstring2String(wstring(GET_WSTRING(address), strLength));
}
string GetStringByStrAddr(DWORD addr)
{
DWORD strLength = GET_DWORD(addr + 4);
return strLength ? string(GET_STRING(addr), strLength) : string();
}
string GetStringByWstrAddr(DWORD addr)
{
DWORD strLength = GET_DWORD(addr + 4);
return strLength ? Wstring2String(wstring(GET_WSTRING(addr), strLength)) : string();
}
DWORD GetMemoryIntByAddress(HANDLE hProcess, DWORD address)
{
DWORD value = 0;
@ -244,3 +257,27 @@ wstring GetUnicodeInfoByAddress(HANDLE hProcess, DWORD address)
return value;
}
void DbgMsg(const char *zcFormat, ...)
{
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
std::string strText(zc.data(), iLen);
OutputDebugStringA(strText.c_str());
}

View File

@ -8,10 +8,16 @@
#define WECHATINJECTDLL L"spy.dll"
#define WECHATINJECTDLL_DEBUG L"spy_debug.dll"
#define GET_DWORD(addr) ((DWORD) * (DWORD *)(addr))
#define GET_STRING(addr) ((CHAR *)(*(DWORD *)(addr)))
#define GET_WSTRING(addr) ((WCHAR *)(*(DWORD *)(addr)))
#define GET_STRING_FROM_P(addr) ((CHAR *)(addr))
#define GET_DWORD(addr) ((DWORD) * (DWORD *)(addr))
#define GET_STRING(addr) ((CHAR *)(*(DWORD *)(addr)))
#define GET_WSTRING(addr) ((WCHAR *)(*(DWORD *)(addr)))
#define GET_STRING_FROM_P(addr) ((CHAR *)(addr))
#define GET_WSTRING_FROM_P(addr) ((WCHAR *)(addr))
typedef struct PortPath {
int port;
char path[MAX_PATH];
} PortPath_t;
DWORD GetWeChatPid();
int OpenWeChat(DWORD *pid);
@ -22,3 +28,6 @@ std::wstring GetUnicodeInfoByAddress(HANDLE hProcess, DWORD address);
std::wstring String2Wstring(std::string s);
std::string Wstring2String(std::wstring ws);
std::string GetStringByAddress(DWORD address);
std::string GetStringByStrAddr(DWORD addr);
std::string GetStringByWstrAddr(DWORD addr);
void DbgMsg(const char *zcFormat, ...);

View File

@ -2,14 +2,11 @@
#include <stdlib.h>
#include <string.h>
#include "framework.h"
#include "log.h"
#include "sdk.h"
void help()
{
LOG_INFO("\nUsage: \n启动: wcf.exe start port [debug]\n关闭: wcf.exe stop\nport: 命令端口, 消息端口为命令端口+1\n");
printf("\nUsage: \n启动: wcf.exe start port [debug]\n关闭: wcf.exe stop\nport: 命令端口, 消息端口为命令端口+1\n");
}
int main(int argc, char *argv[])
@ -34,4 +31,4 @@ int main(int argc, char *argv[])
}
return ret;
}
}