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.
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -30,6 +30,6 @@
|
||||
#*.exe
|
||||
*.out
|
||||
*.app
|
||||
/out
|
||||
CMakePresets.json
|
||||
.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)
|
||||
# include(ExternalProject)
|
||||
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_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)
|
||||
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(3rd)
|
||||
add_subdirectory(spdlog)
|
||||
add_subdirectory(source)
|
||||
|
||||
# find_package(spdlog 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 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
|
||||
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
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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)
|
||||
{
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//
|
||||
//
|
||||
// base64 encoding and decoding with C++.
|
||||
// Version: 2.rc.08 (release candidate)
|
||||
//
|
||||
|
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 "api.h"
|
||||
#include "common.h"
|
||||
#include "pch.h"
|
||||
#include "global_context.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
using namespace wxhelper;
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved) {
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
http_start(19088);
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
GlobalContext::GetInstance().initialize(hModule);
|
||||
break;
|
||||
}
|
||||
case DLL_THREAD_ATTACH: {
|
||||
@ -16,8 +20,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
||||
break;
|
||||
}
|
||||
case DLL_PROCESS_DETACH: {
|
||||
CloseConsole();
|
||||
http_close();
|
||||
GlobalContext::GetInstance().finally();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
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