mirror of
https://git.xeondev.com/HonkaiSlopRail/sr-launcher.git
synced 2025-11-22 06:52:30 -05:00
Supports CNBETA 3.7.51
This commit is contained in:
parent
3844f7b4cf
commit
77c7b0164a
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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user