using System; using System.IO; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; namespace ServerSelector { public class ServerSwitcher { private static int GameAssemblySodiumIntegrityFuncHint = 0x5877FFB; private static byte[] GameAssemblySodiumIntegrityFuncOrg = [0xE8, 0xF0, 0x9D, 0x43, 0xFB]; private static byte[] GameAssemblySodiumIntegrityFuncPatch = [0xb0, 0x01, 0x90, 0x90, 0x90]; public static bool IsUsingOfficalServer() { var hostsFile = File.ReadAllText("C:\\Windows\\System32\\drivers\\etc\\hosts"); return !hostsFile.Contains("cloud.nikke-kr.com"); } public static string CheckIntegrity() { if (IsUsingOfficalServer()) return "Official server"; // TODO return "OK"; } public static async Task SaveCfg(bool useOffical, string gamePath, string launcherPath, string ip) { string sodiumLib = AppDomain.CurrentDomain.BaseDirectory + "sodium.dll"; string gameSodium = gamePath + "/nikke_Data/Plugins/x86_64/sodium.dll"; string gameAssembly = gamePath + "/GameAssembly.dll"; string sodiumBackup = gameSodium + ".bak"; string hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers/etc/hosts"); var CAcert = await File.ReadAllTextAsync(AppDomain.CurrentDomain.BaseDirectory + "myCA.pem"); string launcherCertList = launcherPath + "/intl_service/cacert.pem"; string gameCertList = gamePath + "/nikke_Data/Plugins/x86_64/cacert.pem"; // TODO: allow changing ip address if (useOffical) { var txt = await File.ReadAllTextAsync(hostsFilePath); // remove stuff try { int startIdx = txt.IndexOf("cloud.nikke-kr.com"); // find new line character before start index for (int i = startIdx - 1; i >= 0; i--) { var c = txt[i]; if (c == '\n') { startIdx = i + 1; break; } } int endIdx = txt.IndexOf("y.io") + 4; txt = txt.Substring(0, startIdx) + txt.Substring(endIdx); await File.WriteAllTextAsync(hostsFilePath, txt); } catch { } // remove cert X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadWrite); store.Remove(new X509Certificate2(X509Certificate2.CreateFromCertFile(AppDomain.CurrentDomain.BaseDirectory + "myCA.pfx"))); store.Close(); // restore sodium if (!File.Exists(sodiumBackup)) { throw new Exception("sodium backup does not exist"); } File.Copy(sodiumBackup, gameSodium, true); // revert gameassembly changes var gameAssemblyBytes = await File.ReadAllBytesAsync(gameAssembly); for (int i = 0x5877FFB; i < gameAssemblyBytes.Length; i++) { if (gameAssemblyBytes[i] == GameAssemblySodiumIntegrityFuncOrg[0] && gameAssemblyBytes[i + 1] == GameAssemblySodiumIntegrityFuncOrg[1] && gameAssemblyBytes[i + 2] == GameAssemblySodiumIntegrityFuncOrg[2] && gameAssemblyBytes[i + 3] == GameAssemblySodiumIntegrityFuncOrg[3] && gameAssemblyBytes[i + 4] == GameAssemblySodiumIntegrityFuncOrg[4]) { // was not patched break; } if (gameAssemblyBytes[i] == GameAssemblySodiumIntegrityFuncPatch[0] && gameAssemblyBytes[i + 1] == GameAssemblySodiumIntegrityFuncPatch[1] && gameAssemblyBytes[i + 2] == GameAssemblySodiumIntegrityFuncPatch[2] && gameAssemblyBytes[i + 3] == GameAssemblySodiumIntegrityFuncPatch[3] && gameAssemblyBytes[i + 4] == GameAssemblySodiumIntegrityFuncPatch[4]) { gameAssemblyBytes[i] = GameAssemblySodiumIntegrityFuncOrg[0]; gameAssemblyBytes[i + 1] = GameAssemblySodiumIntegrityFuncOrg[1]; gameAssemblyBytes[i + 2] = GameAssemblySodiumIntegrityFuncOrg[2]; gameAssemblyBytes[i + 3] = GameAssemblySodiumIntegrityFuncOrg[3]; gameAssemblyBytes[i + 4] = GameAssemblySodiumIntegrityFuncOrg[4]; File.WriteAllBytes(gameAssembly, gameAssemblyBytes); break; } } var certList1 = await File.ReadAllTextAsync(launcherCertList); await File.WriteAllTextAsync(launcherCertList, certList1.Substring(0, certList1.IndexOf("Good SSL Ca"))); var certList2 = await File.ReadAllTextAsync(gameCertList); await File.WriteAllTextAsync(gameCertList, certList2.Substring(0, certList2.IndexOf("Good SSL Ca"))); } else { // add to hosts file string hosts = $@"{ip} cloud.nikke-kr.com {ip} global-lobby.nikke-kr.com {ip} jp-lobby.nikke-kr.com {ip} us-lobby.nikke-kr.com {ip} kr-lobby.nikke-kr.com {ip} sea-lobby.nikke-kr.com {ip} hmt-lobby.nikke-kr.com {ip} aws-na-dr.intlgame.com {ip} sg-vas.intlgame.com {ip} aws-na.intlgame.com {ip} na-community.playerinfinite.com {ip} common-web.intlgame.com {ip} li-sg.intlgame.com 255.255.221.21 na.fleetlogd.com {ip} www.jupiterlauncher.com {ip} data-aws-na.intlgame.com 255.255.221.21 sentry.io"; if (!(await File.ReadAllTextAsync(hostsFilePath)).Contains("global-lobby.nikke-kr.com")) { using StreamWriter w = File.AppendText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers/etc/hosts")); w.WriteLine(); w.WriteLine(hosts); } // trust CA X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadWrite); store.Add(new X509Certificate2(X509Certificate2.CreateFromCertFile(AppDomain.CurrentDomain.BaseDirectory + "myCA.pfx"))); store.Close(); // update sodium lib if (!File.Exists(gameSodium)) { throw new Exception("expected sodium library to exist at path " + gameSodium); } // copy backup if sodium size is correct var sod = await File.ReadAllBytesAsync(gameSodium); if (sod.Length <= 307200) { // orignal file size, copy backup await File.WriteAllBytesAsync(sodiumBackup, sod); } // write new sodium library await File.WriteAllBytesAsync(gameSodium, await File.ReadAllBytesAsync(sodiumLib)); // patch gameassembly to remove sodium IntegrityUtility Check introduced in v124.6.10 var gameAssemblyBytes = await File.ReadAllBytesAsync(gameAssembly); for (int i = 0x5877FFB; i < gameAssemblyBytes.Length; i++) { if (gameAssemblyBytes[i] == GameAssemblySodiumIntegrityFuncOrg[0] && gameAssemblyBytes[i + 1] == GameAssemblySodiumIntegrityFuncOrg[1] && gameAssemblyBytes[i + 2] == GameAssemblySodiumIntegrityFuncOrg[2] && gameAssemblyBytes[i + 3] == GameAssemblySodiumIntegrityFuncOrg[3] && gameAssemblyBytes[i + 4] == GameAssemblySodiumIntegrityFuncOrg[4]) { gameAssemblyBytes[i] = GameAssemblySodiumIntegrityFuncPatch[0]; // MOV ax, 1 gameAssemblyBytes[i + 1] = GameAssemblySodiumIntegrityFuncPatch[1]; gameAssemblyBytes[i + 2] = GameAssemblySodiumIntegrityFuncPatch[2]; // NOP gameAssemblyBytes[i + 3] = GameAssemblySodiumIntegrityFuncPatch[3]; // NOP gameAssemblyBytes[i + 4] = GameAssemblySodiumIntegrityFuncPatch[4]; // NOP await File.WriteAllBytesAsync(gameAssembly, gameAssemblyBytes); break; } if (gameAssemblyBytes[i] == GameAssemblySodiumIntegrityFuncPatch[0] && gameAssemblyBytes[i + 1] == GameAssemblySodiumIntegrityFuncPatch[1] && gameAssemblyBytes[i + 2] == GameAssemblySodiumIntegrityFuncPatch[2] && gameAssemblyBytes[i + 3] == GameAssemblySodiumIntegrityFuncPatch[3] && gameAssemblyBytes[i + 4] == GameAssemblySodiumIntegrityFuncPatch[4]) { // was already patched break; } } // update launcher/game ca cert list var certList1 = await File.ReadAllTextAsync(launcherCertList); certList1 += "\nGood SSL Ca\n===============================\n"; certList1 += CAcert; await File.WriteAllTextAsync(launcherCertList, certList1); var certList2 = await File.ReadAllTextAsync(gameCertList); certList2 += "\nGood SSL Ca\n===============================\n"; certList2 += CAcert; await File.WriteAllTextAsync(gameCertList, certList2); } } } }