commit
84834d6b8e
15
.gitignore
vendored
15
.gitignore
vendored
@ -7,7 +7,14 @@ Debug/
|
||||
Release/
|
||||
x64/
|
||||
|
||||
# RPC generated files
|
||||
*_c.c
|
||||
*_s.c
|
||||
*_h.h
|
||||
# Generated files
|
||||
*.pb.h
|
||||
*.pb.cc
|
||||
sdk.dll
|
||||
spy.dll
|
||||
build/
|
||||
logs/
|
||||
|
||||
*_pb2.py
|
||||
*_pb2_grpc.py
|
||||
__pycache__
|
||||
|
157
App/App.cpp
157
App/App.cpp
@ -1,157 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <iostream>
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "sdk.h"
|
||||
/*
|
||||
#pragma comment(lib, "SDK.lib")
|
||||
等效为在属性,链接,输入中添加该依赖
|
||||
*/
|
||||
|
||||
typedef map<int, wstring> sqlTypes_t;
|
||||
static sqlTypes_t sqlTypes
|
||||
= sqlTypes_t { { 1, L"INTEGER" }, { 2, L"FLOAT" }, { 3, L"TEXT" }, { 4, L"BLOB" }, { 5, L"NULL" } };
|
||||
|
||||
void printContacts(ContactMap_t contacts)
|
||||
{
|
||||
wprintf(L"contacts number: %ld\n", contacts.size());
|
||||
for (auto it = contacts.begin(); it != contacts.end(); it++) {
|
||||
wprintf(L"%s\t%s\t%s\t%s\t%s\t%s\t%s\r\n", it->second.wxId.c_str(), it->second.wxCode.c_str(),
|
||||
it->second.wxName.c_str(), it->second.wxGender.c_str(), it->second.wxCountry.c_str(),
|
||||
it->second.wxProvince.c_str(), it->second.wxCity.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void printDbNames(vector<wstring> vDbs)
|
||||
{
|
||||
wprintf(L"db numbers: %ld\n", vDbs.size());
|
||||
for (auto it = vDbs.begin(); it != vDbs.end(); it++) {
|
||||
wprintf(L"%s\n", (*it).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void printDbTables(DbTableVector_t tables)
|
||||
{
|
||||
wprintf(L"table numbers: %ld\n", tables.size());
|
||||
for (auto it = tables.begin(); it != tables.end(); it++) {
|
||||
wprintf(L"%s\n%s\n\n", (it->table).c_str(), (it->sql).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void printDbResults(SqlRetVector_t vvResults)
|
||||
{
|
||||
int rows = vvResults.size();
|
||||
printf("vvResults.size: %d\n", rows);
|
||||
rows = 0;
|
||||
for (auto vv = vvResults.begin(); vv != vvResults.end(); vv++) {
|
||||
printf("Row %d\n", rows++);
|
||||
for (auto v = vv->begin(); v != vv->end(); v++) {
|
||||
wprintf(L"%s[%s]: ", v->column.c_str(), sqlTypes[v->type].c_str());
|
||||
switch (v->type) {
|
||||
case 1: {
|
||||
printf("%d\n", stoi(v->content.c_str()));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
printf("%f\n", stof(v->content.c_str()));
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
printf("%s\n", v->content.c_str());
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
byte *p = (byte *)(v->content.c_str());
|
||||
for (unsigned int i = 0; i < v->content.size(); i++) {
|
||||
printf("%02X ", p[i]);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int onTextMsg(WxMessage_t msg)
|
||||
{
|
||||
wprintf(L"%s msgType: %d, msgSource: %d, isSelf: %d\n", msg.id.c_str(), msg.type, msg.source, msg.self);
|
||||
wprintf(L"%s[%s] >> %s\n", msg.wxId.c_str(), msg.roomId.c_str(), msg.content.c_str());
|
||||
wprintf(L"msgSourceXml: %s\n", msg.xml.c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
DWORD status = 0;
|
||||
wstring wxid = L"filehelper"; // 微信ID
|
||||
wstring at_wxid = L"";
|
||||
wstring content = L"这里填写消息内容";
|
||||
wstring img_path = L"test.jpg";
|
||||
|
||||
setlocale(LC_ALL, "chs"); // 这是个大坑,不设置中文直接不见了。。。
|
||||
|
||||
wprintf(L"WxInitSDK: ");
|
||||
status = WxInitSDK();
|
||||
wprintf(L"%d\n", status);
|
||||
if (status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 自己的 wxid
|
||||
wstring selfWxid = WxGetSelfWxid();
|
||||
wprintf(L"本号WXID:%s\n", selfWxid.c_str());
|
||||
|
||||
// 获取消息类型
|
||||
wprintf(L"获取消息类型\n");
|
||||
const MsgTypesMap_t WxMsgTypes = WxGetMsgTypes();
|
||||
for (auto it = WxMsgTypes.begin(); it != WxMsgTypes.end(); ++it) {
|
||||
wprintf(L"%d: %s\n", it->first, it->second.c_str());
|
||||
}
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
wprintf(L"Message: 接收通知中......\n");
|
||||
WxEnableRecvMsg(onTextMsg);
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
// 测试发送消息
|
||||
wprintf(L"测试发送消息\n");
|
||||
WxSendTextMsg(wxid, content, at_wxid);
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
// 测试发送照片
|
||||
wprintf(L"测试发送照片\n");
|
||||
WxSendImageMsg(wxid, img_path);
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
// 测试获取联系人
|
||||
auto mContact = WxGetContacts();
|
||||
printContacts(mContact);
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
// 测试获取数据库名
|
||||
auto vDbNames = WxGetDbNames();
|
||||
printDbNames(vDbNames);
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
// 测试获取数据库中的表
|
||||
auto vDbTables = WxGetDbTables(L"MicroMsg.db");
|
||||
printDbTables(vDbTables);
|
||||
Sleep(1000); // 等待1秒
|
||||
|
||||
// 测试执行 SQL
|
||||
auto vvResults = WxExecDbQuery(L"MicroMsg.db", L"SELECT * FROM Contact LIMIT 1;");
|
||||
printDbResults(vvResults);
|
||||
|
||||
while (1) {
|
||||
Sleep(10000); // 休眠,释放CPU
|
||||
}
|
||||
}
|
84
App/App.py
84
App/App.py
@ -1,84 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import time
|
||||
import wcferry as sdk
|
||||
|
||||
|
||||
def main():
|
||||
help(sdk) # 查看SDK支持的方法和属性
|
||||
|
||||
# 初始化SDK,如果成功,返回0;否则失败
|
||||
status = sdk.WxInitSDK()
|
||||
if status != 0:
|
||||
print("初始化失败")
|
||||
exit(-1)
|
||||
|
||||
print("初始化成功")
|
||||
WxMsgTypes = sdk.WxGetMsgTypes() # 获取消息类型
|
||||
print(WxMsgTypes) # 查看消息类型
|
||||
|
||||
time.sleep(2)
|
||||
print("打印通讯录......")
|
||||
contacts = sdk.WxGetContacts()
|
||||
for k, v in contacts.items():
|
||||
print(k, v.wxCode, v.wxName, v.wxCountry, v.wxProvince, v.wxCity, v.wxGender)
|
||||
|
||||
time.sleep(2)
|
||||
print("发送文本消息......")
|
||||
sdk.WxSendTextMsg("filehelper", "message from WeChatFerry...") # 往文件传输助手发消息
|
||||
# sdk.WxSendTextMsg("xxxx@chatroom", "message from WeChatFerry...") # 往群里发消息(需要改成正确的 ID,下同)
|
||||
# sdk.WxSendTextMsg("xxxx@chatroom", "message from WeChatFerry... @ ", "wxid_xxxxxxxxxxxx") # 往群里发消息,@某人
|
||||
# sdk.WxSendTextMsg("xxxx@chatroom", "message from WeChatFerry... @ ", "notify@all") # 往群里发消息,@所有人
|
||||
|
||||
time.sleep(2)
|
||||
print("发送图片消息......")
|
||||
sdk.WxSendImageMsg("filehelper", "test.jpg")
|
||||
|
||||
dbs = sdk.WxGetDbNames()
|
||||
for db in dbs:
|
||||
print(db)
|
||||
|
||||
tables = sdk.WxGetDbTables(dbs[0])
|
||||
for t in tables:
|
||||
print(f"{t.table}\n{t.sql}\n\n")
|
||||
|
||||
# 接收消息。先定义消息处理回调
|
||||
def OnTextMsg(msg: sdk.WxMessage):
|
||||
def getName(id):
|
||||
contact = contacts.get(id)
|
||||
if contact is None:
|
||||
return id
|
||||
return contact.wxName
|
||||
|
||||
s = "收到"
|
||||
if msg.self == 1: # 忽略自己发的消息
|
||||
s += f"来自自己的消息"
|
||||
print(f"\n{s}")
|
||||
return 0
|
||||
|
||||
msgType = WxMsgTypes.get(msg.type, '未知类型')
|
||||
nickName = getName(msg.wxId)
|
||||
if msg.source == 1:
|
||||
groupName = getName(msg.roomId)
|
||||
s += f"来自群[{groupName}]的[{nickName}]的{msgType}消息:"
|
||||
else:
|
||||
s += f"来自[{nickName}]的{msgType}消息:"
|
||||
|
||||
s += f"\r\n{msg.content}"
|
||||
if msg.type != 0x01:
|
||||
s += f"\r\n{msg.xml}"
|
||||
|
||||
print(f"\n{s}")
|
||||
|
||||
return 0
|
||||
|
||||
print("Message: 接收通知中......")
|
||||
sdk.WxEnableRecvMsg(OnTextMsg) # 设置回调,接收消息
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="app.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
BIN
App/test.jpg
BIN
App/test.jpg
Binary file not shown.
Before Width: | Height: | Size: 399 KiB |
84
README.MD
84
README.MD
@ -3,35 +3,75 @@
|
||||
|
||||
欢迎加群交流,后台回复 `WeChatFerry `:
|
||||
|
||||

|
||||

|
||||
|
||||
## 快速开始
|
||||
1. 使用 VS2019 编译。
|
||||
2. 打开 `CMD`,运行 `App.exe`
|
||||
### 安装开发环境
|
||||
参见 [A gRPC Demo](https://github.com/lich0821/gRpcDemo/blob/wx/README.MD)。
|
||||
#### 安装 vcpkg
|
||||
* 安装,参考[Vcpkg: 总览](https://github.com/microsoft/vcpkg/blob/master/README_zh_CN.md)。
|
||||
```sh
|
||||
cd C:\Tools
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
.\vcpkg\bootstrap-vcpkg.bat
|
||||
```
|
||||
|
||||
* 添加全局配置:
|
||||
环境变量增加 `vcpkg` 所在路径(本文为:`C:\Tools\vcpkg`)。
|
||||
|
||||
#### 安装 gRPC 相关组件
|
||||
```sh
|
||||
vcpkg install grpc:x86-windows-static
|
||||
vcpkg install protobuf[zlib]:x86-windows-static
|
||||
vcpkg integrate install
|
||||
```
|
||||
|
||||
#### 安装 VS2019
|
||||
|
||||
### 生成编解码文件和接口文件
|
||||
```sh
|
||||
cd WeChatFerry/proto/
|
||||
protoc --cpp_out=. -I=. wcf.proto
|
||||
protoc --grpc_out=. --plugin=protoc-gen-grpc="C:\Tools\vcpkg\packages\grpc_x64-windows\tools\grpc\grpc_cpp_plugin.exe" -I=. wcf.proto
|
||||
```
|
||||
|
||||
执行 `tree .`,可见生成了四个文件:
|
||||
```txt
|
||||
.
|
||||
├── wcf.grpc.pb.cc
|
||||
├── wcf.grpc.pb.h
|
||||
├── wcf.pb.cc
|
||||
├── wcf.pb.h
|
||||
└── wcf.proto
|
||||
```
|
||||
|
||||
### 编译
|
||||
使用 VS2019 打开工程,编译即可。
|
||||
|
||||
### 运行
|
||||
双击 `Release` 里的 `cpp.exe`。如果出现中文乱码,则需要将编码改为 `UTF-8`。
|
||||
|
||||
## 项目结构
|
||||
### Spy
|
||||
|
||||
### cpp
|
||||
C++ 示例应用,介绍如何使用 WeChatFerry。
|
||||
|
||||
### proto
|
||||
RPC 消息及接口定义。
|
||||
|
||||
### python
|
||||
Python 客户端。
|
||||
|
||||
### sdk
|
||||
负责将 `spy` 注入微信进程,并启动 gRPC 服务端。
|
||||
|
||||
### spy
|
||||
间谍模块,注入到微信中,通过 RPC 做消息转发工作。
|
||||
|
||||
### SDK
|
||||
RPC 的客户端,封装接口,供其他方调用。
|
||||
|
||||
### SDKpy
|
||||
用于生成 Python 接口。为编译该项目,需要做一些配置:
|
||||
1. 添加附加包含目录
|
||||
* Python 头:`C:\Program Files (x86)\Python37-32\Include`
|
||||
* Pybind11 头:`C:\Projs\.pyenv\pybind11\lib\site-packages\pybind11\include`
|
||||
*注*:
|
||||
1. pybind11 可以通过 Python 安装(本工程安装到虚拟环境里了):`pip install pybind11`
|
||||
2. 然后通过命令查找:`python -m pybind11 --includes`
|
||||
|
||||
2. 添加 Python 的库
|
||||
* `C:\Program Files (x86)\Python37-32\libs`
|
||||
|
||||
### App
|
||||
示例应用,介绍如何调用 SDK。
|
||||
|
||||
## 版本更新
|
||||
### v3.7.0.30-gRPC(2022.10.15)
|
||||
将 RPC 框架切换为 gRPC!
|
||||
|
||||
### v3.7.0.30-8(2022.09.25)
|
||||
* 获取登录账号微信 ID
|
||||
|
||||
|
75
Rpc/rpc.idl
75
Rpc/rpc.idl
@ -1,75 +0,0 @@
|
||||
[
|
||||
uuid(ed838ecd-8a1e-4da7-bfda-9f2d12d07893),
|
||||
version(1.0),
|
||||
implicit_handle(handle_t hSpyBinding),
|
||||
]
|
||||
|
||||
interface ISpy
|
||||
{
|
||||
import "oaidl.idl";
|
||||
|
||||
typedef struct RpcMessage {
|
||||
int self; // 是否自己发的消息:0=否,1=是
|
||||
int type; // 消息类型
|
||||
int source; // 消息来源:0=好友消息,1=群消息
|
||||
BSTR id; // 消息ID
|
||||
BSTR xml; // 群其他消息
|
||||
BSTR wxId; // 发送人微信ID
|
||||
BSTR roomId; // 群ID
|
||||
BSTR content; // 消息内容,MAC版最大:16384,即16KB
|
||||
} RpcMessage_t;
|
||||
|
||||
// 模拟 map<int, wstring>
|
||||
typedef struct RpcIntBstrPair {
|
||||
int key;
|
||||
BSTR value;
|
||||
} RpcIntBstrPair_t;
|
||||
typedef RpcIntBstrPair_t *PRpcIntBstrPair;
|
||||
typedef RpcIntBstrPair_t **PPRpcIntBstrPair;
|
||||
|
||||
typedef struct RpcContact {
|
||||
BSTR wxId; // 微信ID
|
||||
BSTR wxCode; // 微信号
|
||||
BSTR wxName; // 微信昵称
|
||||
BSTR wxCountry; // 国家
|
||||
BSTR wxProvince; // 省/州
|
||||
BSTR wxCity; // 城市
|
||||
BSTR wxGender; // 性别
|
||||
} RpcContact_t;
|
||||
typedef RpcContact_t *PRpcContact;
|
||||
typedef RpcContact_t **PPRpcContact;
|
||||
|
||||
typedef struct RpcTables {
|
||||
BSTR table; // 表名
|
||||
BSTR sql; // 建表 SQL
|
||||
} RpcTables_t;
|
||||
typedef RpcTables_t *PRpcTables;
|
||||
typedef RpcTables_t **PPRpcTables;
|
||||
|
||||
typedef struct RpcSqlResult {
|
||||
int type;
|
||||
BSTR column;
|
||||
BSTR content;
|
||||
} RpcSqlResult_t;
|
||||
typedef RpcSqlResult_t *PRpcSqlResult;
|
||||
typedef RpcSqlResult_t **PPRpcSqlResult;
|
||||
typedef RpcSqlResult_t ***PPPRpcSqlResult;
|
||||
|
||||
int IsLogin();
|
||||
int GetSelfWxId([ out, string ] wchar_t wxid[20]);
|
||||
int SendTextMsg([ in, string ] const wchar_t *wxid, [ in, string ] const wchar_t *msg,
|
||||
[ in, unique, string ] const wchar_t *atWxids);
|
||||
int SendImageMsg([ in, string ] const wchar_t *wxid, [ in, string ] const wchar_t *path);
|
||||
int GetMsgTypes([out] int *pNum, [ out, size_is(, *pNum) ] PPRpcIntBstrPair *msgTypes);
|
||||
int GetContacts([out] int *pNum, [ out, size_is(, *pNum) ] PPRpcContact *contacts);
|
||||
int GetDbNames([out] int *pNum, [ out, size_is(, *pNum) ] BSTR **dbs);
|
||||
int GetDbTables([ in, string ] const wchar_t *db, [out] int *pNum, [ out, size_is(, *pNum) ] PPRpcTables *tbls);
|
||||
int ExecDbQuery([ in, string ] const wchar_t *db,
|
||||
[ in, string ] const wchar_t *sql, [out] int *pRow, [out] int *pCol,
|
||||
[ out, size_is(, *pRow, *pCol) ] PPPRpcSqlResult *ret);
|
||||
BOOL AcceptNewFriend([in, string] const wchar_t *v3, [in, string] const wchar_t *v4);
|
||||
|
||||
void EnableReceiveMsg();
|
||||
void DisableReceiveMsg();
|
||||
[callback] int ReceiveMsg([in] RpcMessage_t rpcMsg);
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
/******************************************************/
|
||||
/* MIDL allocate and free */
|
||||
/******************************************************/
|
||||
|
||||
void __RPC_FAR *__RPC_USER midl_user_allocate(size_t len) { return (malloc(len)); }
|
||||
|
||||
void __RPC_USER midl_user_free(void __RPC_FAR *ptr) { free(ptr); }
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <windows.h>
|
@ -1,70 +0,0 @@
|
||||
#include "injector.h"
|
||||
|
||||
int InjectDll(DWORD pid, const WCHAR *dllPath)
|
||||
{
|
||||
HANDLE hThread;
|
||||
DWORD dwWriteSize = 0;
|
||||
// 1. 获取目标进程,并在目标进程的内存里开辟空间
|
||||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
// 2. 把 dll 的路径写入到目标进程的内存空间中
|
||||
if (pRemoteAddress) {
|
||||
WriteProcessMemory(hProcess, pRemoteAddress, dllPath, wcslen(dllPath) * 2 + 2, &dwWriteSize);
|
||||
} else {
|
||||
MessageBox(NULL, L"DLL 路径写入失败", L"InjectDll", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 3. 创建一个远程线程,让目标进程调用 LoadLibrary
|
||||
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL);
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, -1);
|
||||
} else {
|
||||
MessageBox(NULL, L"LoadLibrary 调用失败", L"InjectDll", 0);
|
||||
return -2;
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
|
||||
CloseHandle(hProcess);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EjectDll(DWORD pid, const WCHAR *dllPath)
|
||||
{
|
||||
DWORD dwHandle, dwID;
|
||||
HANDLE hThread = NULL;
|
||||
DWORD dwWriteSize = 0;
|
||||
|
||||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (pRemoteAddress)
|
||||
WriteProcessMemory(hProcess, pRemoteAddress, dllPath, wcslen(dllPath) * 2 + 2, &dwWriteSize);
|
||||
else {
|
||||
MessageBox(NULL, L"DLL 路径写入失败", L"EjectDll", 0);
|
||||
return -1;
|
||||
}
|
||||
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetModuleHandleW, pRemoteAddress, 0, &dwID);
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
GetExitCodeThread(hThread, &dwHandle);
|
||||
} else {
|
||||
MessageBox(NULL, L"GetModuleHandleW 调用失败!", L"EjectDll", 0);
|
||||
return -2;
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
|
||||
// 使目标进程调用 FreeLibrary,卸载 DLL
|
||||
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, (LPVOID)dwHandle, 0, &dwID);
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
} else {
|
||||
MessageBox(NULL, L"FreeLibrary 调用失败!", L"EjectDll", 0);
|
||||
return -3;
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
|
||||
CloseHandle(hProcess);
|
||||
return 0;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
int InjectDll(DWORD pid, const WCHAR* dllPath);
|
||||
int EjectDll(DWORD pid, const WCHAR* dllPath);
|
@ -1,278 +0,0 @@
|
||||
#include "rpc_client.h"
|
||||
#include "sdk.h"
|
||||
#include "util.h"
|
||||
|
||||
static RPC_WSTR pszStringBinding = NULL;
|
||||
extern std::function<int(WxMessage_t)> g_cbReceiveTextMsg;
|
||||
|
||||
RPC_STATUS RpcConnectServer()
|
||||
{
|
||||
RPC_STATUS status = 0;
|
||||
// Creates a string binding handle.
|
||||
status = RpcStringBindingCompose(NULL, // UUID to bind to
|
||||
reinterpret_cast<RPC_WSTR>((RPC_WSTR)L"ncalrpc"), // Use TCP/IP protocol
|
||||
NULL, // TCP/IP network address to use
|
||||
reinterpret_cast<RPC_WSTR>((RPC_WSTR)L"wcferry"), // TCP/IP port to use
|
||||
NULL, // Protocol dependent network options to use
|
||||
&pszStringBinding); // String binding output
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Validates the format of the string binding handle and converts it to a binding handle.
|
||||
pszStringBinding: The string binding to validate
|
||||
hSpyBinding: Put the result in the implicit binding(defined in the IDL file)
|
||||
*/
|
||||
status = RpcBindingFromStringBinding(pszStringBinding, &hSpyBinding);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
RPC_STATUS RpcDisconnectServer()
|
||||
{
|
||||
RPC_STATUS status;
|
||||
// Free the memory allocated by a string
|
||||
status = RpcStringFree(&pszStringBinding);
|
||||
|
||||
// Releases binding handle resources and disconnects from the server
|
||||
status = RpcBindingFree(&hSpyBinding);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int RpcEnableReceiveMsg()
|
||||
{
|
||||
unsigned long ulCode = 0;
|
||||
RpcTryExcept
|
||||
{
|
||||
// 建立RPC通道,让服务端能够调用客户端的回调函数。(该接口会被服务端阻塞直到异常退出)
|
||||
client_EnableReceiveMsg();
|
||||
}
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcEnableReceiveMsg exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RpcDisableReceiveMsg()
|
||||
{
|
||||
unsigned long ulCode = 0;
|
||||
RpcTryExcept
|
||||
{
|
||||
// UnHook Message receiving
|
||||
client_DisableReceiveMsg();
|
||||
}
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcDisableReceiveMsg exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RpcIsLogin()
|
||||
{
|
||||
int loginFlag = 0;
|
||||
unsigned long ulCode = 0;
|
||||
RpcTryExcept
|
||||
{
|
||||
// 查询登录状态
|
||||
loginFlag = client_IsLogin();
|
||||
}
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcIsLogin exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
return -1;
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return loginFlag;
|
||||
}
|
||||
|
||||
int RpcGetSelfWxId(wchar_t wxid[20])
|
||||
{
|
||||
int ret = -1;
|
||||
unsigned long ulCode = 0;
|
||||
RpcTryExcept { ret = client_GetSelfWxId(wxid); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcIsLogin exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int RpcSendTextMsg(const wchar_t *wxid, const wchar_t *msg, const wchar_t *atWxids)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
|
||||
RpcTryExcept { ret = client_SendTextMsg(wxid, msg, atWxids); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcSendTextMsg exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int RpcSendImageMsg(const wchar_t *wxid, const wchar_t *path)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
|
||||
RpcTryExcept { ret = client_SendImageMsg(wxid, path); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcSendImageMsg exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PPRpcIntBstrPair RpcGetMsgTypes(int *pNum)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
PPRpcIntBstrPair ppRpcMsgTypes = NULL;
|
||||
|
||||
RpcTryExcept { ret = client_GetMsgTypes(pNum, &ppRpcMsgTypes); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcGetMsgTypes exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
if (ret != 0) {
|
||||
printf("GetMsgTypes Failed: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ppRpcMsgTypes;
|
||||
}
|
||||
|
||||
PPRpcContact RpcGetContacts(int *pNum)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
PPRpcContact ppRpcContacts = NULL;
|
||||
|
||||
RpcTryExcept { ret = client_GetContacts(pNum, &ppRpcContacts); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcGetContacts exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
if (ret != 0) {
|
||||
printf("GetContacts Failed: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ppRpcContacts;
|
||||
}
|
||||
|
||||
BSTR *RpcGetDbNames(int *pNum)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
BSTR *pBstr = NULL;
|
||||
|
||||
RpcTryExcept { ret = client_GetDbNames(pNum, &pBstr); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcGetDbNames exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
if (ret != 0) {
|
||||
printf("RpcGetDbNames Failed: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pBstr;
|
||||
}
|
||||
|
||||
PPRpcTables RpcGetDbTables(const wchar_t *db, int *pNum)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
PPRpcTables ppRpcTables = NULL;
|
||||
|
||||
RpcTryExcept { ret = client_GetDbTables(db, pNum, &ppRpcTables); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcGetDbTables exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
if (ret != 0) {
|
||||
printf("RpcGetDbTables Failed: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ppRpcTables;
|
||||
}
|
||||
|
||||
PPPRpcSqlResult RpcExecDbQuery(const wchar_t *db, const wchar_t *sql, int *pRow, int *pCol)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
PPPRpcSqlResult pppRpcSqlResult = NULL;
|
||||
|
||||
RpcTryExcept { ret = client_ExecDbQuery(db, sql, pRow, pCol, &pppRpcSqlResult); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("RpcExecDbQuery exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
if (ret != 0) {
|
||||
printf("RpcExecDbQuery Failed: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pppRpcSqlResult;
|
||||
}
|
||||
|
||||
BOOL AcceptNewFriend(const wchar_t *v3, const wchar_t *v4)
|
||||
{
|
||||
BOOL ret = 0;
|
||||
unsigned long ulCode = 0;
|
||||
|
||||
RpcTryExcept { ret = client_AcceptNewFriend(v3, v4); }
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("AcceptNewFriend exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int server_ReceiveMsg(RpcMessage_t rpcMsg)
|
||||
{
|
||||
WxMessage_t msg;
|
||||
GetRpcMessage(&msg, rpcMsg);
|
||||
try {
|
||||
g_cbReceiveTextMsg(msg); // 调用接收消息回调
|
||||
} catch (...) {
|
||||
printf("callback error...\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "rpc_h.h"
|
||||
|
||||
RPC_STATUS RpcConnectServer();
|
||||
RPC_STATUS RpcDisconnectServer();
|
||||
|
||||
int RpcEnableReceiveMsg();
|
||||
int RpcDisableReceiveMsg();
|
||||
int RpcIsLogin();
|
||||
int RpcGetSelfWxId(wchar_t wxid[20]);
|
||||
int RpcSendTextMsg(const wchar_t *wxid, const wchar_t *msg, const wchar_t *atWxids);
|
||||
int RpcSendImageMsg(const wchar_t *wxid, const wchar_t *path);
|
||||
PPRpcIntBstrPair RpcGetMsgTypes(int *pNum);
|
||||
PPRpcContact RpcGetContacts(int *pNum);
|
||||
BSTR *RpcGetDbNames(int *pNum);
|
||||
PPRpcTables RpcGetDbTables(const wchar_t *db, int *pNum);
|
||||
PPPRpcSqlResult RpcExecDbQuery(const wchar_t *db, const wchar_t *sql, int *row, int *col);
|
||||
BOOL AcceptNewFriend(const wchar_t *v3, const wchar_t *v4);
|
273
SDK/sdk.cpp
273
SDK/sdk.cpp
@ -1,273 +0,0 @@
|
||||
#include "Shlwapi.h"
|
||||
#include "framework.h"
|
||||
#include <process.h>
|
||||
#include <queue>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <vector>
|
||||
|
||||
#include "injector.h"
|
||||
#include "rpc_client.h"
|
||||
#include "sdk.h"
|
||||
#include "util.h"
|
||||
|
||||
std::function<int(WxMessage_t)> g_cbReceiveTextMsg;
|
||||
|
||||
static DWORD WeChatPID = 0;
|
||||
static WCHAR SpyDllPath[MAX_PATH] = { 0 };
|
||||
|
||||
int WxInitSDK()
|
||||
{
|
||||
int status = 0;
|
||||
unsigned long ulCode = 0;
|
||||
|
||||
GetModuleFileName(GetModuleHandle(WECHATSDKDLL), SpyDllPath, MAX_PATH);
|
||||
PathRemoveFileSpec(SpyDllPath);
|
||||
PathAppend(SpyDllPath, WECHATINJECTDLL);
|
||||
|
||||
if (!PathFileExists(SpyDllPath)) {
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
status = OpenWeChat(&WeChatPID);
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
Sleep(2000); // 等待微信打开
|
||||
if (InjectDll(WeChatPID, SpyDllPath)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Sleep(1000); // 等待SPY就绪
|
||||
status = RpcConnectServer();
|
||||
if (status != 0) {
|
||||
printf("RpcConnectServer: %d\n", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
status = RpcIsLogin();
|
||||
if (status == -1) {
|
||||
return status;
|
||||
} else if (status == 1) {
|
||||
break;
|
||||
}
|
||||
Sleep(1000);
|
||||
} while (1);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int WxDestroySDK()
|
||||
{
|
||||
WxDisableRecvMsg();
|
||||
RpcDisconnectServer();
|
||||
// 关闭 RPC,但不卸载 DLL,方便下次使用。
|
||||
// EjectDll(WeChatPID, SpyDllPath);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int WxEnableRecvMsg(const std::function<int(WxMessage_t)> &onMsg)
|
||||
{
|
||||
if (onMsg) {
|
||||
HANDLE msgThread;
|
||||
g_cbReceiveTextMsg = onMsg;
|
||||
|
||||
msgThread = (HANDLE)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RpcEnableReceiveMsg, NULL, 0, NULL);
|
||||
if (msgThread == NULL) {
|
||||
printf("Failed to create innerWxRecvTextMsg.\n");
|
||||
return -2;
|
||||
}
|
||||
CloseHandle(msgThread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Empty Callback.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WxDisableRecvMsg()
|
||||
{
|
||||
RpcDisableReceiveMsg();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WxSendTextMsg(wstring wxid, wstring msg, wstring atWxids)
|
||||
{
|
||||
return RpcSendTextMsg(wxid.c_str(), msg.c_str(), atWxids.c_str());
|
||||
}
|
||||
|
||||
int WxSendImageMsg(wstring wxid, wstring path) { return RpcSendImageMsg(wxid.c_str(), path.c_str()); }
|
||||
|
||||
static int getAddrHandle(DWORD *addr, HANDLE *handle)
|
||||
{
|
||||
DWORD processID = 0;
|
||||
wstring processName = L"WeChat.exe";
|
||||
wstring moduleName = L"WeChatWin.dll";
|
||||
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
|
||||
while (Process32Next(hSnapshot, &pe32)) {
|
||||
wstring strProcess = pe32.szExeFile;
|
||||
if (strProcess == processName) {
|
||||
processID = pe32.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseHandle(hSnapshot);
|
||||
if (processID == 0) {
|
||||
printf("Failed to get Process ID\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processID);
|
||||
if (hProcessSnapshot == INVALID_HANDLE_VALUE) {
|
||||
printf("Failed to get Process Snapshot\r\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
MODULEENTRY32 me32;
|
||||
SecureZeroMemory(&me32, sizeof(MODULEENTRY32));
|
||||
me32.dwSize = sizeof(MODULEENTRY32);
|
||||
while (Module32Next(hProcessSnapshot, &me32)) {
|
||||
me32.dwSize = sizeof(MODULEENTRY32);
|
||||
|
||||
if (!wcscmp(me32.szModule, moduleName.c_str())) {
|
||||
*addr = (DWORD)me32.modBaseAddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hProcessSnapshot);
|
||||
if (*addr == 0) {
|
||||
printf("Failed to get Module Address\r\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
*handle = OpenProcess(PROCESS_VM_READ, FALSE, processID);
|
||||
if (*handle == 0) {
|
||||
printf("Failed to open Process\r\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
wstring WxGetSelfWxid()
|
||||
{
|
||||
wchar_t wxid[20] = { 0 };
|
||||
RpcGetSelfWxId(wxid);
|
||||
return wstring(wxid);
|
||||
}
|
||||
|
||||
MsgTypesMap_t WxGetMsgTypes()
|
||||
{
|
||||
static MsgTypesMap_t WxMsgTypes;
|
||||
if (WxMsgTypes.empty()) {
|
||||
int size = 0;
|
||||
PPRpcIntBstrPair pp = RpcGetMsgTypes(&size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
WxMsgTypes.insert(make_pair(pp[i]->key, GetWstringFromBstr(pp[i]->value)));
|
||||
midl_user_free(pp[i]);
|
||||
}
|
||||
if (pp) {
|
||||
midl_user_free(pp);
|
||||
}
|
||||
}
|
||||
|
||||
return WxMsgTypes;
|
||||
}
|
||||
|
||||
ContactMap_t WxGetContacts()
|
||||
{
|
||||
ContactMap_t mContact;
|
||||
int size = 0;
|
||||
PPRpcContact pp = RpcGetContacts(&size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
WxContact_t contact;
|
||||
contact.wxId = GetWstringFromBstr(pp[i]->wxId);
|
||||
contact.wxCode = GetWstringFromBstr(pp[i]->wxCode);
|
||||
contact.wxName = GetWstringFromBstr(pp[i]->wxName);
|
||||
contact.wxCountry = GetWstringFromBstr(pp[i]->wxCountry);
|
||||
contact.wxProvince = GetWstringFromBstr(pp[i]->wxProvince);
|
||||
contact.wxCity = GetWstringFromBstr(pp[i]->wxCity);
|
||||
contact.wxGender = GetWstringFromBstr(pp[i]->wxGender);
|
||||
|
||||
mContact.insert(make_pair(contact.wxId, contact));
|
||||
midl_user_free(pp[i]);
|
||||
}
|
||||
|
||||
if (pp) {
|
||||
midl_user_free(pp);
|
||||
}
|
||||
|
||||
return mContact;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> WxGetDbNames()
|
||||
{
|
||||
std::vector<std::wstring> vDbs;
|
||||
int size = 0;
|
||||
BSTR *pBstr = RpcGetDbNames(&size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
vDbs.push_back(GetWstringFromBstr(pBstr[i]));
|
||||
}
|
||||
|
||||
if (pBstr) {
|
||||
midl_user_free(pBstr);
|
||||
}
|
||||
|
||||
return vDbs;
|
||||
}
|
||||
|
||||
DbTableVector_t WxGetDbTables(wstring db)
|
||||
{
|
||||
DbTableVector_t vTables;
|
||||
int size = 0;
|
||||
PPRpcTables pp = RpcGetDbTables(db.c_str(), &size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
WxDbTable_t tbl;
|
||||
tbl.table = GetWstringFromBstr(pp[i]->table);
|
||||
tbl.sql = GetWstringFromBstr(pp[i]->sql);
|
||||
|
||||
vTables.push_back(tbl);
|
||||
midl_user_free(pp[i]);
|
||||
}
|
||||
|
||||
if (pp) {
|
||||
midl_user_free(pp);
|
||||
}
|
||||
|
||||
return vTables;
|
||||
}
|
||||
|
||||
SqlRetVector_t WxExecDbQuery(wstring db, wstring sql)
|
||||
{
|
||||
int row, col = 0;
|
||||
PPPRpcSqlResult ppp = RpcExecDbQuery(db.c_str(), sql.c_str(), &row, &col);
|
||||
vector<vector<WxSqlResult_t>> vvResults;
|
||||
for (int r = 0; r < row; r++) {
|
||||
vector<WxSqlResult_t> vResult;
|
||||
for (int c = 0; c < col; c++) {
|
||||
WxSqlResult_t result = { 0 };
|
||||
result.type = ppp[r][c]->type;
|
||||
result.column = GetWstringFromBstr(ppp[r][c]->column);
|
||||
result.content = GetBytesFromBstr(ppp[r][c]->content);
|
||||
vResult.push_back(result);
|
||||
midl_user_free(ppp[r][c]);
|
||||
}
|
||||
vvResults.push_back(vResult);
|
||||
midl_user_free(ppp[r]);
|
||||
}
|
||||
if (ppp) {
|
||||
midl_user_free(ppp);
|
||||
}
|
||||
|
||||
return vvResults;
|
||||
}
|
||||
|
||||
BOOL WxAcceptNewFriend(wstring v3, wstring v4) { return AcceptNewFriend(v3.c_str(), v4.c_str()); }
|
14
SDK/sdk.def
14
SDK/sdk.def
@ -1,14 +0,0 @@
|
||||
EXPORTS
|
||||
WxInitSDK
|
||||
WxDestroySDK
|
||||
WxEnableRecvMsg
|
||||
WxDisableRecvMsg
|
||||
WxSendTextMsg
|
||||
WxGetSelfWxid
|
||||
WxGetMsgTypes
|
||||
WxSendImageMsg
|
||||
WxGetContacts
|
||||
WxGetDbNames
|
||||
WxGetDbTables
|
||||
WxExecDbQuery
|
||||
WxAcceptNewFriend
|
60
SDK/sdk.h
60
SDK/sdk.h
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef struct WxMessage {
|
||||
int self; // 是否自己发的消息:0=否,1=是
|
||||
int type; // 消息类型
|
||||
int source; // 消息来源:0=好友消息,1=群消息
|
||||
wstring id; // 消息ID
|
||||
wstring xml; // 群其他消息
|
||||
wstring wxId; // 发送人微信ID
|
||||
wstring roomId; // 群ID
|
||||
wstring content; // 消息内容,MAC版最大:16384,即16KB
|
||||
} WxMessage_t;
|
||||
|
||||
typedef struct WxContact {
|
||||
wstring wxId; // 微信ID
|
||||
wstring wxCode; // 微信号
|
||||
wstring wxName; // 微信昵称
|
||||
wstring wxCountry; // 国家
|
||||
wstring wxProvince; // 省/州
|
||||
wstring wxCity; // 城市
|
||||
wstring wxGender; // 性别
|
||||
} WxContact_t;
|
||||
|
||||
typedef struct WxDbTable {
|
||||
wstring table; // 表名
|
||||
wstring sql; // 建表 SQL
|
||||
} WxDbTable_t;
|
||||
|
||||
typedef struct WxSqlResult {
|
||||
int type;
|
||||
wstring column;
|
||||
string content;
|
||||
} WxSqlResult_t;
|
||||
|
||||
typedef map<int, wstring> MsgTypesMap_t;
|
||||
typedef map<wstring, WxContact_t> ContactMap_t;
|
||||
typedef vector<WxDbTable_t> DbTableVector_t;
|
||||
typedef vector<vector<WxSqlResult_t>> SqlRetVector_t;
|
||||
|
||||
int WxInitSDK();
|
||||
int WxDestroySDK();
|
||||
int WxEnableRecvMsg(const std::function<int(WxMessage_t)> &onMsg);
|
||||
int WxDisableRecvMsg();
|
||||
int WxSendTextMsg(wstring wxid, wstring msg, wstring vAtWxids);
|
||||
int WxSendImageMsg(wstring wxid, wstring path);
|
||||
wstring WxGetSelfWxid();
|
||||
ContactMap_t WxGetContacts();
|
||||
MsgTypesMap_t WxGetMsgTypes();
|
||||
vector<wstring> WxGetDbNames();
|
||||
DbTableVector_t WxGetDbTables(wstring db);
|
||||
SqlRetVector_t WxExecDbQuery(wstring db, wstring sql);
|
||||
BOOL WxAcceptNewFriend(wstring v3, wstring v4);
|
@ -1,170 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{121ad245-72fb-47a6-99d6-5427b74fa7b9}</ProjectGuid>
|
||||
<RootNamespace>SDKpy</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetExt>.pyd</TargetExt>
|
||||
<TargetName>wcferry</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;SDKPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;SDKPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)SDK;$(SolutionDir)Rpc;C:\Program Files (x86)\Python37-32\Include;C:\Projs\.pyenv\pybind11\lib\site-packages\pybind11\include;</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>C:\Program Files (x86)\Python37-32\libs;</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(OutDir)SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;SDKPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;SDKPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\SDK\sdk.h" />
|
||||
<ClInclude Include="..\SDK\util.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\SDK\util.cpp" />
|
||||
<ClCompile Include="sdkpy.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
@ -1,92 +0,0 @@
|
||||
#include <pybind11/functional.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "sdk.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
int WxEnableRecvMsgPy(const std::function<int(WxMessage_t)> &onMsg) { return WxEnableRecvMsg(onMsg); }
|
||||
|
||||
py::object WxExecDbQueryPy(std::wstring db, std::wstring sql)
|
||||
{
|
||||
py::list results;
|
||||
SqlRetVector_t cResults = WxExecDbQuery(db, sql);
|
||||
for (auto vv = cResults.begin(); vv != cResults.end(); vv++) {
|
||||
py::dict row;
|
||||
for (auto v = vv->begin(); v != vv->end(); v++) {
|
||||
switch (v->type) {
|
||||
case 1: { // SQLITE_INTEGER
|
||||
row[py::cast(v->column)] = stoi(v->content);
|
||||
break;
|
||||
}
|
||||
case 2: { // SQLITE_FLOAT
|
||||
row[py::cast(v->column)] = stof(v->content);
|
||||
break;
|
||||
}
|
||||
case 3: { // SQLITE_TEXT
|
||||
row[py::cast(v->column)] = String2Wstring(v->content);
|
||||
break;
|
||||
}
|
||||
case 4: { // SQLITE_BLOB
|
||||
row[py::cast(v->column)] = py::bytes(v->content.c_str(), v->content.size());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
row[py::cast(v->column)] = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
results.append(row);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(wcferry, m)
|
||||
{
|
||||
m.doc() = "SDK python API";
|
||||
|
||||
py::class_<WxMessage>(m, "WxMessage")
|
||||
.def_readonly("id", &WxMessage::id)
|
||||
.def_readonly("self", &WxMessage::self)
|
||||
.def_readonly("type", &WxMessage::type)
|
||||
.def_readonly("source", &WxMessage::source)
|
||||
.def_readonly("xml", &WxMessage::xml)
|
||||
.def_readonly("wxId", &WxMessage::wxId)
|
||||
.def_readonly("roomId", &WxMessage::roomId)
|
||||
.def_readonly("content", &WxMessage::content);
|
||||
|
||||
py::class_<WxContact>(m, "WxContact")
|
||||
.def_readonly("wxId", &WxContact::wxId)
|
||||
.def_readonly("wxCode", &WxContact::wxCode)
|
||||
.def_readonly("wxName", &WxContact::wxName)
|
||||
.def_readonly("wxCountry", &WxContact::wxCountry)
|
||||
.def_readonly("wxProvince", &WxContact::wxProvince)
|
||||
.def_readonly("wxCity", &WxContact::wxCity)
|
||||
.def_readonly("wxGender", &WxContact::wxGender);
|
||||
|
||||
py::class_<WxDbTable>(m, "WxDbTable").def_readonly("table", &WxDbTable::table).def_readonly("sql", &WxDbTable::sql);
|
||||
|
||||
m.def("WxInitSDK", &WxInitSDK, "Initiate SDK. Return 0 on success,else on failure.");
|
||||
m.def("WxEnableRecvMsg", &WxEnableRecvMsgPy, "Enable message receiving and provide a callback", py::arg("onMsg"));
|
||||
m.def("WxDisableRecvMsg", &WxDisableRecvMsg, "Disable message receiving.");
|
||||
m.def("WxSendTextMsg", &WxSendTextMsg, "Send text message.", py::arg("wxid"), py::arg("msg"),
|
||||
py::arg("atWxids") = L"");
|
||||
m.def("WxSendImageMsg", &WxSendImageMsg, "Send image message.", py::arg("wxid"), py::arg("path"));
|
||||
m.def("WxGetSelfWxid", &WxGetSelfWxid, "Get Self Wxid.");
|
||||
m.def("WxGetContacts", &WxGetContacts, py::return_value_policy::reference, "Get contact list.");
|
||||
m.def("WxGetMsgTypes", &WxGetMsgTypes, py::return_value_policy::reference, "Get message types.");
|
||||
m.def("WxGetDbNames", &WxGetDbNames, py::return_value_policy::reference, "Get DB names.");
|
||||
m.def("WxGetDbTables", &WxGetDbTables, py::return_value_policy::reference, "Get DB tables.", py::arg("db"));
|
||||
m.def("WxExecDbQuery", &WxExecDbQueryPy, py::return_value_policy::reference, "Get DB tables.", py::arg("db"),
|
||||
py::arg("sql"));
|
||||
m.def("WxAcceptNewFriend", &WxAcceptNewFriend, "Accept new friend application.", py::arg("v3"), py::arg("v4"));
|
||||
|
||||
#ifdef VERSION_INFO
|
||||
m.attr("__version__") = VERSION_INFO;
|
||||
#else
|
||||
m.attr("__version__") = "dev";
|
||||
#endif
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#include "framework.h"
|
||||
|
||||
#include "spy.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
//MessageBox(NULL, L"InitSpy", L"DllMain", 0);
|
||||
InitSpy();
|
||||
break;
|
||||
}
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH: {
|
||||
//MessageBox(NULL, L"DestroySpy", L"DllMain", 0);
|
||||
DestroySpy(hModule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rpc_h.h"
|
||||
|
||||
std::vector<std::wstring> GetDbNames();
|
||||
std::vector<RpcTables_t> GetDbTables(std::wstring db);
|
||||
std::vector<std::vector<RpcSqlResult_t>> ExecDbQuery(std::wstring db, std::wstring sql);
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <windows.h>
|
@ -1,39 +0,0 @@
|
||||
#include "get_contacts.h"
|
||||
#include "load_calls.h"
|
||||
#include "util.h"
|
||||
|
||||
extern WxCalls_t g_WxCalls;
|
||||
extern DWORD g_WeChatWinDllAddr;
|
||||
|
||||
std::vector<RpcContact_t> GetContacts()
|
||||
{
|
||||
int gender = 0;
|
||||
vector<RpcContact_t> vContacts;
|
||||
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 rpcContact = { 0 };
|
||||
rpcContact.wxId = GetBstrByAddress(node + g_WxCalls.contact.wxId);
|
||||
rpcContact.wxCode = GetBstrByAddress(node + g_WxCalls.contact.wxCode);
|
||||
rpcContact.wxName = GetBstrByAddress(node + g_WxCalls.contact.wxName);
|
||||
rpcContact.wxCountry = GetBstrByAddress(node + g_WxCalls.contact.wxCountry);
|
||||
rpcContact.wxProvince = GetBstrByAddress(node + g_WxCalls.contact.wxProvince);
|
||||
rpcContact.wxCity = GetBstrByAddress(node + g_WxCalls.contact.wxCity);
|
||||
|
||||
gender = GET_DWORD(node + g_WxCalls.contact.wxGender);
|
||||
if (gender == 1)
|
||||
rpcContact.wxGender = SysAllocString(L"男");
|
||||
else if (gender == 2)
|
||||
rpcContact.wxGender = SysAllocString(L"女");
|
||||
else
|
||||
rpcContact.wxGender = SysAllocString(L"未知");
|
||||
|
||||
vContacts.push_back(rpcContact);
|
||||
node = GET_DWORD(node);
|
||||
}
|
||||
|
||||
return vContacts;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "rpc_h.h"
|
||||
|
||||
std::vector<RpcContact_t> GetContacts();
|
@ -1,122 +0,0 @@
|
||||
#include "framework.h"
|
||||
|
||||
#include "load_calls.h"
|
||||
#include "receive_msg.h"
|
||||
#include "spy_types.h"
|
||||
#include "util.h"
|
||||
|
||||
extern HANDLE g_hEvent;
|
||||
extern RpcMessage_t *g_pMsg;
|
||||
extern WxCalls_t g_WxCalls;
|
||||
extern DWORD g_WeChatWinDllAddr;
|
||||
|
||||
MsgQueue_t g_MsgQueue;
|
||||
static BOOL isListened = false;
|
||||
static DWORD reg_buffer = 0;
|
||||
static DWORD recvMsgHookAddr = 0;
|
||||
static DWORD recvMsgCallAddr = 0;
|
||||
static DWORD recvMsgJumpBackAddr = 0;
|
||||
static CHAR recvMsgBackupCode[5] = { 0 };
|
||||
static RpcMessage_t lMsg = { 0 };
|
||||
|
||||
extern const MsgTypesMap_t g_WxMsgTypes = MsgTypesMap_t { { 0x01, L"文字" },
|
||||
{ 0x03, L"图片" },
|
||||
{ 0x22, L"语音" },
|
||||
{ 0x25, L"好友确认" },
|
||||
{ 0x28, L"POSSIBLEFRIEND_MSG" },
|
||||
{ 0x2A, L"名片" },
|
||||
{ 0x2B, L"视频" },
|
||||
{ 0x2F, L"石头剪刀布 | 表情图片" },
|
||||
{ 0x30, L"位置" },
|
||||
{ 0x31, L"共享实时位置、文件、转账、链接" },
|
||||
{ 0x32, L"VOIPMSG" },
|
||||
{ 0x33, L"微信初始化" },
|
||||
{ 0x34, L"VOIPNOTIFY" },
|
||||
{ 0x35, L"VOIPINVITE" },
|
||||
{ 0x3E, L"小视频" },
|
||||
{ 0x270F, L"SYSNOTICE" },
|
||||
{ 0x2710, L"红包、系统消息" },
|
||||
{ 0x2712, L"撤回消息" } };
|
||||
|
||||
void HookAddress(DWORD hookAddr, LPVOID funcAddr, CHAR recvMsgBackupCode[5])
|
||||
{
|
||||
//组装跳转数据
|
||||
BYTE jmpCode[5] = { 0 };
|
||||
jmpCode[0] = 0xE9;
|
||||
|
||||
//计算偏移
|
||||
*(DWORD *)&jmpCode[1] = (DWORD)funcAddr - hookAddr - 5;
|
||||
|
||||
// 备份原来的代码
|
||||
ReadProcessMemory(GetCurrentProcess(), (LPVOID)hookAddr, recvMsgBackupCode, 5, 0);
|
||||
// 写入新的代码
|
||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookAddr, jmpCode, 5, 0);
|
||||
}
|
||||
|
||||
void UnHookAddress(DWORD hookAddr, CHAR restoreCode[5])
|
||||
{
|
||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookAddr, restoreCode, 5, 0);
|
||||
}
|
||||
|
||||
void DispatchMsg(DWORD reg)
|
||||
{
|
||||
DWORD *p = (DWORD *)reg; //消息结构基址
|
||||
|
||||
memset(&lMsg, 0, sizeof(RpcMessage_t));
|
||||
|
||||
lMsg.type = GET_DWORD(*p + g_WxCalls.recvMsg.type);
|
||||
lMsg.self = GET_DWORD(*p + g_WxCalls.recvMsg.isSelf);
|
||||
lMsg.id = GetBstrByAddress(*p + g_WxCalls.recvMsg.msgId);
|
||||
lMsg.xml = GetBstrByAddress(*p + g_WxCalls.recvMsg.msgXml);
|
||||
|
||||
// 群里的系统消息,xml 为空
|
||||
if ((lMsg.xml == NULL) || (wcsstr(lMsg.xml, L"<membercount>") != NULL)) {
|
||||
lMsg.source = 1;
|
||||
lMsg.wxId = GetBstrByAddress(*p + g_WxCalls.recvMsg.wxId);
|
||||
lMsg.roomId = GetBstrByAddress(*p + g_WxCalls.recvMsg.roomId);
|
||||
} else {
|
||||
lMsg.wxId = GetBstrByAddress(*p + g_WxCalls.recvMsg.roomId);
|
||||
}
|
||||
lMsg.content = GetBstrByAddress(*p + g_WxCalls.recvMsg.content);
|
||||
g_MsgQueue.push(lMsg); // 发送消息
|
||||
SetEvent(g_hEvent); // 发送消息通知
|
||||
}
|
||||
|
||||
__declspec(naked) void RecieveMsgFunc()
|
||||
{
|
||||
__asm {
|
||||
mov reg_buffer, edi //把值复制出来
|
||||
}
|
||||
|
||||
DispatchMsg(reg_buffer);
|
||||
|
||||
__asm
|
||||
{
|
||||
call recvMsgCallAddr // 这个为被覆盖的call
|
||||
jmp recvMsgJumpBackAddr // 跳回被HOOK指令的下一条指令
|
||||
}
|
||||
}
|
||||
|
||||
void ListenMessage()
|
||||
{
|
||||
// MessageBox(NULL, L"ListenMessage", L"ListenMessage", 0);
|
||||
if (isListened || (g_WeChatWinDllAddr == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
recvMsgHookAddr = g_WeChatWinDllAddr + g_WxCalls.recvMsg.hook;
|
||||
recvMsgCallAddr = g_WeChatWinDllAddr + g_WxCalls.recvMsg.call;
|
||||
recvMsgJumpBackAddr = recvMsgHookAddr + 5;
|
||||
|
||||
HookAddress(recvMsgHookAddr, RecieveMsgFunc, recvMsgBackupCode);
|
||||
isListened = true;
|
||||
}
|
||||
|
||||
void UnListenMessage()
|
||||
{
|
||||
if (!isListened) {
|
||||
return;
|
||||
}
|
||||
UnHookAddress(recvMsgHookAddr, recvMsgBackupCode);
|
||||
isListened = false;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void ListenMessage();
|
||||
void UnListenMessage();
|
@ -1,285 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "accept_new_friend.h"
|
||||
#include "exec_sql.h"
|
||||
#include "get_contacts.h"
|
||||
#include "receive_msg.h"
|
||||
#include "rpc_h.h"
|
||||
#include "rpc_server.h"
|
||||
#include "sdk.h"
|
||||
#include "send_msg.h"
|
||||
#include "spy.h"
|
||||
#include "spy_types.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern int IsLogin(void); // Defined in spy.cpp
|
||||
extern wstring GetSelfWxid(); // Defined in spy.cpp
|
||||
extern HANDLE g_hEvent; // New message signal
|
||||
extern BOOL g_rpcKeepAlive; // Keep RPC server thread running
|
||||
extern MsgQueue_t g_MsgQueue; // Queue for message
|
||||
extern const MsgTypesMap_t g_WxMsgTypes; // Map of WeChat Message types
|
||||
|
||||
static BOOL listenMsgFlag = false;
|
||||
|
||||
RPC_STATUS CALLBACK SecurityCallback(RPC_IF_HANDLE /*hInterface*/, void * /*pBindingHandle*/)
|
||||
{
|
||||
return RPC_S_OK; // Always allow anyone.
|
||||
}
|
||||
|
||||
int RpcStartServer()
|
||||
{
|
||||
RPC_STATUS status;
|
||||
// Uses the protocol combined with the endpoint for receiving
|
||||
// remote procedure calls.
|
||||
status = RpcServerUseProtseqEp(reinterpret_cast<RPC_WSTR>((RPC_WSTR)L"ncalrpc"), // Use TCP/IP protocol
|
||||
RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Backlog queue length for TCP/IP.
|
||||
reinterpret_cast<RPC_WSTR>((RPC_WSTR)L"wcferry"), // TCP/IP port to use
|
||||
NULL // No security
|
||||
);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
// Registers the interface and auto listen
|
||||
// Equal to RpcServerRegisterIf + RpcServerListen
|
||||
status = RpcServerRegisterIf2(server_ISpy_v1_0_s_ifspec, // Interface to register.
|
||||
NULL, // Use the MIDL generated entry-point vector.
|
||||
NULL, // Use the MIDL generated entry-point vector.
|
||||
RPC_IF_ALLOW_LOCAL_ONLY | RPC_IF_AUTOLISTEN, // Forces use of security callback.
|
||||
RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Use default number of concurrent calls.
|
||||
(unsigned)-1, // Infinite max size of incoming data blocks.
|
||||
SecurityCallback); // Naive security callback.
|
||||
|
||||
while (g_rpcKeepAlive) {
|
||||
Sleep(1000); // 休眠,释放CPU
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RpcStopServer()
|
||||
{
|
||||
RPC_STATUS status;
|
||||
|
||||
UnListenMessage();
|
||||
|
||||
listenMsgFlag = false;
|
||||
g_rpcKeepAlive = false;
|
||||
status = RpcMgmtStopServerListening(NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = RpcServerUnregisterIf(server_ISpy_v1_0_s_ifspec, NULL, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
int server_IsLogin() { return IsLogin(); }
|
||||
|
||||
int server_GetSelfWxId(wchar_t wxid[20]) { return wcscpy_s(wxid, 20, GetSelfWxid().c_str()); }
|
||||
|
||||
void server_EnableReceiveMsg()
|
||||
{
|
||||
unsigned long ulCode = 0;
|
||||
ListenMessage();
|
||||
listenMsgFlag = true;
|
||||
RpcTryExcept
|
||||
{
|
||||
// 调用客户端的回调函数
|
||||
while (listenMsgFlag) {
|
||||
// 中断式,兼顾及时性和CPU使用率
|
||||
WaitForSingleObject(g_hEvent, INFINITE); // 等待消息
|
||||
while (!g_MsgQueue.empty()) {
|
||||
client_ReceiveMsg(g_MsgQueue.front()); // 调用接收消息回调
|
||||
g_MsgQueue.pop();
|
||||
}
|
||||
ResetEvent(g_hEvent);
|
||||
}
|
||||
}
|
||||
RpcExcept(1)
|
||||
{
|
||||
ulCode = RpcExceptionCode();
|
||||
printf("server_EnableReceiveMsg exception 0x%lx = %ld\n", ulCode, ulCode);
|
||||
}
|
||||
RpcEndExcept
|
||||
}
|
||||
|
||||
void server_DisableReceiveMsg()
|
||||
{
|
||||
UnListenMessage();
|
||||
listenMsgFlag = false;
|
||||
}
|
||||
|
||||
int server_SendTextMsg(const wchar_t *wxid, const wchar_t *msg, const wchar_t *atWxids)
|
||||
{
|
||||
SendTextMessage(wxid, msg, atWxids);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int server_SendImageMsg(const wchar_t *wxid, const wchar_t *path)
|
||||
{
|
||||
SendImageMessage(wxid, path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int server_GetMsgTypes(int *pNum, PPRpcIntBstrPair *msgTypes)
|
||||
{
|
||||
*pNum = g_WxMsgTypes.size();
|
||||
PPRpcIntBstrPair pp = (PPRpcIntBstrPair)midl_user_allocate(*pNum * sizeof(PRpcIntBstrPair));
|
||||
if (pp == NULL) {
|
||||
printf("server_GetMsgTypes midl_user_allocate Failed for pp\n");
|
||||
return -2;
|
||||
}
|
||||
int index = 0;
|
||||
for (auto it = g_WxMsgTypes.begin(); it != g_WxMsgTypes.end(); it++) {
|
||||
PRpcIntBstrPair p = (PRpcIntBstrPair)midl_user_allocate(sizeof(RpcIntBstrPair_t));
|
||||
if (p == NULL) {
|
||||
printf("server_GetMsgTypes midl_user_allocate Failed for p\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
p->key = it->first;
|
||||
p->value = SysAllocString(it->second.c_str());
|
||||
pp[index++] = p;
|
||||
}
|
||||
|
||||
*msgTypes = pp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int server_GetContacts(int *pNum, PPRpcContact *contacts)
|
||||
{
|
||||
vector<RpcContact_t> vContacts = GetContacts();
|
||||
|
||||
*pNum = vContacts.size();
|
||||
PPRpcContact pp = (PPRpcContact)midl_user_allocate(*pNum * sizeof(PRpcContact));
|
||||
if (pp == NULL) {
|
||||
printf("server_GetMsgTypes midl_user_allocate Failed for pp\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (auto it = vContacts.begin(); it != vContacts.end(); it++) {
|
||||
PRpcContact p = (PRpcContact)midl_user_allocate(sizeof(RpcContact_t));
|
||||
if (p == NULL) {
|
||||
printf("server_GetMsgTypes midl_user_allocate Failed for p\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
p->wxId = it->wxId;
|
||||
p->wxCode = it->wxCode;
|
||||
p->wxName = it->wxName;
|
||||
p->wxCountry = it->wxCountry;
|
||||
p->wxProvince = it->wxProvince;
|
||||
p->wxCity = it->wxCity;
|
||||
p->wxGender = it->wxGender;
|
||||
|
||||
pp[index++] = p;
|
||||
}
|
||||
|
||||
*contacts = pp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int server_GetDbNames(int *pNum, BSTR **dbs)
|
||||
{
|
||||
vector<wstring> vDbs = GetDbNames();
|
||||
*pNum = vDbs.size();
|
||||
BSTR *pp = (BSTR *)midl_user_allocate(*pNum * sizeof(BSTR));
|
||||
if (pp == NULL) {
|
||||
printf("server_GetMsgTypes midl_user_allocate Failed for pp\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (auto it = vDbs.begin(); it != vDbs.end(); it++) {
|
||||
pp[index++] = GetBstrFromWstring(*it);
|
||||
}
|
||||
|
||||
*dbs = pp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int server_GetDbTables(const wchar_t *db, int *pNum, PPRpcTables *tbls)
|
||||
{
|
||||
vector<RpcTables_t> tables = GetDbTables(db);
|
||||
*pNum = tables.size();
|
||||
PPRpcTables pp = (PPRpcTables)midl_user_allocate(*pNum * sizeof(PRpcTables));
|
||||
if (pp == NULL) {
|
||||
printf("server_GetMsgTypes midl_user_allocate Failed for pp\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (auto it = tables.begin(); it != tables.end(); it++) {
|
||||
PRpcTables p = (PRpcTables)midl_user_allocate(sizeof(RpcTables_t));
|
||||
if (p == NULL) {
|
||||
printf("server_GetDbTables midl_user_allocate Failed for p\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
p->table = it->table;
|
||||
p->sql = it->sql;
|
||||
pp[index++] = p;
|
||||
}
|
||||
|
||||
*tbls = pp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int server_ExecDbQuery(const wchar_t *db, const wchar_t *sql, int *pRow, int *pCol, PPPRpcSqlResult *ret)
|
||||
{
|
||||
vector<vector<RpcSqlResult_t>> vvSqlResult = ExecDbQuery(db, sql);
|
||||
if (vvSqlResult.empty()) {
|
||||
*pRow = *pCol = 0;
|
||||
ret = NULL;
|
||||
return -1;
|
||||
}
|
||||
*pRow = vvSqlResult.size();
|
||||
*pCol = vvSqlResult[0].size();
|
||||
PPPRpcSqlResult ppp = (PPPRpcSqlResult)midl_user_allocate(*pRow * sizeof(PPRpcSqlResult));
|
||||
if (ppp == NULL) {
|
||||
printf("server_ExecDbQuery midl_user_allocate Failed for ppp\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (int r = 0; r < *pRow; r++) {
|
||||
PPRpcSqlResult pp = (PPRpcSqlResult)midl_user_allocate(*pCol * sizeof(PRpcSqlResult));
|
||||
if (pp == NULL) {
|
||||
midl_user_free(ppp);
|
||||
printf("server_ExecDbQuery midl_user_allocate Failed for pp\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (int c = 0; c < *pCol; c++) {
|
||||
PRpcSqlResult p = (PRpcSqlResult)midl_user_allocate(sizeof(RpcSqlResult_t));
|
||||
if (p == NULL) {
|
||||
midl_user_free(pp);
|
||||
printf("server_ExecDbQuery midl_user_allocate Failed for p\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
p->type = vvSqlResult[r][c].type;
|
||||
p->column = vvSqlResult[r][c].column;
|
||||
p->content = vvSqlResult[r][c].content;
|
||||
|
||||
pp[c] = p;
|
||||
}
|
||||
|
||||
ppp[r] = pp;
|
||||
}
|
||||
|
||||
*ret = ppp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL server_AcceptNewFriend(const wchar_t *v3, const wchar_t *v4) { return AcceptNewFriend(v3, v4); }
|
@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
int RpcStartServer();
|
||||
int RpcStopServer();
|
46
Spy/spy.cpp
46
Spy/spy.cpp
@ -1,46 +0,0 @@
|
||||
#include "load_calls.h"
|
||||
#include "rpc_server.h"
|
||||
#include "spy.h"
|
||||
#include "util.h"
|
||||
|
||||
HANDLE g_hEvent = NULL;
|
||||
BOOL g_rpcKeepAlive = false;
|
||||
WxCalls_t g_WxCalls = { 0 };
|
||||
DWORD g_WeChatWinDllAddr = 0;
|
||||
|
||||
void InitSpy()
|
||||
{
|
||||
wchar_t version[16] = { 0 };
|
||||
|
||||
g_WeChatWinDllAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll"); //获取wechatWin模块地址
|
||||
if (g_WeChatWinDllAddr == 0) {
|
||||
MessageBox(NULL, L"获取wechatWin.dll模块地址失败", L"错误", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetWeChatVersion(version)) { //获取微信版本
|
||||
MessageBox(NULL, L"获取微信版本失败", L"错误", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LoadCalls(version, &g_WxCalls) != 0) { //加载微信版本对应的Call地址
|
||||
MessageBox(NULL, L"不支持当前版本", L"错误", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
HANDLE rpcThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RpcStartServer, NULL, NULL, 0);
|
||||
if (rpcThread != 0) {
|
||||
CloseHandle(rpcThread);
|
||||
}
|
||||
}
|
||||
|
||||
void DestroySpy(HMODULE hModule)
|
||||
{
|
||||
RpcStopServer();
|
||||
FreeLibraryAndExitThread(hModule, 0);
|
||||
}
|
||||
|
||||
int IsLogin(void) { return (int)GET_DWORD(g_WeChatWinDllAddr + g_WxCalls.login); }
|
||||
|
||||
wstring GetSelfWxid() { return String2Wstring(GET_STRING(g_WeChatWinDllAddr + g_WxCalls.ui.wxid)); }
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -1,72 +1,36 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30503.244
|
||||
VisualStudioVersion = 16.0.32802.440
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Spy", "Spy\Spy.vcxproj", "{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spy", "spy\spy.vcxproj", "{4DE80B82-5F6A-4C4C-9D16-1574308110FA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDK", "SDK\SDK.vcxproj", "{707F2DCD-1001-42A7-B20E-B85B1BBAB228}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp", "cpp\cpp.vcxproj", "{2FC45612-A106-42D1-871F-1DE704095B2C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396} = {4A0A22F7-0B1C-4565-A443-C43FAC6C6396}
|
||||
{ABFCB647-137F-478B-A73E-F0B1E3ADC215} = {ABFCB647-137F-478B-A73E-F0B1E3ADC215}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "App", "App\App.vcxproj", "{44C1A579-22A7-4BA3-A618-45BD01600294}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdk", "sdk\sdk.vcxproj", "{ABFCB647-137F-478B-A73E-F0B1E3ADC215}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9} = {121AD245-72FB-47A6-99D6-5427B74FA7B9}
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228} = {707F2DCD-1001-42A7-B20E-B85B1BBAB228}
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396} = {4A0A22F7-0B1C-4565-A443-C43FAC6C6396}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDKpy", "SDKpy\SDKpy.vcxproj", "{121AD245-72FB-47A6-99D6-5427B74FA7B9}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228} = {707F2DCD-1001-42A7-B20E-B85B1BBAB228}
|
||||
{4DE80B82-5F6A-4C4C-9D16-1574308110FA} = {4DE80B82-5F6A-4C4C-9D16-1574308110FA}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Debug|x64.Build.0 = Debug|x64
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Debug|x86.Build.0 = Debug|Win32
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Release|x64.ActiveCfg = Release|x64
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Release|x64.Build.0 = Release|x64
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4A0A22F7-0B1C-4565-A443-C43FAC6C6396}.Release|x86.Build.0 = Release|Win32
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Debug|x64.Build.0 = Debug|x64
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Debug|x86.Build.0 = Debug|Win32
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Release|x64.ActiveCfg = Release|x64
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Release|x64.Build.0 = Release|x64
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Release|x86.ActiveCfg = Release|Win32
|
||||
{707F2DCD-1001-42A7-B20E-B85B1BBAB228}.Release|x86.Build.0 = Release|Win32
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Debug|x64.Build.0 = Debug|x64
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Debug|x86.Build.0 = Debug|Win32
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Release|x64.ActiveCfg = Release|x64
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Release|x64.Build.0 = Release|x64
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Release|x86.ActiveCfg = Release|Win32
|
||||
{44C1A579-22A7-4BA3-A618-45BD01600294}.Release|x86.Build.0 = Release|Win32
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Debug|x64.Build.0 = Debug|x64
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Debug|x86.Build.0 = Debug|Win32
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Release|x64.ActiveCfg = Release|x64
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Release|x64.Build.0 = Release|x64
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Release|x86.ActiveCfg = Release|Win32
|
||||
{121AD245-72FB-47A6-99D6-5427B74FA7B9}.Release|x86.Build.0 = Release|Win32
|
||||
{4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Release|x86.Build.0 = Release|Win32
|
||||
{2FC45612-A106-42D1-871F-1DE704095B2C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{2FC45612-A106-42D1-871F-1DE704095B2C}.Release|x86.Build.0 = Release|Win32
|
||||
{ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Release|x86.ActiveCfg = Release|Win32
|
||||
{ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {569E7E61-8FD2-43B5-8F34-C61D6417F905}
|
||||
SolutionGuid = {76A678AA-570C-4CB7-B58F-3B2C170ACAC0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -1,164 +1,172 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="app.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{44c1a579-22a7-4ba3-a618-45bd01600294}</ProjectGuid>
|
||||
<RootNamespace>App</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalOptions> /utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(OutDir)SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)SDK;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(OutDir)SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>md wx
|
||||
xcopy /y $(OutDir)Spy.dll $(OutDir)wx\
|
||||
xcopy /y $(OutDir)SDK.dll $(OutDir)wx\
|
||||
xcopy /y $(OutDir)wcferry.pyd $(OutDir)wx\
|
||||
xcopy /y $(OutDir)App.exe $(OutDir)wx\
|
||||
xcopy /y $(SolutionDir)App\App.py $(OutDir)wx\
|
||||
xcopy /y $(SolutionDir)App\test.jpg $(OutDir)wx\</Command>
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Message>Copy output</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{2fc45612-a106-42d1-871f-1de704095b2c}</ProjectGuid>
|
||||
<RootNamespace>cpp</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<VcpkgUseStatic>true</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)/spy;$(SolutionDir)/rpc;C:\Tools\vcpkg\installed\x86-windows-static\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(OutDir)sdk.lib;iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /y $(OutDir)cpp.exe $(OutDir)out\cpp</Command>
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Message>Copy files</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\proto\wcf.grpc.pb.h" />
|
||||
<ClInclude Include="..\proto\wcf.pb.h" />
|
||||
<ClInclude Include="..\sdk\sdk.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\proto\wcf.grpc.pb.cc" />
|
||||
<ClCompile Include="..\proto\wcf.pb.cc" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\proto\wcf.proto" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -1,36 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SDK\sdk.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SDK\util.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="sdkpy.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SDK\util.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="proto">
|
||||
<UniqueIdentifier>{0b2f5211-3264-4e9b-8f6d-9f2b51780156}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\proto\wcf.grpc.pb.h">
|
||||
<Filter>proto</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\proto\wcf.pb.h">
|
||||
<Filter>proto</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\sdk\sdk.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\proto\wcf.grpc.pb.cc">
|
||||
<Filter>proto</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\proto\wcf.pb.cc">
|
||||
<Filter>proto</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\proto\wcf.proto">
|
||||
<Filter>proto</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
525
cpp/main.cpp
Normal file
525
cpp/main.cpp
Normal file
@ -0,0 +1,525 @@
|
||||
/*
|
||||
RPC Client
|
||||
*/
|
||||
#pragma warning(disable : 4251)
|
||||
#pragma execution_character_set("utf-8")
|
||||
|
||||
#include <memory>
|
||||
#include <signal.h>
|
||||
#include <thread>
|
||||
|
||||
#include <grpcpp/grpcpp.h>
|
||||
|
||||
#include "../proto/wcf.grpc.pb.h"
|
||||
#include "../sdk/sdk.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using grpc::Channel;
|
||||
using grpc::ClientContext;
|
||||
using grpc::Status;
|
||||
|
||||
using wcf::Contact;
|
||||
using wcf::Contacts;
|
||||
using wcf::DbField;
|
||||
using wcf::DbNames;
|
||||
using wcf::DbQuery;
|
||||
using wcf::DbRow;
|
||||
using wcf::DbRows;
|
||||
using wcf::DbTable;
|
||||
using wcf::DbTables;
|
||||
using wcf::Empty;
|
||||
using wcf::ImageMsg;
|
||||
using wcf::MsgTypes;
|
||||
using wcf::Response;
|
||||
using wcf::String;
|
||||
using wcf::TextMsg;
|
||||
using wcf::Verification;
|
||||
using wcf::Wcf;
|
||||
using wcf::WxMsg;
|
||||
|
||||
class WcfClient
|
||||
{
|
||||
public:
|
||||
static WcfClient &Instance(string host_port)
|
||||
{
|
||||
static WcfClient instance(grpc::CreateChannel(host_port, grpc::InsecureChannelCredentials()));
|
||||
return instance;
|
||||
}
|
||||
|
||||
~WcfClient()
|
||||
{
|
||||
cout << "~WcfClient()" << endl;
|
||||
this->DisableRecvMsg();
|
||||
WxDestroySDK();
|
||||
}
|
||||
|
||||
int IsLogin()
|
||||
{
|
||||
Empty empty;
|
||||
Response rsp;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
stub_->async()->RpcIsLogin(&context, &empty, &rsp, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "IsLogin rpc failed." << endl;
|
||||
}
|
||||
|
||||
return rsp.status();
|
||||
}
|
||||
|
||||
string GetSelfWxid()
|
||||
{
|
||||
Empty empty;
|
||||
String rsp;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
stub_->async()->RpcGetSelfWxid(&context, &empty, &rsp, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "GetSelfWxid rpc failed." << endl;
|
||||
}
|
||||
|
||||
return rsp.str();
|
||||
}
|
||||
|
||||
void EnableRecvMsg(function<void(WxMsg &)> msg_handle_cb)
|
||||
{
|
||||
class Reader : public grpc::ClientReadReactor<WxMsg>
|
||||
{
|
||||
public:
|
||||
Reader(Wcf::Stub *stub, function<void(WxMsg &)> msg_handle_cb)
|
||||
: msg_handle_cb_(msg_handle_cb)
|
||||
{
|
||||
stub->async()->RpcEnableRecvMsg(&context_, &empty_, this);
|
||||
StartRead(&msg_);
|
||||
StartCall();
|
||||
}
|
||||
|
||||
void OnReadDone(bool ok) override
|
||||
{
|
||||
if (ok) {
|
||||
try {
|
||||
msg_handle_cb_(msg_);
|
||||
} catch (...) {
|
||||
cout << "OnMsg wrong..." << endl;
|
||||
}
|
||||
StartRead(&msg_);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDone(const Status &s) override
|
||||
{
|
||||
unique_lock<mutex> l(mu_);
|
||||
status_ = s;
|
||||
done_ = true;
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
Status Await()
|
||||
{
|
||||
unique_lock<mutex> l(mu_);
|
||||
cv_.wait(l, [this] { return done_; });
|
||||
return move(status_);
|
||||
}
|
||||
|
||||
private:
|
||||
Empty empty_;
|
||||
WxMsg msg_;
|
||||
ClientContext context_;
|
||||
|
||||
mutex mu_;
|
||||
Status status_;
|
||||
bool done_ = false;
|
||||
condition_variable cv_;
|
||||
|
||||
function<void(WxMsg &)> msg_handle_cb_;
|
||||
};
|
||||
|
||||
Reader reader(stub_.get(), msg_handle_cb);
|
||||
Status status = reader.Await();
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "GetMessage rpc failed." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int DisableRecvMsg()
|
||||
{
|
||||
Empty empty;
|
||||
Response rsp;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
stub_->async()->RpcDisableRecvMsg(&context, &empty, &rsp, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "DisableRecvMsg rpc failed." << endl;
|
||||
}
|
||||
|
||||
return rsp.status();
|
||||
}
|
||||
|
||||
int SendTextMsg(string msg, string receiver, string atusers)
|
||||
{
|
||||
Response rsp;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
TextMsg t_msg;
|
||||
t_msg.set_msg(msg);
|
||||
t_msg.set_receiver(receiver);
|
||||
t_msg.set_aters(atusers);
|
||||
|
||||
stub_->async()->RpcSendTextMsg(&context, &t_msg, &rsp, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "SendTextMsg rpc failed." << endl;
|
||||
rsp.set_status(-999); // TODO: Unify error code
|
||||
}
|
||||
|
||||
return rsp.status();
|
||||
}
|
||||
|
||||
int SendImageMsg(string path, string receiver)
|
||||
{
|
||||
Response rsp;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
ImageMsg i_msg;
|
||||
i_msg.set_path(path);
|
||||
i_msg.set_receiver(receiver);
|
||||
|
||||
stub_->async()->RpcSendImageMsg(&context, &i_msg, &rsp, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "SendImageMsg rpc failed." << endl;
|
||||
rsp.set_status(-999); // TODO: Unify error code
|
||||
}
|
||||
|
||||
return rsp.status();
|
||||
}
|
||||
|
||||
MsgTypes GetMsgTypes(void)
|
||||
{
|
||||
Empty empty;
|
||||
MsgTypes mt;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
stub_->async()->RpcGetMsgTypes(&context, &empty, &mt, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "GetMsgTypes rpc failed." << endl;
|
||||
}
|
||||
|
||||
return mt;
|
||||
}
|
||||
|
||||
Contacts GetContacts(void)
|
||||
{
|
||||
Empty empty;
|
||||
Contacts contacts;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
stub_->async()->RpcGetContacts(&context, &empty, &contacts, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "GetContacts rpc failed." << endl;
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
DbNames GetDbNames(void)
|
||||
{
|
||||
Empty empty;
|
||||
DbNames names;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
stub_->async()->RpcGetDbNames(&context, &empty, &names, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "GetDbNames rpc failed." << endl;
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
DbTables GetDbTables(string db)
|
||||
{
|
||||
DbTables tables;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
String s_db;
|
||||
s_db.set_str(db);
|
||||
|
||||
stub_->async()->RpcGetDbTables(&context, &s_db, &tables, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "GetDbTables rpc failed." << endl;
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
DbRows ExecDbQuery(string db, string sql)
|
||||
{
|
||||
DbRows rows;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
DbQuery query;
|
||||
query.set_db(db);
|
||||
query.set_sql(sql);
|
||||
|
||||
stub_->async()->RpcExecDbQuery(&context, &query, &rows, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "ExecDbQuery rpc failed." << endl;
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
int AcceptNewFriend(string v3, string v4)
|
||||
{
|
||||
Response rsp;
|
||||
ClientContext context;
|
||||
std::mutex mu;
|
||||
std::condition_variable cv;
|
||||
bool done = false;
|
||||
Status status;
|
||||
|
||||
Verification v;
|
||||
v.set_v3(v3);
|
||||
v.set_v4(v4);
|
||||
|
||||
stub_->async()->RpcAcceptNewFriend(&context, &v, &rsp, [&mu, &cv, &done, &status](Status s) {
|
||||
status = std::move(s);
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(mu);
|
||||
cv.wait(lock, [&done] { return done; });
|
||||
|
||||
if (!status.ok()) {
|
||||
cout << "ExecDbQuery rpc failed." << endl;
|
||||
rsp.set_status(-999); // TODO: Unify error code
|
||||
}
|
||||
|
||||
return rsp.status();
|
||||
}
|
||||
|
||||
private:
|
||||
unique_ptr<Wcf::Stub> stub_;
|
||||
WcfClient(shared_ptr<Channel> channel)
|
||||
: stub_(Wcf::NewStub(channel))
|
||||
{
|
||||
WxInitSDK();
|
||||
}
|
||||
};
|
||||
|
||||
int OnMsg(WxMsg msg)
|
||||
{
|
||||
cout << "Got Message: \n"
|
||||
<< msg.is_self() << ", " << msg.is_group() << ", " << msg.type() << ", " << msg.id() << ", " << msg.xml()
|
||||
<< ", " << msg.sender() << ", " << msg.roomid() << ", " << msg.content() << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
volatile sig_atomic_t gStop;
|
||||
void handler(int s)
|
||||
{
|
||||
cout << "Ctrl + C" << endl;
|
||||
gStop = 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
signal(SIGINT, handler);
|
||||
|
||||
WcfClient &client = WcfClient::Instance("localhost:10086");
|
||||
|
||||
cout << "IsLogin: " << client.IsLogin() << endl;
|
||||
cout << "Self Wxid: " << client.GetSelfWxid() << endl;
|
||||
|
||||
ret = client.SendTextMsg("来自CPP的消息!", "filehelper", "");
|
||||
cout << "SendTextMsg: " << ret << endl;
|
||||
|
||||
ret = client.SendImageMsg("TEQuant.jpeg", "filehelper");
|
||||
cout << "SendImageMsg: " << ret << endl;
|
||||
|
||||
MsgTypes mts = client.GetMsgTypes();
|
||||
cout << "GetMsgTypes: " << mts.types_size() << endl;
|
||||
map<int32_t, string> m(mts.types().begin(), mts.types().end());
|
||||
for (auto &[k, v] : m) {
|
||||
cout << k << ": " << v << endl;
|
||||
}
|
||||
|
||||
Contacts cnts = client.GetContacts();
|
||||
cout << "GetContacts: " << cnts.contacts().size() << endl;
|
||||
vector<Contact> vcnts(cnts.contacts().begin(), cnts.contacts().end());
|
||||
for (auto &c : vcnts) {
|
||||
string gender = "";
|
||||
if (c.gender() == 1) {
|
||||
gender = "男";
|
||||
} else if (c.gender() == 2) {
|
||||
gender = "女";
|
||||
}
|
||||
cout << c.wxid() << "\t" << c.code() << "\t" << c.name() << "\t" << c.country() << "\t" << c.province() << "\t"
|
||||
<< c.city() << "\t" << gender << endl;
|
||||
}
|
||||
|
||||
DbNames db = client.GetDbNames();
|
||||
cout << "GetDbNames: " << db.names().size() << endl;
|
||||
vector<string> dbs(db.names().begin(), db.names().end());
|
||||
for (auto &name : dbs) {
|
||||
cout << name << endl;
|
||||
}
|
||||
|
||||
DbTables tbls = client.GetDbTables("db");
|
||||
cout << "GetDbTables: " << tbls.tables().size() << endl;
|
||||
vector<DbTable> vtbls(tbls.tables().begin(), tbls.tables().end());
|
||||
for (auto &tbl : vtbls) {
|
||||
cout << tbl.name() << "\n" << tbl.sql() << endl;
|
||||
}
|
||||
|
||||
DbRows r = client.ExecDbQuery("MicroMsg.db", "SELECT * FROM Contact LIMIT 1;");
|
||||
cout << "ExecDbQuery: " << r.rows().size() << endl;
|
||||
vector<DbRow> vrows(r.rows().begin(), r.rows().end());
|
||||
for (auto &row : vrows) {
|
||||
vector<DbField> vfields(row.fields().begin(), row.fields().end());
|
||||
for (auto &field : vfields)
|
||||
cout << field.column() << "[" << field.type() << "]: " << field.content() << endl;
|
||||
}
|
||||
|
||||
// 需要正确的 v3、v4 才能调用
|
||||
// ret = client.AcceptNewFriend("v3", "v4");
|
||||
// cout << "AcceptNewFriend: " << ret << endl;
|
||||
|
||||
function<void(WxMsg &)> cb = OnMsg;
|
||||
thread t1 = thread([&]() { client.EnableRecvMsg(cb); });
|
||||
|
||||
while (!gStop) {
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
cout << "Cleanup" << endl;
|
||||
client.DisableRecvMsg();
|
||||
|
||||
system("pause");
|
||||
client.~WcfClient();
|
||||
|
||||
return 0;
|
||||
}
|
87
proto/wcf.proto
Normal file
87
proto/wcf.proto
Normal file
@ -0,0 +1,87 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package wcf;
|
||||
|
||||
service Wcf {
|
||||
rpc RpcIsLogin(Empty) returns (Response) {}
|
||||
rpc RpcGetSelfWxid(Empty) returns (String) {}
|
||||
rpc RpcEnableRecvMsg(Empty) returns (stream WxMsg) {}
|
||||
rpc RpcDisableRecvMsg(Empty) returns (Response) {}
|
||||
rpc RpcSendTextMsg(TextMsg) returns (Response) {}
|
||||
rpc RpcSendImageMsg(ImageMsg) returns (Response) {}
|
||||
rpc RpcGetMsgTypes(Empty) returns (MsgTypes) {}
|
||||
rpc RpcGetContacts(Empty) returns (Contacts) {}
|
||||
rpc RpcGetDbNames(Empty) returns (DbNames) {}
|
||||
rpc RpcGetDbTables(String) returns (DbTables) {}
|
||||
rpc RpcExecDbQuery(DbQuery) returns (DbRows) {}
|
||||
rpc RpcAcceptNewFriend(Verification) returns (Response) {}
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
|
||||
message WxMsg {
|
||||
bool is_self = 1; // 是否自己发送的
|
||||
bool is_group = 2; // 是否群消息
|
||||
int32 type = 3; // 消息类型
|
||||
string id = 4; // 消息 id
|
||||
string xml = 5; // 消息 xml
|
||||
string sender = 6; // 消息发送者
|
||||
string roomid = 7; // 群 id(如果是群消息的话)
|
||||
string content = 8; // 消息内容
|
||||
}
|
||||
|
||||
message Response {
|
||||
int32 status = 1; // 状态码
|
||||
}
|
||||
|
||||
message TextMsg {
|
||||
string msg = 1; // 要发送的消息内容
|
||||
string receiver = 2; // 消息接收人,当为群时可@
|
||||
string aters = 3; // 要@的人列表,逗号分隔
|
||||
}
|
||||
|
||||
message ImageMsg {
|
||||
string path = 1; // 要发送的图片的路径
|
||||
string receiver = 2; // 消息接收人
|
||||
}
|
||||
|
||||
message MsgTypes { map<int32, string> types = 1; }
|
||||
|
||||
message Contact {
|
||||
string wxid = 1; // 微信 id
|
||||
string code = 2; // 微信号
|
||||
string name = 3; // 微信昵称
|
||||
string country = 4; // 国家
|
||||
string province = 5; // 省/州
|
||||
string city = 6; // 城市
|
||||
int32 gender = 7; // 性别
|
||||
}
|
||||
message Contacts { repeated Contact contacts = 1; }
|
||||
|
||||
message DbNames { repeated string names = 1; }
|
||||
|
||||
message String { string str = 1; }
|
||||
|
||||
message DbTable {
|
||||
string name = 1; // 表名
|
||||
string sql = 2; // 建表 SQL
|
||||
}
|
||||
message DbTables { repeated DbTable tables = 1; }
|
||||
|
||||
message DbQuery {
|
||||
string db = 1; // 目标数据库
|
||||
string sql = 2; // 查询 SQL
|
||||
}
|
||||
|
||||
message DbField {
|
||||
int32 type = 1; // 字段类型
|
||||
string column = 2; // 字段名称
|
||||
bytes content = 3; // 字段内容
|
||||
}
|
||||
message DbRow { repeated DbField fields = 1; }
|
||||
message DbRows { repeated DbRow rows = 1; }
|
||||
|
||||
message Verification {
|
||||
string v3 = 1;
|
||||
string v4 = 2;
|
||||
}
|
26
python/README.MD
Normal file
26
python/README.MD
Normal file
@ -0,0 +1,26 @@
|
||||
# WeChatFerry Python 客户端
|
||||
代码于 `Python3.7` 环境开发。
|
||||
|
||||
## 配置环境
|
||||
```sh
|
||||
# 创建虚拟环境
|
||||
python -m venv .env
|
||||
# 激活虚拟环境
|
||||
source .env/Scripts/activate
|
||||
# 升级 pip
|
||||
pip install --upgrade pip
|
||||
# 安装依赖包
|
||||
pip install grpcio grpcio-tools
|
||||
```
|
||||
|
||||
## 运行
|
||||
```sh
|
||||
# 启动客户端
|
||||
python demo.py
|
||||
```
|
||||
|
||||
## 重新生成 gRPC 文件
|
||||
```sh
|
||||
cd wcf
|
||||
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I=../ wcf.proto
|
||||
```
|
49
python/demo.py
Normal file
49
python/demo.py
Normal file
@ -0,0 +1,49 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import signal
|
||||
from time import sleep
|
||||
|
||||
from wcf.client import Wcf
|
||||
|
||||
|
||||
def main():
|
||||
logging.info("Start demo...")
|
||||
wcf = Wcf()
|
||||
|
||||
def handler(sig, frame):
|
||||
wcf.cleanup()
|
||||
exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, handler)
|
||||
sleep(1) # Slow down
|
||||
print(f"Is Login: {True if wcf.is_login() else False}")
|
||||
print(f"SelfWxid: {wcf.get_self_wxid()}")
|
||||
|
||||
sleep(1)
|
||||
wcf.enable_recv_msg(print)
|
||||
# wcf.disable_recv_msg() # Call anytime when you don't want to receive messages
|
||||
|
||||
ret = wcf.send_text("Hello world.", "filehelper")
|
||||
print(f"send_text: {ret}")
|
||||
|
||||
ret = wcf.send_image("TEQuant.jpeg", "filehelper")
|
||||
print(f"send_image: {ret}")
|
||||
|
||||
print(f"Message types:\n{wcf.get_msg_types()}")
|
||||
print(f"Contacts:\n{wcf.get_contacts()}")
|
||||
|
||||
print(f"DBs:\n{wcf.get_dbs()}")
|
||||
print(f"Tables:\n{wcf.get_tables('db')}")
|
||||
print(f"Results:\n{wcf.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}")
|
||||
|
||||
# wcf.accept_new_friend("v3", "v4") # 需要真正的 V3、V4 信息
|
||||
|
||||
# Keep running to receive messages
|
||||
wcf.keep_running()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level='DEBUG')
|
||||
main()
|
202
python/wcf/client.py
Normal file
202
python/wcf/client.py
Normal file
@ -0,0 +1,202 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import atexit
|
||||
import ctypes
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
from typing import Any, List, Callable, Optional
|
||||
|
||||
import grpc
|
||||
|
||||
WCF_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, WCF_ROOT)
|
||||
import wcf_pb2 # noqa
|
||||
import wcf_pb2_grpc # noqa
|
||||
|
||||
|
||||
class Wcf():
|
||||
"""WeChatFerry, a tool to play WeChat."""
|
||||
class WxMsg():
|
||||
"""微信消息"""
|
||||
|
||||
def __init__(self, msg: wcf_pb2.WxMsg) -> None:
|
||||
self._is_self = msg.is_self
|
||||
self._is_group = msg.is_group
|
||||
self.type = msg.type
|
||||
self.id = msg.id
|
||||
self.xml = msg.xml
|
||||
self.sender = msg.sender
|
||||
self.roomid = msg.roomid
|
||||
self.content = msg.content
|
||||
|
||||
def __str__(self) -> str:
|
||||
s = f"{self.sender}[{self.roomid}]\t{self.id}-{self.type}-{self.xml.replace(chr(10), '').replace(chr(9),'')}\n"
|
||||
s += self.content
|
||||
return s
|
||||
|
||||
def from_self(self) -> bool:
|
||||
"""是否自己发的消息"""
|
||||
return self._is_self == 1
|
||||
|
||||
def from_group(self) -> bool:
|
||||
"""是否群聊消息"""
|
||||
return self._is_group
|
||||
|
||||
def is_at(self, wxid) -> bool:
|
||||
"""是否被@:群消息,在@名单里,并且不是@所有人"""
|
||||
return self.from_group() and re.findall(
|
||||
f"<atuserlist>.*({wxid}).*</atuserlist>", self.xml) and not re.findall(r"@(?:所有人|all)", self.xml)
|
||||
|
||||
def is_text(self) -> bool:
|
||||
"""是否文本消息"""
|
||||
return self.type == 1
|
||||
|
||||
def __init__(self, host_port: str = "localhost:10086") -> None:
|
||||
self._enable_recv_msg = False
|
||||
self.LOG = logging.getLogger("WCF")
|
||||
self._sdk = ctypes.cdll.LoadLibrary(f"{WCF_ROOT}/sdk.dll")
|
||||
if self._sdk.WxInitSDK() != 0:
|
||||
self.LOG.error("初始化失败!")
|
||||
return
|
||||
|
||||
self._channel = grpc.insecure_channel(host_port)
|
||||
self._stub = wcf_pb2_grpc.WcfStub(self._channel)
|
||||
atexit.register(self.disable_recv_msg) # 退出的时候停止消息接收,防止内存泄露
|
||||
self._is_running = True
|
||||
self.contacts = []
|
||||
self._SQL_TYPES = {1: int, 2: float, 3: lambda x: x.decode("utf-8"), 4: bytes, 5: lambda x: None}
|
||||
self.self_wxid = self.get_self_wxid()
|
||||
|
||||
def __del__(self) -> None:
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""停止 gRPC,关闭连接,回收资源"""
|
||||
if not self._is_running:
|
||||
return
|
||||
|
||||
self.disable_recv_msg()
|
||||
self._channel.close()
|
||||
self._sdk.WxDestroySDK()
|
||||
handle = self._sdk._handle
|
||||
del self._sdk
|
||||
ctypes.windll.kernel32.FreeLibrary(handle)
|
||||
self._is_running = False
|
||||
|
||||
def keep_running(self):
|
||||
"""阻塞进程,让 RPC 一直维持连接"""
|
||||
try:
|
||||
while True:
|
||||
sleep(1)
|
||||
except Exception as e:
|
||||
self.cleanup()
|
||||
|
||||
def is_login(self) -> int:
|
||||
"""是否已经登录"""
|
||||
rsp = self._stub.RpcIsLogin(wcf_pb2.Empty())
|
||||
return rsp.status
|
||||
|
||||
def get_self_wxid(self) -> str:
|
||||
"""获取登录账户的 wxid"""
|
||||
rsp = self._stub.RpcGetSelfWxid(wcf_pb2.Empty())
|
||||
return rsp.str
|
||||
|
||||
def _rpc_get_message(self, func):
|
||||
rsps = self._stub.RpcEnableRecvMsg(wcf_pb2.Empty())
|
||||
try:
|
||||
for rsp in rsps:
|
||||
func(self.WxMsg(rsp))
|
||||
except Exception as e:
|
||||
self.LOG.error(f"RpcEnableRecvMsg: {e}")
|
||||
finally:
|
||||
self.disable_recv_msg()
|
||||
|
||||
def enable_recv_msg(self, callback: Callable[[WxMsg], None] = None) -> bool:
|
||||
"""设置接收消息回调"""
|
||||
if self._enable_recv_msg:
|
||||
return True
|
||||
|
||||
if callback is None:
|
||||
return False
|
||||
|
||||
self._enable_recv_msg = True
|
||||
# 阻塞,把控制权交给用户
|
||||
# self._rpc_get_message(callback)
|
||||
|
||||
# 不阻塞,启动一个新的线程来接收消息
|
||||
Thread(target=self._rpc_get_message, name="GetMessage", args=(callback,), daemon=True).start()
|
||||
|
||||
return True
|
||||
|
||||
def disable_recv_msg(self) -> int:
|
||||
"""停止接收消息"""
|
||||
if not self._enable_recv_msg:
|
||||
return -1
|
||||
|
||||
rsp = self._stub.RpcDisableRecvMsg(wcf_pb2.Empty())
|
||||
if rsp.status == 0:
|
||||
self._enable_recv_msg = False
|
||||
|
||||
return rsp.status
|
||||
|
||||
def send_text(self, msg: str, receiver: str, aters: Optional[str] = "") -> int:
|
||||
"""发送文本消息"""
|
||||
rsp = self._stub.RpcSendTextMsg(wcf_pb2.TextMsg(msg=msg, receiver=receiver, aters=aters))
|
||||
return rsp.status
|
||||
|
||||
def send_image(self, path: str, receiver: str) -> int:
|
||||
"""发送图片"""
|
||||
rsp = self._stub.RpcSendImageMsg(wcf_pb2.ImageMsg(path=path, receiver=receiver))
|
||||
return rsp.status
|
||||
|
||||
def get_msg_types(self) -> dict:
|
||||
"""获取所有消息类型"""
|
||||
rsp = self._stub.RpcGetMsgTypes(wcf_pb2.Empty())
|
||||
return dict(sorted(dict(rsp.types).items()))
|
||||
|
||||
def get_contacts(self) -> List[dict]:
|
||||
"""获取完整通讯录"""
|
||||
rsp = self._stub.RpcGetContacts(wcf_pb2.Empty())
|
||||
for cnt in rsp.contacts:
|
||||
gender = ""
|
||||
if cnt.gender == 1:
|
||||
gender = "男"
|
||||
elif cnt.gender == 2:
|
||||
gender = "女"
|
||||
self.contacts.append({"wxid": cnt.wxid, "code": cnt.code, "name": cnt.name,
|
||||
"country": cnt.country, "province": cnt.province, "city": cnt.city, "gender": gender})
|
||||
return self.contacts
|
||||
|
||||
def get_dbs(self) -> List[str]:
|
||||
"""获取所有数据库"""
|
||||
rsp = self._stub.RpcGetDbNames(wcf_pb2.Empty())
|
||||
return rsp.names
|
||||
|
||||
def get_tables(self, db: str) -> List[dict]:
|
||||
"""获取 db 中所有表"""
|
||||
tables = []
|
||||
rsp = self._stub.RpcGetDbTables(wcf_pb2.String(str=db))
|
||||
for tbl in rsp.tables:
|
||||
tables.append({"name": tbl.name, "sql": tbl.sql.replace("\t", "")})
|
||||
return tables
|
||||
|
||||
def query_sql(self, db: str, sql: str) -> List[dict]:
|
||||
"""执行 SQL"""
|
||||
result = []
|
||||
rsp = self._stub.RpcExecDbQuery(wcf_pb2.DbQuery(db=db, sql=sql))
|
||||
for r in rsp.rows:
|
||||
row = {}
|
||||
for f in r.fields:
|
||||
row[f.column] = self._SQL_TYPES[f.type](f.content)
|
||||
result.append(row)
|
||||
return result
|
||||
|
||||
def accept_new_friend(self, v3: str, v4: str) -> int:
|
||||
"""通过好友验证"""
|
||||
rsp = self._stub.RpcAcceptNewFriend(wcf_pb2.Verification(v3=v3, v4=v4))
|
||||
return rsp.status
|
@ -21,8 +21,8 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{707f2dcd-1001-42a7-b20e-b85b1bbab228}</ProjectGuid>
|
||||
<RootNamespace>SDK</RootNamespace>
|
||||
<ProjectGuid>{abfcb647-137f-478b-a73e-f0b1e3adc215}</ProjectGuid>
|
||||
<RootNamespace>sdk</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
@ -82,16 +82,18 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<VcpkgUseStatic>true</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;SDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalOptions> /utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@ -109,8 +111,11 @@
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;SDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@ -119,8 +124,19 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>sdk.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /y $(OutDir)sdk.dll $(OutDir)out\cpp
|
||||
xcopy /y $(OutDir)sdk.dll $(OutDir)out\python\wcf
|
||||
xcopy /y $(SolutionDir)TEQuant.jpeg $(OutDir)out\cpp
|
||||
xcopy /y $(SolutionDir)TEQuant.jpeg $(OutDir)out\python
|
||||
xcopy /y $(SolutionDir)python\demo.py $(OutDir)out\python
|
||||
xcopy /y $(SolutionDir)python\README.MD $(OutDir)out\python
|
||||
xcopy /y $(SolutionDir)python\wcf\*.py $(OutDir)out\python\wcf</Command>
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Message>Copy files</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
@ -159,21 +175,18 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Rpc\rpc_h.h" />
|
||||
<ClInclude Include="..\spy\log.h" />
|
||||
<ClInclude Include="..\spy\util.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="injector.h" />
|
||||
<ClInclude Include="rpc_client.h" />
|
||||
<ClInclude Include="sdk.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Rpc\rpc_c.c" />
|
||||
<ClCompile Include="..\Rpc\rpc_memory.cpp" />
|
||||
<ClCompile Include="..\spy\log.cpp" />
|
||||
<ClCompile Include="..\spy\util.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="injector.cpp" />
|
||||
<ClCompile Include="rpc_client.cpp" />
|
||||
<ClCompile Include="sdk.cpp" />
|
||||
<ClCompile Include="util.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="sdk.def" />
|
@ -21,41 +21,32 @@
|
||||
<ClInclude Include="sdk.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Rpc\rpc_h.h">
|
||||
<ClInclude Include="..\spy\util.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util.h">
|
||||
<ClInclude Include="..\spy\log.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="injector.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpc_client.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\spy\log.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\spy\util.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sdk.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Rpc\rpc_c.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Rpc\rpc_memory.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="util.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="injector.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpc_client.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="sdk.def">
|
14
sdk/dllmain.cpp
Normal file
14
sdk/dllmain.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
// dllmain.cpp : 定义 DLL 应用程序的入口点。
|
||||
#include "framework.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
90
sdk/injector.cpp
Normal file
90
sdk/injector.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "injector.h"
|
||||
|
||||
HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase)
|
||||
{
|
||||
HANDLE hThread;
|
||||
SIZE_T cszDLL = (wcslen(dllPath) + 1) * sizeof(WCHAR);
|
||||
// 1. 打开目标进程
|
||||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
if (hProcess == NULL) {
|
||||
MessageBox(NULL, L"打开进程失败", L"InjectDll", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 2. 在目标进程的内存里开辟空间
|
||||
LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, cszDLL, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (pRemoteAddress == NULL) {
|
||||
MessageBox(NULL, L"DLL 路径写入失败", L"InjectDll", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 3. 把 dll 的路径写入到目标进程的内存空间中
|
||||
WriteProcessMemory(hProcess, pRemoteAddress, dllPath, cszDLL, NULL);
|
||||
|
||||
// 3. 创建一个远程线程,让目标进程调用 LoadLibrary
|
||||
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, 0, NULL);
|
||||
if (hThread == NULL) {
|
||||
MessageBox(NULL, L"LoadLibrary 调用失败", L"InjectDll", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WaitForSingleObject(hThread, -1);
|
||||
GetExitCodeThread(hThread, (LPDWORD)injectedBase);
|
||||
CloseHandle(hThread);
|
||||
VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);
|
||||
// CloseHandle(hProcess); // Close when exit
|
||||
|
||||
return hProcess;
|
||||
}
|
||||
|
||||
bool EjectDll(HANDLE process, HMODULE dllBase)
|
||||
{
|
||||
HANDLE hThread = NULL;
|
||||
|
||||
// 使目标进程调用 FreeLibrary,卸载 DLL
|
||||
hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, (LPVOID)dllBase, 0, NULL);
|
||||
if (hThread == NULL) {
|
||||
MessageBox(NULL, L"FreeLibrary 调用失败!", L"EjectDll", 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
CloseHandle(process);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *GetFuncAddr(LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName)
|
||||
{
|
||||
HMODULE hLoaded = LoadLibrary(dllPath);
|
||||
if (hLoaded == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *absAddr = GetProcAddress(hLoaded, funcName);
|
||||
DWORD offset = (DWORD)absAddr - (DWORD)hLoaded;
|
||||
|
||||
FreeLibrary(hLoaded);
|
||||
|
||||
return (void *)((DWORD)dllBase + offset);
|
||||
}
|
||||
|
||||
bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, DWORD *ret)
|
||||
{
|
||||
void *pFunc = GetFuncAddr(dllPath, dllBase, funcName);
|
||||
if (pFunc == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, NULL, 0, NULL);
|
||||
if (hThread == NULL) {
|
||||
return false;
|
||||
}
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
if (ret != NULL) {
|
||||
GetExitCodeThread(hThread, ret);
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
return true;
|
||||
}
|
7
sdk/injector.h
Normal file
7
sdk/injector.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase);
|
||||
bool EjectDll(HANDLE process, HMODULE dllBase);
|
||||
bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, DWORD *ret);
|
73
sdk/sdk.cpp
Normal file
73
sdk/sdk.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include "Shlwapi.h"
|
||||
#include "framework.h"
|
||||
#include <process.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include "injector.h"
|
||||
#include "log.h"
|
||||
#include "sdk.h"
|
||||
#include "util.h"
|
||||
|
||||
static DWORD wcPid = 0;
|
||||
static HANDLE wcProcess = NULL;
|
||||
static HMODULE spyBase = NULL;
|
||||
static WCHAR spyDllPath[MAX_PATH] = { 0 };
|
||||
|
||||
int WxInitSDK()
|
||||
{
|
||||
int status = 0;
|
||||
InitLogger();
|
||||
LOG_INFO("WxInitSDK.");
|
||||
GetModuleFileName(GetModuleHandle(WECHATSDKDLL), spyDllPath, MAX_PATH);
|
||||
PathRemoveFileSpec(spyDllPath);
|
||||
PathAppend(spyDllPath, WECHATINJECTDLL);
|
||||
|
||||
if (!PathFileExists(spyDllPath)) {
|
||||
LOG_ERROR("DLL does not exists.");
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
status = OpenWeChat(&wcPid);
|
||||
if (status != 0) {
|
||||
LOG_ERROR("OpenWeChat failed: {}.", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
Sleep(2000); // 等待微信打开
|
||||
wcProcess = InjectDll(wcPid, spyDllPath, &spyBase);
|
||||
if (wcProcess == NULL) {
|
||||
LOG_ERROR("Failed to Inject DLL into WeChat.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CallDllFunc(wcProcess, spyDllPath, spyBase, "InitSpy", NULL)) {
|
||||
LOG_ERROR("Failed to InitSpy.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!CallDllFunc(wcProcess, spyDllPath, spyBase, "IsLogin", (DWORD *)&status)) {
|
||||
LOG_ERROR("Failed to check login status.");
|
||||
return -1;
|
||||
}
|
||||
Sleep(1000);
|
||||
} while (status == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WxDestroySDK()
|
||||
{
|
||||
LOG_INFO("WxDestroySDK");
|
||||
if (!CallDllFunc(wcProcess, spyDllPath, spyBase, "CleanupSpy", NULL)) {
|
||||
LOG_ERROR("Failed to CleanupSpy.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!EjectDll(wcProcess, spyBase)) {
|
||||
LOG_ERROR("Failed to Eject DLL.");
|
||||
return -1; // TODO: Unify error codes
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
3
sdk/sdk.def
Normal file
3
sdk/sdk.def
Normal file
@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
WxInitSDK
|
||||
WxDestroySDK
|
@ -21,9 +21,11 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{4a0a22f7-0b1c-4565-a443-c43fac6c6396}</ProjectGuid>
|
||||
<RootNamespace>Spy</RootNamespace>
|
||||
<ProjectGuid>{4de80b82-5f6a-4c4c-9d16-1574308110fa}</ProjectGuid>
|
||||
<RootNamespace>spy</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
|
||||
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@ -75,6 +77,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
@ -82,26 +85,24 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<VcpkgUseStatic>true</VcpkgUseStatic>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;SPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc;$(SolutionDir)SDK;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions> /utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
<Midl>
|
||||
<AdditionalOptions>/app_config /prefix client "client_" server "server_" </AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
@ -112,9 +113,13 @@
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;SPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc;$(SolutionDir)SDK;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions> /utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)rpc;C:\Tools\vcpkg\installed\x86-windows-static\include</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderOutputFile />
|
||||
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@ -122,12 +127,28 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
<Midl>
|
||||
<AdditionalOptions>/prefix client "client_" server "server_" </AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)Rpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<PostBuildEvent>
|
||||
<Command>cd $(OutDir)
|
||||
md out\cpp
|
||||
md out\python\wcf
|
||||
xcopy /y $(OutDir)spy.dll $(OutDir)out\cpp
|
||||
xcopy /y $(OutDir)spy.dll $(OutDir)out\python\wcf
|
||||
xcopy /y $(SolutionDir)proto\wcf.proto $(OutDir)out\python</Command>
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Message>Copy spy.dll</Message>
|
||||
</PostBuildEvent>
|
||||
<PreBuildEvent>
|
||||
<Command>cd $(SolutionDir)proto
|
||||
protoc -I=. --cpp_out=. wcf.proto
|
||||
protoc -I=. --grpc_out=. --plugin=protoc-gen-grpc="C:\Tools\vcpkg\packages\grpc_x64-windows\tools\grpc\grpc_cpp_plugin.exe" wcf.proto</Command>
|
||||
</PreBuildEvent>
|
||||
<PreBuildEvent>
|
||||
<Message>gRPC</Message>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
@ -142,6 +163,7 @@
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@ -161,44 +183,43 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>spy.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Rpc\rpc_h.h" />
|
||||
<ClInclude Include="..\SDK\util.h" />
|
||||
<ClInclude Include="..\proto\wcf.grpc.pb.h" />
|
||||
<ClInclude Include="..\proto\wcf.pb.h" />
|
||||
<ClInclude Include="accept_new_friend.h" />
|
||||
<ClInclude Include="exec_sql.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="get_contacts.h" />
|
||||
<ClInclude Include="load_calls.h" />
|
||||
<ClInclude Include="log.h" />
|
||||
<ClInclude Include="receive_msg.h" />
|
||||
<ClInclude Include="rpc_server.h" />
|
||||
<ClInclude Include="send_msg.h" />
|
||||
<ClInclude Include="spy.h" />
|
||||
<ClInclude Include="spy_types.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Rpc\rpc_memory.cpp" />
|
||||
<ClCompile Include="..\Rpc\rpc_s.c" />
|
||||
<ClCompile Include="..\SDK\util.cpp" />
|
||||
<ClCompile Include="..\proto\wcf.grpc.pb.cc" />
|
||||
<ClCompile Include="..\proto\wcf.pb.cc" />
|
||||
<ClCompile Include="accept_new_friend.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="exec_sql.cpp" />
|
||||
<ClCompile Include="get_contacts.cpp" />
|
||||
<ClCompile Include="load_calls.cpp" />
|
||||
<ClCompile Include="log.cpp" />
|
||||
<ClCompile Include="receive_msg.cpp" />
|
||||
<ClCompile Include="rpc_server.cpp" />
|
||||
<ClCompile Include="send_msg.cpp" />
|
||||
<ClCompile Include="spy.cpp" />
|
||||
<ClCompile Include="util.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\Rpc\rpc.idl">
|
||||
<OutputDirectory Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)Rpc</OutputDirectory>
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/app_config /ms_ext /prefix client "client_" server "server_" </AdditionalOptions>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)Rpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OutputDirectory Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)Rpc</OutputDirectory>
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">/app_config /ms_ext /prefix client "client_" server "server_" %(AdditionalOptions)</AdditionalOptions>
|
||||
</Midl>
|
||||
<None Include="..\proto\wcf.proto" />
|
||||
<None Include="spy.def" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
@ -13,24 +13,30 @@
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Rpc">
|
||||
<UniqueIdentifier>{37eb8a5c-e792-4c10-a858-9abc84f79f80}</UniqueIdentifier>
|
||||
<Filter Include="proto">
|
||||
<UniqueIdentifier>{5da8b2ee-3201-441c-b42f-a420e9aad78b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Rpc\rpc_h.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpc_server.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="load_calls.h">
|
||||
<ClInclude Include="log.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SDK\util.h">
|
||||
<ClInclude Include="accept_new_friend.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="exec_sql.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="get_contacts.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="load_calls.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="receive_msg.h">
|
||||
@ -39,39 +45,42 @@
|
||||
<ClInclude Include="send_msg.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spy_types.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="get_contacts.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="exec_sql.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spy.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="accept_new_friend.h">
|
||||
<ClInclude Include="spy_types.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\proto\wcf.grpc.pb.h">
|
||||
<Filter>proto</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\proto\wcf.pb.h">
|
||||
<Filter>proto</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Rpc\rpc_s.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Rpc\rpc_memory.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpc_server.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="load_calls.cpp">
|
||||
<ClCompile Include="log.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SDK\util.cpp">
|
||||
<ClCompile Include="accept_new_friend.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="exec_sql.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="get_contacts.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="load_calls.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="receive_msg.cpp">
|
||||
@ -80,22 +89,25 @@
|
||||
<ClCompile Include="send_msg.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="get_contacts.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="exec_sql.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spy.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="accept_new_friend.cpp">
|
||||
<ClCompile Include="util.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\proto\wcf.grpc.pb.cc">
|
||||
<Filter>proto</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\proto\wcf.pb.cc">
|
||||
<Filter>proto</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\Rpc\rpc.idl">
|
||||
<Filter>Rpc</Filter>
|
||||
</Midl>
|
||||
<None Include="spy.def">
|
||||
<Filter>源文件</Filter>
|
||||
</None>
|
||||
<None Include="..\proto\wcf.proto">
|
||||
<Filter>proto</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,19 +1,18 @@
|
||||
// dllmain.cpp : 定义 DLL 应用程序的入口点。
|
||||
// dllmain.cpp : 定义 DLL 应用程序的入口点。
|
||||
#include "framework.h"
|
||||
|
||||
#include "sdk.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH: {
|
||||
WxDestroySDK(); // 默认退出时清理 SDK
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "spy.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
@ -48,7 +48,7 @@ using namespace std;
|
||||
extern WxCalls_t g_WxCalls;
|
||||
extern DWORD g_WeChatWinDllAddr;
|
||||
|
||||
typedef map<wstring, DWORD> dbMap_t;
|
||||
typedef map<string, DWORD> dbMap_t;
|
||||
static dbMap_t dbMap;
|
||||
|
||||
// 回调函数指针
|
||||
@ -72,16 +72,15 @@ typedef int(__cdecl *Sqlite3_finalize)(DWORD *);
|
||||
|
||||
static int cbGetTables(void *ret, int argc, char **argv, char **azColName)
|
||||
{
|
||||
vector<RpcTables_t> *p = (vector<RpcTables_t> *)ret;
|
||||
RpcTables_t tbl = { 0 };
|
||||
wcf::DbTables *tbls = (wcf::DbTables *)ret;
|
||||
wcf::DbTable *tbl = tbls->add_tables();
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(azColName[i], "name") == 0) {
|
||||
tbl.table = argv[i] ? GetBstrFromString(argv[i]) : NULL;
|
||||
tbl->set_name(argv[i] ? argv[i] : "");
|
||||
} else if (strcmp(azColName[i], "sql") == 0) {
|
||||
tbl.sql = argv[i] ? GetBstrFromString(argv[i]) : NULL;
|
||||
tbl->set_sql(argv[i] ? argv[i] : "");
|
||||
}
|
||||
}
|
||||
p->push_back(tbl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -96,7 +95,7 @@ dbMap_t GetDbHandles()
|
||||
DWORD sqlHandleEndAddr = *(DWORD *)(sqlHandleBaseAddr + g_WxCalls.sql.end);
|
||||
while (sqlHandleBeginAddr < sqlHandleEndAddr) {
|
||||
DWORD dwHandle = *(DWORD *)sqlHandleBeginAddr;
|
||||
wstring dbName = wstring((wchar_t *)(*(DWORD *)(dwHandle + g_WxCalls.sql.name)));
|
||||
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;
|
||||
@ -107,40 +106,37 @@ dbMap_t GetDbHandles()
|
||||
return dbMap;
|
||||
}
|
||||
|
||||
vector<wstring> GetDbNames()
|
||||
void GetDbNames(wcf::DbNames *names)
|
||||
{
|
||||
vector<wstring> vDbs;
|
||||
if (dbMap.empty()) {
|
||||
dbMap = GetDbHandles();
|
||||
}
|
||||
for (auto it = dbMap.begin(); it != dbMap.end(); it++) {
|
||||
vDbs.push_back(it->first);
|
||||
|
||||
for (auto &[k, v] : dbMap) {
|
||||
auto *name = names->add_names();
|
||||
name->assign(k);
|
||||
}
|
||||
return vDbs;
|
||||
}
|
||||
|
||||
vector<RpcTables_t> GetDbTables(wstring db)
|
||||
void GetDbTables(const string db, wcf::DbTables *tables)
|
||||
{
|
||||
vector<RpcTables_t> vTables;
|
||||
const char *sql = "select * from sqlite_master where type=\"table\";";
|
||||
Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(g_WeChatWinDllAddr + g_WxCalls.sql.exec);
|
||||
|
||||
if (dbMap.empty()) {
|
||||
dbMap = GetDbHandles();
|
||||
}
|
||||
|
||||
auto it = dbMap.find(db);
|
||||
if (it != dbMap.end()) {
|
||||
p_Sqlite3_exec(it->second, sql, (sqlite3_callback)cbGetTables, &vTables, 0);
|
||||
if (it == dbMap.end()) {
|
||||
return; // DB not found
|
||||
}
|
||||
|
||||
return vTables;
|
||||
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);
|
||||
|
||||
p_Sqlite3_exec(it->second, sql, (sqlite3_callback)cbGetTables, tables, 0);
|
||||
}
|
||||
|
||||
vector<vector<RpcSqlResult_t>> ExecDbQuery(wstring db, wstring sql)
|
||||
void ExecDbQuery(const string db, const string sql, wcf::DbRows *rows)
|
||||
{
|
||||
vector<vector<RpcSqlResult_t>> vvSqlResult;
|
||||
|
||||
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);
|
||||
@ -155,29 +151,23 @@ vector<vector<RpcSqlResult_t>> ExecDbQuery(wstring db, wstring sql)
|
||||
}
|
||||
|
||||
DWORD *stmt;
|
||||
int rc = func_prepare(dbMap[db], Wstring2String(sql).c_str(), -1, &stmt, 0);
|
||||
int rc = func_prepare(dbMap[db], sql.c_str(), -1, &stmt, 0);
|
||||
if (rc != SQLITE_OK) {
|
||||
return vvSqlResult;
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t buffer[128] = { 0 };
|
||||
while (func_step(stmt) == SQLITE_ROW) {
|
||||
vector<RpcSqlResult_t> vResult;
|
||||
int col_count = func_column_count(stmt);
|
||||
wcf::DbRow *row = rows->add_rows();
|
||||
int col_count = func_column_count(stmt);
|
||||
for (int i = 0; i < col_count; i++) {
|
||||
RpcSqlResult_t result = { 0 };
|
||||
result.type = func_column_type(stmt, i);
|
||||
result.column = GetBstrFromString(func_column_name(stmt, i));
|
||||
int length = func_column_bytes(stmt, i);
|
||||
const void *blob = func_column_blob(stmt, i);
|
||||
if (length && (result.type != 5)) {
|
||||
result.content = GetBstrFromByteArray((byte *)blob, length);
|
||||
wcf::DbField *field = row->add_fields();
|
||||
field->set_type(func_column_type(stmt, i));
|
||||
field->set_column(func_column_name(stmt, i));
|
||||
int length = func_column_bytes(stmt, i);
|
||||
const void *blob = func_column_blob(stmt, i);
|
||||
if (length && (field->type() != 5)) {
|
||||
field->set_content(string((char *)blob, length));
|
||||
}
|
||||
|
||||
vResult.push_back(result);
|
||||
}
|
||||
vvSqlResult.push_back(vResult);
|
||||
}
|
||||
|
||||
return vvSqlResult;
|
||||
}
|
10
spy/exec_sql.h
Normal file
10
spy/exec_sql.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../proto/wcf.grpc.pb.h"
|
||||
|
||||
void GetDbNames(wcf::DbNames *names);
|
||||
void GetDbTables(const std::string db, wcf::DbTables *tables);
|
||||
void ExecDbQuery(const std::string db, const std::string sql, wcf::DbRows *rows);
|
5
spy/framework.h
Normal file
5
spy/framework.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <windows.h>
|
31
spy/get_contacts.cpp
Normal file
31
spy/get_contacts.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#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;
|
||||
|
||||
bool GetContacts(wcf::Contacts *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) {
|
||||
wcf::Contact *cnt = contacts->add_contacts();
|
||||
cnt->set_wxid(GetStringByAddress(node + g_WxCalls.contact.wxId));
|
||||
cnt->set_code(GetStringByAddress(node + g_WxCalls.contact.wxCode));
|
||||
cnt->set_name(GetStringByAddress(node + g_WxCalls.contact.wxName));
|
||||
cnt->set_country(GetStringByAddress(node + g_WxCalls.contact.wxCountry));
|
||||
cnt->set_province(GetStringByAddress(node + g_WxCalls.contact.wxProvince));
|
||||
cnt->set_city(GetStringByAddress(node + g_WxCalls.contact.wxCity));
|
||||
cnt->set_gender(GET_DWORD(node + g_WxCalls.contact.wxGender));
|
||||
|
||||
node = GET_DWORD(node);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
5
spy/get_contacts.h
Normal file
5
spy/get_contacts.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../proto/wcf.grpc.pb.h"
|
||||
|
||||
bool GetContacts(wcf::Contacts *contacts);
|
21
spy/log.cpp
Normal file
21
spy/log.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "log.h"
|
||||
|
||||
#define LOGGER_NAME "WCF"
|
||||
#define LOGGER_FILE_NAME "logs/wcf.txt"
|
||||
#define LOGGER_MAX_SIZE 1024 * 1024 * 10 // 10M
|
||||
#define LOGGER_MAX_FILES 10 // 10 files
|
||||
|
||||
void InitLogger()
|
||||
{
|
||||
static std::shared_ptr<spdlog::logger> gLogger = nullptr;
|
||||
if (gLogger != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
gLogger = spdlog::rotating_logger_mt(LOGGER_NAME, LOGGER_FILE_NAME, LOGGER_MAX_SIZE, LOGGER_MAX_FILES);
|
||||
// gLogger = spdlog::stdout_color_mt("console");
|
||||
|
||||
spdlog::set_default_logger(gLogger);
|
||||
gLogger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%s::%#::%!] %v");
|
||||
gLogger->flush_on(spdlog::level::info);
|
||||
}
|
14
spy/log.h
Normal file
14
spy/log.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
extern std::shared_ptr<spdlog::logger> gLogger;
|
||||
|
||||
#define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__);
|
||||
#define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__);
|
||||
#define LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__);
|
||||
#define LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__);
|
||||
|
||||
void InitLogger();
|
143
spy/receive_msg.cpp
Normal file
143
spy/receive_msg.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
#pragma execution_character_set("utf-8")
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
#include "load_calls.h"
|
||||
#include "receive_msg.h"
|
||||
#include "spy_types.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using wcf::MsgTypes;
|
||||
using wcf::WxMsg;
|
||||
|
||||
extern bool gIsListening;
|
||||
extern mutex gMutex;
|
||||
extern queue<WxMsg> gMsgQueue;
|
||||
extern condition_variable gCv;
|
||||
extern WxCalls_t g_WxCalls;
|
||||
extern DWORD g_WeChatWinDllAddr;
|
||||
|
||||
static DWORD reg_buffer = 0;
|
||||
static DWORD recvMsgHookAddr = 0;
|
||||
static DWORD recvMsgCallAddr = 0;
|
||||
static DWORD recvMsgJumpBackAddr = 0;
|
||||
static CHAR recvMsgBackupCode[5] = { 0 };
|
||||
|
||||
void GetMsgTypes(MsgTypes *types)
|
||||
{
|
||||
const map<int32_t, string> 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, "撤回消息" } };
|
||||
|
||||
for (auto &[k, v] : m) {
|
||||
(*(types->mutable_types()))[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
void HookAddress(DWORD hookAddr, LPVOID funcAddr, CHAR recvMsgBackupCode[5])
|
||||
{
|
||||
//组装跳转数据
|
||||
BYTE jmpCode[5] = { 0 };
|
||||
jmpCode[0] = 0xE9;
|
||||
|
||||
//计算偏移
|
||||
*(DWORD *)&jmpCode[1] = (DWORD)funcAddr - hookAddr - 5;
|
||||
|
||||
// 备份原来的代码
|
||||
ReadProcessMemory(GetCurrentProcess(), (LPVOID)hookAddr, recvMsgBackupCode, 5, 0);
|
||||
// 写入新的代码
|
||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookAddr, jmpCode, 5, 0);
|
||||
}
|
||||
|
||||
void UnHookAddress(DWORD hookAddr, CHAR restoreCode[5])
|
||||
{
|
||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookAddr, restoreCode, 5, 0);
|
||||
}
|
||||
|
||||
void DispatchMsg(DWORD reg)
|
||||
{
|
||||
WxMsg wxMsg;
|
||||
DWORD *p = (DWORD *)reg; //消息结构基址
|
||||
|
||||
wxMsg.set_type(GET_DWORD(*p + g_WxCalls.recvMsg.type));
|
||||
wxMsg.set_is_self(GET_DWORD(*p + g_WxCalls.recvMsg.isSelf));
|
||||
wxMsg.set_id(GetStringByAddress(*p + g_WxCalls.recvMsg.msgId));
|
||||
wxMsg.set_xml(GetStringByAddress(*p + g_WxCalls.recvMsg.msgXml));
|
||||
|
||||
// 群里的系统消息,xml 为空
|
||||
if ((wxMsg.xml().empty()) || (strstr(wxMsg.xml().c_str(), "<membercount>") != NULL)) {
|
||||
wxMsg.set_is_group(true);
|
||||
wxMsg.set_sender(GetStringByAddress(*p + g_WxCalls.recvMsg.wxId));
|
||||
wxMsg.set_roomid(GetStringByAddress(*p + g_WxCalls.recvMsg.roomId));
|
||||
} else {
|
||||
wxMsg.set_is_group(false);
|
||||
wxMsg.set_sender(GetStringByAddress(*p + g_WxCalls.recvMsg.roomId));
|
||||
}
|
||||
wxMsg.set_content(GetStringByAddress(*p + g_WxCalls.recvMsg.content));
|
||||
|
||||
// 推送到队列
|
||||
unique_lock<std::mutex> lock(gMutex);
|
||||
gMsgQueue.push(wxMsg);
|
||||
lock.unlock();
|
||||
|
||||
// 通知各方消息就绪
|
||||
gCv.notify_all();
|
||||
}
|
||||
|
||||
__declspec(naked) void RecieveMsgFunc()
|
||||
{
|
||||
__asm {
|
||||
mov reg_buffer, edi //把值复制出来
|
||||
}
|
||||
|
||||
DispatchMsg(reg_buffer);
|
||||
|
||||
__asm
|
||||
{
|
||||
call recvMsgCallAddr // 这个为被覆盖的call
|
||||
jmp recvMsgJumpBackAddr // 跳回被HOOK指令的下一条指令
|
||||
}
|
||||
}
|
||||
|
||||
void ListenMessage()
|
||||
{
|
||||
// MessageBox(NULL, L"ListenMessage", L"ListenMessage", 0);
|
||||
if (gIsListening || (g_WeChatWinDllAddr == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
recvMsgHookAddr = g_WeChatWinDllAddr + g_WxCalls.recvMsg.hook;
|
||||
recvMsgCallAddr = g_WeChatWinDllAddr + g_WxCalls.recvMsg.call;
|
||||
recvMsgJumpBackAddr = recvMsgHookAddr + 5;
|
||||
|
||||
HookAddress(recvMsgHookAddr, RecieveMsgFunc, recvMsgBackupCode);
|
||||
gIsListening = true;
|
||||
}
|
||||
|
||||
void UnListenMessage()
|
||||
{
|
||||
if (!gIsListening) {
|
||||
return;
|
||||
}
|
||||
UnHookAddress(recvMsgHookAddr, recvMsgBackupCode);
|
||||
gIsListening = false;
|
||||
}
|
7
spy/receive_msg.h
Normal file
7
spy/receive_msg.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../proto/wcf.grpc.pb.h"
|
||||
|
||||
void ListenMessage();
|
||||
void UnListenMessage();
|
||||
void GetMsgTypes(wcf::MsgTypes *types);
|
284
spy/rpc_server.cpp
Normal file
284
spy/rpc_server.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
#pragma warning(disable : 4251)
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <grpc/grpc.h>
|
||||
#include <grpcpp/security/server_credentials.h>
|
||||
#include <grpcpp/server.h>
|
||||
#include <grpcpp/server_builder.h>
|
||||
#include <grpcpp/server_context.h>
|
||||
|
||||
#include "../proto/wcf.grpc.pb.h"
|
||||
|
||||
#include "accept_new_friend.h"
|
||||
#include "exec_sql.h"
|
||||
#include "get_contacts.h"
|
||||
#include "log.h"
|
||||
#include "receive_msg.h"
|
||||
#include "rpc_server.h"
|
||||
#include "send_msg.h"
|
||||
#include "spy.h"
|
||||
#include "spy_types.h"
|
||||
#include "util.h"
|
||||
|
||||
extern int IsLogin(void); // Defined in spy.cpp
|
||||
extern std::string GetSelfWxid(); // Defined in spy.cpp
|
||||
|
||||
using namespace std;
|
||||
|
||||
using grpc::CallbackServerContext;
|
||||
using grpc::Server;
|
||||
using grpc::ServerBuilder;
|
||||
using grpc::ServerUnaryReactor;
|
||||
using grpc::ServerWriteReactor;
|
||||
using grpc::Status;
|
||||
|
||||
using wcf::Contacts;
|
||||
using wcf::DbField;
|
||||
using wcf::DbNames;
|
||||
using wcf::DbQuery;
|
||||
using wcf::DbRow;
|
||||
using wcf::DbRows;
|
||||
using wcf::DbTable;
|
||||
using wcf::DbTables;
|
||||
using wcf::Empty;
|
||||
using wcf::ImageMsg;
|
||||
using wcf::MsgTypes;
|
||||
using wcf::Response;
|
||||
using wcf::String;
|
||||
using wcf::TextMsg;
|
||||
using wcf::Verification;
|
||||
using wcf::Wcf;
|
||||
using wcf::WxMsg;
|
||||
|
||||
mutex gMutex;
|
||||
queue<WxMsg> gMsgQueue;
|
||||
condition_variable gCv;
|
||||
bool gIsListening;
|
||||
|
||||
class WcfImpl final : public Wcf::CallbackService
|
||||
{
|
||||
public:
|
||||
explicit WcfImpl() { }
|
||||
|
||||
ServerUnaryReactor *RpcIsLogin(CallbackServerContext *context, const Empty *empty, Response *rsp) override
|
||||
{
|
||||
int ret = IsLogin();
|
||||
rsp->set_status(ret);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcGetSelfWxid(CallbackServerContext *context, const Empty *empty, String *rsp) override
|
||||
{
|
||||
string wxid = GetSelfWxid();
|
||||
rsp->set_str(wxid);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerWriteReactor<WxMsg> *RpcEnableRecvMsg(CallbackServerContext *context, const Empty *empty) override
|
||||
{
|
||||
class Getter : public ServerWriteReactor<WxMsg>
|
||||
{
|
||||
public:
|
||||
Getter()
|
||||
{
|
||||
LOG_INFO("Enable message listening.")
|
||||
ListenMessage(); // gIsListening = true;
|
||||
NextWrite();
|
||||
}
|
||||
void OnDone() override { delete this; }
|
||||
void OnWriteDone(bool /*ok*/) override { NextWrite(); }
|
||||
|
||||
private:
|
||||
void NextWrite()
|
||||
{
|
||||
unique_lock<std::mutex> lock(gMutex);
|
||||
gCv.wait(lock, [&] { return !gMsgQueue.empty(); });
|
||||
tmp_ = gMsgQueue.front();
|
||||
gMsgQueue.pop();
|
||||
lock.unlock();
|
||||
if (gIsListening) {
|
||||
StartWrite(&tmp_);
|
||||
} else {
|
||||
LOG_INFO("Disable message listening.")
|
||||
Finish(Status::OK); // 结束本次通信
|
||||
}
|
||||
}
|
||||
WxMsg tmp_; // 如果将它放到 NextWrite 内部,StartWrite 调用时可能已经出了作用域
|
||||
};
|
||||
|
||||
return new Getter();
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcDisableRecvMsg(CallbackServerContext *context, const Empty *empty, Response *rsp) override
|
||||
{
|
||||
if (gIsListening) {
|
||||
UnListenMessage(); // gIsListening = false;
|
||||
// 发送消息,触发 NextWrite 的 Finish
|
||||
WxMsg wxMsg;
|
||||
unique_lock<std::mutex> lock(gMutex);
|
||||
gMsgQueue.push(wxMsg);
|
||||
lock.unlock();
|
||||
gCv.notify_all();
|
||||
}
|
||||
|
||||
rsp->set_status(0);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcSendTextMsg(CallbackServerContext *context, const TextMsg *msg, Response *rsp) override
|
||||
{
|
||||
wstring wswxid = String2Wstring(msg->receiver());
|
||||
wstring wsmsg = String2Wstring(msg->msg());
|
||||
wstring wsatusers = String2Wstring(msg->aters());
|
||||
|
||||
SendTextMessage(wswxid.c_str(), wsmsg.c_str(), wsatusers.c_str());
|
||||
rsp->set_status(0);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcSendImageMsg(CallbackServerContext *context, const ImageMsg *msg, Response *rsp) override
|
||||
{
|
||||
wstring wswxid = String2Wstring(msg->receiver());
|
||||
wstring wspath = String2Wstring(msg->path());
|
||||
|
||||
SendImageMessage(wswxid.c_str(), wspath.c_str());
|
||||
rsp->set_status(0);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcGetMsgTypes(CallbackServerContext *context, const Empty *empty, MsgTypes *rsp) override
|
||||
{
|
||||
GetMsgTypes(rsp);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcGetContacts(CallbackServerContext *context, const Empty *empty, Contacts *rsp) override
|
||||
{
|
||||
bool ret = GetContacts(rsp);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
if (ret) {
|
||||
reactor->Finish(Status::OK);
|
||||
} else {
|
||||
reactor->Finish(Status::CANCELLED);
|
||||
}
|
||||
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcGetDbNames(CallbackServerContext *context, const Empty *empty, DbNames *rsp) override
|
||||
{
|
||||
GetDbNames(rsp);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcGetDbTables(CallbackServerContext *context, const String *db, DbTables *rsp) override
|
||||
{
|
||||
GetDbTables(db->str(), rsp);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcExecDbQuery(CallbackServerContext *context, const DbQuery *query, DbRows *rsp) override
|
||||
{
|
||||
ExecDbQuery(query->db(), query->sql(), rsp);
|
||||
auto *reactor = context->DefaultReactor();
|
||||
reactor->Finish(Status::OK);
|
||||
|
||||
return reactor;
|
||||
}
|
||||
|
||||
ServerUnaryReactor *RpcAcceptNewFriend(CallbackServerContext *context, const Verification *v,
|
||||
Response *rsp) override
|
||||
{
|
||||
bool ret = AcceptNewFriend(String2Wstring(v->v3()), String2Wstring(v->v4()));
|
||||
auto *reactor = context->DefaultReactor();
|
||||
if (ret) {
|
||||
rsp->set_status(0);
|
||||
reactor->Finish(Status::OK);
|
||||
} else {
|
||||
LOG_ERROR("AcceptNewFriend failed.")
|
||||
rsp->set_status(-1); // TODO: Unify error code
|
||||
reactor->Finish(Status::CANCELLED);
|
||||
}
|
||||
|
||||
return reactor;
|
||||
}
|
||||
};
|
||||
|
||||
static DWORD lThreadId = 0;
|
||||
static bool lIsRunning = false;
|
||||
static ServerBuilder lBuilder;
|
||||
|
||||
static unique_ptr<Server> &GetServer()
|
||||
{
|
||||
static unique_ptr<Server> server(lBuilder.BuildAndStart());
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
static int RunServer()
|
||||
{
|
||||
string server_address("localhost:10086");
|
||||
WcfImpl service;
|
||||
|
||||
lBuilder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
||||
lBuilder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 2000);
|
||||
lBuilder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 3000);
|
||||
lBuilder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
|
||||
lBuilder.RegisterService(&service);
|
||||
|
||||
unique_ptr<Server> &server = GetServer();
|
||||
LOG_INFO("Server listening on {}", server_address);
|
||||
LOG_DEBUG("server: {}", fmt::ptr(server));
|
||||
lIsRunning = true;
|
||||
server->Wait();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RpcStartServer()
|
||||
{
|
||||
HANDLE rpcThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunServer, NULL, NULL, &lThreadId);
|
||||
if (rpcThread != 0) {
|
||||
CloseHandle(rpcThread);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RpcStopServer()
|
||||
{
|
||||
if (lIsRunning) {
|
||||
UnListenMessage();
|
||||
unique_ptr<Server> &server = GetServer();
|
||||
LOG_DEBUG("server: {}", fmt::ptr(server));
|
||||
server->Shutdown();
|
||||
LOG_INFO("Server stoped.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
10
spy/rpc_server.h
Normal file
10
spy/rpc_server.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef SPY_EXPORTS
|
||||
#define SPY_API __declspec(dllexport)
|
||||
#else
|
||||
#define SPY_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
int RpcStartServer();
|
||||
int RpcStopServer();
|
@ -3,11 +3,11 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "send_msg.h"
|
||||
#include "spy_types.h"
|
||||
|
||||
extern HANDLE g_hEvent;
|
||||
extern WxCalls_t g_WxCalls;
|
||||
extern RpcMessage_t *g_pMsg;
|
||||
extern DWORD g_WeChatWinDllAddr;
|
||||
|
||||
using namespace std;
|
41
spy/spy.cpp
Normal file
41
spy/spy.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "spy.h"
|
||||
#include "load_calls.h"
|
||||
#include "log.h"
|
||||
#include "rpc_server.h"
|
||||
#include "util.h"
|
||||
|
||||
WxCalls_t g_WxCalls = { 0 };
|
||||
DWORD g_WeChatWinDllAddr = 0;
|
||||
|
||||
void InitSpy()
|
||||
{
|
||||
wchar_t version[16] = { 0 };
|
||||
InitLogger();
|
||||
g_WeChatWinDllAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll"); //获取wechatWin模块地址
|
||||
if (g_WeChatWinDllAddr == 0) {
|
||||
LOG_ERROR("获取wechatWin.dll模块地址失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetWeChatVersion(version)) { //获取微信版本
|
||||
LOG_ERROR("获取微信版本失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (LoadCalls(version, &g_WxCalls) != 0) { //加载微信版本对应的Call地址
|
||||
LOG_ERROR("不支持当前版本");
|
||||
return;
|
||||
}
|
||||
|
||||
RpcStartServer();
|
||||
}
|
||||
|
||||
void CleanupSpy()
|
||||
{
|
||||
RpcStopServer();
|
||||
// FreeLibraryAndExitThread(hModule, 0);
|
||||
}
|
||||
|
||||
int IsLogin(void) { return (int)GET_DWORD(g_WeChatWinDllAddr + g_WxCalls.login); }
|
||||
|
||||
std::string GetSelfWxid() { return GET_STRING(g_WeChatWinDllAddr + g_WxCalls.ui.wxid); }
|
4
spy/spy.def
Normal file
4
spy/spy.def
Normal file
@ -0,0 +1,4 @@
|
||||
EXPORTS
|
||||
InitSpy
|
||||
CleanupSpy
|
||||
IsLogin
|
@ -3,4 +3,5 @@
|
||||
#include "framework.h"
|
||||
|
||||
void InitSpy();
|
||||
void DestroySpy(HMODULE hModule);
|
||||
void CleanupSpy();
|
||||
int IsLogin(void);
|
@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
#include <queue>
|
||||
|
||||
#include "rpc_h.h"
|
||||
#include "../proto/wcf.grpc.pb.h"
|
||||
|
||||
typedef struct UserInfoCall {
|
||||
DWORD wxid;
|
||||
@ -74,5 +72,3 @@ typedef struct TextStruct {
|
||||
DWORD capacity;
|
||||
char fill[8];
|
||||
} TextStruct_t;
|
||||
|
||||
typedef std::queue<RpcMessage_t> MsgQueue_t;
|
@ -1,312 +1,246 @@
|
||||
#include "Shlwapi.h"
|
||||
#include "framework.h"
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <string.h>
|
||||
#include <strsafe.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#pragma comment(lib, "shlwapi")
|
||||
#pragma comment(lib, "Version.lib")
|
||||
|
||||
using namespace std;
|
||||
|
||||
static wstring_convert<codecvt_utf8<wchar_t>, wchar_t> S_WS_Converter;
|
||||
|
||||
wstring String2Wstring(string s) { return S_WS_Converter.from_bytes(s); }
|
||||
string Wstring2String(wstring ws) { return S_WS_Converter.to_bytes(ws); }
|
||||
|
||||
static int GetWeChatPath(wchar_t *path)
|
||||
{
|
||||
int ret = -1;
|
||||
HKEY hKey = NULL;
|
||||
// HKEY_CURRENT_USER\Software\Tencent\WeChat InstallPath = xx
|
||||
if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, L"Software\\Tencent\\WeChat", &hKey)) {
|
||||
ret = GetLastError();
|
||||
return ret;
|
||||
}
|
||||
|
||||
DWORD Type = REG_SZ;
|
||||
DWORD cbData = MAX_PATH * sizeof(WCHAR);
|
||||
if (ERROR_SUCCESS != RegQueryValueEx(hKey, L"InstallPath", 0, &Type, (LPBYTE)path, &cbData)) {
|
||||
ret = GetLastError();
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
if (path != NULL) {
|
||||
PathAppend(path, WECHAREXE);
|
||||
}
|
||||
|
||||
__exit:
|
||||
if (hKey) {
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int GetWeChatWinDLLPath(wchar_t *path)
|
||||
{
|
||||
int ret = GetWeChatPath(path);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
PathRemoveFileSpecW(path);
|
||||
PathAppendW(path, WECHATWINDLL);
|
||||
if (!PathFileExists(path)) {
|
||||
// 微信从(大约)3.7开始,增加了一层版本目录: [3.7.0.29]
|
||||
PathRemoveFileSpec(path);
|
||||
_wfinddata_t findData;
|
||||
wstring dir = wstring(path) + L"\\[*.*";
|
||||
intptr_t handle = _wfindfirst(dir.c_str(), &findData);
|
||||
if (handle == -1) { // 检查是否成功
|
||||
return -1;
|
||||
}
|
||||
wstring dllPath = wstring(path) + L"\\" + findData.name;
|
||||
wcscpy_s(path, MAX_PATH, dllPath.c_str());
|
||||
PathAppend(path, WECHATWINDLL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool GetFileVersion(const wchar_t *filePath, wchar_t *version)
|
||||
{
|
||||
if (wcslen(filePath) > 0 && PathFileExists(filePath)) {
|
||||
VS_FIXEDFILEINFO *pVerInfo = NULL;
|
||||
DWORD dwTemp, dwSize;
|
||||
BYTE *pData = NULL;
|
||||
UINT uLen;
|
||||
|
||||
dwSize = GetFileVersionInfoSize(filePath, &dwTemp);
|
||||
if (dwSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pData = new BYTE[dwSize + 1];
|
||||
if (pData == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetFileVersionInfo(filePath, 0, dwSize, pData)) {
|
||||
delete[] pData;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VerQueryValue(pData, TEXT("\\"), (void **)&pVerInfo, &uLen)) {
|
||||
delete[] pData;
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD verMS = pVerInfo->dwFileVersionMS;
|
||||
DWORD verLS = pVerInfo->dwFileVersionLS;
|
||||
DWORD major = HIWORD(verMS);
|
||||
DWORD minor = LOWORD(verMS);
|
||||
DWORD build = HIWORD(verLS);
|
||||
DWORD revision = LOWORD(verLS);
|
||||
delete[] pData;
|
||||
|
||||
StringCbPrintf(version, 0x20, TEXT("%d.%d.%d.%d"), major, minor, build, revision);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int GetWeChatVersion(wchar_t *version)
|
||||
{
|
||||
WCHAR Path[MAX_PATH] = { 0 };
|
||||
|
||||
int ret = GetWeChatWinDLLPath(Path);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = GetFileVersion(Path, version);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD GetWeChatPid()
|
||||
{
|
||||
DWORD pid = 0;
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
|
||||
while (Process32Next(hSnapshot, &pe32)) {
|
||||
wstring strProcess = pe32.szExeFile;
|
||||
if (strProcess == WECHAREXE) {
|
||||
pid = pe32.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseHandle(hSnapshot);
|
||||
return pid;
|
||||
}
|
||||
|
||||
int OpenWeChat(DWORD *pid)
|
||||
{
|
||||
*pid = GetWeChatPid();
|
||||
if (*pid) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ret = -1;
|
||||
STARTUPINFO si = { sizeof(si) };
|
||||
WCHAR Path[MAX_PATH] = { 0 };
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
|
||||
ret = GetWeChatPath(Path);
|
||||
if (ERROR_SUCCESS != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!CreateProcess(NULL, Path, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
*pid = pi.dwProcessId;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int GetWstringByAddress(DWORD address, wchar_t *buffer, DWORD buffer_size)
|
||||
{
|
||||
DWORD strLength = GET_DWORD(address + 4);
|
||||
if (strLength == 0) {
|
||||
return 0;
|
||||
} else if (strLength > buffer_size) {
|
||||
strLength = buffer_size - 1;
|
||||
}
|
||||
|
||||
wmemcpy_s(buffer, strLength + 1, GET_WSTRING(address), strLength + 1);
|
||||
|
||||
return strLength;
|
||||
}
|
||||
|
||||
BSTR GetBstrByAddress(DWORD address)
|
||||
{
|
||||
wchar_t *p = GET_WSTRING(address);
|
||||
if (p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SysAllocStringLen(GET_WSTRING(address), GET_DWORD(address + 4));
|
||||
}
|
||||
|
||||
wstring GetWstringFromBstr(BSTR p)
|
||||
{
|
||||
wstring ws = L"";
|
||||
if (p != NULL) {
|
||||
ws = wstring(p);
|
||||
SysFreeString(p);
|
||||
}
|
||||
return ws;
|
||||
}
|
||||
|
||||
BSTR GetBstrFromString(const char *str)
|
||||
{
|
||||
int wslen = MultiByteToWideChar(CP_ACP, 0, str, strlen(str), 0, 0);
|
||||
BSTR bstr = SysAllocStringLen(0, wslen);
|
||||
MultiByteToWideChar(CP_ACP, 0, str, strlen(str), bstr, wslen);
|
||||
|
||||
return bstr;
|
||||
}
|
||||
|
||||
BSTR GetBstrFromWstring(wstring ws)
|
||||
{
|
||||
if (!ws.empty()) {
|
||||
return SysAllocStringLen(ws.data(), ws.size());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BSTR GetBstrFromStringBuffer(const char *str, int length)
|
||||
{
|
||||
int wslen = MultiByteToWideChar(CP_ACP, 0, str, length, 0, 0);
|
||||
BSTR bstr = SysAllocStringLen(0, wslen);
|
||||
MultiByteToWideChar(CP_ACP, 0, str, length, bstr, wslen);
|
||||
|
||||
return bstr;
|
||||
}
|
||||
|
||||
BSTR GetBstrFromByteArray(const byte *b, int len)
|
||||
{
|
||||
BSTR bstr = SysAllocStringLen(0, len);
|
||||
if (bstr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy((byte *)bstr, b, len);
|
||||
|
||||
return bstr;
|
||||
}
|
||||
|
||||
string GetBytesFromBstr(BSTR bstr)
|
||||
{
|
||||
string s = "";
|
||||
if (bstr) {
|
||||
int len = SysStringByteLen(bstr) / 2;
|
||||
char *tmp = new char[len];
|
||||
char *p = (char *)bstr;
|
||||
for (int i = 0; i < len; i++) {
|
||||
tmp[i] = p[i];
|
||||
}
|
||||
SysFreeString(bstr);
|
||||
s = string(tmp, len);
|
||||
delete[] tmp;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void GetRpcMessage(WxMessage_t *wxMsg, RpcMessage_t rpcMsg)
|
||||
{
|
||||
wxMsg->self = rpcMsg.self;
|
||||
wxMsg->type = rpcMsg.type;
|
||||
wxMsg->source = rpcMsg.source;
|
||||
wxMsg->id = GetWstringFromBstr(rpcMsg.id);
|
||||
wxMsg->xml = GetWstringFromBstr(rpcMsg.xml);
|
||||
wxMsg->wxId = GetWstringFromBstr(rpcMsg.wxId);
|
||||
wxMsg->roomId = GetWstringFromBstr(rpcMsg.roomId);
|
||||
wxMsg->content = GetWstringFromBstr(rpcMsg.content);
|
||||
}
|
||||
|
||||
DWORD GetMemoryIntByAddress(HANDLE hProcess, DWORD address)
|
||||
{
|
||||
DWORD value = 0;
|
||||
|
||||
unsigned char data[4] = { 0 };
|
||||
if (ReadProcessMemory(hProcess, (LPVOID)address, data, 4, 0)) {
|
||||
value = data[0] & 0xFF;
|
||||
value |= ((data[1] << 8) & 0xFF00);
|
||||
value |= ((data[2] << 16) & 0xFF0000);
|
||||
value |= ((data[3] << 24) & 0xFF000000);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
wstring GetUnicodeInfoByAddress(HANDLE hProcess, DWORD address)
|
||||
{
|
||||
wstring value = L"";
|
||||
|
||||
DWORD strAddress = GetMemoryIntByAddress(hProcess, address);
|
||||
DWORD strLen = GetMemoryIntByAddress(hProcess, address + 0x4);
|
||||
if (strLen > 500)
|
||||
return value;
|
||||
|
||||
wchar_t cValue[500] = { 0 };
|
||||
memset(cValue, 0, sizeof(cValue) / sizeof(wchar_t));
|
||||
if (ReadProcessMemory(hProcess, (LPVOID)strAddress, cValue, (strLen + 1) * 2, 0)) {
|
||||
value = wstring(cValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
#include "Shlwapi.h"
|
||||
#include "framework.h"
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <string.h>
|
||||
#include <strsafe.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#pragma comment(lib, "shlwapi")
|
||||
#pragma comment(lib, "Version.lib")
|
||||
|
||||
using namespace std;
|
||||
|
||||
wstring String2Wstring(string s)
|
||||
{
|
||||
if (s.empty())
|
||||
return wstring();
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &s[0], (int)s.size(), NULL, 0);
|
||||
wstring ws(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, &s[0], (int)s.size(), &ws[0], size_needed);
|
||||
return ws;
|
||||
}
|
||||
|
||||
string Wstring2String(wstring ws)
|
||||
{
|
||||
if (ws.empty())
|
||||
return string();
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), NULL, 0, NULL, NULL);
|
||||
string s(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), &s[0], size_needed, NULL, NULL);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int GetWeChatPath(wchar_t *path)
|
||||
{
|
||||
int ret = -1;
|
||||
HKEY hKey = NULL;
|
||||
// HKEY_CURRENT_USER\Software\Tencent\WeChat InstallPath = xx
|
||||
if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, L"Software\\Tencent\\WeChat", &hKey)) {
|
||||
ret = GetLastError();
|
||||
return ret;
|
||||
}
|
||||
|
||||
DWORD Type = REG_SZ;
|
||||
DWORD cbData = MAX_PATH * sizeof(WCHAR);
|
||||
if (ERROR_SUCCESS != RegQueryValueEx(hKey, L"InstallPath", 0, &Type, (LPBYTE)path, &cbData)) {
|
||||
ret = GetLastError();
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
if (path != NULL) {
|
||||
PathAppend(path, WECHAREXE);
|
||||
}
|
||||
|
||||
__exit:
|
||||
if (hKey) {
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int GetWeChatWinDLLPath(wchar_t *path)
|
||||
{
|
||||
int ret = GetWeChatPath(path);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
PathRemoveFileSpecW(path);
|
||||
PathAppendW(path, WECHATWINDLL);
|
||||
if (!PathFileExists(path)) {
|
||||
// 微信从(大约)3.7开始,增加了一层版本目录: [3.7.0.29]
|
||||
PathRemoveFileSpec(path);
|
||||
_wfinddata_t findData;
|
||||
wstring dir = wstring(path) + L"\\[*.*";
|
||||
intptr_t handle = _wfindfirst(dir.c_str(), &findData);
|
||||
if (handle == -1) { // 检查是否成功
|
||||
return -1;
|
||||
}
|
||||
wstring dllPath = wstring(path) + L"\\" + findData.name;
|
||||
wcscpy_s(path, MAX_PATH, dllPath.c_str());
|
||||
PathAppend(path, WECHATWINDLL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool GetFileVersion(const wchar_t *filePath, wchar_t *version)
|
||||
{
|
||||
if (wcslen(filePath) > 0 && PathFileExists(filePath)) {
|
||||
VS_FIXEDFILEINFO *pVerInfo = NULL;
|
||||
DWORD dwTemp, dwSize;
|
||||
BYTE *pData = NULL;
|
||||
UINT uLen;
|
||||
|
||||
dwSize = GetFileVersionInfoSize(filePath, &dwTemp);
|
||||
if (dwSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pData = new BYTE[dwSize + 1];
|
||||
if (pData == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetFileVersionInfo(filePath, 0, dwSize, pData)) {
|
||||
delete[] pData;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VerQueryValue(pData, TEXT("\\"), (void **)&pVerInfo, &uLen)) {
|
||||
delete[] pData;
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD verMS = pVerInfo->dwFileVersionMS;
|
||||
DWORD verLS = pVerInfo->dwFileVersionLS;
|
||||
DWORD major = HIWORD(verMS);
|
||||
DWORD minor = LOWORD(verMS);
|
||||
DWORD build = HIWORD(verLS);
|
||||
DWORD revision = LOWORD(verLS);
|
||||
delete[] pData;
|
||||
|
||||
StringCbPrintf(version, 0x20, TEXT("%d.%d.%d.%d"), major, minor, build, revision);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int GetWeChatVersion(wchar_t *version)
|
||||
{
|
||||
WCHAR Path[MAX_PATH] = { 0 };
|
||||
|
||||
int ret = GetWeChatWinDLLPath(Path);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = GetFileVersion(Path, version);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD GetWeChatPid()
|
||||
{
|
||||
DWORD pid = 0;
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
|
||||
while (Process32Next(hSnapshot, &pe32)) {
|
||||
wstring strProcess = pe32.szExeFile;
|
||||
if (strProcess == WECHAREXE) {
|
||||
pid = pe32.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseHandle(hSnapshot);
|
||||
return pid;
|
||||
}
|
||||
|
||||
int OpenWeChat(DWORD *pid)
|
||||
{
|
||||
*pid = GetWeChatPid();
|
||||
if (*pid) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ret = -1;
|
||||
STARTUPINFO si = { sizeof(si) };
|
||||
WCHAR Path[MAX_PATH] = { 0 };
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
|
||||
ret = GetWeChatPath(Path);
|
||||
if (ERROR_SUCCESS != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!CreateProcess(NULL, Path, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
*pid = pi.dwProcessId;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int GetWstringByAddress(DWORD address, wchar_t *buffer, DWORD buffer_size)
|
||||
{
|
||||
DWORD strLength = GET_DWORD(address + 4);
|
||||
if (strLength == 0) {
|
||||
return 0;
|
||||
} else if (strLength > buffer_size) {
|
||||
strLength = buffer_size - 1;
|
||||
}
|
||||
|
||||
wmemcpy_s(buffer, strLength + 1, GET_WSTRING(address), strLength + 1);
|
||||
|
||||
return strLength;
|
||||
}
|
||||
|
||||
string GetStringByAddress(DWORD address)
|
||||
{
|
||||
DWORD strLength = GET_DWORD(address + 4);
|
||||
return Wstring2String(wstring(GET_WSTRING(address), strLength));
|
||||
}
|
||||
|
||||
DWORD GetMemoryIntByAddress(HANDLE hProcess, DWORD address)
|
||||
{
|
||||
DWORD value = 0;
|
||||
|
||||
unsigned char data[4] = { 0 };
|
||||
if (ReadProcessMemory(hProcess, (LPVOID)address, data, 4, 0)) {
|
||||
value = data[0] & 0xFF;
|
||||
value |= ((data[1] << 8) & 0xFF00);
|
||||
value |= ((data[2] << 16) & 0xFF0000);
|
||||
value |= ((data[3] << 24) & 0xFF000000);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
wstring GetUnicodeInfoByAddress(HANDLE hProcess, DWORD address)
|
||||
{
|
||||
wstring value = L"";
|
||||
|
||||
DWORD strAddress = GetMemoryIntByAddress(hProcess, address);
|
||||
DWORD strLen = GetMemoryIntByAddress(hProcess, address + 0x4);
|
||||
if (strLen > 500)
|
||||
return value;
|
||||
|
||||
wchar_t cValue[500] = { 0 };
|
||||
memset(cValue, 0, sizeof(cValue) / sizeof(wchar_t));
|
||||
if (ReadProcessMemory(hProcess, (LPVOID)strAddress, cValue, (strLen + 1) * 2, 0)) {
|
||||
value = wstring(cValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
@ -1,31 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rpc_h.h"
|
||||
#include "sdk.h"
|
||||
|
||||
#define WECHAREXE L"WeChat.exe"
|
||||
#define WECHATWINDLL L"WeChatWin.dll"
|
||||
#define WECHATSDKDLL L"SDK.dll"
|
||||
#define WECHATINJECTDLL L"Spy.dll"
|
||||
|
||||
#define GET_DWORD(addr) ((DWORD) * (DWORD *)(addr))
|
||||
#define GET_STRING(addr) ((CHAR *)(*(DWORD *)(addr)))
|
||||
#define GET_WSTRING(addr) ((WCHAR *)(*(DWORD *)(addr)))
|
||||
|
||||
int OpenWeChat(DWORD *pid);
|
||||
int GetWeChatVersion(wchar_t *version);
|
||||
int GetWstringByAddress(DWORD address, wchar_t *buffer, DWORD buffer_size);
|
||||
void GetRpcMessage(WxMessage_t *wxMsg, RpcMessage_t rpcMsg);
|
||||
DWORD GetMemoryIntByAddress(HANDLE hProcess, DWORD address);
|
||||
BSTR GetBstrByAddress(DWORD address);
|
||||
BSTR GetBstrFromString(const char *str);
|
||||
BSTR GetBstrFromWstring(std::wstring ws);
|
||||
BSTR GetBstrFromByteArray(const byte *b, int len);
|
||||
BSTR GetBstrFromStringBuffer(const char *str, int length);
|
||||
std::string GetBytesFromBstr(BSTR bstr);
|
||||
std::wstring GetWstringFromBstr(BSTR bstr);
|
||||
std::wstring GetUnicodeInfoByAddress(HANDLE hProcess, DWORD address);
|
||||
std::wstring String2Wstring(std::string s);
|
||||
std::string Wstring2String(std::wstring ws);
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#define WECHAREXE L"WeChat.exe"
|
||||
#define WECHATWINDLL L"WeChatWin.dll"
|
||||
#define WECHATSDKDLL L"sdk.dll"
|
||||
#define WECHATINJECTDLL L"spy.dll"
|
||||
|
||||
#define GET_DWORD(addr) ((DWORD) * (DWORD *)(addr))
|
||||
#define GET_STRING(addr) ((CHAR *)(*(DWORD *)(addr)))
|
||||
#define GET_WSTRING(addr) ((WCHAR *)(*(DWORD *)(addr)))
|
||||
|
||||
int OpenWeChat(DWORD *pid);
|
||||
int GetWeChatVersion(wchar_t *version);
|
||||
int GetWstringByAddress(DWORD address, wchar_t *buffer, DWORD buffer_size);
|
||||
DWORD GetMemoryIntByAddress(HANDLE hProcess, DWORD address);
|
||||
std::wstring GetUnicodeInfoByAddress(HANDLE hProcess, DWORD address);
|
||||
std::wstring String2Wstring(std::string s);
|
||||
std::string Wstring2String(std::wstring ws);
|
||||
std::string GetStringByAddress(DWORD address);
|
Loading…
Reference in New Issue
Block a user