diff --git a/Xenon/App.config b/Xenon/App.config index 5c8886a..65c1d29 100644 --- a/Xenon/App.config +++ b/Xenon/App.config @@ -13,6 +13,9 @@ + + + \ No newline at end of file diff --git a/Xenon/MainWindow.xaml.cs b/Xenon/MainWindow.xaml.cs index 34bae4a..d52f26c 100644 --- a/Xenon/MainWindow.xaml.cs +++ b/Xenon/MainWindow.xaml.cs @@ -129,23 +129,20 @@ private async void Button_Click(object sender, RoutedEventArgs e) #endif } - private string encodeB64(string str) => Convert.ToBase64String(Encoding.UTF8.GetBytes(str)); - private async Task performLogin(string username, string password) { var req = new HttpRequestMessage() { - RequestUri = new Uri("https://api.nexon.net/auth/login"), + RequestUri = new Uri("https://accounts.nexon.net/account/login/launcher"), Method = HttpMethod.Post, - Content = new StringContent($"{{\"allow_unverified\":true,\"user_id\":\"{username}\",\"user_pw\":\"{password}\"}}", Encoding.UTF8, "application/json") + Content = new StringContent($"{{\"id\":\"{username}\",\"password\":\"{Nexon.Auth.HashHexPassword(password)}\",\"auto_login\":false,\"client_id\":\"{Nexon.Auth.CLIENT_ID}\",\"scope\":\"{Nexon.Auth.SCOPE}\",\"device_id\":\"{Nexon.Auth.DeviceId}\"}}", Encoding.UTF8, "application/json") }; req.Headers.Add("User-Agent", "NexonLauncher node-webkit/0.14.6 (Windows NT 10.0; WOW64) WebKit/537.36 (@c26c0312e940221c424c2730ef72be2c69ac1b67) nexon_client"); - req.Headers.Add("Authorization", "Basic " + encodeB64($"{username.Replace("@", "%40")}:{password}")); // nexon launcher does this, it uriencodes to %40 which is weird but meh HttpResponseMessage res = await httpClient.SendAsync(req); dynamic json = await parseResponseJson(res); - nexonToken = json["token"]; + nexonToken = json["access_token"]; cookieContainer.Add(new Cookie("nxtk", nexonToken, "/", ".nexon.net")); // this cookie is used in all future requests } @@ -162,7 +159,7 @@ private async Task isMapleUpToDate() { var req = new HttpRequestMessage() { - RequestUri = new Uri("https://api.nexon.net/products/10100"), + RequestUri = new Uri("https://api.nexon.io/products/10100"), Method = HttpMethod.Get }; addNexonLauncherData(req); // bearer auth + UA @@ -199,7 +196,7 @@ private async Task getLaunchData() // getting passport cookie to be able to get the launch token var req = new HttpRequestMessage() { - RequestUri = new Uri("https://api.nexon.net/users/me/passport"), + RequestUri = new Uri("https://api.nexon.io/users/me/passport"), Method = HttpMethod.Get }; addNexonLauncherData(req); @@ -261,6 +258,10 @@ private async Task parseResponseJson(HttpResponseMessage res) { // include err checking here dynamic json = JObject.Parse(await res.Content.ReadAsStringAsync()); + + if (!res.IsSuccessStatusCode) + throw new Exception($"Nexon {res.StatusCode} error: \n\n{json["message"]} [{json["code"]}]"); + if (json["error"] != null) throw new Exception($"Nexon error: \n\n{json["error"]["message"]} [{json["error"]["code"]}]"); @@ -269,8 +270,8 @@ private async Task parseResponseJson(HttpResponseMessage res) private void addNexonLauncherData(HttpRequestMessage req) { - req.Headers.Add("User-Agent", "NexonLauncher.nxl-17.02.03-219-5e3143f"); - req.Headers.Add("Authorization", "bearer " + encodeB64(nexonToken)); // this + cookie is used for auth here. + req.Headers.Add("User-Agent", "NexonLauncher.nxl-17.03.02-275-220ecfb"); + req.Headers.Add("Authorization", "bearer " + Util.EncodeB64(nexonToken)); // this + cookie is used for auth here. } private void returnNoError() diff --git a/Xenon/Nexon/Auth.cs b/Xenon/Nexon/Auth.cs new file mode 100644 index 0000000..f3fa341 --- /dev/null +++ b/Xenon/Nexon/Auth.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; + +namespace Xenon.Nexon +{ + public static class Auth + { + public const string CLIENT_ID = "7853644408"; + public const string SCOPE = "us.launcher.all"; + + public static string DeviceId = ""; // lets just make a random 64 string that we use everytime... + + static Auth() + { + DeviceId = Properties.Settings.Default.nexonDeviceId; + + if (String.IsNullOrEmpty(DeviceId)) + { + DeviceId = ""; + + var random = new Random(); + for (int i = 0; i < 64; i++) + DeviceId += random.Next(16).ToString("X"); + + Properties.Settings.Default.nexonDeviceId = DeviceId; + Properties.Settings.Default.Save(); + } + } + + public static string HashHexPassword(string password) + { + // hashes the password as sha-512 and hex as required by nexon + // https://msdn.microsoft.com/en-us/library/s02tk69a(v=vs.110).aspx + SHA512 shaM = new SHA512Managed(); + byte[] shaData = shaM.ComputeHash(Encoding.UTF8.GetBytes(password)); + + var sb = new StringBuilder(); + for (int i = 0; i < shaData.Length; i++) + sb.Append(shaData[i].ToString("x2")); // hex formatted + + return sb.ToString(); + } + } +} diff --git a/Xenon/Properties/Settings.Designer.cs b/Xenon/Properties/Settings.Designer.cs index 1ee556b..1e52bb6 100644 --- a/Xenon/Properties/Settings.Designer.cs +++ b/Xenon/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace Xenon.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -34,5 +34,17 @@ public string username { this["username"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string nexonDeviceId { + get { + return ((string)(this["nexonDeviceId"])); + } + set { + this["nexonDeviceId"] = value; + } + } } } diff --git a/Xenon/Properties/Settings.settings b/Xenon/Properties/Settings.settings index 9c59d40..1bbfd96 100644 --- a/Xenon/Properties/Settings.settings +++ b/Xenon/Properties/Settings.settings @@ -5,5 +5,8 @@ + + + \ No newline at end of file diff --git a/Xenon/Util.cs b/Xenon/Util.cs new file mode 100644 index 0000000..e186701 --- /dev/null +++ b/Xenon/Util.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Xenon +{ + static class Util + { + public static string EncodeB64(string plainText) + => System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(plainText)); + + public static string DecodeB64(string base64EncodedData) + => System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(base64EncodedData)); + } +} diff --git a/Xenon/Xenon.csproj b/Xenon/Xenon.csproj index 8fe93c4..5e7a503 100644 --- a/Xenon/Xenon.csproj +++ b/Xenon/Xenon.csproj @@ -61,6 +61,8 @@ + + MSBuild:Compile Designer @@ -114,6 +116,7 @@ +