├── README.md ├── blobs ├── abl.elf ├── abl │ └── LinuxLoader ├── xbl.elf └── xbl │ └── 068AAE46-BDC6-4073-85D5-31C5EEF19A15 ├── fastboot ├── fake_fastboot.c └── unlock.py └── verify ├── main.c ├── verify.S ├── verify.c └── verify.h /README.md: -------------------------------------------------------------------------------- 1 | # Xiaomi bootloader 2 | 3 | Some research about the boot loader used in Xiaomi phones, mainly about how the boot loader's lock implements. 4 | 5 | **TL;DR: It is impossible to bypass Xiaomi's server to unlock the boot loader, unless the boot loader itself contains vulnerabilities.** 6 | 7 | ## Overview 8 | 9 | As expected, Xiaomi's boot loader is modified from Qualcomm's boot loader. The boot loader is divided into three stages, named PBL (Primary Boot Loader), XBL (eXtensible Boot Loader) and ABL (Android Boot Loader). [[1]](#1) 10 | 11 | When powering up, the PBL is executed at first. This program seems to be stored in a non-writable storage in SoC. It is entirely built by Qualcomm and we even have no way to read it (in binary format, of course). Fortunately, it has nothing to do with the topic we're going to talk about. So we just need to know the PBL will load, verify and finally execute the XBL, that's enough. 12 | 13 | The XBL is built by Qualcomm and Xiaomi. There is no available source code, but it is easy to get the XBL itself from the block device `/dev/block/by-name/xbl`. More conveniently, we can extract the XBL from the official MIUI ROM or the firmware update package. [[2]](#2) This program implements the UEFI interface and some UEFI protocols for the ABL. We will discuss it later. Finally, the XBL will load, verify and execute the ABL. 14 | 15 | The ABL is also built by Qualcomm and Xiaomi. Qualcomm's original ABL is open-source. [[3]](#3) Although the source code is modified by Xiaomi in order to add some new features, such as the boot loader lock, having the original source code will make many things become much easier. Similarly to the XBL, the ABL itself can be gotten from the block device `/dev/block/by-name/abl`, the official MIUI ROM or the firmware update package. Note that the fastboot server is implemented in the ABL. In most cases, the ABL will load, verify and finally execute the Linux kernel. 16 | 17 | ## Blobs 18 | 19 | The files `blobs/xbl.elf` and `blobs/abl.elf` are the XBL and ABL binary files, respectively. We need use imgtool to unpack them into several files. [[4]](#4) `blobs/xbl/068AAE46-BDC6-4073-85D5-31C5EEF19A15` is the Mi-Token UEFI protocol (we'll see what it is later), which is extracted from `blobs/xbl.elf`. `blobs/abl/LinuxLoader` is the only file which is extracted from `blobs/abl.elf`. 20 | 21 | Note that I'm using Redmi Note 8T (ginkgo), and the boot loaders are extracted from MIUI V11.0.4.0.PCOCNXM (2020-01-03). Things may be different for another phone or even for MIUI of another version. 22 | 23 | ## Unlocking Process 24 | 25 | Xiaomi provides us an official unlocking tool. How does the tool unlock our phones? Making use of `fastboot/fake_fastboot.c`, we are able to investigate the question easily. Here are the results. 26 | 27 | 1. Get a token using the command ``fastboot getvar token``. 28 | 2. Ask server for a signature of the token. See the reference for details. [[5]](#5) 29 | 3. Let the device verify the signature, and the device will be unlocked if the verification is successful. 30 | See `fastboot/unlock.py` for how to send the command to the device. 31 | 32 | There remain two questions: How is the token generated? How is the signature verified? We'll answer these questions below. 33 | 34 | ## Mi-Token Protocol 35 | 36 | As we mentioned above, the fastboot server is implemented in the ABL. So we can disassmble `blobs/abl/LinuxLoader` in order to find out how the fastboot server responses to `fastboot getvar token` and `fastboot oem unlock`. 37 | 38 | That is not difficult. Actually, the fastboot server will locate the Mi-Token protocol through the UEFI interface. The Mi-Token protocol provides three functions. One is used to get the token. One is used to verify the signature. The other one is ignored by me. The fastboot server is just a wrapper that calls those functions. 39 | 40 | Then we should turn to `blobs/xbl/068AAE46-BDC6-4073-85D5-31C5EEF19A15` for details in Mi-Token protocol. 41 | 42 | ## Token Generation 43 | 44 | I guess that the function which generates the token is intentionally designed to be a little complex. But it is not very difficult to analyse if we are patient. 45 | 46 | First, generate some binary data (36 bytes). In hex format, it is 47 | ```` 48 | 55 01 01 20 01 10 XX XX XX XX XX XX XX XX XX XX 49 | XX XX XX XX XX XX 03 06 67 69 6E 6B 67 6F 02 04 50 | GH IJ KL MN 51 | ```` 52 | where 53 | 1. the region filled by `X` is random generated by the RNG UEFI protocol, 54 | 2. the `20` is the total length `0x24` minus four, 55 | 3. the `67 69 6E 6B 67 6F` is actually the ASCII representation of the codename `ginkgo` and 56 | 4. `0xGHIJKLMN` (instead of `0xMNKLIJGH`) is the serial number gotten from Qualcomm's EfiChipinfo protocol. 57 | 58 | (**TODO:** I wonder why the serial number here is different from the serial number in the Android's system setting. Are they totally different things?) 59 | 60 | Then, the binary data will be encoded to the base64 format, and `+`, `/` will be respectively replaced by `-`, `_`. In the end, we'll get a string containing 48 characters. That is exactly the token. 61 | 62 | ## Signature Verification 63 | 64 | The core assembly code for signature verification is put into `verify/verify.S`, with some modifications to work correctly with `verify/main.c`. I rewrite the code in C language, which is put into `verify/verify.c`. So it means that we can compile `verify/main.c` and `verify/verify.c` together and we can also compile `verify/main.c` and `verify/verify.S` together. There should be no differences between them. 65 | 66 | According to the code, the signature is implemented by the RSA algorithm, which is an asymmetric cryptography system. So it is impossible to fake the Xiaomi server's signature unless we can get the server's private key. 67 | 68 | **TODO:** The `multiply` function seems to multiply two big numbers together. But the algorithm is very trick. I don't understand it at all. A detailed explanation is needed here. 69 | 70 | ## References 71 | 72 | [1] [https://lineageos.org/engineering/Qualcomm-Firmware/](https://lineageos.org/engineering/Qualcomm-Firmware/) *Qualcomm's Chain of Trust* 73 | 74 | [2] [https://xiaomifirmwareupdater.com/](https://xiaomifirmwareupdater.com/) *Xiaomi Fimware Updater* 75 | 76 | [3] [https://source.codeaurora.org/quic/la/abl/tianocore/edk2/tree/QcomModulePkg](https://source.codeaurora.org/quic/la/abl/tianocore/edk2/tree/QcomModulePkg) *Qualcomm's ABL source code* 77 | 78 | [4] [http://newandroidbook.com/tools/imgtool.html](http://newandroidbook.com/tools/imgtool.html) *imgtool* 79 | 80 | [5] [https://github.com/penn5/miunlock](https://github.com/penn5/miunlock) *Mi-Unlock Python Implementation* 81 | 82 | [6] [https://hex-rays.com/products/ida/](https://hex-rays.com/products/ida/) *IDA Disassembler* 83 | -------------------------------------------------------------------------------- /blobs/abl.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrh2000/Xiaomi-bootloader/c41e2a2442240d866757ce923ea287c2f069ac8d/blobs/abl.elf -------------------------------------------------------------------------------- /blobs/abl/LinuxLoader: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrh2000/Xiaomi-bootloader/c41e2a2442240d866757ce923ea287c2f069ac8d/blobs/abl/LinuxLoader -------------------------------------------------------------------------------- /blobs/xbl.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrh2000/Xiaomi-bootloader/c41e2a2442240d866757ce923ea287c2f069ac8d/blobs/xbl.elf -------------------------------------------------------------------------------- /blobs/xbl/068AAE46-BDC6-4073-85D5-31C5EEF19A15: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrh2000/Xiaomi-bootloader/c41e2a2442240d866757ce923ea287c2f069ac8d/blobs/xbl/068AAE46-BDC6-4073-85D5-31C5EEF19A15 -------------------------------------------------------------------------------- /fastboot/fake_fastboot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Modify the following two lines to meet with your own phone. 5 | #define MI_TOKEN "000000000000000000000000000000000000000000000000" 6 | #define MI_PRODUCT "ginkgo" 7 | 8 | void dump_file(FILE *log, const char *filename) 9 | { 10 | FILE *fp = fopen(filename, "rb"); 11 | int c; 12 | 13 | if (!fp) { 14 | fprintf(log, "ERROR: Failed to open the file %s.\n", filename); 15 | return; 16 | } 17 | 18 | fprintf(log, "File '%s' Content: \n'''", filename); 19 | while ((c = fgetc(fp)) != EOF) 20 | fputc(c, log); 21 | fprintf(log, "'''\n"); 22 | } 23 | 24 | int fake_fastboot(FILE *log, char **arg, int len) 25 | { 26 | if (len <= 0) { 27 | fprintf(log, "ERROR: Command is too short.\n"); 28 | return -2; 29 | } 30 | 31 | if (strcmp(arg[0], "getvar") == 0) { 32 | if (len <= 1) { 33 | fprintf(log, "ERROR: Command is too short.\n"); 34 | return -2; 35 | } 36 | 37 | if (strcmp(arg[1], "token") == 0) { 38 | puts("token: " MI_TOKEN); 39 | puts("finished. total time: 0.001s"); 40 | return 0; 41 | } else if (strcmp(arg[1], "product") == 0) { 42 | puts("product: " MI_PRODUCT); 43 | puts("finished. total time: 0.001s"); 44 | return 0; 45 | } else { 46 | printf("getvar:%s FAILED (remote: GetVar Variable not found)\n", arg[1]); 47 | puts("finished. total time: 0.001s"); 48 | return 1; 49 | } 50 | 51 | } else if (strcmp(arg[0], "oem") == 0) { 52 | if (len <= 1) { 53 | fprintf(log, "ERROR: Command is too short.\n"); 54 | return -2; 55 | } 56 | 57 | puts("..."); 58 | if (strcmp(arg[1], "device-info") == 0) { 59 | puts("(bootloader) Verity mode: true"); 60 | puts("(bootloader) Device unlocked: false"); 61 | puts("(bootloader) Device critical unlocked: false"); 62 | puts("(bootloader) Charger screen enabled: true"); 63 | puts("OKAY [ -0.000s]"); 64 | puts("finished. total time: -0.000s"); 65 | return 0; 66 | } else if (strcmp(arg[1], "unlock") == 0) { 67 | if (len <= 2) { 68 | fprintf(log, "ERROR: Command is too short.\n"); 69 | return -2; 70 | } 71 | dump_file(log, arg[2]); 72 | puts("OKAY [ 0.100s]"); 73 | puts("finished. total time: 0.100s"); 74 | return 0; 75 | } else { 76 | puts("FAILED (remote: unknown command)"); 77 | puts("finished. total time: 0.001s"); 78 | return 1; 79 | } 80 | 81 | } else if (strcmp(arg[0], "devices") == 0) { 82 | puts("1234abcd\tfastboot"); 83 | return 0; 84 | } else { 85 | fprintf(log, "ERROR: Unrecognized command.\n"); 86 | return -2; 87 | } 88 | } 89 | 90 | int main(int argc, char **argv) 91 | { 92 | FILE *log = fopen("fastboot_log", "ab"); 93 | char *real_arg[3]; 94 | int len = 0; 95 | int err; 96 | 97 | if (!log) { 98 | fprintf(stderr, "Cannot open the log file.\n"); 99 | return -1; 100 | } 101 | 102 | fprintf(log, "Command: "); 103 | for (int i = 0;i < argc;++i) 104 | fprintf(log, "%s%c", argv[i], " \n"[i == argc - 1]); 105 | 106 | for (int i = 1;i < argc && len < 3;++i) 107 | { 108 | if (strcmp(argv[i], "-s") == 0) { 109 | ++i; 110 | continue; 111 | } 112 | real_arg[len++] = argv[i]; 113 | } 114 | 115 | err = fake_fastboot(log, real_arg, len); 116 | fprintf(log, "Processed the command! (Return value: %d)\n\n", err); 117 | 118 | fclose(log); 119 | return err; 120 | } 121 | -------------------------------------------------------------------------------- /fastboot/unlock.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import io 3 | from adb.fastboot import FastbootCommands 4 | from adb.fastboot import FastbootRemoteFailure 5 | 6 | if len(sys.argv) != 2: 7 | print(f"Usage: {sys.argv[0]} [signature file]") 8 | sys.exit(1) 9 | 10 | fdev = FastbootCommands() 11 | fdev.ConnectDevice() 12 | 13 | sig = open(sys.argv[1], 'rb') 14 | sig_data = sig.read() 15 | sig.close() 16 | 17 | print("Ready to unlock your phone.") 18 | print("If it succeeds, it will ERASE ALL YOUR DATA. Take your own risk.") 19 | input("Press to continue and to exit...") 20 | print("") 21 | 22 | fdev.Download(io.BytesIO(sig_data), len(sig_data)) 23 | try: 24 | res = fdev.Oem("unlock") 25 | print("Successful!") 26 | print(f"Remote response: {res}") 27 | except FastbootRemoteFailure as e: 28 | print("Failed.") 29 | print(f"Remote response: {e.args[0][6:]}") 30 | -------------------------------------------------------------------------------- /verify/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "verify.h" 5 | 6 | int main(int argc, char **argv) 7 | { 8 | FILE *fp; 9 | char *filename; 10 | uint8_t sig[0x100]; 11 | uint32_t size; 12 | uint8_t token[48]; 13 | int err; 14 | 15 | if (argc != 2) { 16 | fprintf(stderr, "Usage: %s [signature file]\n", argv[0]); 17 | return 1; 18 | } 19 | 20 | fp = fopen(argv[1], "rb"); 21 | if (!fp) { 22 | fprintf(stderr, "Cannot open the signature file.\n"); 23 | return 1; 24 | } 25 | 26 | size = fread(sig, 1, sizeof(sig), fp); 27 | fclose(fp); 28 | if (size != sizeof(sig)) { 29 | fprintf(stderr, "Cannot load the signature data.\n"); 30 | return 1; 31 | } 32 | 33 | err = verify(sig, sizeof(sig), token, sizeof(token), extracted_key); 34 | 35 | if (err) { 36 | fprintf(stderr, "Invaild signature data.\n"); 37 | } else { 38 | printf("Vaild signature data!\n"); 39 | printf("Corresponding token is: "); 40 | for (int i = 0;i < (int)sizeof(token);++i) 41 | putchar(token[i]); 42 | putchar('\n'); 43 | } 44 | 45 | filename = malloc(strlen(argv[1]) + 10); 46 | if (!filename) { 47 | fprintf(stderr, "Cannot allocate memory.\n"); 48 | return 1; 49 | } 50 | 51 | sprintf(filename, "%s_decrypt", argv[1]); 52 | fp = fopen(filename, "wb"); 53 | free(filename); 54 | if (!fp) { 55 | fprintf(stderr, "Cannot open the decrypt file.\n"); 56 | return 1; 57 | } 58 | 59 | size = fwrite(sig, 1, sizeof(sig), fp); 60 | fclose(fp); 61 | if (size != sizeof(sig)) { 62 | fprintf(stderr, "Cannot store the decrypted signature.\n"); 63 | return 1; 64 | } 65 | 66 | printf("The decrypted signature is saved into the file %s_decrypt\n", argv[1]); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /verify/verify.S: -------------------------------------------------------------------------------- 1 | .global verify 2 | .global multiply 3 | .global subtract 4 | .global extracted_key 5 | 6 | .data 7 | 8 | extracted_key: 9 | .quad 0x5d57403f00000040 10 | .quad 0x39165ba135fb5041 11 | .quad 0x20e96f4a5b217172 12 | .quad 0x55e1a5ba05fcf186 13 | .quad 0xe051ba9e51ee9d3e 14 | .quad 0x75e614bcc71e77e1 15 | .quad 0x71652d68d37bc9d9 16 | .quad 0x393ee7739f8b04e2 17 | .quad 0x855c2df1608ed5e6 18 | .quad 0xc271c1ea811b105d 19 | .quad 0x3c6a081d1807e7fc 20 | .quad 0xc07317a190d20585 21 | .quad 0xa876bb41d3dc3d29 22 | .quad 0xb5f62195dbbe4e5b 23 | .quad 0x460ba7cf3cd83cb7 24 | .quad 0x7d57d06f51a34921 25 | .quad 0xb0669915fa3317dd 26 | .quad 0x029a3a73755682d0 27 | .quad 0x151bd2ed7b438945 28 | .quad 0xf2856318edc49bdf 29 | .quad 0xdcbcf49f866ddf6b 30 | .quad 0x998d3235181d9a1e 31 | .quad 0x5bd5da7e50aabfdb 32 | .quad 0xcc97ef2d4476c365 33 | .quad 0x76f964a575f4438c 34 | .quad 0x238d8e9fc32588c6 35 | .quad 0x530679bfcfe00661 36 | .quad 0x0868dae6788f30b5 37 | .quad 0x8804fdf3938be1fa 38 | .quad 0x731ed3e5d4c2d779 39 | .quad 0xbc13f0288d9a7c16 40 | .quad 0x4f0d13c40ec5c8ab 41 | .quad 0xb7fbcde320b827e3 42 | .quad 0xd0c7d6d2a398baab 43 | .quad 0x9acb6543caeebfa4 44 | .quad 0xf577f1e509e7e9a7 45 | .quad 0x7921d6154131f364 46 | .quad 0xbe9e8cc29d1712b4 47 | .quad 0xdc9eb8272d8cc54c 48 | .quad 0x033c4d08dc0dfc7e 49 | .quad 0xfbe3f298bd6e45c2 50 | .quad 0xdc7e57a0e06c0490 51 | .quad 0xbad9c25c0848d21b 52 | .quad 0x04f04b2a0d941cf6 53 | .quad 0x854ecb29807c58ab 54 | .quad 0xd7919c8fd714f2ba 55 | .quad 0x55aae85606957e7e 56 | .quad 0xa53b22c3f2fd57ba 57 | .quad 0x3c6ab0b8599564d6 58 | .quad 0x4927e8617562feeb 59 | .quad 0x44842445b3b9a1df 60 | .quad 0x5917b807baa06ee4 61 | .quad 0x17ec410f63bb8467 62 | .quad 0x408600a06f4b8956 63 | .quad 0x3a3b13f4fd26c73e 64 | .quad 0x5e9db9dbdcd39eb5 65 | .quad 0x334cf7efaeb45e3a 66 | .quad 0xe0de7c5c48932c8f 67 | .quad 0x0f2477833cb41279 68 | .quad 0x3c7e07211e6af139 69 | .quad 0x5378d72028d9697c 70 | .quad 0xf7c21e60570d55eb 71 | .quad 0x61cb74a4d518da21 72 | .quad 0xb26807fa231f0935 73 | .quad 0x49a70dfe41f1b3e2 74 | .long 0x00010001 75 | 76 | sig: 77 | .quad 0xc672a3de7b34dcda 78 | .quad 0x0ef3c9ea00e2c4b4 79 | .quad 0x816f4c4f45c00467 80 | .quad 0x2b7e0974d24f401b 81 | .quad 0x75a456dfffe85bf8 82 | .quad 0xa325e8cd319fd592 83 | .quad 0x0a15e0303a071e60 84 | .quad 0xfe16935bce63d542 85 | .quad 0x1a41067e3ac63805 86 | .quad 0x41d786cf4b9604d3 87 | .quad 0xc9e39cdde57363b7 88 | .quad 0x554d95a0b4c15f49 89 | .quad 0xb79f830c64936380 90 | .quad 0xd47141982f1303fe 91 | .quad 0x2435505cdbf0fb29 92 | .quad 0xb54e95d86c35b0b6 93 | .quad 0x052e35c2f204e36a 94 | .quad 0x2b7c88da0f495810 95 | .quad 0xea54cd1066e9edcd 96 | .quad 0xbcb00cfbc42ea4c7 97 | .quad 0x0ab8fcd1fff36bf5 98 | .quad 0xfaa9f1c3b4b15ed0 99 | .quad 0x4fe9dc8e313bed71 100 | .quad 0xd92d5335afbb4169 101 | .quad 0x65230b08e58719c2 102 | .quad 0xcb4bc042dbe88ca9 103 | .quad 0xd332aca483889604 104 | .quad 0x6dd8a8b02a990254 105 | .quad 0xb14484ec7f03cdde 106 | .quad 0x69d22dfeb8e1af1d 107 | .quad 0x368e75d32e577219 108 | .quad 0xe5d0f426feda8602 109 | 110 | .text 111 | 112 | verify: 113 | STP X28, X0, [SP,#-0x40]! 114 | STP X22, X21, [SP,#0x10] 115 | STP X20, X19, [SP,#0x20] 116 | STP X29, X30, [SP,#0x30] 117 | ADD X29, SP, #0x30 118 | SUB SP, SP, #0x410 119 | MOV X21, X4 120 | MOV X8, X0 121 | MOV X20, X3 122 | MOV X19, X2 123 | MOV W0, #2 124 | CBZ X8, .loc_1DA8 125 | CMP X1, #0x100 126 | B.NE .loc_1DA8 127 | MOV W0, #3 128 | CBZ X19, .loc_1DA8 129 | CMP X20, #0xF5 130 | B.HI .loc_1DA8 131 | CBZ X21, .loc_1C74 132 | MOV X10, XZR 133 | ADD X13, SP, #0x308 134 | B .loc_1B98 135 | .loc_1B8C: 136 | LDRB W9, [X8],#1 137 | ADD X10, X10, #1 138 | STRB W9, [X13],#1 139 | .loc_1B98: 140 | CMP X10, #0x100 141 | B.NE .loc_1B8C 142 | LDRSW X8, [X21] 143 | MOV X10, XZR 144 | ADD X9, SP, #0x208 145 | LSL W12, W8, #2 146 | SUB W11, W12, #4 147 | ADD X12, SP, #0x308 148 | B .loc_1BF4 149 | .loc_1BBC: 150 | ADD W13, W11, #1 151 | LDRB W15, [X12,W11,SXTW] 152 | ADD W14, W11, #2 153 | ADD W16, W11, #3 154 | LDRB W13, [X12,W13,SXTW] 155 | ADD X10, X10, #1 156 | LDRB W14, [X12,W14,SXTW] 157 | LSL W17, W15, #0x18 158 | LDRB W16, [X12,W16,SXTW] 159 | SUB W11, W11, #4 160 | BFI W17, W13, #0x10, #8 161 | BFI W17, W14, #8, #8 162 | ORR W1, W17, W16 163 | STR W1, [X9],#4 164 | .loc_1BF4: 165 | CMP X10, X8 166 | B.LT .loc_1BBC 167 | LDR W8, [X21,#0x208] 168 | CMP W8, #3 169 | B.EQ .loc_1C7C 170 | MOV W11, #0x10001 171 | CMP W8, W11 172 | B.NE .loc_1CC0 173 | ADD X3, X21, #0x108 174 | ADD X1, SP, #0x108 175 | ADD X2, SP, #0x208 176 | MOV X0, X21 177 | BL multiply 178 | MOV W22, WZR 179 | B .loc_1C5C 180 | .loc_1C30: 181 | ADD X1, SP, #8 182 | ADD X2, SP, #0x108 183 | ADD X3, SP, #0x108 184 | MOV X0, X21 185 | BL multiply 186 | ADD X1, SP, #0x108 187 | ADD X2, SP, #8 188 | ADD X3, SP, #8 189 | MOV X0, X21 190 | BL multiply 191 | ADD W22, W22, #2 192 | .loc_1C5C: 193 | CMP W22, #0xF 194 | B.LE .loc_1C30 195 | ADD X22, SP, #8 196 | ADD X1, SP, #8 197 | ADD X2, SP, #0x108 198 | B .loc_1CB0 199 | .loc_1C74: 200 | MOV W0, #4 201 | B .loc_1DA8 202 | .loc_1C7C: 203 | ADD X3, X21, #0x108 204 | ADD X1, SP, #0x108 205 | ADD X2, SP, #0x208 206 | MOV X0, X21 207 | ADD X22, SP, #0x108 208 | BL multiply 209 | ADD X1, SP, #8 210 | ADD X2, SP, #0x108 211 | ADD X3, SP, #0x108 212 | MOV X0, X21 213 | BL multiply 214 | ADD X1, SP, #0x108 215 | ADD X2, SP, #8 216 | .loc_1CB0: 217 | ADD X3, SP, #0x208 218 | MOV X0, X21 219 | BL multiply 220 | B .loc_1CC4 221 | .loc_1CC0: 222 | MOV X22, XZR 223 | .loc_1CC4: 224 | LDR W9, [X21] 225 | SXTW X11, W9 226 | SBFIZ X12, X9, #2, #0x20 227 | .loc_1CD0: 228 | CBZ W11, .loc_1CFC 229 | ADD X16, X22, X12 230 | ADD X17, X21, X12 231 | LDUR W8, [X16,#-4] 232 | LDR W10, [X17,#4] 233 | CMP W8, W10 234 | B.CC .loc_1D0C 235 | SUB X11, X11, #1 236 | SUB X12, X12, #4 237 | CMP W8, W10 238 | B.LS .loc_1CD0 239 | .loc_1CFC: 240 | MOV X0, X21 241 | MOV X1, X22 242 | BL subtract 243 | LDR W9, [X21] 244 | .loc_1D0C: 245 | ADD X8, SP, #0x308 246 | SXTW X13, W9 247 | B .loc_1D44 248 | .loc_1D18: 249 | ADD X0, X22, X13,LSL#2 250 | MOV X13, X11 251 | LDUR W12, [X0,#-4] 252 | LSR W2, W12, #0x18 253 | STRB W12, [X8,#3] 254 | LSR W3, W12, #0x10 255 | LSR W4, W12, #8 256 | STRB W2, [X8] 257 | STRB W3, [X8,#1] 258 | STRB W4, [X8,#2] 259 | ADD X8, X8, #4 260 | .loc_1D44: 261 | SUBS X11, X13, #1 262 | B.GE .loc_1D18 263 | LDRB W5, [SP,#0x308] 264 | CBNZ W5, .loc_1DA4 265 | LDRB W9, [SP,#0x309] 266 | CMP W9, #1 267 | B.NE .loc_1DA4 268 | MOV W12, #0x100 269 | MOV W13, #0xFF 270 | SUB X9, X12, X20 271 | SUB X10, X13, X20 272 | MOV W14, #2 273 | ADD X15, SP, #0x308 274 | MOV W0, #5 275 | .loc_1D7C: 276 | CMP X14, X10 277 | B.CS .loc_1D98 278 | LDRB W17, [X15,X14] 279 | ADD X14, X14, #1 280 | CMP W17, #0xFF 281 | B.EQ .loc_1D7C 282 | B .loc_1DA8 283 | .loc_1D98: 284 | ADD X11, SP, #0x308 285 | LDRB W6, [X11,X10] 286 | CBZ W6, .loc_1DEC 287 | .loc_1DA4: 288 | MOV W0, #5 289 | .loc_1DA8: 290 | ADD SP, SP, #0x410 291 | LDP X29, X30, [SP,#0x30] 292 | LDP X20, X19, [SP,#0x20] 293 | LDP X22, X21, [SP,#0x10] 294 | LDP X28, X1, [SP],#0x40 295 | 296 | ADD X2, SP, #-0x148 297 | MOV X3, #0x20 298 | 1: 299 | LDR X4, [X2],#8 300 | STR X4, [X1],#8 301 | SUBS X3, X3, #1 302 | B.NE 1b 303 | 304 | RET 305 | .loc_1DD4: 306 | LDRB W7, [X11,X9] 307 | STRB W7, [X19] 308 | ADD X9, X9, #1 309 | ADD X19, X19, #1 310 | .loc_1DEC: 311 | CMP X9, #0xFF 312 | B.LS .loc_1DD4 313 | CMP X9, #0x100 314 | CSET W0, NE 315 | B .loc_1DA8 316 | .loc_1E00: 317 | MOV W0, #1 318 | B .loc_1DA8 319 | 320 | multiply: 321 | STR X25, [SP,#-0x50]! 322 | STP X24, X23, [SP,#0x10] 323 | STP X22, X21, [SP,#0x20] 324 | STP X20, X19, [SP,#0x30] 325 | STP X29, X30, [SP,#0x40] 326 | ADD X29, SP, #0x40 327 | MOV X19, X3 328 | MOV X20, X2 329 | MOV X21, X1 330 | MOV X22, X0 331 | MOV X9, XZR 332 | B .loc_1E44 333 | .loc_1E3C: 334 | STR WZR, [X21,X9,LSL#2] 335 | ADD X9, X9, #1 336 | .loc_1E44: 337 | LDRSW X8, [X22] 338 | CMP X9, X8 339 | B.LT .loc_1E3C 340 | MOV X23, XZR 341 | ADD X24, X19, #4 342 | MOV X25, #0x100000000 343 | B .loc_1EF8 344 | .loc_1E60: 345 | LDR W10, [X20],#4 346 | LDR W13, [X19] 347 | MOV X11, XZR 348 | LDR W15, [X21] 349 | MOV X9, XZR 350 | LDP W12, W17, [X22,#4] 351 | MADD X14, X13, X10, X15 352 | MUL W12, W12, W14 353 | MUL X15, X12, X17 354 | ADD X13, X15, W14,UXTW 355 | B .loc_1EC4 356 | .loc_1E8C: 357 | ADD X8, X22, X11,LSL#2 358 | ADD X17, X21, X11,LSL#2 359 | LDR W11, [X24,X11,LSL#2] 360 | ADD X9, X9, X25 361 | LDR W0, [X8,#0xC] 362 | LDR W1, [X17,#4] 363 | MADD X2, X11, X10, X16 364 | MOV X11, X15 365 | MUL X3, X0, X12 366 | ADD X14, X2, X1 367 | ADD X16, X3, X13,LSR#32 368 | ADD X13, X16, W14,UXTW 369 | STR W13, [X17] 370 | LDR W8, [X22] 371 | .loc_1EC4: 372 | ADD X15, X11, #1 373 | LSR X16, X14, #0x20 374 | CMP X15, W8,SXTW 375 | B.LT .loc_1E8C 376 | ADD X4, X16, X13,LSR#32 377 | ASR X5, X9, #0x1E 378 | STR W4, [X21,X5] 379 | TBZ X4, #0x20, .loc_1EF0 380 | MOV X0, X22 381 | MOV X1, X21 382 | BL subtract 383 | .loc_1EF0: 384 | LDR W8, [X22] 385 | ADD X23, X23, #1 386 | .loc_1EF8: 387 | CMP X23, W8,SXTW 388 | B.LT .loc_1E60 389 | LDP X29, X30, [SP,#0x40] 390 | LDP X20, X19, [SP,#0x30] 391 | LDP X22, X21, [SP,#0x20] 392 | LDP X24, X23, [SP,#0x10] 393 | LDR X25, [SP],#0x50 394 | RET 395 | 396 | subtract: 397 | MOV X8, XZR 398 | MOV X10, XZR 399 | ADD X9, X0, #8 400 | B .loc_1F44 401 | .loc_1F28: 402 | LDR W12, [X1] 403 | ADD X8, X8, #1 404 | LDR W13, [X9],#4 405 | SUB X14, X12, X13 406 | ADD X15, X14, X10 407 | ASR X10, X15, #0x20 408 | STR W15, [X1],#4 409 | .loc_1F44: 410 | LDRSW X11, [X0] 411 | CMP X8, X11 412 | B.LT .loc_1F28 413 | RET 414 | -------------------------------------------------------------------------------- /verify/verify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "verify.h" 3 | 4 | const uint32_t extracted_key[0x83] = { 5 | 0x00000040, 0x5d57403f, 0x35fb5041, 0x39165ba1, 6 | 0x5b217172, 0x20e96f4a, 0x05fcf186, 0x55e1a5ba, 7 | 0x51ee9d3e, 0xe051ba9e, 0xc71e77e1, 0x75e614bc, 8 | 0xd37bc9d9, 0x71652d68, 0x9f8b04e2, 0x393ee773, 9 | 0x608ed5e6, 0x855c2df1, 0x811b105d, 0xc271c1ea, 10 | 0x1807e7fc, 0x3c6a081d, 0x90d20585, 0xc07317a1, 11 | 0xd3dc3d29, 0xa876bb41, 0xdbbe4e5b, 0xb5f62195, 12 | 0x3cd83cb7, 0x460ba7cf, 0x51a34921, 0x7d57d06f, 13 | 0xfa3317dd, 0xb0669915, 0x755682d0, 0x029a3a73, 14 | 0x7b438945, 0x151bd2ed, 0xedc49bdf, 0xf2856318, 15 | 0x866ddf6b, 0xdcbcf49f, 0x181d9a1e, 0x998d3235, 16 | 0x50aabfdb, 0x5bd5da7e, 0x4476c365, 0xcc97ef2d, 17 | 0x75f4438c, 0x76f964a5, 0xc32588c6, 0x238d8e9f, 18 | 0xcfe00661, 0x530679bf, 0x788f30b5, 0x0868dae6, 19 | 0x938be1fa, 0x8804fdf3, 0xd4c2d779, 0x731ed3e5, 20 | 0x8d9a7c16, 0xbc13f028, 0x0ec5c8ab, 0x4f0d13c4, 21 | 0x20b827e3, 0xb7fbcde3, 0xa398baab, 0xd0c7d6d2, 22 | 0xcaeebfa4, 0x9acb6543, 0x09e7e9a7, 0xf577f1e5, 23 | 0x4131f364, 0x7921d615, 0x9d1712b4, 0xbe9e8cc2, 24 | 0x2d8cc54c, 0xdc9eb827, 0xdc0dfc7e, 0x033c4d08, 25 | 0xbd6e45c2, 0xfbe3f298, 0xe06c0490, 0xdc7e57a0, 26 | 0x0848d21b, 0xbad9c25c, 0x0d941cf6, 0x04f04b2a, 27 | 0x807c58ab, 0x854ecb29, 0xd714f2ba, 0xd7919c8f, 28 | 0x06957e7e, 0x55aae856, 0xf2fd57ba, 0xa53b22c3, 29 | 0x599564d6, 0x3c6ab0b8, 0x7562feeb, 0x4927e861, 30 | 0xb3b9a1df, 0x44842445, 0xbaa06ee4, 0x5917b807, 31 | 0x63bb8467, 0x17ec410f, 0x6f4b8956, 0x408600a0, 32 | 0xfd26c73e, 0x3a3b13f4, 0xdcd39eb5, 0x5e9db9db, 33 | 0xaeb45e3a, 0x334cf7ef, 0x48932c8f, 0xe0de7c5c, 34 | 0x3cb41279, 0x0f247783, 0x1e6af139, 0x3c7e0721, 35 | 0x28d9697c, 0x5378d720, 0x570d55eb, 0xf7c21e60, 36 | 0xd518da21, 0x61cb74a4, 0x231f0935, 0xb26807fa, 37 | 0x41f1b3e2, 0x49a70dfe, 0x00010001, 38 | }; 39 | 40 | static inline uint32_t reverse_bytes(uint32_t k) 41 | { 42 | return ((k & 0xff) << 0x18) | ((k & 0xff00) << 0x8) | 43 | ((k & 0xff0000) >> 0x8) | ((k & 0xff000000) >> 0x18); 44 | } 45 | 46 | void substract(const uint32_t *key, uint32_t *dst) 47 | { 48 | uint32_t len = *key; 49 | uint64_t x10 = 0; 50 | 51 | for (uint32_t j = 0;j < len;++j) 52 | { 53 | // w12 = dst[j] 54 | // w13 = key[j + 2] 55 | uint64_t x15; 56 | 57 | x15 = (uint64_t)dst[j] - (uint64_t)key[j + 2] + x10; 58 | x10 = (int64_t)x15 >> 32; 59 | dst[j] = (uint32_t)x15; 60 | } 61 | } 62 | 63 | void multiply(const uint32_t *key, uint32_t *dst, 64 | const uint32_t *src1, const uint32_t *src2) 65 | { 66 | uint32_t len = *key; // X8 67 | 68 | memset(dst, 0, sizeof(uint32_t) * len); 69 | 70 | for (uint32_t i = 0;i < len;++i) // X23 71 | { 72 | // w10 = src1[i]; 73 | // w13 = src2[0]; 74 | // w15 = dst[0]; 75 | // w12 = key[1]; 76 | // w17 = key[2]; 77 | uint32_t w12; 78 | uint64_t x14, x13, x4; 79 | 80 | x14 = (uint64_t)src2[0] * (uint64_t)src1[i] + (uint64_t)dst[0]; 81 | w12 = key[1] * (uint32_t)x14; 82 | x13 = (uint64_t)w12 * (uint64_t)key[2] + (uint64_t)((uint32_t)x14); 83 | 84 | for (uint32_t j = 0;j + 1 < len;++j) // X11 85 | { 86 | // x8 = &key[j] 87 | // x17 = &dst[j] 88 | // w11 = src2[j + 1]; 89 | // w0 = key[j + 3]; 90 | // w1 = dst[j + 1]; 91 | uint64_t x2, x16; 92 | 93 | x2 = (uint64_t)src2[j + 1] * (uint64_t)src1[i] + (x14 >> 32); 94 | x16 = (uint64_t)w12 * (uint64_t)key[j + 3] + (x13 >> 32); 95 | x14 = x2 + (uint64_t)dst[j + 1]; 96 | x13 = (uint32_t)x14 + x16; 97 | 98 | dst[j] = (uint32_t)x13; 99 | } 100 | x4 = (x14 >> 32) + (x13 >> 32); 101 | dst[len - 1] = (uint32_t)x4; 102 | 103 | if (x4 & (1ull << 32)) 104 | substract(key, dst); 105 | } 106 | } 107 | 108 | int verify(void *data, uint32_t data_len, 109 | uint8_t *token, uint32_t token_len, const uint32_t *key) 110 | { 111 | uint32_t num1[0x40], num2[0x40], num3[0x40], num4[0x40]; 112 | uint32_t len = *key; // 0x40 113 | uint8_t *num = (uint8_t *)num1; 114 | 115 | if (!data) 116 | return -1; 117 | if (data_len != 0x100) 118 | return -1; 119 | if (!token) 120 | return -1; 121 | if (token_len > 0xf5) 122 | return -1; 123 | if (!key) 124 | return -1; 125 | 126 | memcpy(num1, data, data_len); 127 | for (uint32_t i = 0;i < len;++i) 128 | num2[i] = reverse_bytes(num1[len - i - 1]); 129 | 130 | if (key[0x82] != 0x10001) 131 | return -1; // Not implemented. 132 | 133 | multiply(key, num3, num2, &key[0x42]); 134 | for (uint32_t i = 0;i < 16;i += 2) 135 | { 136 | multiply(key, num4, num3, num3); 137 | multiply(key, num3, num4, num4); 138 | } 139 | multiply(key, num4, num3, num2); 140 | 141 | for (uint32_t i = len - 1;~i;--i) 142 | { 143 | if (num4[i] < key[i + 2]) 144 | break; 145 | if (num4[i] > key[i + 2]) { 146 | substract(key, num4); 147 | break; 148 | } 149 | } 150 | 151 | for (uint32_t i = len - 1;~i;--i) 152 | num1[len - i - 1] = reverse_bytes(num4[i]); 153 | memcpy(data, num, data_len); 154 | 155 | if (num[0] != 0) 156 | return -2; 157 | if (num[1] != 1) 158 | return -2; 159 | 160 | for (uint32_t i = 2;i < 0xff - token_len;++i) 161 | if (num[i] != 0xff) 162 | return -2; 163 | if (num[0xff - token_len] != 0) 164 | return -2; 165 | 166 | for (uint32_t i = 0;i < token_len;++i) 167 | token[i] = num[0x100 - token_len + i]; 168 | 169 | return 0; 170 | } 171 | -------------------------------------------------------------------------------- /verify/verify.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void substract(const uint32_t *key, uint32_t *dst); 5 | 6 | void multiply(const uint32_t *key, uint32_t *dst, 7 | const uint32_t *src1, const uint32_t *src2); 8 | 9 | int verify(void *data, uint32_t data_len, 10 | uint8_t *token, uint32_t token_len, const uint32_t *key); 11 | 12 | extern const uint32_t extracted_key[0x83]; 13 | --------------------------------------------------------------------------------