mirror of
https://git.xeondev.com/HonkaiSlopRail/sr-launcher.git
synced 2026-01-02 11:56:05 +08:00
Supports CNBETA 3.7.51
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.direnv/
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
1
assets/sdk_public_key.xml
Normal file
1
assets/sdk_public_key.xml
Normal file
@@ -0,0 +1 @@
|
||||
<RSAKeyValue><Exponent>AQAB</Exponent><Modulus>hEegnKISgDas5VTuRBUlixB+bvmPvXKa3kVO22UEZjPGMUFLmIl3DhH+dsZo7qJn/GfJCUkP1FA0MJ5Bj8PX8IatLJKIJ9dMCNdnAlkXTlMg86QQAhHZN83vP4swj5ILcrGNKl3YAZ49fvzo7nheuTt0/40f0HkHdNa1dUHECBs=</Modulus></RSAKeyValue>
|
||||
35
build.zig
Normal file
35
build.zig
Normal file
@@ -0,0 +1,35 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const target = b.standardTargetOptions(.{ .default_target = .{ .os_tag = .windows } });
|
||||
|
||||
const launcher = b.addExecutable(.{
|
||||
.name = "cyrene",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("injector.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
|
||||
const dll = b.addLibrary(.{
|
||||
.name = "cyrene",
|
||||
.linkage = .dynamic,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{.{ .name = "zigzag", .module = b.dependency("zigzag", .{}).module("zigzag") }},
|
||||
}),
|
||||
});
|
||||
|
||||
const assets = &.{"sdk_public_key.xml"};
|
||||
|
||||
inline for (assets) |asset| {
|
||||
dll.root_module.addAnonymousImport(asset, .{ .root_source_file = b.path("assets/" ++ asset) });
|
||||
}
|
||||
|
||||
b.installArtifact(launcher);
|
||||
b.installArtifact(dll);
|
||||
}
|
||||
13
build.zig.zon
Normal file
13
build.zig.zon
Normal file
@@ -0,0 +1,13 @@
|
||||
.{
|
||||
.name = .cyrene_patch,
|
||||
.version = "0.0.1",
|
||||
.dependencies = .{
|
||||
.zigzag = .{
|
||||
.url = "git+https://github.com/uniboi/zigzag#5d90b669bd64cbbb445f5555de3b4c28cf508b80",
|
||||
.hash = "zigzag-0.0.1-wqJDFn2LAAArjRA9fApCcj5xG3JSZcDexs5ZRM9GWUzM",
|
||||
},
|
||||
},
|
||||
.minimum_zig_version = "0.15.1",
|
||||
.paths = .{""},
|
||||
.fingerprint = 0x80008f6dd30735e9,
|
||||
}
|
||||
270
flake.lock
generated
Normal file
270
flake.lock
generated
Normal file
@@ -0,0 +1,270 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"zls",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1755704039,
|
||||
"narHash": "sha256-gKlP0LbyJ3qX0KObfIWcp5nbuHSb5EHwIvU6UcNBg2A=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9cb344e96d5b6918e94e1bca2d9f3ea1e9615545",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"zig-overlay": "zig-overlay",
|
||||
"zig2nix": "zig2nix",
|
||||
"zls": "zls"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zig-overlay": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755864794,
|
||||
"narHash": "sha256-hgnov6RLA+DD4Uocs/vCbiH3/3sKvqiJOKHpdhGyVAI=",
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"rev": "5cd601f8760d2383210b7b8c8a45fc79388f3ddf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zig-overlay_2": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"nixpkgs": [
|
||||
"zls",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755864794,
|
||||
"narHash": "sha256-hgnov6RLA+DD4Uocs/vCbiH3/3sKvqiJOKHpdhGyVAI=",
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"rev": "5cd601f8760d2383210b7b8c8a45fc79388f3ddf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zig2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755679254,
|
||||
"narHash": "sha256-PXiDdFjJAbFYIk9hfRdgbrsSRob0/UcEfLeihRiTD0Y=",
|
||||
"owner": "Cloudef",
|
||||
"repo": "zig2nix",
|
||||
"rev": "cf297c89f32c94d42023d3ab5a496b16d0d3daf2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Cloudef",
|
||||
"repo": "zig2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zls": {
|
||||
"inputs": {
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"zig-overlay": "zig-overlay_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755880254,
|
||||
"narHash": "sha256-B1Ta6oShfEUSeABk8ahVR0GVzXuuMIf+zDCbtrFmbX4=",
|
||||
"owner": "zigtools",
|
||||
"repo": "zls",
|
||||
"rev": "da5c31e8e380bcf397276702cfa44571d96043af",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "zigtools",
|
||||
"repo": "zls",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
43
flake.nix
Normal file
43
flake.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||
zig-overlay = {
|
||||
url = "github:mitchellh/zig-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
zig2nix = {
|
||||
url = "github:Cloudef/zig2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
zls = {
|
||||
url = "github:zigtools/zls";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
zig-overlay,
|
||||
zig2nix,
|
||||
zls,
|
||||
}:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
zig-version = "0.15.1";
|
||||
zig = zig-overlay.packages.${system}.${zig-version};
|
||||
in
|
||||
{
|
||||
devShells.${system}.default = pkgs.callPackage (
|
||||
{ mkShell }:
|
||||
mkShell {
|
||||
nativeBuildInputs = [
|
||||
zig
|
||||
zls.packages.${system}.zls
|
||||
];
|
||||
}
|
||||
) { };
|
||||
};
|
||||
}
|
||||
127
injector.zig
Normal file
127
injector.zig
Normal file
@@ -0,0 +1,127 @@
|
||||
const std = @import("std");
|
||||
const unicode = std.unicode;
|
||||
const windows = std.os.windows;
|
||||
|
||||
const game_executables = &.{"StarRail.exe"};
|
||||
|
||||
const dll_path = "cyrene.dll" ++ .{0};
|
||||
const kernel32_name = unicode.utf8ToUtf16LeStringLiteral("kernel32.dll");
|
||||
|
||||
pub extern "kernel32" fn ResumeThread(*anyopaque) callconv(.winapi) void;
|
||||
|
||||
extern "kernel32" fn VirtualAllocEx(
|
||||
windows.HANDLE,
|
||||
?*anyopaque,
|
||||
windows.SIZE_T,
|
||||
windows.DWORD,
|
||||
windows.DWORD,
|
||||
) callconv(.winapi) windows.LPVOID;
|
||||
|
||||
extern "kernel32" fn VirtualFreeEx(
|
||||
windows.HANDLE,
|
||||
windows.LPVOID,
|
||||
windows.SIZE_T,
|
||||
windows.DWORD,
|
||||
) callconv(.winapi) windows.BOOL;
|
||||
|
||||
extern "kernel32" fn CreateRemoteThread(
|
||||
windows.HANDLE,
|
||||
?*anyopaque,
|
||||
windows.SIZE_T,
|
||||
windows.LPTHREAD_START_ROUTINE,
|
||||
windows.LPVOID,
|
||||
windows.DWORD,
|
||||
*windows.DWORD,
|
||||
) callconv(.winapi) windows.HANDLE;
|
||||
|
||||
pub fn main() !void {
|
||||
const game_executable = whichExecutable() orelse {
|
||||
try std.fs.File.stdout().writeAll("Game executable doesn't exist. Press any key to exit...\n");
|
||||
|
||||
var buf: [1]u8 = undefined;
|
||||
_ = std.fs.File.stdin().read(&buf) catch {};
|
||||
return;
|
||||
};
|
||||
|
||||
var proc_info: windows.PROCESS_INFORMATION = undefined;
|
||||
var startup_info: windows.STARTUPINFOW = .{
|
||||
.cb = 0,
|
||||
.lpReserved = null,
|
||||
.lpDesktop = null,
|
||||
.lpTitle = null,
|
||||
.dwX = 0,
|
||||
.dwY = 0,
|
||||
.dwXSize = 0,
|
||||
.dwYSize = 0,
|
||||
.dwXCountChars = 0,
|
||||
.dwYCountChars = 0,
|
||||
.dwFillAttribute = 0,
|
||||
.dwFlags = 0,
|
||||
.wShowWindow = 0,
|
||||
.cbReserved2 = 0,
|
||||
.lpReserved2 = null,
|
||||
.hStdInput = null,
|
||||
.hStdOutput = null,
|
||||
.hStdError = null,
|
||||
};
|
||||
|
||||
try windows.CreateProcessW(
|
||||
game_executable,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
.{ .create_suspended = true },
|
||||
null,
|
||||
null,
|
||||
&startup_info,
|
||||
&proc_info,
|
||||
);
|
||||
|
||||
const load_library = windows.kernel32.GetProcAddress(
|
||||
windows.kernel32.GetModuleHandleW(kernel32_name).?,
|
||||
"LoadLibraryA",
|
||||
).?;
|
||||
|
||||
const dll_path_addr = VirtualAllocEx(
|
||||
proc_info.hProcess,
|
||||
null,
|
||||
dll_path.len,
|
||||
windows.MEM_COMMIT | windows.MEM_RESERVE,
|
||||
windows.PAGE_READWRITE,
|
||||
);
|
||||
_ = try windows.WriteProcessMemory(proc_info.hProcess, dll_path_addr, dll_path);
|
||||
|
||||
// call LoadLibraryA in the remote process, this will also call DllMain so we should wait for it and then resume the target.
|
||||
var thread_id: windows.DWORD = 0;
|
||||
const loader_thread = CreateRemoteThread(
|
||||
proc_info.hProcess,
|
||||
null,
|
||||
0,
|
||||
@ptrCast(load_library),
|
||||
dll_path_addr,
|
||||
0,
|
||||
&thread_id,
|
||||
);
|
||||
|
||||
try windows.WaitForSingleObject(loader_thread, 0xFFFFFFFF);
|
||||
|
||||
// cleanup
|
||||
_ = VirtualFreeEx(proc_info.hProcess, dll_path_addr, 0, windows.MEM_RELEASE);
|
||||
windows.CloseHandle(loader_thread);
|
||||
ResumeThread(proc_info.hThread);
|
||||
windows.CloseHandle(proc_info.hThread);
|
||||
windows.CloseHandle(proc_info.hProcess);
|
||||
}
|
||||
|
||||
fn whichExecutable() ?[:0]const u16 {
|
||||
inline for (game_executables) |exe_name| {
|
||||
if (fileExists(exe_name)) return unicode.utf8ToUtf16LeStringLiteral(exe_name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn fileExists(name: []const u8) bool {
|
||||
return if (windows.GetFileAttributes(name)) |_| true else |_| false;
|
||||
}
|
||||
189
src/root.zig
Normal file
189
src/root.zig
Normal file
@@ -0,0 +1,189 @@
|
||||
const std = @import("std");
|
||||
const zz = @import("zigzag");
|
||||
const util = @import("util.zig");
|
||||
|
||||
const windows = std.os.windows;
|
||||
const unicode = std.unicode;
|
||||
|
||||
const DLL_PROCESS_ATTACH = 1;
|
||||
|
||||
extern "kernel32" fn AllocConsole() callconv(.winapi) void;
|
||||
extern "kernel32" fn FreeConsole() callconv(.winapi) void;
|
||||
|
||||
const ntdll_name = unicode.utf8ToUtf16LeStringLiteral("ntdll.dll");
|
||||
const wintrust_dll_name = std.unicode.utf8ToUtf16LeStringLiteral("wintrust.dll");
|
||||
const game_assembly_name = unicode.utf8ToUtf16LeStringLiteral("GameAssembly.dll");
|
||||
|
||||
const wintrust_funcs: []const [:0]const u8 = &.{
|
||||
"CryptCATAdminEnumCatalogFromHash",
|
||||
"CryptCATCatalogInfoFromContext",
|
||||
"CryptCATAdminReleaseCatalogContext",
|
||||
};
|
||||
|
||||
const wintrust_stub: []const u8 = &.{ 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 };
|
||||
|
||||
pub var base: usize = undefined;
|
||||
|
||||
fn onAttach() void {
|
||||
FreeConsole();
|
||||
AllocConsole();
|
||||
|
||||
std.fs.File.stdout().writeAll(
|
||||
\\ .@@::: #
|
||||
\\ @::@@-%:@ @@@@@@@@ % @
|
||||
\\ .-= #--@::* @#:::::::::@@@@@ @
|
||||
\\ @=. =:-:@@@:::::::-#----=@@ .=
|
||||
\\ @:#.-:-@:%.-@@::::---@:----@-@ .:@ @
|
||||
\\ . -*@@::@---------:---@:--@ @::::-@ @#
|
||||
\\ @-@:++=+*@#--:+@------@----@:-. ::@:- @@@
|
||||
\\ @@:+@@=@@-@--------------@----@:-@. :::@-@%@@ @
|
||||
\\ @:::-*:@--@-----:-::---::------@::::----@-+-@
|
||||
\\ @::-----:@-----::::::-:-::-:-:---@:::-::::@----@%
|
||||
\\ @::----::@-------:::-:---:--:-:---@:::---::#------@
|
||||
\\ @:------::*----@---------------@=--@:%:-----:@------
|
||||
\\ @-:-----::@----@-::--------------@--@:-#-------@%----@
|
||||
\\ @----+:::@----+@:::---------------@-@:-@:-------::%@@@
|
||||
\\ @----@:-::@--@@@#:::--------------#..@::@::---------@-@
|
||||
\\ @--@@:--::@--@@@=@@::---------@---@..@-@--:---@@+*@@--#
|
||||
\\ @-@@:---::@@@@ ###@@-------@@@@@@@@@@-@#:------@%..@%
|
||||
\\ *@ @:---@@:::++++==##@@.@@@@@=.@@@@@@#.@*:::-----@..-@@
|
||||
\\ #@@::-@::::::+=======@.........@ ###@.@:::------@@--@
|
||||
\\ @ @::%::::::::@...=+@..........+++=+###@@:::----@-----@
|
||||
\\ :::@::::::::::...............+=======@::------@----@@
|
||||
\\ @:::-@:::::::::....@..@........=...==@:::------@---@=*
|
||||
\\ *::--@:::::::.....-@@.@@.....:::@@::@::-------@--@====@
|
||||
\\ @:::---@::::.................::::::@::-----@--@@====++++@
|
||||
\\ @-@-=:-----@@@................:::::@:--@----@--+===+++**@
|
||||
\\ @====@::--@@--@ @@@@@@+.....:#@@%---@@----@---@@ @@@@
|
||||
\\ @@@@@+@:---@ *---@+@@%++@
|
||||
\\ @=++@@@@@@ @---@ @@+++@@
|
||||
\\ @@-@@@
|
||||
\\
|
||||
) catch {};
|
||||
|
||||
std.log.debug("Successfully injected. Waiting for the game startup.", .{});
|
||||
|
||||
if (isWine()) {
|
||||
const wintrust = windows.kernel32.LoadLibraryW(wintrust_dll_name).?;
|
||||
inline for (wintrust_funcs) |func_name| {
|
||||
const func: [*]u8 = @ptrCast(windows.kernel32.GetProcAddress(wintrust, func_name).?);
|
||||
var prot: windows.DWORD = windows.PAGE_EXECUTE_READWRITE;
|
||||
windows.VirtualProtect(@ptrCast(func), 6, prot, &prot) catch unreachable;
|
||||
@memcpy(func, wintrust_stub);
|
||||
windows.VirtualProtect(@ptrCast(func), 6, prot, &prot) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
base = while (true) {
|
||||
if (windows.kernel32.GetModuleHandleW(game_assembly_name)) |addr| break @intFromPtr(addr);
|
||||
std.Thread.sleep(std.time.ns_per_ms * 100);
|
||||
};
|
||||
|
||||
std.log.debug("GameAssembly is located at: 0x{X}", .{base});
|
||||
std.Thread.sleep(std.time.ns_per_s * 2);
|
||||
|
||||
disableMemoryProtection() catch |err| {
|
||||
std.log.err("Failed to disable memory protection: {}", .{err});
|
||||
return;
|
||||
};
|
||||
|
||||
var pca = zz.PageChunkAllocator.init() catch unreachable;
|
||||
const allocator = pca.allocator();
|
||||
|
||||
_ = intercept(allocator, base + 0x15E60900, MakeInitialUrlHook);
|
||||
|
||||
const dither_func: usize = 0x75F4C90;
|
||||
|
||||
var prot: windows.DWORD = windows.PAGE_EXECUTE_READWRITE;
|
||||
windows.VirtualProtect(@ptrFromInt(base + dither_func), 1, prot, &prot) catch unreachable;
|
||||
@as(*u8, @ptrFromInt(base + dither_func)).* = 0xC3;
|
||||
windows.VirtualProtect(@ptrFromInt(base + dither_func), 1, prot, &prot) catch unreachable;
|
||||
|
||||
prot = windows.PAGE_EXECUTE_READWRITE;
|
||||
|
||||
std.log.debug("Successfully initialized", .{});
|
||||
}
|
||||
|
||||
const MakeInitialUrlHook = struct {
|
||||
const global_dispatch_prefix = unicode.utf8ToUtf16LeStringLiteral("https://globaldp-beta-cn01.bhsr.com");
|
||||
const cn_sdk_domain = unicode.utf8ToUtf16LeStringLiteral("mihoyo.com");
|
||||
const global_sdk_domain = unicode.utf8ToUtf16LeStringLiteral("hoyoverse.com");
|
||||
|
||||
const custom_dispatch_prefix = unicode.utf8ToUtf16LeStringLiteral("http://127.0.0.1:21000");
|
||||
const custom_sdk_prefix = unicode.utf8ToUtf16LeStringLiteral("http://127.0.0.1:21000");
|
||||
|
||||
pub var originalFn: *const fn (usize, usize) callconv(.c) usize = undefined;
|
||||
|
||||
pub fn callback(a1: usize, a2: usize) callconv(.c) usize {
|
||||
var buf: [4096]u8 = undefined;
|
||||
|
||||
const str = util.readCSharpString(a1);
|
||||
const len = std.unicode.utf16LeToUtf8(&buf, str) catch unreachable;
|
||||
std.log.debug("{s}", .{buf[0..len]});
|
||||
|
||||
if (std.mem.startsWith(u16, str, global_dispatch_prefix)) {
|
||||
std.log.debug("dispatch request detected.", .{});
|
||||
util.csharpStringReplace(a1, global_dispatch_prefix, custom_dispatch_prefix);
|
||||
} else if (std.mem.indexOf(u16, str, cn_sdk_domain)) |index| {
|
||||
std.log.debug("CN SDK request detected.", .{});
|
||||
util.csharpStringReplace(a1, str[0 .. index + cn_sdk_domain.len], custom_sdk_prefix);
|
||||
} else if (std.mem.indexOf(u16, str, global_sdk_domain)) |index| {
|
||||
std.log.debug("GLOBAL SDK request detected.", .{});
|
||||
util.csharpStringReplace(a1, str[0 .. index + global_sdk_domain.len], custom_sdk_prefix);
|
||||
}
|
||||
|
||||
return @This().originalFn(a1, a2);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn intercept(ca: zz.ChunkAllocator, address: usize, hook_struct: anytype) zz.Hook(@TypeOf(hook_struct.callback)) {
|
||||
const hook = zz.Hook(@TypeOf(hook_struct.callback)).init(ca, @ptrFromInt(address), hook_struct.callback) catch |err| {
|
||||
std.log.err("failed to intercept function at 0x{X}: {}", .{ address - base, err });
|
||||
@panic("intercept failed");
|
||||
};
|
||||
|
||||
hook_struct.originalFn = hook.delegate;
|
||||
return hook;
|
||||
}
|
||||
|
||||
pub export fn DllMain(_: windows.HINSTANCE, reason: windows.DWORD, _: windows.LPVOID) callconv(.winapi) windows.BOOL {
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
const thread = std.Thread.spawn(.{}, onAttach, .{}) catch unreachable;
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn disableMemoryProtection() !void {
|
||||
const ntdll = windows.kernel32.GetModuleHandleW(ntdll_name).?;
|
||||
const proc_addr = windows.kernel32.GetProcAddress(ntdll, "NtProtectVirtualMemory").?;
|
||||
|
||||
const nt_func = nt_func: {
|
||||
if (isWine()) {
|
||||
break :nt_func windows.kernel32.GetProcAddress(ntdll, "NtPulseEvent").?;
|
||||
} else {
|
||||
break :nt_func windows.kernel32.GetProcAddress(ntdll, "NtQuerySection").?;
|
||||
}
|
||||
};
|
||||
|
||||
var protection: windows.DWORD = windows.PAGE_EXECUTE_READWRITE;
|
||||
try windows.VirtualProtect(proc_addr, 1, protection, &protection);
|
||||
|
||||
const routine: *u32 = @ptrCast(@alignCast(nt_func));
|
||||
const routine_val = @as(*usize, @ptrCast(@alignCast(routine))).*;
|
||||
const lower_bits_mask = ~(@as(u64, 0xFF) << 32);
|
||||
const lower_bits = routine_val & @as(usize, @intCast(lower_bits_mask));
|
||||
|
||||
const offset_val = @as(*const u32, @ptrFromInt(@as(usize, @intFromPtr(routine)) + 4)).*;
|
||||
const upper_bits = @as(usize, @intCast(@subWithOverflow(offset_val, 1).@"0")) << 32;
|
||||
const result = lower_bits | upper_bits;
|
||||
@as(*usize, @ptrCast(@alignCast(proc_addr))).* = result;
|
||||
|
||||
try windows.VirtualProtect(proc_addr, 1, protection, &protection);
|
||||
}
|
||||
|
||||
fn isWine() bool {
|
||||
const ntdll = windows.kernel32.GetModuleHandleW(ntdll_name).?;
|
||||
return windows.kernel32.GetProcAddress(ntdll, "wine_get_version") != null;
|
||||
}
|
||||
19
src/util.zig
Normal file
19
src/util.zig
Normal file
@@ -0,0 +1,19 @@
|
||||
const root = @import("root");
|
||||
|
||||
pub fn readCSharpString(data: usize) []u16 {
|
||||
const len = @as(*const u32, @ptrFromInt(data + 16)).*;
|
||||
const ptr = @as([*]u16, @ptrFromInt(data + 20));
|
||||
return ptr[0..len];
|
||||
}
|
||||
|
||||
pub fn csharpStringReplace(object: usize, pattern: []const u16, replacement: []const u16) void {
|
||||
const str = readCSharpString(object);
|
||||
|
||||
@memcpy(str[0..replacement.len], replacement);
|
||||
@memmove(str[replacement.len .. str.len - (pattern.len - replacement.len)], str[pattern.len..str.len]);
|
||||
@as(*u32, @ptrFromInt(object + 16)).* = @intCast(str.len - (pattern.len - replacement.len));
|
||||
}
|
||||
|
||||
pub fn il2cppStringNew(str: []const u8) usize {
|
||||
return @as(*const fn ([*]const u8) callconv(.c) usize, @ptrFromInt(root.base + 0x1690F70))(str.ptr);
|
||||
}
|
||||
Reference in New Issue
Block a user