KCP gameserver with packet dispatch, scene streaming, combat, gacha and the shop, plus a small HTTP server for the login flow and the wish history page. Game data under assets is pulled out of the client instead of being typed by hand. The excel tables come from the packed bin container and the spawn points from the compiled scene scripts.
72 lines
3.2 KiB
JavaScript
72 lines
3.2 KiB
JavaScript
// Optional RSA / session-seed patch, equivalent to xeon/EncryptionPatch (RSAPatch).
|
|
//
|
|
// NOT required for the 0.9.10 client this project targets: that build does not
|
|
// enforce RSA signature verification on query_cur_region, and it reads the
|
|
// session seed straight from the plaintext GetPlayerTokenRsp.secretKey field
|
|
// (which the server already sets to 1337). Ship redirect.js alone for 0.9.10.
|
|
//
|
|
// Enable this only for signed builds (~2.7+) by adding it to gadget.config and
|
|
// filling in the class/method names or byte patterns for that exact build.
|
|
|
|
function patch(stage, parameters) {
|
|
const module = "UserAssembly.dll";
|
|
|
|
const verify = Resolve.Method("Assembly-CSharp.dll", "MoleMole", "MiHoYoSDKUtil", "RsaVerify", 3);
|
|
if (verify) {
|
|
Interceptor.replace(verify, new NativeCallback(() => 1, 'bool', ['pointer', 'pointer', 'pointer']));
|
|
console.log("[Yuanshen] RSA verify forced true");
|
|
}
|
|
|
|
const process = Process.getModuleByName(module);
|
|
const scan = signature => {
|
|
const hits = Memory.scanSync(process.base, process.size, signature);
|
|
return hits.length ? hits[0].address : null;
|
|
};
|
|
const nop = (address, count) =>
|
|
Memory.patchCode(address, count, code => code.writeByteArray(Array(count).fill(0x90)));
|
|
|
|
let hit = scan("48 83 F8 01 75 08 49 8B C7 E9 E7 00 00 00 4C 8B 0D 9F E6 AB");
|
|
if (hit) nop(hit.add(4), 2);
|
|
|
|
hit = scan("0F 85 EF 00 00 00 33 D2 49 8B CF E8 6B D2 6E 05 48 8B F8");
|
|
if (hit) nop(hit, 6);
|
|
|
|
hit = scan("8B D8 E9 00 00 00 00 E8 DC 4A B5 F2 48 8B C8 49");
|
|
if (hit) Memory.patchCode(hit, 7, code => code.writeByteArray([0xC7, 0xC3, 0x39, 0x05, 0x00, 0x00, 0x90]));
|
|
|
|
console.log("[Yuanshen] rsapatch scan complete");
|
|
}
|
|
|
|
const Resolve = {
|
|
Method(moduleName, namespace, className, methodName, argCount) {
|
|
const api = (name, ret, argTypes, args) => {
|
|
const fn = new NativeFunction(Module.findExportByName(null, name), ret, argTypes);
|
|
return fn.apply(fn, args);
|
|
};
|
|
const domain = api('il2cpp_domain_get', 'pointer', [], []);
|
|
const sizeOut = Memory.alloc(8);
|
|
const assemblies = api('il2cpp_domain_get_assemblies', 'pointer', ['pointer', 'pointer'], [domain, sizeOut]);
|
|
const size = sizeOut.readU64();
|
|
|
|
let image = NULL;
|
|
for (let i = 0; i < size; i++) {
|
|
const assembly = assemblies.add(i * 8).readPointer();
|
|
const candidate = api('il2cpp_assembly_get_image', 'pointer', ['pointer'], [assembly]);
|
|
if (api('il2cpp_image_get_name', 'pointer', ['pointer'], [candidate]).readUtf8String() === moduleName) {
|
|
image = candidate;
|
|
break;
|
|
}
|
|
}
|
|
if (image.isNull()) return null;
|
|
|
|
const klass = api('il2cpp_class_from_name', 'pointer', ['pointer', 'pointer', 'pointer'],
|
|
[image, Memory.allocUtf8String(namespace), Memory.allocUtf8String(className)]);
|
|
if (klass.isNull()) return null;
|
|
|
|
const method = api('il2cpp_class_get_method_from_name', 'pointer', ['pointer', 'pointer', 'int'],
|
|
[klass, Memory.allocUtf8String(methodName), argCount]);
|
|
return method.isNull() ? null : method.readPointer();
|
|
}
|
|
};
|
|
|
|
rpc.exports.init = patch;
|