Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9099d50ba8 | |||
| 7aa7047ccd | |||
| d0b966eac7 | |||
| 0903e39be2 | |||
| 46c0597492 | |||
| 1b32246f9a | |||
| cb3db372df | |||
| 2fee742f75 | |||
| b36d217284 | |||
| 8520356083 | |||
| 9bd1379244 | |||
| 7d7967f477 | |||
| 8550abdf9f | |||
| 912bc2e55f | |||
| f0da1b8456 | |||
| af0c685fb3 | |||
| 35cd117ffc | |||
| 9f011ff103 | |||
| ecbab96427 | |||
| 2da21065a6 | |||
| f8c4c5ad82 | |||
| 6b5e303df6 | |||
| 84e2c172a3 | |||
| 981a78ae53 | |||
| 55324bb5a8 | |||
| 211f40ae53 | |||
| deddf93d3f | |||
| c37d0e92f4 | |||
| fa3a3aa9cc | |||
| 186471508f | |||
| 1f0a6bc016 | |||
| da08b565f8 | |||
| ead6489e63 | |||
| 19d6139e13 | |||
| 0047d7b64f | |||
| 264cdc2528 | |||
| ab4615dfe9 | |||
| cf34ac6e01 | |||
| 6c7bbabced | |||
| 81ddaeeda7 | |||
| b9cae22212 | |||
| 8201e29e26 | |||
| 71144b4cd1 | |||
| 5af8d2463a | |||
| ea1d2f91f2 | |||
| f0ed718cf3 | |||
| bc09349b8e | |||
| 66e51c6d9a | |||
| bc7b24f4d9 | |||
| 9b55d4f160 | |||
| 57b2423a8a | |||
| e96bf23a30 | |||
| 9296e00edd | |||
| d22ded3d27 | |||
| 15c403b0d1 | |||
| 1cc16ecbd9 | |||
| ffe75e9de7 | |||
| 243e39d04b | |||
| daa5585527 | |||
| 8bf5aac0ec | |||
| 4c7dd302d5 | |||
| 63eed9d796 | |||
| 671f9ba246 | |||
| 868b4e27be | |||
| 56eefd83d7 | |||
| 3b2d33ad24 | |||
| ba6a054ec3 | |||
| 7693e63619 | |||
| 5d5d2117ec | |||
| ca65bbd70c | |||
| c17cf00409 | |||
| c80635fc71 | |||
| e9d2130105 | |||
| 0bfab4f682 | |||
| e0fcca3701 | |||
| 3b7cda6c5f | |||
| e066466339 | |||
| 612c2e74e6 | |||
| 4060fb5d4e | |||
| 54978e367c | |||
| e0d89875a0 | |||
| 89c6347315 | |||
| de15c00f2a | |||
| 694c734e67 | |||
| 0641006998 | |||
| 79cf7d20cc | |||
| 505d4b12dd | |||
| bb8d41c06d | |||
| b8aa6f968b | |||
| 9a3d623883 | |||
| 6b9f9b6d93 | |||
| 400729a3dc | |||
| 7eac309372 | |||
| 592ce62e6b | |||
| 4911f8d903 | |||
| 970561afb9 | |||
| dcb482ab8e | |||
| 5e2b015cc0 | |||
| 7beab899a6 | |||
| ac67488255 | |||
| 1375549216 | |||
| 19056bed0d |
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 mkrsym1 <mkrsym1@gmail.com>
|
||||
Copyright (c) 2023-2024 mkrsym1 <mkrsym1@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
73
README.md
73
README.md
@ -1,66 +1,75 @@
|
||||
### Games and regions
|
||||
- **3rd**: glb/sea/tw/kr/jp v6.7.0, cn v6.8.0
|
||||
- **SR**: os/cn v1.2.0 (potentially unsafe, but no bans were reported since v1.1.0)
|
||||
# Jadeite Autopatcher
|
||||
### Current game support:
|
||||
- **3rd**: glb/sea/tw/kr/jp **v7.3.0+**, cn **v7.3.0+**
|
||||
- **SR**: os/cn **v2.0.0**\*
|
||||
|
||||
It may be possilbe to completely remove the region and version-specific data in the future. Refer to the source code in `game_payload/src` for details.
|
||||
You can expect newer versions to work immediately after release with the same jadeite binary if the version is specified with a + above.
|
||||
|
||||
### Information
|
||||
**Note:** you can test the experimental version-independent patching method for SR. See [#37](https://codeberg.org/mkrsym1/jadeite/issues/37).
|
||||
|
||||
**Note:** the Steam version of 3rd was reported to work, however I neither tested it nor intend to support it, so it might break at any time. Use at your own risk.
|
||||
|
||||
## Information
|
||||
The anticheat the games use is fundamentally incompatible with Wine in multiple ways. This tool launches the game without it (`injector`) and imitates it's behaviour (`game_payload`).
|
||||
|
||||
**SR-specific**: this tool disables the use of DirectX shared resources in a rather hacky way. It is required, as there is no (and most likely never will be) shared resources support in DirectX translation layers (WineD3D/DXVK). Refer to [configuration](#configuration) if you wish to run the game without the fix.
|
||||
|
||||
**Using third-party software (such as this tool) with the games violates their Terms of Service**. Therefore, **you may receive a ban**. No bans were ever reported with 3rd, however the legacy patch for SR did cause many. **Use at your own risk and only if you understand all the possible consequences**.
|
||||
|
||||
**This is not a cheating tool**. Using it with Windows is not possible, and Windows support is not planned or intended in any way. However, as it does not perform any on-disk file modifications, you may reuse the same game install for Windows if you have a dual-boot setup.
|
||||
|
||||
### Usage
|
||||
**Refer to [third-party launchers](#third-party-launchers)** for convenient usage. If you don't want to (or can't) use third-party launchers, continue reading the section below.
|
||||
## Usage
|
||||
**Refer to [third-party launchers](#third-party-launchers)** for convenient usage. If you don't want to (or can't) use third-party launchers, continue reading the section below
|
||||
|
||||
**Wine 8.0+ is recommended**, as lower versions leak "The Wine project" as the device identifier. Not critical, but taking a precaution never hurt anyone. **DXVK is strongly recommended.**
|
||||
**Wine 8.0+ is recommended**, as lower versions leak "The Wine project" as the device identifier. Not critical, but taking a precaution never hurt anyone. **DXVK is strongly recommended**
|
||||
|
||||
**3rd-specific**: In some cases, and if you're not using Proton GE, **a fix for Media Foundation may be required to play videos. The Game may crash without it.** You can download it from [here](https://github.com/z0z0z/mf-install). You might need to [limit the number of cores available to the game](https://github.com/z0z0z/mf-install/issues/44) if your CPU has more than 8. **IMPORTANT: do not run the mfplat fix under Proton GE. Doing so may irreparably damage your game installation!**
|
||||
**3rd-specific**:
|
||||
- **Wine 8.21+ or [Wine-GE-Proton](https://github.com/GloriousEggroll/wine-ge-custom) is required to play videos. The game may show a black screen or crash on lower versions.**
|
||||
- You might need to [limit the number of cores available to the game](https://github.com/z0z0z/mf-install/issues/44) if your CPU has more than 8.
|
||||
|
||||
Manual usage instructions:
|
||||
- Download the game you want to run
|
||||
- Download a release from this repository
|
||||
- Download the latest release from this repository
|
||||
- Extract the archive (**NOT INTO THE GAME DIRECTORY! THIS IS IMPORTANT!**)
|
||||
- Block analytics servers in your `hosts` file. You can find the list in SERVERS.txt
|
||||
- Run `./block_analytics.sh` from the archive to block the games from accessing analytics servers (you might have to do a `chmod +x block_analytics.sh` first). This will require superuser privileges
|
||||
- Run `wine jadeite.exe 'Z:\wine\path\to\game.exe'`
|
||||
|
||||
This tool is capable of starting the games from a different process. This may be useful for spoofing the parent process (SR is known to report it). Use `wine jadeite.exe 'Z:\wine\path\to\game.exe' 'Z:\wine\path\to\launcher.exe'`. `explorer.exe` is used as the default.
|
||||
Detailed command line interface reference: `jadeite.exe [game path] <launcher path> <game args...>`, where:
|
||||
- `game path` - Wine path to the game (required)
|
||||
- `launcher path` - Wine path to the launcher process (optional, default is `C:\Windows\explorer.exe`). The launcher process will be used to start the game. You can specify the path to the official launcher here. Specifying anything other than `explorer.exe` or the official launcher is not recommended. Specify `--` to skip this argument and use the default
|
||||
- `game args...` - arguments to pass to the game process (optional)
|
||||
|
||||
To pass commandline arguments to the game, append them after the launcher path: `wine jadeite.exe 'Z:\wine\path\to\game.exe' 'Z:\wine\path\to\launcher.exe' -arg1 -arg2 -arg3`. To use the default launcher process, use `--`: `wine jadeite.exe 'Z:\wine\path\to\game.exe' -- -arg1 -arg2 -arg3`.
|
||||
Example command: `jadeite.exe 'Z:\path\to\game.exe' -- -screen-fullscreen 1`
|
||||
|
||||
### Configuration
|
||||
## Configuration
|
||||
These environment variables can be used to configure the behaviour of the tool. Any value except empty string counts as set. `1` will be used in all examples.
|
||||
|
||||
**Global**:
|
||||
- `WAIT_BEFORE_RESUME=1` - show a messagebox and wait for user input before resuming the game process. Useful on my side for debugging
|
||||
|
||||
**SR-exclusive**:
|
||||
- `SRFIX_DISABLE=1` - disable shared resources fix. Not recommended. Doing so will most likely cause the game to not run at all
|
||||
**SR-specific**:
|
||||
- `BREAK_CRYPTCAT=1` - use the experimental patching method. See [#37](https://codeberg.org/mkrsym1/jadeite/issues/37) for details
|
||||
|
||||
### Internals
|
||||
This tool consists of three parts: the main injector (`injector/src/exe.c`), the launcher payload (`injector/src/dll.c`) and the game payload (`game_payload`).
|
||||
|
||||
I am very bad at explaining, so just take a look at the source code. Maybe I'll write a detailed explanation in the future.
|
||||
## Internals and building
|
||||
To compile jadeite, you will need meson, mingw and nasm. You can probably install all three using your repository's package manager. Once all dependencies are installed, run `./build.sh` in this directory. The compiled files will be located in `./out`.
|
||||
|
||||
A part of the source code is witheld (`game_payload/src/core.c`). This is a forced measure to make abuse more difficult. However, a precompiled blob is provided in the repo. `build.sh` will use it automatically.
|
||||
|
||||
### Guildelines
|
||||
All source code except `core.c` is available in this repository. You can take look around.
|
||||
|
||||
## Guildelines
|
||||
1. **Please don't share this project in public.** This might attract unnecessary attention from either the Game Company or the Anticheat Company
|
||||
2. **Please don't abuse this project for cheating.** We're just trying to play the games through Wine
|
||||
|
||||
### Troubleshooting
|
||||
## Troubleshooting
|
||||
Please do not report any issues with the Game to the official channels. Use the issue tracker of this repository
|
||||
|
||||
### Third-party launchers
|
||||
- Honkers Launcher — Linux launcher for 3rd ([GitHub](https://github.com/an-anime-team/honkers-launcher) | [Codeberg](https://codeberg.org/an-anime-team/honkers-launcher))
|
||||
- The Honkers Railway Launcher — Linux launcher for SR ([GitHub](https://github.com/an-anime-team/the-honkers-railway-launcher) | [Codeberg](https://codeberg.org/an-anime-team/the-honkers-railway-launcher))
|
||||
## Third-party launchers
|
||||
- **Honkers Launcher** — Linux launcher for 3rd ([GitHub](https://github.com/an-anime-team/honkers-launcher) | [Codeberg](https://codeberg.org/an-anime-team/honkers-launcher))
|
||||
- **The Honkers Railway Launcher** — Linux launcher for SR ([GitHub](https://github.com/an-anime-team/the-honkers-railway-launcher) | [Codeberg](https://codeberg.org/an-anime-team/the-honkers-railway-launcher))
|
||||
|
||||
### Credits
|
||||
- mkrsym1 — project leader, reverse engineering
|
||||
- [EternalStudentDesuKa](https://github.com/EternalStudentDesuKa) — major help with analyzing network activity
|
||||
- [An Anime Team](https://github.com/an-anime-team) — Honkers Launcher and The Honkers Railway Launcher
|
||||
- Some others credited in the source code
|
||||
## Credits
|
||||
- **mkrsym1** — project leader, reverse engineering
|
||||
- **[EternalStudentDesuKa](https://github.com/EternalStudentDesuKa)** — major help with analyzing network activity
|
||||
- **[An Anime Team](https://github.com/an-anime-team)** — Honkers Launcher and The Honkers Railway Launcher
|
||||
- Some others (credited in the source code)
|
||||
|
||||
License: MIT
|
||||
|
||||
12
SERVERS.txt
12
SERVERS.txt
@ -1,12 +0,0 @@
|
||||
# Honkai Impact 3rd logging servers:
|
||||
0.0.0.0 log-upload-os.hoyoverse.com
|
||||
0.0.0.0 sg-public-data-api.hoyoverse.com
|
||||
0.0.0.0 dump.gamesafe.qq.com
|
||||
|
||||
# Honkai Star Rail logging servers (oversea)
|
||||
0.0.0.0 log-upload-os.hoyoverse.com
|
||||
0.0.0.0 sg-public-data-api.hoyoverse.com
|
||||
|
||||
# Honkai Star Rail logging servers (China)
|
||||
0.0.0.0 log-upload.mihoyo.com
|
||||
0.0.0.0 public-data-api.mihoyo.com
|
||||
36
block_analytics.sh
Normal file
36
block_analytics.sh
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
analytics_servers=$(cat <<EOF
|
||||
|
||||
# Honkai Impact 3rd analytics servers (glb/sea/tw/kr/jp):
|
||||
0.0.0.0 log-upload-os.hoyoverse.com
|
||||
0.0.0.0 sg-public-data-api.hoyoverse.com
|
||||
0.0.0.0 dump.gamesafe.qq.com
|
||||
|
||||
# Honkai Impact 3rd analytics servers (cn):
|
||||
0.0.0.0 log-upload.mihoyo.com
|
||||
0.0.0.0 public-data-api.mihoyo.com
|
||||
0.0.0.0 dump.gamesafe.qq.com
|
||||
|
||||
|
||||
# Honkai Star Rail analytics servers (os)
|
||||
0.0.0.0 log-upload-os.hoyoverse.com
|
||||
0.0.0.0 sg-public-data-api.hoyoverse.com
|
||||
|
||||
# Honkai Star Rail analytics servers (cn)
|
||||
0.0.0.0 log-upload.mihoyo.com
|
||||
0.0.0.0 public-data-api.mihoyo.com
|
||||
|
||||
EOF
|
||||
)
|
||||
|
||||
if [[ ! `cat /etc/hosts` == *"$analytics_servers"* ]]; then
|
||||
echo "Blocking analytics servers. This will require superuser privileges"
|
||||
echo "$analytics_servers" | pkexec tee -a /etc/hosts 2>&1 >> /dev/null
|
||||
if test $? -ne 0; then
|
||||
echo "Could not block analytics servers. Please add the following lines to your /etc/hosts manually:"
|
||||
echo "$analytics_servers"
|
||||
fi
|
||||
else
|
||||
echo "Analytics servers are already blocked"
|
||||
fi
|
||||
1
build.sh
1
build.sh
@ -14,6 +14,7 @@ mkdir out
|
||||
cp ./build/injector/jadeite.exe ./out
|
||||
cp ./build/injector/launcher_payload.dll ./out
|
||||
cp ./build/game_payload/game_payload.dll ./out
|
||||
cp ./block_analytics.sh ./out
|
||||
cp ./LICENSE.txt ./out
|
||||
|
||||
$strip ./out/*.{exe,dll}
|
||||
|
||||
Binary file not shown.
@ -2,7 +2,9 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
void ace_fake_driver_files();
|
||||
|
||||
HMODULE ace_load_base_module(const char *exeName);
|
||||
HMODULE ace_load_base_module(struct game_data *game);
|
||||
HMODULE ace_load_driver_module();
|
||||
|
||||
1
game_payload/include/config.h.in
Normal file
1
game_payload/include/config.h.in
Normal file
@ -0,0 +1 @@
|
||||
#define JADEITE_VERSION "@version@"
|
||||
@ -4,4 +4,6 @@
|
||||
|
||||
#include <game.h>
|
||||
|
||||
void core_setup_patcher(struct game_data *game, HMODULE baseModule);
|
||||
void core_setup_patcher(struct game_data *game, HMODULE baseModule, wchar_t *txFile);
|
||||
|
||||
void *core_perform_tx(struct game_data *game, size_t *outLength);
|
||||
|
||||
@ -8,11 +8,13 @@
|
||||
/* CRC-32C (iSCSI) polynomial in reversed bit order. */
|
||||
#define __POLY 0x82f63b78
|
||||
|
||||
static inline uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len) {
|
||||
static inline uint32_t crc32c(uint32_t crc, const void *buf, size_t len) {
|
||||
const unsigned char *cbuf = (const unsigned char*)buf;
|
||||
|
||||
crc = ~crc;
|
||||
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
crc ^= *cbuf++;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
crc = crc & 1 ? (crc >> 1) ^ __POLY : crc >> 1;
|
||||
}
|
||||
|
||||
@ -2,29 +2,14 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
enum game_id {
|
||||
GAME_INVALID,
|
||||
|
||||
GAME_HI3_GLB,
|
||||
GAME_HI3_SEA,
|
||||
GAME_HI3_CN,
|
||||
GAME_HI3_TW,
|
||||
GAME_HI3_KR,
|
||||
GAME_HI3_JP,
|
||||
|
||||
GAME_HSR_OS,
|
||||
GAME_HSR_CN
|
||||
};
|
||||
|
||||
#define INVOKE_CALLBACK(callback, ...) if (callback) { callback(__VA_ARGS__); }
|
||||
|
||||
typedef void (*unityplayer_callback_t)(HMODULE unityModule);
|
||||
|
||||
struct game_data {
|
||||
enum game_id id; // Temporary
|
||||
const char *name;
|
||||
const char *assembly_name;
|
||||
const char *tp6_section_name; // Unused for now
|
||||
const char *base_module_name;
|
||||
const char *assembly_path;
|
||||
const char *txs_section_name;
|
||||
const char *tvm_section_name;
|
||||
|
||||
unityplayer_callback_t unityplayer_callback;
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define ISSUE_SUFFIX "Please open an issue on the jadeite repository specifying your game edition/region and version"
|
||||
|
||||
void unload_ctr_inc();
|
||||
void unload_ctr_dec();
|
||||
|
||||
void request_restart();
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
void msg_err_a(const char *format, ...);
|
||||
void msg_err_w(const wchar_t *format, ...);
|
||||
void __attribute__((noreturn)) msg_err_a(const char *format, ...);
|
||||
void __attribute__((noreturn)) msg_err_w(const wchar_t *format, ...);
|
||||
|
||||
void msg_warn_a(const char *format, ...);
|
||||
void msg_warn_w(const wchar_t *format, ...);
|
||||
|
||||
@ -27,10 +27,32 @@ typedef union _LDR_DLL_NOTIFICATION_DATA {
|
||||
|
||||
typedef void (*LdrDllNotification_t)(ULONG reason, const PLDR_DLL_NOTIFICATION_DATA data, void *context);
|
||||
|
||||
typedef NTSTATUS (*LdrRegisterDllNotification_t)(ULONG flags, LdrDllNotification_t notificationFunction, void *context, void **cookie);
|
||||
typedef NTSTATUS (*LdrUnregisterDllNotification_t)(void *cookie);
|
||||
#define DYNAMIC_FN_TYPE(ret, name, args) typedef ret (*name##_t)args
|
||||
|
||||
extern LdrRegisterDllNotification_t LdrRegisterDllNotification;
|
||||
extern LdrUnregisterDllNotification_t LdrUnregisterDllNotification;
|
||||
#ifdef NTDLL_DYNAMIC_LINK_IMPL
|
||||
#define DYNAMIC_FN_VAR(name) extern name##_t name; name##_t name
|
||||
#else
|
||||
#define DYNAMIC_FN_VAR(name) extern name##_t name
|
||||
#endif
|
||||
|
||||
void ntdll_link();
|
||||
#define DYNAMIC_FN_DEF(ret, name, args) DYNAMIC_FN_TYPE(ret, name, args); DYNAMIC_FN_VAR(name)
|
||||
|
||||
DYNAMIC_FN_DEF(NTSTATUS, LdrRegisterDllNotification, (ULONG flags, LdrDllNotification_t notification, void *context, void **cookie));
|
||||
DYNAMIC_FN_DEF(NTSTATUS, LdrUnregisterDllNotification, (void *cookie));
|
||||
|
||||
#ifdef NTDLL_DYNAMIC_LINK_IMPL
|
||||
#define DYNAMIC_FN_LINK(module, name) name = (name##_t)GetProcAddress(module, #name)
|
||||
|
||||
static void _ntdll_link() {
|
||||
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
||||
|
||||
DYNAMIC_FN_LINK(ntdll, LdrRegisterDllNotification);
|
||||
DYNAMIC_FN_LINK(ntdll, LdrUnregisterDllNotification);
|
||||
}
|
||||
|
||||
#undef DYNAMIC_FN_LINK
|
||||
#endif
|
||||
|
||||
#undef DYNAMIC_FN_TYPE
|
||||
#undef DYNAMIC_FN_VAR
|
||||
#undef DYNAMIC_FN_DEF
|
||||
|
||||
@ -2,5 +2,6 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATION *buf);
|
||||
IMAGE_SECTION_HEADER *pe_find_section(const void *module, const char *section);
|
||||
|
||||
void *pe_find_entry_point(HMODULE module);
|
||||
|
||||
3
game_payload/include/tx.h
Normal file
3
game_payload/include/tx.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include <game.h>
|
||||
|
||||
void tx_table_file(struct game_data *game, wchar_t *buf);
|
||||
@ -1,7 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t utils_file_crc32c(const char *filePath);
|
||||
#define UTILS_COUNT(arr) (sizeof(arr) / sizeof(*arr))
|
||||
|
||||
struct file_mapping {
|
||||
HANDLE file;
|
||||
HANDLE mapping;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
void utils_map_file(const wchar_t *path, struct file_mapping *map);
|
||||
void utils_unmap_file(struct file_mapping *map);
|
||||
|
||||
int utils_path_exists(const wchar_t *filePath);
|
||||
uint32_t utils_file_crc32c(const wchar_t *filePath);
|
||||
|
||||
void utils_create_parent_dirs(const wchar_t *path);
|
||||
|
||||
void utils_save_to_file(const wchar_t *filePath, const void *buf, size_t length);
|
||||
|
||||
char utils_env_enabled(const char *env);
|
||||
|
||||
void utils_write_protected_memory(void *addr, const void *buf, size_t size);
|
||||
|
||||
@ -5,47 +5,23 @@ include_dir = include_directories('include')
|
||||
# Input files
|
||||
sources = [
|
||||
'src/main.c',
|
||||
'src/ntdll.c',
|
||||
'src/ace.c',
|
||||
'src/pe.c',
|
||||
'src/game.c',
|
||||
'src/hi3.c',
|
||||
'src/hsr.c',
|
||||
'src/utils.c',
|
||||
'src/msg.c'
|
||||
]
|
||||
resources = [
|
||||
'res/hi3/glb.dat',
|
||||
'res/hi3/sea.dat',
|
||||
'res/hi3/cn.dat',
|
||||
'res/hi3/tw.dat',
|
||||
'res/hi3/kr.dat',
|
||||
'res/hi3/jp.dat',
|
||||
'src/msg.c',
|
||||
'src/tx.c',
|
||||
|
||||
'res/hsr/os.dat',
|
||||
'res/hsr/cn.dat'
|
||||
]
|
||||
'src/hi3/hi3.c',
|
||||
|
||||
# Generate resource files for ./res
|
||||
res_header = custom_target(
|
||||
'resources.h',
|
||||
output: 'resources.h',
|
||||
input: resources,
|
||||
command: [ gen_res, '--header', meson.current_source_dir(), '@OUTPUT0@', '@INPUT@' ]
|
||||
)
|
||||
res_object = custom_target(
|
||||
'resources.o',
|
||||
output: 'resources.o',
|
||||
input: resources,
|
||||
command: [ gen_res, '--object', meson.current_source_dir(), '@OUTPUT0@', '@INPUT@' ]
|
||||
)
|
||||
'src/hsr/hsr.c'
|
||||
]
|
||||
|
||||
if fs.exists('src/core.c')
|
||||
# Compile the real file first (dirty hack)
|
||||
core_fake_exe = executable(
|
||||
'core.o',
|
||||
'src/core.c',
|
||||
res_header,
|
||||
link_args: [ '-r' ], # Output an object file
|
||||
include_directories: include_dir
|
||||
)
|
||||
@ -70,12 +46,16 @@ else
|
||||
core_blob = [ 'blob/core.o' ]
|
||||
endif
|
||||
|
||||
conf_data = configuration_data()
|
||||
conf_data.set('version', meson.project_version())
|
||||
|
||||
conf = configure_file(input: 'include/config.h.in', output: 'config.h', configuration: conf_data)
|
||||
|
||||
shared_library(
|
||||
'game_payload',
|
||||
sources,
|
||||
res_header,
|
||||
res_object,
|
||||
core_target,
|
||||
conf,
|
||||
objects: core_blob,
|
||||
include_directories: include_dir,
|
||||
name_prefix: ''
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
#include <ntdll.h>
|
||||
#include <pe.h>
|
||||
#include <msg.h>
|
||||
#include <utils.h>
|
||||
|
||||
#include <ace.h>
|
||||
|
||||
@ -23,14 +24,7 @@ static void _dll_notification(ULONG reason, const PLDR_DLL_NOTIFICATION_DATA dat
|
||||
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
|
||||
0xC3 // ret
|
||||
};
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(entryPoint, sizeof(ENTRY_POINT_STUB), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
memcpy(entryPoint, ENTRY_POINT_STUB, sizeof(ENTRY_POINT_STUB));
|
||||
|
||||
VirtualProtect(entryPoint, sizeof(ENTRY_POINT_STUB), oldProtect, &oldProtect);
|
||||
|
||||
utils_write_protected_memory(entryPoint, ENTRY_POINT_STUB, sizeof(ENTRY_POINT_STUB));
|
||||
}
|
||||
|
||||
static void _create_driver_file(const char *path) {
|
||||
@ -70,10 +64,9 @@ static HMODULE _load_module_patched(wchar_t *path) {
|
||||
return module;
|
||||
}
|
||||
|
||||
HMODULE ace_load_base_module(const char *exeName) {
|
||||
HMODULE ace_load_base_module(struct game_data *game) {
|
||||
wchar_t baseModuleName[MAX_PATH];
|
||||
swprintf(baseModuleName, MAX_PATH, L"%sBase.dll", exeName);
|
||||
wcslwr(baseModuleName);
|
||||
MultiByteToWideChar(CP_UTF8, 0, game->base_module_name, strlen(game->base_module_name) + 1, baseModuleName, MAX_PATH);
|
||||
|
||||
return _load_module_patched(baseModuleName);
|
||||
}
|
||||
|
||||
@ -16,3 +16,16 @@
|
||||
### 2.0.0
|
||||
- Almost a full rewrite, functionality unchanged
|
||||
- Added support for HI3 sea/cn/tw/jp/kr
|
||||
|
||||
### 3.0.0
|
||||
- Integrated table extractor
|
||||
|
||||
### 3.0.1
|
||||
- Fixed a bug that caused HI3 to crash
|
||||
|
||||
### 3.0.2
|
||||
- Fixed multiple error messageboxes showing invalid characters
|
||||
- Added handling for more error conditions
|
||||
|
||||
### 3.0.4
|
||||
- Moved LoadLibrary call into core from main
|
||||
|
||||
@ -1,32 +1,39 @@
|
||||
#include <msg.h>
|
||||
#include <utils.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
typedef void (*fill_fn)(struct game_data *buf);
|
||||
|
||||
struct name_fn_pair {
|
||||
const char *name;
|
||||
const wchar_t *name;
|
||||
fill_fn fill;
|
||||
};
|
||||
|
||||
const struct name_fn_pair GAMES[] = {
|
||||
{ "bh3.exe", &hi3_fill_data },
|
||||
{ "starrail.exe", &hsr_fill_data }
|
||||
{ L"BH3", &hi3_fill_data },
|
||||
{ L"StarRail", &hsr_fill_data }
|
||||
};
|
||||
|
||||
void game_detect(struct game_data *buf) {
|
||||
char exePath[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, exePath, MAX_PATH);
|
||||
wchar_t exePath[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, exePath, MAX_PATH);
|
||||
|
||||
char *exeName = strrchr(exePath, '\\') + 1;
|
||||
strlwr(exeName);
|
||||
// Leave only the basename
|
||||
wchar_t *exeName = wcsrchr(exePath, L'\\') + 1;
|
||||
|
||||
for (size_t i = 0; i < sizeof(GAMES) / sizeof(struct name_fn_pair); i++) {
|
||||
if (strcmp(exeName, GAMES[i].name) == 0) {
|
||||
// Cut off extension (.exe)
|
||||
wchar_t *extensionDot = wcsrchr(exeName, L'.');
|
||||
if (extensionDot != NULL) {
|
||||
*extensionDot = L'\0';
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < UTILS_COUNT(GAMES); i++) {
|
||||
if (wcsicmp(exeName, GAMES[i].name) == 0) {
|
||||
GAMES[i].fill(buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
msg_err_a("Unknown game: %s", exeName);
|
||||
msg_err_w(L"Unknown game: %ls", exeName);
|
||||
}
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
#include <utils.h>
|
||||
#include <msg.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
const char *HI3_NAME = "BH3";
|
||||
const char *HI3_ASSEMBLY_NAME = "UserAssembly.dll";
|
||||
const char *HI3_TP6_SECTION_NAME = ".bh3";
|
||||
const char *HI3_TVM_SECTION_NAME = ".tvm0";
|
||||
|
||||
struct crc_id_pair {
|
||||
uint32_t crc;
|
||||
enum game_id id;
|
||||
};
|
||||
|
||||
const struct crc_id_pair HI3_REGIONS[] = {
|
||||
// It may be possible to get rid of region-specific data altogether in the future
|
||||
|
||||
{ 0x45221647, GAME_HI3_GLB }, // glb v6.7.0
|
||||
{ 0x32f97d39, GAME_HI3_SEA }, // sea v6.7.0
|
||||
{ 0x2efd9099, GAME_HI3_CN }, // cn v6.8.0
|
||||
{ 0x4a47e6c8, GAME_HI3_TW }, // tw v6.7.0
|
||||
{ 0x03b67cb4, GAME_HI3_KR }, // kr v6.7.0
|
||||
{ 0x449ff855, GAME_HI3_JP } // jp v6.7.0
|
||||
};
|
||||
|
||||
void hi3_fill_data(struct game_data *buf) {
|
||||
uint32_t crc = utils_file_crc32c("UnityPlayer.dll");
|
||||
|
||||
enum game_id id = GAME_INVALID;
|
||||
for (size_t i = 0; i < sizeof(HI3_REGIONS) / sizeof(struct crc_id_pair); i++) {
|
||||
if (HI3_REGIONS[i].crc == crc) {
|
||||
id = HI3_REGIONS[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == GAME_INVALID) {
|
||||
msg_err_a("Invalid UnityPlayer.dll checksum: %x", crc);
|
||||
}
|
||||
|
||||
buf->id = id;
|
||||
buf->name = HI3_NAME;
|
||||
buf->assembly_name = HI3_ASSEMBLY_NAME;
|
||||
buf->tp6_section_name = HI3_TP6_SECTION_NAME;
|
||||
buf->tvm_section_name = HI3_TVM_SECTION_NAME;
|
||||
|
||||
buf->unityplayer_callback = NULL;
|
||||
}
|
||||
21
game_payload/src/hi3/hi3.c
Normal file
21
game_payload/src/hi3/hi3.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include <utils.h>
|
||||
#include <msg.h>
|
||||
#include <pe.h>
|
||||
#include <main.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
const char *HI3_BASE_MODULE_NAME = "BH3Base.dll";
|
||||
const char *HI3_ASSEMBLY_PATH = "BH3_Data\\Native\\UserAssembly.dll";
|
||||
const char *HI3_TXS_SECTION_NAME = ".ace";
|
||||
const char *HI3_TVM_SECTION_NAME = ".tvm0";
|
||||
|
||||
|
||||
void hi3_fill_data(struct game_data *buf) {
|
||||
buf->base_module_name = HI3_BASE_MODULE_NAME;
|
||||
buf->assembly_path = HI3_ASSEMBLY_PATH;
|
||||
buf->txs_section_name = HI3_TXS_SECTION_NAME;
|
||||
buf->tvm_section_name = HI3_TVM_SECTION_NAME;
|
||||
|
||||
buf->unityplayer_callback = NULL;
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
#include <utils.h>
|
||||
#include <msg.h>
|
||||
#include <main.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
const char *HSR_NAME = "StarRail";
|
||||
const char *HSR_ASSEMBLY_NAME = "GameAssembly.dll";
|
||||
const char *HSR_TP6_SECTION_NAME = ".ace";
|
||||
const char *HSR_TVM_SECTION_NAME = ".tvm0";
|
||||
|
||||
struct crc_id_pair {
|
||||
uint32_t crc;
|
||||
enum game_id id;
|
||||
};
|
||||
|
||||
const struct crc_id_pair HSR_REGIONS[] = {
|
||||
// It may be possible to get rid of region-specific data altogether in the future
|
||||
|
||||
{ 0x9eb3084e, GAME_HSR_OS }, // os v1.2.0
|
||||
{ 0x14be07e9, GAME_HSR_CN } // cn v1.2.0
|
||||
};
|
||||
|
||||
#define JUMP_SIZE (6 + sizeof(void*))
|
||||
|
||||
// Temporarily hardcoded offset
|
||||
// v1.2.0, same for os and cn
|
||||
#define WTSUD_PATCH_OFFSET 0x16430
|
||||
|
||||
char wtsud_original_bytes[JUMP_SIZE];
|
||||
char *wtsud_patch_addr;
|
||||
|
||||
static void _wtsud_stub() {
|
||||
// Recover original bytes
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
memcpy(wtsud_patch_addr, wtsud_original_bytes, JUMP_SIZE);
|
||||
|
||||
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, oldProtect, &oldProtect);
|
||||
|
||||
unload_ctr_dec();
|
||||
}
|
||||
|
||||
static void _unityplayer_callback(HMODULE unityModule) {
|
||||
if (utils_env_enabled("SRFIX_DISABLE")) {
|
||||
msg_info_a("Shared resources fix disabled. The game may not work");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove dependency on shared resources by patching WriteTextureStatisticUserData
|
||||
unload_ctr_inc();
|
||||
|
||||
wtsud_patch_addr = ((char*)unityModule) + WTSUD_PATCH_OFFSET;
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
// Save original bytes
|
||||
memcpy(wtsud_original_bytes, wtsud_patch_addr, JUMP_SIZE);
|
||||
|
||||
// Write jump
|
||||
const char JUMP_INST[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp [$ + 6]
|
||||
memcpy(wtsud_patch_addr, JUMP_INST, sizeof(JUMP_INST));
|
||||
|
||||
// Write destination address
|
||||
void *destAddr = &_wtsud_stub;
|
||||
memcpy(wtsud_patch_addr + sizeof(JUMP_INST), &destAddr, sizeof(destAddr));
|
||||
|
||||
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
void hsr_fill_data(struct game_data *buf) {
|
||||
uint32_t crc = utils_file_crc32c("UnityPlayer.dll");
|
||||
|
||||
enum game_id id = GAME_INVALID;
|
||||
for (size_t i = 0; i < sizeof(HSR_REGIONS) / sizeof(struct crc_id_pair); i++) {
|
||||
if (HSR_REGIONS[i].crc == crc) {
|
||||
id = HSR_REGIONS[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == GAME_INVALID) {
|
||||
msg_err_a("Invalid UnityPlayer.dll checksum: %x", crc);
|
||||
}
|
||||
|
||||
buf->id = id;
|
||||
buf->name = HSR_NAME;
|
||||
buf->assembly_name = HSR_ASSEMBLY_NAME;
|
||||
buf->tp6_section_name = HSR_TP6_SECTION_NAME;
|
||||
buf->tvm_section_name = HSR_TVM_SECTION_NAME;
|
||||
|
||||
buf->unityplayer_callback = &_unityplayer_callback;
|
||||
}
|
||||
122
game_payload/src/hsr/hsr.c
Normal file
122
game_payload/src/hsr/hsr.c
Normal file
@ -0,0 +1,122 @@
|
||||
#include <utils.h>
|
||||
#include <msg.h>
|
||||
#include <main.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
const char *HSR_BASE_MODULE_NAME = "StarRailBase.dll";
|
||||
const char *HSR_ASSEMBLY_PATH = "GameAssembly.dll";
|
||||
const char *HSR_TXS_SECTION_NAME = ".ace";
|
||||
const char *HSR_TVM_SECTION_NAME = ".tvm0";
|
||||
|
||||
#define HSR_VERSION "2.0.0"
|
||||
|
||||
enum hsr_region {
|
||||
HSR_INVALID,
|
||||
HSR_OS,
|
||||
HSR_CN
|
||||
};
|
||||
|
||||
struct crc_region_pair {
|
||||
uint32_t crc;
|
||||
enum hsr_region id;
|
||||
};
|
||||
|
||||
const struct crc_region_pair HSR_REGIONS[] = {
|
||||
{ 0x5be3a560, HSR_OS }, // os v2.0.0
|
||||
{ 0x974e826f, HSR_CN } // cn v2.0.0
|
||||
};
|
||||
|
||||
#define JUMP_SIZE (6 + sizeof(void*))
|
||||
|
||||
// Temporarily hardcoded offset
|
||||
// v2.0.0, same for os and cn
|
||||
#define WTSUD_PATCH_OFFSET 0x16510
|
||||
|
||||
char wtsud_original_bytes[JUMP_SIZE];
|
||||
char *wtsud_patch_addr;
|
||||
|
||||
static void _wtsud_stub() {
|
||||
// Recover original bytes
|
||||
utils_write_protected_memory(wtsud_patch_addr, wtsud_original_bytes, JUMP_SIZE);
|
||||
|
||||
unload_ctr_dec();
|
||||
}
|
||||
|
||||
static void _unityplayer_callback(HMODULE unityModule) {
|
||||
if (utils_env_enabled("CHECKSUM_PATCH_DISABLE")) {
|
||||
msg_info_a("DirectX library verification patch disabled. The game will not work");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove check by patching WriteTextureStatisticUserData
|
||||
unload_ctr_inc();
|
||||
|
||||
wtsud_patch_addr = ((char*)unityModule) + WTSUD_PATCH_OFFSET;
|
||||
|
||||
// Save original bytes
|
||||
memcpy(wtsud_original_bytes, wtsud_patch_addr, JUMP_SIZE);
|
||||
|
||||
// Prepare payload
|
||||
char payload[JUMP_SIZE];
|
||||
|
||||
const char JUMP_INST[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp [$ + 6]
|
||||
memcpy(payload, JUMP_INST, sizeof(JUMP_INST));
|
||||
|
||||
void *destAddr = &_wtsud_stub;
|
||||
memcpy(payload + sizeof(JUMP_INST), &destAddr, sizeof(destAddr));
|
||||
|
||||
// Write payload
|
||||
utils_write_protected_memory(wtsud_patch_addr, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
static void _break_cryptcat() {
|
||||
const char STUB[] = {
|
||||
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
|
||||
0xC3 // ret
|
||||
};
|
||||
|
||||
const char *STUB_FUNCTIONS[] = {
|
||||
"CryptCATAdminEnumCatalogFromHash",
|
||||
"CryptCATCatalogInfoFromContext",
|
||||
"CryptCATAdminReleaseCatalogContext"
|
||||
};
|
||||
|
||||
HMODULE wintrust = LoadLibraryA("wintrust.dll");
|
||||
|
||||
for (size_t i = 0; i < UTILS_COUNT(STUB_FUNCTIONS); i++) {
|
||||
void *fn = GetProcAddress(wintrust, STUB_FUNCTIONS[i]);
|
||||
utils_write_protected_memory(fn, STUB, sizeof(STUB));
|
||||
}
|
||||
}
|
||||
|
||||
void hsr_fill_data(struct game_data *buf) {
|
||||
if (!utils_env_enabled("BREAK_CRYPTCAT")) {
|
||||
uint32_t crc = utils_file_crc32c(L"UnityPlayer.dll");
|
||||
|
||||
enum hsr_region id = HSR_INVALID;
|
||||
for (size_t i = 0; i < UTILS_COUNT(HSR_REGIONS); i++) {
|
||||
if (HSR_REGIONS[i].crc == crc) {
|
||||
id = HSR_REGIONS[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == HSR_INVALID) {
|
||||
msg_err_a("Invalid UnityPlayer.dll checksum: 0x%08x. This patch is intended to be used with HSR v" HSR_VERSION, crc);
|
||||
}
|
||||
|
||||
// WriteTextureStatisticUserData patch
|
||||
buf->unityplayer_callback = &_unityplayer_callback;
|
||||
} else {
|
||||
msg_warn_a("Using experimental patching method");
|
||||
_break_cryptcat();
|
||||
|
||||
buf->unityplayer_callback = NULL;
|
||||
}
|
||||
|
||||
buf->base_module_name = HSR_BASE_MODULE_NAME;
|
||||
buf->assembly_path = HSR_ASSEMBLY_PATH;
|
||||
buf->txs_section_name = HSR_TXS_SECTION_NAME;
|
||||
buf->tvm_section_name = HSR_TVM_SECTION_NAME;
|
||||
}
|
||||
@ -1,10 +1,14 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdll.h>
|
||||
#include <ace.h>
|
||||
#include <game.h>
|
||||
#include <core.h>
|
||||
#include <utils.h>
|
||||
#include <msg.h>
|
||||
#include <tx.h>
|
||||
|
||||
#define NTDLL_DYNAMIC_LINK_IMPL
|
||||
#include <ntdll.h>
|
||||
|
||||
#include <main.h>
|
||||
|
||||
@ -23,34 +27,77 @@ void unload_ctr_dec() {
|
||||
}
|
||||
}
|
||||
|
||||
void request_restart() {
|
||||
wchar_t restartFlagFile[MAX_PATH];
|
||||
GetTempPathW(MAX_PATH, restartFlagFile);
|
||||
wcscat(restartFlagFile, L"jadeite\\restart_flag");
|
||||
|
||||
HANDLE hRestartFlag = CreateFileW(restartFlagFile, FILE_WRITE_ACCESS, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
CloseHandle(hRestartFlag);
|
||||
}
|
||||
|
||||
static void _run_game(struct game_data *game, wchar_t *txFile) {
|
||||
// Create fake ACE driver files
|
||||
ace_fake_driver_files();
|
||||
|
||||
// Load both ACE modules
|
||||
HMODULE baseModule = ace_load_base_module(game);
|
||||
ace_load_driver_module();
|
||||
|
||||
// ...magic
|
||||
core_setup_patcher(game, baseModule, txFile);
|
||||
|
||||
// Load the UnityPlayer module and invoke the callback
|
||||
HMODULE unityModule = LoadLibraryA("UnityPlayer.dll");
|
||||
INVOKE_CALLBACK(game->unityplayer_callback, unityModule);
|
||||
}
|
||||
|
||||
static void _run_tx(struct game_data *game, wchar_t *txFile) {
|
||||
// ...more magic
|
||||
size_t tableSize;
|
||||
void *table = core_perform_tx(game, &tableSize);
|
||||
|
||||
// Save to file
|
||||
utils_create_parent_dirs(txFile);
|
||||
utils_save_to_file(txFile, table, tableSize);
|
||||
|
||||
// Cleanup
|
||||
free(table);
|
||||
|
||||
// The file should now exist: restart and launch the game
|
||||
request_restart();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
||||
// Only listen to attach
|
||||
if (reason != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LPWSTR targetExe = malloc(MAX_PATH);
|
||||
GetModuleFileNameW(NULL, targetExe, 0);
|
||||
SetCurrentDirectoryW(targetExe);
|
||||
free(targetExe);
|
||||
|
||||
this_module = instance;
|
||||
|
||||
// Dynamically link functions from ntdll
|
||||
ntdll_link();
|
||||
_ntdll_link();
|
||||
|
||||
// Detect which game the user is trying to run
|
||||
struct game_data game;
|
||||
game_detect(&game);
|
||||
|
||||
// Create fake ACE driver files
|
||||
ace_fake_driver_files();
|
||||
// Get required table file path
|
||||
wchar_t txFile[MAX_PATH];
|
||||
tx_table_file(&game, txFile);
|
||||
|
||||
// Load both ACE modules
|
||||
HMODULE baseModule = ace_load_base_module(game.name);
|
||||
ace_load_driver_module();
|
||||
|
||||
// ...magic
|
||||
core_setup_patcher(&game, baseModule);
|
||||
|
||||
// Load the UnityPlayer module and invoke the callback
|
||||
HMODULE unityModule = LoadLibraryA("UnityPlayer.dll");
|
||||
INVOKE_CALLBACK(game.unityplayer_callback, unityModule);
|
||||
if (utils_path_exists(txFile)) {
|
||||
_run_game(&game, txFile);
|
||||
} else {
|
||||
_run_tx(&game, txFile);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <config.h>
|
||||
|
||||
#include <msg.h>
|
||||
|
||||
@ -21,8 +22,8 @@
|
||||
suffix; \
|
||||
}
|
||||
|
||||
const char *TITLE_A = "Jadeite Autopatcher";
|
||||
const wchar_t *TITLE_W = L"Jadeite Autopatcher";
|
||||
const char *TITLE_A = "v" JADEITE_VERSION " Jadeite Autopatcher";
|
||||
const wchar_t *TITLE_W = L"v" JADEITE_VERSION " Jadeite Autopatcher";
|
||||
|
||||
// Error
|
||||
DEF_MSG_FN(msg_err_a, char, _vsnprintf, MessageBoxA, TITLE_A, MB_OK | MB_ICONERROR, exit(1))
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
#include <ntdll.h>
|
||||
|
||||
LdrRegisterDllNotification_t LdrRegisterDllNotification;
|
||||
LdrUnregisterDllNotification_t LdrUnregisterDllNotification;
|
||||
|
||||
void ntdll_link() {
|
||||
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
||||
|
||||
LdrRegisterDllNotification = (LdrRegisterDllNotification_t)GetProcAddress(ntdll, "LdrRegisterDllNotification");
|
||||
LdrUnregisterDllNotification = (LdrUnregisterDllNotification_t)GetProcAddress(ntdll, "LdrUnregisterDllNotification");
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
#include <pe.h>
|
||||
|
||||
void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATION *buf) {
|
||||
char *cModule = (char*)module;
|
||||
IMAGE_SECTION_HEADER *pe_find_section(const void *module, const char *section) {
|
||||
const char *cModule = (const char*)module;
|
||||
|
||||
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)module;
|
||||
IMAGE_NT_HEADERS64* ntHeaders = (IMAGE_NT_HEADERS64*)(cModule + dosHeader->e_lfanew);
|
||||
@ -9,17 +9,15 @@ void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATI
|
||||
WORD sectionCount = ntHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)(ntHeaders + 1);
|
||||
|
||||
void* targetAddress = 0x0;
|
||||
for (WORD i = 0; i < sectionCount; i++) {
|
||||
if (strncmp((char*)sectionHeader->Name, section, 8) == 0) {
|
||||
targetAddress = (void*)(cModule + sectionHeader->VirtualAddress);
|
||||
break;
|
||||
return sectionHeader;
|
||||
}
|
||||
|
||||
sectionHeader++;
|
||||
}
|
||||
|
||||
VirtualQuery(targetAddress, buf, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *pe_find_entry_point(HMODULE module) {
|
||||
|
||||
38
game_payload/src/tx.c
Normal file
38
game_payload/src/tx.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <crc32.h>
|
||||
#include <msg.h>
|
||||
#include <pe.h>
|
||||
#include <main.h>
|
||||
#include <config.h>
|
||||
#include <utils.h>
|
||||
|
||||
#include <tx.h>
|
||||
|
||||
void tx_table_file(struct game_data *game, wchar_t *buf) {
|
||||
// Get temp directory path
|
||||
wchar_t tempDir[MAX_PATH];
|
||||
GetTempPathW(MAX_PATH, tempDir);
|
||||
|
||||
// Memorymap the base module
|
||||
wchar_t baseModuleW[MAX_PATH];
|
||||
MultiByteToWideChar(CP_UTF8, 0, game->base_module_name, strlen(game->base_module_name) + 1, baseModuleW, MAX_PATH);
|
||||
|
||||
struct file_mapping map;
|
||||
utils_map_file(baseModuleW, &map);
|
||||
|
||||
// Checksum the TXS section
|
||||
IMAGE_SECTION_HEADER *txsSection = pe_find_section(map.data, game->txs_section_name);
|
||||
if (!txsSection) {
|
||||
msg_err_a("Could not find %s in %s. " ISSUE_SUFFIX, game->txs_section_name, game->base_module_name);
|
||||
}
|
||||
|
||||
uint32_t txsChecksum = crc32c(0, map.data + txsSection->PointerToRawData, txsSection->SizeOfRawData);
|
||||
|
||||
// Format the path
|
||||
wsprintfW(buf, L"%sjadeite\\" JADEITE_VERSION "\\%hs.%x.dat", tempDir, game->base_module_name, txsChecksum);
|
||||
|
||||
// Cleanup
|
||||
utils_unmap_file(&map);
|
||||
}
|
||||
@ -5,31 +5,93 @@
|
||||
|
||||
#include <utils.h>
|
||||
|
||||
uint32_t utils_file_crc32c(const char *filePath) {
|
||||
HANDLE file = CreateFileA(filePath, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!file) {
|
||||
msg_err_a("Could not open file: %s", filePath);
|
||||
void utils_map_file(const wchar_t *path, struct file_mapping *map) {
|
||||
wchar_t* final_path = malloc(MAX_PATH);
|
||||
if (wcsstr(path, L"C:\\") == NULL) {
|
||||
wchar_t* tmp = malloc(MAX_PATH);
|
||||
GetEnvironmentVariableW(L"GAME_PATH", tmp, MAX_PATH);
|
||||
swprintf(final_path, MAX_PATH, L"%ls\\%ls", tmp, path);
|
||||
free(tmp);
|
||||
} else {
|
||||
swprintf(final_path, MAX_PATH, L"%ls", path);
|
||||
}
|
||||
map->file = CreateFileW(final_path, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (map->file == INVALID_HANDLE_VALUE) {
|
||||
msg_err_w(L"Could not open file: %ls", final_path);
|
||||
}
|
||||
|
||||
map->mapping = CreateFileMappingA(map->file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
map->data = MapViewOfFile(map->mapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!map->data) {
|
||||
msg_err_w(L"Could not map view of file %ls", final_path);
|
||||
}
|
||||
free(final_path);
|
||||
}
|
||||
|
||||
void utils_unmap_file(struct file_mapping *map) {
|
||||
UnmapViewOfFile(map->data);
|
||||
CloseHandle(map->mapping);
|
||||
CloseHandle(map->file);
|
||||
}
|
||||
|
||||
int utils_path_exists(const wchar_t *filePath) {
|
||||
return GetFileAttributesW(filePath) != INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
uint32_t utils_file_crc32c(const wchar_t *filePath) {
|
||||
struct file_mapping map;
|
||||
// LPWSTR cwd = malloc(MAX_PATH);
|
||||
// GetCurrentDirectoryW(MAX_PATH, cwd);
|
||||
// msg_info_w(L"File %ls %ls", filePath, cwd);
|
||||
// free(cwd);
|
||||
utils_map_file(filePath, &map);
|
||||
|
||||
LARGE_INTEGER fileSize;
|
||||
GetFileSizeEx(file, &fileSize);
|
||||
GetFileSizeEx(map.file, &fileSize);
|
||||
|
||||
HANDLE hMap = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
char *map = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!map) {
|
||||
msg_err_a("Could not create file mapping for %s", filePath);
|
||||
uint32_t crc = crc32c(0, map.data, fileSize.QuadPart);
|
||||
|
||||
utils_unmap_file(&map);
|
||||
return crc;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/16719260
|
||||
void utils_create_parent_dirs(const wchar_t *path) {
|
||||
wchar_t dir[MAX_PATH];
|
||||
ZeroMemory(dir, sizeof(dir));
|
||||
|
||||
const wchar_t *end = path - 1;
|
||||
|
||||
while((end = wcschr(++end, L'\\')) != NULL) {
|
||||
wcsncpy(dir, path, end - path + 1);
|
||||
|
||||
if (!utils_path_exists(dir) && !CreateDirectoryW(dir, NULL)) {
|
||||
msg_err_w(L"Failed to create directory: %ls", dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void utils_save_to_file(const wchar_t *filePath, const void *buf, size_t length) {
|
||||
HANDLE file = CreateFileW(filePath, FILE_WRITE_ACCESS, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
msg_err_w(L"Could not create file: %ls", filePath);
|
||||
}
|
||||
|
||||
uint32_t crc = crc32c(0, (unsigned char*)map, fileSize.QuadPart);
|
||||
WriteFile(file, buf, length, NULL, FALSE);
|
||||
|
||||
UnmapViewOfFile(map);
|
||||
CloseHandle(hMap);
|
||||
CloseHandle(file);
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
char utils_env_enabled(const char *env) {
|
||||
char *envText = getenv(env);
|
||||
return envText && *envText;
|
||||
}
|
||||
|
||||
void utils_write_protected_memory(void *addr, const void *buf, size_t size) {
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(addr, size, PAGE_READWRITE, &oldProtect);
|
||||
|
||||
memcpy(addr, buf, size);
|
||||
|
||||
VirtualProtect(addr, size, oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <inject.h>
|
||||
#include <envs.h>
|
||||
|
||||
@ -9,6 +10,75 @@ typedef char *(*wgufn_t)(wchar_t* path); // wine_get_unix_file_name
|
||||
|
||||
const wchar_t *J_MB_TITLE = L"Jadeite Launcher Payload";
|
||||
|
||||
// Copied from https://cocomelonc.github.io/pentest/2021/09/29/findmyprocess.html
|
||||
// Find process ID by process name
|
||||
DWORD find_proc_id(const char *procname) {
|
||||
|
||||
HANDLE hSnapshot;
|
||||
PROCESSENTRY32 pe;
|
||||
DWORD pid = 0;
|
||||
BOOL hResult;
|
||||
|
||||
// snapshot of all processes in the system
|
||||
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
|
||||
|
||||
// initializing size: needed for using Process32First
|
||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
// info about first process encountered in a system snapshot
|
||||
hResult = Process32First(hSnapshot, &pe);
|
||||
|
||||
// retrieve information about the processes
|
||||
// and exit if unsuccessful
|
||||
while (hResult) {
|
||||
// if we find the process: return process ID
|
||||
if (strcmp(procname, pe.szExeFile) == 0) {
|
||||
pid = pe.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
hResult = Process32Next(hSnapshot, &pe);
|
||||
}
|
||||
|
||||
// closes an open handle (CreateToolhelp32Snapshot)
|
||||
CloseHandle(hSnapshot);
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Find thread ID by process PID
|
||||
int find_main_thread(DWORD pid) {
|
||||
|
||||
HANDLE hSnapshot;
|
||||
THREADENTRY32 pe;
|
||||
DWORD threadId = 0;
|
||||
BOOL hResult;
|
||||
|
||||
// snapshot of all processes in the system
|
||||
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
|
||||
|
||||
// initializing size: needed for using Process32First
|
||||
pe.dwSize = sizeof(THREADENTRY32);
|
||||
|
||||
// info about first process encountered in a system snapshot
|
||||
hResult = Thread32First(hSnapshot, &pe);
|
||||
|
||||
// retrieve information about the processes
|
||||
// and exit if unsuccessful
|
||||
while (hResult) {
|
||||
// if we find the process: return process ID
|
||||
if (pid == pe.th32OwnerProcessID) {
|
||||
threadId = pe.th32ThreadID;
|
||||
break;
|
||||
}
|
||||
hResult = Thread32Next(hSnapshot, &pe);
|
||||
}
|
||||
|
||||
// closes an open handle (CreateToolhelp32Snapshot)
|
||||
CloseHandle(hSnapshot);
|
||||
return threadId;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
// Only listen for attach
|
||||
if (reason != DLL_PROCESS_ATTACH) {
|
||||
@ -32,6 +102,13 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
wcscpy(workdir, targetExe);
|
||||
*(wcsrchr(workdir, L'\\')) = L'\0';
|
||||
|
||||
// Change the game's working directory
|
||||
LPWSTR game_path = malloc(MAX_PATH);
|
||||
GetEnvironmentVariableW(L"GAME_PATH", game_path, MAX_PATH);
|
||||
wchar_t message[64];
|
||||
wsprintfW(message, L"Game Path: %ls", game_path);
|
||||
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
|
||||
|
||||
// SAFETY: verify that the injector is not inside the game directory
|
||||
HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
|
||||
wgufn_t wine_get_unix_file_name = (wgufn_t)GetProcAddress(kernel32, "wine_get_unix_file_name");
|
||||
@ -61,12 +138,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
MessageBoxW(NULL, L"Could not find wine_get_unix_file_name! Wine version too old?", J_MB_TITLE, MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
|
||||
// Get restart flag file path
|
||||
wchar_t restartFlagFile[MAX_PATH];
|
||||
GetTempPathW(MAX_PATH, restartFlagFile);
|
||||
wcscat(restartFlagFile, L"jadeite\\restart_flag");
|
||||
|
||||
do {
|
||||
// Start the game
|
||||
STARTUPINFOW si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (!CreateProcessW(
|
||||
@ -75,37 +158,58 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
CREATE_SUSPENDED,
|
||||
INHERIT_PARENT_AFFINITY,
|
||||
NULL,
|
||||
workdir,
|
||||
&si,
|
||||
&pi
|
||||
)) {
|
||||
wchar_t message[1024];
|
||||
wsprintfW(message, L"Failed to start game process: %ld", GetLastError());
|
||||
wsprintfW(message, L"Failed to start game process: %ld\nGame executable path: '%ls'", GetLastError(), targetExe);
|
||||
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONERROR);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Find the game process
|
||||
wprintf(L"Waiting for game process to start...\n");
|
||||
DWORD game_pid = 0;
|
||||
while (!game_pid) {
|
||||
// wprintf(L"Looking for game process...\n");
|
||||
game_pid = find_proc_id("StarRail.exe");
|
||||
Sleep(1);
|
||||
}
|
||||
DWORD thread_id = 0;
|
||||
while (!thread_id) {
|
||||
// wprintf(L"Looking for game process...\n");
|
||||
thread_id = find_main_thread(game_pid);
|
||||
Sleep(1);
|
||||
}
|
||||
HANDLE game = OpenProcess(PROCESS_ALL_ACCESS, FALSE, game_pid);
|
||||
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id);
|
||||
SuspendThread(hThread);
|
||||
// Inject
|
||||
void *payloadStart = &_binary_game_p_o_p_game_p_bin_start;
|
||||
size_t payloadSize = (size_t)&_binary_game_p_o_p_game_p_bin_size;
|
||||
inject(pi.hProcess, payloadStart, payloadSize, injectDll);
|
||||
inject(game, payloadStart, payloadSize, injectDll);
|
||||
|
||||
// Remove the restart flag file
|
||||
DeleteFileW(restartFlagFile);
|
||||
|
||||
// Optional: wait for user input before resuming (useful for debugging)
|
||||
char *waitEnabled = getenv("WAIT_BEFORE_RESUME");
|
||||
if (waitEnabled && *waitEnabled) {
|
||||
wchar_t message[64];
|
||||
wsprintfW(message, L"PID: %ld. Press OK to continue", pi.dwProcessId);
|
||||
wsprintfW(message, L"PID: %ld. Thread ID: %ld. Press OK to continue", game_pid, thread_id);
|
||||
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
// Resume the process
|
||||
ResumeThread(pi.hThread);
|
||||
ResumeThread(hThread);
|
||||
|
||||
// The launcher process should now hang untill the game terminates
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
WaitForSingleObject(game, INFINITE);
|
||||
} while (GetFileAttributesW(restartFlagFile) != INVALID_FILE_ATTRIBUTES);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -85,9 +85,9 @@ int wmain(int argc, wchar_t **argv) {
|
||||
|
||||
STARTUPINFOW si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (!CreateProcessW(
|
||||
|
||||
@ -21,15 +21,12 @@ static inline void write_protected_process_memory(HANDLE process, void *address,
|
||||
DWORD oldProtect;
|
||||
VirtualProtectEx(process, address, size, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
size_t bytesWritten;
|
||||
WriteProcessMemory(process, address, buf, size, &bytesWritten);
|
||||
WriteProcessMemory(process, address, buf, size, NULL);
|
||||
|
||||
VirtualProtectEx(process, address, size, oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar_t *dllPath) {
|
||||
size_t _; // Contrary to the docs, {Write,Read}ProcessMemory likes to crash if the last arg is NULL
|
||||
|
||||
// Find the EXE header in the process
|
||||
char exeHeader[1024];
|
||||
IMAGE_DOS_HEADER *dosHeader = NULL;
|
||||
@ -38,7 +35,7 @@ void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar
|
||||
MEMORY_BASIC_INFORMATION memoryInfo;
|
||||
char *currentAddress = 0x0;
|
||||
while (VirtualQueryEx(process, currentAddress, &memoryInfo, sizeof(memoryInfo))) {
|
||||
ReadProcessMemory(process, currentAddress, exeHeader, sizeof(exeHeader), &_);
|
||||
ReadProcessMemory(process, currentAddress, exeHeader, sizeof(exeHeader), NULL);
|
||||
|
||||
dosHeader = (IMAGE_DOS_HEADER*)exeHeader;
|
||||
|
||||
@ -84,8 +81,8 @@ void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar
|
||||
char *remoteAlloc = VirtualAllocEx(process, NULL, allocSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
|
||||
// Write the assembly payload and dll path
|
||||
WriteProcessMemory(process, remoteAlloc, payload, payloadSize, &_);
|
||||
WriteProcessMemory(process, remoteAlloc + payloadSize, dllPath, dllPathSize, &_);
|
||||
WriteProcessMemory(process, remoteAlloc, payload, payloadSize, NULL);
|
||||
WriteProcessMemory(process, remoteAlloc + payloadSize, dllPath, dllPathSize, NULL);
|
||||
|
||||
|
||||
// Modify the executable to run the assembly payload
|
||||
@ -97,7 +94,7 @@ void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar
|
||||
|
||||
// Save the original entry point address and bytes
|
||||
rd.entryPointAddress = entryPoint;
|
||||
ReadProcessMemory(process, rd.entryPointAddress, rd.entryPointData, sizeof(rd.entryPointData), &_);
|
||||
ReadProcessMemory(process, rd.entryPointAddress, rd.entryPointData, sizeof(rd.entryPointData), NULL);
|
||||
|
||||
// Replace the entry point with a jump to the assembly payload
|
||||
write_protected_process_memory(process, entryPoint, JUMP_INST, sizeof(JUMP_INST));
|
||||
@ -110,7 +107,7 @@ void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar
|
||||
|
||||
// Save the original descriptor address and bytes
|
||||
rd.importDescriptorAddress = importDescriptors;
|
||||
ReadProcessMemory(process, rd.importDescriptorAddress, &rd.importDescriptorData, sizeof(rd.importDescriptorData), &_);
|
||||
ReadProcessMemory(process, rd.importDescriptorAddress, &rd.importDescriptorData, sizeof(rd.importDescriptorData), NULL);
|
||||
|
||||
// Overwrite with zeroes
|
||||
IMAGE_IMPORT_DESCRIPTOR firstDescriptor;
|
||||
@ -122,12 +119,12 @@ void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar
|
||||
|
||||
// Save the original value
|
||||
rd.sizeFieldAddress = ddAddr;
|
||||
ReadProcessMemory(process, rd.sizeFieldAddress, &rd.sizeFieldData, sizeof(rd.sizeFieldData), &_);
|
||||
ReadProcessMemory(process, rd.sizeFieldAddress, &rd.sizeFieldData, sizeof(rd.sizeFieldData), NULL);
|
||||
|
||||
// Set to 0
|
||||
DWORD newSize = 0;
|
||||
write_protected_process_memory(process, ddAddr, &newSize, sizeof(newSize));
|
||||
|
||||
// Write recovery data to the allocation
|
||||
WriteProcessMemory(process, remoteAlloc + payloadSize + dllPathSize, &rd, sizeof(rd), &_);
|
||||
WriteProcessMemory(process, remoteAlloc + payloadSize + dllPathSize, &rd, sizeof(rd), NULL);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
project('jadeite', 'c', version: '2.0.0')
|
||||
project('jadeite', 'c', version: '3.1.1')
|
||||
|
||||
nasm = find_program('nasm')
|
||||
gen_res = find_program('gen_resources.sh')
|
||||
|
||||
@ -1,42 +1,42 @@
|
||||
{
|
||||
"jadeite": {
|
||||
"version": "2.0.0"
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"games": {
|
||||
"hi3rd": {
|
||||
"global": {
|
||||
"status": "verified",
|
||||
"version": "6.7.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"sea": {
|
||||
"status": "verified",
|
||||
"version": "6.7.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"china": {
|
||||
"status": "verified",
|
||||
"version": "6.8.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"taiwan": {
|
||||
"status": "verified",
|
||||
"version": "6.7.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"korea": {
|
||||
"status": "verified",
|
||||
"version": "6.7.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"japan": {
|
||||
"status": "verified",
|
||||
"version": "6.7.0"
|
||||
"version": "7.3.0"
|
||||
}
|
||||
},
|
||||
"hsr": {
|
||||
"global": {
|
||||
"status": "verified",
|
||||
"version": "1.2.0"
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"china": {
|
||||
"status": "verified",
|
||||
"version": "1.2.0"
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user