diff --git a/WeChatFerry/.editorconfig b/.editorconfig similarity index 72% rename from WeChatFerry/.editorconfig rename to .editorconfig index 46da929..8fcd9de 100644 --- a/WeChatFerry/.editorconfig +++ b/.editorconfig @@ -4,4 +4,7 @@ charset = utf-8-bom trim_trailing_whitespace = true insert_final_newline = true indent_style = space -indent_size = 4 \ No newline at end of file +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 \ No newline at end of file diff --git a/.github/workflows/Build-WeChatFerry.yml b/.github/workflows/Build-WeChatFerry.yml deleted file mode 100644 index 1305750..0000000 --- a/.github/workflows/Build-WeChatFerry.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Build-WeChatFerry - -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+" - -jobs: - build: - runs-on: windows-latest - - steps: - - name: 检出代码 - uses: actions/checkout@v4 - - - name: 获取版本号和微信版本号 - run: | - $version_full = (Select-String -Path "WeChatFerry/spy/spy.rc" -Pattern 'VALUE "FileVersion", "(.*)"').Matches.Groups[1].Value.Trim() - $wechat_version = (Select-String -Path "WeChatFerry/spy/spy.rc" -Pattern 'VALUE "ProductVersion", "(.*)"').Matches.Groups[1].Value.Trim() - $version = $version_full -replace '(\d+\.\d+\.\d+)\.\d+', '$1' - echo "version=$version" >> $env:GITHUB_ENV - echo "wechat_version=$wechat_version" >> $env:GITHUB_ENV - echo "Program Version: $version" - echo "WeChat Version: $wechat_version" - shell: pwsh - - - name: 设置 Visual Studio 2019 - uses: microsoft/setup-msbuild@v2 - with: - vs-version: "16.0" # 16.x 对应 Visual Studio 2019 - - - name: 设置 Python 3 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: 安装 Python 依赖项 - run: | - python -m pip install --upgrade pip - pip install grpcio-tools==1.48.2 - - - name: 设置缓存 - id: cache-vcpkg - uses: actions/cache@v4 - with: - path: | - C:/Tools/vcpkg - ${{ github.workspace }}/WeChatFerry/vcpkg_installed - key: vcpkg-${{ hashFiles('WeChatFerry/vcpkg.json') }} - restore-keys: | - vcpkg- - - - name: 安装 vcpkg 并初始化依赖项 - run: | - # 设置 vcpkg 目录 - if (!(Test-Path -Path 'C:/Tools')) { - New-Item -ItemType Directory -Force -Path 'C:/Tools' - } - cd C:/Tools - - # 克隆并引导 vcpkg - if (!(Test-Path -Path 'C:/Tools/vcpkg')) { - git clone https://github.com/microsoft/vcpkg - } - .\vcpkg\bootstrap-vcpkg.bat - - # 设置 VCPKG_ROOT 环境变量 - echo "VCPKG_ROOT=C:/Tools/vcpkg" >> $GITHUB_ENV - $env:VCPKG_ROOT = 'C:/Tools/vcpkg' - - # 返回到项目目录并安装依赖 - cd ${{ github.workspace }}/WeChatFerry - C:/Tools/vcpkg/vcpkg install --triplet x64-windows-static - - # 将 vcpkg 与 Visual Studio 集成 - C:/Tools/vcpkg/vcpkg integrate install - - - name: 解析并构建配置 - run: | - $configurations = "Release,Debug".Split(',') - foreach ($config in $configurations) { - Write-Host "Building configuration: $config" - msbuild WeChatFerry/WeChatFerry.sln ` - /p:Configuration=$config ` - /p:Platform="x64" ` - /p:VcpkgTriplet="x64-windows-static" ` - /p:VcpkgEnableManifest=true ` - /verbosity:minimal - } - shell: pwsh - - - name: 打包输出文件及下载 WeChat 安装包 - run: | - New-Item -ItemType Directory -Force -Path "WeChatFerry/tmp" - Compress-Archive -Path "WeChatFerry/Out/sdk.dll", "WeChatFerry/Out/spy.dll", "WeChatFerry/Out/spy_debug.dll", "WeChatFerry/Out/DISCLAIMER.md" -DestinationPath "WeChatFerry/tmp/v${{ env.version }}.zip" - Invoke-WebRequest -Uri "https://github.com/tom-snow/wechat-windows-versions/releases/download/v${{ env.wechat_version }}/WeChatSetup-${{ env.wechat_version }}.exe" -OutFile "WeChatFerry/tmp/WeChatSetup-${{ env.wechat_version }}.exe" - shell: pwsh - - - name: 列出预发布文件 - run: | - Get-ChildItem -Path "WeChatFerry/tmp" -Recurse - - - name: 发布固件到 Github Releases - uses: ncipollo/release-action@main - with: - name: v${{ env.version }} - tag: v${{ env.version }} - token: ${{ secrets.REPO_TOKEN }} - allowUpdates: true - artifacts: "WeChatFerry/tmp/*" - body: | - 程序版本:`v${{ env.version }}` - 配套微信版本:`${{ env.wechat_version }}` - [📖 Python 文档](https://wechatferry.readthedocs.io/) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml new file mode 100644 index 0000000..972c09a --- /dev/null +++ b/.github/workflows/build-ci.yml @@ -0,0 +1,86 @@ +name: Build CI + +on: + workflow_call: + +jobs: + build: + name: 编译校验 + runs-on: windows-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置 Visual Studio 2019 + uses: microsoft/setup-msbuild@v2 + with: + vs-version: "16.0" + + - name: 设置 Python 3 + uses: actions/setup-python@v5 + with: + python-version: "3.9" + + - name: 安装 Python 依赖项 + run: | + python -m pip install --upgrade pip + pip install grpcio-tools==1.48.2 + shell: pwsh + + - name: 设置 vcpkg 缓存 + id: cache-vcpkg + uses: actions/cache@v4 + with: + path: | + C:/Tools/vcpkg + ${{ github.workspace }}/WeChatFerry/vcpkg_installed + key: vcpkg-${{ hashFiles('WeChatFerry/vcpkg.json') }} + restore-keys: | + vcpkg- + + - name: Clone & bootstrap vcpkg (首次或缓存失效时) + if: steps.cache-vcpkg.outputs.cache-hit != 'true' + shell: pwsh + run: | + if (!(Test-Path 'C:/Tools')) { New-Item -ItemType Directory -Force -Path 'C:/Tools' | Out-Null } + cd C:/Tools + git clone --single-branch https://github.com/microsoft/vcpkg vcpkg + $retry = 0 + while ($retry -lt 3) { + try { .\vcpkg\bootstrap-vcpkg.bat -disableMetrics ; break } + catch { $retry++; if ($retry -ge 3) { throw }; Write-Host "bootstrap 失败,重试第 $retry 次..." ; Start-Sleep 15 } + } + + - name: 设置 VCPKG_ROOT + shell: pwsh + run: | + "VCPKG_ROOT=C:/Tools/vcpkg" | Out-File $Env:GITHUB_ENV -Encoding utf8 -Append + + - name: 安装/更新第三方依赖 + shell: pwsh + run: | + cd ${{ github.workspace }}/WeChatFerry + C:/Tools/vcpkg/vcpkg install --triplet x64-windows-static + C:/Tools/vcpkg/vcpkg integrate install + + - name: 解析并构建 Release/Debug + run: | + $configs = "Release","Debug" + foreach ($cfg in $configs) { + Write-Host "Building $cfg" + msbuild WeChatFerry/WeChatFerry.sln ` + /p:Configuration=$cfg ` + /p:Platform="x64" ` + /p:VcpkgTriplet="x64-windows-static" ` + /p:VcpkgEnableManifest=true ` + /verbosity:minimal + } + shell: pwsh + + - name: 上传 + uses: actions/upload-artifact@v4 + with: + name: wechatferry-binaries + path: | + WeChatFerry/Out/*.dll + WeChatFerry/Out/*.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e90dd0c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,14 @@ +name: CI + +on: + pull_request: + branches: + - master + +permissions: + contents: read + actions: write + +jobs: + build: + uses: ./.github/workflows/build-ci.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..30be5f6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Release + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + +permissions: + contents: write + actions: write + +jobs: + build: + uses: ./.github/workflows/build-ci.yml + + release: + name: 打包 & 发布 + needs: build + runs-on: windows-latest + if: ${{ github.event_name == 'push' }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 下载编译产物 + uses: actions/download-artifact@v4 + with: + name: wechatferry-binaries + path: tmp + + - name: 获取版本号和微信版本号 + shell: pwsh + run: | + $version_full = (Select-String -Path "WeChatFerry/spy/spy.rc" -Pattern 'VALUE "FileVersion", "(.*)"').Matches.Groups[1].Value.Trim() + $wechat_version = (Select-String -Path "WeChatFerry/spy/spy.rc" -Pattern 'VALUE "ProductVersion", "(.*)"').Matches.Groups[1].Value.Trim() + $version = $version_full -replace '(\d+\.\d+\.\d+)\.\d+', '$1' + echo "version=$version" >> $env:GITHUB_ENV + echo "wechat_version=$wechat_version" >> $env:GITHUB_ENV + echo "Program Version: $version" + echo "WeChat Version: $wechat_version" + + - name: 打包输出文件及下载 WeChat 安装包 + shell: pwsh + run: | + Compress-Archive ` + -Path "tmp/*" ` + -DestinationPath "tmp/v${{ env.version }}.zip" + + # 下载对应版本微信安装包 + Invoke-WebRequest ` + -Uri "https://github.com/tom-snow/wechat-windows-versions/releases/download/v${{ env.wechat_version }}/WeChatSetup-${{ env.wechat_version }}.exe" ` + -OutFile "tmp/WeChatSetup-${{ env.wechat_version }}.exe" + + - name: 发布到 GitHub Releases + uses: ncipollo/release-action@main + with: + name: v${{ env.version }} + tag: v${{ env.version }} + token: ${{ secrets.GITHUB_TOKEN }} + allowUpdates: true + artifacts: "tmp/*" + body: | + 程序版本:`v${{ env.version }}` + 配套微信版本:`${{ env.wechat_version }}` + [📖 Python 文档](https://wechatferry.readthedocs.io/) diff --git a/README.MD b/README.MD index 7956df7..25aa748 100644 --- a/README.MD +++ b/README.MD @@ -9,7 +9,7 @@ -|[📖 Python 文档](https://wechatferry.readthedocs.io/)|[📺 Python 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/UbzPuw3-2xZLEzUABXMEdw)| +|[📖 Python 文档](https://wechatferry.readthedocs.io/)|[📺 Python 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/c2JggTBlOP8fP9j-MlMAvg)| |:-:|:-:|:-:| 👉 [WeChatRobot🤖](https://github.com/lich0821/WeChatRobot),一个基于 WeChatFerry 的 Python 机器人示例。 @@ -32,7 +32,7 @@ * 发送图片消息 * 发送文件消息 * 发送卡片消息 -* 发送 XML +* 发送 XML 消息 * 发送 GIF 消息 * 拍一拍群友 * 转发消息 @@ -59,7 +59,7 @@ ## 感谢大佬们贡献代码 -![](https://contrib.rocks/image?repo=lich0821/WeChatFerry&columns=8) +![](https://contrib.rocks/image?repo=lich0821/WeChatFerry&columns=8&anon=1) ## 快速开始 ### Python @@ -207,9 +207,8 @@ WeChatFerry ## 版本更新 -### v39.4.4 - -* 实现通发送 XML 功能。 +### v39.5.2 +* 没有新功能
点击查看更多 @@ -221,6 +220,22 @@ WeChatFerry * `y` 是 `WeChatFerry` 的版本,从 0 开始 * `z` 是各客户端的版本,从 0 开始 +### v39.5.1 +* 修复邀请进群偶发失败 +* 修复获取 wxid 失败 + +### v39.5.0 + +* 适配 `3.9.12.51`。 + +### v39.4.5 + +* 修复发送 XML 功能。 + +### v39.4.4 + +* 实现发送 XML 功能。 + ### v39.4.3 * 实现通过好友申请功能。 diff --git a/WeChatFerry/CMakeLists.txt b/WeChatFerry/CMakeLists.txt new file mode 100644 index 0000000..0abeef3 --- /dev/null +++ b/WeChatFerry/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.15) + +# Common compiler flags +# 设置 C 语言标准为 C17 +set(CMAKE_C_STANDARD 17) +# 确保要求该标准为必须 +# set(CMAKE_C_STANDARD_REQUIRED ON) +# 默认情况下禁用 GNU 扩展 +set(CMAKE_C_EXTENSIONS OFF) +# 设置 C++ 语言标准为 C++17 +set(CMAKE_CXX_STANDARD 17) +# set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# 集成vcpkg +set(VCPKG_TARGET_TRIPLET "x64-mingw-static" CACHE STRING "Vcpkg target triplet") +set(VCPKG_HOST_TRIPLET "x64-mingw-static" CACHE STRING "Vcpkg host triplet") +set(VCPKG_MANIFEST_MODE ON CACHE BOOL "Enable manifest mode") + +if(DEFINED ENV{VCPKG_ROOT}) + set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file") +else() + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file") +endif() + +# include(FetchContent) +# include(ExternalProject) + +project(WeChatFerry LANGUAGES C CXX) + +add_compile_options( + -Wall + -Wextra + -fPIC +) + +add_link_options( + -static +) + +# Include directories +include_directories( + ${CMAKE_SOURCE_DIR}/com + ${CMAKE_SOURCE_DIR}/rpc + ${CMAKE_SOURCE_DIR}/rpc/nanopb + ${CMAKE_SOURCE_DIR}/rpc/proto + ${CMAKE_SOURCE_DIR}/sdk + ${CMAKE_SOURCE_DIR}/spy + ${CMAKE_SOURCE_DIR}/smc +) + +# Add subdirectories + +add_subdirectory(sdk) +add_subdirectory(spy) + diff --git a/WeChatFerry/sdk/framework.h b/WeChatFerry/com/framework.h similarity index 100% rename from WeChatFerry/sdk/framework.h rename to WeChatFerry/com/framework.h diff --git a/WeChatFerry/com/log.hpp b/WeChatFerry/com/log.hpp index 4a08c8b..a8fa8f8 100644 --- a/WeChatFerry/com/log.hpp +++ b/WeChatFerry/com/log.hpp @@ -13,6 +13,7 @@ #include #include #include +#include "framework.h" #define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) #define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index b82e82f..f5a8376 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -7,7 +7,6 @@ #include #include -#include "framework.h" #include #include @@ -64,8 +63,8 @@ static DWORD get_wechat_pid() HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return 0; - PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; - while (Process32Next(hSnapshot, &pe32)) { + PROCESSENTRY32W pe32 = { sizeof(PROCESSENTRY32W) }; + while (Process32NextW(hSnapshot, &pe32)) { if (pe32.szExeFile == s2w(WECHATEXE)) { pid = pe32.th32ProcessID; break; diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index 1998fbb..6e83b01 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -14,7 +14,7 @@ struct PortPath { char path[MAX_PATH]; }; -DWORD get_wechat_pid(); +static DWORD get_wechat_pid(); int open_wechat(DWORD &pid); std::string get_wechat_version(); uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr); diff --git a/WeChatFerry/rpc/proto/wcf.proto b/WeChatFerry/rpc/proto/wcf.proto index e965576..ad44e9e 100644 --- a/WeChatFerry/rpc/proto/wcf.proto +++ b/WeChatFerry/rpc/proto/wcf.proto @@ -36,6 +36,7 @@ enum Functions { FUNC_ADD_ROOM_MEMBERS = 0x70; FUNC_DEL_ROOM_MEMBERS = 0x71; FUNC_INV_ROOM_MEMBERS = 0x72; + FUNC_SHUTDOWN = 0xFF; } message Request diff --git a/WeChatFerry/rpc/tool/proto/nanopb_pb2.py b/WeChatFerry/rpc/tool/proto/nanopb_pb2.py index eea6b3e..5db3058 100644 --- a/WeChatFerry/rpc/tool/proto/nanopb_pb2.py +++ b/WeChatFerry/rpc/tool/proto/nanopb_pb2.py @@ -2,9 +2,11 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: nanopb.proto """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder +from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) @@ -16,8 +18,52 @@ from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'nanopb_pb2', globals()) +_FIELDTYPE = DESCRIPTOR.enum_types_by_name['FieldType'] +FieldType = enum_type_wrapper.EnumTypeWrapper(_FIELDTYPE) +_INTSIZE = DESCRIPTOR.enum_types_by_name['IntSize'] +IntSize = enum_type_wrapper.EnumTypeWrapper(_INTSIZE) +_TYPENAMEMANGLING = DESCRIPTOR.enum_types_by_name['TypenameMangling'] +TypenameMangling = enum_type_wrapper.EnumTypeWrapper(_TYPENAMEMANGLING) +_DESCRIPTORSIZE = DESCRIPTOR.enum_types_by_name['DescriptorSize'] +DescriptorSize = enum_type_wrapper.EnumTypeWrapper(_DESCRIPTORSIZE) +FT_DEFAULT = 0 +FT_CALLBACK = 1 +FT_POINTER = 4 +FT_STATIC = 2 +FT_IGNORE = 3 +FT_INLINE = 5 +IS_DEFAULT = 0 +IS_8 = 8 +IS_16 = 16 +IS_32 = 32 +IS_64 = 64 +M_NONE = 0 +M_STRIP_PACKAGE = 1 +M_FLATTEN = 2 +M_PACKAGE_INITIALS = 3 +DS_AUTO = 0 +DS_1 = 1 +DS_2 = 2 +DS_4 = 4 +DS_8 = 8 + +NANOPB_FILEOPT_FIELD_NUMBER = 1010 +nanopb_fileopt = DESCRIPTOR.extensions_by_name['nanopb_fileopt'] +NANOPB_MSGOPT_FIELD_NUMBER = 1010 +nanopb_msgopt = DESCRIPTOR.extensions_by_name['nanopb_msgopt'] +NANOPB_ENUMOPT_FIELD_NUMBER = 1010 +nanopb_enumopt = DESCRIPTOR.extensions_by_name['nanopb_enumopt'] +NANOPB_FIELD_NUMBER = 1010 +nanopb = DESCRIPTOR.extensions_by_name['nanopb'] + +_NANOPBOPTIONS = DESCRIPTOR.message_types_by_name['NanoPBOptions'] +NanoPBOptions = _reflection.GeneratedProtocolMessageType('NanoPBOptions', (_message.Message,), { + 'DESCRIPTOR' : _NANOPBOPTIONS, + '__module__' : 'nanopb_pb2' + # @@protoc_insertion_point(class_scope:NanoPBOptions) + }) +_sym_db.RegisterMessage(NanoPBOptions) + if _descriptor._USE_C_DESCRIPTORS == False: google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(nanopb_fileopt) google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(nanopb_msgopt) diff --git a/WeChatFerry/sdk/CMakeLists.txt b/WeChatFerry/sdk/CMakeLists.txt new file mode 100644 index 0000000..158a441 --- /dev/null +++ b/WeChatFerry/sdk/CMakeLists.txt @@ -0,0 +1,57 @@ +# SDK project - dynamic library +project(SDK LANGUAGES C CXX) + +find_package(spdlog REQUIRED) + +add_library(sdk SHARED + dllmain.cpp + injector.cpp + injector.h + sdk.cpp + sdk.h + sdk.def + + # Common files + ${CMAKE_SOURCE_DIR}/com/util.cpp + ${CMAKE_SOURCE_DIR}/com/util.h +) + +target_link_libraries(sdk PRIVATE + version + shlwapi + spdlog::spdlog + c++ +) + +# Set compiler definitions +target_compile_definitions(sdk PRIVATE + SDK_EXPORTS + _WINDOWS + _USRDLL +) + +# add_compile_options( +# # -Wall +# -fPIC +# # -fms-extensions +# ) +# Set output name for debug builds +set_target_properties(sdk PROPERTIES + DEBUG_POSTFIX "d" +) + +# # Post-build copy commands +# add_custom_command(TARGET sdk POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy +# $ +# ${CMAKE_SOURCE_DIR}/Out +# COMMAND ${CMAKE_COMMAND} -E copy +# $ +# ${CMAKE_SOURCE_DIR}/../clients/python/wcferry +# COMMAND ${CMAKE_COMMAND} -E copy +# ${CMAKE_SOURCE_DIR}/DISCLAIMER.md +# ${CMAKE_SOURCE_DIR}/Out +# COMMAND ${CMAKE_COMMAND} -E copy +# ${CMAKE_SOURCE_DIR}/DISCLAIMER.md +# ${CMAKE_SOURCE_DIR}/../clients/python/wcferry +# ) diff --git a/WeChatFerry/sdk/SDK.vcxproj b/WeChatFerry/sdk/SDK.vcxproj index cb0fd9d..054ecc1 100644 --- a/WeChatFerry/sdk/SDK.vcxproj +++ b/WeChatFerry/sdk/SDK.vcxproj @@ -86,7 +86,8 @@ Windows true false - sdk.def + + @@ -103,7 +104,8 @@ Windows true false - sdk.def + + @@ -130,7 +132,8 @@ true false false - sdk.def + + xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)Out @@ -142,7 +145,7 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry - + @@ -152,9 +155,6 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry - - - diff --git a/WeChatFerry/sdk/SDK.vcxproj.filters b/WeChatFerry/sdk/SDK.vcxproj.filters index 5adfeab..9c2e881 100644 --- a/WeChatFerry/sdk/SDK.vcxproj.filters +++ b/WeChatFerry/sdk/SDK.vcxproj.filters @@ -15,7 +15,7 @@ - + 头文件 @@ -42,9 +42,4 @@ 源文件 - - - 源文件 - - \ No newline at end of file diff --git a/WeChatFerry/sdk/injector.cpp b/WeChatFerry/sdk/injector.cpp index 913ba87..6eb935e 100644 --- a/WeChatFerry/sdk/injector.cpp +++ b/WeChatFerry/sdk/injector.cpp @@ -122,7 +122,7 @@ static uint64_t get_func_offset(const string &dll_path, const string &func_name) return 0; } - LPVOID absAddr = GetProcAddress(dll, func_name.c_str()); + LPVOID absAddr = reinterpret_cast(GetProcAddress(dll, func_name.c_str())); uint64_t offset = reinterpret_cast(absAddr) - reinterpret_cast(dll); FreeLibrary(dll); diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index 8363414..831a855 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -21,9 +21,16 @@ static HANDLE wcProcess = NULL; static HMODULE spyBase = NULL; static std::string spyDllPath; +//区分MSVC和MinGW +#ifdef _MSC_VER constexpr char WCFSDKDLL[] = "sdk.dll"; constexpr char WCFSPYDLL[] = "spy.dll"; constexpr char WCFSPYDLL_DEBUG[] = "spy_debug.dll"; +#else +constexpr char WCFSDKDLL[] = "libsdk.dll"; +constexpr char WCFSPYDLL[] = "libspy.dll"; +constexpr char WCFSPYDLL_DEBUG[] = "libspyd.dll"; +#endif constexpr std::string_view DISCLAIMER_FLAG = ".license_accepted.flag"; constexpr std::string_view DISCLAIMER_TEXT_FILE = "DISCLAIMER.md"; @@ -91,8 +98,8 @@ static std::string get_dll_path(bool debug) return path.string(); } - -int WxInitSDK(bool debug, int port) +extern "C" { +__declspec(dllexport) int WxInitSDK(bool debug, int port) { if (!show_disclaimer()) { exit(-1); // 用户拒绝协议,退出程序 @@ -134,7 +141,7 @@ int WxInitSDK(bool debug, int port) return status; } -int WxDestroySDK() +__declspec(dllexport) int WxDestroySDK() { if (!injected) { return 1; // 未注入 @@ -151,3 +158,4 @@ int WxDestroySDK() return 0; } +} \ No newline at end of file diff --git a/WeChatFerry/sdk/sdk.def b/WeChatFerry/sdk/sdk.def deleted file mode 100644 index d8c9be9..0000000 --- a/WeChatFerry/sdk/sdk.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS - WxInitSDK - WxDestroySDK diff --git a/WeChatFerry/sdk/sdk.h b/WeChatFerry/sdk/sdk.h index 0ebec29..2002d03 100644 --- a/WeChatFerry/sdk/sdk.h +++ b/WeChatFerry/sdk/sdk.h @@ -1,4 +1,6 @@ #pragma once -int WxInitSDK(bool debug, int port); -int WxDestroySDK(); +extern "C" { +__declspec(dllexport) int WxInitSDK(bool debug, int port); +__declspec(dllexport) int WxDestroySDK(); +} \ No newline at end of file diff --git a/WeChatFerry/smc/libCodec.a b/WeChatFerry/smc/libCodec.a new file mode 100644 index 0000000..1370505 Binary files /dev/null and b/WeChatFerry/smc/libCodec.a differ diff --git a/WeChatFerry/smc/libmp3lame.a b/WeChatFerry/smc/libmp3lame.a new file mode 100644 index 0000000..96e03ae Binary files /dev/null and b/WeChatFerry/smc/libmp3lame.a differ diff --git a/WeChatFerry/spy/CMakeLists.txt b/WeChatFerry/spy/CMakeLists.txt new file mode 100644 index 0000000..986c53b --- /dev/null +++ b/WeChatFerry/spy/CMakeLists.txt @@ -0,0 +1,108 @@ +# Spy project - dynamic library +project(Spy LANGUAGES C CXX) + +find_package(spdlog REQUIRED) +find_package(magic_enum REQUIRED) +find_package(minhook CONFIG REQUIRED) +find_package(nng REQUIRED) + +add_library(spy SHARED + chatroom_manager.cpp + chatroom_manager.h + misc_manager.cpp + misc_manager.h + database_executor.cpp + database_executor.h + contact_manager.cpp + contact_manager.h + message_handler.cpp + message_handler.h + rpc_server.cpp + rpc_server.h + message_sender.cpp + message_sender.h + spy.cpp + spy.h + spy_types.h + account_manager.cpp + account_manager.h + resource.h + rpc_helper.h + sqlite3.h + dllmain.cpp + spy.def + + # Common files + ${CMAKE_SOURCE_DIR}/com/util.cpp + ${CMAKE_SOURCE_DIR}/com/util.h + ${CMAKE_SOURCE_DIR}/com/log.hpp + + # RPC files + ${CMAKE_SOURCE_DIR}/rpc/pb_util.cpp + ${CMAKE_SOURCE_DIR}/rpc/pb_util.h + ${CMAKE_SOURCE_DIR}/rpc/pb_types.h + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb.h + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb_common.h + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb_decode.h + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb_encode.h + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb_common.c + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb_decode.c + ${CMAKE_SOURCE_DIR}/rpc/nanopb/pb_encode.c + ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.pb.c + ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.pb.h +) + +# link directories +target_link_directories(spy PRIVATE + ${CMAKE_SOURCE_DIR}/smc +) + +# Link dependencies +target_link_libraries(spy PRIVATE + Codec + mp3lame + version + shlwapi + iphlpapi + wsock32 + ws2_32 + crypt32 + magic_enum::magic_enum + nng::nng + spdlog::spdlog + minhook::minhook + c++ +) + +add_custom_command( + OUTPUT + ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.pb.c + ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.pb.h + COMMAND ${CMAKE_SOURCE_DIR}/rpc/tool/protoc --nanopb_out=${CMAKE_SOURCE_DIR}/rpc/proto wcf.proto + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/rpc/proto + DEPENDS ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.proto + COMMENT "Generated protobuf files" +) + +# Add generated files as dependencies +add_custom_target(protobuf_generation + DEPENDS + ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.pb.c + ${CMAKE_SOURCE_DIR}/rpc/proto/wcf.pb.h +) +add_dependencies(spy protobuf_generation) + +# Set output name for debug builds +set_target_properties(spy PROPERTIES + DEBUG_POSTFIX "d" +) + +# Post-build copy commands +# add_custom_command(TARGET spy POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy +# $ +# ${CMAKE_SOURCE_DIR}/Out +# COMMAND ${CMAKE_COMMAND} -E copy +# $ +# ${CMAKE_SOURCE_DIR}/../clients/python/wcferry +# ) diff --git a/WeChatFerry/spy/Spy.vcxproj b/WeChatFerry/spy/Spy.vcxproj index 0854bf5..ca8ea82 100644 --- a/WeChatFerry/spy/Spy.vcxproj +++ b/WeChatFerry/spy/Spy.vcxproj @@ -105,7 +105,8 @@ Windows true false - spy.def + + /ignore:4099 %(AdditionalOptions) $(SolutionDir)smc;%(AdditionalLibraryDirectories) iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies) @@ -158,7 +159,8 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferryWindows true false - spy.def + + /ignore:4099 %(AdditionalOptions) $(SolutionDir)smc;%(AdditionalLibraryDirectories) iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies) @@ -212,7 +214,8 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferrytrue false false - spy.def + + $(SolutionDir)smc;%(AdditionalLibraryDirectories) iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies) /ignore:4099 %(AdditionalOptions) @@ -250,7 +253,7 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry - + @@ -282,7 +285,6 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry - diff --git a/WeChatFerry/spy/Spy.vcxproj.filters b/WeChatFerry/spy/Spy.vcxproj.filters index a2a537b..dafe11c 100644 --- a/WeChatFerry/spy/Spy.vcxproj.filters +++ b/WeChatFerry/spy/Spy.vcxproj.filters @@ -18,7 +18,7 @@ - + 头文件 @@ -142,9 +142,6 @@ - - 源文件 - nnrpc diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index d0353af..232fe85 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -17,14 +17,6 @@ namespace OsAcc = Offsets::Account; using get_account_service_t = QWORD (*)(); using get_data_path_t = QWORD (*)(QWORD); -// 缓存避免重复查询 -static std::optional cachedWxid; -static std::optional cachedHomePath; - -// 清除缓存 -static void clear_cached_wxid() { cachedWxid.reset(); } -static void clear_cached_home_path() { cachedHomePath.reset(); } - static uint64_t get_account_service() { static auto GetService = Spy::getFunction(OsAcc::SERVICE); @@ -39,37 +31,44 @@ static std::string get_string_value(uint64_t base_addr, uint64_t offset) bool is_logged_in() { - clear_cached_wxid(); - clear_cached_home_path(); uint64_t service_addr = get_account_service(); return service_addr && util::get_qword(service_addr + OsAcc::LOGIN) != 0; } fs::path get_home_path() { - if (cachedHomePath) { - return *cachedHomePath; - } - WxString home; - auto GetDataPath = Spy::getFunction(OsAcc::PATH); - int64_t service_addr = get_account_service(); - GetDataPath((QWORD)&home); - if (home.wptr) { - cachedHomePath = util::w2s(std::wstring(home.wptr, home.size)); - } - return *cachedHomePath; + static fs::path home_path; + static std::once_flag home_once; + + std::call_once(home_once, []() { + WxString home {}; + if (auto getDataPath = Spy::getFunction(OsAcc::PATH)) { + getDataPath(reinterpret_cast(&home)); + if (home.wptr) { + std::wstring wstr(home.wptr, home.size); + home_path = util::w2s(std::move(wstr)); + } + } + }); + + return home_path; } std::string get_self_wxid() { - if (cachedWxid) { - return *cachedWxid; - } - uint64_t service_addr = get_account_service(); - if (!service_addr) return ""; + static std::string cached_wxid; + static std::once_flag wxid_once; - cachedWxid = get_string_value(service_addr, OsAcc::WXID); - return *cachedWxid; + std::call_once(wxid_once, []() { + if (uint64_t svc = get_account_service(); svc) { + cached_wxid = get_string_value(svc, OsAcc::WXID); + if (cached_wxid.empty()) { + cached_wxid = get_string_value(svc, OsAcc::ALIAS); + } + } + }); + + return cached_wxid; } UserInfo_t get_user_info() diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index 912bda2..5763283 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -12,10 +12,11 @@ namespace chatroom { namespace OsRoom = Offsets::Chatroom; +using new_t = QWORD (*)(QWORD, WxString *); using get_mgr_t = QWORD (*)(); using add_member_t = QWORD (*)(QWORD, QWORD, WxString *, QWORD); using delete_member_t = QWORD (*)(QWORD, QWORD, WxString *); -using invite_members_t = QWORD (*)(const wchar_t *, QWORD, WxString *, QWORD); +using invite_members_t = QWORD (*)(const wchar_t *, QWORD, QWORD, QWORD); template bool rpc_chatroom_common(const MemberMgmt &m, uint8_t *out, size_t *len, Func func) @@ -36,9 +37,11 @@ int add_chatroom_member(const string &roomid, const string &wxids) WxString *wx_roomid = util::CreateWxString(roomid); - QWORD tmp[2] = { 0 }; - auto wx_members = util::parse_wxids(wxids).wxWxids; - QWORD p_members = reinterpret_cast(&wx_members); + QWORD tmp[2] = { 0 }; + + auto split = util::parse_wxids(wxids); + auto &wx_members = split.wxWxids; + QWORD p_members = reinterpret_cast(&wx_members); return static_cast(add_members(get_chatroom_mgr(), p_members, wx_roomid, reinterpret_cast(tmp))); } @@ -49,24 +52,32 @@ int del_chatroom_member(const string &roomid, const string &wxids) auto del_members = Spy::getFunction(OsRoom::DEL); WxString *wx_roomid = util::CreateWxString(roomid); - auto wx_members = util::parse_wxids(wxids).wxWxids; - QWORD p_members = reinterpret_cast(&wx_members); + + auto split = util::parse_wxids(wxids); + auto &wx_members = split.wxWxids; + QWORD p_members = reinterpret_cast(&wx_members); return static_cast(del_members(get_chatroom_mgr(), p_members, wx_roomid)); } int invite_chatroom_member(const string &roomid, const string &wxids) { + auto init_roomid = Spy::getFunction(OsRoom::NEW); auto invite_members = Spy::getFunction(OsRoom::INV); - wstring ws_roomid = util::s2w(roomid); - WxString *wx_roomid = util::CreateWxString(roomid); + wstring ws_roomid = util::s2w(roomid); + WxString wx_roomid(ws_roomid); - QWORD tmp[2] = { 0 }; - auto wx_members = util::parse_wxids(wxids).wxWxids; - QWORD p_members = reinterpret_cast(&wx_members); + QWORD tmp[2] = { 0 }; + QWORD array[4] = { 0 }; - return static_cast(invite_members(ws_roomid.c_str(), p_members, wx_roomid, reinterpret_cast(tmp))); + auto split = util::parse_wxids(wxids); + auto &wx_members = split.wxWxids; + QWORD p_members = reinterpret_cast(&wx_members); + QWORD p_roomid = init_roomid(reinterpret_cast(&array), &wx_roomid); + LOG_BUFFER((uint8_t *)*(QWORD *)(*(QWORD *)p_members), 40); + + return static_cast(invite_members(ws_roomid.c_str(), p_members, p_roomid, reinterpret_cast(tmp))); } bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp deleted file mode 100644 index ce36eac..0000000 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "framework.h" -#include -#include - -#include "chatroom_mgmt.h" -#include "log.h" -#include "util.h" - -using namespace std; -extern QWORD g_WeChatWinDllAddr; - -#define OS_GET_CHATROOM_MGR 0x1B894E0 -#define OS_ADD_MEMBERS 0x215A820 -#define OS_DELETE_MEMBERS 0x215AE60 -#define OS_INVITE_MEMBERS 0x215A200 - -typedef QWORD (*GetChatRoomMgr_t)(); -typedef QWORD (*AddMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*DelMemberFromChatRoom_t)(QWORD, QWORD, QWORD); -typedef QWORD (*InviteMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); - -int AddChatroomMember(string roomid, string wxids) -{ - int status = -1; - - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return status; - } - - GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); - AddMemberToChatRoom_t AddMembers = (AddMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_ADD_MEMBERS); - - vector vMembers; - vector vWxMembers; - wstringstream wss(String2Wstring(wxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vMembers.push_back(wstr); - WxString wxMember(vMembers.back()); - vWxMembers.push_back(wxMember); - } - - QWORD temp[2] = { 0 }; - WxString *pWxRoomid = NewWxStringFromStr(roomid); - QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; - - QWORD mgr = GetChatRoomMgr(); - status = (int)AddMembers(mgr, pMembers, (QWORD)pWxRoomid, (QWORD)temp); - return status; -} - -int DelChatroomMember(string roomid, string wxids) -{ - int status = -1; - - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return status; - } - - GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); - DelMemberFromChatRoom_t DelMembers = (DelMemberFromChatRoom_t)(g_WeChatWinDllAddr + OS_DELETE_MEMBERS); - - vector vMembers; - vector vWxMembers; - wstringstream wss(String2Wstring(wxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vMembers.push_back(wstr); - WxString wxMember(vMembers.back()); - vWxMembers.push_back(wxMember); - } - - WxString *pWxRoomid = NewWxStringFromStr(roomid); - QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; - - QWORD mgr = GetChatRoomMgr(); - status = (int)DelMembers(mgr, pMembers, (QWORD)pWxRoomid); - return status; -} - -int InviteChatroomMember(string roomid, string wxids) -{ - int status = -1; - - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return status; - } - - InviteMemberToChatRoom_t InviteMembers = (InviteMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_INVITE_MEMBERS); - - vector vMembers; - vector vWxMembers; - wstringstream wss(String2Wstring(wxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vMembers.push_back(wstr); - WxString wxMember(vMembers.back()); - vWxMembers.push_back(wxMember); - } - QWORD temp[2] = { 0 }; - wstring wsRoomid = String2Wstring(roomid); - WxString *pWxRoomid = NewWxStringFromWstr(wsRoomid); - QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; - - status = (int)InviteMembers((QWORD)wsRoomid.c_str(), pMembers, (QWORD)pWxRoomid, (QWORD)temp); - return status; -} - diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index 57ff600..2863aed 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -18,7 +18,7 @@ namespace OsCon = Offsets::Contact; using get_contact_mgr_t = QWORD (*)(); using get_contact_list_t = QWORD (*)(QWORD, QWORD); using func_verify_new_t = QWORD (*)(QWORD, WxString *); -using func_verify_ok_t = QWORD (*)(QWORD, WxString *, QWORD *, QWORD, QWORD, QWORD *, WxString *, QWORD *, WxString *); +using func_verify_ok_t = QWORD (*)(QWORD, WxString *, QWORD *, QWORD, QWORD, QWORD, QWORD, QWORD, WxString *); #define FEAT_LEN 5 static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 }; @@ -92,26 +92,24 @@ vector get_contacts() int accept_new_friend(const std::string &v3, const std::string &v4, int scene) { - // TODO: 处理来源、备注、标签等 + // TODO: 备注、标签等 auto func_new = Spy::getFunction(OsCon::VERIFY_NEW); auto func_verify = Spy::getFunction(OsCon::VERIFY_OK); QWORD helper = util::get_qword(Spy::WeChatDll.load() + OsCon::ADD_FRIEND_HELPER); QWORD fvdf = util::get_qword(Spy::WeChatDll.load() + OsCon::FVDF); - QWORD mgr = util::get_qword(Spy::WeChatDll.load() + OsCon::VERIFY_MGR); - QWORD a8 = util::get_qword(Spy::WeChatDll.load() + OsCon::VERIFY_A8); auto pV3 = util::CreateWxString(v3); auto pV4 = util::CreateWxString(v4); QWORD v4Array[4] = { 0 }; - QWORD p_v4Buff = func_new(reinterpret_cast(&v4Array), pV4); + QWORD pV4Buff = func_new(reinterpret_cast(&v4Array), pV4); char buff[0x100] = { 0 }; memcpy(buff, &helper, sizeof(&helper)); QWORD a1 = reinterpret_cast(&buff); - QWORD ret = func_verify(a1, pV3, &fvdf, 0x3A08A4, p_v4Buff, &mgr, pV4, &a8, pV4); + QWORD ret = func_verify(a1, pV3, &fvdf, 0x1D08B4, pV4Buff, 0x1, pV4Buff, scene, 0x0); util::FreeWxString(pV3); util::FreeWxString(pV4); diff --git a/WeChatFerry/spy/framework.h b/WeChatFerry/spy/framework.h deleted file mode 100644 index 80cbbc9..0000000 --- a/WeChatFerry/spy/framework.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 -// Windows 头文件 -#include diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 9537bf1..851584f 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -5,8 +5,6 @@ #include #include -#include "framework.h" - #include "account_manager.h" #include "log.hpp" #include "offsets.h" @@ -192,8 +190,8 @@ int Handler::EnableLog() funcWxLog = Spy::getFunction(OsLog::CALL); if (InitializeHook() != MH_OK) return -1; - if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -2; - if (MH_EnableHook(funcWxLog) != MH_OK) return -3; + if (MH_CreateHook(reinterpret_cast(funcWxLog), reinterpret_cast(&PrintWxLog), reinterpret_cast(&realWxLog)) != MH_OK) return -2; + if (MH_EnableHook(reinterpret_cast(funcWxLog)) != MH_OK) return -3; *pLogLevel = 0; isLogging = true; @@ -203,8 +201,9 @@ int Handler::EnableLog() int Handler::DisableLog() { if (!isLogging) return 1; - if (MH_DisableHook(funcWxLog) != MH_OK) return -1; - if (UninitializeHook() != MH_OK) return -2; + if (MH_DisableHook(reinterpret_cast(funcWxLog)) != MH_OK) return -1; + if (MH_RemoveHook(reinterpret_cast(funcWxLog)) != MH_OK) return -2; + if (UninitializeHook() != MH_OK) return -3; *pLogLevel = 6; isLogging = false; return 0; @@ -216,8 +215,8 @@ int Handler::ListenMsg() funcRecvMsg = Spy::getFunction(OsRecv::CALL); if (InitializeHook() != MH_OK) return -1; - if (MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast(&realRecvMsg)) != MH_OK) return -1; - if (MH_EnableHook(funcRecvMsg) != MH_OK) return -1; + if (MH_CreateHook(reinterpret_cast(funcRecvMsg), reinterpret_cast(&DispatchMsg), reinterpret_cast(&realRecvMsg)) != MH_OK) return -2; + if (MH_EnableHook(reinterpret_cast(funcRecvMsg)) != MH_OK) return -3; isListeningMsg = true; return 0; @@ -226,8 +225,9 @@ int Handler::ListenMsg() int Handler::UnListenMsg() { if (!isListeningMsg) return 1; - if (MH_DisableHook(funcRecvMsg) != MH_OK) return -1; - if (UninitializeHook() != MH_OK) return -1; + if (MH_DisableHook(reinterpret_cast(funcRecvMsg)) != MH_OK) return -1; + if (MH_RemoveHook(reinterpret_cast(funcRecvMsg)) != MH_OK) return -2; + if (UninitializeHook() != MH_OK) return -3; isListeningMsg = false; return 0; } @@ -238,8 +238,8 @@ int Handler::ListenPyq() funcRecvPyq = Spy::getFunction(OsRecv::PYQ_CALL); if (InitializeHook() != MH_OK) return -1; - if (MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast(&realRecvPyq)) != MH_OK) return -1; - if (MH_EnableHook(funcRecvPyq) != MH_OK) return -1; + if (MH_CreateHook(reinterpret_cast(funcRecvPyq), reinterpret_cast(&DispatchPyq), reinterpret_cast(&realRecvPyq)) != MH_OK) return -1; + if (MH_EnableHook(reinterpret_cast(funcRecvPyq)) != MH_OK) return -1; isListeningPyq = true; return 0; @@ -248,8 +248,9 @@ int Handler::ListenPyq() int Handler::UnListenPyq() { if (!isListeningPyq) return 1; - if (MH_DisableHook(funcRecvPyq) != MH_OK) return -1; - if (UninitializeHook() != MH_OK) return -1; + if (MH_DisableHook(reinterpret_cast(funcRecvPyq)) != MH_OK) return -1; + if (MH_RemoveHook(reinterpret_cast(funcRecvPyq)) != MH_OK) return -2; + if (UninitializeHook() != MH_OK) return -3; isListeningPyq = false; return 0; } diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index e565ae4..544c747 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -313,8 +313,8 @@ bool Sender::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len) LOG_ERROR("Empty content or receiver."); rsp.msg.status = -1; } else { - // send_xml(xml.receiver, xml.content, xml.path, xml.type); - rsp.msg.status = -1; + send_xml(xml.receiver, xml.content, xml.path, xml.type); + rsp.msg.status = 0; } }); } diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 5db4fbc..7a7f445 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -4,14 +4,13 @@ #include #include -#include "framework.h" - #include "codec.h" #include "database_executor.h" #include "log.hpp" #include "message_handler.h" #include "offsets.h" #include "rpc_helper.h" +#include "rpc_server.h" #include "spy.h" #include "spy_types.h" #include "util.h" @@ -418,4 +417,17 @@ bool rpc_receive_transfer(const Transfer &tf, uint8_t *out, size_t *len) return fill_response( out, len, [&](Response &rsp) { rsp.msg.status = receive_transfer(tf.wxid, tf.tfid, tf.taid); }); } + +bool rpc_shutdown(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + rsp.msg.status = 0; + std::thread([]() { + Sleep(100); + RpcServer::destroyInstance(); + Spy::Cleanup(); + }).detach(); + return true; + }); +} } // namespace misc diff --git a/WeChatFerry/spy/misc_manager.h b/WeChatFerry/spy/misc_manager.h index 0792c3f..1351438 100644 --- a/WeChatFerry/spy/misc_manager.h +++ b/WeChatFerry/spy/misc_manager.h @@ -36,5 +36,6 @@ bool rpc_download_attachment(const AttachMsg &att, uint8_t *out, size_t *len); bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len); bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len); bool rpc_receive_transfer(const Transfer &tf, uint8_t *out, size_t *len); +bool rpc_shutdown(uint8_t *out, size_t *len); // clang-format on } // namespace misc diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 5066766..a29370e 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -7,8 +7,8 @@ namespace Offsets namespace Account { - constexpr uint64_t SERVICE = 0x1B58B50; // 账户服务 - constexpr uint64_t PATH = 0x25E9090; // 数据路径 + constexpr uint64_t SERVICE = 0x1B5CA40; // 账户服务 + constexpr uint64_t PATH = 0x25F4A40; // 数据路径 constexpr uint64_t WXID = 0x80; // WXID constexpr uint64_t NAME = 0x1E8; // 昵称 constexpr uint64_t MOBILE = 0x128; // 手机号 @@ -18,16 +18,17 @@ namespace Account namespace Chatroom { - constexpr uint64_t MGR = 0x1B86F60; - constexpr uint64_t DEL = 0x2158830; - constexpr uint64_t ADD = 0x21581F0; - constexpr uint64_t INV = 0x2157BD0; + constexpr uint64_t MGR = 0x1B8AE40; + constexpr uint64_t NEW = 0x262D800; + constexpr uint64_t DEL = 0x2163070; + constexpr uint64_t ADD = 0x2162A30; + constexpr uint64_t INV = 0x2162410; } namespace Contact { - constexpr uint64_t MGR = 0x1B44B20; - constexpr uint64_t LIST = 0x21A1E00; + constexpr uint64_t MGR = 0x1B489D0; + constexpr uint64_t LIST = 0x21ACBE0; constexpr uint64_t BIN = 0x200; constexpr uint64_t BIN_LEN = 0x208; constexpr uint64_t WXID = 0x10; @@ -37,18 +38,16 @@ namespace Contact constexpr uint64_t GENDER = 0x0E; constexpr uint64_t STEP = 0x6A8; - constexpr uint64_t VERIFY_NEW = 0x2621B00; - constexpr uint64_t VERIFY_OK = 0x1F421E0; - constexpr uint64_t VERIFY_MGR = 0x4F022A8; - constexpr uint64_t VERIFY_A8 = 0x2621B91; - constexpr uint64_t ADD_FRIEND_HELPER = 0x4EE4A20; - constexpr uint64_t FVDF = 0x4F02768; // FriendVeriyDialogFragment + constexpr uint64_t VERIFY_NEW = Chatroom::NEW; + constexpr uint64_t VERIFY_OK = 0x1F48850; + constexpr uint64_t ADD_FRIEND_HELPER = 0x4F7FB18; // a1 + constexpr uint64_t FVDF = 0x4F9DE28; // FriendVeriyDialogFragment } namespace Db { - constexpr uint64_t INSTANCE = 0x59226C8; // 数据库实例地址 - constexpr uint64_t MSG_I = 0x5980420; // MSGi.db & MediaMsgi.db + constexpr uint64_t INSTANCE = 0x59D2008; // 数据库实例地址 + constexpr uint64_t MSG_I = 0x5A30158; // MSGi.db & MediaMsgi.db constexpr uint64_t MICROMSG = 0xB8; constexpr uint64_t CHAT_MSG = 0x2C8; constexpr uint64_t MISC = 0x5F0; @@ -59,7 +58,7 @@ namespace Db constexpr uint64_t NAME = 0x28; // SQLITE3 - constexpr uint64_t EXEC = 0x3A76430; + constexpr uint64_t EXEC = 0x3A820A0; // constexpr uint64_t BACKUP_INIT = EXEC - 0x1D113E0; constexpr uint64_t PREPARE = EXEC + 0x7CB0; // constexpr uint64_t OPEN = EXEC - 0x1CA2430; @@ -83,13 +82,13 @@ namespace Message { namespace Log { - constexpr uint64_t LEVEL = 0x56E4244; // 日志级别 - constexpr uint64_t CALL = 0x261B890; // 日志函数 + constexpr uint64_t LEVEL = 0x578DF28; // 日志级别 + constexpr uint64_t CALL = 0x2627590; // 日志函数 } namespace Receive { - constexpr uint64_t CALL = 0x2141E80; // 接收消息 Call + constexpr uint64_t CALL = 0x214C6C0; // 接收消息 Call constexpr uint64_t ID = 0x30; // 消息 ID constexpr uint64_t TYPE = 0x38; // 消息类型 constexpr uint64_t SELF = 0x3C; // 消息是否来自自己 @@ -102,7 +101,7 @@ namespace Message constexpr uint64_t EXTRA = 0x2A0; // 原图路径 constexpr uint64_t XML = 0x308; // 消息 XML - constexpr uint64_t PYQ_CALL = 0x2E56080; // 接收朋友圈 Call + constexpr uint64_t PYQ_CALL = 0x2E621D0; // 接收朋友圈 Call constexpr uint64_t PYQ_START = 0x30; // 开始地址 constexpr uint64_t PYQ_END = 0x38; // 结束地址 constexpr uint64_t PYQ_SENDER = 0x18; // 发布者 @@ -113,45 +112,45 @@ namespace Message namespace Send { - constexpr uint64_t MGR = 0x1B57350; - constexpr uint64_t INSTANCE = 0x1B614C0; - constexpr uint64_t FREE = 0x1B58BD0; - constexpr uint64_t TEXT = 0x22C9CA0; - constexpr uint64_t IMAGE = 0x22BF430; - constexpr uint64_t APP_MGR = 0x1B5C2F0; - constexpr uint64_t FILE = 0x20D30E0; - constexpr uint64_t XML = 0x20D2210; - constexpr uint64_t XML_BUF_SIGN = 0x24F95C0; - constexpr uint64_t EMOTION_MGR = 0x1BD2310; - constexpr uint64_t EMOTION = 0x21B8100; + constexpr uint64_t MGR = 0x1B5B210; + constexpr uint64_t INSTANCE = 0x1B653B0; + constexpr uint64_t FREE = 0x1B5CAC0; + constexpr uint64_t TEXT = 0x22D4A90; + constexpr uint64_t IMAGE = 0x22CA2A0; + constexpr uint64_t APP_MGR = 0x1B601E0; + constexpr uint64_t FILE = 0x20DE200; + constexpr uint64_t XML = 0x20DD330; + constexpr uint64_t XML_BUF_SIGN = 0x2503760; + constexpr uint64_t EMOTION_MGR = 0x1BD6300; + constexpr uint64_t EMOTION = 0x21C2EE0; - constexpr uint64_t NEW_MM_READER = 0x1B60A10; - constexpr uint64_t FREE_MM_READER = 0x1B5FDE0; - constexpr uint64_t RICH_TEXT = 0x20DD0C0; + constexpr uint64_t NEW_MM_READER = 0x1B64900; + constexpr uint64_t FREE_MM_READER = 0x1B63CD0; + constexpr uint64_t RICH_TEXT = 0x20E81E0; - constexpr uint64_t PAT = 0x2CC1E90; + constexpr uint64_t PAT = 0x2CCDDC0; - constexpr uint64_t FORWARD = 0x22C9220; + constexpr uint64_t FORWARD = 0x22D4010; } } namespace Misc { - constexpr uint64_t QR_CODE = 0x2025A80; + constexpr uint64_t QR_CODE = 0x202D3C0; constexpr uint64_t INSATNCE = Message::Send::INSTANCE; constexpr uint64_t FREE = Message::Send::FREE; - constexpr uint64_t CHAT_MGR = 0x1B8AA50; - constexpr uint64_t PRE_LOCAL_ID_MGR = 0x2142BF0; - constexpr uint64_t PRE_DOWNLOAD_MGR = 0x1C12260; - constexpr uint64_t PUSH_ATTACH_TASK = 0x1CE3050; + constexpr uint64_t CHAT_MGR = 0x1B8E930; + constexpr uint64_t PRE_LOCAL_ID_MGR = 0x214D430; + constexpr uint64_t PRE_DOWNLOAD_MGR = 0x1C17660; + constexpr uint64_t PUSH_ATTACH_TASK = 0x1CE8500; namespace Sns { - constexpr uint64_t DATA_MGR = 0x21E52F0; - constexpr uint64_t TIMELINE = 0x2DC6180; - constexpr uint64_t FIRST = 0x2E346C0; - constexpr uint64_t NEXT = 0x2E5A270; + constexpr uint64_t DATA_MGR = 0x21F00D0; + constexpr uint64_t TIMELINE = 0x2DD2320; + constexpr uint64_t FIRST = 0x2E40810; + constexpr uint64_t NEXT = 0x2E663C0; } } } diff --git a/WeChatFerry/spy/rpc_helper.h b/WeChatFerry/spy/rpc_helper.h index feeb081..0ecbdcb 100644 --- a/WeChatFerry/spy/rpc_helper.h +++ b/WeChatFerry/spy/rpc_helper.h @@ -2,6 +2,8 @@ #include +#define MAGIC_ENUM_RANGE_MIN 0 +#define MAGIC_ENUM_RANGE_MAX 256 #include #include "wcf.pb.h" @@ -41,7 +43,8 @@ static const std::unordered_map rpc_tag_map { Functions_FUNC_EXEC_OCR, Response_ocr_tag }, { Functions_FUNC_ADD_ROOM_MEMBERS, Response_status_tag }, { Functions_FUNC_DEL_ROOM_MEMBERS, Response_status_tag }, - { Functions_FUNC_INV_ROOM_MEMBERS, Response_status_tag } }; + { Functions_FUNC_INV_ROOM_MEMBERS, Response_status_tag }, + { Functions_FUNC_SHUTDOWN, Response_status_tag } }; template bool fill_response(uint8_t *out, size_t *len, AssignFunc assign) { diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 094420b..354f7da 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -214,7 +213,7 @@ bool RpcServer::start_message_listener(bool pyq, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = handler_.ListenMsg(); - if (rsp.msg.status == 0) { + if (rsp.msg.status >= 0) { if (pyq) { handler_.ListenPyq(); } @@ -227,7 +226,7 @@ bool RpcServer::stop_message_listener(uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = handler_.UnListenMsg(); - if (rsp.msg.status == 0) { + if (rsp.msg.status >= 0) { handler_.UnListenPyq(); if (msgThread_.joinable()) { msgThread_.join(); @@ -269,6 +268,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_SHUTDOWN, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_shutdown(out, len); }} // clang-format on }; diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index dc6a700..cc7e6b0 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -14,7 +14,7 @@ int Init(void *args) auto *pp = static_cast(args); Log::InitLogger(pp->path); - if (auto dll_addr = GetModuleHandle(L"WeChatWin.dll")) { + if (auto dll_addr = GetModuleHandleW(L"WeChatWin.dll")) { WeChatDll.store(reinterpret_cast(dll_addr)); } else { LOG_ERROR("获取 WeChatWin.dll 模块地址失败"); @@ -44,5 +44,5 @@ void Cleanup() extern "C" { __declspec(dllexport) int InitSpy(void *args) { return Spy::Init(args); } -__declspec(dllexport) void CleanupSpy() { Spy::Cleanup(); } +__declspec(dllexport) int CleanupSpy() { Spy::Cleanup(); return 0;} } diff --git a/WeChatFerry/spy/spy.def b/WeChatFerry/spy/spy.def deleted file mode 100644 index 7632d5c..0000000 --- a/WeChatFerry/spy/spy.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS - InitSpy - CleanupSpy diff --git a/WeChatFerry/spy/spy.h b/WeChatFerry/spy/spy.h index ae8f402..e460bc9 100644 --- a/WeChatFerry/spy/spy.h +++ b/WeChatFerry/spy/spy.h @@ -6,7 +6,7 @@ namespace Spy { -constexpr std::string_view SUPPORT_VERSION = "3.9.12.17"; +constexpr std::string_view SUPPORT_VERSION = "3.9.12.51"; inline std::atomic WeChatDll { 0 }; template inline T getFunction(std::uintptr_t offset) { return reinterpret_cast(WeChatDll + offset); } diff --git a/WeChatFerry/spy/spy.rc b/WeChatFerry/spy/spy.rc index 1153695..346545c 100644 --- a/WeChatFerry/spy/spy.rc +++ b/WeChatFerry/spy/spy.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 39,4,4,0 - PRODUCTVERSION 3,9,12,17 + FILEVERSION 39,5,2,0 + PRODUCTVERSION 3,9,12,51 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "WeChatFerry" VALUE "FileDescription", "WeChatFerry" - VALUE "FileVersion", "39.4.4.0" + VALUE "FileVersion", "39.5.2.0" VALUE "InternalName", "spy.dll" VALUE "LegalCopyright", "Copyright (C) 2023" VALUE "OriginalFilename", "spy.dll" VALUE "ProductName", "WeChatFerry" - VALUE "ProductVersion", "3.9.12.17" + VALUE "ProductVersion", "3.9.12.51" END END BLOCK "VarFileInfo" diff --git a/WeChatFerry/vcpkg.json b/WeChatFerry/vcpkg.json index d735952..5f077e0 100644 --- a/WeChatFerry/vcpkg.json +++ b/WeChatFerry/vcpkg.json @@ -2,14 +2,10 @@ "name": "wcf", "version-string": "1.0.0", "dependencies": [ - { - "name": "protobuf", - "features": [ "zlib" ] - }, "spdlog", "nng", "magic-enum", "minhook" ], - "builtin-baseline": "80d54ff62d528339c626a6fbc3489a7f25956ade" + "builtin-baseline": "d6995a0cf3cafda5e9e52749fad075dd62bfd90c" } diff --git a/clients/python/README.MD b/clients/python/README.MD index bd80fa3..258d184 100644 --- a/clients/python/README.MD +++ b/clients/python/README.MD @@ -1,7 +1,7 @@ # WeChatFerry Python 客户端 [![PyPi](https://img.shields.io/pypi/v/wcferry.svg)](https://pypi.python.org/pypi/wcferry) [![Downloads](https://static.pepy.tech/badge/wcferry)](https://pypi.python.org/pypi/wcferry) [![Documentation Status](https://readthedocs.org/projects/wechatferry/badge/?version=latest)](https://wechatferry.readthedocs.io/zh/latest/?badge=latest) -|[📖 Python 文档](https://wechatferry.readthedocs.io/)|[📺 Python 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/YvgFFhF6D-79kXDzRqtg6w)| +|[📖 Python 文档](https://wechatferry.readthedocs.io/)|[📺 Python 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/c2JggTBlOP8fP9j-MlMAvg)| |:-:|:-:|:-:| 🤖示例机器人框架:[WeChatRobot](https://github.com/lich0821/WeChatRobot)。 @@ -44,8 +44,8 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc ## 版本更新 -### v39.4.4.0 -* 实现发送 XML 功能 +### v39.5.2.0 +* 没有新功能
点击查看更多 @@ -71,7 +71,7 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc * 发送图片消息 * 发送文件消息 * 发送卡片消息 -* 发送 XML +* 发送 XML 消息 * 发送 GIF 消息 * 拍一拍群友 * 转发消息 diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index af70b4e..71675ae 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -1,11 +1,13 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- -__version__ = "39.4.4.0" +__version__ = "39.5.2.0" import atexit import base64 import ctypes +import ctypes.wintypes +import gc import logging import mimetypes import os @@ -80,10 +82,7 @@ class Wcf(): if host is None: self._local_mode = True self.host = "127.0.0.1" - self.sdk = ctypes.cdll.LoadLibrary(f"{self._wcf_root}/sdk.dll") - if self.sdk.WxInitSDK(debug, port) != 0: - self.LOG.error("初始化失败!") - os._exit(-1) + self._sdk_init(debug, port) self.cmd_url = f"tcp://{self.host}:{self.port}" @@ -125,15 +124,42 @@ class Wcf(): except subprocess.CalledProcessError as e: self.LOG.error(f"修改控制台代码页失败: {e}") + def _sdk_init(self, debug, port): + sdk = ctypes.cdll.LoadLibrary(f"{self._wcf_root}/sdk.dll") + if sdk.WxInitSDK(debug, port) != 0: + self.LOG.error("初始化失败!") + os._exit(-1) + + # 主动卸载 + ctypes.windll.kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE] + ctypes.windll.kernel32.FreeLibrary(sdk._handle) + del sdk # 删除 Python 对象、触发垃圾回收 + gc.collect() + + def _sdk_destroy(self): + sdk = ctypes.cdll.LoadLibrary(f"{self._wcf_root}/sdk.dll") + sdk.WxDestroySDK() + # 主动卸载 + ctypes.windll.kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE] + ctypes.windll.kernel32.FreeLibrary(sdk._handle) + del sdk # 删除 Python 对象、触发垃圾回收 + gc.collect() + def cleanup(self) -> None: """关闭连接,回收资源""" if not self._is_running: return self.disable_recv_msg() - self.cmd_socket.close() - if self._local_mode and self.sdk and self.sdk.WxDestroySDK() != 0: + req = wcf_pb2.Request() + req.func = wcf_pb2.FUNC_SHUTDOWN + _ = self._send_request(req) + + self.cmd_socket.close() + self.msg_socket.close() + + if self._local_mode and self.sdk and self._sdk_destroy() != 0: self.LOG.error("退出失败!") self._is_running = False @@ -537,9 +563,6 @@ class Wcf(): else: self.msgQ.put(WxMsg(rsp.wxmsg)) - # 退出前关闭通信通道 - self.msg_socket.close() - if self._is_receiving_msg: return True @@ -574,8 +597,6 @@ class Wcf(): pass else: callback(WxMsg(rsp.wxmsg)) - # 退出前关闭通信通道 - self.msg_socket.close() if self._is_receiving_msg: return True @@ -666,9 +687,9 @@ class Wcf(): friends = [] for cnt in self.get_contacts(): if (cnt["wxid"].endswith("@chatroom") or # 群聊 - cnt["wxid"].startswith("gh_") or # 公众号 - cnt["wxid"] in not_friends.keys() # 其他杂号 - ): + cnt["wxid"].startswith("gh_") or # 公众号 + cnt["wxid"] in not_friends.keys() # 其他杂号 + ): continue friends.append(cnt) @@ -843,7 +864,7 @@ class Wcf(): Returns: str: 成功返回存储路径;空字符串为失败,原因见日志。 """ - sleep(1) # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 + sleep(1) # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 if (not os.path.exists(extra)) and (self.download_attach(id, "", extra) != 0): self.LOG.error(f"下载失败") return "" @@ -870,7 +891,7 @@ class Wcf(): Returns: str: 成功返回存储路径;空字符串为失败,原因见日志。 """ - sleep(1) # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 + sleep(1) # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 base, _ = os.path.splitext(thumb) file_path = base + ".mp4" file_name = os.path.basename(file_path) diff --git a/clients/python/wcferry/wcf_pb2.py b/clients/python/wcferry/wcf_pb2.py index acb1981..36cf857 100644 --- a/clients/python/wcferry/wcf_pb2.py +++ b/clients/python/wcferry/wcf_pb2.py @@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xff\x03\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.MemberMgmtH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x12\x12\n\x04ui64\x18\x0c \x01(\x04\x42\x02\x30\x01H\x00\x12\x0e\n\x04\x66lag\x18\r \x01(\x08H\x00\x12\x1d\n\x03\x61tt\x18\x0e \x01(\x0b\x32\x0e.wcf.AttachMsgH\x00\x12\x1b\n\x02\x61m\x18\x0f \x01(\x0b\x32\r.wcf.AudioMsgH\x00\x12\x1b\n\x02rt\x18\x10 \x01(\x0b\x32\r.wcf.RichTextH\x00\x12\x19\n\x02pm\x18\x11 \x01(\x0b\x32\x0b.wcf.PatMsgH\x00\x12\x1d\n\x02\x66m\x18\x12 \x01(\x0b\x32\x0f.wcf.ForwardMsgH\x00\x42\x05\n\x03msg\"\xc7\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x12\x1a\n\x03ocr\x18\x0b \x01(\x0b\x32\x0b.wcf.OcrMsgH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xbe\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0e\n\x02id\x18\x03 \x01(\x04\x42\x02\x30\x01\x12\x0c\n\x04type\x18\x04 \x01(\r\x12\n\n\x02ts\x18\x05 \x01(\r\x12\x0e\n\x06roomid\x18\x06 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\t\x12\x0e\n\x06sender\x18\x08 \x01(\t\x12\x0c\n\x04sign\x18\t \x01(\t\x12\r\n\x05thumb\x18\n \x01(\t\x12\r\n\x05\x65xtra\x18\x0b \x01(\t\x12\x0b\n\x03xml\x18\x0c \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x04\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nMemberMgmt\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t\"9\n\tAttachMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05thumb\x18\x02 \x01(\t\x12\r\n\x05\x65xtra\x18\x03 \x01(\t\"\'\n\x08\x41udioMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x0b\n\x03\x64ir\x18\x02 \x01(\t\"y\n\x08RichText\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0e\n\x06\x64igest\x18\x04 \x01(\t\x12\x0b\n\x03url\x18\x05 \x01(\t\x12\x10\n\x08thumburl\x18\x06 \x01(\t\x12\x10\n\x08receiver\x18\x07 \x01(\t\"&\n\x06PatMsg\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\x0c\n\x04wxid\x18\x02 \x01(\t\"(\n\x06OcrMsg\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\t\".\n\nForwardMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x10\n\x08receiver\x18\x02 \x01(\t\"\xb7\x02\n\x08RoomData\x12)\n\x07members\x18\x01 \x03(\x0b\x32\x18.wcf.RoomData.RoomMember\x12\x14\n\x07\x66ield_2\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x0f\n\x07\x66ield_3\x18\x03 \x01(\x05\x12\x14\n\x07\x66ield_4\x18\x04 \x01(\x05H\x01\x88\x01\x01\x12\x10\n\x08\x63\x61pacity\x18\x05 \x01(\x05\x12\x14\n\x07\x66ield_6\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x0f\n\x07\x66ield_7\x18\x07 \x01(\x05\x12\x0f\n\x07\x66ield_8\x18\x08 \x01(\x05\x12\x0e\n\x06\x61\x64mins\x18\t \x03(\t\x1a\x45\n\nRoomMember\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x11\n\x04name\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05state\x18\x03 \x01(\x05\x42\x07\n\x05_nameB\n\n\x08_field_2B\n\n\x08_field_4B\n\n\x08_field_6*\xf2\x05\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x16\n\x12\x46UNC_GET_AUDIO_MSG\x10\x16\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x16\n\x12\x46UNC_SEND_RICH_TXT\x10%\x12\x15\n\x11\x46UNC_SEND_PAT_MSG\x10&\x12\x14\n\x10\x46UNC_FORWARD_MSG\x10\'\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x14\n\x10\x46UNC_REFRESH_PYQ\x10S\x12\x18\n\x14\x46UNC_DOWNLOAD_ATTACH\x10T\x12\x19\n\x15\x46UNC_GET_CONTACT_INFO\x10U\x12\x13\n\x0f\x46UNC_REVOKE_MSG\x10V\x12\x17\n\x13\x46UNC_REFRESH_QRCODE\x10W\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x11\n\rFUNC_EXEC_OCR\x10\x61\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10q\x12\x19\n\x15\x46UNC_INV_ROOM_MEMBERS\x10rB\r\n\x0b\x63om.iamteerb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xff\x03\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.MemberMgmtH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x12\x12\n\x04ui64\x18\x0c \x01(\x04\x42\x02\x30\x01H\x00\x12\x0e\n\x04\x66lag\x18\r \x01(\x08H\x00\x12\x1d\n\x03\x61tt\x18\x0e \x01(\x0b\x32\x0e.wcf.AttachMsgH\x00\x12\x1b\n\x02\x61m\x18\x0f \x01(\x0b\x32\r.wcf.AudioMsgH\x00\x12\x1b\n\x02rt\x18\x10 \x01(\x0b\x32\r.wcf.RichTextH\x00\x12\x19\n\x02pm\x18\x11 \x01(\x0b\x32\x0b.wcf.PatMsgH\x00\x12\x1d\n\x02\x66m\x18\x12 \x01(\x0b\x32\x0f.wcf.ForwardMsgH\x00\x42\x05\n\x03msg\"\xc7\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x12\x1a\n\x03ocr\x18\x0b \x01(\x0b\x32\x0b.wcf.OcrMsgH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xbe\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0e\n\x02id\x18\x03 \x01(\x04\x42\x02\x30\x01\x12\x0c\n\x04type\x18\x04 \x01(\r\x12\n\n\x02ts\x18\x05 \x01(\r\x12\x0e\n\x06roomid\x18\x06 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\t\x12\x0e\n\x06sender\x18\x08 \x01(\t\x12\x0c\n\x04sign\x18\t \x01(\t\x12\r\n\x05thumb\x18\n \x01(\t\x12\r\n\x05\x65xtra\x18\x0b \x01(\t\x12\x0b\n\x03xml\x18\x0c \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x04\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nMemberMgmt\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"S\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\x12\r\n\x05\x61lias\x18\x05 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t\"9\n\tAttachMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05thumb\x18\x02 \x01(\t\x12\r\n\x05\x65xtra\x18\x03 \x01(\t\"\'\n\x08\x41udioMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x0b\n\x03\x64ir\x18\x02 \x01(\t\"y\n\x08RichText\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0e\n\x06\x64igest\x18\x04 \x01(\t\x12\x0b\n\x03url\x18\x05 \x01(\t\x12\x10\n\x08thumburl\x18\x06 \x01(\t\x12\x10\n\x08receiver\x18\x07 \x01(\t\"&\n\x06PatMsg\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\x0c\n\x04wxid\x18\x02 \x01(\t\"(\n\x06OcrMsg\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\t\".\n\nForwardMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x10\n\x08receiver\x18\x02 \x01(\t\"\xb7\x02\n\x08RoomData\x12)\n\x07members\x18\x01 \x03(\x0b\x32\x18.wcf.RoomData.RoomMember\x12\x14\n\x07\x66ield_2\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x0f\n\x07\x66ield_3\x18\x03 \x01(\x05\x12\x14\n\x07\x66ield_4\x18\x04 \x01(\x05H\x01\x88\x01\x01\x12\x10\n\x08\x63\x61pacity\x18\x05 \x01(\x05\x12\x14\n\x07\x66ield_6\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x0f\n\x07\x66ield_7\x18\x07 \x01(\x05\x12\x0f\n\x07\x66ield_8\x18\x08 \x01(\x05\x12\x0e\n\x06\x61\x64mins\x18\t \x03(\t\x1a\x45\n\nRoomMember\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x11\n\x04name\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05state\x18\x03 \x01(\x05\x42\x07\n\x05_nameB\n\n\x08_field_2B\n\n\x08_field_4B\n\n\x08_field_6*\x86\x06\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x16\n\x12\x46UNC_GET_AUDIO_MSG\x10\x16\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x16\n\x12\x46UNC_SEND_RICH_TXT\x10%\x12\x15\n\x11\x46UNC_SEND_PAT_MSG\x10&\x12\x14\n\x10\x46UNC_FORWARD_MSG\x10\'\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x14\n\x10\x46UNC_REFRESH_PYQ\x10S\x12\x18\n\x14\x46UNC_DOWNLOAD_ATTACH\x10T\x12\x19\n\x15\x46UNC_GET_CONTACT_INFO\x10U\x12\x13\n\x0f\x46UNC_REVOKE_MSG\x10V\x12\x17\n\x13\x46UNC_REFRESH_QRCODE\x10W\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x11\n\rFUNC_EXEC_OCR\x10\x61\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10q\x12\x19\n\x15\x46UNC_INV_ROOM_MEMBERS\x10r\x12\x12\n\rFUNC_SHUTDOWN\x10\xff\x01\x42\r\n\x0b\x63om.iamteerb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -44,8 +44,8 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['_AUDIOMSG'].fields_by_name['id']._serialized_options = b'0\001' _globals['_FORWARDMSG'].fields_by_name['id']._loaded_options = None _globals['_FORWARDMSG'].fields_by_name['id']._serialized_options = b'0\001' - _globals['_FUNCTIONS']._serialized_start=2728 - _globals['_FUNCTIONS']._serialized_end=3482 + _globals['_FUNCTIONS']._serialized_start=2743 + _globals['_FUNCTIONS']._serialized_end=3517 _globals['_REQUEST']._serialized_start=19 _globals['_REQUEST']._serialized_end=530 _globals['_RESPONSE']._serialized_start=533 @@ -87,25 +87,25 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['_MEMBERMGMT']._serialized_start=1854 _globals['_MEMBERMGMT']._serialized_end=1897 _globals['_USERINFO']._serialized_start=1899 - _globals['_USERINFO']._serialized_end=1967 - _globals['_DECPATH']._serialized_start=1969 - _globals['_DECPATH']._serialized_end=2004 - _globals['_TRANSFER']._serialized_start=2006 - _globals['_TRANSFER']._serialized_end=2058 - _globals['_ATTACHMSG']._serialized_start=2060 - _globals['_ATTACHMSG']._serialized_end=2117 - _globals['_AUDIOMSG']._serialized_start=2119 - _globals['_AUDIOMSG']._serialized_end=2158 - _globals['_RICHTEXT']._serialized_start=2160 - _globals['_RICHTEXT']._serialized_end=2281 - _globals['_PATMSG']._serialized_start=2283 - _globals['_PATMSG']._serialized_end=2321 - _globals['_OCRMSG']._serialized_start=2323 - _globals['_OCRMSG']._serialized_end=2363 - _globals['_FORWARDMSG']._serialized_start=2365 - _globals['_FORWARDMSG']._serialized_end=2411 - _globals['_ROOMDATA']._serialized_start=2414 - _globals['_ROOMDATA']._serialized_end=2725 - _globals['_ROOMDATA_ROOMMEMBER']._serialized_start=2620 - _globals['_ROOMDATA_ROOMMEMBER']._serialized_end=2689 + _globals['_USERINFO']._serialized_end=1982 + _globals['_DECPATH']._serialized_start=1984 + _globals['_DECPATH']._serialized_end=2019 + _globals['_TRANSFER']._serialized_start=2021 + _globals['_TRANSFER']._serialized_end=2073 + _globals['_ATTACHMSG']._serialized_start=2075 + _globals['_ATTACHMSG']._serialized_end=2132 + _globals['_AUDIOMSG']._serialized_start=2134 + _globals['_AUDIOMSG']._serialized_end=2173 + _globals['_RICHTEXT']._serialized_start=2175 + _globals['_RICHTEXT']._serialized_end=2296 + _globals['_PATMSG']._serialized_start=2298 + _globals['_PATMSG']._serialized_end=2336 + _globals['_OCRMSG']._serialized_start=2338 + _globals['_OCRMSG']._serialized_end=2378 + _globals['_FORWARDMSG']._serialized_start=2380 + _globals['_FORWARDMSG']._serialized_end=2426 + _globals['_ROOMDATA']._serialized_start=2429 + _globals['_ROOMDATA']._serialized_end=2740 + _globals['_ROOMDATA_ROOMMEMBER']._serialized_start=2635 + _globals['_ROOMDATA_ROOMMEMBER']._serialized_end=2704 # @@protoc_insertion_point(module_scope)