diff --git a/README.MD b/README.MD index 3f6c0e0..f3bb2a8 100644 --- a/README.MD +++ b/README.MD @@ -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 机器人框架。
点击查看功能清单 diff --git a/python/README.MD b/python/README.MD index d714730..43bea9c 100644 --- a/python/README.MD +++ b/python/README.MD @@ -1,6 +1,8 @@ # WeChatFerry Python 客户端 [![PyPi](https://img.shields.io/pypi/v/wcferry.svg)](https://pypi.python.org/pypi/wcferry) [![Downloads](https://static.pepy.tech/badge/wcferry)](https://pypi.python.org/pypi/wcferry) [![Documentation Status](https://readthedocs.org/projects/wechatferry/badge/?version=latest)](https://wechatferry.readthedocs.io/zh/latest/?badge=latest) +|[📖 文档](https://wechatferry.readthedocs.io/)|[📺 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/XTJ9H-FsCPCscixAts8i_A)| +|:-:|:-:|:-:| 🤖示例机器人框架:[WeChatRobot](https://github.com/lich0821/WeChatRobot)。 ## 快速开始 @@ -37,7 +39,6 @@ def process_msg(wcf: Wcf): def main(): LOG.info("Start demo...") wcf = Wcf(debug=True) # 默认连接本地服务 - # wcf = Wcf("tcp://127.0.0.1:10086") # 连接远端服务 sleep(5) # 等微信加载好,以免信息显示异常 LOG.info(f"已经登录: {True if wcf.is_login() else False}") @@ -110,10 +111,49 @@ pip install grpcio-tools pynng ### 重新生成 PB 文件 ```sh -cd python\wcferry # CMD +cd python\wcferry python -m grpc_tools.protoc --python_out=. --proto_path=..\..\rpc\proto\ wcf.proto # GitBash +cd python/wcferry python -m grpc_tools.protoc --python_out=. --proto_path=../../rpc/proto/ wcf.proto ``` + +## 版本更新 +版本号:`w.x.y.z`。 + +其中: +* `w` 是微信的大版本号,如 `37` (3.7.a.a), `38` (3.8.a.a), `39` (3.9.a.a) +* `x` 是适配的微信的小版本号,从 0 开始 +* `y` 是 `WeChatFerry` 的版本,从 0 开始 +* `z` 是各客户端的版本,从 0 开始 + +### 37.1.25.5 (2023.05.19) +支持 `3.7.0.30` 的最后一个版本。 + +功能: + +* 检查登录状态 +* 获取登录账号的 wxid +* 获取消息类型 +* 获取所有联系人 +* 获取所有好友 +* 获取数据库 +* 获取某数据库下的表 +* 发送文本消息(可 @) +* 发送图片(Python 客户端支持网络路径) +* 发送文件 +* 发送 XML +* 发送表情 +* 允许接收消息 +* 停止接收消息 +* 执行 SQL 查询 +* 接受好友申请 +* 添加群成员 +* 解密图片 +* 某功能 + +
历史更新 + +
diff --git a/python/wcferry/client.py b/python/wcferry/client.py index 09fa128..ffff015 100644 --- a/python/wcferry/client.py +++ b/python/wcferry/client.py @@ -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 diff --git a/python/wcferry/wcf_pb2.py b/python/wcferry/wcf_pb2.py index ea59a4e..94b36fc 100644 --- a/python/wcferry/wcf_pb2.py +++ b/python/wcferry/wcf_pb2.py @@ -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) diff --git a/rpc/proto/wcf.proto b/rpc/proto/wcf.proto index 34d1d83..caf79f6 100644 --- a/rpc/proto/wcf.proto +++ b/rpc/proto/wcf.proto @@ -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 } diff --git a/sdk/injector.cpp b/sdk/injector.cpp index 38e5f97..3717708 100644 --- a/sdk/injector.cpp +++ b/sdk/injector.cpp @@ -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; +} diff --git a/sdk/injector.h b/sdk/injector.h index 67d33a5..e212a0e 100644 --- a/sdk/injector.h +++ b/sdk/injector.h @@ -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); diff --git a/sdk/sdk.cpp b/sdk/sdk.cpp index 3073711..b720a4d 100644 --- a/sdk/sdk.cpp +++ b/sdk/sdk.cpp @@ -1,10 +1,10 @@ #include "Shlwapi.h" #include "framework.h" +#include #include #include #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; } diff --git a/spy/Spy.vcxproj b/spy/Spy.vcxproj index 1b4f3e5..2c78b33 100644 --- a/spy/Spy.vcxproj +++ b/spy/Spy.vcxproj @@ -107,7 +107,7 @@ $(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include - 4251;4819 + 4251;4731;4819 MultiThreaded stdcpp17 /EHa %(AdditionalOptions) @@ -153,7 +153,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto $(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include - 4251;4819 + 4251;4731;4819 MultiThreaded stdcpp17 @@ -226,12 +226,11 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto - - + - + @@ -241,6 +240,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto + @@ -250,12 +250,11 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto - - + - + diff --git a/spy/Spy.vcxproj.filters b/spy/Spy.vcxproj.filters index 2e558b4..a7ce58b 100644 --- a/spy/Spy.vcxproj.filters +++ b/spy/Spy.vcxproj.filters @@ -27,13 +27,10 @@ 头文件 - - 头文件 - 头文件 - + 头文件 @@ -75,7 +72,7 @@ nnrpc - + 头文件 @@ -90,6 +87,9 @@ 头文件 + + 头文件 + @@ -101,13 +101,10 @@ 源文件 - - 源文件 - 源文件 - + 源文件 @@ -140,7 +137,7 @@ nnrpc - + 源文件 diff --git a/spy/accept_new_friend.cpp b/spy/accept_new_friend.cpp deleted file mode 100644 index 938cf72..0000000 --- a/spy/accept_new_friend.cpp +++ /dev/null @@ -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 -} diff --git a/spy/add_chatroom_member.cpp b/spy/add_chatroom_member.cpp deleted file mode 100644 index 2341cc3..0000000 --- a/spy/add_chatroom_member.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "framework.h" -#include - -#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 vMembers; - vector 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; -} diff --git a/spy/chatroom_mgmt.cpp b/spy/chatroom_mgmt.cpp new file mode 100644 index 0000000..e064178 --- /dev/null +++ b/spy/chatroom_mgmt.cpp @@ -0,0 +1,128 @@ +#include "framework.h" +#include +#include + +#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 vMembers; + vector 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 vMembers; + vector 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; +} diff --git a/spy/add_chatroom_member.h b/spy/chatroom_mgmt.h similarity index 60% rename from spy/add_chatroom_member.h rename to spy/chatroom_mgmt.h index eacb825..a53b55b 100644 --- a/spy/add_chatroom_member.h +++ b/spy/chatroom_mgmt.h @@ -1,6 +1,6 @@ #pragma once #include -#include int AddChatroomMember(std::string roomid, std::string wxids); +int DelChatroomMember(std::string roomid, std::string wxids); diff --git a/spy/contact_mgmt.cpp b/spy/contact_mgmt.cpp new file mode 100644 index 0000000..bb9780b --- /dev/null +++ b/spy/contact_mgmt.cpp @@ -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 GetContacts() +{ + vector 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 +} diff --git a/spy/accept_new_friend.h b/spy/contact_mgmt.h similarity index 56% rename from spy/accept_new_friend.h rename to spy/contact_mgmt.h index b6b7d70..b045b64 100644 --- a/spy/accept_new_friend.h +++ b/spy/contact_mgmt.h @@ -1,5 +1,9 @@ #pragma once #include "string" +#include +#include "pb_types.h" + +vector GetContacts(); int AcceptNewFriend(std::string v3, std::string v4, int scene); diff --git a/spy/decrypt_image.cpp b/spy/decrypt_image.cpp index 73b812f..8be811d 100644 --- a/spy/decrypt_image.cpp +++ b/spy/decrypt_image.cpp @@ -1,3 +1,5 @@ +#pragma warning( disable: 4244 ) + #include #include "decrypt_image.h" diff --git a/spy/exec_sql.cpp b/spy/exec_sql.cpp index 145c23d..6fc3e3a 100644 --- a/spy/exec_sql.cpp +++ b/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 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(); diff --git a/spy/get_contacts.cpp b/spy/get_contacts.cpp deleted file mode 100644 index 0fbd5ce..0000000 --- a/spy/get_contacts.cpp +++ /dev/null @@ -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 GetContacts() -{ - vector 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; -} diff --git a/spy/get_contacts.h b/spy/get_contacts.h deleted file mode 100644 index a0ff70d..0000000 --- a/spy/get_contacts.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -#include "pb_types.h" - -vector GetContacts(); diff --git a/spy/load_calls.cpp b/spy/load_calls.cpp index b0c0bc5..a989338 100644 --- a/spy/load_calls.cpp +++ b/spy/load_calls.cpp @@ -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) diff --git a/spy/log.cpp b/spy/log.cpp index 9b432ab..50e8d58 100644 --- a/spy/log.cpp +++ b/spy/log.cpp @@ -1,27 +1,34 @@ +#include + #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 gLogger = nullptr; - if (gLogger != nullptr) { + static std::shared_ptr 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"); } diff --git a/spy/log.h b/spy/log.h index c6a2926..dbc66d5 100644 --- a/spy/log.h +++ b/spy/log.h @@ -1,5 +1,7 @@ #pragma once +#include + #ifdef ENABLE_DEBUG_LOG #include @@ -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); diff --git a/spy/receive_msg.cpp b/spy/receive_msg.cpp index 77f666e..6be0b1e 100644 --- a/spy/receive_msg.cpp +++ b/spy/receive_msg.cpp @@ -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)) { diff --git a/spy/receive_transfer.cpp b/spy/receive_transfer.cpp index 80346c2..6e1f006 100644 --- a/spy/receive_transfer.cpp +++ b/spy/receive_transfer.cpp @@ -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; diff --git a/spy/receive_transfer.h b/spy/receive_transfer.h index 82fd0aa..1942fb1 100644 --- a/spy/receive_transfer.h +++ b/spy/receive_transfer.h @@ -2,4 +2,4 @@ #include -int ReceiveTransfer(std::string wxid, std::string transferid); +int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid); diff --git a/spy/rpc_server.cpp b/spy/rpc_server.cpp index fd8790f..bbc56a6 100644 --- a/spy/rpc_server.cpp +++ b/spy/rpc_server.cpp @@ -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; diff --git a/spy/send_msg.cpp b/spy/send_msg.cpp index 7c93e5e..a53e306 100644 --- a/spy/send_msg.cpp +++ b/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 }; diff --git a/spy/spy.aps b/spy/spy.aps index dd243d5..e053b40 100644 Binary files a/spy/spy.aps and b/spy/spy.aps differ diff --git a/spy/spy.cpp b/spy/spy.cpp index 5f7d33e..55c98ba 100644 --- a/spy/spy.cpp +++ b/spy/spy.cpp @@ -1,16 +1,22 @@ -#include "spy.h" +#include + #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模块地址失败"); diff --git a/spy/spy.rc b/spy/spy.rc index b52930f..abf36e5 100644 --- a/spy/spy.rc +++ b/spy/spy.rc @@ -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" diff --git a/spy/spy_types.h b/spy/spy_types.h index e11860e..89287af 100644 --- a/spy/spy_types.h +++ b/spy/spy_types.h @@ -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; diff --git a/spy/sqlite3.h b/spy/sqlite3.h new file mode 100644 index 0000000..ac3d2e8 --- /dev/null +++ b/spy/sqlite3.h @@ -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); diff --git a/spy/user_info.cpp b/spy/user_info.cpp index 3797e45..79e800d 100644 --- a/spy/user_info.cpp +++ b/spy/user_info.cpp @@ -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; } diff --git a/spy/util.cpp b/spy/util.cpp index 5604b34..7b22be5 100644 --- a/spy/util.cpp +++ b/spy/util.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #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 zc(iLen + 1); + std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs); + va_end(vaArgs); + std::string strText(zc.data(), iLen); + + OutputDebugStringA(strText.c_str()); +} diff --git a/spy/util.h b/spy/util.h index 147f706..5cf305f 100644 --- a/spy/util.h +++ b/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, ...); diff --git a/wcf/main.cpp b/wcf/main.cpp index 98ff667..43d797b 100644 --- a/wcf/main.cpp +++ b/wcf/main.cpp @@ -2,14 +2,11 @@ #include #include -#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; -} \ No newline at end of file +}