mirror of
https://github.com/ttttupup/wxhelper.git
synced 2025-04-20 03:49:17 +08:00
Compare commits
183 Commits
3.9.0.28-s
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
24bb7212fa | ||
|
1528b66b94 | ||
|
62a641d7f7 | ||
|
49188b7ef0 | ||
|
e4656e5139 | ||
|
0bd8b47da0 | ||
|
7a5a8a90df | ||
|
b5cc83be77 | ||
|
3b5f191bf2 | ||
|
dff024745c | ||
|
8b6e2d7160 | ||
|
5d7c1d1d48 | ||
|
d574af863a | ||
|
632d4222fa | ||
|
ee7d4ee9a3 | ||
|
1ba2ed87b7 | ||
|
2863f8258f | ||
|
fa31de81e0 | ||
|
758b773d99 | ||
|
b17af7a9e0 | ||
|
b364885daf | ||
|
427b43304c | ||
|
0593dbe79e | ||
|
13f182a95b | ||
|
1718ddefdd | ||
|
4c3c02283e | ||
|
bb46b9b8d9 | ||
|
e806914775 | ||
|
e5ead96c05 | ||
|
938e7e9bc2 | ||
|
8acad316a9 | ||
|
a5a2f0f17d | ||
|
3b76b2332c | ||
|
5088f5171b | ||
|
d8c8078f5c | ||
|
8bdc8c7e46 | ||
|
212613e3b6 | ||
|
608c6d26de | ||
|
2f6760e28e | ||
|
6436ce072a | ||
|
58caad4f34 | ||
|
bdf91943f3 | ||
|
41b3b6b18d | ||
|
129bb55c36 | ||
|
e9a6e0da57 | ||
|
e6e72dd492 | ||
|
9b4d326d63 | ||
|
64ba6007a8 | ||
|
f9b859baca | ||
|
ae44434662 | ||
|
4ab28cfc03 | ||
|
1268d37a57 | ||
|
e6c57f8ede | ||
|
17aff3b6aa | ||
|
51a4693330 | ||
|
7e06eaf4e4 | ||
|
b996a9d91c | ||
|
e0c946bf72 | ||
|
bf52de1560 | ||
|
2ed409e7b0 | ||
|
6f97a499b8 | ||
|
d33df9391e | ||
|
23fc28d011 | ||
|
e373b2f063 | ||
|
7e0b877d99 | ||
|
2ee16e36bf | ||
|
91bd8f5977 | ||
|
a9029e4a3d | ||
|
53a7938335 | ||
|
ce72262d58 | ||
|
bdf19fe4e4 | ||
|
44623e5b2e | ||
|
1824d6507c | ||
|
9b643901cd | ||
|
7ab8d4733f | ||
|
539d026146 | ||
|
272d62b0e9 | ||
|
0bbba4a902 | ||
|
5a9f91b425 | ||
|
66ea9d5428 | ||
|
f25f04f54e | ||
|
8229a15089 | ||
|
6d8218428f | ||
|
88e5c8fb9c | ||
|
6062a75b02 | ||
|
7ff6242c18 | ||
|
1aa7182a9a | ||
|
777faa5673 | ||
|
a94dfa9f71 | ||
|
a3d689d80a | ||
|
5a91d975a6 | ||
|
a2ebb527f9 | ||
|
7d9aa01d8d | ||
|
6875302b04 | ||
|
e39e4362fe | ||
|
292b9da378 | ||
|
bce22ee3df | ||
|
8472a5c36e | ||
|
e058879295 | ||
|
74831d9d8b | ||
|
51498cde8a | ||
|
391011b696 | ||
|
b5bb1e4e77 | ||
|
2d16226d10 | ||
|
37b1f727b8 | ||
|
003ceca7d6 | ||
|
3f66d7b6d0 | ||
|
81ea48b534 | ||
|
fa27a73355 | ||
|
fad5920bbe | ||
|
472c53b863 | ||
|
2ceece90f6 | ||
|
3b4a4aa3da | ||
|
4c9a292c77 | ||
|
a4540117e3 | ||
|
c0b55da015 | ||
|
34eb14edb2 | ||
|
f9a7924658 | ||
|
6105789fcc | ||
|
40940c0b7a | ||
|
620d4d38db | ||
|
0942a691ed | ||
|
9248d2f91a | ||
|
233b9f11f4 | ||
|
dffdbfb9d7 | ||
|
8279303614 | ||
|
5ba4f72d5c | ||
|
7220b23cb3 | ||
|
bfe5b0ee12 | ||
|
384aade1a8 | ||
|
c453b8fb30 | ||
|
af54cbcc82 | ||
|
61c9e6f8c1 | ||
|
13e7e3bba3 | ||
|
cae25147f1 | ||
|
c590a95425 | ||
|
3626c40492 | ||
|
b56f93e372 | ||
|
4a3ef64467 | ||
|
6a58ad71b1 | ||
|
789b4b6e75 | ||
|
1a1a1fe964 | ||
|
93798ddeca | ||
|
df57bb3317 | ||
|
a097ae334a | ||
|
4b88354f46 | ||
|
437f657faf | ||
|
8ea5cbec8a | ||
|
b8db52d522 | ||
|
e52a9b87ed | ||
|
5dd3584122 | ||
|
20250dd7ca | ||
|
055adf5eab | ||
|
75ca6f9477 | ||
|
ee90ec3755 | ||
|
3ebb4ec269 | ||
|
259d40c73f | ||
|
63632ad8cd | ||
|
ae2d5ba437 | ||
|
a1cff41756 | ||
|
259cb4eeb3 | ||
|
e66d6c35e3 | ||
|
64d49d3a94 | ||
|
95160ae175 | ||
|
6a89f08519 | ||
|
b1e267d991 | ||
|
5ef79f9b93 | ||
|
96bbf8be85 | ||
|
0db008e298 | ||
|
60a1b59f73 | ||
|
f324db9b7b | ||
|
380663b5b5 | ||
|
bc731b47e4 | ||
|
4060da487b | ||
|
5e2a32713a | ||
|
1dad7fe3fd | ||
|
95d8bb50c9 | ||
|
81e5150bef | ||
|
fe56d50041 | ||
|
ca66f987f6 | ||
|
8670d70147 | ||
|
6a5e174f27 | ||
|
65625a573e |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -30,6 +30,6 @@
|
|||||||
#*.exe
|
#*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
/out
|
|
||||||
CMakePresets.json
|
CMakePresets.json
|
||||||
.vscode
|
.vscode
|
||||||
|
out
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "spdlog"]
|
||||||
|
path = spdlog
|
||||||
|
url = https://github.com/gabime/spdlog
|
@ -1,31 +1,48 @@
|
|||||||
cmake_minimum_required(VERSION 3.0.0)
|
cmake_minimum_required(VERSION 3.0.0)
|
||||||
|
# include(ExternalProject)
|
||||||
project(wxhelper VERSION 1.0.0)
|
project(wxhelper VERSION 1.0.0)
|
||||||
|
enable_language(ASM_MASM)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# SET(CMAKE_ASM_NASM_FLAGS "-w0")
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D '_UNICODE' /D 'UNICODE'")
|
|
||||||
|
|
||||||
file(GLOB CPP_FILES ${PROJECT_SOURCE_DIR}/src/*.cc ${PROJECT_SOURCE_DIR}/src/*.cpp)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D '_UNICODE' /D 'UNICODE' ")
|
||||||
|
|
||||||
|
file(GLOB CPP_FILES ${PROJECT_SOURCE_DIR}/src/*.cc ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/src/*.c )
|
||||||
|
|
||||||
|
file(GLOB ASM_FILES ${PROJECT_SOURCE_DIR}/src/*.asm )
|
||||||
|
|
||||||
|
include_directories(${VCPKG_INSTALLED_DIR}/x64-windows/include ${PROJECT_SOURCE_DIR}/spdlog/include ${DETOURS_INCLUDE_DIRS})
|
||||||
|
# include_directories(${VCPKG_INSTALLED_DIR}/x64-windows/include ${PROJECT_SOURCE_DIR}/spdlog/include )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
include_directories(${VCPKG_INSTALLED_DIR}/x86-windows/include)
|
add_subdirectory(spdlog)
|
||||||
|
add_subdirectory(source)
|
||||||
# add_subdirectory(3rd)
|
|
||||||
|
|
||||||
|
# find_package(spdlog CONFIG REQUIRED)
|
||||||
|
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
find_package(unofficial-mongoose CONFIG REQUIRED)
|
|
||||||
|
|
||||||
add_library(wxhelper SHARED ${CPP_FILES} )
|
find_path(DETOURS_INCLUDE_DIRS "detours/detours.h")
|
||||||
|
find_library(DETOURS_LIBRARY detours REQUIRED)
|
||||||
|
|
||||||
|
|
||||||
|
add_library(wxhelper SHARED ${CPP_FILES} ${ASM_FILES} )
|
||||||
|
|
||||||
|
|
||||||
#target_include_directories(wxhelper PUBLIC 3rd/mongoose )
|
|
||||||
|
|
||||||
target_link_libraries(wxhelper PRIVATE nlohmann_json::nlohmann_json)
|
target_link_libraries(wxhelper PRIVATE nlohmann_json::nlohmann_json)
|
||||||
target_link_libraries(wxhelper PRIVATE unofficial::mongoose::mongoose)
|
target_link_libraries(wxhelper PRIVATE spdlog::spdlog spdlog::spdlog_header_only)
|
||||||
|
target_link_libraries(wxhelper PRIVATE ${DETOURS_LIBRARY})
|
||||||
|
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(wxhelper PROPERTIES LINKER_LANGUAGE C
|
SET_TARGET_PROPERTIES(wxhelper PROPERTIES LINKER_LANGUAGE C
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
|
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
|
||||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 ttttupup
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
1633
doc/3.9.2.23.md
Normal file
1633
doc/3.9.2.23.md
Normal file
File diff suppressed because it is too large
Load Diff
1829
doc/3.9.5.81.md
Normal file
1829
doc/3.9.5.81.md
Normal file
File diff suppressed because it is too large
Load Diff
640
doc/postman.json
Normal file
640
doc/postman.json
Normal file
@ -0,0 +1,640 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"name": "Wechat Hook 395",
|
||||||
|
"_postman_id": "d2b6a4f2-6d7d-4a21-9bbf-65b5a5a3a5a",
|
||||||
|
"description": "A collection of Wechat Hook 395 API requests.",
|
||||||
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||||
|
},
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"name": "checkLogin",
|
||||||
|
"request": {
|
||||||
|
"url": {
|
||||||
|
"raw": "http://127.0.0.1:19088/api/checkLogin",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"127.0.0.1"
|
||||||
|
],
|
||||||
|
"port": "19088",
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"checkLogin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to check login status."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "userInfo",
|
||||||
|
"request": {
|
||||||
|
"url": {
|
||||||
|
"raw": "http://127.0.0.1:19088/api/userInfo",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"127.0.0.1"
|
||||||
|
],
|
||||||
|
"port": "19088",
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"userInfo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to get user information."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sendTextMsg",
|
||||||
|
"request": {
|
||||||
|
"url": {
|
||||||
|
"raw": "http://127.0.0.1:19088/api/sendTextMsg",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"127.0.0.1"
|
||||||
|
],
|
||||||
|
"port": "19088",
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"sendTextMsg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxid\": \"filehelper\",\"msg\": \"12www\"}"
|
||||||
|
},
|
||||||
|
"description": "API to send text messages."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sendImagesMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/sendImagesMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxid\": \"filehelper\",\"imagePath\": \"C:\\pic.png\"}"
|
||||||
|
},
|
||||||
|
"description": "API to send image messages."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sendFileMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/sendFileMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxid\": \"filehelper\",\"filePath\": \"C:\\test.zip\"}"
|
||||||
|
},
|
||||||
|
"description": "API to send file messages."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hookSyncMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/hookSyncMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"port\": \"19099\",\"ip\": \"127.0.0.1\",\"url\": \"http://localhost:8080\",\"timeout\": \"3000\",\"enableHttp\": \"0\"}"
|
||||||
|
},
|
||||||
|
"description": "API to hook sync messages."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unhookSyncMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/unhookSyncMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to unhook sync messages."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getContactList",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getContactList",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to get the contact list."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getDBInfo",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getDBInfo",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to get database information."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "execSql",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/execSql",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"dbHandle\": 1713425147584,\"sql\": \"select * from MSG where localId =100;\"}"
|
||||||
|
},
|
||||||
|
"description": "API to execute SQL queries."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getChatRoomDetailInfo",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getChatRoomDetailInfo",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123333@chatroom\"}"
|
||||||
|
},
|
||||||
|
"description": "API to get chat room detail information."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addMemberToChatRoom",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/addMemberToChatRoom",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123@chatroom\",\"memberIds\": \"wxid_123\"}"
|
||||||
|
},
|
||||||
|
"description": "API to add member to chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "delMemberFromChatRoom",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/delMemberFromChatRoom",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"21363231004@chatroom\",\"memberIds\": \"wxid_123\"}"
|
||||||
|
},
|
||||||
|
"description": "API to delete member from chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "modifyNickname",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/modifyNickname",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123@chatroom\",\"wxid\": \"wxid_123\",\"nickName\": \"test\"}"
|
||||||
|
},
|
||||||
|
"description": "API to modify a nickname in a chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getMemberFromChatRoom",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getMemberFromChatRoom",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123@chatroom\"}"
|
||||||
|
},
|
||||||
|
"description": "API to get members from a chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "topMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/topMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"msgId\": 1222222}"
|
||||||
|
},
|
||||||
|
"description": "API to top a message."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "removeTopMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/removeTopMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123@chatroom\",\"msgId\": 123}"
|
||||||
|
},
|
||||||
|
"description": "API to remove a topped message."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "InviteMemberToChatRoom",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/InviteMemberToChatRoom",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123@chatroom\",\"memberIds\": \"wxid_123\"}"
|
||||||
|
},
|
||||||
|
"description": "API to invite members to a chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hookLog",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/hookLog",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to hook logs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unhookLog",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/unhookLog",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to unhook logs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "createChatRoom",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/createChatRoom",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"memberIds\": \"wxid_8yn4k908tdqp22,wxid_oyb662qhop4422\"}"
|
||||||
|
},
|
||||||
|
"description": "API to create a chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "quitChatRoom",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/quitChatRoom",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"chatRoomId\": \"123@chatroom\"}"
|
||||||
|
},
|
||||||
|
"description": "API to quit a chat room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "forwardMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/forwardMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxid\": \"filehelper\",\"msgId\": \"12331\"}"
|
||||||
|
},
|
||||||
|
"description": "API to forward a message."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getSNSFirstPage",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getSNSFirstPage",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"description": "API to get the first page of SNS data."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getSNSNextPage",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getSNSNextPage",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"snsId\": \"\"}"
|
||||||
|
},
|
||||||
|
"description": "API to get the next page of SNS data."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addFavFromMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/addFavFromMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"msgId\": \"1222222\"}"
|
||||||
|
},
|
||||||
|
"description": "API to add a favorite from a message."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addFavFromImage",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/addFavFromImage",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxid\": \"\",\"imagePath\": \"\"}"
|
||||||
|
},
|
||||||
|
"description": "API to add a favorite from an image."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getContactProfile",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getContactProfile",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxid\": \"\"}"
|
||||||
|
},
|
||||||
|
"description": "API to get contact profile."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sendAtText",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/sendAtText",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"wxids\": \"notify@all\",\"chatRoomId\": \"123@chatroom\",\"msg\": \"你好啊\"}"
|
||||||
|
},
|
||||||
|
"description": "API to send an at-text message."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "forwardPublicMsg",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/forwardPublicMsg",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"appName\": \"\",\"userName\": \"\",\"title\": \"\",\"url\": \"\",\"thumbUrl\": \"\",\"digest\": \"\",\"wxid\": \"filehelper\"}"
|
||||||
|
},
|
||||||
|
"description": "API to forward a public message."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "forwardPublicMsgByMsgId",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/forwardPublicMsgByMsgId",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"msgId\": 123,\"wxid\": \"filehelper\"}"
|
||||||
|
},
|
||||||
|
"description": "API to forward a public message by message ID."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "downloadAttach",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/downloadAttach",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"msgId\": 123}"
|
||||||
|
},
|
||||||
|
"description": "API to download an attachment."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "decodeImage",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/decodeImage",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"filePath\": \"C:\\66664816980131.dat\",\"storeDir\": \"C:\\test\"}"
|
||||||
|
},
|
||||||
|
"description": "API to decode an image."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getVoiceByMsgId",
|
||||||
|
"request": {
|
||||||
|
"url": "http://127.0.0.1:19088/api/getVoiceByMsgId",
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"description": "Specify that the request body is in JSON format."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\"msgId\": 7880439644200,\"storeDir\": \"c:\\test\"}"
|
||||||
|
},
|
||||||
|
"description": "API to get voice by message ID."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
go_client/main.go
Normal file
11
go_client/main.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_client/tcpserver"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
|
tcpserver.Listen(19099)
|
||||||
|
}
|
44
go_client/tcpserver/tcpserver.go
Normal file
44
go_client/tcpserver/tcpserver.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Listen(port int) {
|
||||||
|
p := strconv.Itoa(port)
|
||||||
|
adress := "127.0.0.1:" + p
|
||||||
|
ln, err := net.Listen("tcp", adress)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
log.Println("tcp server started")
|
||||||
|
for {
|
||||||
|
conn, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handle(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(conn net.Conn) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Println("发生了未处理的异常", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
defer conn.Close()
|
||||||
|
scanner := bufio.NewScanner(conn)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Bytes()
|
||||||
|
log.Println("收到消息:", string(line))
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Println("错误:", err)
|
||||||
|
}
|
||||||
|
}
|
33
java_client/.gitignore
vendored
Normal file
33
java_client/.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
21
java_client/README.md
Normal file
21
java_client/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
环境为jdk17
|
||||||
|
执行之后会在当前项目所处磁盘根路径生成一个exec文件夹,然后会把src/main/resources/exec下的文件放在那避免因为路径问题出错
|
||||||
|
java_client/src/main/resources/exec/c.exe 为注入器,只不过把名字改短了,更新的话换成最新版,改个名字就行, wxhelper.dll同理
|
||||||
|
|
||||||
|
项目启动之后,会生成一个tcp服务端,用来接受hook信息,然后把接收的信息放在队列中,之后用一个线程去循环处理消息.
|
||||||
|
具体实现可以看
|
||||||
|
```com.example.wxhk.tcp.vertx```包下的三个文件
|
||||||
|
|
||||||
|
com.example.wxhk.tcp.vertx.VertxTcp 这个是tcp服务端,接受信息
|
||||||
|
|
||||||
|
com.example.wxhk.tcp.vertx.InitWeChat 微信环境初始化
|
||||||
|
|
||||||
|
com.example.wxhk.tcp.vertx.ArrHandle 循环消息处理
|
||||||
|
|
||||||
|
com.example.wxhk.server.WxSmgServer 为消息处理接口,实现其中的方法即可
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
启动项目需要去修改配置文件的微信路径
|
185
java_client/pom.xml
Normal file
185
java_client/pom.xml
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.2</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>wxhk</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>wxhk</name>
|
||||||
|
<description>wxhk</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
<vertx-web-client.version>4.5.3</vertx-web-client.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||||
|
<!-- <artifactId>spring-boot-starter-thymeleaf</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||||
|
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>4.1.105.Final</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>4.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.vertx</groupId>
|
||||||
|
<artifactId>vertx-core</artifactId>
|
||||||
|
<version>${vertx-web-client.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.vertx</groupId>
|
||||||
|
<artifactId>vertx-web</artifactId>
|
||||||
|
<version>${vertx-web-client.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.vertx</groupId>
|
||||||
|
<artifactId>vertx-web-client</artifactId>
|
||||||
|
<version>${vertx-web-client.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.vertx</groupId>
|
||||||
|
<artifactId>vertx-mysql-client</artifactId>
|
||||||
|
<version>${vertx-web-client.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>6.0.0.M3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.15.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<!-- 排除文件配置 -->
|
||||||
|
<!-- <excludes> -->
|
||||||
|
<!-- <exclude>*.**</exclude> -->
|
||||||
|
<!-- <exclude>*/**.xml</exclude> -->
|
||||||
|
<!-- </excludes> -->
|
||||||
|
|
||||||
|
<!-- 包含文件配置,现在只打包 com 文件夹 -->
|
||||||
|
<includes>
|
||||||
|
<include>
|
||||||
|
**/com/example/wxhk/**
|
||||||
|
</include>
|
||||||
|
</includes>
|
||||||
|
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<!-- 配置加入依赖包 -->
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<classpathPrefix>lib/</classpathPrefix>
|
||||||
|
<useUniqueVersions>false</useUniqueVersions>
|
||||||
|
<!-- Spring Boot 启动类(自行修改) -->
|
||||||
|
<mainClass>com.example.wxhk.WxhkApplication</mainClass>
|
||||||
|
</manifest>
|
||||||
|
<manifestEntries>
|
||||||
|
<!-- 外部资源路径加入 manifest.mf 的 Class-Path -->
|
||||||
|
<Class-Path>resources/</Class-Path>
|
||||||
|
</manifestEntries>
|
||||||
|
</archive>
|
||||||
|
<!-- jar 输出目录 -->
|
||||||
|
<outputDirectory>${project.build.directory}/pack/</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<!-- 复制依赖 -->
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<!-- 依赖包 输出目录 -->
|
||||||
|
<outputDirectory>${project.build.directory}/pack/lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<!-- 复制资源 -->
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<!-- 资源文件 输出目录 -->
|
||||||
|
<outputDirectory>${project.build.directory}/pack/resources</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.example.wxhk;
|
||||||
|
|
||||||
|
import io.vertx.core.Vertx;
|
||||||
|
import io.vertx.core.VertxOptions;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class WxhkApplication {
|
||||||
|
public static final Vertx vertx;
|
||||||
|
|
||||||
|
static {
|
||||||
|
vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(5).setEventLoopPoolSize(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
//ConsoleInject.exe -i WeChat.exe -p D:\wxhelper.dll
|
||||||
|
//ConsoleApplication.exe -I 4568 -p C:\wxhelper.dll -m 17484 -P 1888
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(WxhkApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.example.wxhk.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受到的微信消息类型
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/26
|
||||||
|
*/
|
||||||
|
public enum WxMsgType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
私聊信息(1),
|
||||||
|
好友请求(37),
|
||||||
|
收到名片(42),
|
||||||
|
表情(47),
|
||||||
|
转账和收款(49),
|
||||||
|
收到转账之后或者文件助手等信息(51),
|
||||||
|
|
||||||
|
入群(10000),
|
||||||
|
/**
|
||||||
|
* 扫码触发,会触发2次, 有一次有编号,一次没有,还有登陆之后也有,很多情况都会调用这个
|
||||||
|
*/
|
||||||
|
扫码触发(10002),
|
||||||
|
|
||||||
|
;
|
||||||
|
Integer type;
|
||||||
|
|
||||||
|
WxMsgType(Integer type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.example.wxhk.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
|
||||||
|
public class WxMsgController {
|
||||||
|
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
11
java_client/src/main/java/com/example/wxhk/infe/Resp.java
Normal file
11
java_client/src/main/java/com/example/wxhk/infe/Resp.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.wxhk.infe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http 响应
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
public interface Resp extends java.io.Serializable{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
17
java_client/src/main/java/com/example/wxhk/infe/SendMsg.java
Normal file
17
java_client/src/main/java/com/example/wxhk/infe/SendMsg.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.example.wxhk.infe;
|
||||||
|
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http接口请求的基础接口
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
public interface SendMsg<T> extends java.io.Serializable{
|
||||||
|
|
||||||
|
default JsonObject toJson(){
|
||||||
|
return JsonObject.mapFrom(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.example.wxhk.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私聊
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/26
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class PrivateChatMsg implements Serializable {
|
||||||
|
|
||||||
|
String path;
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
/**
|
||||||
|
* 当是群聊的时候 为群id,否则为微信id
|
||||||
|
*/
|
||||||
|
private String fromGroup;
|
||||||
|
/**
|
||||||
|
* 微信id
|
||||||
|
*/
|
||||||
|
private String fromUser;
|
||||||
|
private Integer isSendMsg;
|
||||||
|
/**
|
||||||
|
* 1通过手机发送
|
||||||
|
*/
|
||||||
|
private Integer isSendByPhone;
|
||||||
|
private Long msgId;
|
||||||
|
private Integer pid;
|
||||||
|
private String sign;
|
||||||
|
private String signature;
|
||||||
|
private String time;
|
||||||
|
private Integer timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对用户,如果是文件助手是filehelper
|
||||||
|
*/
|
||||||
|
private String toUser;
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.example.wxhk.model.dto;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付信息
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @param receiverUsername 付款人
|
||||||
|
* @param decimal 收款金额
|
||||||
|
* @param remark 备注
|
||||||
|
* @param transcationid
|
||||||
|
* @param transferid
|
||||||
|
* @date 2023/06/06
|
||||||
|
*/
|
||||||
|
public record PayoutInformation(String receiverUsername, BigDecimal decimal, String remark,String transcationid,String transferid) implements java.io.Serializable {
|
||||||
|
|
||||||
|
public PayoutInformation(String receiverUsername, BigDecimal decimal, String remark) {
|
||||||
|
this(receiverUsername, decimal, remark, null, null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加wxid 好友
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AddFriends implements SendMsg<AddFriends> {
|
||||||
|
String wxid;
|
||||||
|
/**
|
||||||
|
* 验证信息
|
||||||
|
*/
|
||||||
|
String msg;
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认收款
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ConfirmThePayment implements SendMsg<ConfirmThePayment> {
|
||||||
|
/**
|
||||||
|
* 转账人微信id,从hook的消息中获取
|
||||||
|
*/
|
||||||
|
String wxid;
|
||||||
|
/**
|
||||||
|
* 从hook的消息中获取对应的字段内容
|
||||||
|
*/
|
||||||
|
String transcationId;
|
||||||
|
/**
|
||||||
|
* 从hook的消息中获取对应的字段内容。
|
||||||
|
*/
|
||||||
|
String transferId;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过手机或者qq查找微信
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class FindWeChat implements SendMsg<FindWeChat> {
|
||||||
|
/**
|
||||||
|
* 通过 手机或qq查询信息
|
||||||
|
*/
|
||||||
|
String keyword;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转发消息
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ForwardMessages implements SendMsg<ForwardMessages> {
|
||||||
|
/**
|
||||||
|
* 消息接收人wxid
|
||||||
|
*/
|
||||||
|
String wxid;
|
||||||
|
/**
|
||||||
|
* 消息id
|
||||||
|
*/
|
||||||
|
String msgid;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取群成员
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class GetGroupMembers implements SendMsg<GetGroupMembers> {
|
||||||
|
String chatRoomId;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取群成员昵称
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class GetsTheNicknameOfAGroupMember implements SendMsg<GetsTheNicknameOfAGroupMember> {
|
||||||
|
/**
|
||||||
|
* 聊天室id
|
||||||
|
*/
|
||||||
|
String chatRoomId;
|
||||||
|
/**
|
||||||
|
* 成员id
|
||||||
|
*/
|
||||||
|
String memberId;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加群成员
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class IncreaseGroupMembership implements SendMsg<IncreaseGroupMembership> {
|
||||||
|
/**
|
||||||
|
* 聊天室id
|
||||||
|
*/
|
||||||
|
String chatRoomId;
|
||||||
|
/**
|
||||||
|
* 成员id,以,分割
|
||||||
|
*/
|
||||||
|
String memberIds;
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启hook
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class OpenHook implements SendMsg<OpenHook> {
|
||||||
|
String port;
|
||||||
|
String ip;
|
||||||
|
/**
|
||||||
|
* 0/1 :1.启用http 0.不启用http
|
||||||
|
*/
|
||||||
|
boolean enableHttp;
|
||||||
|
/**
|
||||||
|
* 超时时间,单位ms
|
||||||
|
*/
|
||||||
|
String timeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http的请求地址,enableHttp=1时,不能为空
|
||||||
|
*/
|
||||||
|
String url;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送at文本
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class SendAtText implements SendMsg<SendAtText> {
|
||||||
|
/**
|
||||||
|
* 聊天室id,群聊用
|
||||||
|
*/
|
||||||
|
String chatRoomId;
|
||||||
|
/**
|
||||||
|
* 群聊的时候用at多个用逗号隔开,@所有人则是<b>notify@all</b>
|
||||||
|
*/
|
||||||
|
String wxids;
|
||||||
|
|
||||||
|
String msg;
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送文件
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class SendFile implements SendMsg<SendFile> {
|
||||||
|
String wxid;
|
||||||
|
/**
|
||||||
|
* 发送文件路径
|
||||||
|
* "filePath": "C:/Users/123.txt"
|
||||||
|
*/
|
||||||
|
String filePath;
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送图片
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class SendImg implements SendMsg<SendImg> {
|
||||||
|
String wxid;
|
||||||
|
/**
|
||||||
|
* 发送图片接口
|
||||||
|
* "imagePath": "C:/Users/123.png"
|
||||||
|
*/
|
||||||
|
String imagePath;
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http请求参数
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class SendMsg {
|
||||||
|
/**
|
||||||
|
* wxid
|
||||||
|
*/
|
||||||
|
String wxid;
|
||||||
|
/**
|
||||||
|
* 消息内容
|
||||||
|
*/
|
||||||
|
String msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聊天室id,群聊用
|
||||||
|
*/
|
||||||
|
String chatRoomId;
|
||||||
|
/**
|
||||||
|
* 成员id
|
||||||
|
*/
|
||||||
|
String memberId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群聊的时候用at多个用逗号隔开,@所有人则是<b>notify@all</b>
|
||||||
|
*/
|
||||||
|
String wxids;
|
||||||
|
/**
|
||||||
|
* 发送图片接口
|
||||||
|
* "imagePath": "C:/Users/123.png"
|
||||||
|
*/
|
||||||
|
String imagePath;
|
||||||
|
/**
|
||||||
|
* 发送文件路径
|
||||||
|
* "filePath": "C:/Users/123.txt"
|
||||||
|
*/
|
||||||
|
String filePath;
|
||||||
|
/**
|
||||||
|
* 通过 手机或qq查询信息
|
||||||
|
*/
|
||||||
|
String keyword;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送文本
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class SendText implements SendMsg<SendText> {
|
||||||
|
String wxid;
|
||||||
|
String msg;
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.example.wxhk.model.request;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.SendMsg;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过好友请求
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ThroughFriends implements SendMsg<ThroughFriends> {
|
||||||
|
/**
|
||||||
|
* 添加好友消息内容里的encryptusername
|
||||||
|
*/
|
||||||
|
String v3;
|
||||||
|
/**
|
||||||
|
* 添加好友消息内容里的ticket
|
||||||
|
*/
|
||||||
|
String v4;
|
||||||
|
/**
|
||||||
|
* 好友权限,0是无限制,1是不让他看我,2是不看他,3是1+2
|
||||||
|
*/
|
||||||
|
String permission;
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.example.wxhk.model.response;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.Resp;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 联系人列表
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class ContactList implements Resp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* code : 1
|
||||||
|
* data : [{"customAccount":"","delFlag":0,"type":1,"userName":"朋友推荐消息","verifyFlag":0,"wxid":"fmessage"},{"customAccount":"tencent_cloud","delFlag":0,"type":3,"userName":"腾讯云助手","verifyFlag":24,"wxid":"gh_a73e2407e0f8"},{"customAccount":"","delFlag":0,"type":1,"userName":"语音记事本","verifyFlag":0,"wxid":"medianote"},{"customAccount":"","delFlag":0,"type":1,"userName":"漂流瓶","verifyFlag":0,"wxid":"floatbottle"},{"customAccount":"jys-wt","delFlag":0,"type":8651011,"userName":"时光似水戏流年","verifyFlag":0,"wxid":"wxid_gf1fogt5a0pq22"},{"customAccount":"wxzhifu","delFlag":0,"type":3,"userName":"微信支付","verifyFlag":24,"wxid":"gh_3dfda90e39d6"},{"customAccount":"dhkzfr","delFlag":0,"type":3,"userName":"阿芙(代发)","verifyFlag":0,"wxid":"wxid_kh16lri40gzj22"},{"customAccount":"","delFlag":0,"type":3,"userName":"文件传输助手","verifyFlag":0,"wxid":"filehelper"},{"customAccount":"","delFlag":0,"type":3,"userName":"fff","verifyFlag":0,"wxid":"24964676359@chatroom"},{"customAccount":"","delFlag":0,"type":2,"userName":"最美阿芙","verifyFlag":0,"wxid":"23793178249@chatroom"},{"customAccount":"afu943344","delFlag":0,"type":2,"userName":"A-阿芙4号-LOL永劫云顶出租-代发","verifyFlag":0,"wxid":"wxid_1gxthknqbmwv22"},{"customAccount":"","delFlag":0,"type":3,"userName":"微信收款助手","verifyFlag":24,"wxid":"gh_f0a92aa7146c"},{"customAccount":"","delFlag":0,"type":0,"userName":"","verifyFlag":0,"wxid":"25984984710827869@openim"}]
|
||||||
|
* result : OK
|
||||||
|
*/
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
private String result;
|
||||||
|
private List<DataBean> data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public static class DataBean implements Serializable {
|
||||||
|
/**
|
||||||
|
* customAccount :
|
||||||
|
* delFlag : 0
|
||||||
|
* type : 1
|
||||||
|
* userName : 朋友推荐消息
|
||||||
|
* verifyFlag : 0
|
||||||
|
* wxid : fmessage
|
||||||
|
*/
|
||||||
|
|
||||||
|
private String customAccount;
|
||||||
|
private Integer delFlag;
|
||||||
|
private Integer type;
|
||||||
|
private String userName;
|
||||||
|
private Integer verifyFlag;
|
||||||
|
private String wxid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.example.wxhk.model.response;
|
||||||
|
|
||||||
|
import com.example.wxhk.infe.Resp;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class GroupMembers implements Resp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* code : 1
|
||||||
|
* data : {"admin":"wxid_gf1fogt5a0pq22","chatRoomId":"24964676359@chatroom","members":"wxid_gf1fogt5a0pq22^Gwxid_4yr8erik0uho22"}
|
||||||
|
* result : OK
|
||||||
|
*/
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
private DataBean data;
|
||||||
|
private String result;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class DataBean implements Serializable {
|
||||||
|
/**
|
||||||
|
* admin : wxid_gf1fogt5a0pq22
|
||||||
|
* chatRoomId : 24964676359@chatroom
|
||||||
|
* members : wxid_gf1fogt5a0pq22^Gwxid_4yr8erik0uho22
|
||||||
|
*/
|
||||||
|
|
||||||
|
private String admin;
|
||||||
|
private String chatRoomId;
|
||||||
|
private String members;
|
||||||
|
}
|
||||||
|
}
|
256
java_client/src/main/java/com/example/wxhk/msg/WxMsgHandle.java
Normal file
256
java_client/src/main/java/com/example/wxhk/msg/WxMsgHandle.java
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
package com.example.wxhk.msg;
|
||||||
|
|
||||||
|
import com.example.wxhk.constant.WxMsgType;
|
||||||
|
import com.example.wxhk.model.PrivateChatMsg;
|
||||||
|
import com.example.wxhk.model.dto.PayoutInformation;
|
||||||
|
import com.example.wxhk.server.WxSmgServer;
|
||||||
|
import com.example.wxhk.tcp.vertx.InitWeChat;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.dromara.hutool.core.util.XmlUtil;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WxMsgHandle {
|
||||||
|
public static final ConcurrentHashMap<Integer, Handle> map = new ConcurrentHashMap<>(32);
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
/**
|
||||||
|
* 文件传输助手
|
||||||
|
*/
|
||||||
|
public static final String FILEHELPER = "filehelper";
|
||||||
|
/**
|
||||||
|
* 收款码缓存 因为有2段信息,一段是交易id,里面可以解析出来源方,二段解析出金额
|
||||||
|
*/
|
||||||
|
public static ConcurrentHashMap<String, String> collection_code_caching = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public static WxSmgServer wxSmgServer;
|
||||||
|
/**
|
||||||
|
* 看
|
||||||
|
*/
|
||||||
|
public static final ReentrantReadWriteLock LOOK = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setWxSmgServer(WxSmgServer wxSmgServer) {
|
||||||
|
WxMsgHandle.wxSmgServer = wxSmgServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
add(chatMsg -> {
|
||||||
|
if(Objects.equals(chatMsg.getToUser(), FILEHELPER)){
|
||||||
|
wxSmgServer.文件助手(chatMsg);
|
||||||
|
}else{
|
||||||
|
wxSmgServer.私聊(chatMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, WxMsgType.私聊信息);
|
||||||
|
add(chatMsg -> {
|
||||||
|
if (FILEHELPER.equals(chatMsg.getFromUser())) {
|
||||||
|
wxSmgServer.文件助手(chatMsg);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}, WxMsgType.收到转账之后或者文件助手等信息);
|
||||||
|
add(chatMsg -> {
|
||||||
|
wxSmgServer.收到名片(chatMsg);
|
||||||
|
return 1;
|
||||||
|
}, WxMsgType.收到名片);
|
||||||
|
add(chatMsg -> {
|
||||||
|
wxSmgServer.收到好友请求(chatMsg);
|
||||||
|
return 1;
|
||||||
|
}, WxMsgType.好友请求);// 好友请求
|
||||||
|
add(chatMsg -> {
|
||||||
|
boolean f = 解析扫码支付第二段(chatMsg);
|
||||||
|
if (f) {
|
||||||
|
f = 解析收款信息1段(chatMsg);
|
||||||
|
if (f) {
|
||||||
|
解析收款信息2段(chatMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, WxMsgType.转账和收款);
|
||||||
|
add(chatMsg -> {
|
||||||
|
boolean f = 解析扫码支付第一段(chatMsg);
|
||||||
|
return null;
|
||||||
|
}, WxMsgType.扫码触发);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析扫码支付第一段,得到交易id和微信id
|
||||||
|
*
|
||||||
|
* @param chatMsg
|
||||||
|
* @return boolean 返回true 则继续解析,否则解析成功,不需要解析了
|
||||||
|
*/
|
||||||
|
public static boolean 解析扫码支付第一段(PrivateChatMsg chatMsg) {
|
||||||
|
try {
|
||||||
|
Document document = XmlUtil.parseXml(chatMsg.getContent());
|
||||||
|
Element documentElement = document.getDocumentElement();
|
||||||
|
String localName = documentElement.getLocalName();
|
||||||
|
if ("sysmsg".equals(localName)) {
|
||||||
|
String type = documentElement.getAttribute("type");
|
||||||
|
if ("paymsg".equals(type)) {
|
||||||
|
NodeList outtradeno = documentElement.getElementsByTagName("outtradeno");
|
||||||
|
if (outtradeno.getLength() > 0) {
|
||||||
|
String textContent = outtradeno.item(0).getTextContent();
|
||||||
|
String textContent1 = documentElement.getElementsByTagName("username").item(0).getTextContent();
|
||||||
|
collection_code_caching.put(textContent, textContent1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析扫码支付第二段
|
||||||
|
*
|
||||||
|
* @param chatMsg 聊天味精
|
||||||
|
* @return boolean true 则 继续解析, false则解析成功,不需要再解析了
|
||||||
|
*/
|
||||||
|
public static boolean 解析扫码支付第二段(PrivateChatMsg chatMsg) {
|
||||||
|
try {
|
||||||
|
Document document = XmlUtil.parseXml(chatMsg.getContent());
|
||||||
|
Element documentElement = document.getDocumentElement();
|
||||||
|
String localName = documentElement.getLocalName();
|
||||||
|
if ("msg".equals(localName)) {
|
||||||
|
NodeList outtradeno = documentElement.getElementsByTagName("weapp_path");
|
||||||
|
if (outtradeno.getLength() > 1) {
|
||||||
|
String textContent = outtradeno.item(1).getTextContent();
|
||||||
|
Set<Map.Entry<String, String>> entries = collection_code_caching.entrySet();
|
||||||
|
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, String> next = iterator.next();
|
||||||
|
if (textContent.contains(next.getKey())) {
|
||||||
|
// 得到了交易信息
|
||||||
|
NodeList word = documentElement.getElementsByTagName("word");
|
||||||
|
String monery = word.item(1).getTextContent();
|
||||||
|
String remark = word.item(3).getTextContent();
|
||||||
|
if (monery.startsWith("¥")) {
|
||||||
|
String substring = monery.substring(1);
|
||||||
|
BigDecimal decimal = new BigDecimal(substring);
|
||||||
|
log.info("扫码收款:{},付款人:{},付款备注:{}", decimal.stripTrailingZeros().toPlainString(), next.getValue(), remark);
|
||||||
|
wxSmgServer.扫码收款(new PayoutInformation(next.getValue(),decimal,remark));
|
||||||
|
iterator.remove();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean 解析收款信息2段(PrivateChatMsg chatMsg) {
|
||||||
|
try {
|
||||||
|
Document document = XmlUtil.parseXml(chatMsg.getContent());
|
||||||
|
Element documentElement = document.getDocumentElement();
|
||||||
|
String localName = documentElement.getLocalName();
|
||||||
|
if ("msg".equals(localName)) {
|
||||||
|
if (documentElement.getElementsByTagName("transcationid").getLength() > 0) {
|
||||||
|
String remark = documentElement.getElementsByTagName("pay_memo").item(0).getTextContent();
|
||||||
|
String monery = documentElement.getElementsByTagName("feedesc").item(0).getTextContent();
|
||||||
|
String receiver_username = documentElement.getElementsByTagName("receiver_username").item(0).getTextContent();
|
||||||
|
// 如果是机器人发出的,则跳过解析
|
||||||
|
if (InitWeChat.WXID_MAP.contains(receiver_username) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (monery.startsWith("¥")) {
|
||||||
|
String substring = monery.substring(1);
|
||||||
|
BigDecimal decimal = new BigDecimal(substring);
|
||||||
|
log.info("收款:{},付款人:{},付款备注:{}", decimal.stripTrailingZeros().toPlainString(), receiver_username, remark);
|
||||||
|
wxSmgServer.收款之后(new PayoutInformation(receiver_username, decimal, remark));
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析收款信息1段
|
||||||
|
* <b>会自动进行收款</b>
|
||||||
|
*
|
||||||
|
* @param chatMsg
|
||||||
|
* @return boolean true则 继续解析,false则不需要解析了
|
||||||
|
*/
|
||||||
|
public static boolean 解析收款信息1段(PrivateChatMsg chatMsg) {
|
||||||
|
try {
|
||||||
|
String content = chatMsg.getContent();
|
||||||
|
Document document = XmlUtil.parseXml(content);
|
||||||
|
NodeList paysubtype1 = document.getElementsByTagName("paysubtype");
|
||||||
|
if (paysubtype1.getLength() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Node paysubtype = paysubtype1.item(0);
|
||||||
|
if ("1".equals(paysubtype.getTextContent().trim())) {
|
||||||
|
// 手机发出去的
|
||||||
|
String textContent = document.getElementsByTagName("receiver_username").item(0).getTextContent();
|
||||||
|
if (!InitWeChat.WXID_MAP.contains(textContent)) {
|
||||||
|
// 如果不是机器人收款,则认为不需要解析了,大概率是机器人自己发出去的
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String remark = document.getElementsByTagName("pay_memo").item(0).getTextContent();
|
||||||
|
String monery = document.getElementsByTagName("feedesc").item(0).getTextContent();
|
||||||
|
String receiver_username = document.getElementsByTagName("receiver_username").item(0).getTextContent();
|
||||||
|
if (monery.startsWith("¥")) {
|
||||||
|
String substring = monery.substring(1);
|
||||||
|
BigDecimal decimal = new BigDecimal(substring);
|
||||||
|
Node transcationid = document.getDocumentElement().getElementsByTagName("transcationid").item(0);
|
||||||
|
Node transferid = document.getDocumentElement().getElementsByTagName("transferid").item(0);
|
||||||
|
wxSmgServer.接到收款(new PayoutInformation(chatMsg.getFromUser(), decimal, remark, transcationid.getTextContent(), transferid.getTextContent()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exec(PrivateChatMsg chatMsg) {
|
||||||
|
Handle handle = map.get(chatMsg.getType());
|
||||||
|
if (handle != null) {
|
||||||
|
handle.handle(chatMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Handle handle, WxMsgType... type) {
|
||||||
|
for (WxMsgType integer : type) {
|
||||||
|
map.put(integer.getType(), handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Handle {
|
||||||
|
Object handle(PrivateChatMsg chatMsg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.example.wxhk.server;
|
||||||
|
|
||||||
|
import com.example.wxhk.model.PrivateChatMsg;
|
||||||
|
import com.example.wxhk.model.dto.PayoutInformation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信消息处理提取
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/06/06
|
||||||
|
*/
|
||||||
|
public interface WxSmgServer {
|
||||||
|
/**
|
||||||
|
* 接到收款
|
||||||
|
*
|
||||||
|
* @param payoutInformation 支付信息
|
||||||
|
*/
|
||||||
|
void 接到收款(PayoutInformation payoutInformation);
|
||||||
|
|
||||||
|
void 收款之后(PayoutInformation pay);
|
||||||
|
|
||||||
|
void 私聊(PrivateChatMsg chatMsg);
|
||||||
|
|
||||||
|
void 文件助手(PrivateChatMsg chatMsg);
|
||||||
|
|
||||||
|
void 收到名片(PrivateChatMsg chatMsg);
|
||||||
|
|
||||||
|
void 收到好友请求(PrivateChatMsg chatMsg);
|
||||||
|
|
||||||
|
void 扫码收款(PayoutInformation payoutInformation);
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.example.wxhk.server.impl;
|
||||||
|
|
||||||
|
import com.example.wxhk.model.PrivateChatMsg;
|
||||||
|
import com.example.wxhk.model.dto.PayoutInformation;
|
||||||
|
import com.example.wxhk.model.request.ConfirmThePayment;
|
||||||
|
import com.example.wxhk.util.HttpSendUtil;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
import org.dromara.hutool.core.util.XmlUtil;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class WxSmgServerImpl implements com.example.wxhk.server.WxSmgServer {
|
||||||
|
|
||||||
|
protected static final Log log=Log.get();
|
||||||
|
|
||||||
|
public static final String FILEHELPER = "filehelper";
|
||||||
|
@Override
|
||||||
|
public void 接到收款(PayoutInformation payoutInformation) {
|
||||||
|
HttpSendUtil.确认收款(new ConfirmThePayment().setWxid(payoutInformation.receiverUsername()).setTranscationId(payoutInformation.transcationid()).setTransferId(payoutInformation.transferid()));
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void 收款之后(PayoutInformation pay) {
|
||||||
|
HttpSendUtil.发送文本(pay.receiverUsername(), StrUtil.format("收到款项:{},备注:{}", pay.decimal().stripTrailingZeros().toPlainString(), pay.remark()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void 私聊(PrivateChatMsg chatMsg) {
|
||||||
|
if (Objects.equals(chatMsg.getIsSendMsg(), 1) && Objects.equals(chatMsg.getIsSendByPhone(), 1)) {
|
||||||
|
log.info("手机端对:{}发出:{}", chatMsg.getFromUser(), chatMsg.getContent());
|
||||||
|
}else{
|
||||||
|
log.info("收到私聊{}",chatMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void 文件助手(PrivateChatMsg chatMsg) {
|
||||||
|
log.info("文件助手:{}",chatMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void 收到名片(PrivateChatMsg chatMsg) {
|
||||||
|
if (FILEHELPER.equals(chatMsg.getFromUser())) {
|
||||||
|
Document document = XmlUtil.parseXml(chatMsg.getContent());
|
||||||
|
Element documentElement = document.getDocumentElement();
|
||||||
|
String username = documentElement.getAttribute("username");
|
||||||
|
if (StrUtil.isNotBlank(username)) {
|
||||||
|
HttpSendUtil.发送文本(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void 收到好友请求(PrivateChatMsg chatMsg) {
|
||||||
|
HttpSendUtil.通过好友请求(chatMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void 扫码收款(PayoutInformation payoutInformation) {
|
||||||
|
HttpSendUtil.发送文本(payoutInformation.receiverUsername(), StrUtil.format("扫码收款:{},备注:{}", payoutInformation.decimal().stripTrailingZeros().toPlainString(), payoutInformation.remark()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.example.wxhk.tcp.vertx;
|
||||||
|
|
||||||
|
import com.example.wxhk.model.PrivateChatMsg;
|
||||||
|
import com.example.wxhk.msg.WxMsgHandle;
|
||||||
|
import com.example.wxhk.util.HttpSendUtil;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.dromara.hutool.core.thread.NamedThreadFactory;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息处理
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/31
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ArrHandle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程处理消息队列,但是必须保证核心数大于2,其中必定要有一个线程可以单独处理交易队列信息
|
||||||
|
*/
|
||||||
|
public static final ThreadPoolExecutor sub = new ThreadPoolExecutor(4, 10, 30, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new NamedThreadFactory("sub", false));
|
||||||
|
public static final ThreadLocal<PrivateChatMsg> chatMsgThreadLocal = new InheritableThreadLocal<>();
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到当前正在处理的消息
|
||||||
|
*
|
||||||
|
* @return {@link PrivateChatMsg}
|
||||||
|
*/
|
||||||
|
public static PrivateChatMsg getPriMsg() {
|
||||||
|
return chatMsgThreadLocal.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void exec() {
|
||||||
|
for (int i = 0; i < sub.getCorePoolSize()-1; i++) {
|
||||||
|
sub.submit(() -> {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
JsonObject take = VertxTcp.LINKED_BLOCKING_QUEUE.take();
|
||||||
|
log.info("{}", take.encode());
|
||||||
|
PrivateChatMsg privateChatMsg = take.mapTo(PrivateChatMsg.class);
|
||||||
|
chatMsgThreadLocal.set(privateChatMsg);
|
||||||
|
if ("weixin".equals(privateChatMsg.getFromUser())) {
|
||||||
|
String s = HttpSendUtil.获取当前登陆微信id();
|
||||||
|
InitWeChat.WXID_MAP.add(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
WxMsgHandle.exec(privateChatMsg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}finally {
|
||||||
|
chatMsgThreadLocal.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.error("退出线程了");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sub.submit(() -> {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
JsonObject take = VertxTcp.LINKED_BLOCKING_QUEUE_MON.take();
|
||||||
|
log.info("{}", take.encode());
|
||||||
|
PrivateChatMsg privateChatMsg = take.mapTo(PrivateChatMsg.class);
|
||||||
|
chatMsgThreadLocal.set(privateChatMsg);
|
||||||
|
if ("weixin".equals(privateChatMsg.getFromUser())) {
|
||||||
|
String s = HttpSendUtil.获取当前登陆微信id();
|
||||||
|
InitWeChat.WXID_MAP.add(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
WxMsgHandle.exec(privateChatMsg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}finally {
|
||||||
|
chatMsgThreadLocal.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.error("退出线程了");
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,158 @@
|
|||||||
|
package com.example.wxhk.tcp.vertx;
|
||||||
|
|
||||||
|
import com.example.wxhk.util.HttpAsyncUtil;
|
||||||
|
import com.example.wxhk.util.HttpSyncUtil;
|
||||||
|
import io.vertx.core.impl.ConcurrentHashSet;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import org.dromara.hutool.core.io.file.FileUtil;
|
||||||
|
import org.dromara.hutool.core.net.NetUtil;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.dromara.hutool.setting.Setting;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信注入环境初始化和相关方法
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/16
|
||||||
|
*/
|
||||||
|
@Order(-1)
|
||||||
|
@Component
|
||||||
|
public class InitWeChat implements CommandLineRunner {
|
||||||
|
|
||||||
|
public final static Log log = Log.get();
|
||||||
|
public static final ConcurrentHashSet<String> WXID_MAP = new ConcurrentHashSet<>();
|
||||||
|
public static String wxPath;
|
||||||
|
public static Integer wxPort;
|
||||||
|
public static Integer vertxPort;
|
||||||
|
/**
|
||||||
|
* wxhelper.dll 所在路径
|
||||||
|
*/
|
||||||
|
public static File DLL_PATH;
|
||||||
|
|
||||||
|
public static void 注入dll(String wxPid) throws IOException {
|
||||||
|
String format = StrUtil.format("cmd /C c.exe -I {} -p {}\\wxhelper.dll -m {}", wxPid, DLL_PATH.getAbsolutePath(), wxPid);
|
||||||
|
Process exec = Runtime.getRuntime().exec(format, null, DLL_PATH);
|
||||||
|
log.info("注入结果:{}", new String(exec.getInputStream().readAllBytes(), "gbk"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static File 环境初始化() {
|
||||||
|
File target = new File(new File("").getAbsolutePath().split("\\\\")[0] + "\\exec\\");
|
||||||
|
try {
|
||||||
|
File wxPathFile = new File(wxPath);
|
||||||
|
File config = new File(wxPathFile.getParentFile(), "config.ini");
|
||||||
|
Setting setting = new Setting(config.getAbsolutePath());
|
||||||
|
setting.getGroupedMap().put("config", "port", String.valueOf(wxPort));
|
||||||
|
setting.store();
|
||||||
|
ClassPathResource classPathResource = new ClassPathResource("exec");
|
||||||
|
File file = classPathResource.getFile();
|
||||||
|
target.mkdir();
|
||||||
|
for (File listFile : file.listFiles()) {
|
||||||
|
FileUtil.copy(listFile, target, true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e, "环境初始化失败,请检查");
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回最后一个微信的pid
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
* @throws IOException ioexception
|
||||||
|
*/
|
||||||
|
public static String createWx() throws IOException {
|
||||||
|
Runtime.getRuntime().exec("cmd /C \"" + wxPath + "\"");
|
||||||
|
return getWxPid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String getWxPid() throws IOException {
|
||||||
|
String line = null;
|
||||||
|
try {
|
||||||
|
Process exec = Runtime.getRuntime().exec("cmd /C tasklist /FI \"IMAGENAME eq WeChat.exe\" ");
|
||||||
|
byte[] bytes = exec.getInputStream().readAllBytes();
|
||||||
|
line = new String(bytes, "gbk");
|
||||||
|
String[] split = line.split("\n");
|
||||||
|
if (!line.contains("WeChat.exe")) {
|
||||||
|
return createWx();
|
||||||
|
}
|
||||||
|
String[] split1 = split[split.length - 1].replaceAll("\\s{2,}", " ").split(" ");
|
||||||
|
return split1[1];
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("获取端口错误:{}", line);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getWxPort() {
|
||||||
|
return wxPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${wx.port}")
|
||||||
|
public void setWxPort(Integer wxPort) {
|
||||||
|
InitWeChat.wxPort = wxPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getWxPath() {
|
||||||
|
return wxPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${wx.path}")
|
||||||
|
public void setWxPath(String wxPath) {
|
||||||
|
InitWeChat.wxPath = wxPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getVertxPort() {
|
||||||
|
return vertxPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${vertx.port}")
|
||||||
|
public void setVertxPort(Integer vertxPort) {
|
||||||
|
InitWeChat.vertxPort = vertxPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
//tasklist /FI "IMAGENAME eq WeChat.exe" /m
|
||||||
|
boolean usableLocalPort = NetUtil.isUsableLocalPort(wxPort);
|
||||||
|
if (usableLocalPort) {
|
||||||
|
DLL_PATH = 环境初始化();
|
||||||
|
String wxPid = getWxPid();
|
||||||
|
注入dll(wxPid);
|
||||||
|
}
|
||||||
|
ThreadUtil.execute(() -> {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
JsonObject exec = HttpSyncUtil.exec(HttpAsyncUtil.Type.检查微信登陆, new JsonObject());
|
||||||
|
if (exec.getInteger("code").equals(1)) {
|
||||||
|
JsonObject dl = HttpSyncUtil.exec(HttpAsyncUtil.Type.获取登录信息, new JsonObject());
|
||||||
|
JsonObject jsonObject = dl.getJsonObject("data");
|
||||||
|
String wx = jsonObject.getString("wxid");
|
||||||
|
WXID_MAP.add(wx);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("检测到微信登陆:{}", wx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ThreadUtil.safeSleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
// FIXME: 2023/6/2 程序结束后关闭hook会偶尔出现微信闪退情况,暂时禁用
|
||||||
|
// Runtime.getRuntime().addShutdownHook(new Thread(HttpSendUtil::关闭hook));
|
||||||
|
//netstat -aon|findstr "端口号"
|
||||||
|
// c.exe -I 4568 -p D:\exec\wxhelper.dll -m 4568
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.example.wxhk.tcp.vertx;
|
||||||
|
|
||||||
|
import com.example.wxhk.WxhkApplication;
|
||||||
|
import com.example.wxhk.constant.WxMsgType;
|
||||||
|
import com.example.wxhk.model.request.OpenHook;
|
||||||
|
import com.example.wxhk.util.HttpSendUtil;
|
||||||
|
import io.vertx.core.AbstractVerticle;
|
||||||
|
import io.vertx.core.DeploymentOptions;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.Promise;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import io.vertx.core.net.NetServer;
|
||||||
|
import io.vertx.core.net.NetServerOptions;
|
||||||
|
import io.vertx.core.parsetools.JsonParser;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受微信hook信息
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/26
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Order()
|
||||||
|
public class VertxTcp extends AbstractVerticle implements CommandLineRunner {
|
||||||
|
public final static LinkedBlockingQueue<JsonObject> LINKED_BLOCKING_QUEUE = new LinkedBlockingQueue<>();
|
||||||
|
/**
|
||||||
|
* 这个只保留交易相关的类型
|
||||||
|
*/
|
||||||
|
public final static LinkedBlockingQueue<JsonObject> LINKED_BLOCKING_QUEUE_MON = new LinkedBlockingQueue<>();
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
NetServer netServer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Promise<Void> startPromise) throws Exception {
|
||||||
|
netServer = vertx.createNetServer(new NetServerOptions()
|
||||||
|
.setPort(InitWeChat.getVertxPort())
|
||||||
|
.setIdleTimeout(0)
|
||||||
|
.setLogActivity(false)
|
||||||
|
);
|
||||||
|
netServer.connectHandler(socket -> {
|
||||||
|
JsonParser parser = JsonParser.newParser();
|
||||||
|
parser.objectValueMode();
|
||||||
|
parser.handler(event -> {
|
||||||
|
switch (event.type()) {
|
||||||
|
case START_OBJECT -> {
|
||||||
|
}
|
||||||
|
case END_OBJECT -> {
|
||||||
|
}
|
||||||
|
case START_ARRAY -> {
|
||||||
|
}
|
||||||
|
case END_ARRAY -> {
|
||||||
|
}
|
||||||
|
case VALUE -> {
|
||||||
|
JsonObject entries = event.objectValue();
|
||||||
|
|
||||||
|
if(Objects.equals(entries.getInteger("type"), WxMsgType.扫码触发.getType()) ||
|
||||||
|
Objects.equals(entries.getInteger("type"), WxMsgType.转账和收款.getType())){
|
||||||
|
LINKED_BLOCKING_QUEUE_MON.add(entries);
|
||||||
|
}else{
|
||||||
|
LINKED_BLOCKING_QUEUE.add(entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.handler(parser);
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<NetServer> listen = netServer.listen();
|
||||||
|
listen.onComplete(event -> {
|
||||||
|
boolean succeeded = event.succeeded();
|
||||||
|
if (succeeded) {
|
||||||
|
HttpSendUtil.开启hook(new OpenHook().setPort(InitWeChat.getVertxPort().toString()).setIp("127.0.0.1")
|
||||||
|
.setEnableHttp(false)
|
||||||
|
.setTimeout("5000"));
|
||||||
|
// HttpAsyncUtil.exec(HttpAsyncUtil.Type.开启hook, new JsonObject().put("port", InitWeChat.getVertxPort().toString()).put("ip", "127.0.0.1"));
|
||||||
|
startPromise.complete();
|
||||||
|
} else {
|
||||||
|
startPromise.fail(event.cause());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
WxhkApplication.vertx.deployVerticle(this, new DeploymentOptions().setWorkerPoolSize(6));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.example.wxhk.util;
|
||||||
|
|
||||||
|
import com.example.wxhk.WxhkApplication;
|
||||||
|
import com.example.wxhk.tcp.vertx.InitWeChat;
|
||||||
|
import io.vertx.core.AsyncResult;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.Handler;
|
||||||
|
import io.vertx.core.buffer.Buffer;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import io.vertx.ext.web.client.HttpResponse;
|
||||||
|
import io.vertx.ext.web.client.WebClient;
|
||||||
|
import io.vertx.ext.web.client.WebClientOptions;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http异步请求
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/25
|
||||||
|
*/
|
||||||
|
public class HttpAsyncUtil {
|
||||||
|
public static final WebClient client = WebClient.create(WxhkApplication.vertx, new WebClientOptions().setDefaultHost("localhost").setDefaultPort(InitWeChat.wxPort)
|
||||||
|
.setConnectTimeout(10000).setMaxPoolSize(10).setPoolEventLoopSize(10));
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
|
||||||
|
public static Future<HttpResponse<Buffer>> exec(Type type, JsonObject object) {
|
||||||
|
return client.post(InitWeChat.wxPort, "localhost", "/api/" + type.getType())
|
||||||
|
.sendJsonObject(object)
|
||||||
|
.onSuccess(event ->
|
||||||
|
{
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("type:{},{}", type.getType(), event.bodyAsJsonObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Future<HttpResponse<Buffer>> exec(Type type, JsonObject object, Handler<AsyncResult<HttpResponse<Buffer>>> handler) {
|
||||||
|
return client.post(InitWeChat.wxPort, "localhost", "/api/" + type.getType())
|
||||||
|
.sendJsonObject(object)
|
||||||
|
.onComplete(handler)
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
检查微信登陆("checkLogin"),
|
||||||
|
获取登录信息("userInfo"),
|
||||||
|
发送文本("sendTextMsg"),
|
||||||
|
转发消息("forwardMsg"),
|
||||||
|
发送at文本("sendAtText"),
|
||||||
|
发送图片("5"),
|
||||||
|
发送文件("sendFileMsg"),
|
||||||
|
开启hook("hookSyncMsg"),
|
||||||
|
关闭hook("unhookSyncMsg"),
|
||||||
|
添加好友("20"),
|
||||||
|
通过好友申请("23"),
|
||||||
|
获取群成员("getMemberFromChatRoom"),
|
||||||
|
获取群成员基础信息("getContactProfile"),
|
||||||
|
获取群详情("getChatRoomDetailInfo"),
|
||||||
|
添加群成员("addMemberToChatRoom"),
|
||||||
|
修改群昵称("modifyNickname"),
|
||||||
|
删除群成员("delMemberFromChatRoom"),
|
||||||
|
置顶群消息("topMsg"),
|
||||||
|
取消置顶群消息("removeTopMsg"),
|
||||||
|
邀请入群("InviteMemberToChatRoom"),
|
||||||
|
确认收款("45"),
|
||||||
|
联系人列表("getContactList"),
|
||||||
|
查询微信信息("55"),
|
||||||
|
下载附件("downloadAttach"),
|
||||||
|
解码("decodeImage"),
|
||||||
|
|
||||||
|
|
||||||
|
;
|
||||||
|
String type;
|
||||||
|
|
||||||
|
Type(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
package com.example.wxhk.util;
|
||||||
|
|
||||||
|
import com.example.wxhk.model.PrivateChatMsg;
|
||||||
|
import com.example.wxhk.model.request.*;
|
||||||
|
import com.example.wxhk.model.response.ContactList;
|
||||||
|
import com.example.wxhk.model.response.GroupMembers;
|
||||||
|
import com.example.wxhk.tcp.vertx.ArrHandle;
|
||||||
|
import com.example.wxhk.tcp.vertx.InitWeChat;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import org.dromara.hutool.core.util.XmlUtil;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常见方法
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/29
|
||||||
|
*/
|
||||||
|
public class HttpSendUtil {
|
||||||
|
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
|
||||||
|
public static JsonObject 通过好友请求(PrivateChatMsg msg) {
|
||||||
|
Document document = XmlUtil.parseXml(msg.getContent());
|
||||||
|
String encryptusername = document.getDocumentElement().getAttribute("encryptusername");
|
||||||
|
String ticket = document.getDocumentElement().getAttribute("ticket");
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.通过好友申请, new JsonObject().put("v3", encryptusername).put("v4", ticket).put("permission", "0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 确认收款(PrivateChatMsg msg) {
|
||||||
|
try {
|
||||||
|
String content = msg.getContent();
|
||||||
|
Document document = XmlUtil.parseXml(content);
|
||||||
|
Node paysubtype = document.getElementsByTagName("paysubtype").item(0);
|
||||||
|
if ("1".equals(paysubtype.getTextContent().trim())) {
|
||||||
|
// 手机发出去的
|
||||||
|
String textContent = document.getElementsByTagName("receiver_username").item(0).getTextContent();
|
||||||
|
if (!InitWeChat.WXID_MAP.contains(textContent)) {
|
||||||
|
return new JsonObject().put("spick", true);
|
||||||
|
}
|
||||||
|
Node transcationid = document.getDocumentElement().getElementsByTagName("transcationid").item(0);
|
||||||
|
Node transferid = document.getDocumentElement().getElementsByTagName("transferid").item(0);
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.确认收款, new JsonObject().put("wxid", msg.getFromUser())
|
||||||
|
.put("transcationId", transcationid.getTextContent())
|
||||||
|
.put("transferId", transferid.getTextContent()));
|
||||||
|
|
||||||
|
}
|
||||||
|
// 如果是确认接受收款,则跳过
|
||||||
|
return new JsonObject();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static JsonObject 发送文本(String wxid, String msg) {
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.发送文本, JsonObject.mapFrom(new SendMsg().setMsg(msg).setWxid(wxid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送文本(String msg) {
|
||||||
|
return 发送文本(ArrHandle.getPriMsg().getFromUser(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送at文本(String chatRoomId, String wxids, String msg) {
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.发送at文本, JsonObject.mapFrom(new SendMsg().setMsg(msg).setWxids(wxids).setChatRoomId(chatRoomId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送at文本(String wxids, String msg) {
|
||||||
|
return 发送at文本(ArrHandle.getPriMsg().getFromGroup(), wxids, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送图片(String wxid, String msg) {
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.发送图片, JsonObject.mapFrom(new SendMsg().setImagePath(msg).setWxid(wxid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送图片(String msg) {
|
||||||
|
return 发送图片(ArrHandle.getPriMsg().getFromUser(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送文件(String wxid, String msg) {
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.发送文件, JsonObject.mapFrom(new SendMsg().setFilePath(msg).setWxid(wxid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 发送文件(String msg) {
|
||||||
|
return 发送文件(ArrHandle.getPriMsg().getFromUser(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject 添加好友(AddFriends p) {
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.添加好友, p.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String 获取当前登陆微信id() {
|
||||||
|
JsonObject exec = HttpSyncUtil.exec(HttpAsyncUtil.Type.获取登录信息, new JsonObject());
|
||||||
|
return exec.getJsonObject("data").getString("wxid");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ContactList 联系人列表(){
|
||||||
|
JsonObject exec = HttpSyncUtil.exec(HttpAsyncUtil.Type.联系人列表, new JsonObject());
|
||||||
|
return exec.mapTo(ContactList.class);
|
||||||
|
}
|
||||||
|
public static JsonObject 开启hook(OpenHook hook){
|
||||||
|
JsonObject exec = HttpSyncUtil.exec(HttpAsyncUtil.Type.开启hook,hook.toJson());
|
||||||
|
return exec;
|
||||||
|
}
|
||||||
|
public static JsonObject 关闭hook(){
|
||||||
|
JsonObject exec = HttpSyncUtil.exec(HttpAsyncUtil.Type.关闭hook,new JsonObject());
|
||||||
|
return exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupMembers 获取群成员(GetGroupMembers p){
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.获取群成员, p.toJson()).mapTo(GroupMembers.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static JsonObject 确认收款(ConfirmThePayment payment){
|
||||||
|
return HttpSyncUtil.exec(HttpAsyncUtil.Type.确认收款, payment.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static com.example.wxhk.infe.SendMsg of(HttpAsyncUtil.Type type) {
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
case 检查微信登陆 -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
case 获取登录信息 -> {
|
||||||
|
}
|
||||||
|
case 发送文本 -> {
|
||||||
|
return new SendText();
|
||||||
|
}
|
||||||
|
case 发送at文本 -> {
|
||||||
|
return new SendAtText();
|
||||||
|
}
|
||||||
|
case 发送图片 -> {
|
||||||
|
return new SendImg();
|
||||||
|
}
|
||||||
|
case 发送文件 -> {
|
||||||
|
return new SendFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return new SendText();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.example.wxhk.util;
|
||||||
|
|
||||||
|
import com.example.wxhk.tcp.vertx.InitWeChat;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import org.dromara.hutool.http.client.ClientConfig;
|
||||||
|
import org.dromara.hutool.http.client.Request;
|
||||||
|
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||||
|
import org.dromara.hutool.http.client.engine.ClientEngineFactory;
|
||||||
|
import org.dromara.hutool.http.meta.Method;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http同步请求
|
||||||
|
*
|
||||||
|
* @author wt
|
||||||
|
* @date 2023/05/25
|
||||||
|
*/
|
||||||
|
public class HttpSyncUtil {
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
static final ClientEngine engine;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ClientConfig clientConfig = ClientConfig.of()
|
||||||
|
.setTimeout(30 * 1000);
|
||||||
|
engine = ClientEngineFactory.createEngine(clientConfig);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject exec(HttpAsyncUtil.Type type, JsonObject obj) {
|
||||||
|
String post = engine.send(Request.of("http://localhost:" + InitWeChat.wxPort + "/api/" + type.getType()).method(Method.POST).body(obj.encode())).bodyStr();
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("type:{},{}", type.getType(), post);
|
||||||
|
}
|
||||||
|
return new JsonObject(post);
|
||||||
|
}
|
||||||
|
}
|
4
java_client/src/main/resources/application.properties
Normal file
4
java_client/src/main/resources/application.properties
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
wx.path=D:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe
|
||||||
|
wx.port=19088
|
||||||
|
spring.profiles.active=local
|
||||||
|
vertx.port=8080
|
BIN
java_client/src/main/resources/exec/c.exe
Normal file
BIN
java_client/src/main/resources/exec/c.exe
Normal file
Binary file not shown.
BIN
java_client/src/main/resources/exec/wxhelper.dll
Normal file
BIN
java_client/src/main/resources/exec/wxhelper.dll
Normal file
Binary file not shown.
171
java_client/src/main/resources/logback-spring.xml
Normal file
171
java_client/src/main/resources/logback-spring.xml
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<!--日志格式应用spring boot默认的格式,也可以自己更改-->
|
||||||
|
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||||
|
|
||||||
|
<!--定义日志存放的位置,默认存放在项目启动的相对路径的目录-->
|
||||||
|
<springProperty scope="context" name="LOG_PATH" source="log.path" defaultValue="log"/>
|
||||||
|
<property name="withLineNumber_debug"
|
||||||
|
value="%clr(%d{HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) [%t] %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
|
||||||
|
|
||||||
|
<property name="file_pattern"
|
||||||
|
value="%d{MM-dd HH:mm:ss.SSS} %-5level [${PID:- } %thread] %logger{50}#%method,%line : %msg%n"/>
|
||||||
|
|
||||||
|
<!-- ****************************************************************************************** -->
|
||||||
|
<!-- ****************************** 本地开发只在控制台打印日志 ************************************ -->
|
||||||
|
<!-- ****************************************************************************************** -->
|
||||||
|
<springProfile name="local">
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${withLineNumber_debug}</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 日志记录器,日期滚动记录 -->
|
||||||
|
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>log_error.log</file>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${LOG_PATH}/error/%d{yyyy-MM}/log_error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<maxHistory>100</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
|
||||||
|
<!-- 追加方式记录日志 -->
|
||||||
|
<append>true</append>
|
||||||
|
|
||||||
|
<!-- 日志文件的格式 -->
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${file_pattern}</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
|
||||||
|
<!-- 此日志文件只记录error级别的 -->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>error</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
|
||||||
|
<!--默认所有的包以info-->
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
<!--各个服务的包在本地执行的时候,打开debug模式-->
|
||||||
|
<logger name="com.example.wxhk" level="debug" additivity="false">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
<appender-ref ref="FILE_ERROR"/>
|
||||||
|
</logger>
|
||||||
|
<logger name="org.springframework" level="info" additivity="false">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</logger>
|
||||||
|
</springProfile>
|
||||||
|
|
||||||
|
<!-- ********************************************************************************************** -->
|
||||||
|
<!-- **** 放到服务器上不管在什么环境都只在文件记录日志,控制台(catalina.out)打印logback捕获不到的日志 **** -->
|
||||||
|
<!-- ********************************************************************************************** -->
|
||||||
|
<springProfile name="!local">
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 日志记录器,日期滚动记录 -->
|
||||||
|
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>${LOG_PATH}/log_error.log</file>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${LOG_PATH}/error/%d{yyyy-MM}/log_error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<maxHistory>100</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
|
||||||
|
<!-- 追加方式记录日志 -->
|
||||||
|
<append>true</append>
|
||||||
|
|
||||||
|
<!-- 日志文件的格式 -->
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${file_pattern}</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
|
||||||
|
<!-- 此日志文件只记录error级别的 -->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>error</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 日志记录器,日期滚动记录 -->
|
||||||
|
<appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>${LOG_PATH}/log_total.log</file>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${LOG_PATH}/total/%d{yyyy-MM}/log_total-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<maxHistory>100</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<!-- 追加方式记录日志 -->
|
||||||
|
<append>true</append>
|
||||||
|
<!-- 日志文件的格式 -->
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${file_pattern}</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<!-- 业务错误 -->
|
||||||
|
<appender name="business_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>${LOG_PATH}/log_business.log</file>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${LOG_PATH}/business/%d{yyyy-MM}/log_business-%d{yyyy-MM-dd}.%i.log.gz
|
||||||
|
</fileNamePattern>
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<maxHistory>100</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<!-- 追加方式记录日志 -->
|
||||||
|
<append>true</append>
|
||||||
|
<!-- 日志文件的格式 -->
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${file_pattern}</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="com.example.wxhk" level="info" additivity="false">
|
||||||
|
<appender-ref ref="business_log"/>
|
||||||
|
<appender-ref ref="FILE_ERROR"/>
|
||||||
|
</logger>
|
||||||
|
<logger name="p6spy" level="info" additivity="false">
|
||||||
|
<appender-ref ref="business_log"/>
|
||||||
|
</logger>
|
||||||
|
<logger name="org.springframework" level="warn"/>
|
||||||
|
|
||||||
|
<!--记录到文件时,记录两类一类是error日志,一个是所有日志-->
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="FILE_ERROR"/>
|
||||||
|
<appender-ref ref="FILE_ALL"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
|
||||||
|
</springProfile>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
|
||||||
|
|
10
java_client/src/main/resources/spy.properties
Normal file
10
java_client/src/main/resources/spy.properties
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
|
||||||
|
# 使用Slf4J记录sql
|
||||||
|
appender=com.p6spy.engine.spy.appender.Slf4JLogger
|
||||||
|
# 是否开启慢SQL记录
|
||||||
|
outagedetection=true
|
||||||
|
# 慢SQL记录标准,单位秒
|
||||||
|
outagedetectioninterval=2
|
||||||
|
#日期格式
|
||||||
|
dateformat=HH:mm:ss
|
||||||
|
customLogMessageFormat=%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.wxhk;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class WxhkApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.example.wxhk.tcp;
|
||||||
|
|
||||||
|
import com.example.wxhk.util.HttpAsyncUtil;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.buffer.Buffer;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import io.vertx.ext.web.client.HttpResponse;
|
||||||
|
import org.dromara.hutool.core.lang.Console;
|
||||||
|
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||||
|
import org.dromara.hutool.log.Log;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class HttpAsyncUtilTest {
|
||||||
|
|
||||||
|
protected static final Log log = Log.get();
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exec() {
|
||||||
|
Future<HttpResponse<Buffer>> exec = HttpAsyncUtil.exec(HttpAsyncUtil.Type.联系人列表, new JsonObject());
|
||||||
|
exec.onSuccess(event -> {
|
||||||
|
Console.log(event.bodyAsJsonObject());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void exec1() {
|
||||||
|
|
||||||
|
for(int i=0;i<10000;i++){
|
||||||
|
int finalI = i;
|
||||||
|
HttpAsyncUtil.exec(HttpAsyncUtil.Type.获取登录信息, new JsonObject(), event -> {
|
||||||
|
if (event.succeeded()) {
|
||||||
|
log.info("i:{},{}", finalI,event.result().bodyAsJsonObject());
|
||||||
|
}else{
|
||||||
|
event.cause().printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log.info("发出请求:{}",i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadUtil.sync(this);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void exec2() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
92
java_client/src/test/java/com/example/wxhk/tcp/XmlTest.java
Normal file
92
java_client/src/test/java/com/example/wxhk/tcp/XmlTest.java
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,49 @@
|
|||||||
|
package com.example.wxhk.util;
|
||||||
|
|
||||||
|
import com.example.wxhk.model.request.GetGroupMembers;
|
||||||
|
import com.example.wxhk.model.response.ContactList;
|
||||||
|
import com.example.wxhk.model.response.GroupMembers;
|
||||||
|
import org.dromara.hutool.core.lang.Console;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class HttpSendUtilTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void 获取当前登陆微信id() {
|
||||||
|
String s = HttpSendUtil.获取当前登陆微信id();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void 联系人列表() {
|
||||||
|
ContactList contactList = HttpSendUtil.联系人列表();
|
||||||
|
|
||||||
|
List<ContactList.DataBean> data = contactList.getData();
|
||||||
|
for (ContactList.DataBean datum : data) {
|
||||||
|
Console.log(datum.getWxid(),datum.getUserName());
|
||||||
|
}
|
||||||
|
Console.log(contactList);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void 开启hook() {
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void 关闭ook() {
|
||||||
|
HttpSendUtil.关闭hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void 获取群成员() {
|
||||||
|
GroupMembers 获取群成员 = HttpSendUtil.获取群成员(new GetGroupMembers().setChatRoomId("24964676359@chatroom"));
|
||||||
|
Console.log(获取群成员);
|
||||||
|
|
||||||
|
Duration between = Duration.between(LocalDateTime.now(), LocalDateTime.now());
|
||||||
|
}
|
||||||
|
}
|
26
nodejs_client/tcp_server.js
Normal file
26
nodejs_client/tcp_server.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const net = require('net')
|
||||||
|
|
||||||
|
const server = net.createServer(socket => {
|
||||||
|
console.log('New client connected')
|
||||||
|
|
||||||
|
let data = Buffer.from('')
|
||||||
|
|
||||||
|
socket.on('data', data => {
|
||||||
|
data = Buffer.concat([data, chunk])
|
||||||
|
console.log(`Received data: ${data}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('end', () => {
|
||||||
|
const decodedData = data.toString('utf8')
|
||||||
|
console.log(`Received data: ${decodedData}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('close', () => {
|
||||||
|
console.log('Client disconnected')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const port = 19099
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`Server listening on port ${port}`)
|
||||||
|
})
|
493
python/3.9.5.81/http_client.py
Normal file
493
python/3.9.5.81/http_client.py
Normal file
@ -0,0 +1,493 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def checkLogin():
|
||||||
|
url = "127.0.0.1:19088/api/checkLogin"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def userInfo():
|
||||||
|
url = "127.0.0.1:19088/api/userInfo"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def sendTextMsg():
|
||||||
|
url = "127.0.0.1:19088/api/sendTextMsg"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"msg": "12www"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def sendImagesMsg():
|
||||||
|
url = "127.0.0.1:19088/api/sendImagesMsg"
|
||||||
|
print("modify imagePath")
|
||||||
|
raise RuntimeError("modify imagePath then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"imagePath": "C:\\pic.png"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def sendFileMsg():
|
||||||
|
url = "127.0.0.1:19088/api/sendFileMsg"
|
||||||
|
print("modify filePath")
|
||||||
|
raise RuntimeError("modify filePath then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"filePath": "C:\\test.zip"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def hookSyncMsg():
|
||||||
|
url = "127.0.0.1:19088/api/hookSyncMsg"
|
||||||
|
print("modify ip port url ")
|
||||||
|
raise RuntimeError("modify ip port url then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"port": "19099",
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"url": "http://localhost:8080",
|
||||||
|
"timeout": "3000",
|
||||||
|
"enableHttp": "0"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def unhookSyncMsg():
|
||||||
|
url = "127.0.0.1:19088/api/unhookSyncMsg"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def getContactList():
|
||||||
|
url = "127.0.0.1:19088/api/getContactList"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def getDBInfo():
|
||||||
|
url = "127.0.0.1:19088/api/getDBInfo"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def execSql():
|
||||||
|
url = "127.0.0.1:19088/api/execSql"
|
||||||
|
print("modify dbHandle ")
|
||||||
|
raise RuntimeError("modify dbHandle then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"dbHandle": 1713425147584,
|
||||||
|
"sql": "select * from MSG where localId =100;"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def getChatRoomDetailInfo():
|
||||||
|
url = "127.0.0.1:19088/api/getChatRoomDetailInfo"
|
||||||
|
print("modify chatRoomId ")
|
||||||
|
raise RuntimeError("modify chatRoomId then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123333@chatroom"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def addMemberToChatRoom():
|
||||||
|
url = "127.0.0.1:19088/api/addMemberToChatRoom"
|
||||||
|
print("modify chatRoomId memberIds ")
|
||||||
|
raise RuntimeError("modify chatRoomId memberIds then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123@chatroom",
|
||||||
|
"memberIds": "wxid_123"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def delMemberFromChatRoom():
|
||||||
|
url = "127.0.0.1:19088/api/delMemberFromChatRoom"
|
||||||
|
print("modify chatRoomId memberIds ")
|
||||||
|
raise RuntimeError("modify chatRoomId memberIds then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "21363231004@chatroom",
|
||||||
|
"memberIds": "wxid_123"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def modifyNickname():
|
||||||
|
url = "127.0.0.1:19088/api/modifyNickname"
|
||||||
|
print("modify chatRoomId wxid nickName")
|
||||||
|
raise RuntimeError("modify chatRoomId wxid nickName then deleted me")
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123@chatroom",
|
||||||
|
"wxid": "wxid_123",
|
||||||
|
"nickName": "test"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def getMemberFromChatRoom():
|
||||||
|
print("modify chatRoomId ")
|
||||||
|
raise RuntimeError("modify chatRoomId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/getMemberFromChatRoom"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123@chatroom"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def topMsg():
|
||||||
|
print("modify msgId ")
|
||||||
|
raise RuntimeError("modify msgId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/topMsg"
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": 1222222
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def removeTopMsg():
|
||||||
|
print("modify msgId chatRoomId ")
|
||||||
|
raise RuntimeError("modify msgId chatRoomId then deleted me")
|
||||||
|
|
||||||
|
url = "127.0.0.1:19088/api/removeTopMsg"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123@chatroom",
|
||||||
|
"msgId": 123
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def InviteMemberToChatRoom():
|
||||||
|
print("modify memberIds chatRoomId ")
|
||||||
|
raise RuntimeError("modify memberIds chatRoomId then deleted me")
|
||||||
|
|
||||||
|
url = "127.0.0.1:19088/api/InviteMemberToChatRoom"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123@chatroom",
|
||||||
|
"memberIds": "wxid_123"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def hookLog():
|
||||||
|
url = "127.0.0.1:19088/api/hookLog"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def unhookLog():
|
||||||
|
url = "127.0.0.1:19088/api/unhookLog"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def createChatRoom():
|
||||||
|
print("modify memberIds ")
|
||||||
|
raise RuntimeError("modify memberIds then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/createChatRoom"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"memberIds": "wxid_8yn4k908tdqp22,wxid_oyb662qhop4422"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def quitChatRoom():
|
||||||
|
print("modify chatRoomId ")
|
||||||
|
raise RuntimeError("modify chatRoomId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/quitChatRoom"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "123@chatroom"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def forwardMsg():
|
||||||
|
print("modify msgId ")
|
||||||
|
raise RuntimeError("modify msgId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/forwardMsg"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"msgId": "12331"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def getSNSFirstPage():
|
||||||
|
url = "127.0.0.1:19088/api/getSNSFirstPage"
|
||||||
|
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def getSNSNextPage():
|
||||||
|
print("modify snsId ")
|
||||||
|
raise RuntimeError("modify snsId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/getSNSNextPage"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"snsId": ""
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def addFavFromMsg():
|
||||||
|
print("modify msgId ")
|
||||||
|
raise RuntimeError("modify msgId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/addFavFromMsg"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": "1222222"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def addFavFromImage():
|
||||||
|
print("modify wxid imagePath ")
|
||||||
|
raise RuntimeError("modify wxid imagePath then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/addFavFromImage"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "",
|
||||||
|
"imagePath": ""
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def getContactProfile():
|
||||||
|
print("modify wxid ")
|
||||||
|
raise RuntimeError("modify wxid then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/getContactProfile"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": ""
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def sendAtText():
|
||||||
|
print("modify wxids chatRoomId")
|
||||||
|
raise RuntimeError("modify wxids chatRoomId then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/sendAtText"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxids": "notify@all",
|
||||||
|
"chatRoomId": "123@chatroom",
|
||||||
|
"msg": "你好啊"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def forwardPublicMsg():
|
||||||
|
print("modify param ")
|
||||||
|
raise RuntimeError("modify param then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/forwardPublicMsg"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"appName": "",
|
||||||
|
"userName": "",
|
||||||
|
"title": "",
|
||||||
|
"url": "",
|
||||||
|
"thumbUrl": "",
|
||||||
|
"digest": "",
|
||||||
|
"wxid": "filehelper"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def forwardPublicMsgByMsgId():
|
||||||
|
print("modify param ")
|
||||||
|
raise RuntimeError("modify param then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/forwardPublicMsgByMsgId"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": 123,
|
||||||
|
"wxid": "filehelper"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
def downloadAttach():
|
||||||
|
print("modify param ")
|
||||||
|
raise RuntimeError("modify param then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/downloadAttach"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": 123
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def decodeImage():
|
||||||
|
print("modify param ")
|
||||||
|
raise RuntimeError("modify param then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/decodeImage"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"filePath": "C:\\66664816980131.dat",
|
||||||
|
"storeDir": "C:\\test"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def getVoiceByMsgId():
|
||||||
|
print("modify param ")
|
||||||
|
raise RuntimeError("modify param then deleted me")
|
||||||
|
url = "127.0.0.1:19088/api/getVoiceByMsgId"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": 7880439644200,
|
||||||
|
"storeDir": "c:\\test"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
checkLogin()
|
||||||
|
# userInfo()
|
595
python/client.py
Normal file
595
python/client.py
Normal file
@ -0,0 +1,595 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def check_login():
|
||||||
|
"""
|
||||||
|
0.检查是否登录
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=0"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def user_info():
|
||||||
|
"""
|
||||||
|
登录用户信息
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=8"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def send_text():
|
||||||
|
"""
|
||||||
|
发送文本
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=2"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"msg": "123"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def send_at():
|
||||||
|
"""
|
||||||
|
发送@消息
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=3"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "12333@chatroom",
|
||||||
|
"wxids": "notify@all",
|
||||||
|
"msg": "12333"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def send_img():
|
||||||
|
"""
|
||||||
|
发送图片
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=5"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"imagePath": "C:/123.png"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def send_file():
|
||||||
|
"""
|
||||||
|
发送文件
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=6"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"filePath": "C:/test.txt"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def hook_msg():
|
||||||
|
"""
|
||||||
|
hook 消息
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=9"
|
||||||
|
payload = json.dumps({
|
||||||
|
"port": "19099",
|
||||||
|
"ip": "127.0.0.1"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def unhook_msg():
|
||||||
|
"""
|
||||||
|
取消消息hook
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=10"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def hook_img():
|
||||||
|
"""
|
||||||
|
hook 图片
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=11"
|
||||||
|
payload = json.dumps({
|
||||||
|
"imgDir": "C:\\img"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def unhook_img():
|
||||||
|
"""
|
||||||
|
取消hook 图片
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=12"
|
||||||
|
payload = json.dumps({
|
||||||
|
"imgDir": "C:\\img"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def hook_voice():
|
||||||
|
"""
|
||||||
|
hook 语音
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=56"
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": 322456091115784000
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def unhook_voice():
|
||||||
|
"""
|
||||||
|
取消hook 语音
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=14"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def del_friend():
|
||||||
|
"""
|
||||||
|
删除好友
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=17"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "wxid_1124423322"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def search_friend():
|
||||||
|
"""
|
||||||
|
网络搜素用户
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=19"
|
||||||
|
payload = json.dumps({
|
||||||
|
"keyword": "13812345678"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def add_friend():
|
||||||
|
"""
|
||||||
|
添加好友
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=20"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "wxid_o11222334422"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_chat_room_members():
|
||||||
|
"""
|
||||||
|
群成员
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=25"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "2112222004@chatroom"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_member_nickname():
|
||||||
|
"""
|
||||||
|
群成员昵称
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=26"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "322333384@chatroom",
|
||||||
|
"memberId": "wxid_4m1112222u22"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def del_member():
|
||||||
|
"""
|
||||||
|
删除群成员
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=27"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "31122263384@chatroom",
|
||||||
|
"memberIds": "wxid_12223334422"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def add_member():
|
||||||
|
"""
|
||||||
|
增加群成员
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=28"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "1111163384@chatroom",
|
||||||
|
"memberIds": "wxid_o12222222"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def modify_room_name():
|
||||||
|
"""
|
||||||
|
修改群昵称
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=31"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "222285428@chatroom",
|
||||||
|
"wxid": "wxid_222222512",
|
||||||
|
"nickName": "qqq"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_handlers():
|
||||||
|
"""
|
||||||
|
获取sqlite3的操作句柄
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=32"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def query_db_by_sql():
|
||||||
|
"""
|
||||||
|
查询数据库
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=34"
|
||||||
|
payload = json.dumps({
|
||||||
|
"dbHandle": 116201928,
|
||||||
|
"sql": "select localId from MSG where MsgSvrID= 7533111101686156"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def hook_log():
|
||||||
|
"""
|
||||||
|
hook 日志
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=36"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def unhook_log():
|
||||||
|
"""
|
||||||
|
取消hook日志
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=37"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def forward():
|
||||||
|
"""
|
||||||
|
转发消息
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=40"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "filehelper",
|
||||||
|
"msgid": "705117679011122708"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def logout():
|
||||||
|
"""
|
||||||
|
退出登录
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=44"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_receipt():
|
||||||
|
"""
|
||||||
|
确认收款
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=45"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "wxid_1111112622",
|
||||||
|
"transcationId": "10000500012312222212243388865912",
|
||||||
|
"transferId": "100005000120212222173123036"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def contact_list():
|
||||||
|
"""
|
||||||
|
好友列表
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=46"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def room_detail():
|
||||||
|
"""
|
||||||
|
群详情
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=47"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "199134446111@chatroom"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def ocr():
|
||||||
|
"""
|
||||||
|
ocr提取文字
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=49"
|
||||||
|
payload = json.dumps({
|
||||||
|
"imagePath": "C:\\WeChat Files\\b23e84997144dd12f21554b0.dat"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def pat():
|
||||||
|
"""
|
||||||
|
拍一拍
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=50"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "211111121004@chatroom",
|
||||||
|
"wxid": "wxid_111111111422"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def top_msg():
|
||||||
|
"""
|
||||||
|
消息置顶
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=51"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "wxid_o11114422",
|
||||||
|
"msgid": 3728307145189195000
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def close_top_msg():
|
||||||
|
"""
|
||||||
|
取消置顶
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=52"
|
||||||
|
payload = json.dumps({
|
||||||
|
"chatRoomId": "213222231004@chatroom",
|
||||||
|
"msgid": 3728307145189195000
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def sns_first():
|
||||||
|
"""
|
||||||
|
朋友圈首页
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=53"
|
||||||
|
payload = {}
|
||||||
|
headers = {}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def sns_next():
|
||||||
|
"""
|
||||||
|
朋友圈下一页
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=54"
|
||||||
|
payload = json.dumps({
|
||||||
|
"snsId": "14091988153735844377"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def query_nickname():
|
||||||
|
"""
|
||||||
|
查询联系人或群名称
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=55"
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"id": "wxid_1112p4422"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def download_msg_attach():
|
||||||
|
"""
|
||||||
|
下载消息附件
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=56"
|
||||||
|
payload = json.dumps({
|
||||||
|
"msgId": 6080100336053626000
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_member_info():
|
||||||
|
"""
|
||||||
|
获取群/群成员信息
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = "127.0.0.1:19088/api/?type=57"
|
||||||
|
payload = json.dumps({
|
||||||
|
"wxid": "wxid_tx8k6tu21112"
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
check_login()
|
||||||
|
user_info()
|
||||||
|
send_text()
|
51
python/decrypt.py
Normal file
51
python/decrypt.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import ctypes
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
|
||||||
|
# pip install pycryptodome
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt(password, input_file, out_file):
|
||||||
|
password = bytes.fromhex(password.replace(' ', ''))
|
||||||
|
with open(input_file, 'rb') as (f):
|
||||||
|
blist = f.read()
|
||||||
|
print(len(blist))
|
||||||
|
salt = blist[:16]
|
||||||
|
key = hashlib.pbkdf2_hmac('sha1', password, salt, DEFAULT_ITER, KEY_SIZE)
|
||||||
|
first = blist[16:DEFAULT_PAGESIZE]
|
||||||
|
mac_salt = bytes([x ^ 58 for x in salt])
|
||||||
|
mac_key = hashlib.pbkdf2_hmac('sha1', key, mac_salt, 2, KEY_SIZE)
|
||||||
|
hash_mac = hmac.new(mac_key, digestmod='sha1')
|
||||||
|
hash_mac.update(first[:-32])
|
||||||
|
hash_mac.update(bytes(ctypes.c_int(1)))
|
||||||
|
if hash_mac.digest() == first[-32:-12]:
|
||||||
|
print('decrypt success')
|
||||||
|
else:
|
||||||
|
print('password error')
|
||||||
|
return
|
||||||
|
blist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
|
||||||
|
with open(out_file, 'wb') as (f):
|
||||||
|
f.write(SQLITE_FILE_HEADER)
|
||||||
|
t = AES.new(key, AES.MODE_CBC, first[-48:-32])
|
||||||
|
f.write(t.decrypt(first[:-48]))
|
||||||
|
f.write(first[-48:])
|
||||||
|
for i in blist:
|
||||||
|
t = AES.new(key, AES.MODE_CBC, i[-48:-32])
|
||||||
|
f.write(t.decrypt(i[:-48]))
|
||||||
|
f.write(i[-48:])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
password = '565735E30E474DA09250CB5AA047E3940FFA1C6F767C4263B13ABB512933DA49'
|
||||||
|
input_file = 'C:/var/Applet.db'
|
||||||
|
out_file = 'c:/var/out/Applet.db'
|
||||||
|
decrypt(password, input_file, out_file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
SQLITE_FILE_HEADER = bytes('SQLite format 3', encoding='ASCII') + bytes(1)
|
||||||
|
KEY_SIZE = 32
|
||||||
|
DEFAULT_PAGESIZE = 4096
|
||||||
|
DEFAULT_ITER = 64000
|
||||||
|
main()
|
26
python/http_server.py
Normal file
26
python/http_server.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from fastapi import FastAPI, Request
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# pip install fastapi
|
||||||
|
# run command :uvicorn test:app --reload
|
||||||
|
# 127.0.0.1:8000/api
|
||||||
|
|
||||||
|
@app.post("/api")
|
||||||
|
def create_item(request: Request):
|
||||||
|
print("recv msg")
|
||||||
|
return {"code": 0, "msg": "success"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def TestCustomMiddleware(request: Request, call_next):
|
||||||
|
the_headers = request.headers
|
||||||
|
the_body = await request.json()
|
||||||
|
|
||||||
|
print(the_headers)
|
||||||
|
print(the_body)
|
||||||
|
|
||||||
|
response = await call_next(request)
|
||||||
|
|
||||||
|
return response
|
10
python/readme.md
Normal file
10
python/readme.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
### 常用的一些工具
|
||||||
|
|
||||||
|
|
||||||
|
client.py : 快速测试dll的http接口。
|
||||||
|
|
||||||
|
decrpt.py : 微信数据库解密工具。password 为dll个人信息里返回的dbkey。
|
||||||
|
|
||||||
|
http_server.py : 一个简单的http server,用来接收hook的消息。
|
||||||
|
|
||||||
|
tcpserver.py: 一个简单的tcp server,用来接收hook的消息。
|
20
source/CMakeLists.txt
Normal file
20
source/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0.0)
|
||||||
|
project(injector VERSION 1.0.0)
|
||||||
|
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D '_UNICODE' /D 'UNICODE'")
|
||||||
|
|
||||||
|
file(GLOB INJECT_CPP_FILES ${PROJECT_SOURCE_DIR}/*.cc ${PROJECT_SOURCE_DIR}/*.cpp)
|
||||||
|
|
||||||
|
add_executable (injector ${INJECT_CPP_FILES})
|
||||||
|
|
||||||
|
SET_TARGET_PROPERTIES(injector PROPERTIES LINKER_LANGUAGE C
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin
|
||||||
|
OUTPUT_NAME "injector"
|
||||||
|
PREFIX "")
|
||||||
|
|
659
source/getopt.h
Normal file
659
source/getopt.h
Normal file
@ -0,0 +1,659 @@
|
|||||||
|
#ifndef __GETOPT_H__
|
||||||
|
/**
|
||||||
|
* DISCLAIMER
|
||||||
|
* This file is part of the mingw-w64 runtime package.
|
||||||
|
*
|
||||||
|
* The mingw-w64 runtime package and its code is distributed in the hope that it
|
||||||
|
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
|
||||||
|
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
|
||||||
|
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* Sponsored in part by the Defense Advanced Research Projects
|
||||||
|
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||||
|
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||||
|
*/
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by Dieter Baron and Thomas Klausner.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma warning(disable:4996);
|
||||||
|
|
||||||
|
#define __GETOPT_H__
|
||||||
|
|
||||||
|
/* All the headers include this file. */
|
||||||
|
#include <crtdefs.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
|
||||||
|
|
||||||
|
#ifdef REPLACE_GETOPT
|
||||||
|
int opterr = 1; /* if error message should be printed */
|
||||||
|
int optind = 1; /* index into parent argv vector */
|
||||||
|
int optopt = '?'; /* character checked for validity */
|
||||||
|
#undef optreset /* see getopt.h */
|
||||||
|
#define optreset __mingw_optreset
|
||||||
|
int optreset; /* reset getopt */
|
||||||
|
char* optarg; /* argument associated with option */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//extern int optind; /* index of first non-option in argv */
|
||||||
|
//extern int optopt; /* single option character, as parsed */
|
||||||
|
//extern int opterr; /* flag to enable built-in diagnostics... */
|
||||||
|
// /* (user may set to zero, to suppress) */
|
||||||
|
//
|
||||||
|
//extern char *optarg; /* pointer to argument of current option */
|
||||||
|
|
||||||
|
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||||
|
|
||||||
|
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||||
|
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||||
|
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||||
|
|
||||||
|
/* return values */
|
||||||
|
#define BADCH (int)'?'
|
||||||
|
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||||
|
#define INORDER (int)1
|
||||||
|
|
||||||
|
#ifndef __CYGWIN__
|
||||||
|
#define __progname __argv[0]
|
||||||
|
#else
|
||||||
|
extern char __declspec(dllimport)* __progname;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
static char EMSG[] = "";
|
||||||
|
#else
|
||||||
|
#define EMSG ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int getopt_internal(int, char* const*, const char*,
|
||||||
|
const struct option*, int*, int);
|
||||||
|
static int parse_long_options(char* const*, const char*,
|
||||||
|
const struct option*, int*, int);
|
||||||
|
static int gcd(int, int);
|
||||||
|
static void permute_args(int, int, int, char* const*);
|
||||||
|
|
||||||
|
static char* place = EMSG; /* option letter processing */
|
||||||
|
|
||||||
|
/* XXX: set optreset to 1 rather than these two */
|
||||||
|
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||||
|
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||||
|
|
||||||
|
/* Error messages */
|
||||||
|
static const char recargchar[] = "option requires an argument -- %c";
|
||||||
|
static const char recargstring[] = "option requires an argument -- %s";
|
||||||
|
static const char ambig[] = "ambiguous option -- %.*s";
|
||||||
|
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||||
|
static const char illoptchar[] = "unknown option -- %c";
|
||||||
|
static const char illoptstring[] = "unknown option -- %s";
|
||||||
|
|
||||||
|
static void
|
||||||
|
_vwarnx(const char* fmt, va_list ap)
|
||||||
|
{
|
||||||
|
(void)fprintf(stderr, "%s: ", __progname);
|
||||||
|
if (fmt != NULL)
|
||||||
|
(void)vfprintf(stderr, fmt, ap);
|
||||||
|
(void)fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
warnx(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
_vwarnx(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the greatest common divisor of a and b.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
gcd(int a, int b)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = a % b;
|
||||||
|
while (c != 0) {
|
||||||
|
a = b;
|
||||||
|
b = c;
|
||||||
|
c = a % b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||||
|
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||||
|
* in each block).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||||
|
char* const* nargv)
|
||||||
|
{
|
||||||
|
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||||
|
char* swap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compute lengths of blocks and number and size of cycles
|
||||||
|
*/
|
||||||
|
nnonopts = panonopt_end - panonopt_start;
|
||||||
|
nopts = opt_end - panonopt_end;
|
||||||
|
ncycle = gcd(nnonopts, nopts);
|
||||||
|
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||||
|
|
||||||
|
for (i = 0; i < ncycle; i++) {
|
||||||
|
cstart = panonopt_end + i;
|
||||||
|
pos = cstart;
|
||||||
|
for (j = 0; j < cyclelen; j++) {
|
||||||
|
if (pos >= panonopt_end)
|
||||||
|
pos -= nnonopts;
|
||||||
|
else
|
||||||
|
pos += nopts;
|
||||||
|
swap = nargv[pos];
|
||||||
|
/* LINTED const cast */
|
||||||
|
((char**)nargv)[pos] = nargv[cstart];
|
||||||
|
/* LINTED const cast */
|
||||||
|
((char**)nargv)[cstart] = swap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef REPLACE_GETOPT
|
||||||
|
/*
|
||||||
|
* getopt --
|
||||||
|
* Parse argc/argv argument vector.
|
||||||
|
*
|
||||||
|
* [eventually this will replace the BSD getopt]
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getopt(int nargc, char* const* nargv, const char* options)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||||
|
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||||
|
*
|
||||||
|
* Furthermore, since many privileged programs call getopt()
|
||||||
|
* before dropping privileges it makes sense to keep things
|
||||||
|
* as simple (and bug-free) as possible.
|
||||||
|
*/
|
||||||
|
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
|
||||||
|
}
|
||||||
|
#endif /* REPLACE_GETOPT */
|
||||||
|
|
||||||
|
//extern int getopt(int nargc, char * const *nargv, const char *options);
|
||||||
|
|
||||||
|
#ifdef _BSD_SOURCE
|
||||||
|
/*
|
||||||
|
* BSD adds the non-standard `optreset' feature, for reinitialisation
|
||||||
|
* of `getopt' parsing. We support this feature, for applications which
|
||||||
|
* proclaim their BSD heritage, before including this header; however,
|
||||||
|
* to maintain portability, developers are advised to avoid it.
|
||||||
|
*/
|
||||||
|
# define optreset __mingw_optreset
|
||||||
|
extern int optreset;
|
||||||
|
#endif
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* POSIX requires the `getopt' API to be specified in `unistd.h';
|
||||||
|
* thus, `unistd.h' includes this header. However, we do not want
|
||||||
|
* to expose the `getopt_long' or `getopt_long_only' APIs, when
|
||||||
|
* included in this manner. Thus, close the standard __GETOPT_H__
|
||||||
|
* declarations block, and open an additional __GETOPT_LONG_H__
|
||||||
|
* specific block, only when *not* __UNISTD_H_SOURCED__, in which
|
||||||
|
* to declare the extended API.
|
||||||
|
*/
|
||||||
|
#endif /* !defined(__GETOPT_H__) */
|
||||||
|
|
||||||
|
#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
|
||||||
|
#define __GETOPT_LONG_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct option /* specification for a long form option... */
|
||||||
|
{
|
||||||
|
const char* name; /* option name, without leading hyphens */
|
||||||
|
int has_arg; /* does it take an argument? */
|
||||||
|
int* flag; /* where to save its status, or NULL */
|
||||||
|
int val; /* its associated status value */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum /* permitted values for its `has_arg' field... */
|
||||||
|
{
|
||||||
|
no_argument = 0, /* option never takes an argument */
|
||||||
|
required_argument, /* option always requires an argument */
|
||||||
|
optional_argument /* option may take an argument */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parse_long_options --
|
||||||
|
* Parse long options in argc/argv argument vector.
|
||||||
|
* Returns -1 if short_too is set and the option does not match long_options.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
parse_long_options(char* const* nargv, const char* options,
|
||||||
|
const struct option* long_options, int* idx, int short_too)
|
||||||
|
{
|
||||||
|
char* current_argv, * has_equal;
|
||||||
|
size_t current_argv_len;
|
||||||
|
int i, ambiguous, match;
|
||||||
|
|
||||||
|
#define IDENTICAL_INTERPRETATION(_x, _y) \
|
||||||
|
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
|
||||||
|
long_options[(_x)].flag == long_options[(_y)].flag && \
|
||||||
|
long_options[(_x)].val == long_options[(_y)].val)
|
||||||
|
|
||||||
|
current_argv = place;
|
||||||
|
match = -1;
|
||||||
|
ambiguous = 0;
|
||||||
|
|
||||||
|
optind++;
|
||||||
|
|
||||||
|
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||||
|
/* argument found (--option=arg) */
|
||||||
|
current_argv_len = has_equal - current_argv;
|
||||||
|
has_equal++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_argv_len = strlen(current_argv);
|
||||||
|
|
||||||
|
for (i = 0; long_options[i].name; i++) {
|
||||||
|
/* find matching long option */
|
||||||
|
if (strncmp(current_argv, long_options[i].name,
|
||||||
|
current_argv_len))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strlen(long_options[i].name) == current_argv_len) {
|
||||||
|
/* exact match */
|
||||||
|
match = i;
|
||||||
|
ambiguous = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If this is a known short option, don't allow
|
||||||
|
* a partial match of a single character.
|
||||||
|
*/
|
||||||
|
if (short_too && current_argv_len == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (match == -1) /* partial match */
|
||||||
|
match = i;
|
||||||
|
else if (!IDENTICAL_INTERPRETATION(i, match))
|
||||||
|
ambiguous = 1;
|
||||||
|
}
|
||||||
|
if (ambiguous) {
|
||||||
|
/* ambiguous abbreviation */
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(ambig, (int)current_argv_len,
|
||||||
|
current_argv);
|
||||||
|
optopt = 0;
|
||||||
|
return (BADCH);
|
||||||
|
}
|
||||||
|
if (match != -1) { /* option found */
|
||||||
|
if (long_options[match].has_arg == no_argument
|
||||||
|
&& has_equal) {
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(noarg, (int)current_argv_len,
|
||||||
|
current_argv);
|
||||||
|
/*
|
||||||
|
* XXX: GNU sets optopt to val regardless of flag
|
||||||
|
*/
|
||||||
|
if (long_options[match].flag == NULL)
|
||||||
|
optopt = long_options[match].val;
|
||||||
|
else
|
||||||
|
optopt = 0;
|
||||||
|
return (BADARG);
|
||||||
|
}
|
||||||
|
if (long_options[match].has_arg == required_argument ||
|
||||||
|
long_options[match].has_arg == optional_argument) {
|
||||||
|
if (has_equal)
|
||||||
|
optarg = has_equal;
|
||||||
|
else if (long_options[match].has_arg ==
|
||||||
|
required_argument) {
|
||||||
|
/*
|
||||||
|
* optional argument doesn't use next nargv
|
||||||
|
*/
|
||||||
|
optarg = nargv[optind++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((long_options[match].has_arg == required_argument)
|
||||||
|
&& (optarg == NULL)) {
|
||||||
|
/*
|
||||||
|
* Missing argument; leading ':' indicates no error
|
||||||
|
* should be generated.
|
||||||
|
*/
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(recargstring,
|
||||||
|
current_argv);
|
||||||
|
/*
|
||||||
|
* XXX: GNU sets optopt to val regardless of flag
|
||||||
|
*/
|
||||||
|
if (long_options[match].flag == NULL)
|
||||||
|
optopt = long_options[match].val;
|
||||||
|
else
|
||||||
|
optopt = 0;
|
||||||
|
--optind;
|
||||||
|
return (BADARG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { /* unknown option */
|
||||||
|
if (short_too) {
|
||||||
|
--optind;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(illoptstring, current_argv);
|
||||||
|
optopt = 0;
|
||||||
|
return (BADCH);
|
||||||
|
}
|
||||||
|
if (idx)
|
||||||
|
*idx = match;
|
||||||
|
if (long_options[match].flag) {
|
||||||
|
*long_options[match].flag = long_options[match].val;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return (long_options[match].val);
|
||||||
|
#undef IDENTICAL_INTERPRETATION
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt_internal --
|
||||||
|
* Parse argc/argv argument vector. Called by user level routines.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
getopt_internal(int nargc, char* const* nargv, const char* options,
|
||||||
|
const struct option* long_options, int* idx, int flags)
|
||||||
|
{
|
||||||
|
char* oli; /* option letter list index */
|
||||||
|
int optchar, short_too;
|
||||||
|
static int posixly_correct = -1;
|
||||||
|
|
||||||
|
if (options == NULL)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||||
|
* XXX using optreset. Work around this braindamage.
|
||||||
|
*/
|
||||||
|
if (optind == 0)
|
||||||
|
optind = optreset = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||||
|
* string begins with a '+'.
|
||||||
|
*
|
||||||
|
* CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
|
||||||
|
* optreset != 0 for GNU compatibility.
|
||||||
|
*/
|
||||||
|
if (posixly_correct == -1 || optreset != 0)
|
||||||
|
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||||
|
if (*options == '-')
|
||||||
|
flags |= FLAG_ALLARGS;
|
||||||
|
else if (posixly_correct || *options == '+')
|
||||||
|
flags &= ~FLAG_PERMUTE;
|
||||||
|
if (*options == '+' || *options == '-')
|
||||||
|
options++;
|
||||||
|
|
||||||
|
optarg = NULL;
|
||||||
|
if (optreset)
|
||||||
|
nonopt_start = nonopt_end = -1;
|
||||||
|
start:
|
||||||
|
if (optreset || !*place) { /* update scanning pointer */
|
||||||
|
optreset = 0;
|
||||||
|
if (optind >= nargc) { /* end of argument vector */
|
||||||
|
place = EMSG;
|
||||||
|
if (nonopt_end != -1) {
|
||||||
|
/* do permutation, if we have to */
|
||||||
|
permute_args(nonopt_start, nonopt_end,
|
||||||
|
optind, nargv);
|
||||||
|
optind -= nonopt_end - nonopt_start;
|
||||||
|
}
|
||||||
|
else if (nonopt_start != -1) {
|
||||||
|
/*
|
||||||
|
* If we skipped non-options, set optind
|
||||||
|
* to the first of them.
|
||||||
|
*/
|
||||||
|
optind = nonopt_start;
|
||||||
|
}
|
||||||
|
nonopt_start = nonopt_end = -1;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (*(place = nargv[optind]) != '-' ||
|
||||||
|
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||||
|
place = EMSG; /* found non-option */
|
||||||
|
if (flags & FLAG_ALLARGS) {
|
||||||
|
/*
|
||||||
|
* GNU extension:
|
||||||
|
* return non-option as argument to option 1
|
||||||
|
*/
|
||||||
|
optarg = nargv[optind++];
|
||||||
|
return (INORDER);
|
||||||
|
}
|
||||||
|
if (!(flags & FLAG_PERMUTE)) {
|
||||||
|
/*
|
||||||
|
* If no permutation wanted, stop parsing
|
||||||
|
* at first non-option.
|
||||||
|
*/
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
/* do permutation */
|
||||||
|
if (nonopt_start == -1)
|
||||||
|
nonopt_start = optind;
|
||||||
|
else if (nonopt_end != -1) {
|
||||||
|
permute_args(nonopt_start, nonopt_end,
|
||||||
|
optind, nargv);
|
||||||
|
nonopt_start = optind -
|
||||||
|
(nonopt_end - nonopt_start);
|
||||||
|
nonopt_end = -1;
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
/* process next argument */
|
||||||
|
goto start;
|
||||||
|
}
|
||||||
|
if (nonopt_start != -1 && nonopt_end == -1)
|
||||||
|
nonopt_end = optind;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have "-" do nothing, if "--" we are done.
|
||||||
|
*/
|
||||||
|
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||||
|
optind++;
|
||||||
|
place = EMSG;
|
||||||
|
/*
|
||||||
|
* We found an option (--), so if we skipped
|
||||||
|
* non-options, we have to permute.
|
||||||
|
*/
|
||||||
|
if (nonopt_end != -1) {
|
||||||
|
permute_args(nonopt_start, nonopt_end,
|
||||||
|
optind, nargv);
|
||||||
|
optind -= nonopt_end - nonopt_start;
|
||||||
|
}
|
||||||
|
nonopt_start = nonopt_end = -1;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check long options if:
|
||||||
|
* 1) we were passed some
|
||||||
|
* 2) the arg is not just "-"
|
||||||
|
* 3) either the arg starts with -- we are getopt_long_only()
|
||||||
|
*/
|
||||||
|
if (long_options != NULL && place != nargv[optind] &&
|
||||||
|
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||||
|
short_too = 0;
|
||||||
|
if (*place == '-')
|
||||||
|
place++; /* --foo long option */
|
||||||
|
else if (*place != ':' && strchr(options, *place) != NULL)
|
||||||
|
short_too = 1; /* could be short option too */
|
||||||
|
|
||||||
|
optchar = parse_long_options(nargv, options, long_options,
|
||||||
|
idx, short_too);
|
||||||
|
if (optchar != -1) {
|
||||||
|
place = EMSG;
|
||||||
|
return (optchar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((optchar = (int)*place++) == (int)':' ||
|
||||||
|
(optchar == (int)'-' && *place != '\0') ||
|
||||||
|
(oli = (char*)strchr(options, optchar)) == NULL) {
|
||||||
|
/*
|
||||||
|
* If the user specified "-" and '-' isn't listed in
|
||||||
|
* options, return -1 (non-option) as per POSIX.
|
||||||
|
* Otherwise, it is an unknown option character (or ':').
|
||||||
|
*/
|
||||||
|
if (optchar == (int)'-' && *place == '\0')
|
||||||
|
return (-1);
|
||||||
|
if (!*place)
|
||||||
|
++optind;
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(illoptchar, optchar);
|
||||||
|
optopt = optchar;
|
||||||
|
return (BADCH);
|
||||||
|
}
|
||||||
|
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||||
|
/* -W long-option */
|
||||||
|
if (*place) /* no space */
|
||||||
|
/* NOTHING */;
|
||||||
|
else if (++optind >= nargc) { /* no arg */
|
||||||
|
place = EMSG;
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(recargchar, optchar);
|
||||||
|
optopt = optchar;
|
||||||
|
return (BADARG);
|
||||||
|
}
|
||||||
|
else /* white space */
|
||||||
|
place = nargv[optind];
|
||||||
|
optchar = parse_long_options(nargv, options, long_options,
|
||||||
|
idx, 0);
|
||||||
|
place = EMSG;
|
||||||
|
return (optchar);
|
||||||
|
}
|
||||||
|
if (*++oli != ':') { /* doesn't take argument */
|
||||||
|
if (!*place)
|
||||||
|
++optind;
|
||||||
|
}
|
||||||
|
else { /* takes (optional) argument */
|
||||||
|
optarg = NULL;
|
||||||
|
if (*place) /* no white space */
|
||||||
|
optarg = place;
|
||||||
|
else if (oli[1] != ':') { /* arg not optional */
|
||||||
|
if (++optind >= nargc) { /* no arg */
|
||||||
|
place = EMSG;
|
||||||
|
if (PRINT_ERROR)
|
||||||
|
warnx(recargchar, optchar);
|
||||||
|
optopt = optchar;
|
||||||
|
return (BADARG);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
optarg = nargv[optind];
|
||||||
|
}
|
||||||
|
place = EMSG;
|
||||||
|
++optind;
|
||||||
|
}
|
||||||
|
/* dump back option letter */
|
||||||
|
return (optchar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt_long --
|
||||||
|
* Parse argc/argv argument vector.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getopt_long(int nargc, char* const* nargv, const char* options,
|
||||||
|
const struct option* long_options, int* idx)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||||
|
FLAG_PERMUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getopt_long_only --
|
||||||
|
* Parse argc/argv argument vector.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getopt_long_only(int nargc, char* const* nargv, const char* options,
|
||||||
|
const struct option* long_options, int* idx)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||||
|
FLAG_PERMUTE | FLAG_LONGONLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
//extern int getopt_long(int nargc, char * const *nargv, const char *options,
|
||||||
|
// const struct option *long_options, int *idx);
|
||||||
|
//extern int getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||||
|
// const struct option *long_options, int *idx);
|
||||||
|
/*
|
||||||
|
* Previous MinGW implementation had...
|
||||||
|
*/
|
||||||
|
#ifndef HAVE_DECL_GETOPT
|
||||||
|
/*
|
||||||
|
* ...for the long form API only; keep this for compatibility.
|
||||||
|
*/
|
||||||
|
# define HAVE_DECL_GETOPT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
|
1170
source/injector.cc
Normal file
1170
source/injector.cc
Normal file
File diff suppressed because it is too large
Load Diff
1
spdlog
Submodule
1
spdlog
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit ad0e89cbfb4d0c1ce4d097e134eb7be67baebb36
|
755
src/api.cc
755
src/api.cc
@ -1,755 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "api.h"
|
|
||||||
#include <mongoose.h>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#include "send_text.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "send_image.h"
|
|
||||||
#include "send_file.h"
|
|
||||||
#include "hook_recv_msg.h"
|
|
||||||
#include "get_db_handle.h"
|
|
||||||
#include "wechat_data.h"
|
|
||||||
#include "forward.h"
|
|
||||||
#include "db_operation.h"
|
|
||||||
#include "contact.h"
|
|
||||||
#include "chat_room.h"
|
|
||||||
#include "self_info.h"
|
|
||||||
#include "hook_img.h"
|
|
||||||
#include "ocr.h"
|
|
||||||
#include "pat.h"
|
|
||||||
#include "confirm_receipt.h"
|
|
||||||
#include "sns.h"
|
|
||||||
#include "search_contact.h"
|
|
||||||
#include "download.h"
|
|
||||||
#include "hook_log.h"
|
|
||||||
#include "hook_voice.h"
|
|
||||||
|
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
|
||||||
using namespace std;
|
|
||||||
using namespace nlohmann;
|
|
||||||
|
|
||||||
#define STR2INT(str) (is_digit(str) ? stoi(str) : 0)
|
|
||||||
#define WS2LW(wstr) (LPWSTR) wstr.c_str()
|
|
||||||
|
|
||||||
static bool kHttpRuning = false;
|
|
||||||
static HANDLE kHttpThread = NULL;
|
|
||||||
|
|
||||||
bool is_digit(string str) {
|
|
||||||
if (str.length() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it : str) {
|
|
||||||
if (it < '0' || it > '9') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string get_var(mg_http_message *hm, string name) {
|
|
||||||
string ret;
|
|
||||||
char *buffer = new char[hm->query.len + 1];
|
|
||||||
ZeroMemory(buffer, hm->query.len + 1);
|
|
||||||
int len = mg_http_get_var(&hm->query, name.c_str(), buffer, hm->query.len);
|
|
||||||
if (len > 0) ret = string(buffer, len);
|
|
||||||
delete[] buffer;
|
|
||||||
buffer = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/// @brief 获取request中的请求参数int类型
|
|
||||||
/// @param hm 消息
|
|
||||||
/// @param data json数据
|
|
||||||
/// @param key key
|
|
||||||
/// @param method 是否是post,暂时全部用post
|
|
||||||
/// @return int
|
|
||||||
static int get_http_req_param_int(mg_http_message *hm, json data, string key, int method){
|
|
||||||
int result;
|
|
||||||
switch (method) {
|
|
||||||
case 0: {
|
|
||||||
result = STR2INT(get_var(hm,key).c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
try {
|
|
||||||
result = data[key].get<int>();
|
|
||||||
} catch (json::exception) {
|
|
||||||
result = STR2INT(data[key].get<string>());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief 获取request中的请求参数
|
|
||||||
/// @param hm 消息
|
|
||||||
/// @param data json数据
|
|
||||||
/// @param key key
|
|
||||||
/// @param method 是否是post,暂时全部用post
|
|
||||||
/// @return
|
|
||||||
static wstring get_http_req_param(mg_http_message *hm, json data, string key, int method){
|
|
||||||
wstring result;
|
|
||||||
switch (method) {
|
|
||||||
case 0: {
|
|
||||||
result = utf8_to_unicode(get_var(hm,key).c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
result = utf8_to_unicode(data[key].get<string>().c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long long get_http_param_ulong64(mg_http_message *hm,
|
|
||||||
json j_data, string key,
|
|
||||||
int method) {
|
|
||||||
unsigned long long result = 0;
|
|
||||||
switch (method) {
|
|
||||||
case 0: {
|
|
||||||
string value = get_var(hm, key);
|
|
||||||
istringstream is(value);
|
|
||||||
is >> result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
try {
|
|
||||||
result = j_data[key].get<ULONG64>();
|
|
||||||
} catch (json::exception) {
|
|
||||||
string value = j_data[key].get<string>();
|
|
||||||
istringstream is(value);
|
|
||||||
is >> result;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_http_param_int(mg_http_message *hm, json j_data, string key,
|
|
||||||
int method) {
|
|
||||||
int result = 0;
|
|
||||||
switch (method) {
|
|
||||||
case 0: {
|
|
||||||
result = STR2INT(get_var(hm, key));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
try {
|
|
||||||
result = j_data[key].get<int>();
|
|
||||||
} catch (json::exception) {
|
|
||||||
result = STR2INT(j_data[key].get<string>());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static vector<wstring> get_http_param_array(mg_http_message *hm, json j_data,
|
|
||||||
string key, int method) {
|
|
||||||
vector<wstring> result;
|
|
||||||
switch (method) {
|
|
||||||
case 0: {
|
|
||||||
result = split(utf8_to_unicode(get_var(hm, key).c_str()), L',');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
result = split(utf8_to_unicode(j_data[key].get<string>().c_str()), L',');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief api接口入口解析
|
|
||||||
/// @param hm mg_http_message
|
|
||||||
/// @param c connection
|
|
||||||
/// @param ret json数据
|
|
||||||
void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) {
|
|
||||||
int is_post = 0;
|
|
||||||
|
|
||||||
if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
|
||||||
is_post = 1;
|
|
||||||
}
|
|
||||||
#ifdef _DEBUG
|
|
||||||
printf("method:%s body: %s", hm->method.ptr,hm->body.ptr);
|
|
||||||
#endif
|
|
||||||
if (is_post == 0){
|
|
||||||
json ret_data = {{"result", "ERROR"}, {"msg", "not support method"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
json j_param =
|
|
||||||
json::parse(hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false);
|
|
||||||
if (hm->body.len != 0 && j_param.is_discarded() == true) {
|
|
||||||
json ret_data = {{"result", "ERROR"}, {"msg", "json string is invalid."}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int api_number = STR2INT(get_var(hm, "type"));
|
|
||||||
switch (api_number) {
|
|
||||||
case WECHAT_IS_LOGIN: {
|
|
||||||
int success = CheckLogin();
|
|
||||||
json ret_data = {{"result", "OK"}, {"code", success}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_SELF_INFO: {
|
|
||||||
SelfInfoInner self_info;
|
|
||||||
int success = GetSelfInfo(self_info);
|
|
||||||
json ret_data = {{"result", "OK"}, {"code", success}};
|
|
||||||
if (success) {
|
|
||||||
json j_info = {
|
|
||||||
{"name", self_info.name},
|
|
||||||
{"city", self_info.city},
|
|
||||||
{"province", self_info.province},
|
|
||||||
{"country", self_info.country},
|
|
||||||
{"account", self_info.account},
|
|
||||||
{"wxid", self_info.wxid},
|
|
||||||
{"mobile", self_info.mobile},
|
|
||||||
{"headImage", self_info.head_img},
|
|
||||||
{"signature",self_info.signature},
|
|
||||||
{"dataSavePath",self_info.data_save_path},
|
|
||||||
{"currentDataPath",self_info.current_data_path},
|
|
||||||
};
|
|
||||||
ret_data["data"] = j_info;
|
|
||||||
}
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_TEXT: {
|
|
||||||
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
wstring msg = get_http_req_param(hm, j_param, "msg", is_post);
|
|
||||||
int success = SendText(WS2LW(wxid), WS2LW(msg));
|
|
||||||
json ret_data = {{"result", "OK"}, {"code", success}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_AT: {
|
|
||||||
wstring chat_room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
vector<wstring> wxids = get_http_param_array(hm, j_param, "wxids", is_post);
|
|
||||||
wstring msg = get_http_req_param(hm, j_param, "msg", is_post);
|
|
||||||
vector<wchar_t *> wxid_list;
|
|
||||||
for (unsigned int i = 0; i < wxids.size(); i++){
|
|
||||||
wxid_list.push_back(WS2LW(wxids[i]));
|
|
||||||
}
|
|
||||||
int success = SendAtText(WS2LW(chat_room_id), wxid_list.data(), wxid_list.size(),WS2LW(msg));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_CARD: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_IMAGE: {
|
|
||||||
wstring receiver = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
wstring img_path = get_http_req_param(hm, j_param, "imagePath", is_post);
|
|
||||||
int success = SendImage(WS2LW(receiver), WS2LW(img_path));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_FILE: {
|
|
||||||
wstring receiver = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
wstring file_path = get_http_req_param(hm, j_param, "filePath", is_post);
|
|
||||||
int success = SendFile(WS2LW(receiver), WS2LW(file_path));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_ARTICLE: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_APP: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_START_HOOK: {
|
|
||||||
int port = get_http_req_param_int(hm, j_param, "port", is_post);
|
|
||||||
wstring ip = get_http_req_param(hm, j_param, "ip", is_post);
|
|
||||||
string client_ip = Wstring2String(ip);
|
|
||||||
char ip_cstr[16];
|
|
||||||
strcpy_s(ip_cstr,client_ip.c_str());
|
|
||||||
int success = HookRecvMsg(ip_cstr,port);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_STOP_HOOK: {
|
|
||||||
int success = UnHookRecvMsg();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_START_IMAGE_HOOK: {
|
|
||||||
wstring img_dir = get_http_req_param(hm, j_param, "imgDir", is_post);
|
|
||||||
int success = HookImg(img_dir);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_STOP_IMAGE_HOOK: {
|
|
||||||
int success = UnHookImg();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_START_VOICE_HOOK: {
|
|
||||||
wstring voice_dir = get_http_req_param(hm, j_param, "voiceDir", is_post);
|
|
||||||
int success = HookVoice(voice_dir);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_STOP_VOICE_HOOK: {
|
|
||||||
int success = UnHookVoice();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_GET_LIST: {
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_CHECK_STATUS: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_DEL: {
|
|
||||||
wstring user_id = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
int success = DelContact(WS2LW(user_id));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_SEARCH_BY_CACHE: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_SEARCH_BY_NET: {
|
|
||||||
wstring keyword = get_http_req_param(hm, j_param, "keyword", is_post);
|
|
||||||
UserInfo *user = nullptr;
|
|
||||||
int success = SearchContactNetScene(WS2LW(keyword), &user);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
if (user) {
|
|
||||||
json info = {
|
|
||||||
{"bigImage", unicode_to_utf8(user->big_image)},
|
|
||||||
{"smallImage", unicode_to_utf8(user->small_image)},
|
|
||||||
{"city", unicode_to_utf8(user->city)},
|
|
||||||
{"nation", unicode_to_utf8(user->nation)},
|
|
||||||
{"nickname", unicode_to_utf8(user->nickname)},
|
|
||||||
{"province", unicode_to_utf8(user->province)},
|
|
||||||
{"sex", user->sex},
|
|
||||||
{"signature", unicode_to_utf8(user->signature)},
|
|
||||||
{"v2", unicode_to_utf8(user->v2)},
|
|
||||||
{"v3", unicode_to_utf8(user->v3)},
|
|
||||||
};
|
|
||||||
ret_data["userInfo"] = info;
|
|
||||||
}
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_ADD_BY_WXID: {
|
|
||||||
wstring user_id = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
int success = AddFriendByWxid(WS2LW(user_id));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_ADD_BY_V3: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_ADD_BY_PUBLIC_ID: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_VERIFY_APPLY: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_EDIT_REMARK: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_GET_MEMBER_LIST: {
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
ChatRoomInner out{0};
|
|
||||||
int success = GetMemberFromChatRoom(WS2LW(room_id),out);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
if (success){
|
|
||||||
json member_info ={
|
|
||||||
{"admin",unicode_to_utf8(out.admin)},
|
|
||||||
{"chatRoomId",unicode_to_utf8(out.chat_room)},
|
|
||||||
{"members",out.members},
|
|
||||||
};
|
|
||||||
ret_data["data"] = member_info;
|
|
||||||
}
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_GET_MEMBER_NICKNAME: {
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
wstring member_id = get_http_req_param(hm, j_param, "memberId", is_post);
|
|
||||||
|
|
||||||
wstring nickname = GetChatRoomMemberNickname(WS2LW(room_id),WS2LW(member_id));
|
|
||||||
json ret_data = {{"code", 1}, {"result", "OK"},{"nickname",unicode_to_utf8(WS2LW(nickname))}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_DEL_MEMBER: {
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
vector<wstring> wxids = get_http_param_array(hm, j_param, "memberIds", is_post);
|
|
||||||
vector<wchar_t *> wxid_list;
|
|
||||||
for (unsigned int i = 0; i < wxids.size(); i++){
|
|
||||||
wxid_list.push_back(WS2LW(wxids[i]));
|
|
||||||
}
|
|
||||||
int success = DelMemberFromChatRoom(WS2LW(room_id), wxid_list.data(),wxid_list.size());
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_ADD_MEMBER: {
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
vector<wstring> wxids = get_http_param_array(hm, j_param, "memberIds", is_post);
|
|
||||||
vector<wchar_t *> wxid_list;
|
|
||||||
for (unsigned int i = 0; i < wxids.size(); i++){
|
|
||||||
wxid_list.push_back(WS2LW(wxids[i]));
|
|
||||||
}
|
|
||||||
int success = AddMemberToChatRoom(WS2LW(room_id), wxid_list.data(),wxid_list.size());
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_SET_ANNOUNCEMENT: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_SET_CHATROOM_NAME: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CHATROOM_SET_SELF_NICKNAME: {
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
wstring nick = get_http_req_param(hm, j_param, "nickName", is_post);
|
|
||||||
int success = ModChatRoomMemberNickName(WS2LW(room_id),WS2LW(wxid),WS2LW(nick));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_DATABASE_GET_HANDLES: {
|
|
||||||
vector<void *> v_ptr = GetDbHandles();
|
|
||||||
json ret_data = {{"data", json::array()}, {"result", "OK"}};
|
|
||||||
for (unsigned int i = 0; i < v_ptr.size(); i++) {
|
|
||||||
json db_info;
|
|
||||||
db_info["tables"] = json::array();
|
|
||||||
DatabaseInfo *db = reinterpret_cast<DatabaseInfo *>(v_ptr[i]);
|
|
||||||
db_info["handle"] = db->handle;
|
|
||||||
db_info["databaseName"] = unicode_to_utf8(db->db_name);
|
|
||||||
for (auto table : db->tables) {
|
|
||||||
json table_info = {{"name", table.name},
|
|
||||||
{"tableName", table.table_name},
|
|
||||||
{"sql", table.sql},
|
|
||||||
{"rootpage", table.rootpage}};
|
|
||||||
db_info["tables"].push_back(table_info);
|
|
||||||
}
|
|
||||||
ret_data["data"].push_back(db_info);
|
|
||||||
}
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_DATABASE_BACKUP: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_DATABASE_QUERY: {
|
|
||||||
DWORD db_handle = get_http_param_int(hm, j_param, "dbHandle", is_post);
|
|
||||||
wstring sql = get_http_req_param(hm, j_param, "sql", is_post);
|
|
||||||
string sql_str = unicode_to_utf8(WS2LW(sql));
|
|
||||||
vector<vector<string>> items;
|
|
||||||
int success = Select(db_handle, sql_str.c_str(),items);
|
|
||||||
json ret_data = {{"data", json::array()}, {"code",success},{"result", "OK"}};
|
|
||||||
if(success == 0){
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (auto it : items) {
|
|
||||||
json temp_arr = json::array();
|
|
||||||
for (size_t i = 0; i < it.size(); i++) {
|
|
||||||
temp_arr.push_back(it[i]);
|
|
||||||
}
|
|
||||||
ret_data["data"].push_back(temp_arr);
|
|
||||||
}
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_SET_VERSION: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_LOG_START_HOOK: {
|
|
||||||
int success = HookLog();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_LOG_STOP_HOOK: {
|
|
||||||
int success = UnHookLog();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_BROWSER_OPEN_WITH_URL: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_PUBLIC_MSG: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_FORWARD_MESSAGE: {
|
|
||||||
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post);
|
|
||||||
int success = ForwardMsg(WS2LW(wxid), msgid);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_QRCODE_IMAGE: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_A8KEY: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_MSG_SEND_XML: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_LOGOUT: {
|
|
||||||
int success = Logout();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_TRANSFER: {
|
|
||||||
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
wstring transcationid = get_http_req_param(hm, j_param, "transcationId", is_post);
|
|
||||||
wstring transferid = get_http_req_param(hm, j_param, "transferId", is_post);
|
|
||||||
BOOL response = DoConfirmReceipt(WS2LW(wxid), WS2LW(transcationid), WS2LW(transferid));
|
|
||||||
json ret_data = {{"msg", response}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_CONTACT_ALL: {
|
|
||||||
vector<Contact> vec;
|
|
||||||
int success = GetAllContact(vec);
|
|
||||||
json ret_data = {
|
|
||||||
{"data", json::array()}, {"code", success}, {"result", "OK"}};
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < vec.size(); i++) {
|
|
||||||
#ifdef _DEBUG
|
|
||||||
cout << "vector :" <<i << endl;
|
|
||||||
cout<< "custom :" << vec[i].custom_account.ptr << endl;
|
|
||||||
cout<< "userName :" << vec[i].encrypt_name.ptr << endl;
|
|
||||||
cout<< "wxid :" << vec[i].wxid.ptr << endl;
|
|
||||||
#endif
|
|
||||||
json item = {
|
|
||||||
{"customAccount",
|
|
||||||
vec[i].custom_account.length > 0
|
|
||||||
? vec[i].custom_account.ptr != nullptr
|
|
||||||
? unicode_to_utf8(vec[i].custom_account.ptr)
|
|
||||||
: string()
|
|
||||||
: string()},
|
|
||||||
{"delFlag", vec[i].del_flag},
|
|
||||||
{"userName", vec[i].encrypt_name.length > 0
|
|
||||||
? vec[i].encrypt_name.ptr != nullptr
|
|
||||||
? unicode_to_utf8(vec[i].encrypt_name.ptr)
|
|
||||||
: string()
|
|
||||||
: string()},
|
|
||||||
{"type", vec[i].type},
|
|
||||||
{"verifyFlag", vec[i].verify_flag},
|
|
||||||
{"verifyFlag", vec[i].verify_flag},
|
|
||||||
{"wxid", vec[i].wxid.length > 0 ? unicode_to_utf8(vec[i].wxid.ptr)
|
|
||||||
: string()},
|
|
||||||
};
|
|
||||||
ret_data["data"].push_back(item);
|
|
||||||
}
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_CHATROOM_INFO: {
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
ChatRoomInfoInner chat_room_detail{0};
|
|
||||||
int success = GetChatRoomDetailInfo(WS2LW(room_id), chat_room_detail);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
if(!success){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
json detail = {
|
|
||||||
{"chatRoomId",
|
|
||||||
chat_room_detail.chat_room_id.length > 0
|
|
||||||
? unicode_to_utf8(chat_room_detail.chat_room_id.ptr)
|
|
||||||
: string()},
|
|
||||||
{"notice", chat_room_detail.notice.length > 0
|
|
||||||
? unicode_to_utf8(chat_room_detail.notice.ptr)
|
|
||||||
: string()},
|
|
||||||
{"admin", chat_room_detail.admin.length > 0
|
|
||||||
? unicode_to_utf8(chat_room_detail.admin.ptr)
|
|
||||||
: string()},
|
|
||||||
{"xml", chat_room_detail.xml.length > 0
|
|
||||||
? unicode_to_utf8(chat_room_detail.xml.ptr)
|
|
||||||
: string()},
|
|
||||||
};
|
|
||||||
ret_data["data"]=detail;
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_GET_IMG_BY_NAME: {
|
|
||||||
wstring image_path = get_http_req_param(hm, j_param, "imagePath", is_post);
|
|
||||||
wstring save_path = get_http_req_param(hm, j_param, "savePath", is_post);
|
|
||||||
int success = GetImgByName(WS2LW(image_path),WS2LW(save_path));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_DO_OCR:{
|
|
||||||
wstring image_path = get_http_req_param(hm, j_param, "imagePath", is_post);
|
|
||||||
string text("");
|
|
||||||
int success = DoOCRTask(WS2LW(image_path),text);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"},{"text",text}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_SEND_PAT_MSG:{
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
int success = SendPatMsg(WS2LW(room_id),WS2LW(wxid));
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_SET_TOP_MSG:{
|
|
||||||
wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post);
|
|
||||||
ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post);
|
|
||||||
int success = SetTopMsg(WS2LW(wxid),msgid);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_REMOVE_TOP_MSG:{
|
|
||||||
wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post);
|
|
||||||
ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post);
|
|
||||||
int success = RemoveTopMsg(WS2LW(room_id),msgid);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_SNS_GET_FIRST_PAGE:{
|
|
||||||
int success = GetFirstPage();
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_SNS_GET_NEXT_PAGE: {
|
|
||||||
ULONG64 snsid = get_http_param_ulong64(hm, j_param, "snsId", is_post);
|
|
||||||
int success = GetNextPage(snsid);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_CONTACT_NAME:{
|
|
||||||
wstring pri_id = get_http_req_param(hm, j_param, "id", is_post);
|
|
||||||
wstring name =GetContactOrChatRoomNickname(WS2LW(pri_id));
|
|
||||||
json ret_data = {{"code", 1}, {"result", "OK"},{"name",unicode_to_utf8(WS2LW(name))}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WECHAT_ATTACH_DOWNLOAD:{
|
|
||||||
ULONG64 msg_id = get_http_param_ulong64(hm, j_param, "msgId", is_post);
|
|
||||||
int success = DoDownloadTask(msg_id);
|
|
||||||
json ret_data = {{"code", success}, {"result", "OK"}};
|
|
||||||
ret = ret_data.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief 事件回调函数
|
|
||||||
/// @param c 链接
|
|
||||||
/// @param ev 事件
|
|
||||||
/// @param ev_data 事件数据
|
|
||||||
/// @param fn_data 回调数据
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|
||||||
struct mg_http_serve_opts opts = {0};
|
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
|
||||||
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
|
|
||||||
string ret = R"({"result":"OK"})";
|
|
||||||
if (mg_http_match_uri(hm, "/api/")) {
|
|
||||||
try {
|
|
||||||
api_handle(hm, c, ret);
|
|
||||||
} catch (json::exception &e) {
|
|
||||||
json res = {{"result", "ERROR"}, {"msg", e.what()}};
|
|
||||||
ret = res.dump();
|
|
||||||
}
|
|
||||||
if (ret != "") {
|
|
||||||
mg_http_reply(c, 200, "", ret.c_str(), 0, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mg_http_reply(c, 500, NULL, "%s", "Invalid URI");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(void)fn_data;
|
|
||||||
}
|
|
||||||
/// @brief http server
|
|
||||||
/// @param port 端口
|
|
||||||
void http_server(int port) {
|
|
||||||
string lsten_addr = "http://0.0.0.0:" + to_string(port);
|
|
||||||
struct mg_mgr mgr;
|
|
||||||
// Init manager
|
|
||||||
mg_mgr_init(&mgr);
|
|
||||||
// Setup listener
|
|
||||||
mg_http_listen(&mgr, lsten_addr.c_str(), fn, &mgr);
|
|
||||||
// Event loop
|
|
||||||
for (;;) mg_mgr_poll(&mgr, 1000);
|
|
||||||
// Cleanup
|
|
||||||
mg_mgr_free(&mgr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief 启动http服务
|
|
||||||
/// @param port 端口
|
|
||||||
/// @return 成功返回0
|
|
||||||
int http_start(int port) {
|
|
||||||
if (kHttpRuning) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#ifdef _DEBUG
|
|
||||||
CreateConsole();
|
|
||||||
#endif
|
|
||||||
kHttpRuning = true;
|
|
||||||
kHttpThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)http_server,
|
|
||||||
(LPVOID)port, NULL, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int http_close() {
|
|
||||||
if (!kHttpRuning) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
kHttpRuning = false;
|
|
||||||
if (kHttpThread) {
|
|
||||||
WaitForSingleObject(kHttpThread, -1);
|
|
||||||
CloseHandle(kHttpThread);
|
|
||||||
kHttpThread = NULL;
|
|
||||||
}
|
|
||||||
UnHookRecvMsg();
|
|
||||||
UnHookImg();
|
|
||||||
UnHookSearchContact();
|
|
||||||
UnHookLog();
|
|
||||||
return 0;
|
|
||||||
}
|
|
80
src/api.h
80
src/api.h
@ -1,80 +0,0 @@
|
|||||||
#ifndef API_H_
|
|
||||||
#define API_H_
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum WECHAT_HTTP_APISTag
|
|
||||||
{
|
|
||||||
// login check
|
|
||||||
WECHAT_IS_LOGIN = 0,
|
|
||||||
// self info
|
|
||||||
WECHAT_GET_SELF_INFO,
|
|
||||||
// send message
|
|
||||||
WECHAT_MSG_SEND_TEXT,
|
|
||||||
WECHAT_MSG_SEND_AT,
|
|
||||||
WECHAT_MSG_SEND_CARD,
|
|
||||||
WECHAT_MSG_SEND_IMAGE,
|
|
||||||
WECHAT_MSG_SEND_FILE,
|
|
||||||
WECHAT_MSG_SEND_ARTICLE,
|
|
||||||
WECHAT_MSG_SEND_APP,
|
|
||||||
// receive message
|
|
||||||
WECHAT_MSG_START_HOOK,
|
|
||||||
WECHAT_MSG_STOP_HOOK,
|
|
||||||
WECHAT_MSG_START_IMAGE_HOOK,
|
|
||||||
WECHAT_MSG_STOP_IMAGE_HOOK,
|
|
||||||
WECHAT_MSG_START_VOICE_HOOK,
|
|
||||||
WECHAT_MSG_STOP_VOICE_HOOK,
|
|
||||||
// contact
|
|
||||||
WECHAT_CONTACT_GET_LIST,
|
|
||||||
WECHAT_CONTACT_CHECK_STATUS,
|
|
||||||
WECHAT_CONTACT_DEL,
|
|
||||||
WECHAT_CONTACT_SEARCH_BY_CACHE,
|
|
||||||
WECHAT_CONTACT_SEARCH_BY_NET,
|
|
||||||
WECHAT_CONTACT_ADD_BY_WXID,
|
|
||||||
WECHAT_CONTACT_ADD_BY_V3,
|
|
||||||
WECHAT_CONTACT_ADD_BY_PUBLIC_ID,
|
|
||||||
WECHAT_CONTACT_VERIFY_APPLY,
|
|
||||||
WECHAT_CONTACT_EDIT_REMARK,
|
|
||||||
// chatroom
|
|
||||||
WECHAT_CHATROOM_GET_MEMBER_LIST,
|
|
||||||
WECHAT_CHATROOM_GET_MEMBER_NICKNAME,
|
|
||||||
WECHAT_CHATROOM_DEL_MEMBER,
|
|
||||||
WECHAT_CHATROOM_ADD_MEMBER,
|
|
||||||
WECHAT_CHATROOM_SET_ANNOUNCEMENT,
|
|
||||||
WECHAT_CHATROOM_SET_CHATROOM_NAME,
|
|
||||||
WECHAT_CHATROOM_SET_SELF_NICKNAME,
|
|
||||||
// database
|
|
||||||
WECHAT_DATABASE_GET_HANDLES,
|
|
||||||
WECHAT_DATABASE_BACKUP,
|
|
||||||
WECHAT_DATABASE_QUERY,
|
|
||||||
// version
|
|
||||||
WECHAT_SET_VERSION,
|
|
||||||
// log
|
|
||||||
WECHAT_LOG_START_HOOK,
|
|
||||||
WECHAT_LOG_STOP_HOOK,
|
|
||||||
// browser
|
|
||||||
WECHAT_BROWSER_OPEN_WITH_URL,
|
|
||||||
WECHAT_GET_PUBLIC_MSG,
|
|
||||||
WECHAT_MSG_FORWARD_MESSAGE,
|
|
||||||
WECHAT_GET_QRCODE_IMAGE,
|
|
||||||
WECHAT_GET_A8KEY,
|
|
||||||
WECHAT_MSG_SEND_XML,
|
|
||||||
WECHAT_LOGOUT,
|
|
||||||
WECHAT_GET_TRANSFER,
|
|
||||||
WECHAT_GET_CONTACT_ALL,
|
|
||||||
WECHAT_GET_CHATROOM_INFO,
|
|
||||||
WECHAT_GET_IMG_BY_NAME,
|
|
||||||
WECHAT_DO_OCR,
|
|
||||||
WECHAT_SEND_PAT_MSG,
|
|
||||||
WECHAT_SET_TOP_MSG,
|
|
||||||
WECHAT_REMOVE_TOP_MSG,
|
|
||||||
WECHAT_SNS_GET_FIRST_PAGE,
|
|
||||||
WECHAT_SNS_GET_NEXT_PAGE,
|
|
||||||
WECHAT_CONTACT_NAME,
|
|
||||||
WECHAT_ATTACH_DOWNLOAD,
|
|
||||||
} WECHAT_HTTP_APIS,
|
|
||||||
*PWECHAT_HTTP_APIS;
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" __declspec(dllexport) int http_start(int port);
|
|
||||||
extern "C" __declspec(dllexport) int http_close();
|
|
||||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
/*
|
/*
|
||||||
base64.cpp and base64.h
|
base64.cpp and base64.h
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
Version: 2.rc.08 (release candidate)
|
Version: 2.rc.08 (release candidate)
|
||||||
|
|
||||||
Copyright (C) 2004-2017, 2020, 2021 Ren¨¦ Nyffenegger
|
Copyright (C) 2004-2017, 2020, 2021 Ren<EFBFBD><EFBFBD> Nyffenegger
|
||||||
|
|
||||||
This source code is provided 'as-is', without any express or implied
|
This source code is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the author be held liable for any damages
|
warranty. In no event will the author be held liable for any damages
|
||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
Ren¨¦ Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
Ren<EFBFBD><EFBFBD> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ template <typename String>
|
|||||||
static std::string decode(String encoded_string, bool remove_linebreaks)
|
static std::string decode(String encoded_string, bool remove_linebreaks)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// decode(¡) is templated so that it can be used with String = const std::string&
|
// decode(<EFBFBD><EFBFBD>) is templated so that it can be used with String = const std::string&
|
||||||
// or std::string_view (requires at least C++17)
|
// or std::string_view (requires at least C++17)
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -309,4 +309,4 @@ std::string base64_decode(std::string_view s, bool remove_linebreaks)
|
|||||||
return decode(s, remove_linebreaks);
|
return decode(s, remove_linebreaks);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __cplusplus >= 201703L
|
#endif // __cplusplus >= 201703L
|
@ -1,4 +1,4 @@
|
|||||||
//
|
//
|
||||||
// base64 encoding and decoding with C++.
|
// base64 encoding and decoding with C++.
|
||||||
// Version: 2.rc.08 (release candidate)
|
// Version: 2.rc.08 (release candidate)
|
||||||
//
|
//
|
||||||
@ -32,4 +32,4 @@ std::string base64_encode_mime(std::string_view s);
|
|||||||
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
|
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
|
||||||
#endif // __cplusplus >= 201703L
|
#endif // __cplusplus >= 201703L
|
||||||
|
|
||||||
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
|
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
|
418
src/chat_room.cc
418
src/chat_room.cc
@ -1,418 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "chat_room.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "get_db_handle.h"
|
|
||||||
#include "wechat_data.h"
|
|
||||||
#include "base64.h"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define WX_CHAT_ROOM_MGR_OFFSET 0x72cf60
|
|
||||||
#define WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET 0xb6f260
|
|
||||||
#define WX_NEW_CHAT_ROOM_INFO_OFFSET 0xe15de0
|
|
||||||
#define WX_FREE_CHAT_ROOM_INFO_OFFSET 0xe160b0
|
|
||||||
#define WX_DEL_CHAT_ROOM_MEMBER_OFFSET 0xb64180
|
|
||||||
#define WX_INIT_CHAT_MSG_OFFSET 0xed3be0
|
|
||||||
#define WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET 0xb63c50
|
|
||||||
#define WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET 0xB70260
|
|
||||||
#define WX_INIT_CHAT_ROOM_OFFSET 0xe13b30
|
|
||||||
#define WX_FREE_CHAT_ROOM_OFFSET 0xe13d50
|
|
||||||
#define WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET 0xb6adf0
|
|
||||||
#define WX_NEW_CHAT_MSG_OFFSET 0x70e2a0
|
|
||||||
#define WX_FREE_CHAT_MSG_OFFSET 0x6f4ea0
|
|
||||||
#define WX_TOP_MSG_OFFSET 0xb727e0
|
|
||||||
#define WX_REMOVE_TOP_MSG_OFFSET 0xb725a0
|
|
||||||
#define WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET 0x6f5370
|
|
||||||
#define WX_GET_MEMBER_NICKNAME_OFFSET 0xb703f0
|
|
||||||
#define WX_CONTACT_MGR_INSTANCE_OFFSET 0x6f8990
|
|
||||||
#define WX_GET_CONTACT_OFFSET 0xb93b20
|
|
||||||
#define WX_FREE_CONTACT_OFFSET 0xe23690
|
|
||||||
|
|
||||||
int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) {
|
|
||||||
int success = 0;
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD get_chat_room_detail_addr = base + WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET;
|
|
||||||
DWORD create_chat_room_info_addr = base + WX_NEW_CHAT_ROOM_INFO_OFFSET;
|
|
||||||
DWORD free_chat_room_info_addr = base + WX_FREE_CHAT_ROOM_INFO_OFFSET;
|
|
||||||
char chat_room_info[0xDC] = {0};
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
LEA ECX,chat_room_info
|
|
||||||
CALL create_chat_room_info_addr
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
PUSH 0x0
|
|
||||||
LEA ECX,chat_room_info
|
|
||||||
PUSH ECX
|
|
||||||
LEA ECX,chat_room
|
|
||||||
PUSH ECX
|
|
||||||
MOV ECX,EAX
|
|
||||||
CALL get_chat_room_detail_addr
|
|
||||||
MOV success,EAX
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
DWORD room_id_len = *(DWORD*)(chat_room_info + 0x8);
|
|
||||||
DWORD room_id_max_len = *(DWORD*)(chat_room_info + 0xC);
|
|
||||||
wchar_t * room_id = new wchar_t[room_id_len + 1];
|
|
||||||
wmemcpy(room_id,*(wchar_t**)(chat_room_info + 0x4),room_id_len + 1);
|
|
||||||
room_info.chat_room_id.ptr = room_id;
|
|
||||||
room_info.chat_room_id.length = room_id_len;
|
|
||||||
room_info.chat_room_id.max_length = room_id_max_len;
|
|
||||||
|
|
||||||
|
|
||||||
DWORD notice_len = *(DWORD*)(chat_room_info + 0x1C);
|
|
||||||
DWORD notice_max_len = *(DWORD*)(chat_room_info + 0x20);
|
|
||||||
wchar_t* notice_ptr = *(wchar_t**)(chat_room_info + 0x18);
|
|
||||||
if(notice_len <= 0){
|
|
||||||
room_info.notice.ptr = nullptr;
|
|
||||||
}else{
|
|
||||||
wchar_t * notice = new wchar_t[notice_len + 1];
|
|
||||||
wmemcpy(notice,notice_ptr,notice_len+1);
|
|
||||||
room_info.notice.ptr = notice;
|
|
||||||
}
|
|
||||||
room_info.notice.length = notice_len;
|
|
||||||
room_info.notice.max_length = notice_max_len;
|
|
||||||
|
|
||||||
DWORD admin_len = *(DWORD*)(chat_room_info + 0x30);
|
|
||||||
DWORD admin_max_len = *(DWORD*)(chat_room_info + 0x34);
|
|
||||||
wchar_t* admin_ptr = *(wchar_t**)(chat_room_info + 0x2C);
|
|
||||||
if(admin_len <= 0){
|
|
||||||
room_info.admin.ptr = nullptr;
|
|
||||||
}else{
|
|
||||||
wchar_t * admin = new wchar_t[admin_len + 1];
|
|
||||||
wmemcpy(admin,admin_ptr,admin_len+1);
|
|
||||||
room_info.admin.ptr = admin;
|
|
||||||
}
|
|
||||||
room_info.admin.length = admin_len;
|
|
||||||
room_info.admin.max_length = admin_max_len;
|
|
||||||
|
|
||||||
DWORD xml_len = *(DWORD*)(chat_room_info + 0x54);
|
|
||||||
DWORD xml_max_len = *(DWORD*)(chat_room_info + 0x58);
|
|
||||||
wchar_t* xml_ptr = *(wchar_t**)(chat_room_info + 0x50);
|
|
||||||
if (xml_len <= 0){
|
|
||||||
room_info.xml.ptr = nullptr;
|
|
||||||
}else{
|
|
||||||
wchar_t * xml = new wchar_t[xml_len + 1];
|
|
||||||
wmemcpy(xml,xml_ptr,xml_len+1);
|
|
||||||
room_info.xml.ptr = xml;
|
|
||||||
}
|
|
||||||
room_info.xml.length = xml_len;
|
|
||||||
room_info.xml.max_length = xml_max_len;
|
|
||||||
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
LEA ECX,chat_room_info
|
|
||||||
CALL free_chat_room_info_addr
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DelMemberFromChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len) {
|
|
||||||
int success = 0;
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
vector<WeChatString> members;
|
|
||||||
VectorInner *list = (VectorInner *)&members;
|
|
||||||
DWORD members_ptr = (DWORD)&list->start;
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
WeChatString pwxid(wxids[i]);
|
|
||||||
members.push_back(pwxid);
|
|
||||||
}
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD del_member_addr = base + WX_DEL_CHAT_ROOM_MEMBER_OFFSET;
|
|
||||||
DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET;
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ESI,EAX
|
|
||||||
MOV ECX,ESP
|
|
||||||
LEA EDI,chat_room
|
|
||||||
PUSH EDI
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
MOV ECX,ESI
|
|
||||||
MOV EAX,dword ptr[members_ptr]
|
|
||||||
PUSH EAX
|
|
||||||
CALL del_member_addr
|
|
||||||
MOV success,EAX
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len){
|
|
||||||
int success = 0;
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
vector<WeChatString> members;
|
|
||||||
VectorInner *list = (VectorInner *)&members;
|
|
||||||
DWORD members_ptr = (DWORD)&list->start;
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
WeChatString pwxid(wxids[i]);
|
|
||||||
members.push_back(pwxid);
|
|
||||||
}
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD add_member_addr = base + WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET;
|
|
||||||
DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET;
|
|
||||||
DWORD temp=0;
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
SUB ESP,0x8
|
|
||||||
MOV temp,EAX
|
|
||||||
MOV ECX,ESP
|
|
||||||
MOV dword ptr [ECX],0x0
|
|
||||||
MOV dword ptr [ECX + 4],0x0
|
|
||||||
TEST ESI,ESI
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ECX,ESP
|
|
||||||
LEA EAX,chat_room
|
|
||||||
PUSH EAX
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
MOV ECX,temp
|
|
||||||
MOV EAX,dword ptr[members_ptr]
|
|
||||||
PUSH EAX
|
|
||||||
CALL add_member_addr
|
|
||||||
MOV success,EAX
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out){
|
|
||||||
int success = 0;
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
DWORD chat_room_ptr = (DWORD) &chat_room;
|
|
||||||
char buffer[0x1D4] = {0};
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_member_addr = base + WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET;
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD create_chat_room_addr = base + WX_INIT_CHAT_ROOM_OFFSET;
|
|
||||||
DWORD free_chat_room_addr = base + WX_FREE_CHAT_ROOM_OFFSET;
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
LEA ECX,buffer
|
|
||||||
CALL create_chat_room_addr
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
LEA EAX, buffer
|
|
||||||
PUSH EAX
|
|
||||||
PUSH chat_room_ptr
|
|
||||||
CALL get_member_addr
|
|
||||||
MOVZX EAX,AL
|
|
||||||
MOV success,EAX
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
char* members = *(char **)(buffer +0x1c);
|
|
||||||
wchar_t* room = *(wchar_t **)(buffer +0x8);
|
|
||||||
wchar_t* admin = *(wchar_t **)(buffer +0x4c);
|
|
||||||
|
|
||||||
out.members = new char[strlen(members) + 1];
|
|
||||||
memcpy(out.members, members, strlen(members) + 1);
|
|
||||||
|
|
||||||
out.chat_room = new wchar_t[wcslen(room)+1];
|
|
||||||
wmemcpy(out.chat_room ,room,wcslen(room)+1);
|
|
||||||
|
|
||||||
out.admin = new wchar_t[wcslen(admin)+1];
|
|
||||||
wmemcpy(out.admin ,admin,wcslen(admin)+1);
|
|
||||||
|
|
||||||
__asm{
|
|
||||||
LEA ECX,buffer
|
|
||||||
CALL free_chat_room_addr
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick){
|
|
||||||
int success = 0;
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
WeChatString self_wxid(wxid);
|
|
||||||
WeChatString new_nick(nick);
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD mod_member_nick_name_addr = base + WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET;
|
|
||||||
DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET;
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ECX,ESP
|
|
||||||
LEA EDI,new_nick
|
|
||||||
PUSH EDI
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
SUB ESP,0x14
|
|
||||||
LEA EAX,self_wxid
|
|
||||||
MOV ECX,ESP
|
|
||||||
PUSH EAX
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
SUB ESP,0x14
|
|
||||||
LEA EAX,chat_room
|
|
||||||
MOV ECX,ESP
|
|
||||||
PUSH EAX
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
CALL mod_member_nick_name_addr
|
|
||||||
MOVZX EAX,AL
|
|
||||||
MOV success,EAX
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SetTopMsg(wchar_t* wxid,ULONG64 msg_id){
|
|
||||||
int success = -1;
|
|
||||||
char chat_msg[0x2C4] ={0};
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET;
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD handle_top_msg_addr = base + WX_TOP_MSG_OFFSET;
|
|
||||||
// DWORD free_addr = base + WX_FREE_CHAT_MSG_OFFSET;
|
|
||||||
DWORD free_addr = base + WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET;
|
|
||||||
vector<string> local_msg = GetChatMsgByMsgId(msg_id);
|
|
||||||
if(local_msg.empty()){
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
string type = local_msg[3];
|
|
||||||
string status = local_msg[10];
|
|
||||||
string talker = local_msg[13];
|
|
||||||
string content = local_msg[14];
|
|
||||||
wstring w_talker = String2Wstring(talker);
|
|
||||||
wstring w_content = String2Wstring(content);
|
|
||||||
int msg_type =stoi(type);
|
|
||||||
int msg_status =stoi(status);
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
wcout << "w_talker:" <<w_talker <<endl;
|
|
||||||
wcout << "w_content:" <<w_content <<endl;
|
|
||||||
#endif
|
|
||||||
WeChatString chat_room(w_talker);
|
|
||||||
WeChatString msg_content(w_content);
|
|
||||||
|
|
||||||
WeChatString user_id(wxid);
|
|
||||||
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
LEA ECX,chat_msg
|
|
||||||
CALL new_chat_msg_addr
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&chat_msg[0x30],&msg_id,sizeof(msg_id));
|
|
||||||
memcpy(&chat_msg[0x38],&msg_type,sizeof(msg_type));
|
|
||||||
memcpy(&chat_msg[0x40],&msg_status,sizeof(msg_status));
|
|
||||||
memcpy(&chat_msg[0x48],&chat_room,sizeof(chat_room));
|
|
||||||
|
|
||||||
memcpy(&chat_msg[0x70],&msg_content,sizeof(msg_content));
|
|
||||||
memcpy(&chat_msg[0x174],&user_id,sizeof(user_id));
|
|
||||||
|
|
||||||
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
PUSH 0x0
|
|
||||||
LEA EAX,chat_msg
|
|
||||||
PUSH EAX
|
|
||||||
CALL handle_top_msg_addr
|
|
||||||
MOV success,EAX
|
|
||||||
LEA ECX,chat_msg
|
|
||||||
PUSH 0x0
|
|
||||||
CALL free_addr
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int RemoveTopMsg(wchar_t* chat_room_id,ULONG64 msg_id){
|
|
||||||
|
|
||||||
int success = -1;
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET;
|
|
||||||
DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET;
|
|
||||||
DWORD remove_top_msg_addr = base + WX_REMOVE_TOP_MSG_OFFSET;
|
|
||||||
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
MOV EDI,dword ptr [msg_id]
|
|
||||||
LEA EAX,chat_room
|
|
||||||
MOV ESI,dword ptr [msg_id + 0x4]
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ECX,ESP
|
|
||||||
PUSH EAX
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
PUSH ESI
|
|
||||||
PUSH EDI
|
|
||||||
CALL remove_top_msg_addr
|
|
||||||
MOV success,EAX
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::wstring GetChatRoomMemberNickname(wchar_t* chat_room_id,wchar_t* wxid){
|
|
||||||
WeChatString chat_room(chat_room_id);
|
|
||||||
WeChatString member_id(wxid);
|
|
||||||
WeChatString nickname(NULL);
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET;
|
|
||||||
DWORD get_nickname_addr = base + WX_GET_MEMBER_NICKNAME_OFFSET;
|
|
||||||
DWORD contact_mgr_addr = base + WX_CONTACT_MGR_INSTANCE_OFFSET;
|
|
||||||
DWORD get_contact_addr = base + WX_GET_CONTACT_OFFSET;
|
|
||||||
DWORD free_contact_addr = base + WX_FREE_CONTACT_OFFSET;
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL get_chat_room_mgr_addr
|
|
||||||
LEA ECX,nickname
|
|
||||||
PUSH ECX
|
|
||||||
LEA ECX,member_id
|
|
||||||
PUSH ECX
|
|
||||||
LEA ECX,chat_room
|
|
||||||
PUSH ECX
|
|
||||||
MOV ECX,EAX
|
|
||||||
CALL get_nickname_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
wstring name = L"";
|
|
||||||
if (nickname.ptr) {
|
|
||||||
name += wstring(nickname.ptr);
|
|
||||||
}else {
|
|
||||||
char buff[0x440] = {0};
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL contact_mgr_addr
|
|
||||||
LEA ECX,buff
|
|
||||||
PUSH ECX
|
|
||||||
LEA ECX,member_id
|
|
||||||
PUSH ECX
|
|
||||||
MOV ECX,EAX
|
|
||||||
CALL get_contact_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
name += READ_WSTRING(buff, 0x6C);
|
|
||||||
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
LEA ECX,buff
|
|
||||||
CALL free_contact_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef CHAT_ROOM_H_
|
|
||||||
#define CHAT_ROOM_H_
|
|
||||||
#include "wechat_data.h"
|
|
||||||
|
|
||||||
int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info);
|
|
||||||
int DelMemberFromChatRoom(wchar_t* chat_room_id,wchar_t** wxids,int len);
|
|
||||||
int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len);
|
|
||||||
|
|
||||||
int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out);
|
|
||||||
int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick);
|
|
||||||
|
|
||||||
int SetTopMsg(wchar_t* wxid,ULONG64 msg_id);
|
|
||||||
int RemoveTopMsg(wchar_t* chat_room_id,ULONG64 msg_id);
|
|
||||||
|
|
||||||
std::wstring GetChatRoomMemberNickname(wchar_t* chat_room_id,wchar_t* wxid);
|
|
||||||
#endif
|
|
145
src/common.cc
145
src/common.cc
@ -1,145 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/// @brief utf8 转换成unicode
|
|
||||||
/// @param buffer utf8
|
|
||||||
/// @return unicode
|
|
||||||
wstring utf8_to_unicode(const char *buffer) {
|
|
||||||
int c_size = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, NULL, 0);
|
|
||||||
if (c_size > 0) {
|
|
||||||
wchar_t *temp = new wchar_t[c_size + 1];
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, buffer, -1, temp, c_size);
|
|
||||||
temp[c_size] = L'\0';
|
|
||||||
wstring ret(temp);
|
|
||||||
delete[] temp;
|
|
||||||
temp = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return wstring();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief unicode转换utf8
|
|
||||||
/// @param wstr unicode
|
|
||||||
/// @return string utf8
|
|
||||||
string unicode_to_utf8(wchar_t *wstr) {
|
|
||||||
int c_size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, FALSE);
|
|
||||||
if (c_size > 0) {
|
|
||||||
char *buffer = new char[c_size + 1];
|
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, c_size, NULL, FALSE);
|
|
||||||
buffer[c_size] = '\0';
|
|
||||||
string str(buffer);
|
|
||||||
delete[] buffer;
|
|
||||||
buffer = NULL;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief 获取WeChatWin.dll基址
|
|
||||||
/// @return 基址
|
|
||||||
DWORD GetWeChatWinBase() { return (DWORD)GetModuleHandleA("WeChatWin.dll"); }
|
|
||||||
|
|
||||||
/// @brief 创建窗口
|
|
||||||
/// @param void
|
|
||||||
/// @return 创建结果
|
|
||||||
BOOL CreateConsole(void) {
|
|
||||||
if (AllocConsole()) {
|
|
||||||
AttachConsole(GetCurrentProcessId());
|
|
||||||
FILE *retStream;
|
|
||||||
freopen_s(&retStream, "CONOUT$", "w", stdout);
|
|
||||||
if (!retStream) throw std::runtime_error("Stdout redirection failed.");
|
|
||||||
freopen_s(&retStream, "CONOUT$", "w", stderr);
|
|
||||||
if (!retStream) throw std::runtime_error("Stderr redirection failed.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief hook any addr
|
|
||||||
/// @param hook_addr need hook of addr
|
|
||||||
/// @param jmp_addr hook function addr
|
|
||||||
/// @param origin origin code
|
|
||||||
void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin) {
|
|
||||||
BYTE jmp_code[5] = {0};
|
|
||||||
jmp_code[0] = 0xE9;
|
|
||||||
*(DWORD *)&jmp_code[1] = (DWORD)jmp_addr - hook_addr - 5;
|
|
||||||
DWORD old_protext = 0;
|
|
||||||
VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext);
|
|
||||||
ReadProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0);
|
|
||||||
memcpy((void *)hook_addr, jmp_code, 5);
|
|
||||||
VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief unhook
|
|
||||||
/// @param hook_addr hook addr
|
|
||||||
/// @param origin origin addr code
|
|
||||||
void UnHookAnyAddress(DWORD hook_addr, char *origin) {
|
|
||||||
DWORD old_protext = 0;
|
|
||||||
VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext);
|
|
||||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0);
|
|
||||||
VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief get timeW
|
|
||||||
/// @param timestamp timestamp
|
|
||||||
/// @return str
|
|
||||||
wstring GetTimeW(long long timestamp) {
|
|
||||||
wchar_t *wstr = new wchar_t[20];
|
|
||||||
memset(wstr, 0, 20 * 2);
|
|
||||||
tm tm_out;
|
|
||||||
localtime_s(&tm_out, ×tamp);
|
|
||||||
swprintf_s(wstr, 20, L"%04d-%02d-%02d %02d:%02d:%02d", 1900 + tm_out.tm_year,
|
|
||||||
tm_out.tm_mon + 1, tm_out.tm_mday, tm_out.tm_hour, tm_out.tm_min,
|
|
||||||
tm_out.tm_sec);
|
|
||||||
wstring strTimeW(wstr);
|
|
||||||
delete[] wstr;
|
|
||||||
return strTimeW;
|
|
||||||
}
|
|
||||||
|
|
||||||
wstring String2Wstring(string str) {
|
|
||||||
wstring result;
|
|
||||||
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
|
|
||||||
wchar_t *buffer = new wchar_t[len + 1];
|
|
||||||
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
|
|
||||||
buffer[len] = '\0';
|
|
||||||
result.append(buffer);
|
|
||||||
delete[] buffer;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Wstring2String(wstring wstr) {
|
|
||||||
string result;
|
|
||||||
int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0,
|
|
||||||
NULL, NULL);
|
|
||||||
char *buffer = new char[len + 1];
|
|
||||||
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL,
|
|
||||||
NULL);
|
|
||||||
buffer[len] = '\0';
|
|
||||||
result.append(buffer);
|
|
||||||
delete[] buffer;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL FindOrCreateDirectoryW(const wchar_t *path) {
|
|
||||||
WIN32_FIND_DATAW fd;
|
|
||||||
HANDLE hFind = ::FindFirstFileW(path, &fd);
|
|
||||||
if (hFind != INVALID_HANDLE_VALUE && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
||||||
FindClose(hFind);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!::CreateDirectoryW(path, NULL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloseConsole(){
|
|
||||||
fclose(stdin);
|
|
||||||
fclose(stdout);
|
|
||||||
fclose(stderr);
|
|
||||||
FreeConsole();
|
|
||||||
}
|
|
83
src/common.h
83
src/common.h
@ -1,83 +0,0 @@
|
|||||||
#ifndef COMMON_H_
|
|
||||||
#define COMMON_H_
|
|
||||||
#include <string>
|
|
||||||
#define READ_WSTRING(addr, offset) ((*(DWORD *)(addr + offset + 0x4) == 0) ? std::wstring(L"") : std::wstring((wchar_t *)(*(DWORD *)(addr + offset)), *(DWORD *)(addr + offset + 0x4)))
|
|
||||||
|
|
||||||
/// @brief utf8 转换成unicode
|
|
||||||
/// @param buffer utf8
|
|
||||||
/// @return unicode
|
|
||||||
std::wstring utf8_to_unicode(const char *buffer);
|
|
||||||
|
|
||||||
/// @brief unicode转换utf8
|
|
||||||
/// @param wstr unicode
|
|
||||||
/// @return utf8
|
|
||||||
std::string unicode_to_utf8(wchar_t *wstr);
|
|
||||||
|
|
||||||
/// @brief 获取WeChatWin.dll基址
|
|
||||||
/// @return 基址
|
|
||||||
DWORD GetWeChatWinBase();
|
|
||||||
/// @brief 创建窗口
|
|
||||||
/// @param void
|
|
||||||
/// @return 创建结果
|
|
||||||
BOOL CreateConsole(void);
|
|
||||||
/// @brief hook任意地址
|
|
||||||
/// @param hook_addr 被hook的地址
|
|
||||||
/// @param jmp_addr 被hook的函数的地址
|
|
||||||
/// @param origin 原始code
|
|
||||||
void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin);
|
|
||||||
|
|
||||||
/// @brief 取消hook
|
|
||||||
/// @param hook_addr 被hook的地址
|
|
||||||
/// @param origin 原始code
|
|
||||||
void UnHookAnyAddress(DWORD hook_addr, char *origin);
|
|
||||||
|
|
||||||
/// @brief get timeW
|
|
||||||
/// @param timestamp timestamp
|
|
||||||
/// @return str
|
|
||||||
std::wstring GetTimeW(long long timestamp);
|
|
||||||
/// @brief unicode trans utf8
|
|
||||||
/// @param str unicode str
|
|
||||||
/// @return utf8 str
|
|
||||||
std::string UnicodeToUtf8(const wchar_t *str);
|
|
||||||
/// @brief string convert wstring
|
|
||||||
/// @param str
|
|
||||||
/// @return
|
|
||||||
std::wstring String2Wstring(std::string str);
|
|
||||||
/// @brief wstring convert string
|
|
||||||
/// @param str
|
|
||||||
/// @return
|
|
||||||
std::string Wstring2String(std::wstring wstr);
|
|
||||||
|
|
||||||
/// @brief create dir
|
|
||||||
/// @param path
|
|
||||||
/// @return
|
|
||||||
BOOL FindOrCreateDirectoryW(const wchar_t *path);
|
|
||||||
|
|
||||||
void CloseConsole();
|
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
|
||||||
std::vector<T1> split(T1 str, T2 letter) {
|
|
||||||
vector<T1> arr;
|
|
||||||
size_t pos;
|
|
||||||
while ((pos = str.find_first_of(letter)) != T1::npos) {
|
|
||||||
T1 str1 = str.substr(0, pos);
|
|
||||||
arr.push_back(str1);
|
|
||||||
str = str.substr(pos + 1, str.length() - pos - 1);
|
|
||||||
}
|
|
||||||
arr.push_back(str);
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
|
||||||
T1 replace(T1 source, T2 replaced, T1 replaceto) {
|
|
||||||
vector<T1> v_arr = split(source, replaced);
|
|
||||||
if (v_arr.size() < 2) return source;
|
|
||||||
T1 temp;
|
|
||||||
for (unsigned int i = 0; i < v_arr.size() - 1; i++) {
|
|
||||||
temp += v_arr[i];
|
|
||||||
temp += replaceto;
|
|
||||||
}
|
|
||||||
temp += v_arr[v_arr.size() - 1];
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
#endif
|
|
14
src/config.cc
Normal file
14
src/config.cc
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace wxhelper {
|
||||||
|
Config::Config(/* args */) {}
|
||||||
|
|
||||||
|
Config::~Config() {}
|
||||||
|
|
||||||
|
void Config::Initialize() {
|
||||||
|
port_ = GetPrivateProfileInt("config", "Port", 19088, "./config.ini");
|
||||||
|
}
|
||||||
|
int Config::GetPort() { return port_; }
|
||||||
|
|
||||||
|
} // namespace wxhelper
|
17
src/config.h
Normal file
17
src/config.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef WXHELPER_CONFIG_H_
|
||||||
|
#define WXHELPER_CONFIG_H_
|
||||||
|
|
||||||
|
namespace wxhelper {
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
public:
|
||||||
|
Config();
|
||||||
|
~Config();
|
||||||
|
void Initialize();
|
||||||
|
int GetPort();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int port_;
|
||||||
|
};
|
||||||
|
} // namespace wxhelper
|
||||||
|
#endif
|
@ -1,49 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "confirm_receipt.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "wechat_data.h"
|
|
||||||
|
|
||||||
#define WX_NEW_WCPAYINFO_OFFSET 0x756340
|
|
||||||
#define WX_FREE_WCPAYINFO_OFFSET 0x73c170
|
|
||||||
#define WX_CONFIRM_RECEIPT_OFFSET 0x15287a0
|
|
||||||
|
|
||||||
int DoConfirmReceipt(wchar_t *wxid, wchar_t *transcationid,
|
|
||||||
wchar_t *transferid) {
|
|
||||||
int success = -1;
|
|
||||||
WeChatString recv_id(wxid);
|
|
||||||
WeChatString transcation_id(transcationid);
|
|
||||||
WeChatString transfer_id(transferid);
|
|
||||||
char pay_info[0x134] = {0};
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD new_pay_info_addr = base + WX_NEW_WCPAYINFO_OFFSET;
|
|
||||||
DWORD free_pay_info_addr = base + WX_FREE_WCPAYINFO_OFFSET;
|
|
||||||
DWORD do_confirm_addr = base + WX_CONFIRM_RECEIPT_OFFSET;
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
LEA ECX,pay_info
|
|
||||||
CALL new_pay_info_addr
|
|
||||||
MOV dword ptr [pay_info + 0x4], 0x1
|
|
||||||
MOV dword ptr [pay_info + 0x4C], 0x1
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
memcpy(&pay_info[0x1c], &transcation_id, sizeof(transcation_id));
|
|
||||||
memcpy(&pay_info[0x38], &transfer_id, sizeof(transfer_id));
|
|
||||||
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSH 0x1
|
|
||||||
SUB ESP,0x8
|
|
||||||
LEA EDX,recv_id
|
|
||||||
LEA ECX,pay_info
|
|
||||||
CALL do_confirm_addr
|
|
||||||
MOV success,EAX
|
|
||||||
ADD ESP,0xC
|
|
||||||
PUSH 0x0
|
|
||||||
LEA ECX,pay_info
|
|
||||||
CALL free_pay_info_addr
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#ifndef CONFIRM_RECEIPT_H
|
|
||||||
#define CONFIRM_RECEIPT_H
|
|
||||||
|
|
||||||
int DoConfirmReceipt(wchar_t* wxid,wchar_t *transcationid, wchar_t *transferid);
|
|
||||||
|
|
||||||
#endif
|
|
192
src/contact.cc
192
src/contact.cc
@ -1,192 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "contact.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "wechat_data.h"
|
|
||||||
using namespace std;
|
|
||||||
#define WX_CONTACT_MGR_INSTANCE_OFFSET 0x6f8990
|
|
||||||
#define WX_CONTACT_GET_LIST_OFFSET 0xb97550
|
|
||||||
#define WX_CONTACT_DEL_OFFSET 0xb9b3b0
|
|
||||||
#define WX_INIT_CHAT_MSG_OFFSET 0xed3be0
|
|
||||||
#define WX_SYNC_MGR_OFFSET 0xa87fd0
|
|
||||||
#define WX_SET_VALUE_OFFSET 0x1f80900
|
|
||||||
#define WX_DO_DEL_CONTACT_OFFSET 0xca6480
|
|
||||||
#define WX_FREE_CONTACT_OFFSET 0xe23690
|
|
||||||
#define WX_GET_CONTACT_OFFSET 0xb93b20
|
|
||||||
#define WX_DO_VERIFY_USER_OFFSET 0xB91160
|
|
||||||
|
|
||||||
int GetAllContact(vector<Contact> &vec) {
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD get_instance = base + WX_CONTACT_MGR_INSTANCE_OFFSET;
|
|
||||||
DWORD contact_get_list = base + WX_CONTACT_GET_LIST_OFFSET;
|
|
||||||
// char contact[0xc] = {0};
|
|
||||||
DWORD* contact[3] = {0, 0, 0};
|
|
||||||
int success = 0;
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
CALL get_instance
|
|
||||||
LEA ECX,contact
|
|
||||||
PUSH ECX
|
|
||||||
MOV ECX,EAX
|
|
||||||
CALL contact_get_list
|
|
||||||
MOVZX EAX,AL
|
|
||||||
MOV success,EAX
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
DWORD start = (DWORD)contact[0];
|
|
||||||
DWORD end = (DWORD)contact[2];
|
|
||||||
#ifdef _DEBUG
|
|
||||||
cout << "address = " << &contact << endl;
|
|
||||||
cout << "refresh contact = " << success << endl;
|
|
||||||
cout << "start = " << start << endl;
|
|
||||||
cout << "end = " << end << endl;
|
|
||||||
#endif
|
|
||||||
while (start < end) {
|
|
||||||
Contact temp{0};
|
|
||||||
|
|
||||||
temp.wxid.ptr = *(wchar_t **)(start + 0x10);
|
|
||||||
temp.wxid.length = *(DWORD *)(start + 0x14);
|
|
||||||
temp.wxid.max_length = *(DWORD *)(start + 0x18);
|
|
||||||
|
|
||||||
temp.custom_account.ptr = *(wchar_t **)(start + 0x24);
|
|
||||||
temp.custom_account.length = *(DWORD *)(start + 0x28);
|
|
||||||
temp.custom_account.max_length = *(DWORD *)(start + 0x2C);
|
|
||||||
|
|
||||||
temp.encrypt_name.ptr = *(wchar_t **)(start + 0x6c);
|
|
||||||
temp.encrypt_name.length = *(DWORD *)(start + 0x70);
|
|
||||||
temp.encrypt_name.max_length = *(DWORD *)(start + 0x74);
|
|
||||||
|
|
||||||
temp.pinyin.ptr = *(wchar_t **)(start + 0xAC);
|
|
||||||
temp.pinyin.length = *(DWORD *)(start + 0xB0);
|
|
||||||
temp.pinyin.max_length = *(DWORD *)(start + 0xB4);
|
|
||||||
|
|
||||||
temp.pinyin_all.ptr = *(wchar_t **)(start + 0xC0);
|
|
||||||
temp.pinyin_all.length = *(DWORD *)(start + 0xC4);
|
|
||||||
temp.pinyin_all.max_length = *(DWORD *)(start + 0xC8);
|
|
||||||
|
|
||||||
temp.del_flag = *(DWORD *)(start + 0x4c);
|
|
||||||
temp.type = *(DWORD *)(start + 0x50);
|
|
||||||
temp.verify_flag = *(DWORD *)(start + 0x54);
|
|
||||||
vec.push_back(temp);
|
|
||||||
start += 0x438;
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
// note maybe not delete
|
|
||||||
int DelContact(wchar_t *wxid) {
|
|
||||||
int success = -1;
|
|
||||||
WeChatString user_id(wxid);
|
|
||||||
DWORD id_ptr = (DWORD) &user_id;
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD sync_mgr_addr = base + WX_SYNC_MGR_OFFSET;
|
|
||||||
DWORD set_id_addr = base + WX_SET_VALUE_OFFSET;
|
|
||||||
DWORD del_contact_addr = base + WX_DO_DEL_CONTACT_OFFSET;
|
|
||||||
int len = user_id.length;
|
|
||||||
|
|
||||||
string id_cstr = unicode_to_utf8(wxid);
|
|
||||||
char id_[0x20]={0};
|
|
||||||
memcpy(id_,id_cstr.c_str(),id_cstr.size()+1);
|
|
||||||
char buff[0x10]={0};
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL sync_mgr_addr
|
|
||||||
MOV ECX,EAX
|
|
||||||
LEA EAX,buff
|
|
||||||
MOV [ECX + 4],EAX
|
|
||||||
LEA EAX,id_
|
|
||||||
Mov dword ptr[buff +0x4],EAX
|
|
||||||
CALL del_contact_addr
|
|
||||||
MOV success,EAX
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetContactOrChatRoomNickname(wchar_t *id) {
|
|
||||||
int success = -1;
|
|
||||||
char buff[0x440] = {0};
|
|
||||||
WeChatString pri(id);
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD contact_mgr_addr = base + WX_CONTACT_MGR_INSTANCE_OFFSET;
|
|
||||||
DWORD get_contact_addr = base + WX_GET_CONTACT_OFFSET;
|
|
||||||
DWORD free_contact_addr = base + WX_FREE_CONTACT_OFFSET;
|
|
||||||
wstring name = L"";
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL contact_mgr_addr
|
|
||||||
LEA ECX,buff
|
|
||||||
PUSH ECX
|
|
||||||
LEA ECX,pri
|
|
||||||
PUSH ECX
|
|
||||||
MOV ECX,EAX
|
|
||||||
CALL get_contact_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
name += READ_WSTRING(buff, 0x6C);
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
LEA ECX,buff
|
|
||||||
CALL free_contact_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int AddFriendByWxid(wchar_t *wxid){
|
|
||||||
int success = -1;
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD contact_mgr_addr = base + WX_CONTACT_MGR_INSTANCE_OFFSET;
|
|
||||||
DWORD set_group_addr = base + 0x746E20;
|
|
||||||
DWORD fn2_addr = base + 0x285D968;
|
|
||||||
DWORD fn3_addr = base + 0x6F6050;
|
|
||||||
DWORD fn4_addr = base + 0xED3BE0;
|
|
||||||
DWORD do_verify_user_addr = base + WX_DO_VERIFY_USER_OFFSET;
|
|
||||||
|
|
||||||
DWORD instance =0;
|
|
||||||
WeChatString chat_room(NULL);
|
|
||||||
WeChatString user_id(wxid);
|
|
||||||
__asm{
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL contact_mgr_addr
|
|
||||||
SUB ESP,0x18
|
|
||||||
MOV dword ptr [instance],EAX
|
|
||||||
MOV ECX,ESP
|
|
||||||
PUSH ECX
|
|
||||||
LEA ECX,chat_room
|
|
||||||
CALL set_group_addr
|
|
||||||
MOV EAX,fn2_addr
|
|
||||||
SUB ESP,0x18
|
|
||||||
MOV ECX,ESP
|
|
||||||
PUSH EAX
|
|
||||||
CALL fn3_addr
|
|
||||||
PUSH 0x0
|
|
||||||
PUSH 0XE
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ESI,ESP
|
|
||||||
MOV dword ptr [ESI],0x0
|
|
||||||
MOV dword ptr [ESI+0x4],0x0
|
|
||||||
MOV dword ptr [ESI+0x8],0x0
|
|
||||||
MOV dword ptr [ESI+0xC],0x0
|
|
||||||
MOV dword ptr [ESI+0x10],0x0
|
|
||||||
PUSH 0x1
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ECX,ESP
|
|
||||||
LEA EAX,user_id
|
|
||||||
PUSH EAX
|
|
||||||
CALL fn4_addr
|
|
||||||
MOV ECX,dword ptr [instance]
|
|
||||||
CALL do_verify_user_addr
|
|
||||||
MOV success,EAX
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
#ifndef CONTACT_H_
|
|
||||||
#define CONTACT_H_
|
|
||||||
#include <vector>
|
|
||||||
#include "wechat_data.h"
|
|
||||||
|
|
||||||
int GetAllContact(std::vector<Contact> &vec);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int DelContact(wchar_t* wxid);
|
|
||||||
|
|
||||||
std::wstring GetContactOrChatRoomNickname(wchar_t* id);
|
|
||||||
|
|
||||||
int AddFriendByWxid(wchar_t *wxid);
|
|
||||||
#endif
|
|
511
src/db.cc
Normal file
511
src/db.cc
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "db.h"
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include "wechat_function.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace offset = wxhelper::V3_9_5_81::offset;
|
||||||
|
|
||||||
|
|
||||||
|
namespace wxhelper {
|
||||||
|
|
||||||
|
void DB::init(UINT64 base) {
|
||||||
|
base_addr_ = base;
|
||||||
|
dbmap_ = {};
|
||||||
|
dbs_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeResult(std::vector<std::vector<common::SqlResult>> &data) {
|
||||||
|
if (data.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < data.size(); i++) {
|
||||||
|
for (unsigned j = 0; j < data[i].size(); j++) {
|
||||||
|
common::SqlResult *sr = (common::SqlResult *)&data[i][j];
|
||||||
|
if (sr->column_name) {
|
||||||
|
delete[] sr->column_name;
|
||||||
|
sr->column_name = NULL;
|
||||||
|
}
|
||||||
|
if (sr->content) {
|
||||||
|
delete[] sr->content;
|
||||||
|
sr->content = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data[i].clear();
|
||||||
|
}
|
||||||
|
data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DB::SelectDataInner(UINT64 db, const char *sql,
|
||||||
|
std::vector<std::vector<common::SqlResult>> &data) {
|
||||||
|
common::sqlite3_prepare p_sqlite3_prepare =
|
||||||
|
(common::sqlite3_prepare)(base_addr_ + offset::k_sqlite3_prepare);
|
||||||
|
common::sqlite3_step p_sqlite3_step =
|
||||||
|
(common::sqlite3_step)(base_addr_ + offset::k_sqlite3_step);
|
||||||
|
common::sqlite3_column_count p_sqlite3_column_count =
|
||||||
|
(common::sqlite3_column_count)(base_addr_ + offset::k_sqlite3_column_count);
|
||||||
|
common::sqlite3_column_name p_sqlite3_column_name =
|
||||||
|
(common::sqlite3_column_name)(base_addr_ + offset::k_sqlite3_column_name);
|
||||||
|
common::sqlite3_column_type p_sqlite3_column_type =
|
||||||
|
(common::sqlite3_column_type)(base_addr_ + offset::k_sqlite3_column_type);
|
||||||
|
common::sqlite3_column_blob p_sqlite3_column_blob =
|
||||||
|
(common::sqlite3_column_blob)(base_addr_ + offset::k_sqlite3_column_blob);
|
||||||
|
common::sqlite3_column_bytes p_sqlite3_column_bytes =
|
||||||
|
(common::sqlite3_column_bytes)(base_addr_ + offset::k_sqlite3_column_bytes);
|
||||||
|
common::sqlite3_finalize p_sqlite3_finalize =
|
||||||
|
(common::sqlite3_finalize)(base_addr_ + offset::k_sqlite3_finalize);
|
||||||
|
UINT64 *stmt;
|
||||||
|
int rc = p_sqlite3_prepare(db, sql, -1, &stmt, 0);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while (p_sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
int col_count = p_sqlite3_column_count(stmt);
|
||||||
|
std::vector<common::SqlResult> tempStruct;
|
||||||
|
for (int i = 0; i < col_count; i++) {
|
||||||
|
common::SqlResult temp = {0};
|
||||||
|
const char *ColName = p_sqlite3_column_name(stmt, i);
|
||||||
|
int nType = p_sqlite3_column_type(stmt, i);
|
||||||
|
const void *pReadBlobData = p_sqlite3_column_blob(stmt, i);
|
||||||
|
int nLength = p_sqlite3_column_bytes(stmt, i);
|
||||||
|
temp.column_name = new char[strlen(ColName) + 1];
|
||||||
|
memcpy(temp.column_name, ColName, strlen(ColName) + 1);
|
||||||
|
temp.column_name_len = strlen(ColName);
|
||||||
|
temp.content_len = nLength;
|
||||||
|
switch (nType) {
|
||||||
|
case SQLITE_BLOB: {
|
||||||
|
temp.content = new char[nLength];
|
||||||
|
memcpy(temp.content, pReadBlobData, nLength);
|
||||||
|
temp.is_blob = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (nLength != 0) {
|
||||||
|
temp.content = new char[nLength + 1];
|
||||||
|
memcpy(temp.content, pReadBlobData, nLength + 1);
|
||||||
|
} else {
|
||||||
|
temp.content = new char[2];
|
||||||
|
ZeroMemory(temp.content, 2);
|
||||||
|
}
|
||||||
|
temp.is_blob = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempStruct.push_back(temp);
|
||||||
|
}
|
||||||
|
data.push_back(tempStruct);
|
||||||
|
}
|
||||||
|
p_sqlite3_finalize(stmt);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DB::Select(UINT64 db_hanle, const char *sql,
|
||||||
|
std::vector<std::vector<std::string>> &query_result) {
|
||||||
|
std::vector<std::vector<common::SqlResult>> data;
|
||||||
|
int status = SelectDataInner(db_hanle, sql, data);
|
||||||
|
if (status == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (data.size() == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::vector<std::string> index;
|
||||||
|
for (size_t i = 0; i < data[0].size(); i++) {
|
||||||
|
index.push_back(data[0][i].column_name);
|
||||||
|
}
|
||||||
|
query_result.push_back(index);
|
||||||
|
|
||||||
|
for (auto it : data) {
|
||||||
|
std::vector<std::string> item;
|
||||||
|
for (size_t i = 0; i < it.size(); i++) {
|
||||||
|
if (!it[i].is_blob) {
|
||||||
|
bool is_utf8 = Utils::IsTextUtf8(it[i].content, it[i].content_len);
|
||||||
|
if (is_utf8) {
|
||||||
|
std::string content(it[i].content);
|
||||||
|
item.push_back(content);
|
||||||
|
} else {
|
||||||
|
std::string base64_str =
|
||||||
|
base64_encode((BYTE *)it[i].content, it[i].content_len);
|
||||||
|
item.push_back(base64_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
std::string b64_str =
|
||||||
|
base64_encode((BYTE *)it[i].content, it[i].content_len);
|
||||||
|
item.push_back(b64_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query_result.push_back(item);
|
||||||
|
}
|
||||||
|
FreeResult(data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SelectDbInfo(void *data, int argc, char **argv, char **name) {
|
||||||
|
std::vector<common::SqlResult> result;
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
common::SqlResult temp = {0};
|
||||||
|
temp.column_name = new char[strlen(name[i]) + 1];
|
||||||
|
memcpy(temp.column_name, name[i], strlen(name[i]) + 1);
|
||||||
|
temp.column_name_len = strlen(name[i]);
|
||||||
|
if (argv[i]) {
|
||||||
|
temp.content = new char[strlen(argv[i]) + 1];
|
||||||
|
memcpy(temp.content, argv[i], strlen(argv[i]) + 1);
|
||||||
|
temp.content_len = strlen(argv[i]);
|
||||||
|
} else {
|
||||||
|
temp.content = new char[2];
|
||||||
|
ZeroMemory(temp.content, 2);
|
||||||
|
temp.content_len = 0;
|
||||||
|
}
|
||||||
|
result.push_back(temp);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DB::ExecuteSQL(UINT64 db, const char *sql, UINT64 callback, void *data) {
|
||||||
|
UINT64 sqlite3_exec_addr = base_addr_ + offset::k_sqlite3_exec;
|
||||||
|
common::sqlite3_exec fn_sqlite3_exec = (common::sqlite3_exec)sqlite3_exec_addr;
|
||||||
|
int status = fn_sqlite3_exec(db, sql, (common::sqlite3_callback)callback, data, 0);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetDbInfo(void *data, int argc, char **argv, char **name) {
|
||||||
|
common::DatabaseInfo *pdata = (common::DatabaseInfo *)data;
|
||||||
|
common::TableInfo tb = {0};
|
||||||
|
if (argv[1]) {
|
||||||
|
tb.name = new char[strlen(argv[1]) + 1];
|
||||||
|
memcpy(tb.name, argv[1], strlen(argv[1]) + 1);
|
||||||
|
} else {
|
||||||
|
tb.name = (char *)"NULL";
|
||||||
|
}
|
||||||
|
if (argv[2]) {
|
||||||
|
tb.table_name = new char[strlen(argv[2]) + 1];
|
||||||
|
memcpy(tb.table_name, argv[2], strlen(argv[2]) + 1);
|
||||||
|
} else {
|
||||||
|
tb.table_name = (char *)"NULL";
|
||||||
|
}
|
||||||
|
if (argv[3]) {
|
||||||
|
tb.rootpage = new char[strlen(argv[3]) + 1];
|
||||||
|
memcpy(tb.rootpage, argv[3], strlen(argv[3]) + 1);
|
||||||
|
} else {
|
||||||
|
tb.rootpage = (char *)"NULL";
|
||||||
|
}
|
||||||
|
if (argv[4]) {
|
||||||
|
tb.sql = new char[strlen(argv[4]) + 1];
|
||||||
|
memcpy(tb.sql, argv[4], strlen(argv[4]) + 1);
|
||||||
|
} else {
|
||||||
|
tb.sql = (char *)"NULL";
|
||||||
|
}
|
||||||
|
tb.name_len = strlen(tb.name);
|
||||||
|
tb.table_name_len = strlen(tb.table_name);
|
||||||
|
tb.sql_len = strlen(tb.sql);
|
||||||
|
tb.rootpage_len = strlen(tb.rootpage);
|
||||||
|
pdata->tables.push_back(tb);
|
||||||
|
pdata->count = pdata->tables.size();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<void *> DB::GetDbHandles() {
|
||||||
|
dbs_.clear();
|
||||||
|
dbmap_.clear();
|
||||||
|
UINT64 p_contact_addr = *(UINT64 *)(base_addr_ + offset::kGPInstance);
|
||||||
|
UINT64 micro_msg_db_addr = *(UINT64 *)(p_contact_addr + offset::kMicroMsgDB);
|
||||||
|
UINT64 chat_msg_db_addr = *(UINT64 *)(p_contact_addr + offset::kChatMsgDB);
|
||||||
|
UINT64 misc_db_addr = *(UINT64 *)(p_contact_addr + offset::kMiscDB);
|
||||||
|
UINT64 emotion_db_addr = *(UINT64 *)(p_contact_addr + offset::kEmotionDB);
|
||||||
|
UINT64 media_db_addr = *(UINT64 *)(p_contact_addr + offset::kMediaDB);
|
||||||
|
UINT64 bizchat_msg_db_addr =
|
||||||
|
*(UINT64 *)(p_contact_addr + offset::kBizchatMsgDB);
|
||||||
|
UINT64 function_msg_db_addr =
|
||||||
|
*(UINT64 *)(p_contact_addr + offset::kFunctionMsgDB);
|
||||||
|
|
||||||
|
// microMsg.db
|
||||||
|
common::DatabaseInfo micro_msg_db{0};
|
||||||
|
micro_msg_db.db_name = (wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kMicroMsgDB + offset::kDBName));
|
||||||
|
micro_msg_db.db_name_len =
|
||||||
|
*(DWORD *)(p_contact_addr + offset::kMicroMsgDB + offset::kDBName + 0x8);
|
||||||
|
micro_msg_db.handle = micro_msg_db_addr;
|
||||||
|
ExecuteSQL(micro_msg_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, µ_msg_db);
|
||||||
|
dbs_.push_back(micro_msg_db);
|
||||||
|
std::wstring micro_msg_name = std::wstring((wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kMicroMsgDB + offset::kDBName)));
|
||||||
|
dbmap_[micro_msg_name] = micro_msg_db;
|
||||||
|
|
||||||
|
// chatMsg.db
|
||||||
|
common::DatabaseInfo chat_msg_db{0};
|
||||||
|
chat_msg_db.db_name = (wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kChatMsgDB + offset::kDBName));
|
||||||
|
chat_msg_db.db_name_len =
|
||||||
|
*(DWORD *)(p_contact_addr + offset::kChatMsgDB + offset::kDBName + 0x8);
|
||||||
|
chat_msg_db.handle = chat_msg_db_addr;
|
||||||
|
ExecuteSQL(chat_msg_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &chat_msg_db);
|
||||||
|
dbs_.push_back(chat_msg_db);
|
||||||
|
std::wstring chat_msg_name = std::wstring((wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kChatMsgDB + offset::kDBName)));
|
||||||
|
dbmap_[chat_msg_name] = chat_msg_db;
|
||||||
|
|
||||||
|
// misc.db
|
||||||
|
common::DatabaseInfo misc_db{0};
|
||||||
|
misc_db.db_name =
|
||||||
|
(wchar_t *)(*(UINT64 *)(p_contact_addr + offset::kMiscDB + offset::kDBName));
|
||||||
|
misc_db.db_name_len =
|
||||||
|
*(DWORD *)(p_contact_addr + offset::kMiscDB + offset::kDBName + 0x8);
|
||||||
|
misc_db.handle = misc_db_addr;
|
||||||
|
ExecuteSQL(misc_db_addr, "select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &misc_db);
|
||||||
|
dbs_.push_back(misc_db);
|
||||||
|
std::wstring misc_name = std::wstring((
|
||||||
|
wchar_t *)(*(UINT64 *)(p_contact_addr + offset::kMiscDB + offset::kDBName)));
|
||||||
|
dbmap_[misc_name] = misc_db;
|
||||||
|
|
||||||
|
// emotion.db
|
||||||
|
common::DatabaseInfo emotion_db{0};
|
||||||
|
emotion_db.db_name = (wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kEmotionDB + offset::kDBName));
|
||||||
|
emotion_db.db_name_len =
|
||||||
|
*(DWORD *)(p_contact_addr + offset::kEmotionDB + offset::kDBName + 0x8);
|
||||||
|
emotion_db.handle = emotion_db_addr;
|
||||||
|
ExecuteSQL(emotion_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &emotion_db);
|
||||||
|
dbs_.push_back(emotion_db);
|
||||||
|
std::wstring emotion_name = std::wstring((wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kEmotionDB + offset::kDBName)));
|
||||||
|
dbmap_[emotion_name] = emotion_db;
|
||||||
|
|
||||||
|
// media.db
|
||||||
|
common::DatabaseInfo media_db{0};
|
||||||
|
media_db.db_name = (wchar_t *)(*(UINT64 *)(p_contact_addr + offset::kMediaDB +
|
||||||
|
offset::kDBName));
|
||||||
|
media_db.db_name_len =
|
||||||
|
*(DWORD *)(p_contact_addr + offset::kMediaDB + offset::kDBName + 0x8);
|
||||||
|
media_db.handle = media_db_addr;
|
||||||
|
ExecuteSQL(media_db_addr, "select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &media_db);
|
||||||
|
dbs_.push_back(media_db);
|
||||||
|
std::wstring media_name = std::wstring((wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kMediaDB + offset::kDBName)));
|
||||||
|
dbmap_[media_name] = media_db;
|
||||||
|
|
||||||
|
// functionMsg.db
|
||||||
|
common::DatabaseInfo function_msg_db{0};
|
||||||
|
function_msg_db.db_name = (wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kFunctionMsgDB + offset::kDBName));
|
||||||
|
function_msg_db.db_name_len = *(
|
||||||
|
DWORD *)(p_contact_addr + offset::kFunctionMsgDB + offset::kDBName + 0x8);
|
||||||
|
function_msg_db.handle = function_msg_db_addr;
|
||||||
|
ExecuteSQL(function_msg_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &function_msg_db);
|
||||||
|
dbs_.push_back(function_msg_db);
|
||||||
|
std::wstring function_msg_name = std::wstring((wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kFunctionMsgDB + offset::kDBName)));
|
||||||
|
dbmap_[function_msg_name] = function_msg_db;
|
||||||
|
|
||||||
|
if (bizchat_msg_db_addr) {
|
||||||
|
// functionMsg.db maybe null
|
||||||
|
common::DatabaseInfo bizchat_msg_db{0};
|
||||||
|
bizchat_msg_db.db_name = (wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kFunctionMsgDB + offset::kDBName));
|
||||||
|
bizchat_msg_db.db_name_len =
|
||||||
|
*(DWORD *)(p_contact_addr + offset::kFunctionMsgDB + offset::kDBName +
|
||||||
|
0x8);
|
||||||
|
bizchat_msg_db.handle = bizchat_msg_db_addr;
|
||||||
|
ExecuteSQL(bizchat_msg_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &bizchat_msg_db);
|
||||||
|
dbs_.push_back(bizchat_msg_db);
|
||||||
|
std::wstring bizchat_msg_name = std::wstring((wchar_t *)(*(
|
||||||
|
UINT64 *)(p_contact_addr + offset::kFunctionMsgDB + offset::kDBName)));
|
||||||
|
dbmap_[bizchat_msg_name] = bizchat_msg_db;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 multi_db_mgr_addr = base_addr_ + offset::kMultiDBMgr;
|
||||||
|
UINT64 public_msg_mgr_addr = base_addr_ + offset::kPublicMsgMgr;
|
||||||
|
UINT64 favorite_storage_mgr_addr = base_addr_ + offset::kFavoriteStorageMgr;
|
||||||
|
|
||||||
|
// MsgX.db
|
||||||
|
UINT64 wrap_ptr = *(UINT64 *)(multi_db_mgr_addr);
|
||||||
|
UINT64 current_db_num = *(UINT64 *)(wrap_ptr + 0x68);
|
||||||
|
UINT64 begin_ptr = *(UINT64 *)(wrap_ptr + 0x50);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < current_db_num; i++) {
|
||||||
|
UINT64 next_addr = begin_ptr + i * 0x8;
|
||||||
|
UINT64 db_addr = *(UINT64 *)next_addr;
|
||||||
|
if (db_addr) {
|
||||||
|
UINT64 msg0_db_addr = *(UINT64 *)(db_addr + 0x78);
|
||||||
|
common::DatabaseInfo msg0_db{0};
|
||||||
|
msg0_db.db_name = (wchar_t *)(*(UINT64 *)(db_addr));
|
||||||
|
msg0_db.db_name_len = *(DWORD *)(db_addr + 0x8);
|
||||||
|
msg0_db.handle = msg0_db_addr;
|
||||||
|
msg0_db.extrainfo = *(UINT64 *)(*(UINT64 *)(db_addr + 0x28) + 0x1E8);
|
||||||
|
ExecuteSQL(msg0_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &msg0_db);
|
||||||
|
dbs_.push_back(msg0_db);
|
||||||
|
std::wstring msg_db_name = std::wstring(msg0_db.db_name,msg0_db.db_name_len);
|
||||||
|
dbmap_[msg_db_name] = msg0_db;
|
||||||
|
|
||||||
|
// BufInfoStorage
|
||||||
|
UINT64 buf_info_addr = *(UINT64 *)(db_addr + 0x20);
|
||||||
|
|
||||||
|
UINT64 buf_info_handle = *(UINT64 *)(buf_info_addr + 0x50);
|
||||||
|
common::DatabaseInfo media_msg0_db{0};
|
||||||
|
media_msg0_db.db_name = (wchar_t *)(*(UINT64 *)(buf_info_addr + 0x78));
|
||||||
|
media_msg0_db.db_name_len = *(DWORD *)(buf_info_addr + 0x80);
|
||||||
|
media_msg0_db.handle = buf_info_handle;
|
||||||
|
ExecuteSQL(buf_info_handle,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &media_msg0_db);
|
||||||
|
dbs_.push_back(media_msg0_db);
|
||||||
|
std::wstring media_msg_db_name =
|
||||||
|
std::wstring(media_msg0_db.db_name,media_msg0_db.db_name_len);
|
||||||
|
dbmap_[media_msg_db_name] = media_msg0_db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// publicMsg.db
|
||||||
|
UINT64 public_msg_mgr_ptr =*(UINT64 *)(public_msg_mgr_addr);
|
||||||
|
for (unsigned int i = 1; i < 4; i++) {
|
||||||
|
UINT64 public_msg_ptr = *(UINT64 *)(public_msg_mgr_ptr + i * 0x8);
|
||||||
|
if (public_msg_ptr) {
|
||||||
|
UINT64 public_msg_db_addr = *(UINT64 *)(public_msg_ptr + 0x50);
|
||||||
|
common::DatabaseInfo public_msg_db{0};
|
||||||
|
public_msg_db.db_name = (wchar_t *)(*(UINT64 *)(public_msg_ptr + 0x78));
|
||||||
|
public_msg_db.db_name_len = *(DWORD *)(public_msg_ptr + 0x80);
|
||||||
|
public_msg_db.handle = public_msg_db_addr;
|
||||||
|
ExecuteSQL(public_msg_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &public_msg_db);
|
||||||
|
dbs_.push_back(public_msg_db);
|
||||||
|
std::wstring public_msg_db_name =
|
||||||
|
std::wstring(public_msg_db.db_name , public_msg_db.db_name_len);
|
||||||
|
dbmap_[public_msg_db_name] = public_msg_db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Favorite.db
|
||||||
|
UINT64 favItems_ptr =
|
||||||
|
*(UINT64 *)(*(UINT64 *)(*(UINT64 *)(favorite_storage_mgr_addr) + 0x10) + 0x8);
|
||||||
|
if (favItems_ptr) {
|
||||||
|
UINT64 favorite_db_addr = *(UINT64 *)(favItems_ptr + 0x50);
|
||||||
|
common::DatabaseInfo favorite_db{0};
|
||||||
|
favorite_db.db_name = (wchar_t *)(*(UINT64 *)(favItems_ptr + 0x78));
|
||||||
|
favorite_db.db_name_len = *(DWORD *)(favItems_ptr + 0x80);
|
||||||
|
favorite_db.handle = favorite_db_addr;
|
||||||
|
ExecuteSQL(favorite_db_addr,
|
||||||
|
"select * from sqlite_master where type=\"table\";",
|
||||||
|
(UINT64)GetDbInfo, &favorite_db);
|
||||||
|
dbs_.push_back(favorite_db);
|
||||||
|
std::wstring public_msg_db_name =
|
||||||
|
std::wstring(favorite_db.db_name,favorite_db.db_name_len);
|
||||||
|
dbmap_[public_msg_db_name] = favorite_db;
|
||||||
|
}
|
||||||
|
common::DatabaseInfo db_end = {0};
|
||||||
|
dbs_.push_back(db_end);
|
||||||
|
std::vector<void *> ret_array;
|
||||||
|
for (unsigned int i = 0; i < dbs_.size() - 1; i++){
|
||||||
|
ret_array.push_back(&dbs_[i]);
|
||||||
|
}
|
||||||
|
return ret_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 DB::GetDbHandleByDbName(wchar_t *dbname) {
|
||||||
|
if (dbmap_.size() == 0) {
|
||||||
|
GetDbHandles();
|
||||||
|
}
|
||||||
|
if (dbmap_.find(dbname) != dbmap_.end()) {
|
||||||
|
return dbmap_[dbname].handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT64 DB::GetLocalIdByMsgId(ULONG64 msgid, INT64 &dbIndex) {
|
||||||
|
char sql[260] = {0};
|
||||||
|
sprintf_s(sql, "select localId from MSG where MsgSvrID=%llu;", msgid);
|
||||||
|
wchar_t dbname[20] = {0};
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
swprintf_s(dbname, L"MSG%d.db", i);
|
||||||
|
UINT64 handle = GetDbHandleByDbName(dbname);
|
||||||
|
if (handle == 0) {
|
||||||
|
SPDLOG_INFO("MSG db handle is null");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::vector<std::vector<std::string>> result;
|
||||||
|
int ret = Select(handle, (const char *)sql, result);
|
||||||
|
if (result.size() == 0) continue;
|
||||||
|
dbIndex = dbmap_[dbname].extrainfo;
|
||||||
|
return stoi(result[1][0]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> DB::GetChatMsgByMsgId(ULONG64 msgid) {
|
||||||
|
char sql[260] = {0};
|
||||||
|
sprintf_s(sql,
|
||||||
|
"select "
|
||||||
|
"localId,TalkerId,MsgSvrID,Type,SubType,IsSender,CreateTime,"
|
||||||
|
"Sequence,StatusEx,FlagEx,Status,MsgServerSeq,MsgSequence,"
|
||||||
|
"StrTalker,StrContent,BytesExtra from MSG where MsgSvrID=%llu;",
|
||||||
|
msgid);
|
||||||
|
wchar_t dbname[20] = {0};
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
swprintf_s(dbname, L"MSG%d.db", i);
|
||||||
|
UINT64 handle = GetDbHandleByDbName(dbname);
|
||||||
|
if (handle == 0) {
|
||||||
|
// LOG(INFO) << "MSG db handle is null";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::vector<std::vector<std::string>> result;
|
||||||
|
int ret = Select(handle, (const char *)sql, result);
|
||||||
|
if (result.size() == 0) continue;
|
||||||
|
return result[1];
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DB::GetVoiceBuffByMsgId(ULONG64 msgid) {
|
||||||
|
char sql[260] = {0};
|
||||||
|
sprintf_s(sql, "SELECT Buf from Media WHERE Reserved0=%llu;", msgid);
|
||||||
|
wchar_t dbname[20] = {0};
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
swprintf_s(dbname, L"MediaMSG%d.db", i);
|
||||||
|
UINT64 handle = GetDbHandleByDbName(dbname);
|
||||||
|
if (handle == 0) {
|
||||||
|
// LOG(INFO) << "Media db handle is null";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::vector<std::vector<std::string>> result;
|
||||||
|
int ret = Select(handle, (const char *)sql, result);
|
||||||
|
if (result.size() == 0) continue;
|
||||||
|
return result[1][0];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DB::GetPublicMsgCompressContentByMsgId(ULONG64 msgid) {
|
||||||
|
char sql[260] = {0};
|
||||||
|
sprintf_s(sql, "SELECT CompressContent from PublicMsg WHERE MsgSvrID=%llu;", msgid);
|
||||||
|
wchar_t dbname[20] = {0};
|
||||||
|
swprintf_s( dbname, 20, L"%s", L"PublicMsg.db");
|
||||||
|
UINT64 handle = GetDbHandleByDbName(dbname);
|
||||||
|
if (handle == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::vector<std::vector<std::string>> result;
|
||||||
|
int ret = Select(handle, (const char *)sql, result);
|
||||||
|
if (result.size() == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return result[1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wxhelper
|
39
src/db.h
Normal file
39
src/db.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef WXHELPER_DB_H_
|
||||||
|
#define WXHELPER_DB_H_
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "wechat_function.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "singleton.h"
|
||||||
|
namespace wxhelper {
|
||||||
|
class DB :public Singleton<DB>{
|
||||||
|
public:
|
||||||
|
void init(UINT64 base);
|
||||||
|
int ExecuteSQL(UINT64 db, const char *sql, UINT64 callback, void *data);
|
||||||
|
|
||||||
|
int Select(UINT64 db_hanle, const char *sql,
|
||||||
|
std::vector<std::vector<std::string>> &query_result);
|
||||||
|
|
||||||
|
std::vector<void *> GetDbHandles();
|
||||||
|
UINT64 GetDbHandleByDbName(wchar_t *dbname);
|
||||||
|
INT64 GetLocalIdByMsgId(ULONG64 msgid, INT64 &dbIndex);
|
||||||
|
std::vector<std::string> GetChatMsgByMsgId(ULONG64 msgid);
|
||||||
|
|
||||||
|
std::string GetVoiceBuffByMsgId(ULONG64 msgid);
|
||||||
|
|
||||||
|
std::string GetPublicMsgCompressContentByMsgId(ULONG64 msgid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int SelectDataInner(UINT64 db, const char *sql,
|
||||||
|
std::vector<std::vector<common::SqlResult>> &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::wstring, common::DatabaseInfo> dbmap_;
|
||||||
|
std::vector<common::DatabaseInfo> dbs_;
|
||||||
|
UINT64 base_addr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wxhelper
|
||||||
|
|
||||||
|
#endif
|
@ -1,160 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "db_operation.h"
|
|
||||||
|
|
||||||
#include "base64.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "new_sqlite3.h"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/// @brief free data
|
|
||||||
void FreeResult(vector<vector<SqlResult>> &data) {
|
|
||||||
if (data.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < data.size(); i++) {
|
|
||||||
for (unsigned j = 0; j < data[i].size(); j++) {
|
|
||||||
SqlResult *sr = (SqlResult *)&data[i][j];
|
|
||||||
if (sr->column_name) {
|
|
||||||
delete[] sr->column_name;
|
|
||||||
sr->column_name = NULL;
|
|
||||||
}
|
|
||||||
if (sr->content) {
|
|
||||||
delete[] sr->content;
|
|
||||||
sr->content = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data[i].clear();
|
|
||||||
}
|
|
||||||
data.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int SelectDataInner(DWORD db, const char *sql,
|
|
||||||
vector<vector<SqlResult>> &data) {
|
|
||||||
DWORD wxBaseAddress = GetWeChatWinBase();
|
|
||||||
Sqlite3_prepare p_Sqlite3_prepare =
|
|
||||||
(Sqlite3_prepare)(wxBaseAddress + SQLITE3_PREPARE_OFFSET);
|
|
||||||
Sqlite3_step p_Sqlite3_step =
|
|
||||||
(Sqlite3_step)(wxBaseAddress + SQLITE3_STEP_OFFSET);
|
|
||||||
Sqlite3_column_count p_Sqlite3_column_count =
|
|
||||||
(Sqlite3_column_count)(wxBaseAddress + SQLITE3_COLUMN_COUNT_OFFSET);
|
|
||||||
Sqlite3_column_name p_Sqlite3_column_name =
|
|
||||||
(Sqlite3_column_name)(wxBaseAddress + SQLITE3_COLUMN_NAME_OFFSET);
|
|
||||||
Sqlite3_column_type p_Sqlite3_column_type =
|
|
||||||
(Sqlite3_column_type)(wxBaseAddress + SQLITE3_COLUMN_TYPE_OFFSET);
|
|
||||||
Sqlite3_column_blob p_Sqlite3_column_blob =
|
|
||||||
(Sqlite3_column_blob)(wxBaseAddress + SQLITE3_COLUMN_BLOB_OFFSET);
|
|
||||||
Sqlite3_column_bytes p_Sqlite3_column_bytes =
|
|
||||||
(Sqlite3_column_bytes)(wxBaseAddress + SQLITE3_COLUMN_BYTES_OFFSET);
|
|
||||||
Sqlite3_finalize p_Sqlite3_finalize =
|
|
||||||
(Sqlite3_finalize)(wxBaseAddress + SQLITE3_FINALIZE_OFFSET);
|
|
||||||
DWORD *stmt;
|
|
||||||
int rc = p_Sqlite3_prepare(db, sql, -1, &stmt, 0);
|
|
||||||
if (rc != SQLITE_OK) return NULL;
|
|
||||||
while (p_Sqlite3_step(stmt) == SQLITE_ROW) {
|
|
||||||
int col_count = p_Sqlite3_column_count(stmt);
|
|
||||||
vector<SqlResult> tempStruct;
|
|
||||||
for (int i = 0; i < col_count; i++) {
|
|
||||||
SqlResult temp = {0};
|
|
||||||
const char *ColName = p_Sqlite3_column_name(stmt, i);
|
|
||||||
int nType = p_Sqlite3_column_type(stmt, i);
|
|
||||||
const void *pReadBlobData = p_Sqlite3_column_blob(stmt, i);
|
|
||||||
int nLength = p_Sqlite3_column_bytes(stmt, i);
|
|
||||||
temp.column_name = new char[strlen(ColName) + 1];
|
|
||||||
memcpy(temp.column_name, ColName, strlen(ColName) + 1);
|
|
||||||
temp.column_name_len = strlen(ColName);
|
|
||||||
temp.content_len = nLength;
|
|
||||||
switch (nType) {
|
|
||||||
case SQLITE_BLOB: {
|
|
||||||
temp.content = new char[nLength];
|
|
||||||
memcpy(temp.content, pReadBlobData, nLength);
|
|
||||||
temp.is_blob = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (nLength != 0) {
|
|
||||||
temp.content = new char[nLength + 1];
|
|
||||||
memcpy(temp.content, pReadBlobData, nLength + 1);
|
|
||||||
} else {
|
|
||||||
temp.content = new char[2];
|
|
||||||
ZeroMemory(temp.content, 2);
|
|
||||||
}
|
|
||||||
temp.is_blob = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tempStruct.push_back(temp);
|
|
||||||
}
|
|
||||||
data.push_back(tempStruct);
|
|
||||||
}
|
|
||||||
p_Sqlite3_finalize(stmt);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Select(DWORD db_hanle, const char *sql,
|
|
||||||
vector<vector<string>> &query_result) {
|
|
||||||
vector<vector<SqlResult>> data;
|
|
||||||
int status = SelectDataInner(db_hanle, sql, data);
|
|
||||||
if (status == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(data.size() == 0){
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
vector<string> index;
|
|
||||||
for (size_t i = 0; i < data[0].size(); i++){
|
|
||||||
index.push_back(data[0][i].column_name);
|
|
||||||
}
|
|
||||||
query_result.push_back(index);
|
|
||||||
|
|
||||||
for (auto it : data) {
|
|
||||||
vector<string> item;
|
|
||||||
for (size_t i = 0; i < it.size(); i++) {
|
|
||||||
if (!it[i].is_blob) {
|
|
||||||
string content(it[i].content);
|
|
||||||
item.push_back(content);
|
|
||||||
} else {
|
|
||||||
string b64_str =
|
|
||||||
base64_encode((BYTE *)it[i].content, it[i].content_len);
|
|
||||||
item.push_back(b64_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
query_result.push_back(item);
|
|
||||||
}
|
|
||||||
FreeResult(data);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SelectDbInfo(void *data, int argc, char **argv, char **name) {
|
|
||||||
vector<SqlResult> result;
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
SqlResult temp = {0};
|
|
||||||
temp.column_name = new char[strlen(name[i]) + 1];
|
|
||||||
memcpy(temp.column_name, name[i], strlen(name[i]) + 1);
|
|
||||||
temp.column_name_len = strlen(name[i]);
|
|
||||||
if (argv[i]) {
|
|
||||||
temp.content = new char[strlen(argv[i]) + 1];
|
|
||||||
memcpy(temp.content, argv[i], strlen(argv[i]) + 1);
|
|
||||||
temp.content_len = strlen(argv[i]);
|
|
||||||
} else {
|
|
||||||
temp.content = new char[2];
|
|
||||||
ZeroMemory(temp.content, 2);
|
|
||||||
temp.content_len = 0;
|
|
||||||
}
|
|
||||||
result.push_back(temp);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief sqlite3_exec
|
|
||||||
/// @param db
|
|
||||||
/// @param sql
|
|
||||||
/// @param callback
|
|
||||||
/// @param data
|
|
||||||
/// @return
|
|
||||||
int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data) {
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD sqlite3_exec_addr = base + SQLITE3_EXEC_OFFSET;
|
|
||||||
Sqlite3_exec fn_sqlite3_exec = (Sqlite3_exec)sqlite3_exec_addr;
|
|
||||||
int status = fn_sqlite3_exec(db, sql, (Sqlite3_callback)callback, data, 0);
|
|
||||||
return status;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
#ifndef DB_OPERATION_H_
|
|
||||||
#define DB_OPERATION_H_
|
|
||||||
#include <windows.h>
|
|
||||||
#include <vector>
|
|
||||||
struct SqlResult {
|
|
||||||
char *column_name;
|
|
||||||
DWORD column_name_len;
|
|
||||||
char *content;
|
|
||||||
DWORD content_len;
|
|
||||||
BOOL is_blob;
|
|
||||||
};
|
|
||||||
/// @brief exec sql
|
|
||||||
/// @param db opened db
|
|
||||||
/// @param sql sql
|
|
||||||
/// @param callback callback func
|
|
||||||
/// @param data data
|
|
||||||
/// @return
|
|
||||||
int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data);
|
|
||||||
|
|
||||||
int Select(DWORD db_hanle, const char *sql,std::vector<std::vector<std::string>> &query_result);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,12 +1,16 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "api.h"
|
#include "global_context.h"
|
||||||
#include "common.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace wxhelper;
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
||||||
LPVOID lpReserved) {
|
LPVOID lpReserved) {
|
||||||
switch (ul_reason_for_call) {
|
switch (ul_reason_for_call) {
|
||||||
case DLL_PROCESS_ATTACH: {
|
case DLL_PROCESS_ATTACH: {
|
||||||
http_start(19088);
|
DisableThreadLibraryCalls(hModule);
|
||||||
|
GlobalContext::GetInstance().initialize(hModule);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DLL_THREAD_ATTACH: {
|
case DLL_THREAD_ATTACH: {
|
||||||
@ -16,10 +20,9 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DLL_PROCESS_DETACH: {
|
case DLL_PROCESS_DETACH: {
|
||||||
CloseConsole();
|
GlobalContext::GetInstance().finally();
|
||||||
http_close();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
183
src/download.cc
183
src/download.cc
@ -1,183 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "download.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "get_db_handle.h"
|
|
||||||
|
|
||||||
#include "wechat_data.h"
|
|
||||||
|
|
||||||
#define WX_NEW_CHAT_MSG_OFFSET 0x70e2a0
|
|
||||||
#define WX_GET_PRE_DOWNLOAD_MGR_OFFSET 0x7ae310
|
|
||||||
#define WX_PUSH_ATTACH_TASK_OFFSET 0x7c94a0
|
|
||||||
#define WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET 0x6f5370
|
|
||||||
#define WX_FREE_CHAT_MSG_OFFSET 0x6f4ea0
|
|
||||||
#define WX_CHAT_MGR_OFFSET 0x732660
|
|
||||||
#define WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET 0xb54950
|
|
||||||
#define WX_GET_CURRENT_DATA_PATH_OFFSET 0xc11140
|
|
||||||
#define WX_APP_MSG_INFO_OFFSET 0x7571d0
|
|
||||||
#define WX_GET_APP_MSG_XML_OFFSET 0xddef80
|
|
||||||
#define WX_FREE_APP_MSG_INFO_OFFSET 0x73d820
|
|
||||||
#define WX_PUSH_THUMB_TASK_OFFSET 0x7c93a0
|
|
||||||
#define WX_VIDEO_MGR_OFFSET 0x7c7300
|
|
||||||
#define WX_DOWNLOAD_VIDEO_IMG_OFFSET 0xcc6d80
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int DoDownloadTask(ULONG64 msg_id) {
|
|
||||||
int success = -1;
|
|
||||||
int db_index = 0;
|
|
||||||
int local_id = GetLocalIdByMsgId(msg_id, db_index);
|
|
||||||
if (local_id < 1) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
char chat_msg[0x2C4] = {0};
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET;
|
|
||||||
DWORD get_chat_mgr_addr = base + WX_CHAT_MGR_OFFSET;
|
|
||||||
DWORD pre_download_mgr_addr = base + WX_GET_PRE_DOWNLOAD_MGR_OFFSET;
|
|
||||||
DWORD push_attach_task_addr = base + WX_PUSH_ATTACH_TASK_OFFSET;
|
|
||||||
DWORD free_addr = base + WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET;
|
|
||||||
DWORD get_by_local_Id_addr = base + WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET;
|
|
||||||
DWORD get_current_data_path_addr = base + WX_GET_CURRENT_DATA_PATH_OFFSET;
|
|
||||||
DWORD free_app_msg_info_addr = base + WX_FREE_APP_MSG_INFO_OFFSET;
|
|
||||||
DWORD push_thumb_task_addr = base + WX_PUSH_THUMB_TASK_OFFSET;
|
|
||||||
DWORD video_mgr_addr = base + WX_VIDEO_MGR_OFFSET;
|
|
||||||
DWORD download_video_image_addr = base + WX_VIDEO_MGR_OFFSET;
|
|
||||||
|
|
||||||
WeChatString current_data_path;
|
|
||||||
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
LEA ECX,current_data_path
|
|
||||||
CALL get_current_data_path_addr
|
|
||||||
|
|
||||||
LEA ECX,chat_msg
|
|
||||||
CALL new_chat_msg_addr
|
|
||||||
|
|
||||||
CALL get_chat_mgr_addr
|
|
||||||
PUSH dword ptr [db_index]
|
|
||||||
LEA ECX,chat_msg
|
|
||||||
PUSH dword ptr [local_id]
|
|
||||||
CALL get_by_local_Id_addr
|
|
||||||
ADD ESP,0x8
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
wstring save_path = L"";
|
|
||||||
wstring thumb_path = L"";
|
|
||||||
if (current_data_path.length > 0) {
|
|
||||||
save_path += current_data_path.ptr;
|
|
||||||
save_path += L"wxhelper";
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FindOrCreateDirectoryW(save_path.c_str())) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
DWORD type = *(DWORD *)(chat_msg + 0x38);
|
|
||||||
wchar_t *content = *(wchar_t **)(chat_msg + 0x70);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 0x3: {
|
|
||||||
save_path += L"\\image";
|
|
||||||
if (!FindOrCreateDirectoryW(save_path.c_str())) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
save_path = save_path +L"\\"+ to_wstring(msg_id) + L".png";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x3E:
|
|
||||||
case 0x2B: {
|
|
||||||
save_path += L"\\video";
|
|
||||||
if (!FindOrCreateDirectoryW(save_path.c_str())) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
thumb_path = save_path + L"\\"+ to_wstring(msg_id) + L".jpg";
|
|
||||||
save_path = save_path + L"\\"+ to_wstring(msg_id) + L".mp4";
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x31: {
|
|
||||||
save_path += L"\\file";
|
|
||||||
wcout << save_path << endl;
|
|
||||||
if (!FindOrCreateDirectoryW(save_path.c_str())) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
char xml_app_msg[0xC80] = {0};
|
|
||||||
DWORD new_app_msg_addr = base + WX_APP_MSG_INFO_OFFSET;
|
|
||||||
DWORD get_xml_addr = base + WX_GET_APP_MSG_XML_OFFSET;
|
|
||||||
WeChatString w_content(content);
|
|
||||||
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
LEA ECX,xml_app_msg
|
|
||||||
CALL new_app_msg_addr
|
|
||||||
PUSH 0x1
|
|
||||||
LEA EAX,w_content
|
|
||||||
PUSH EAX
|
|
||||||
LEA ECX,xml_app_msg
|
|
||||||
CALL get_xml_addr
|
|
||||||
MOV success,EAX
|
|
||||||
LEA ECX,xml_app_msg
|
|
||||||
CALL free_app_msg_info_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
if (success != 1) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
WeChatString *file_name = (WeChatString *)((DWORD)xml_app_msg + 0x44);
|
|
||||||
save_path = save_path +L"\\" + to_wstring(msg_id) + L"_" +
|
|
||||||
wstring(file_name->ptr, file_name->length);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
WeChatString w_save_path(save_path);
|
|
||||||
WeChatString w_thumb_path(thumb_path);
|
|
||||||
int temp =1;
|
|
||||||
memcpy(&chat_msg[0x19C], &w_thumb_path, sizeof(w_thumb_path));
|
|
||||||
memcpy(&chat_msg[0x1B0], &w_save_path, sizeof(w_save_path));
|
|
||||||
memcpy(&chat_msg[0x290], &temp, sizeof(temp));
|
|
||||||
// note: the image has been downloaded and will not be downloaded again
|
|
||||||
// use low-level method
|
|
||||||
// this function does not work, need to modify chatmsg.
|
|
||||||
// if (type == 0x3E || type == 0x2B){
|
|
||||||
// __asm{
|
|
||||||
// PUSHAD
|
|
||||||
// PUSHFD
|
|
||||||
// CALL video_mgr_addr
|
|
||||||
// LEA ECX,chat_msg
|
|
||||||
// PUSH ECX
|
|
||||||
// MOV ECX,EAX
|
|
||||||
// CALL download_video_image_addr
|
|
||||||
// POPFD
|
|
||||||
// POPAD
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
CALL pre_download_mgr_addr
|
|
||||||
PUSH 0x1
|
|
||||||
PUSH 0x0
|
|
||||||
LEA ECX,chat_msg
|
|
||||||
PUSH ECX
|
|
||||||
MOV ECX,EAX
|
|
||||||
CALL push_attach_task_addr
|
|
||||||
MOV success,EAX
|
|
||||||
LEA ECX,chat_msg
|
|
||||||
PUSH 0x0
|
|
||||||
CALL free_addr
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
#ifndef DOWNLOAD_H_
|
|
||||||
#define DOWNLOAD_H_
|
|
||||||
|
|
||||||
int DoDownloadTask(ULONG64 msg_id);
|
|
||||||
#endif
|
|
61
src/export.asm
Normal file
61
src/export.asm
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
;#################################################################
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
|
||||||
|
;检测wechat登录状态
|
||||||
|
;param: get_account_service_addr 函数地址
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
_GetAccountService PROC,
|
||||||
|
get_account_service_addr:QWORD ; 函数地址
|
||||||
|
sub rsp,28h
|
||||||
|
call rcx
|
||||||
|
add rsp,28h
|
||||||
|
ret
|
||||||
|
_GetAccountService ENDP
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
|
||||||
|
;获取wechat数据保存路径
|
||||||
|
;param: addr 函数地址
|
||||||
|
;return:路径地址
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
_GetDataSavePath PROC,
|
||||||
|
get_data_path_addr:QWORD, ; 函数地址
|
||||||
|
out_path:QWORD ; 输出
|
||||||
|
sub rsp,40h
|
||||||
|
mov rax,rcx
|
||||||
|
mov rcx,rdx
|
||||||
|
call rax
|
||||||
|
add rsp,40h
|
||||||
|
ret
|
||||||
|
_GetDataSavePath ENDP
|
||||||
|
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
|
||||||
|
;获取wechat当前数据保存路径
|
||||||
|
;param: addr 函数地址
|
||||||
|
;return:路径地址
|
||||||
|
|
||||||
|
;#################################################################
|
||||||
|
_GetCurrentDataPath PROC,
|
||||||
|
get_current_path_addr: QWORD, ; 函数地址
|
||||||
|
out_path: QWORD ; 输出
|
||||||
|
sub rsp,28h
|
||||||
|
mov rax,rcx
|
||||||
|
mov rcx,rdx
|
||||||
|
call rax
|
||||||
|
add rsp,28h
|
||||||
|
ret
|
||||||
|
_GetCurrentDataPath ENDP
|
||||||
|
|
||||||
|
|
||||||
|
END
|
8
src/export.h
Normal file
8
src/export.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef WXHELPER_EXPORT_H_
|
||||||
|
#define WXHELPER_EXPORT_H_
|
||||||
|
|
||||||
|
extern "C" UINT64 _GetAccountService(UINT64 addr);
|
||||||
|
extern "C" UINT64 _GetDataSavePath(UINT64 addr,ULONG_PTR out);
|
||||||
|
extern "C" UINT64 _GetCurrentDataPath(UINT64 addr,ULONG_PTR out);
|
||||||
|
extern "C" UINT64 _SendTextMsg(UINT64 mgr_addr,UINT64 send_text_addr,UINT64 free_addr,UINT64 receiver,UINT64 msg,UINT64 chat_msg);
|
||||||
|
#endif
|
@ -1,41 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "forward.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "get_db_handle.h"
|
|
||||||
#include "wechat_data.h"
|
|
||||||
#define WX_FORWARD_MSG_OFFSET 0xc715f0
|
|
||||||
#define WX_INIT_CHAT_MSG_OFFSET 0xed3be0
|
|
||||||
|
|
||||||
int ForwardMsg(wchar_t *wxid, unsigned long long msgid) {
|
|
||||||
int success = 0;
|
|
||||||
|
|
||||||
int db_index = 0;
|
|
||||||
int localid = GetLocalIdByMsgId(msgid, db_index);
|
|
||||||
|
|
||||||
if (localid == 0) return 0;
|
|
||||||
WeChatString to_user(wxid);
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD forward_msg_addr = base + WX_FORWARD_MSG_OFFSET;
|
|
||||||
DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET;
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
MOV EDX, DWORD PTR [db_index]
|
|
||||||
PUSH EDX
|
|
||||||
MOV EAX, DWORD PTR [localid]
|
|
||||||
PUSH EAX
|
|
||||||
SUB ESP,0x14
|
|
||||||
MOV ECX,ESP
|
|
||||||
LEA ESI,to_user
|
|
||||||
PUSH ESI
|
|
||||||
CALL init_chat_msg_addr
|
|
||||||
CALL forward_msg_addr
|
|
||||||
MOVZX EAX,AL
|
|
||||||
MOV success,EAX
|
|
||||||
ADD ESP,0x1c
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
#ifndef FORWARD_H_
|
|
||||||
#define FORWARD_H_
|
|
||||||
|
|
||||||
int ForwardMsg(wchar_t *wxid, unsigned long long msgid);
|
|
||||||
#endif
|
|
@ -1,7 +0,0 @@
|
|||||||
#ifndef FRAMEWORK_H_
|
|
||||||
#define FRAMEWORK_H_
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,318 +0,0 @@
|
|||||||
#include "get_db_handle.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "db_operation.h"
|
|
||||||
#include "new_sqlite3.h"
|
|
||||||
#include "pch.h"
|
|
||||||
#include "wechat_data.h"
|
|
||||||
#define CONTACT_G_PINSTANCE_OFFSET 0x2e2d628
|
|
||||||
#define DB_MICRO_MSG_OFFSET 0x68
|
|
||||||
#define DB_CHAT_MSG_OFFSET 0x1C0
|
|
||||||
#define DB_MISC_OFFSET 0x3D8
|
|
||||||
#define DB_EMOTION_OFFSET 0x558
|
|
||||||
#define DB_MEDIA_OFFSET 0x9B8
|
|
||||||
#define DB_BIZCHAT_MSG_OFFSET 0x1120
|
|
||||||
#define DB_FUNCTION_MSG_OFFSET 0x11B0
|
|
||||||
#define DB_NAME_OFFSET 0x14
|
|
||||||
|
|
||||||
#define PUBLIC_MSG_MGR_OFFSET 0x2e6ce20
|
|
||||||
#define MULTI_DB_MSG_MGR_OFFSET 0x2e6ec84
|
|
||||||
#define FAVORITE_STORAGE_MGR_OFFSET 0x2e6e630
|
|
||||||
#define FTS_FAVORITE_MGR_OFFSET 0x2e2e168
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
map<wstring, DatabaseInfo> dbmap;
|
|
||||||
std::vector<DatabaseInfo> dbs;
|
|
||||||
|
|
||||||
int GetDbInfo(void *data, int argc, char **argv, char **name) {
|
|
||||||
DatabaseInfo *pdata = (DatabaseInfo *)data;
|
|
||||||
TableInfo tb = {0};
|
|
||||||
if (argv[1]) {
|
|
||||||
tb.name = new char[strlen(argv[1]) + 1];
|
|
||||||
memcpy(tb.name, argv[1], strlen(argv[1]) + 1);
|
|
||||||
} else {
|
|
||||||
tb.name = (char *)"NULL";
|
|
||||||
}
|
|
||||||
if (argv[2]) {
|
|
||||||
tb.table_name = new char[strlen(argv[2]) + 1];
|
|
||||||
memcpy(tb.table_name, argv[2], strlen(argv[2]) + 1);
|
|
||||||
} else {
|
|
||||||
tb.table_name = (char *)"NULL";
|
|
||||||
}
|
|
||||||
if (argv[3]) {
|
|
||||||
tb.rootpage = new char[strlen(argv[3]) + 1];
|
|
||||||
memcpy(tb.rootpage, argv[3], strlen(argv[3]) + 1);
|
|
||||||
} else {
|
|
||||||
tb.rootpage = (char *)"NULL";
|
|
||||||
}
|
|
||||||
if (argv[4]) {
|
|
||||||
tb.sql = new char[strlen(argv[4]) + 1];
|
|
||||||
memcpy(tb.sql, argv[4], strlen(argv[4]) + 1);
|
|
||||||
} else {
|
|
||||||
tb.sql = (char *)"NULL";
|
|
||||||
}
|
|
||||||
tb.name_len = strlen(tb.name);
|
|
||||||
tb.table_name_len = strlen(tb.table_name);
|
|
||||||
tb.sql_len = strlen(tb.sql);
|
|
||||||
tb.rootpage_len = strlen(tb.rootpage);
|
|
||||||
pdata->tables.push_back(tb);
|
|
||||||
pdata->count = pdata->tables.size();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<void *> GetDbHandles() {
|
|
||||||
dbs.clear();
|
|
||||||
dbmap.clear();
|
|
||||||
DWORD base = GetWeChatWinBase();
|
|
||||||
DWORD p_contact_addr = *(DWORD *)(base + CONTACT_G_PINSTANCE_OFFSET);
|
|
||||||
DWORD micro_msg_db_addr = *(DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET);
|
|
||||||
DWORD chat_msg_db_addr = *(DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET);
|
|
||||||
DWORD misc_db_addr = *(DWORD *)(p_contact_addr + DB_MISC_OFFSET);
|
|
||||||
DWORD emotion_db_addr = *(DWORD *)(p_contact_addr + DB_EMOTION_OFFSET);
|
|
||||||
DWORD media_db_addr = *(DWORD *)(p_contact_addr + DB_MEDIA_OFFSET);
|
|
||||||
DWORD bizchat_msg_db_addr =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_BIZCHAT_MSG_OFFSET);
|
|
||||||
DWORD function_msg_db_addr =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET);
|
|
||||||
|
|
||||||
// microMsg.db
|
|
||||||
DatabaseInfo micro_msg_db{0};
|
|
||||||
micro_msg_db.db_name = (wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET));
|
|
||||||
micro_msg_db.db_name_len =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET + 0x4);
|
|
||||||
micro_msg_db.handle = micro_msg_db_addr;
|
|
||||||
ExecuteSQL(micro_msg_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, µ_msg_db);
|
|
||||||
dbs.push_back(micro_msg_db);
|
|
||||||
wstring micro_msg_name = wstring((wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[micro_msg_name] = micro_msg_db;
|
|
||||||
|
|
||||||
// chatMsg.db
|
|
||||||
DatabaseInfo chat_msg_db{0};
|
|
||||||
chat_msg_db.db_name = (wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET));
|
|
||||||
chat_msg_db.db_name_len =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET + 0x4);
|
|
||||||
chat_msg_db.handle = chat_msg_db_addr;
|
|
||||||
ExecuteSQL(chat_msg_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &chat_msg_db);
|
|
||||||
dbs.push_back(chat_msg_db);
|
|
||||||
wstring chat_msg_name = wstring((wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[chat_msg_name] = chat_msg_db;
|
|
||||||
|
|
||||||
// misc.db
|
|
||||||
DatabaseInfo misc_db{0};
|
|
||||||
misc_db.db_name =
|
|
||||||
(wchar_t *)(*(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET));
|
|
||||||
misc_db.db_name_len =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET + 0x4);
|
|
||||||
misc_db.handle = misc_db_addr;
|
|
||||||
ExecuteSQL(misc_db_addr, "select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &misc_db);
|
|
||||||
dbs.push_back(misc_db);
|
|
||||||
wstring misc_name = wstring((
|
|
||||||
wchar_t *)(*(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[misc_name] = misc_db;
|
|
||||||
|
|
||||||
// emotion.db
|
|
||||||
DatabaseInfo emotion_db{0};
|
|
||||||
emotion_db.db_name = (wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET));
|
|
||||||
emotion_db.db_name_len =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET + 0x4);
|
|
||||||
emotion_db.handle = emotion_db_addr;
|
|
||||||
ExecuteSQL(emotion_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &emotion_db);
|
|
||||||
dbs.push_back(emotion_db);
|
|
||||||
wstring emotion_name = wstring((wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[emotion_name] = emotion_db;
|
|
||||||
|
|
||||||
// media.db
|
|
||||||
DatabaseInfo media_db{0};
|
|
||||||
media_db.db_name = (wchar_t *)(*(DWORD *)(p_contact_addr + DB_MEDIA_OFFSET +
|
|
||||||
DB_NAME_OFFSET));
|
|
||||||
media_db.db_name_len =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_MEDIA_OFFSET + DB_NAME_OFFSET + 0x4);
|
|
||||||
media_db.handle = media_db_addr;
|
|
||||||
ExecuteSQL(media_db_addr, "select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &media_db);
|
|
||||||
dbs.push_back(media_db);
|
|
||||||
wstring media_name = wstring((wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_MEDIA_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[media_name] = media_db;
|
|
||||||
|
|
||||||
// functionMsg.db
|
|
||||||
DatabaseInfo function_msg_db{0};
|
|
||||||
function_msg_db.db_name = (wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET));
|
|
||||||
function_msg_db.db_name_len = *(
|
|
||||||
DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET + 0x4);
|
|
||||||
function_msg_db.handle = function_msg_db_addr;
|
|
||||||
ExecuteSQL(function_msg_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &function_msg_db);
|
|
||||||
dbs.push_back(function_msg_db);
|
|
||||||
wstring function_msg_name = wstring((wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[function_msg_name] = function_msg_db;
|
|
||||||
|
|
||||||
if (bizchat_msg_db_addr) {
|
|
||||||
// functionMsg.db maybe null
|
|
||||||
DatabaseInfo bizchat_msg_db{0};
|
|
||||||
bizchat_msg_db.db_name = (wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET));
|
|
||||||
bizchat_msg_db.db_name_len =
|
|
||||||
*(DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET +
|
|
||||||
0x4);
|
|
||||||
bizchat_msg_db.handle = bizchat_msg_db_addr;
|
|
||||||
ExecuteSQL(bizchat_msg_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &bizchat_msg_db);
|
|
||||||
dbs.push_back(bizchat_msg_db);
|
|
||||||
wstring bizchat_msg_name = wstring((wchar_t *)(*(
|
|
||||||
DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET)));
|
|
||||||
dbmap[bizchat_msg_name] = bizchat_msg_db;
|
|
||||||
}
|
|
||||||
DWORD multi_db_mgr_addr = base + MULTI_DB_MSG_MGR_OFFSET;
|
|
||||||
DWORD public_msg_mgr_addr = base + PUBLIC_MSG_MGR_OFFSET;
|
|
||||||
DWORD favorite_storage_mgr_addr = base + FAVORITE_STORAGE_MGR_OFFSET;
|
|
||||||
DWORD fts_favorite_mgr_addr = base + FTS_FAVORITE_MGR_OFFSET;
|
|
||||||
|
|
||||||
// MsgX.db
|
|
||||||
DWORD wrap_ptr = *(DWORD *)(multi_db_mgr_addr);
|
|
||||||
DWORD db_num = *(DWORD *)(wrap_ptr+0x30);
|
|
||||||
DWORD current_db_num = *(DWORD *)(wrap_ptr+0x38);
|
|
||||||
DWORD begin_ptr = *(DWORD *)(wrap_ptr + 0x2c);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < current_db_num; i++) {
|
|
||||||
DWORD next_addr = begin_ptr + i * 0x4;
|
|
||||||
DWORD db_addr = *(DWORD *) next_addr;
|
|
||||||
if (db_addr) {
|
|
||||||
DWORD msg0_db_addr = *(DWORD *)(db_addr + 0x60);
|
|
||||||
DatabaseInfo msg0_db{0};
|
|
||||||
msg0_db.db_name = (wchar_t *)(*(DWORD *)(db_addr));
|
|
||||||
msg0_db.db_name_len = *(DWORD *)(db_addr + 0x4);
|
|
||||||
msg0_db.handle = msg0_db_addr;
|
|
||||||
msg0_db.extrainfo = *(DWORD *) (*(DWORD *)(db_addr + 0x18) +0x144);
|
|
||||||
ExecuteSQL(
|
|
||||||
msg0_db_addr, "select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &msg0_db);
|
|
||||||
dbs.push_back(msg0_db);
|
|
||||||
wstring msg_db_name = wstring((wchar_t *)(*(DWORD *)(db_addr)));
|
|
||||||
dbmap[msg_db_name] = msg0_db;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// publicMsg.db
|
|
||||||
DWORD public_msg_ptr = *(DWORD *) (*(DWORD *)(public_msg_mgr_addr) + 0x8);
|
|
||||||
if (public_msg_ptr) {
|
|
||||||
DWORD public_msg_db_addr = *(DWORD *)(public_msg_ptr + 0x38);
|
|
||||||
DatabaseInfo public_msg_db{0};
|
|
||||||
public_msg_db.db_name = (wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C));
|
|
||||||
public_msg_db.db_name_len =*(DWORD *)(public_msg_ptr + 0x50);
|
|
||||||
public_msg_db.handle = public_msg_db_addr;
|
|
||||||
ExecuteSQL(public_msg_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &public_msg_db);
|
|
||||||
dbs.push_back(public_msg_db);
|
|
||||||
wstring public_msg_db_name =wstring((wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C)));
|
|
||||||
dbmap[public_msg_db_name] = public_msg_db;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Favorite.db
|
|
||||||
DWORD favItems_ptr = *(DWORD *)(*(DWORD *)(favorite_storage_mgr_addr) + 0x8);
|
|
||||||
if (favItems_ptr) {
|
|
||||||
DWORD favorite_db_addr = *(DWORD *)(favItems_ptr + 0x38);
|
|
||||||
DatabaseInfo favorite_db{0};
|
|
||||||
favorite_db.db_name =(wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C));
|
|
||||||
favorite_db.db_name_len = *(DWORD *)(favItems_ptr + 0x50);
|
|
||||||
favorite_db.handle = favorite_db_addr;
|
|
||||||
ExecuteSQL(favorite_db_addr,
|
|
||||||
"select * from sqlite_master where type=\"table\";",
|
|
||||||
(DWORD)GetDbInfo, &favorite_db);
|
|
||||||
dbs.push_back(favorite_db);
|
|
||||||
wstring public_msg_db_name =wstring((wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C)));
|
|
||||||
dbmap[public_msg_db_name] = favorite_db;
|
|
||||||
}
|
|
||||||
|
|
||||||
DatabaseInfo db_end = {0};
|
|
||||||
dbs.push_back(db_end);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
for (unsigned int i = 0; i < dbs.size() - 1; i++) {
|
|
||||||
printf("dbname = %ws,handle = 0x%08X,table_count:%d\n", dbs[i].db_name,
|
|
||||||
dbs[i].handle, dbs[i].tables.size());
|
|
||||||
for (unsigned int j = 0; j < dbs[i].tables.size(); j++) {
|
|
||||||
cout << "name = " << dbs[i].tables[j].name << endl;
|
|
||||||
cout << "tbl_name = " << dbs[i].tables[j].table_name << endl;
|
|
||||||
cout << "rootpage = " << dbs[i].tables[j].rootpage << endl;
|
|
||||||
cout << "sql = " << dbs[i].tables[j].sql << endl;
|
|
||||||
cout << endl;
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
vector<void *> ret_array;
|
|
||||||
for (unsigned int i = 0; i < dbs.size() - 1; i++)
|
|
||||||
ret_array.push_back(&dbs[i]);
|
|
||||||
return ret_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD GetDbHandleByDbName(wchar_t *dbname) {
|
|
||||||
if (dbmap.size() == 0) {
|
|
||||||
GetDbHandles();
|
|
||||||
}
|
|
||||||
if (dbmap.find(dbname) != dbmap.end()) {
|
|
||||||
return dbmap[dbname].handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex) {
|
|
||||||
char sql[260] = {0};
|
|
||||||
sprintf_s(sql, "select localId from MSG where MsgSvrID=%llu;", msgid);
|
|
||||||
wchar_t dbname[20] = {0};
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
swprintf_s(dbname, L"MSG%d.db", i);
|
|
||||||
DWORD handle = GetDbHandleByDbName(dbname);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
cout <<" handle =" <<handle<<endl;
|
|
||||||
#endif
|
|
||||||
if (handle == 0) return 0;
|
|
||||||
vector<vector<string>> result;
|
|
||||||
int ret = Select(handle, (const char *)sql, result);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
cout <<" size =" <<result.size()<<endl;
|
|
||||||
#endif
|
|
||||||
if (result.size() == 0) continue;
|
|
||||||
dbIndex = dbmap[dbname].extrainfo;
|
|
||||||
return stoi(result[1][0]);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> GetChatMsgByMsgId(ULONG64 msgid){
|
|
||||||
char sql[260] = {0};
|
|
||||||
sprintf_s(sql, "select localId,TalkerId,MsgSvrID,Type,SubType,IsSender,CreateTime,Sequence,StatusEx,FlagEx,Status,MsgServerSeq,MsgSequence,StrTalker,StrContent,BytesExtra from MSG where MsgSvrID=%llu;", msgid);
|
|
||||||
wchar_t dbname[20] = {0};
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
swprintf_s(dbname, L"MSG%d.db", i);
|
|
||||||
DWORD handle = GetDbHandleByDbName(dbname);
|
|
||||||
if (handle == 0) return {};
|
|
||||||
vector<vector<string>> result;
|
|
||||||
int ret = Select(handle, (const char *)sql, result);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
cout <<" size =" <<result.size()<<endl;
|
|
||||||
#endif
|
|
||||||
if (result.size() == 0) continue;
|
|
||||||
return result[1];
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#ifndef GET_DB_HANDLE_H_
|
|
||||||
#define GET_DB_HANDLE_H_
|
|
||||||
#include "windows.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
std::vector<void *> GetDbHandles();
|
|
||||||
DWORD GetDbHandleByDbName(wchar_t *dbname);
|
|
||||||
unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex);
|
|
||||||
std::vector<std::string> GetChatMsgByMsgId(ULONG64 msgid);
|
|
||||||
#endif
|
|
41
src/global_context.cc
Normal file
41
src/global_context.cc
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "global_context.h"
|
||||||
|
#include "thread_pool.h"
|
||||||
|
#include "db.h"
|
||||||
|
|
||||||
|
namespace wxhelper {
|
||||||
|
|
||||||
|
GlobalContext::~GlobalContext() {
|
||||||
|
if (config.has_value()) {
|
||||||
|
config.reset();
|
||||||
|
}
|
||||||
|
if (log.has_value()) {
|
||||||
|
log.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void GlobalContext::initialize(HMODULE module) {
|
||||||
|
state =GlobalContextState::INITIALIZING;
|
||||||
|
module_ = module;
|
||||||
|
#ifndef _DEBUG
|
||||||
|
Utils::Hide(module);
|
||||||
|
#endif
|
||||||
|
UINT64 base = Utils::GetWeChatWinBase();
|
||||||
|
config.emplace();
|
||||||
|
config->Initialize();
|
||||||
|
log.emplace();
|
||||||
|
log->Initialize();
|
||||||
|
http_server = std::unique_ptr<HttpServer>( new HttpServer(config->GetPort()));
|
||||||
|
http_server->HttpStart();
|
||||||
|
ThreadPool::GetInstance().Create(2, 8);
|
||||||
|
mgr = std::unique_ptr<Manager>(new Manager(base));
|
||||||
|
DB::GetInstance().init(base);
|
||||||
|
state =GlobalContextState::INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalContext::finally() {
|
||||||
|
if (http_server) {
|
||||||
|
http_server->HttpClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace wxhelper
|
34
src/global_context.h
Normal file
34
src/global_context.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef GLOBAL_CONTEXT_H_
|
||||||
|
#define GLOBAL_CONTEXT_H_
|
||||||
|
#include "config.h"
|
||||||
|
#include "http_server.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "singleton.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
namespace wxhelper {
|
||||||
|
|
||||||
|
enum class GlobalContextState { NOT_INITIALIZED, INITIALIZING, INITIALIZED };
|
||||||
|
|
||||||
|
class GlobalContext : public Singleton<GlobalContext> {
|
||||||
|
friend class Singleton<GlobalContext>;
|
||||||
|
~GlobalContext();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void initialize(HMODULE module);
|
||||||
|
void finally();
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::optional<Config> config;
|
||||||
|
std::optional<Log> log;
|
||||||
|
std::unique_ptr<HttpServer> http_server;
|
||||||
|
std::unique_ptr<Manager> mgr;
|
||||||
|
|
||||||
|
GlobalContextState state = GlobalContextState::NOT_INITIALIZED;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HMODULE module_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wxhelper
|
||||||
|
#endif
|
231
src/hook_img.cc
231
src/hook_img.cc
@ -1,231 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "hook_img.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// #define WX_HOOK_IMG_OFFSET 0xd7eaa5
|
|
||||||
// #define WX_HOOK_IMG_NEXT_OFFSET 0xda56e0
|
|
||||||
#define WX_HOOK_IMG_OFFSET 0xd723dc
|
|
||||||
#define WX_HOOK_IMG_NEXT_OFFSET 0xe91d90
|
|
||||||
#define WX_SELF_ID_OFFSET 0x2E2CD3C
|
|
||||||
#define BUFSIZE 1024
|
|
||||||
|
|
||||||
#define JPEG0 0xFF
|
|
||||||
#define JPEG1 0xD8
|
|
||||||
#define JPEG2 0xFF
|
|
||||||
#define PNG0 0x89
|
|
||||||
#define PNG1 0x50
|
|
||||||
#define PNG2 0x4E
|
|
||||||
#define BMP0 0x42
|
|
||||||
#define BMP1 0x4D
|
|
||||||
#define GIF0 0x47
|
|
||||||
#define GIF1 0x49
|
|
||||||
#define GIF2 0x46
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static wstring kImgStorePath = L"";
|
|
||||||
static int kImgHooked = FALSE;
|
|
||||||
static DWORD kWeChatWinBase = GetWeChatWinBase();
|
|
||||||
static char kOriginImgAsmCode[5] = {0};
|
|
||||||
|
|
||||||
static DWORD kHookImgNextAddress = kWeChatWinBase + WX_HOOK_IMG_NEXT_OFFSET;
|
|
||||||
static DWORD kHookImgJmpBackAddress = kWeChatWinBase + WX_HOOK_IMG_OFFSET + 0x5;
|
|
||||||
|
|
||||||
void OnHookImg(DWORD obj_addr) {
|
|
||||||
DWORD wxid_addr = GetWeChatWinBase() + WX_SELF_ID_OFFSET;
|
|
||||||
string wxid = string(*(char **)wxid_addr, *(DWORD *)(wxid_addr + 0x10));
|
|
||||||
wstring self_id = String2Wstring(wxid);
|
|
||||||
wstring save_path = kImgStorePath + self_id;
|
|
||||||
if (!FindOrCreateDirectoryW(save_path.c_str())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wchar_t *origin_file_path = *(wchar_t **)obj_addr;
|
|
||||||
wstring img_path(origin_file_path);
|
|
||||||
if (img_path.find(L"_t.dat") != wstring::npos) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos_begin = img_path.find_last_of(L"\\") + 1;
|
|
||||||
int pos_end = img_path.find_last_of(L".");
|
|
||||||
wstring file_name = img_path.substr(pos_begin, pos_end - pos_begin);
|
|
||||||
char buffer[BUFSIZE] = {0};
|
|
||||||
DWORD bytes_read = 0;
|
|
||||||
DWORD bytes_write = 0;
|
|
||||||
unsigned char magic_head[4] = {0};
|
|
||||||
wchar_t suffix[5] = {0};
|
|
||||||
|
|
||||||
HANDLE h_origin_file =
|
|
||||||
CreateFileW(origin_file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
if (h_origin_file == INVALID_HANDLE_VALUE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) {
|
|
||||||
memcpy(magic_head, buffer, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (magic_head[0] == PNG0 && magic_head[1] == PNG1 && magic_head[2] == PNG2) {
|
|
||||||
lstrcpyW(suffix, L".png");
|
|
||||||
} else if (magic_head[0] == GIF0 && magic_head[1] == GIF1 &&
|
|
||||||
magic_head[2] == GIF2) {
|
|
||||||
lstrcpyW(suffix, L".gif");
|
|
||||||
} else if (magic_head[0] == JPEG0 && magic_head[1] == JPEG1 &&
|
|
||||||
magic_head[2] == JPEG2) {
|
|
||||||
lstrcpyW(suffix, L".jpg");
|
|
||||||
} else {
|
|
||||||
lstrcpyW(suffix, L"");
|
|
||||||
}
|
|
||||||
|
|
||||||
wstring save_img_path = save_path + L"\\" + file_name + suffix;
|
|
||||||
HANDLE save_file = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL,
|
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
if (save_file == INVALID_HANDLE_VALUE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!WriteFile(save_file, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) {
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
CloseHandle(save_file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) {
|
|
||||||
if (!WriteFile(save_file, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) {
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
CloseHandle(save_file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (bytes_read == BUFSIZE);
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
CloseHandle(save_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief hook img implement
|
|
||||||
_declspec(naked) void handle_img() {
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
PUSH ECX
|
|
||||||
CALL OnHookImg
|
|
||||||
ADD ESP, 0x4
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
CALL kHookImgNextAddress
|
|
||||||
JMP kHookImgJmpBackAddress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief hook image
|
|
||||||
/// @param save_path image save dir
|
|
||||||
/// @return
|
|
||||||
int HookImg(wstring save_path) {
|
|
||||||
kWeChatWinBase = GetWeChatWinBase();
|
|
||||||
if (!kWeChatWinBase) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (kImgHooked) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
kImgStorePath = save_path;
|
|
||||||
if (kImgStorePath.back() != '\\') {
|
|
||||||
kImgStorePath += L"\\";
|
|
||||||
}
|
|
||||||
wstring createpath = kImgStorePath.substr(0, kImgStorePath.length() - 1);
|
|
||||||
if (!FindOrCreateDirectoryW(createpath.c_str())) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_IMG_OFFSET;
|
|
||||||
kHookImgNextAddress = kWeChatWinBase + WX_HOOK_IMG_NEXT_OFFSET;
|
|
||||||
static DWORD kHookImgJmpBackAddress = hook_img_addr + 0x5;
|
|
||||||
HookAnyAddress(hook_img_addr, (LPVOID)handle_img, kOriginImgAsmCode);
|
|
||||||
kImgHooked = TRUE;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int UnHookImg() {
|
|
||||||
if (!kImgHooked) return 1;
|
|
||||||
DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_IMG_OFFSET;
|
|
||||||
UnHookAnyAddress(hook_img_addr, kOriginImgAsmCode);
|
|
||||||
kImgHooked = FALSE;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetImgByName(wchar_t* file_path,wchar_t* save_dir) {
|
|
||||||
wstring save_path(save_dir);
|
|
||||||
wstring orign_file_path(file_path);
|
|
||||||
if (!FindOrCreateDirectoryW(save_path.c_str())) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos_begin = orign_file_path.find_last_of(L"\\") + 1;
|
|
||||||
int pos_end = orign_file_path.find_last_of(L".");
|
|
||||||
wstring file_name = orign_file_path.substr(pos_begin, pos_end - pos_begin);
|
|
||||||
HANDLE h_origin_file =
|
|
||||||
CreateFileW(file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
char buffer[BUFSIZE] = {0};
|
|
||||||
DWORD bytes_read = 0;
|
|
||||||
DWORD bytes_write = 0;
|
|
||||||
unsigned char magic_head[4] = {0};
|
|
||||||
wstring suffix;
|
|
||||||
short key = 0;
|
|
||||||
if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) {
|
|
||||||
memcpy(magic_head, buffer, 3);
|
|
||||||
} else {
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ((magic_head[0] ^ JPEG0) == (magic_head[1] ^ JPEG1)) {
|
|
||||||
key = magic_head[0] ^ JPEG0;
|
|
||||||
suffix = L".jpg";
|
|
||||||
} else if ((magic_head[0] ^ PNG1) == (magic_head[1] ^ PNG2)) {
|
|
||||||
key = magic_head[0] ^ PNG1;
|
|
||||||
suffix = L".png";
|
|
||||||
} else if ((magic_head[0] ^ GIF0) == (magic_head[1] ^ GIF1)) {
|
|
||||||
key = magic_head[0] ^ GIF0;
|
|
||||||
suffix = L".gif";
|
|
||||||
} else if ((magic_head[0] ^ BMP0) == (magic_head[1] ^ BMP1)) {
|
|
||||||
key = magic_head[0] ^ BMP0;
|
|
||||||
suffix = L".bmp";
|
|
||||||
} else {
|
|
||||||
key = -1;
|
|
||||||
suffix = L".dat";
|
|
||||||
}
|
|
||||||
wstring save_img_path = save_path + L"\\" + file_name + suffix;
|
|
||||||
HANDLE save_img = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL,
|
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
if (save_img == INVALID_HANDLE_VALUE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (key > 0) {
|
|
||||||
for (unsigned int i = 0; i < bytes_read; i++) {
|
|
||||||
buffer[i]^=key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) {
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
CloseHandle(save_img);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) {
|
|
||||||
if (key > 0) {
|
|
||||||
for (unsigned int i = 0; i < bytes_read; i++) {
|
|
||||||
buffer[i] ^= key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) {
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
CloseHandle(save_img);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (bytes_read == BUFSIZE);
|
|
||||||
CloseHandle(h_origin_file);
|
|
||||||
CloseHandle(save_img);
|
|
||||||
return 1;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#ifndef HOOK_IMG_H_
|
|
||||||
#define HOOK_IMG_H_
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
int HookImg(std::wstring save_path);
|
|
||||||
int UnHookImg();
|
|
||||||
|
|
||||||
int GetImgByName(wchar_t* file_path,wchar_t* save_dir);
|
|
||||||
#endif
|
|
@ -1,78 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "hook_log.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define WX_HOOK_LOG_OFFSET 0xed1675
|
|
||||||
#define WX_HOOK_LOG_NEXT_OFFSET 0x2344832
|
|
||||||
|
|
||||||
static int kLogHooked = FALSE;
|
|
||||||
static DWORD kWeChatWinBase = GetWeChatWinBase();
|
|
||||||
static char kOriginLogAsmCode[5] = {0};
|
|
||||||
|
|
||||||
static DWORD kHookLogAddress = kWeChatWinBase + WX_HOOK_LOG_OFFSET;
|
|
||||||
static DWORD kHookLogNextAddress = kWeChatWinBase + WX_HOOK_LOG_NEXT_OFFSET;
|
|
||||||
static DWORD kHookLogJmpBackAddress = kWeChatWinBase + WX_HOOK_LOG_OFFSET + 0x5;
|
|
||||||
|
|
||||||
void log_print(DWORD addr) {
|
|
||||||
if (!addr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DWORD dwId = 0;
|
|
||||||
char *msg = (char *)addr;
|
|
||||||
int size = MultiByteToWideChar(CP_UTF8, 0, msg, -1, 0, 0);
|
|
||||||
wchar_t *w_msg = new wchar_t[size + 1];
|
|
||||||
memset(w_msg, 0, (size + 1) * 2);
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, size);
|
|
||||||
size = WideCharToMultiByte(CP_ACP, 0, w_msg, -1, 0, 0, 0, 0);
|
|
||||||
char *ansi_message = new char[size + 1];
|
|
||||||
memset(ansi_message, 0, size + 1);
|
|
||||||
WideCharToMultiByte(CP_ACP, 0, w_msg, -1, ansi_message, size, 0, 0);
|
|
||||||
delete[] w_msg;
|
|
||||||
w_msg = NULL;
|
|
||||||
cout << ansi_message;
|
|
||||||
delete[] ansi_message;
|
|
||||||
ansi_message = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_declspec(naked) void handle_log() {
|
|
||||||
__asm {
|
|
||||||
PUSHAD
|
|
||||||
PUSHFD
|
|
||||||
PUSH EAX
|
|
||||||
CALL log_print
|
|
||||||
ADD ESP, 0x4
|
|
||||||
POPFD
|
|
||||||
POPAD
|
|
||||||
CALL kHookLogNextAddress
|
|
||||||
JMP kHookLogJmpBackAddress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int HookLog() {
|
|
||||||
kWeChatWinBase = GetWeChatWinBase();
|
|
||||||
if (!kWeChatWinBase) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (kLogHooked) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
kHookLogAddress = kWeChatWinBase + WX_HOOK_LOG_OFFSET;
|
|
||||||
kHookLogNextAddress = kWeChatWinBase + WX_HOOK_LOG_NEXT_OFFSET;
|
|
||||||
kHookLogJmpBackAddress = kHookLogAddress + 0x5;
|
|
||||||
HookAnyAddress(kHookLogAddress, (LPVOID)handle_log, kOriginLogAsmCode);
|
|
||||||
kLogHooked = TRUE;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int UnHookLog() {
|
|
||||||
if (!kLogHooked) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_LOG_OFFSET;
|
|
||||||
UnHookAnyAddress(hook_img_addr, kOriginLogAsmCode);
|
|
||||||
kLogHooked = FALSE;
|
|
||||||
return 1;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#ifndef HOOK_LOG_H_
|
|
||||||
#define HOOK_LOG_H_
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
int HookLog();
|
|
||||||
int UnHookLog();
|
|
||||||
|
|
||||||
#endif
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user