commit
703c2f62d2
@ -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>
|
||||
|
@ -1,6 +1,8 @@
|
||||
# WeChatFerry Python 客户端
|
||||
[](https://pypi.python.org/pypi/wcferry) [](https://pypi.python.org/pypi/wcferry) [](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>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
29
sdk/sdk.cpp
29
sdk/sdk.cpp
@ -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;
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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)¶m, 0xB5, (DWORD)¶m, 0xB0, (DWORD)¶m, 0xB1, (DWORD)¶m, 0x00 };
|
||||
|
||||
param.handle = acceptNewFriendHandle;
|
||||
param.status = status;
|
||||
param.statusEnd1 = (DWORD)status + 0x20;
|
||||
param.statusEnd2 = (DWORD)status + 0x20;
|
||||
NewFriendParam_t *pParam = ¶m;
|
||||
|
||||
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
|
||||
}
|
@ -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
128
spy/chatroom_mgmt.cpp
Normal 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;
|
||||
}
|
@ -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
149
spy/contact_mgmt.cpp
Normal 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
|
||||
}
|
@ -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);
|
@ -1,3 +1,5 @@
|
||||
#pragma warning( disable: 4244 )
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "decrypt_image.h"
|
||||
|
117
spy/exec_sql.cpp
117
spy/exec_sql.cpp
@ -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();
|
||||
|
@ -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;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "pb_types.h"
|
||||
|
||||
vector<RpcContact_t> GetContacts();
|
@ -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)
|
||||
|
27
spy/log.cpp
27
spy/log.cpp
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
155
spy/send_msg.cpp
155
spy/send_msg.cpp
@ -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 };
|
||||
|
BIN
spy/spy.aps
BIN
spy/spy.aps
Binary file not shown.
12
spy/spy.cpp
12
spy/spy.cpp
@ -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模块地址失败");
|
||||
|
@ -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"
|
||||
|
@ -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
192
spy/sqlite3.h
Normal 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);
|
@ -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;
|
||||
}
|
||||
|
37
spy/util.cpp
37
spy/util.cpp
@ -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());
|
||||
}
|
||||
|
17
spy/util.h
17
spy/util.h
@ -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, ...);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user