Compare commits
38 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 |
@ -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
|
||||
|
||||
65
README.md
65
README.md
@ -1,25 +1,28 @@
|
||||
### Games and regions
|
||||
- **3rd**: glb/sea/tw/kr/jp **v7.0.0+**, cn **v7.0.0+**
|
||||
- **SR**: os/cn **v1.4.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**\*
|
||||
|
||||
You can expect newer versions to work immediately after release with the same jadeite binary if the version is specified with a + above.
|
||||
|
||||
### 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`).
|
||||
**Note:** you can test the experimental version-independent patching method for SR. See [#37](https://codeberg.org/mkrsym1/jadeite/issues/37).
|
||||
|
||||
**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.
|
||||
**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`).
|
||||
|
||||
**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).~~ **Unfortunately, it appears like the fix is no longer available, please use [Wine-GE-Proton](https://github.com/GloriousEggroll/wine-ge-custom) instead.**
|
||||
- **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:
|
||||
@ -29,40 +32,44 @@ Manual usage instructions:
|
||||
- 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
|
||||
|
||||
Binary file not shown.
@ -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
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
|
||||
@ -12,3 +22,5 @@ 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,15 +5,16 @@ 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',
|
||||
'src/tx.c'
|
||||
'src/tx.c',
|
||||
|
||||
'src/hi3/hi3.c',
|
||||
|
||||
'src/hsr/hsr.c'
|
||||
]
|
||||
|
||||
if fs.exists('src/core.c')
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
#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 "1.4.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[] = {
|
||||
{ 0xbae3646f, HSR_OS }, // os v1.4.0
|
||||
{ 0x9e5c2f80, HSR_CN } // cn v1.4.0
|
||||
};
|
||||
|
||||
#define JUMP_SIZE (6 + sizeof(void*))
|
||||
|
||||
// Temporarily hardcoded offset
|
||||
// v1.4.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(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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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,6 +1,5 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdll.h>
|
||||
#include <ace.h>
|
||||
#include <game.h>
|
||||
#include <core.h>
|
||||
@ -8,6 +7,9 @@
|
||||
#include <msg.h>
|
||||
#include <tx.h>
|
||||
|
||||
#define NTDLL_DYNAMIC_LINK_IMPL
|
||||
#include <ntdll.h>
|
||||
|
||||
#include <main.h>
|
||||
|
||||
HMODULE this_module;
|
||||
@ -73,10 +75,15 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
||||
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;
|
||||
|
||||
@ -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");
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
#include <pe.h>
|
||||
#include <main.h>
|
||||
#include <config.h>
|
||||
#include <utils.h>
|
||||
|
||||
#include <tx.h>
|
||||
|
||||
@ -15,30 +16,23 @@ void tx_table_file(struct game_data *game, wchar_t *buf) {
|
||||
GetTempPathW(MAX_PATH, tempDir);
|
||||
|
||||
// Memorymap the base module
|
||||
HANDLE baseFile = CreateFileA(game->base_module_name, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!baseFile) {
|
||||
msg_err_a("Could not open file: %s", game->base_module_name);
|
||||
}
|
||||
wchar_t baseModuleW[MAX_PATH];
|
||||
MultiByteToWideChar(CP_UTF8, 0, game->base_module_name, strlen(game->base_module_name) + 1, baseModuleW, MAX_PATH);
|
||||
|
||||
HANDLE hBaseMap = CreateFileMappingA(baseFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
char *baseMap = MapViewOfFile(hBaseMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!baseMap) {
|
||||
msg_err_a("Could not create file mapping for %s", game->base_module_name);
|
||||
}
|
||||
struct file_mapping map;
|
||||
utils_map_file(baseModuleW, &map);
|
||||
|
||||
// Checksum the TXS section
|
||||
IMAGE_SECTION_HEADER *txsSection = pe_find_section(baseMap, game->txs_section_name);
|
||||
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, baseMap + txsSection->PointerToRawData, txsSection->SizeOfRawData);
|
||||
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
|
||||
UnmapViewOfFile(baseMap);
|
||||
CloseHandle(hBaseMap);
|
||||
CloseHandle(baseFile);
|
||||
utils_unmap_file(&map);
|
||||
}
|
||||
|
||||
@ -5,31 +5,53 @@
|
||||
|
||||
#include <utils.h>
|
||||
|
||||
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) {
|
||||
HANDLE file = CreateFileW(filePath, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!file) {
|
||||
msg_err_w(L"Could not open file: %ls", 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_w(L"Could not create file mapping for %ls", filePath);
|
||||
}
|
||||
|
||||
uint32_t crc = crc32c(0, map, fileSize.QuadPart);
|
||||
|
||||
UnmapViewOfFile(map);
|
||||
CloseHandle(hMap);
|
||||
CloseHandle(file);
|
||||
uint32_t crc = crc32c(0, map.data, fileSize.QuadPart);
|
||||
|
||||
utils_unmap_file(&map);
|
||||
return crc;
|
||||
}
|
||||
|
||||
@ -51,8 +73,8 @@ void utils_create_parent_dirs(const wchar_t *path) {
|
||||
|
||||
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) {
|
||||
msg_err_w(L"Could not open file: %ls", filePath);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
msg_err_w(L"Could not create file: %ls", filePath);
|
||||
}
|
||||
|
||||
WriteFile(file, buf, length, NULL, FALSE);
|
||||
@ -64,3 +86,12 @@ 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");
|
||||
@ -81,7 +158,7 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
CREATE_SUSPENDED,
|
||||
INHERIT_PARENT_AFFINITY,
|
||||
NULL,
|
||||
workdir,
|
||||
&si,
|
||||
@ -94,10 +171,27 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
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);
|
||||
@ -106,15 +200,15 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
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;
|
||||
|
||||
@ -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: '3.0.10')
|
||||
project('jadeite', 'c', version: '3.1.1')
|
||||
|
||||
nasm = find_program('nasm')
|
||||
gen_res = find_program('gen_resources.sh')
|
||||
|
||||
@ -1,42 +1,42 @@
|
||||
{
|
||||
"jadeite": {
|
||||
"version": "3.0.10"
|
||||
"version": "3.1.1"
|
||||
},
|
||||
"games": {
|
||||
"hi3rd": {
|
||||
"global": {
|
||||
"status": "verified",
|
||||
"version": "7.0.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"sea": {
|
||||
"status": "verified",
|
||||
"version": "7.0.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"china": {
|
||||
"status": "verified",
|
||||
"version": "7.0.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"taiwan": {
|
||||
"status": "verified",
|
||||
"version": "7.0.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"korea": {
|
||||
"status": "verified",
|
||||
"version": "7.0.0"
|
||||
"version": "7.3.0"
|
||||
},
|
||||
"japan": {
|
||||
"status": "verified",
|
||||
"version": "7.0.0"
|
||||
"version": "7.3.0"
|
||||
}
|
||||
},
|
||||
"hsr": {
|
||||
"global": {
|
||||
"status": "verified",
|
||||
"version": "1.4.0"
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"china": {
|
||||
"status": "verified",
|
||||
"version": "1.4.0"
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user