mirror of
https://github.com/TheAirBlow/HyperSploit.git
synced 2025-06-30 23:06:26 +00:00
297 lines
No EOL
13 KiB
C#
297 lines
No EOL
13 KiB
C#
using System.Diagnostics;
|
|
using System.IO.Compression;
|
|
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using AdvancedSharpAdbClient;
|
|
using AdvancedSharpAdbClient.Exceptions;
|
|
using AdvancedSharpAdbClient.Models;
|
|
using Spectre.Console;
|
|
|
|
namespace HyperSploit;
|
|
|
|
/// <summary>
|
|
/// Main program class
|
|
/// </summary>
|
|
public static class Program {
|
|
/// <summary>
|
|
/// Unlock API host
|
|
/// </summary>
|
|
private static string Host => IsGlobal ? "https://unlock.update.intl.miui.com" : "https://unlock.update.miui.com";
|
|
|
|
/// <summary>
|
|
/// Should global API be used
|
|
/// </summary>
|
|
private static bool IsGlobal;
|
|
|
|
/// <summary>
|
|
/// Program entrypoint
|
|
/// </summary>
|
|
/// <param name="args">Arguments</param>
|
|
public static async Task Main(string[] args) {
|
|
AnsiConsole.Write(new FigletText("HyperSploit").LeftJustified().Color(Color.Cyan1));
|
|
AnsiConsole.MarkupLine("[green]Welcome to HyperSploit v1.1 by TheAirBlow![/]");
|
|
var root = Extract();
|
|
if (!AdbServer.Instance.GetStatus().IsRunning) {
|
|
AnsiConsole.MarkupLine("[yellow]No ADB server is running, trying to start...[/]");
|
|
var server = new AdbServer();
|
|
var path = Path.Combine(root, "adb");
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
path += ".exe";
|
|
var result = await server.StartServerAsync(path);
|
|
if (result != StartServerResult.Started) {
|
|
AnsiConsole.MarkupLine("[red]Failed to start ADB server![/]");
|
|
AnsiConsole.MarkupLine("[red]Install ADB first you're on Linux or MacOS.[/]");
|
|
AnsiConsole.MarkupLine("[red]If you're on Windows - make a GitHub issue.[/]");
|
|
return;
|
|
}
|
|
}
|
|
|
|
var client = new AdbClient();
|
|
while (true) {
|
|
var devices = await client.GetDevicesAsync();
|
|
var dict = devices.ToDictionary(x => $"{x.Model} codename {x.Name}", x => x);
|
|
var choice = AnsiConsole.Prompt(new SelectionPrompt<string>()
|
|
.Title("[cyan]Choose android device to use:[/]")
|
|
.AddChoices(dict.Keys.Append("[yellow]Refresh list[/]")));
|
|
if (!dict.TryGetValue(choice, out var device)) continue;
|
|
AnsiConsole.MarkupLine($"[green]You chose [cyan]{device.Model} codename {device.Name}[/]![/]");
|
|
if (device.State != DeviceState.Online) {
|
|
AnsiConsole.MarkupLine($"[yellow]Chosen device is in an invalid state: {device.State}![/]");
|
|
AnsiConsole.MarkupLine("[yellow]Make sure to authorize your computer if asked to.[/]");
|
|
continue;
|
|
}
|
|
|
|
IsGlobal = !AnsiConsole.Confirm("[yellow]Is this device from Mainland China?[/]", false);
|
|
try {
|
|
await Bypass(client, device);
|
|
} catch (Exception e) {
|
|
AnsiConsole.MarkupLine("[red]Unhandled exception caught, can't continue![/]");
|
|
Console.WriteLine(e);
|
|
}
|
|
if (!AnsiConsole.Confirm("[yellow]Would you like to run HyperSploit on another device?[/]", false)) break;
|
|
}
|
|
|
|
if (root != "") Directory.Delete(root);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main bypass implementation
|
|
/// </summary>
|
|
/// <param name="client">ADB client</param>
|
|
/// <param name="device">Device data</param>
|
|
private static async Task Bypass(AdbClient client, DeviceData device) {
|
|
var tablet = await IsTablet(client, device);
|
|
AnsiConsole.MarkupLine(tablet
|
|
? "[yellow]Make sure you have a Wi-Fi connection with internet access![/]"
|
|
: "[yellow]Make sure you have a cellular connection with internet access![/]");
|
|
if (tablet) {
|
|
await client.ExecuteRemoteCommandAsync("svc wifi enable", device);
|
|
} else {
|
|
await client.ExecuteRemoteCommandAsync("svc wifi disable", device);
|
|
await client.ExecuteRemoteCommandAsync("svc data enable", device);
|
|
}
|
|
|
|
AnsiConsole.MarkupLine("[green]Open Mi Unlock Status and attempt to bind account[/]");
|
|
await client.ExecuteRemoteCommandAsync("am start -a android.settings.APPLICATION_DEVELOPMENT_SETTINGS", device);
|
|
|
|
var receiver = new EventOutputReceiver();
|
|
string? arguments = null;
|
|
string? headers = null;
|
|
receiver.OnOutput += (_, line) => {
|
|
var match = Regex.Match(line, "args: (.*)");
|
|
if (match.Success) {
|
|
AnsiConsole.MarkupLine("[green]Disabling mobile internet, taking over![/]");
|
|
client.ExecuteRemoteCommand(tablet ? "svc wifi disable" : "svc data disable", device);
|
|
arguments = Decrypt(match.Groups[1].Value);
|
|
return;
|
|
}
|
|
|
|
match = Regex.Match(line, "headers: (.*)");
|
|
if (match.Success) {
|
|
headers = Decrypt(match.Groups[1].Value);
|
|
receiver.Terminate();
|
|
}
|
|
};
|
|
|
|
await client.ExecuteRemoteCommandAsync("logcat -T 1 *:S CloudDeviceStatus:V", device, receiver);
|
|
if (arguments == null || headers == null) {
|
|
await client.ExecuteRemoteCommandAsync(tablet ? "svc wifi enable" : "svc data enable", device);
|
|
AnsiConsole.MarkupLine("[red]Failed to decrypt arguments and headers![/]");
|
|
AnsiConsole.MarkupLine("[yellow]This probably means you have a patched Settings app.[/]");
|
|
if (AnsiConsole.Confirm("[yellow]Try downgrading to an unpatched version?[/]", false))
|
|
await Downgrade(client, device);
|
|
return;
|
|
}
|
|
|
|
AnsiConsole.MarkupLine("[green]Successfully decrypted arguments and headers![/]");
|
|
#if DEBUG
|
|
Console.WriteLine($"Headers: {headers}");
|
|
Console.WriteLine($"Arguments: {arguments}");
|
|
#endif
|
|
|
|
AnsiConsole.MarkupLine("[cyan]Sending account bind request impersonating MIUI 10...[/]");
|
|
#if DEBUG
|
|
arguments = AnsiConsole.Ask<string>("[yellow]Modified arguments:[/] ");
|
|
#else
|
|
arguments = arguments.Replace("V816", "V10");
|
|
#endif
|
|
await SendRequest(headers, arguments);
|
|
|
|
await client.ExecuteRemoteCommandAsync(tablet ? "svc wifi enable" : "svc data enable", device);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the target device is a tablet
|
|
/// </summary>
|
|
/// <param name="client">ADB client</param>
|
|
/// <param name="device">Device data</param>
|
|
/// <returns>True if tablet</returns>
|
|
private static async Task<bool> IsTablet(AdbClient client, DeviceData device) {
|
|
var tablet = true;
|
|
var receiver = new EventOutputReceiver();
|
|
receiver.OnOutput += (_, line) => {
|
|
if (line == "feature:android.hardware.telephony") tablet = false;
|
|
};
|
|
|
|
await client.ExecuteRemoteCommandAsync("pm list features", device, receiver);
|
|
return tablet;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends an account binding request
|
|
/// </summary>
|
|
/// <param name="headers">Headers</param>
|
|
/// <param name="arguments">Arguments</param>
|
|
private static async Task SendRequest(string headers, string arguments) {
|
|
const string signKey = "10f29ff413c89c8de02349cb3eb9a5f510f29ff413c89c8de02349cb3eb9a5f5";
|
|
var toSign = $"POST\n/v1/unlock/applyBind\ndata={arguments}&sid=miui_sec_android";
|
|
var signature = Convert.ToHexString(HMACSHA1.HashData(Encoding.ASCII.GetBytes(signKey), Encoding.UTF8.GetBytes(toSign)));
|
|
|
|
var cookies = new CookieContainer();
|
|
using var http = new HttpClient(new HttpClientHandler {
|
|
CookieContainer = cookies
|
|
});
|
|
|
|
var match = Regex.Match(headers, "Cookie=\\[(.*)]").Groups[1].Value;
|
|
foreach (var cookie in match.Split(";")) {
|
|
var split = cookie.Split("=");
|
|
cookies.Add(new Cookie(split[0], split[1], "/", "miui.com"));
|
|
}
|
|
|
|
var resp = await http.PostAsync($"{Host}/v1/unlock/applyBind",
|
|
new FormUrlEncodedContent(new Dictionary<string, string> {
|
|
["data"] = arguments,
|
|
["sid"] = "miui_sec_android",
|
|
["sign"] = signature
|
|
}));
|
|
var text = await resp.Content.ReadAsStringAsync();
|
|
#if DEBUG
|
|
Console.WriteLine($"Response: {text}");
|
|
#endif
|
|
var json = JsonSerializer.Deserialize(text,
|
|
SourceGenerationContext.Default.JsonResponse)!;
|
|
|
|
AnsiConsole.MarkupLine(json.Code switch {
|
|
0 => "[green]Phone was successfully bound to Xiaomi account, you can now use Mi Unlock![/]",
|
|
401 => "[red]Error 401: Xiaomi account credentials expired, login again[/]",
|
|
10001 => "[red]Error 10001: Invalid bind request signature, make a GitHub issue[/]",
|
|
20086 => "[red]Error 20086: Xiaomi account credentials expired, login again[/]",
|
|
30001 => "[red]Error 30001: Device forced to verify, you're out of luck[/]",
|
|
86015 => "[red]Error 86015: Invalid device signature[/]",
|
|
_ => $"[red]Error {json.Code}: {json.Description}[/]"
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main downgrading implementation
|
|
/// </summary>
|
|
/// <param name="client">ADB client</param>
|
|
/// <param name="device">Device data</param>
|
|
/// <param name="nuclear">Nuclear option</param>
|
|
private static async Task Downgrade(AdbClient client, DeviceData device, bool nuclear = false) {
|
|
AnsiConsole.MarkupLine("[green]Decompressing Settings.apk bundled within HyperSploit...[/]");
|
|
await using var apk = typeof(Program).Assembly
|
|
.GetManifestResourceStream("Settings.apk.gz")!;
|
|
await using var stream = new GZipStream(
|
|
apk, CompressionMode.Decompress);
|
|
using var memory = new MemoryStream();
|
|
await stream.CopyToAsync(memory);
|
|
memory.Position = 0;
|
|
|
|
if (nuclear) {
|
|
AnsiConsole.MarkupLine("[green]Uninstalling the Settings app...[/]");
|
|
await client.ExecuteRemoteCommandAsync("pm uninstall --user 0 com.android.settings", device);
|
|
}
|
|
|
|
AnsiConsole.MarkupLine("[green]Installing unpatched Settings APK to the device...[/]");
|
|
try {
|
|
await client.InstallAsync(device, memory);
|
|
} catch (AdbException e) {
|
|
// ReSharper disable once InvertIf ~ can you please stop nagging me
|
|
if (e.Message.Contains("INSTALL_FAILED_VERSION_DOWNGRADE") || e.Message.Contains("INSTALL_FAILED_INVALID_APK")) {
|
|
AnsiConsole.MarkupLine(nuclear
|
|
? "[red]Unfortunately, you're out of luck.[/] [yellow]You have a patched HyperOS version.[/]"
|
|
: "[yellow]Downgrade failed, but you can still try out the nuclear option (it will auto restore)[/]");
|
|
if (!nuclear && AnsiConsole.Confirm("[yellow]Try deleting the Settings app altogether?[/]", false))
|
|
await Downgrade(client, device, true);
|
|
|
|
if (nuclear) {
|
|
AnsiConsole.MarkupLine("[green]Restoring the original Settings app...[/]");
|
|
await client.ExecuteRemoteCommandAsync("cmd package install-existing com.android.settings", device);
|
|
}
|
|
return;
|
|
}
|
|
|
|
throw;
|
|
}
|
|
|
|
AnsiConsole.MarkupLine("[green]Installation finished, trying the bypass again...[/]");
|
|
await Bypass(client, device);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypt log line
|
|
/// </summary>
|
|
/// <param name="text">Text</param>
|
|
/// <returns>Decrypted text</returns>
|
|
private static string? Decrypt(string text) {
|
|
if (text.StartsWith("#&^")) return null;
|
|
using var aes = Aes.Create();
|
|
aes.Key = "20nr1aobv2xi8ax4"u8.ToArray();
|
|
aes.IV = "0102030405060708"u8.ToArray();
|
|
using var decryptor = aes.CreateDecryptor();
|
|
using var output = new MemoryStream();
|
|
using var input = new MemoryStream(Convert.FromBase64String(text));
|
|
using var stream = new CryptoStream(input, decryptor, CryptoStreamMode.Read);
|
|
stream.CopyTo(output); return Encoding.UTF8.GetString(output.ToArray());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts ADB binaries
|
|
/// </summary>
|
|
/// <returns>Path to folder</returns>
|
|
private static string Extract() {
|
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return "";
|
|
var root = Path.Combine(Path.GetTempPath(), "hypersploit");
|
|
if (Directory.Exists(root)) return root;
|
|
AnsiConsole.MarkupLine("[yellow]Extracting Windows ADB binaries...[/]");
|
|
var assembly = typeof(Program).Assembly;
|
|
var files = assembly.GetManifestResourceNames()
|
|
.Where(x => x.StartsWith("Assets/adb"));
|
|
foreach (var file in files) {
|
|
var name = file.Replace("Assets/adb-windows/", "");
|
|
var path = Path.Combine(root, name);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
|
using (var src = assembly.GetManifestResourceStream(file)!)
|
|
using (var dst = new FileStream(path, FileMode.Create, FileAccess.Write))
|
|
src.CopyTo(dst);
|
|
Process.Start("chmod", ["+x", path]);
|
|
}
|
|
|
|
return root;
|
|
}
|
|
} |