blob: 5d671bb672f68966b09330071b024fdaa78ec853 [file] [log] [blame]
Nolen Johnson802fd002019-08-07 23:56:48 -04001/*
2 * loki_patch
3 *
4 * A utility to patch unsigned boot and recovery images to make
5 * them suitable for booting on the AT&T/Verizon Samsung
6 * Galaxy S4, Galaxy Stellar, and various locked LG devices
7 *
8 * by Dan Rosenberg (@djrbliss)
9 *
10 */
11
12#include <stdio.h>
13#include <fcntl.h>
14#include <sys/mman.h>
15#include <sys/stat.h>
16#include <string.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include "loki.h"
20
21struct target {
22 char *vendor;
23 char *device;
24 char *build;
25 unsigned long check_sigs;
26 unsigned long hdr;
27 int lg;
28};
29
30struct target targets[] = {
31 {
32 .vendor = "AT&T",
33 .device = "Samsung Galaxy S4",
34 .build = "JDQ39.I337UCUAMDB or JDQ39.I337UCUAMDL",
35 .check_sigs = 0x88e0ff98,
36 .hdr = 0x88f3bafc,
37 .lg = 0,
38 },
39 {
40 .vendor = "Verizon",
41 .device = "Samsung Galaxy S4",
42 .build = "JDQ39.I545VRUAMDK",
43 .check_sigs = 0x88e0fe98,
44 .hdr = 0x88f372fc,
45 .lg = 0,
46 },
47 {
48 .vendor = "DoCoMo",
49 .device = "Samsung Galaxy S4",
50 .build = "JDQ39.SC04EOMUAMDI",
51 .check_sigs = 0x88e0fcd8,
52 .hdr = 0x88f0b2fc,
53 .lg = 0,
54 },
55 {
56 .vendor = "Verizon",
57 .device = "Samsung Galaxy Stellar",
58 .build = "IMM76D.I200VRALH2",
59 .check_sigs = 0x88e0f5c0,
60 .hdr = 0x88ed32e0,
61 .lg = 0,
62 },
63 {
64 .vendor = "Verizon",
65 .device = "Samsung Galaxy Stellar",
66 .build = "JZO54K.I200VRBMA1",
67 .check_sigs = 0x88e101ac,
68 .hdr = 0x88ed72e0,
69 .lg = 0,
70 },
71 {
72 .vendor = "T-Mobile",
73 .device = "LG Optimus F3Q",
74 .build = "D52010c",
75 .check_sigs = 0x88f1079c,
76 .hdr = 0x88f64508,
77 .lg = 1,
78 },
79 {
80 .vendor = "DoCoMo",
81 .device = "LG Optimus G",
82 .build = "L01E20b",
83 .check_sigs = 0x88F10E48,
84 .hdr = 0x88F54418,
85 .lg = 1,
86 },
87 {
88 .vendor = "DoCoMo",
89 .device = "LG Optimus it L05E",
90 .build = "L05E10d",
91 .check_sigs = 0x88f1157c,
92 .hdr = 0x88f31e10,
93 .lg = 1,
94 },
95 {
96 .vendor = "DoCoMo",
97 .device = "LG Optimus G Pro",
98 .build = "L04E10f",
99 .check_sigs = 0x88f1102c,
100 .hdr = 0x88f54418,
101 .lg = 1,
102 },
103 {
104 .vendor = "AT&T or HK",
105 .device = "LG Optimus G Pro",
106 .build = "E98010g or E98810b",
107 .check_sigs = 0x88f11084,
108 .hdr = 0x88f54418,
109 .lg = 1,
110 },
111 {
112 .vendor = "KT, LGU, or SKT",
113 .device = "LG Optimus G Pro",
114 .build = "F240K10o, F240L10v, or F240S10w",
115 .check_sigs = 0x88f110b8,
116 .hdr = 0x88f54418,
117 .lg = 1,
118 },
119 {
120 .vendor = "KT, LGU, or SKT",
121 .device = "LG Optimus LTE 2",
122 .build = "F160K20g, F160L20f, F160LV20d, or F160S20f",
123 .check_sigs = 0x88f10864,
124 .hdr = 0x88f802b8,
125 .lg = 1,
126 },
127 {
128 .vendor = "MetroPCS",
129 .device = "LG Spirit",
130 .build = "MS87010a_05",
131 .check_sigs = 0x88f0e634,
132 .hdr = 0x88f68194,
133 .lg = 1,
134 },
135 {
136 .vendor = "MetroPCS",
137 .device = "LG Motion",
138 .build = "MS77010f_01",
139 .check_sigs = 0x88f1015c,
140 .hdr = 0x88f58194,
141 .lg = 1,
142 },
143 {
144 .vendor = "Verizon",
145 .device = "LG Lucid 2",
146 .build = "VS87010B_12",
147 .check_sigs = 0x88f10adc,
148 .hdr = 0x88f702bc,
149 .lg = 1,
150 },
151 {
152 .vendor = "Verizon",
153 .device = "LG Spectrum 2",
154 .build = "VS93021B_05",
155 .check_sigs = 0x88f10c10,
156 .hdr = 0x88f84514,
157 .lg = 1,
158 },
159 {
160 .vendor = "Boost Mobile",
161 .device = "LG Optimus F7",
162 .build = "LG870ZV4_06",
163 .check_sigs = 0x88f11714,
164 .hdr = 0x88f842ac,
165 .lg = 1,
166 },
167 {
168 .vendor = "US Cellular",
169 .device = "LG Optimus F7",
170 .build = "US78011a",
171 .check_sigs = 0x88f112c8,
172 .hdr = 0x88f84518,
173 .lg = 1,
174 },
175 {
176 .vendor = "Sprint",
177 .device = "LG Optimus F7",
178 .build = "LG870ZV5_02",
179 .check_sigs = 0x88f11710,
180 .hdr = 0x88f842a8,
181 .lg = 1,
182 },
183 {
184 .vendor = "Virgin Mobile",
185 .device = "LG Optimus F3",
186 .build = "LS720ZV5",
187 .check_sigs = 0x88f108f0,
188 .hdr = 0x88f854f4,
189 .lg = 1,
190 },
191 {
192 .vendor = "T-Mobile and MetroPCS",
193 .device = "LG Optimus F3",
194 .build = "LS720ZV5",
195 .check_sigs = 0x88f10264,
196 .hdr = 0x88f64508,
197 .lg = 1,
198 },
199 {
200 .vendor = "AT&T",
201 .device = "LG G2",
202 .build = "D80010d",
203 .check_sigs = 0xf8132ac,
204 .hdr = 0xf906440,
205 .lg = 1,
206 },
207 {
208 .vendor = "Verizon",
209 .device = "LG G2",
210 .build = "VS98010b",
211 .check_sigs = 0xf8131f0,
212 .hdr = 0xf906440,
213 .lg = 1,
214 },
215 {
216 .vendor = "AT&T",
217 .device = "LG G2",
218 .build = "D80010o",
219 .check_sigs = 0xf813428,
220 .hdr = 0xf904400,
221 .lg = 1,
222 },
223 {
224 .vendor = "Verizon",
225 .device = "LG G2",
226 .build = "VS98012b",
227 .check_sigs = 0xf813210,
228 .hdr = 0xf906440,
229 .lg = 1,
230 },
231 {
232 .vendor = "T-Mobile or Canada",
233 .device = "LG G2",
234 .build = "D80110c or D803",
235 .check_sigs = 0xf813294,
236 .hdr = 0xf906440,
237 .lg = 1,
238 },
239 {
240 .vendor = "International",
241 .device = "LG G2",
242 .build = "D802b",
243 .check_sigs = 0xf813a70,
244 .hdr = 0xf9041c0,
245 .lg = 1,
246 },
247 {
248 .vendor = "Sprint",
249 .device = "LG G2",
250 .build = "LS980ZV7",
251 .check_sigs = 0xf813460,
252 .hdr = 0xf9041c0,
253 .lg = 1,
254 },
255 {
256 .vendor = "KT or LGU",
257 .device = "LG G2",
258 .build = "F320K, F320L",
259 .check_sigs = 0xf81346c,
260 .hdr = 0xf8de440,
261 .lg = 1,
262 },
263 {
264 .vendor = "SKT",
265 .device = "LG G2",
266 .build = "F320S",
267 .check_sigs = 0xf8132e4,
268 .hdr = 0xf8ee440,
269 .lg = 1,
270 },
271 {
272 .vendor = "SKT",
273 .device = "LG G2",
274 .build = "F320S11c",
275 .check_sigs = 0xf813470,
276 .hdr = 0xf8de440,
277 .lg = 1,
278 },
279 {
280 .vendor = "DoCoMo",
281 .device = "LG G2",
282 .build = "L-01F",
283 .check_sigs = 0xf813538,
284 .hdr = 0xf8d41c0,
285 .lg = 1,
286 },
287 {
288 .vendor = "KT",
289 .device = "LG G Flex",
290 .build = "F340K",
291 .check_sigs = 0xf8124a4,
292 .hdr = 0xf8b6440,
293 .lg = 1,
294 },
295 {
296 .vendor = "KDDI",
297 .device = "LG G Flex",
298 .build = "LGL2310d",
299 .check_sigs = 0xf81261c,
300 .hdr = 0xf8b41c0,
301 .lg = 1,
302 },
303 {
304 .vendor = "International",
305 .device = "LG Optimus F5",
306 .build = "P87510e",
307 .check_sigs = 0x88f10a9c,
308 .hdr = 0x88f702b8,
309 .lg = 1,
310 },
311 {
312 .vendor = "SKT",
313 .device = "LG Optimus LTE 3",
314 .build = "F260S10l",
315 .check_sigs = 0x88f11398,
316 .hdr = 0x88f8451c,
317 .lg = 1,
318 },
319 {
320 .vendor = "International",
321 .device = "LG G Pad 8.3",
322 .build = "V50010a",
323 .check_sigs = 0x88f10814,
324 .hdr = 0x88f801b8,
325 .lg = 1,
326 },
327 {
328 .vendor = "International",
329 .device = "LG G Pad 8.3",
330 .build = "V50010c or V50010e",
331 .check_sigs = 0x88f108bc,
332 .hdr = 0x88f801b8,
333 .lg = 1,
334 },
335 {
336 .vendor = "Verizon",
337 .device = "LG G Pad 8.3",
338 .build = "VK81010c",
339 .check_sigs = 0x88f11080,
340 .hdr = 0x88fd81b8,
341 .lg = 1,
342 },
343 {
344 .vendor = "International",
345 .device = "LG Optimus L9 II",
346 .build = "D60510a",
347 .check_sigs = 0x88f10d98,
348 .hdr = 0x88f84aa4,
349 .lg = 1,
350 },
351 {
352 .vendor = "MetroPCS",
353 .device = "LG Optimus F6",
354 .build = "MS50010e",
355 .check_sigs = 0x88f10260,
356 .hdr = 0x88f70508,
357 .lg = 1,
358 },
359 {
360 .vendor = "Open EU",
361 .device = "LG Optimus F6",
362 .build = "D50510a",
363 .check_sigs = 0x88f10284,
364 .hdr = 0x88f70aa4,
365 .lg = 1,
366 },
367 {
368 .vendor = "KDDI",
369 .device = "LG Isai",
370 .build = "LGL22",
371 .check_sigs = 0xf813458,
372 .hdr = 0xf8d41c0,
373 .lg = 1,
374 },
375 {
376 .vendor = "KDDI",
377 .device = "LG",
378 .build = "LGL21",
379 .check_sigs = 0x88f10218,
380 .hdr = 0x88f50198,
381 .lg = 1,
382 },
383 {
384 .vendor = "KT",
385 .device = "LG Optimus GK",
386 .build = "F220K",
387 .check_sigs = 0x88f11034,
388 .hdr = 0x88f54418,
389 .lg = 1,
390 },
391 {
392 .vendor = "International",
393 .device = "LG Vu 3",
394 .build = "F300L",
395 .check_sigs = 0xf813170,
396 .hdr = 0xf8d2440,
397 .lg = 1,
398 },
399 {
400 .vendor = "Sprint",
401 .device = "LG Viper",
402 .build = "LS840ZVK",
403 .check_sigs = 0x4010fe18,
404 .hdr = 0x40194198,
405 .lg = 1,
406 },
407 {
408 .vendor = "International",
409 .device = "LG G Flex",
410 .build = "D95510a",
411 .check_sigs = 0xf812490,
412 .hdr = 0xf8c2440,
413 .lg = 1,
414 },
415 {
416 .vendor = "Sprint",
417 .device = "LG Mach",
418 .build = "LS860ZV7",
419 .check_sigs = 0x88f102b4,
420 .hdr = 0x88f6c194,
421 .lg = 1,
422 },
423};
424
425static unsigned char patch[] = PATCH;
426
427int patch_shellcode(unsigned int header, unsigned int ramdisk)
428{
429
430 unsigned int i;
431 int found_header, found_ramdisk;
432 unsigned int *ptr;
433
434 found_header = 0;
435 found_ramdisk = 0;
436
437 for (i = 0; i < sizeof(patch); i++) {
438 ptr = (unsigned int *)&patch[i];
439 if (*ptr == 0xffffffff) {
440 *ptr = header;
441 found_header = 1;
442 }
443
444 if (*ptr == 0xeeeeeeee) {
445 *ptr = ramdisk;
446 found_ramdisk = 1;
447 }
448 }
449
450 if (found_header && found_ramdisk)
451 return 0;
452
453 return -1;
454}
455
456int loki_patch(const char* partition_label, const char* aboot_image, const char* in_image, const char* out_image)
457{
458 int ifd, ofd, aboot_fd, pos, i, recovery, offset, fake_size;
459 unsigned int orig_ramdisk_size, orig_kernel_size, page_kernel_size, page_ramdisk_size, page_size, page_mask;
460 unsigned long target, aboot_base;
461 void *orig, *aboot, *ptr;
462 struct target *tgt;
463 struct stat st;
464 struct boot_img_hdr *hdr;
465 struct loki_hdr *loki_hdr;
466 char *buf;
467
468 if (!strcmp(partition_label, "boot")) {
469 recovery = 0;
470 } else if (!strcmp(partition_label, "recovery")) {
471 recovery = 1;
472 } else {
473 printf("[+] First argument must be \"boot\" or \"recovery\".\n");
474 return 1;
475 }
476
477 /* Open input files */
478 aboot_fd = open(aboot_image, O_RDONLY);
479 if (aboot_fd < 0) {
480 printf("[-] Failed to open %s for reading.\n", aboot_image);
481 return 1;
482 }
483
484 ifd = open(in_image, O_RDONLY);
485 if (ifd < 0) {
486 printf("[-] Failed to open %s for reading.\n", in_image);
487 return 1;
488 }
489
490 ofd = open(out_image, O_WRONLY|O_CREAT|O_TRUNC, 0644);
491 if (ofd < 0) {
492 printf("[-] Failed to open %s for writing.\n", out_image);
493 return 1;
494 }
495
496 /* Find the signature checking function via pattern matching */
497 if (fstat(aboot_fd, &st)) {
498 printf("[-] fstat() failed.\n");
499 return 1;
500 }
501
502 aboot = mmap(0, (st.st_size + 0xfff) & ~0xfff, PROT_READ, MAP_PRIVATE, aboot_fd, 0);
503 if (aboot == MAP_FAILED) {
504 printf("[-] Failed to mmap aboot.\n");
505 return 1;
506 }
507
508 target = 0;
509 aboot_base = *(unsigned int *)(aboot + 12) - 0x28;
510
511 for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
512 if (!memcmp(ptr, PATTERN1, 8) ||
513 !memcmp(ptr, PATTERN2, 8) ||
514 !memcmp(ptr, PATTERN3, 8) ||
515 !memcmp(ptr, PATTERN4, 8) ||
516 !memcmp(ptr, PATTERN5, 8)) {
517
518 target = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
519 break;
520 }
521 }
522
523 /* Do a second pass for the second LG pattern. This is necessary because
524 * apparently some LG models have both LG patterns, which throws off the
525 * fingerprinting. */
526
527 if (!target) {
528 for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
529 if (!memcmp(ptr, PATTERN6, 8)) {
530
531 target = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
532 break;
533 }
534 }
535 }
536
537 if (!target) {
538 printf("[-] Failed to find function to patch.\n");
539 return 1;
540 }
541
542 tgt = NULL;
543
544 for (i = 0; i < (sizeof(targets)/sizeof(targets[0])); i++) {
545 if (targets[i].check_sigs == target) {
546 tgt = &targets[i];
547 break;
548 }
549 }
550
551 if (!tgt) {
552 printf("[-] Unsupported aboot image.\n");
553 return 1;
554 }
555
556 printf("[+] Detected target %s %s build %s\n", tgt->vendor, tgt->device, tgt->build);
557
558 /* Map the original boot/recovery image */
559 if (fstat(ifd, &st)) {
560 printf("[-] fstat() failed.\n");
561 return 1;
562 }
563
564 orig = mmap(0, (st.st_size + 0x2000 + 0xfff) & ~0xfff, PROT_READ|PROT_WRITE, MAP_PRIVATE, ifd, 0);
565 if (orig == MAP_FAILED) {
566 printf("[-] Failed to mmap input file.\n");
567 return 1;
568 }
569
570 hdr = orig;
571 loki_hdr = orig + 0x400;
572
573 if (!memcmp(loki_hdr->magic, "LOKI", 4)) {
574 printf("[-] Input file is already a Loki image.\n");
575
576 /* Copy the entire file to the output transparently */
577 if (write(ofd, orig, st.st_size) != st.st_size) {
578 printf("[-] Failed to copy Loki image.\n");
579 return 1;
580 }
581
582 printf("[+] Copied Loki image to %s.\n", out_image);
583
584 return 0;
585 }
586
587 /* Set the Loki header */
588 memcpy(loki_hdr->magic, "LOKI", 4);
589 loki_hdr->recovery = recovery;
590 strncpy(loki_hdr->build, tgt->build, sizeof(loki_hdr->build) - 1);
591
592 page_size = hdr->page_size;
593 page_mask = hdr->page_size - 1;
594
595 orig_kernel_size = hdr->kernel_size;
596 orig_ramdisk_size = hdr->ramdisk_size;
597
598 printf("[+] Original kernel address: %.08x\n", hdr->kernel_addr);
599 printf("[+] Original ramdisk address: %.08x\n", hdr->ramdisk_addr);
600
601 /* Store the original values in unused fields of the header */
602 loki_hdr->orig_kernel_size = orig_kernel_size;
603 loki_hdr->orig_ramdisk_size = orig_ramdisk_size;
604 loki_hdr->ramdisk_addr = hdr->kernel_addr + ((hdr->kernel_size + page_mask) & ~page_mask);
605
606 if (patch_shellcode(tgt->hdr, hdr->ramdisk_addr) < 0) {
607 printf("[-] Failed to patch shellcode.\n");
608 return 1;
609 }
610
611 /* Ramdisk must be aligned to a page boundary */
612 hdr->kernel_size = ((hdr->kernel_size + page_mask) & ~page_mask) + hdr->ramdisk_size;
613
614 /* Guarantee 16-byte alignment */
615 offset = tgt->check_sigs & 0xf;
616
617 hdr->ramdisk_addr = tgt->check_sigs - offset;
618
619 if (tgt->lg) {
620 fake_size = page_size;
621 hdr->ramdisk_size = page_size;
622 }
623 else {
624 fake_size = 0x200;
625 hdr->ramdisk_size = 0;
626 }
627
628 /* Write the image header */
629 if (write(ofd, orig, page_size) != page_size) {
630 printf("[-] Failed to write header to output file.\n");
631 return 1;
632 }
633
634 page_kernel_size = (orig_kernel_size + page_mask) & ~page_mask;
635
636 /* Write the kernel */
637 if (write(ofd, orig + page_size, page_kernel_size) != page_kernel_size) {
638 printf("[-] Failed to write kernel to output file.\n");
639 return 1;
640 }
641
642 page_ramdisk_size = (orig_ramdisk_size + page_mask) & ~page_mask;
643
644 /* Write the ramdisk */
645 if (write(ofd, orig + page_size + page_kernel_size, page_ramdisk_size) != page_ramdisk_size) {
646 printf("[-] Failed to write ramdisk to output file.\n");
647 return 1;
648 }
649
650 /* Write fake_size bytes of original code to the output */
651 buf = malloc(fake_size);
652 if (!buf) {
653 printf("[-] Out of memory.\n");
654 return 1;
655 }
656
657 lseek(aboot_fd, tgt->check_sigs - aboot_base - offset, SEEK_SET);
658 read(aboot_fd, buf, fake_size);
659
660 if (write(ofd, buf, fake_size) != fake_size) {
661 printf("[-] Failed to write original aboot code to output file.\n");
662 return 1;
663 }
664
665 /* Save this position for later */
666 pos = lseek(ofd, 0, SEEK_CUR);
667
668 /* Write the device tree if needed */
669 if (hdr->dt_size) {
670
671 printf("[+] Writing device tree.\n");
672
673 if (write(ofd, orig + page_size + page_kernel_size + page_ramdisk_size, hdr->dt_size) != hdr->dt_size) {
674 printf("[-] Failed to write device tree to output file.\n");
675 return 1;
676 }
677 }
678
679 lseek(ofd, pos - (fake_size - offset), SEEK_SET);
680
681 /* Write the patch */
682 if (write(ofd, patch, sizeof(patch)) != sizeof(patch)) {
683 printf("[-] Failed to write patch to output file.\n");
684 return 1;
685 }
686
687 close(ifd);
688 close(ofd);
689 close(aboot_fd);
690
691 printf("[+] Output file written to %s\n", out_image);
692
693 return 0;
694}