mirror of
https://github.com/ttttupup/wxhelper.git
synced 2025-04-20 03:49:17 +08:00
Compare commits
3 Commits
main
...
3.9.0.28-s
Author | SHA1 | Date | |
---|---|---|---|
|
4af7e3f58b | ||
|
1638a49dd4 | ||
|
41091b1c6f |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
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
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -30,6 +30,6 @@
|
|||||||
#*.exe
|
#*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
/out
|
||||||
CMakePresets.json
|
CMakePresets.json
|
||||||
.vscode
|
.vscode
|
||||||
out
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "spdlog"]
|
|
||||||
path = spdlog
|
|
||||||
url = https://github.com/gabime/spdlog
|
|
@ -1,48 +1,31 @@
|
|||||||
cmake_minimum_required(VERSION 3.0.0)
|
cmake_minimum_required(VERSION 3.0.0)
|
||||||
# include(ExternalProject)
|
|
||||||
project(wxhelper VERSION 1.0.0)
|
project(wxhelper VERSION 1.0.0)
|
||||||
enable_language(ASM_MASM)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# SET(CMAKE_ASM_NASM_FLAGS "-w0")
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D '_UNICODE' /D 'UNICODE'")
|
||||||
|
|
||||||
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 )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory(spdlog)
|
include_directories(${VCPKG_INSTALLED_DIR}/x86-windows/include)
|
||||||
add_subdirectory(source)
|
|
||||||
|
# add_subdirectory(3rd)
|
||||||
|
|
||||||
# find_package(spdlog CONFIG REQUIRED)
|
|
||||||
|
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
find_package(unofficial-mongoose CONFIG REQUIRED)
|
||||||
|
|
||||||
find_path(DETOURS_INCLUDE_DIRS "detours/detours.h")
|
add_library(wxhelper SHARED ${CPP_FILES} )
|
||||||
find_library(DETOURS_LIBRARY detours REQUIRED)
|
|
||||||
|
|
||||||
|
|
||||||
add_library(wxhelper SHARED ${CPP_FILES} ${ASM_FILES} )
|
|
||||||
|
|
||||||
|
|
||||||
|
#target_include_directories(wxhelper PUBLIC 3rd/mongoose )
|
||||||
|
|
||||||
target_link_libraries(wxhelper PRIVATE nlohmann_json::nlohmann_json)
|
target_link_libraries(wxhelper PRIVATE nlohmann_json::nlohmann_json)
|
||||||
target_link_libraries(wxhelper PRIVATE spdlog::spdlog spdlog::spdlog_header_only)
|
target_link_libraries(wxhelper PRIVATE unofficial::mongoose::mongoose)
|
||||||
target_link_libraries(wxhelper PRIVATE ${DETOURS_LIBRARY})
|
|
||||||
|
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(wxhelper PROPERTIES LINKER_LANGUAGE C
|
SET_TARGET_PROPERTIES(wxhelper PROPERTIES LINKER_LANGUAGE C
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
|
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
|
||||||
|
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
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
1633
doc/3.9.2.23.md
File diff suppressed because it is too large
Load Diff
1829
doc/3.9.5.81.md
1829
doc/3.9.5.81.md
File diff suppressed because it is too large
Load Diff
640
doc/postman.json
640
doc/postman.json
@ -1,640 +0,0 @@
|
|||||||
{
|
|
||||||
"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."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go_client/tcpserver"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
||||||
tcpserver.Listen(19099)
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
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
33
java_client/.gitignore
vendored
@ -1,33 +0,0 @@
|
|||||||
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/
|
|
@ -1,21 +0,0 @@
|
|||||||
环境为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 为消息处理接口,实现其中的方法即可
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
启动项目需要去修改配置文件的微信路径
|
|
@ -1,185 +0,0 @@
|
|||||||
<?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>
|
|
@ -1,22 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package com.example.wxhk.controller;
|
|
||||||
|
|
||||||
|
|
||||||
import org.dromara.hutool.log.Log;
|
|
||||||
|
|
||||||
public class WxMsgController {
|
|
||||||
|
|
||||||
protected static final Log log = Log.get();
|
|
||||||
|
|
||||||
|
|
||||||
void init() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package com.example.wxhk.infe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* http 响应
|
|
||||||
* @author wt
|
|
||||||
* @date 2023/06/01
|
|
||||||
*/
|
|
||||||
public interface Resp extends java.io.Serializable{
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,256 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
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("退出线程了");
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,158 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,151 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
wx.path=D:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe
|
|
||||||
wx.port=19088
|
|
||||||
spring.profiles.active=local
|
|
||||||
vertx.port=8080
|
|
Binary file not shown.
Binary file not shown.
@ -1,171 +0,0 @@
|
|||||||
<?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>
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
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)
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.example.wxhk;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
|
|
||||||
@SpringBootTest
|
|
||||||
class WxhkApplicationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void contextLoads() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@ -1,49 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
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}`)
|
|
||||||
})
|
|
@ -1,493 +0,0 @@
|
|||||||
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
595
python/client.py
@ -1,595 +0,0 @@
|
|||||||
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()
|
|
@ -1,51 +0,0 @@
|
|||||||
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()
|
|
@ -1,26 +0,0 @@
|
|||||||
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
|
|
@ -1,10 +0,0 @@
|
|||||||
### 常用的一些工具
|
|
||||||
|
|
||||||
|
|
||||||
client.py : 快速测试dll的http接口。
|
|
||||||
|
|
||||||
decrpt.py : 微信数据库解密工具。password 为dll个人信息里返回的dbkey。
|
|
||||||
|
|
||||||
http_server.py : 一个简单的http server,用来接收hook的消息。
|
|
||||||
|
|
||||||
tcpserver.py: 一个简单的tcp server,用来接收hook的消息。
|
|
@ -1,20 +0,0 @@
|
|||||||
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
659
source/getopt.h
@ -1,659 +0,0 @@
|
|||||||
#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
1170
source/injector.cc
File diff suppressed because it is too large
Load Diff
1
spdlog
1
spdlog
@ -1 +0,0 @@
|
|||||||
Subproject commit ad0e89cbfb4d0c1ce4d097e134eb7be67baebb36
|
|
755
src/api.cc
Normal file
755
src/api.cc
Normal file
@ -0,0 +1,755 @@
|
|||||||
|
#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
Normal file
80
src/api.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef API_H_
|
||||||
|
#define API_H_
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum WECHAT_HTTP_APISTag
|
||||||
|
{
|
||||||
|
// login check
|
||||||
|
WECHAT_IS_LOGIN = 0,
|
||||||
|
// self info
|
||||||
|
WECHAT_GET_SELF_INFO,
|
||||||
|
// send message
|
||||||
|
WECHAT_MSG_SEND_TEXT,
|
||||||
|
WECHAT_MSG_SEND_AT,
|
||||||
|
WECHAT_MSG_SEND_CARD,
|
||||||
|
WECHAT_MSG_SEND_IMAGE,
|
||||||
|
WECHAT_MSG_SEND_FILE,
|
||||||
|
WECHAT_MSG_SEND_ARTICLE,
|
||||||
|
WECHAT_MSG_SEND_APP,
|
||||||
|
// receive message
|
||||||
|
WECHAT_MSG_START_HOOK,
|
||||||
|
WECHAT_MSG_STOP_HOOK,
|
||||||
|
WECHAT_MSG_START_IMAGE_HOOK,
|
||||||
|
WECHAT_MSG_STOP_IMAGE_HOOK,
|
||||||
|
WECHAT_MSG_START_VOICE_HOOK,
|
||||||
|
WECHAT_MSG_STOP_VOICE_HOOK,
|
||||||
|
// contact
|
||||||
|
WECHAT_CONTACT_GET_LIST,
|
||||||
|
WECHAT_CONTACT_CHECK_STATUS,
|
||||||
|
WECHAT_CONTACT_DEL,
|
||||||
|
WECHAT_CONTACT_SEARCH_BY_CACHE,
|
||||||
|
WECHAT_CONTACT_SEARCH_BY_NET,
|
||||||
|
WECHAT_CONTACT_ADD_BY_WXID,
|
||||||
|
WECHAT_CONTACT_ADD_BY_V3,
|
||||||
|
WECHAT_CONTACT_ADD_BY_PUBLIC_ID,
|
||||||
|
WECHAT_CONTACT_VERIFY_APPLY,
|
||||||
|
WECHAT_CONTACT_EDIT_REMARK,
|
||||||
|
// chatroom
|
||||||
|
WECHAT_CHATROOM_GET_MEMBER_LIST,
|
||||||
|
WECHAT_CHATROOM_GET_MEMBER_NICKNAME,
|
||||||
|
WECHAT_CHATROOM_DEL_MEMBER,
|
||||||
|
WECHAT_CHATROOM_ADD_MEMBER,
|
||||||
|
WECHAT_CHATROOM_SET_ANNOUNCEMENT,
|
||||||
|
WECHAT_CHATROOM_SET_CHATROOM_NAME,
|
||||||
|
WECHAT_CHATROOM_SET_SELF_NICKNAME,
|
||||||
|
// database
|
||||||
|
WECHAT_DATABASE_GET_HANDLES,
|
||||||
|
WECHAT_DATABASE_BACKUP,
|
||||||
|
WECHAT_DATABASE_QUERY,
|
||||||
|
// version
|
||||||
|
WECHAT_SET_VERSION,
|
||||||
|
// log
|
||||||
|
WECHAT_LOG_START_HOOK,
|
||||||
|
WECHAT_LOG_STOP_HOOK,
|
||||||
|
// browser
|
||||||
|
WECHAT_BROWSER_OPEN_WITH_URL,
|
||||||
|
WECHAT_GET_PUBLIC_MSG,
|
||||||
|
WECHAT_MSG_FORWARD_MESSAGE,
|
||||||
|
WECHAT_GET_QRCODE_IMAGE,
|
||||||
|
WECHAT_GET_A8KEY,
|
||||||
|
WECHAT_MSG_SEND_XML,
|
||||||
|
WECHAT_LOGOUT,
|
||||||
|
WECHAT_GET_TRANSFER,
|
||||||
|
WECHAT_GET_CONTACT_ALL,
|
||||||
|
WECHAT_GET_CHATROOM_INFO,
|
||||||
|
WECHAT_GET_IMG_BY_NAME,
|
||||||
|
WECHAT_DO_OCR,
|
||||||
|
WECHAT_SEND_PAT_MSG,
|
||||||
|
WECHAT_SET_TOP_MSG,
|
||||||
|
WECHAT_REMOVE_TOP_MSG,
|
||||||
|
WECHAT_SNS_GET_FIRST_PAGE,
|
||||||
|
WECHAT_SNS_GET_NEXT_PAGE,
|
||||||
|
WECHAT_CONTACT_NAME,
|
||||||
|
WECHAT_ATTACH_DOWNLOAD,
|
||||||
|
} WECHAT_HTTP_APIS,
|
||||||
|
*PWECHAT_HTTP_APIS;
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) int http_start(int port);
|
||||||
|
extern "C" __declspec(dllexport) int http_close();
|
||||||
|
#endif
|
@ -1,4 +1,4 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
/*
|
/*
|
||||||
base64.cpp and base64.h
|
base64.cpp and base64.h
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
Version: 2.rc.08 (release candidate)
|
Version: 2.rc.08 (release candidate)
|
||||||
|
|
||||||
Copyright (C) 2004-2017, 2020, 2021 Ren<EFBFBD><EFBFBD> Nyffenegger
|
Copyright (C) 2004-2017, 2020, 2021 Ren¨¦ Nyffenegger
|
||||||
|
|
||||||
This source code is provided 'as-is', without any express or implied
|
This source code is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the author be held liable for any damages
|
warranty. In no event will the author be held liable for any damages
|
||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
Ren<EFBFBD><EFBFBD> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
Ren¨¦ Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ template <typename String>
|
|||||||
static std::string decode(String encoded_string, bool remove_linebreaks)
|
static std::string decode(String encoded_string, bool remove_linebreaks)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// decode(<EFBFBD><EFBFBD>) is templated so that it can be used with String = const std::string&
|
// decode(¡) is templated so that it can be used with String = const std::string&
|
||||||
// or std::string_view (requires at least C++17)
|
// or std::string_view (requires at least C++17)
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -309,4 +309,4 @@ std::string base64_decode(std::string_view s, bool remove_linebreaks)
|
|||||||
return decode(s, remove_linebreaks);
|
return decode(s, remove_linebreaks);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __cplusplus >= 201703L
|
#endif // __cplusplus >= 201703L
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//
|
//
|
||||||
// base64 encoding and decoding with C++.
|
// base64 encoding and decoding with C++.
|
||||||
// Version: 2.rc.08 (release candidate)
|
// Version: 2.rc.08 (release candidate)
|
||||||
//
|
//
|
||||||
@ -32,4 +32,4 @@ std::string base64_encode_mime(std::string_view s);
|
|||||||
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
|
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
|
||||||
#endif // __cplusplus >= 201703L
|
#endif // __cplusplus >= 201703L
|
||||||
|
|
||||||
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
|
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
|
||||||
|
418
src/chat_room.cc
Normal file
418
src/chat_room.cc
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
#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;
|
||||||
|
}
|
16
src/chat_room.h
Normal file
16
src/chat_room.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#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
Normal file
145
src/common.cc
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#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
Normal file
83
src/common.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#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
|
@ -1,14 +0,0 @@
|
|||||||
#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
17
src/config.h
@ -1,17 +0,0 @@
|
|||||||
#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
|
|
49
src/confirm_receipt.cc
Normal file
49
src/confirm_receipt.cc
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#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;
|
||||||
|
}
|
6
src/confirm_receipt.h
Normal file
6
src/confirm_receipt.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef CONFIRM_RECEIPT_H
|
||||||
|
#define CONFIRM_RECEIPT_H
|
||||||
|
|
||||||
|
int DoConfirmReceipt(wchar_t* wxid,wchar_t *transcationid, wchar_t *transferid);
|
||||||
|
|
||||||
|
#endif
|
192
src/contact.cc
Normal file
192
src/contact.cc
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#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;
|
||||||
|
}
|
15
src/contact.h
Normal file
15
src/contact.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#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
511
src/db.cc
@ -1,511 +0,0 @@
|
|||||||
#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
39
src/db.h
@ -1,39 +0,0 @@
|
|||||||
#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
|
|
160
src/db_operation.cc
Normal file
160
src/db_operation.cc
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#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;
|
||||||
|
}
|
22
src/db_operation.h
Normal file
22
src/db_operation.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#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,16 +1,12 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "global_context.h"
|
#include "api.h"
|
||||||
#include "utils.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace wxhelper;
|
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
||||||
LPVOID lpReserved) {
|
LPVOID lpReserved) {
|
||||||
switch (ul_reason_for_call) {
|
switch (ul_reason_for_call) {
|
||||||
case DLL_PROCESS_ATTACH: {
|
case DLL_PROCESS_ATTACH: {
|
||||||
DisableThreadLibraryCalls(hModule);
|
// http_start(19088);
|
||||||
GlobalContext::GetInstance().initialize(hModule);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DLL_THREAD_ATTACH: {
|
case DLL_THREAD_ATTACH: {
|
||||||
@ -20,9 +16,10 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DLL_PROCESS_DETACH: {
|
case DLL_PROCESS_DETACH: {
|
||||||
GlobalContext::GetInstance().finally();
|
CloseConsole();
|
||||||
|
http_close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
183
src/download.cc
Normal file
183
src/download.cc
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#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;
|
||||||
|
}
|
5
src/download.h
Normal file
5
src/download.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#ifndef DOWNLOAD_H_
|
||||||
|
#define DOWNLOAD_H_
|
||||||
|
|
||||||
|
int DoDownloadTask(ULONG64 msg_id);
|
||||||
|
#endif
|
@ -1,61 +0,0 @@
|
|||||||
;#################################################################
|
|
||||||
|
|
||||||
.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
|
|
@ -1,8 +0,0 @@
|
|||||||
#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
|
|
41
src/forward.cc
Normal file
41
src/forward.cc
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#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;
|
||||||
|
}
|
5
src/forward.h
Normal file
5
src/forward.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#ifndef FORWARD_H_
|
||||||
|
#define FORWARD_H_
|
||||||
|
|
||||||
|
int ForwardMsg(wchar_t *wxid, unsigned long long msgid);
|
||||||
|
#endif
|
7
src/framework.h
Normal file
7
src/framework.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef FRAMEWORK_H_
|
||||||
|
#define FRAMEWORK_H_
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#endif
|
318
src/get_db_handle.cc
Normal file
318
src/get_db_handle.cc
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#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 {};
|
||||||
|
}
|
11
src/get_db_handle.h
Normal file
11
src/get_db_handle.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#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
|
@ -1,41 +0,0 @@
|
|||||||
#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
|
|
@ -1,34 +0,0 @@
|
|||||||
#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
Normal file
231
src/hook_img.cc
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
#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;
|
||||||
|
}
|
9
src/hook_img.h
Normal file
9
src/hook_img.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#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
|
78
src/hook_log.cc
Normal file
78
src/hook_log.cc
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#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;
|
||||||
|
}
|
8
src/hook_log.h
Normal file
8
src/hook_log.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#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