├── 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 |
--------------------------------------------------------------------------------