refreshDeviceList(); $t = array(); foreach ($s as $d) { if ($d["status"] === $a::CONNECT_TYPE_DEVICE) { $t[] = array($d["serial"], $d["transport"]); } } return $t; } /** * Formatted Log * @param $m string optional Message * @param $c string optional Color * @param $p string optional Indicator * @param $t string optional Type (Level) * @author NekoYuzu (MlgmXyysd) * @date 2022/03/24 14:50:01 */ function logf(string $m = "", string $c = "", string $p = "-", string $t = "I"): void { switch (strtoupper($c)) { case "G": $c = "\033[32m"; break; case "R": $c = "\033[31m"; break; case "Y": $c = "\033[33m"; break; default: $c = ""; } switch (strtoupper($t)) { case "W": $t = "WARN"; break; case "E": $t = "ERROR"; break; case "I": default: $t = "INFO"; } print(date("[Y-m-d] [H:i:s]") . " [" . $t . "] " . $p . " " . $c . $m . "\033[0m" . PHP_EOL); } /** * Curl HTTP wrapper function * @param $url string required Target url * @param $method string required Request method * @param $fields array optional Request body * @param $header array optional Request header * @param $useForm bool optional Treat request body as urlencoded form * @return array Curl response * @author NekoYuzu (MlgmXyysd) * @date 2023/11/20 23:50:39 */ function http(string $url, string $method, array $fields = array(), array $header = array(), bool $useForm = false): array { if ($useForm) { $fields = http_build_query($fields); } $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => "", CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_MAXREDIRS => 10, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_TIMEOUT => 6, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_POST => $method == "POST", CURLOPT_POSTFIELDS => $fields, CURLOPT_HTTPHEADER => $header )); $response = curl_exec($curl); $info = curl_getinfo($curl); $info["errno"] = curl_errno($curl); $info["error"] = curl_error($curl); $info["request"] = json_encode($fields); $info["response"] = $response; curl_close($curl); return $info; } /** * HTTP POST wrapper * @param $_api string required Target endpoint * @param $data array optional Request body * @param $header array optional Request header * @param $useForm bool optional Treat request body as urlencoded form * @return array Curl response * @return false Response code is not HTTP 200 OK * @author NekoYuzu (MlgmXyysd) * @date 2023/11/20 23:55:41 */ function postApi(string $_api, array $data = array(), array $header = array(), bool $useForm = false): array|false { $response = http($GLOBALS["api"] . $_api, "POST", $data, $header, $useForm); if ($response["http_code"] != 200) { return false; } return json_decode($response["response"], true); } /** * Sign data using HMAC SHA-1 * @param $data string required Data to sign * @return string Signed hash * @author NekoYuzu (MlgmXyysd) * @date 2023/11/21 00:20:56 */ function signData(string $data): string { return strtolower(bin2hex(hash_hmac("sha1", "POST\n/v1/unlock/applyBind\ndata=" . $data . "&sid=miui_sec_android", $GLOBALS["sign_key"], true))); } /** * Decrypt data using AES/CBC/PKCS5Padding * @param $data string required Data to decrypt * @return string Decrypted data * @return false Failed to decrypt * @author NekoYuzu (MlgmXyysd) * @date 2023/11/21 00:15:30 */ function decryptData(string $data): string|false { return openssl_decrypt(base64_decode($data), "AES-128-CBC", $GLOBALS["data_pass"], OPENSSL_RAW_DATA, $GLOBALS["data_iv"]); } /*********************** * Functions End * ***********************/ /********************** * Banner Start * **********************/ logf("************************************", "g"); logf("* Xiaomi HyperOS BootLoader Bypass *", "g"); logf("* By NekoYuzu Version " . $version . " *", "g"); logf("************************************", "g"); logf("GitHub: https://github.com/MlgmXyysd"); logf("XDA: https://xdaforums.com/m/mlgmxyysd.8430637"); logf("X (Twitter): https://x.com/realMlgmXyysd"); logf("PayPal: https://paypal.me/MlgmXyysd"); logf("My Blog: https://www.neko.ink/"); logf("************************************", "g"); /******************** * Banner End * ********************/ /******************** * Main Logic * ********************/ logf("Starting ADB server..."); $adb = new ADB(__DIR__ . DIRECTORY_SEPARATOR . "libraries"); $devices = parseDeviceList($adb); $devices_count = count($devices); while ($devices_count != 1) { if ($devices_count == 0) { logf("Waiting for device connection..."); } else { logf("Only one device is allowed to connect, disconnect others to continue. Current number of devices: " . $devices_count); } sleep(1); $devices = parseDeviceList($adb); $devices_count = count($devices); } $device = $devices[0]; $id = $adb -> getDeviceId($device[1], true); logf("Processing device " . $device[0] . "(" . $device[1] . ")..."); $adb -> clearLogcat($id); $adb -> runAdb($id . "shell svc data enable"); logf("Finding BootLoader unlock bind request..."); $focus = $adb -> getCurrentActivity(); if ($focus[0] != "com.android.settings") { if ($focus[0] != "NotificationShade") { $adb -> runAdb($id . "shell am start -a android.settings.APPLICATION_DEVELOPMENT_SETTINGS"); } } else { if ($focus[1] != "com.android.settings.bootloader.BootloaderStatusActivity") { $adb -> runAdb($id . "shell am start -a android.settings.APPLICATION_DEVELOPMENT_SETTINGS"); } } logf("Now you can bind account in the developer options.", "y", "*"); $args = $headers = null; $process = proc_open($adb -> bin . " " . $id . "logcat *:S CloudDeviceStatus:V", array( 1 => ["pipe", "w"] ), $pipes); if (is_resource($process)) { while (!feof($pipes[1])) { $output = fgets($pipes[1]); if (str_contains($output, "CloudDeviceStatus: args:")) { if (preg_match("/args:(.*)/", $output, $matches)) { $args = trim($matches[1]); } $adb -> runAdb($id . "shell svc data disable"); } if (str_contains($output, "CloudDeviceStatus: headers:")) { if (preg_match("/headers:(.*)/", $output, $matches)) { $headers = trim($matches[1]); } logf("Account bind request found! Let's block it."); break; } } fclose($pipes[1]); } logf("Refactoring parameters..."); $data = json_decode(decryptData($args), true); // V816 is the special identity for HyperOS in MIUI version $data["rom_version"] = str_replace("V816", "V14", $data["rom_version"]); $data = json_encode($data); $sign = signData($data); $headers = decryptData($headers); $cookies = null; if (preg_match("/Cookie=\[(.*)\]/", $headers, $matches)) { $cookies = trim($matches[1]); } logf("Sending POST request..."); $res = postApi("unlock/applyBind", array( "data" => $data, "sid" => "miui_sec_android", "sign" => $sign ), array( "Cookie: " . $cookies, "Content-Type: application/x-www-form-urlencoded" ), true); $adb -> runAdb($id . "shell svc data enable"); if (!$res) { logf("Fail to send request, check your internet connection.", "r", "!"); exit(); } switch ($res["code"]) { case 0: logf("Target account: " . $res["data"]["userId"], "g"); logf("Account bound successfully, wait time can be viewed in the unlock tool.", "g"); break; case 401: logf("Account credentials have expired, re-login to your account in your phone. (401)", "y"); break; case 20086: logf("Device credentials expired. (20086)", "y"); break; case 30001: logf("Binding failed, this device has been forced to verify the account qualification by Xiaomi. (30001)", "y"); break; case 86015: logf("Fail to bind account, invalid device signature. (86015)", "y"); break; default: logf($res["descEN"] . " (" . $res["code"] . ")", "y"); }