mkbootimg: Add Loki support
authorNolen Johnson <johnsonnolen@gmail.com>
Thu, 8 Aug 2019 03:56:48 +0000 (23:56 -0400)
committerNolen Johnson <johnsonnolen@gmail.com>
Sun, 11 Aug 2019 06:31:45 +0000 (08:31 +0200)
* I want my target-files built images to be Loki'd if the device
  needs Loki.
* With this, devices that rely on Loki can ship Lineage Recovery
  officially.
* Import loki_tool from: https://github.com/Stricted/android_external_loki.

Change-Id: I45ef363e05566268c8f24f7e8939a2d785478fbe

12 files changed:
Android.mk
loki_tool/Android.mk [new file with mode: 0644]
loki_tool/LICENSE.txt [new file with mode: 0644]
loki_tool/Makefile [new file with mode: 0644]
loki_tool/README.txt [new file with mode: 0644]
loki_tool/loki.h [new file with mode: 0644]
loki_tool/loki_find.c [new file with mode: 0644]
loki_tool/loki_flash.c [new file with mode: 0644]
loki_tool/loki_patch.c [new file with mode: 0644]
loki_tool/loki_unlok.c [new file with mode: 0644]
loki_tool/main.c [new file with mode: 0644]
mkbootimg.mk

index 69ab1afb8d6e855d8667b0f7d68ff50271911be4..b711f944df7f1a685177cb01434ec2ace62c38fd 100644 (file)
@@ -35,6 +35,11 @@ include $(SAM_ROOT)/macloader/Android.mk
 include $(SAM_ROOT)/wifiloader/Android.mk
 endif
 
+# Loki
+ifeq ($(TAGET_NEEDS_LOKI),true)
+include $(SAM_ROOT)/loki_tool/Android.mk
+endif
+
 ifeq ($(BOARD_VENDOR),samsung)
 include $(SAM_ROOT)/AdvancedDisplay/Android.mk
 include $(SAM_ROOT)/audio/Android.mk
diff --git a/loki_tool/Android.mk b/loki_tool/Android.mk
new file mode 100644 (file)
index 0000000..b8f7a4a
--- /dev/null
@@ -0,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+
+# build static binary
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Wno-pointer-arith -Wno-unused-result -Wno-sign-compare
+LOCAL_SRC_FILES := loki_flash.c loki_patch.c loki_find.c loki_unlok.c main.c
+LOCAL_MODULE := loki_tool_static
+LOCAL_MODULE_STEM := loki_tool
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+# build host binary
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Wno-pointer-arith -Wno-unused-result -Wno-sign-compare
+LOCAL_SRC_FILES := loki_flash.c loki_patch.c loki_find.c loki_unlok.c main.c
+LOCAL_MODULE := loki_tool
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/loki_tool/LICENSE.txt b/loki_tool/LICENSE.txt
new file mode 100644 (file)
index 0000000..bc2a914
--- /dev/null
@@ -0,0 +1,25 @@
+Copyright (c) 2013 Dan Rosenberg. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+   
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/loki_tool/Makefile b/loki_tool/Makefile
new file mode 100644 (file)
index 0000000..8d58748
--- /dev/null
@@ -0,0 +1,30 @@
+SRC_LOKI := loki_flash.c loki_patch.c loki_find.c loki_unlok.c main.c
+OBJ_LOKI = $(SRC_LOKI:.c=.o)
+MODULE_LOKI := loki_tool
+
+CC := arm-linux-androideabi-gcc
+CC_STRIP := arm-linux-androideabi-strip
+
+CFLAGS += -g -static -Wall
+#$(LDFLAGS) +=
+
+all: $(MODULE_LOKI)
+
+host: CC := gcc
+host: $(MODULE_LOKI)
+
+%.o: %.c
+       $(CC) -c -o $@ $< $(CFLAGS)
+
+$(MODULE_LOKI): $(OBJ_LOKI)
+       $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
+
+strip:
+       $(CC_STRIP) --strip-unneeded $(MODULE_LOKI)
+       $(CC_STRIP) --strip-debug $(MODULE_LOKI)
+
+clean:
+       rm -f *.o
+       rm -f loki_tool
+
+.phony: host all
diff --git a/loki_tool/README.txt b/loki_tool/README.txt
new file mode 100644 (file)
index 0000000..4b4368a
--- /dev/null
@@ -0,0 +1,81 @@
+
+WARNING:
+
+This project is no longer actively maintained, because the vulnerability that
+it leverages was patched several years ago. No new updates will be provided.
+The current codebase will remain here for reference purposes.
+
+
+=============================
+Loki
+by Dan Rosenberg (@djrbliss)
+=============================
+
+Loki is a set of tools for creating and flashing custom kernels and recoveries
+on the AT&T and Verizon branded Samsung Galaxy S4, the Samsung Galaxy Stellar,
+and various locked LG devices. For an explanation of how the exploit works,
+please see the technical blog post at:
+
+http://blog.azimuthsecurity.com/2013/05/exploiting-samsung-galaxy-s4-secure-boot.html
+
+Devices must be rooted in order to flash custom kernels and recoveries.
+
+loki_tool:
+[patch] option is primarily intended for developers to create custom
+kernels and recoveries. It's designed to take a specific aboot image and an
+unmodified boot or recovery image, and it generates an output image in a new
+file format, ".lok". The resulting .lok image is specifically tailored for the
+device build it was created with, and can be flashed directly to the recovery
+or boot partition on the target device.
+
+[flash] option can be used to flash a .lok image to an actual device.
+It will verify that the provided .lok image is safe to flash for a given target
+and then perform the flashing if validation is successful. It is also possible
+to simply use "dd" to flash a .lok image directly to the boot or recovery partition,
+but using [flash] option is recommended in order to validate that the .lok matches
+the target device.
+
+
+=============
+Sample usage
+=============
+
+First, a developer must pull the aboot image from a target device:
+
+
+dan@pc:~$ adb shell
+shell@android:/ $ su
+shell@android:/ # dd if=/dev/block/platform/msm_sdcc.1/by-name/aboot of=/data/local/tmp/aboot.img
+shell@android:/ # chmod 644 /data/local/tmp/aboot.img
+shell@android:/ # exit
+shell@android:/ $ exit
+dan@pc:~$ adb pull /data/local/tmp/aboot.img
+3293 KB/s (2097152 bytes in 0.621s)
+
+
+Next, a .lok image can be prepared using loki_tool [patch]:
+
+
+dan@pc:~$ loki_tool patch
+Usage: ./loki_tool [patch] [boot|recovery] [aboot.img] [in.img] [out.lok]
+dan@pc:~$ loki_tool patch recovery aboot.img cwm.img cwm.lok
+[+] Detected target AT&T build JDQ39.I337UCUAMDB or JDQ39.I337UCUAMDL
+[+] Output file written to cwm.lok
+
+
+Finally, the .lok image can be flashed using loki_tool [flash]:
+
+
+dan@pc:~$ adb push cwm.lok /data/local/tmp
+dan@pc:~$ adb push loki_tool /data/local/tmp
+dan@pc:~$ adb shell
+shell@android:/ $ su
+shell@android:/ # chmod 755 /data/local/tmp/loki_tool
+shell@android:/ # /data/local/tmp/loki_tool
+Usage: /data/local/tmp/loki_tool [flash] [boot|recovery] [in.lok]
+shell@android:/ # /data/local/tmp/loki_tool flash recovery /data/local/tmp/cwm.lok
+[+] Loki validation passed, flashing image.
+2253+1 records in
+2253+1 records out
+9230848 bytes transferred in 0.656 secs (14071414 bytes/sec)
+[+] Loki flashing complete!
diff --git a/loki_tool/loki.h b/loki_tool/loki.h
new file mode 100644 (file)
index 0000000..c1d6b3e
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef __LOKI_H_
+#define __LOKI_H_
+
+#define VERSION "2.1"
+
+#define BOOT_MAGIC_SIZE 8
+#define BOOT_NAME_SIZE 16
+#define BOOT_ARGS_SIZE 512
+
+#define BOOT_PARTITION      "/dev/block/platform/msm_sdcc.1/by-name/boot"
+#define RECOVERY_PARTITION  "/dev/block/platform/msm_sdcc.1/by-name/recovery"
+#define ABOOT_PARTITION     "/dev/block/platform/msm_sdcc.1/by-name/aboot"
+
+#define PATTERN1 "\xf0\xb5\x8f\xb0\x06\x46\xf0\xf7"
+#define PATTERN2 "\xf0\xb5\x8f\xb0\x07\x46\xf0\xf7"
+#define PATTERN3 "\x2d\xe9\xf0\x41\x86\xb0\xf1\xf7"
+#define PATTERN4 "\x2d\xe9\xf0\x4f\xad\xf5\xc6\x6d"
+#define PATTERN5 "\x2d\xe9\xf0\x4f\xad\xf5\x21\x7d"
+#define PATTERN6 "\x2d\xe9\xf0\x4f\xf3\xb0\x05\x46"
+
+#define ABOOT_BASE_SAMSUNG 0x88dfffd8
+#define ABOOT_BASE_LG 0x88efffd8
+#define ABOOT_BASE_G2 0xf7fffd8
+#define ABOOT_BASE_VIPER 0x40100000
+
+struct boot_img_hdr {
+    unsigned char magic[BOOT_MAGIC_SIZE];
+    unsigned kernel_size;   /* size in bytes */
+    unsigned kernel_addr;   /* physical load addr */
+    unsigned ramdisk_size;  /* size in bytes */
+    unsigned ramdisk_addr;  /* physical load addr */
+    unsigned second_size;   /* size in bytes */
+    unsigned second_addr;   /* physical load addr */
+    unsigned tags_addr;     /* physical addr for kernel tags */
+    unsigned page_size;     /* flash page size we assume */
+    unsigned dt_size;       /* device_tree in bytes */
+    unsigned unused;        /* future expansion: should be 0 */
+    unsigned char name[BOOT_NAME_SIZE];    /* asciiz product name */
+    unsigned char cmdline[BOOT_ARGS_SIZE];
+    unsigned id[8];         /* timestamp / checksum / sha1 / etc */
+};
+
+struct loki_hdr {
+    unsigned char magic[4];     /* 0x494b4f4c */
+    unsigned int recovery;      /* 0 = boot.img, 1 = recovery.img */
+    char build[128];   /* Build number */
+
+    unsigned int orig_kernel_size;
+    unsigned int orig_ramdisk_size;
+    unsigned int ramdisk_addr;
+};
+
+int loki_patch(const char* partition_label, const char* aboot_image, const char* in_image, const char* out_image);
+int loki_flash(const char* partition_label, const char* loki_image);
+int loki_find(const char* aboot_image);
+int loki_unlok(const char* in_image, const char* out_image);
+
+#define PATCH  "\xfe\xb5"                      \
+                               "\x0d\x4d"                      \
+                               "\xd5\xf8"                      \
+                               "\x88\x04"                      \
+                               "\xab\x68"                      \
+                               "\x98\x42"                      \
+                               "\x12\xd0"                      \
+                               "\xd5\xf8"                      \
+                               "\x90\x64"                      \
+                               "\x0a\x4c"                      \
+                               "\xd5\xf8"                      \
+                               "\x8c\x74"                      \
+                               "\x07\xf5\x80\x57"      \
+                               "\x0f\xce"                      \
+                               "\x0f\xc4"                      \
+                               "\x10\x3f"                      \
+                               "\xfb\xdc"                      \
+                               "\xd5\xf8"                      \
+                               "\x88\x04"                      \
+                               "\x04\x49"                      \
+                               "\xd5\xf8"                      \
+                               "\x8c\x24"                      \
+                               "\xa8\x60"                      \
+                               "\x69\x61"                      \
+                               "\x2a\x61"                      \
+                               "\x00\x20"                      \
+                               "\xfe\xbd"                      \
+                               "\xff\xff\xff\xff"      \
+                               "\xee\xee\xee\xee"
+
+#endif //__LOKI_H_
diff --git a/loki_tool/loki_find.c b/loki_tool/loki_find.c
new file mode 100644 (file)
index 0000000..151a309
--- /dev/null
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "loki.h"
+
+#define BOOT_PATTERN1 "\x4f\xf4\x70\x40\xb3\x49\x2d\xe9"       /* Samsung GS4 */
+#define BOOT_PATTERN2 "\x2d\xe9\xf0\x4f\xad\xf5\x82\x5d"       /* LG */
+#define BOOT_PATTERN3 "\x2d\xe9\xf0\x4f\x4f\xf4\x70\x40"       /* LG */
+#define BOOT_PATTERN4 "\x2d\xe9\xf0\x4f\xad\xf5\x80\x5d"       /* LG G2 */
+
+int loki_find(const char* aboot_image)
+{
+       int aboot_fd;
+       struct stat st;
+       void *aboot, *ptr;
+       unsigned long aboot_base, check_sigs, boot_mmc;
+
+       aboot_fd = open(aboot_image, O_RDONLY);
+       if (aboot_fd < 0) {
+               printf("[-] Failed to open %s for reading.\n", aboot_image);
+               return 1;
+       }
+
+       if (fstat(aboot_fd, &st)) {
+               printf("[-] fstat() failed.\n");
+               return 1;
+       }
+
+       aboot = mmap(0, (st.st_size + 0xfff) & ~0xfff, PROT_READ, MAP_PRIVATE, aboot_fd, 0);
+       if (aboot == MAP_FAILED) {
+               printf("[-] Failed to mmap aboot.\n");
+               return 1;
+       }
+
+       check_sigs = 0;
+       aboot_base = *(unsigned int *)(aboot + 12) - 0x28;
+
+       /* Do a pass to find signature checking function */
+       for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
+               if (!memcmp(ptr, PATTERN1, 8) ||
+                       !memcmp(ptr, PATTERN2, 8) ||
+                       !memcmp(ptr, PATTERN3, 8) ||
+                       !memcmp(ptr, PATTERN4, 8) ||
+                       !memcmp(ptr, PATTERN5, 8)) {
+
+                       check_sigs = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
+                       break;
+               }
+
+               if (!memcmp(ptr, PATTERN6, 8)) {
+
+                       check_sigs = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
+
+                       /* Don't break, because the other LG patterns override this one */
+                       continue;
+               }
+       }
+
+       if (!check_sigs) {
+               printf("[-] Could not find signature checking function.\n");
+               return 1;
+       }
+
+       printf("[+] Signature check function: %.08lx\n", check_sigs);
+
+       boot_mmc = 0;
+
+       /* Do a second pass for the boot_linux_from_emmc function */
+       for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
+               if (!memcmp(ptr, BOOT_PATTERN1, 8) ||
+                       !memcmp(ptr, BOOT_PATTERN2, 8) ||
+                       !memcmp(ptr, BOOT_PATTERN3, 8) ||
+                       !memcmp(ptr, BOOT_PATTERN4, 8)) {
+
+                       boot_mmc = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
+                       break;
+               }
+    }
+
+       if (!boot_mmc) {
+               printf("[-] Could not find boot_linux_from_mmc.\n");
+               return 1;
+       }
+
+       printf("[+] boot_linux_from_mmc: %.08lx\n", boot_mmc);
+
+       return 0;
+}
diff --git a/loki_tool/loki_flash.c b/loki_tool/loki_flash.c
new file mode 100644 (file)
index 0000000..16bdd4d
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * loki_flash
+ *
+ * A sample utility to validate and flash .lok files
+ *
+ * by Dan Rosenberg (@djrbliss)
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "loki.h"
+
+int loki_flash(const char* partition_label, const char* loki_image)
+{
+       int ifd, aboot_fd, ofd, recovery, offs, match;
+       void *orig, *aboot, *patch;
+       struct stat st;
+       struct boot_img_hdr *hdr;
+       struct loki_hdr *loki_hdr;
+       char outfile[1024];
+
+       if (!strcmp(partition_label, "boot")) {
+               recovery = 0;
+       } else if (!strcmp(partition_label, "recovery")) {
+               recovery = 1;
+       } else {
+               printf("[+] First argument must be \"boot\" or \"recovery\".\n");
+               return 1;
+       }
+
+       /* Verify input file */
+       aboot_fd = open(ABOOT_PARTITION, O_RDONLY);
+       if (aboot_fd < 0) {
+               printf("[-] Failed to open aboot for reading.\n");
+               return 1;
+       }
+
+       ifd = open(loki_image, O_RDONLY);
+       if (ifd < 0) {
+               printf("[-] Failed to open %s for reading.\n", loki_image);
+               return 1;
+       }
+
+       /* Map the image to be flashed */
+       if (fstat(ifd, &st)) {
+               printf("[-] fstat() failed.\n");
+               return 1;
+       }
+
+       orig = mmap(0, (st.st_size + 0x2000 + 0xfff) & ~0xfff, PROT_READ, MAP_PRIVATE, ifd, 0);
+       if (orig == MAP_FAILED) {
+               printf("[-] Failed to mmap Loki image.\n");
+               return 1;
+       }
+
+       hdr = orig;
+       loki_hdr = orig + 0x400;
+
+       /* Verify this is a Loki image */
+       if (memcmp(loki_hdr->magic, "LOKI", 4)) {
+               printf("[-] Input file is not a Loki image.\n");
+               return 1;
+       }
+
+       /* Verify this is the right type of image */
+       if (loki_hdr->recovery != recovery) {
+               printf("[-] Loki image is not a %s image.\n", recovery ? "recovery" : "boot");
+               return 1;
+       }
+
+       /* Verify the to-be-patched address matches the known code pattern */
+       aboot = mmap(0, 0x40000, PROT_READ, MAP_PRIVATE, aboot_fd, 0);
+       if (aboot == MAP_FAILED) {
+               printf("[-] Failed to mmap aboot.\n");
+               return 1;
+       }
+
+       match = 0;
+
+       for (offs = 0; offs < 0x10; offs += 0x4) {
+
+               patch = NULL;
+
+               if (hdr->ramdisk_addr > ABOOT_BASE_LG)
+                       patch = hdr->ramdisk_addr - ABOOT_BASE_LG + aboot + offs;
+               else if (hdr->ramdisk_addr > ABOOT_BASE_SAMSUNG)
+                       patch = hdr->ramdisk_addr - ABOOT_BASE_SAMSUNG + aboot + offs;
+               else if (hdr->ramdisk_addr > ABOOT_BASE_VIPER)
+                       patch = hdr->ramdisk_addr - ABOOT_BASE_VIPER + aboot + offs;
+               else if (hdr->ramdisk_addr > ABOOT_BASE_G2)
+                       patch = hdr->ramdisk_addr - ABOOT_BASE_G2 + aboot + offs;
+
+               if (patch < aboot || patch > aboot + 0x40000 - 8) {
+                       printf("[-] Invalid .lok file.\n");
+                       return 1;
+               }
+
+               if (!memcmp(patch, PATTERN1, 8) ||
+                       !memcmp(patch, PATTERN2, 8) ||
+                       !memcmp(patch, PATTERN3, 8) ||
+                       !memcmp(patch, PATTERN4, 8) ||
+                       !memcmp(patch, PATTERN5, 8) ||
+                       !memcmp(patch, PATTERN6, 8)) {
+
+                       match = 1;
+                       break;
+               }
+       }
+
+       if (!match) {
+               printf("[-] Loki aboot version does not match device.\n");
+               return 1;
+       }
+
+       printf("[+] Loki validation passed, flashing image.\n");
+
+       snprintf(outfile, sizeof(outfile),
+                        "%s",
+                        recovery ? RECOVERY_PARTITION : BOOT_PARTITION);
+
+       ofd = open(outfile, O_WRONLY);
+       if (ofd < 0) {
+               printf("[-] Failed to open output block device.\n");
+               return 1;
+       }
+
+       if (write(ofd, orig, st.st_size) != st.st_size) {
+               printf("[-] Failed to write to block device.\n");
+               return 1;
+       }
+
+       printf("[+] Loki flashing complete!\n");
+
+       close(ifd);
+       close(aboot_fd);
+       close(ofd);
+
+       return 0;
+}
diff --git a/loki_tool/loki_patch.c b/loki_tool/loki_patch.c
new file mode 100644 (file)
index 0000000..5d671bb
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * loki_patch
+ *
+ * A utility to patch unsigned boot and recovery images to make
+ * them suitable for booting on the AT&T/Verizon Samsung
+ * Galaxy S4, Galaxy Stellar, and various locked LG devices
+ *
+ * by Dan Rosenberg (@djrbliss)
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "loki.h"
+
+struct target {
+       char *vendor;
+       char *device;
+       char *build;
+       unsigned long check_sigs;
+       unsigned long hdr;
+       int lg;
+};
+
+struct target targets[] = {
+       {
+               .vendor = "AT&T",
+               .device = "Samsung Galaxy S4",
+               .build = "JDQ39.I337UCUAMDB or JDQ39.I337UCUAMDL",
+               .check_sigs = 0x88e0ff98,
+               .hdr = 0x88f3bafc,
+               .lg = 0,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "Samsung Galaxy S4",
+               .build = "JDQ39.I545VRUAMDK",
+               .check_sigs = 0x88e0fe98,
+               .hdr = 0x88f372fc,
+               .lg = 0,
+       },
+       {
+               .vendor = "DoCoMo",
+               .device = "Samsung Galaxy S4",
+               .build = "JDQ39.SC04EOMUAMDI",
+               .check_sigs = 0x88e0fcd8,
+               .hdr = 0x88f0b2fc,
+               .lg = 0,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "Samsung Galaxy Stellar",
+               .build = "IMM76D.I200VRALH2",
+               .check_sigs = 0x88e0f5c0,
+               .hdr = 0x88ed32e0,
+               .lg = 0,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "Samsung Galaxy Stellar",
+               .build = "JZO54K.I200VRBMA1",
+               .check_sigs = 0x88e101ac,
+               .hdr = 0x88ed72e0,
+               .lg = 0,
+       },
+       {
+               .vendor = "T-Mobile",
+               .device = "LG Optimus F3Q",
+               .build = "D52010c",
+               .check_sigs = 0x88f1079c,
+               .hdr = 0x88f64508,
+               .lg = 1,
+       },
+       {
+               .vendor = "DoCoMo",
+               .device = "LG Optimus G",
+               .build = "L01E20b",
+               .check_sigs = 0x88F10E48,
+               .hdr = 0x88F54418,
+               .lg = 1,
+       },
+       {
+               .vendor = "DoCoMo",
+               .device = "LG Optimus it L05E",
+               .build = "L05E10d",
+               .check_sigs = 0x88f1157c,
+               .hdr = 0x88f31e10,
+               .lg = 1,
+       },
+       {
+               .vendor = "DoCoMo",
+               .device = "LG Optimus G Pro",
+               .build = "L04E10f",
+               .check_sigs = 0x88f1102c,
+               .hdr = 0x88f54418,
+               .lg = 1,
+       },
+       {
+               .vendor = "AT&T or HK",
+               .device = "LG Optimus G Pro",
+               .build = "E98010g or E98810b",
+               .check_sigs = 0x88f11084,
+               .hdr = 0x88f54418,
+               .lg = 1,
+       },
+       {
+               .vendor = "KT, LGU, or SKT",
+               .device = "LG Optimus G Pro",
+               .build = "F240K10o, F240L10v, or F240S10w",
+               .check_sigs = 0x88f110b8,
+               .hdr = 0x88f54418,
+               .lg = 1,
+       },
+       {
+               .vendor = "KT, LGU, or SKT",
+               .device = "LG Optimus LTE 2",
+               .build = "F160K20g, F160L20f, F160LV20d, or F160S20f",
+               .check_sigs = 0x88f10864,
+               .hdr = 0x88f802b8,
+               .lg = 1,
+       },
+       {
+               .vendor = "MetroPCS",
+               .device = "LG Spirit",
+               .build = "MS87010a_05",
+               .check_sigs = 0x88f0e634,
+               .hdr = 0x88f68194,
+               .lg = 1,
+       },
+       {
+               .vendor = "MetroPCS",
+               .device = "LG Motion",
+               .build = "MS77010f_01",
+               .check_sigs = 0x88f1015c,
+               .hdr = 0x88f58194,
+               .lg = 1,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "LG Lucid 2",
+               .build = "VS87010B_12",
+               .check_sigs = 0x88f10adc,
+               .hdr = 0x88f702bc,
+               .lg = 1,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "LG Spectrum 2",
+               .build = "VS93021B_05",
+               .check_sigs = 0x88f10c10,
+               .hdr = 0x88f84514,
+               .lg = 1,
+       },
+       {
+               .vendor = "Boost Mobile",
+               .device = "LG Optimus F7",
+               .build = "LG870ZV4_06",
+               .check_sigs = 0x88f11714,
+               .hdr = 0x88f842ac,
+               .lg = 1,
+       },
+       {
+               .vendor = "US Cellular",
+               .device = "LG Optimus F7",
+               .build = "US78011a",
+               .check_sigs = 0x88f112c8,
+               .hdr = 0x88f84518,
+               .lg = 1,
+       },
+       {
+               .vendor = "Sprint",
+               .device = "LG Optimus F7",
+               .build = "LG870ZV5_02",
+               .check_sigs = 0x88f11710,
+               .hdr = 0x88f842a8,
+               .lg = 1,
+       },
+       {
+               .vendor = "Virgin Mobile",
+               .device = "LG Optimus F3",
+               .build = "LS720ZV5",
+               .check_sigs = 0x88f108f0,
+               .hdr = 0x88f854f4,
+               .lg = 1,
+       },
+       {
+               .vendor = "T-Mobile and MetroPCS",
+               .device = "LG Optimus F3",
+               .build = "LS720ZV5",
+               .check_sigs = 0x88f10264,
+               .hdr = 0x88f64508,
+               .lg = 1,
+       },
+       {
+               .vendor = "AT&T",
+               .device = "LG G2",
+               .build = "D80010d",
+               .check_sigs = 0xf8132ac,
+               .hdr = 0xf906440,
+               .lg = 1,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "LG G2",
+               .build = "VS98010b",
+               .check_sigs = 0xf8131f0,
+               .hdr = 0xf906440,
+               .lg = 1,
+       },
+       {
+               .vendor = "AT&T",
+               .device = "LG G2",
+               .build = "D80010o",
+               .check_sigs = 0xf813428,
+               .hdr = 0xf904400,
+               .lg = 1,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "LG G2",
+               .build = "VS98012b",
+               .check_sigs = 0xf813210,
+               .hdr = 0xf906440,
+               .lg = 1,
+       },
+       {
+               .vendor = "T-Mobile or Canada",
+               .device = "LG G2",
+               .build = "D80110c or D803",
+               .check_sigs = 0xf813294,
+               .hdr = 0xf906440,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG G2",
+               .build = "D802b",
+               .check_sigs = 0xf813a70,
+               .hdr = 0xf9041c0,
+               .lg = 1,
+       },
+       {
+               .vendor = "Sprint",
+               .device = "LG G2",
+               .build = "LS980ZV7",
+               .check_sigs = 0xf813460,
+               .hdr = 0xf9041c0,
+               .lg = 1,
+       },
+       {
+               .vendor = "KT or LGU",
+               .device = "LG G2",
+               .build = "F320K, F320L",
+               .check_sigs = 0xf81346c,
+               .hdr = 0xf8de440,
+               .lg = 1,
+       },
+       {
+               .vendor = "SKT",
+               .device = "LG G2",
+               .build = "F320S",
+               .check_sigs = 0xf8132e4,
+               .hdr = 0xf8ee440,
+               .lg = 1,
+       },
+       {
+               .vendor = "SKT",
+               .device = "LG G2",
+               .build = "F320S11c",
+               .check_sigs = 0xf813470,
+               .hdr = 0xf8de440,
+               .lg = 1,
+       },
+       {
+               .vendor = "DoCoMo",
+               .device = "LG G2",
+               .build = "L-01F",
+               .check_sigs = 0xf813538,
+               .hdr = 0xf8d41c0,
+               .lg = 1,
+       },
+       {
+               .vendor = "KT",
+               .device = "LG G Flex",
+               .build = "F340K",
+               .check_sigs = 0xf8124a4,
+               .hdr = 0xf8b6440,
+               .lg = 1,
+       },
+       {
+               .vendor = "KDDI",
+               .device = "LG G Flex",
+               .build = "LGL2310d",
+               .check_sigs = 0xf81261c,
+               .hdr = 0xf8b41c0,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG Optimus F5",
+               .build = "P87510e",
+               .check_sigs = 0x88f10a9c,
+               .hdr = 0x88f702b8,
+               .lg = 1,
+       },
+       {
+               .vendor = "SKT",
+               .device = "LG Optimus LTE 3",
+               .build = "F260S10l",
+               .check_sigs = 0x88f11398,
+               .hdr = 0x88f8451c,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG G Pad 8.3",
+               .build = "V50010a",
+               .check_sigs = 0x88f10814,
+               .hdr = 0x88f801b8,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG G Pad 8.3",
+               .build = "V50010c or V50010e",
+               .check_sigs = 0x88f108bc,
+               .hdr = 0x88f801b8,
+               .lg = 1,
+       },
+       {
+               .vendor = "Verizon",
+               .device = "LG G Pad 8.3",
+               .build = "VK81010c",
+               .check_sigs = 0x88f11080,
+               .hdr = 0x88fd81b8,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG Optimus L9 II",
+               .build = "D60510a",
+               .check_sigs = 0x88f10d98,
+               .hdr = 0x88f84aa4,
+               .lg = 1,
+       },
+       {
+               .vendor = "MetroPCS",
+               .device = "LG Optimus F6",
+               .build = "MS50010e",
+               .check_sigs = 0x88f10260,
+               .hdr = 0x88f70508,
+               .lg = 1,
+       },
+       {
+               .vendor = "Open EU",
+               .device = "LG Optimus F6",
+               .build = "D50510a",
+               .check_sigs = 0x88f10284,
+               .hdr = 0x88f70aa4,
+               .lg = 1,
+       },
+       {
+               .vendor = "KDDI",
+               .device = "LG Isai",
+               .build = "LGL22",
+               .check_sigs = 0xf813458,
+               .hdr = 0xf8d41c0,
+               .lg = 1,
+       },
+       {
+               .vendor = "KDDI",
+               .device = "LG",
+               .build = "LGL21",
+               .check_sigs = 0x88f10218,
+               .hdr = 0x88f50198,
+               .lg = 1,
+       },
+       {
+               .vendor = "KT",
+               .device = "LG Optimus GK",
+               .build = "F220K",
+               .check_sigs = 0x88f11034,
+               .hdr = 0x88f54418,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG Vu 3",
+               .build = "F300L",
+               .check_sigs = 0xf813170,
+               .hdr = 0xf8d2440,
+               .lg = 1,
+       },
+       {
+               .vendor = "Sprint",
+               .device = "LG Viper",
+               .build = "LS840ZVK",
+               .check_sigs = 0x4010fe18,
+               .hdr = 0x40194198,
+               .lg = 1,
+       },
+       {
+               .vendor = "International",
+               .device = "LG G Flex",
+               .build = "D95510a",
+               .check_sigs = 0xf812490,
+               .hdr = 0xf8c2440,
+               .lg = 1,
+       },
+       {
+               .vendor = "Sprint",
+               .device = "LG Mach",
+               .build = "LS860ZV7",
+               .check_sigs = 0x88f102b4,
+               .hdr = 0x88f6c194,
+               .lg = 1,
+       },
+};
+
+static unsigned char patch[] = PATCH;
+
+int patch_shellcode(unsigned int header, unsigned int ramdisk)
+{
+
+       unsigned int i;
+       int found_header, found_ramdisk;
+       unsigned int *ptr;
+
+       found_header = 0;
+       found_ramdisk = 0;
+
+       for (i = 0; i < sizeof(patch); i++) {
+               ptr = (unsigned int *)&patch[i];
+               if (*ptr == 0xffffffff) {
+                       *ptr = header;
+                       found_header = 1;
+               }
+
+               if (*ptr == 0xeeeeeeee) {
+                       *ptr = ramdisk;
+                       found_ramdisk = 1;
+               }
+       }
+
+       if (found_header && found_ramdisk)
+               return 0;
+
+       return -1;
+}
+
+int loki_patch(const char* partition_label, const char* aboot_image, const char* in_image, const char* out_image)
+{
+       int ifd, ofd, aboot_fd, pos, i, recovery, offset, fake_size;
+       unsigned int orig_ramdisk_size, orig_kernel_size, page_kernel_size, page_ramdisk_size, page_size, page_mask;
+       unsigned long target, aboot_base;
+       void *orig, *aboot, *ptr;
+       struct target *tgt;
+       struct stat st;
+       struct boot_img_hdr *hdr;
+       struct loki_hdr *loki_hdr;
+       char *buf;
+
+       if (!strcmp(partition_label, "boot")) {
+               recovery = 0;
+       } else if (!strcmp(partition_label, "recovery")) {
+               recovery = 1;
+       } else {
+               printf("[+] First argument must be \"boot\" or \"recovery\".\n");
+               return 1;
+       }
+
+       /* Open input files */
+       aboot_fd = open(aboot_image, O_RDONLY);
+       if (aboot_fd < 0) {
+               printf("[-] Failed to open %s for reading.\n", aboot_image);
+               return 1;
+       }
+
+       ifd = open(in_image, O_RDONLY);
+       if (ifd < 0) {
+               printf("[-] Failed to open %s for reading.\n", in_image);
+               return 1;
+       }
+
+       ofd = open(out_image, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (ofd < 0) {
+               printf("[-] Failed to open %s for writing.\n", out_image);
+               return 1;
+       }
+
+       /* Find the signature checking function via pattern matching */
+       if (fstat(aboot_fd, &st)) {
+               printf("[-] fstat() failed.\n");
+               return 1;
+       }
+
+       aboot = mmap(0, (st.st_size + 0xfff) & ~0xfff, PROT_READ, MAP_PRIVATE, aboot_fd, 0);
+       if (aboot == MAP_FAILED) {
+               printf("[-] Failed to mmap aboot.\n");
+               return 1;
+       }
+
+       target = 0;
+       aboot_base = *(unsigned int *)(aboot + 12) - 0x28;
+
+       for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
+               if (!memcmp(ptr, PATTERN1, 8) ||
+                       !memcmp(ptr, PATTERN2, 8) ||
+                       !memcmp(ptr, PATTERN3, 8) ||
+                       !memcmp(ptr, PATTERN4, 8) ||
+                       !memcmp(ptr, PATTERN5, 8)) {
+
+                       target = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
+                       break;
+               }
+       }
+
+       /* Do a second pass for the second LG pattern. This is necessary because
+        * apparently some LG models have both LG patterns, which throws off the
+        * fingerprinting. */
+
+       if (!target) {
+               for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
+                       if (!memcmp(ptr, PATTERN6, 8)) {
+
+                               target = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
+                               break;
+                       }
+               }
+       }
+
+       if (!target) {
+               printf("[-] Failed to find function to patch.\n");
+               return 1;
+       }
+
+       tgt = NULL;
+
+       for (i = 0; i < (sizeof(targets)/sizeof(targets[0])); i++) {
+               if (targets[i].check_sigs == target) {
+                       tgt = &targets[i];
+                       break;
+               }
+       }
+
+       if (!tgt) {
+               printf("[-] Unsupported aboot image.\n");
+               return 1;
+       }
+
+       printf("[+] Detected target %s %s build %s\n", tgt->vendor, tgt->device, tgt->build);
+
+       /* Map the original boot/recovery image */
+       if (fstat(ifd, &st)) {
+               printf("[-] fstat() failed.\n");
+               return 1;
+       }
+
+       orig = mmap(0, (st.st_size + 0x2000 + 0xfff) & ~0xfff, PROT_READ|PROT_WRITE, MAP_PRIVATE, ifd, 0);
+       if (orig == MAP_FAILED) {
+               printf("[-] Failed to mmap input file.\n");
+               return 1;
+       }
+
+       hdr = orig;
+       loki_hdr = orig + 0x400;
+
+       if (!memcmp(loki_hdr->magic, "LOKI", 4)) {
+               printf("[-] Input file is already a Loki image.\n");
+
+               /* Copy the entire file to the output transparently */
+               if (write(ofd, orig, st.st_size) != st.st_size) {
+                       printf("[-] Failed to copy Loki image.\n");
+                       return 1;
+               }
+
+               printf("[+] Copied Loki image to %s.\n", out_image);
+
+               return 0;
+       }
+
+       /* Set the Loki header */
+       memcpy(loki_hdr->magic, "LOKI", 4);
+       loki_hdr->recovery = recovery;
+       strncpy(loki_hdr->build, tgt->build, sizeof(loki_hdr->build) - 1);
+
+       page_size = hdr->page_size;
+       page_mask = hdr->page_size - 1;
+
+       orig_kernel_size = hdr->kernel_size;
+       orig_ramdisk_size = hdr->ramdisk_size;
+
+       printf("[+] Original kernel address: %.08x\n", hdr->kernel_addr);
+       printf("[+] Original ramdisk address: %.08x\n", hdr->ramdisk_addr);
+
+       /* Store the original values in unused fields of the header */
+       loki_hdr->orig_kernel_size = orig_kernel_size;
+       loki_hdr->orig_ramdisk_size = orig_ramdisk_size;
+       loki_hdr->ramdisk_addr = hdr->kernel_addr + ((hdr->kernel_size + page_mask) & ~page_mask);
+
+       if (patch_shellcode(tgt->hdr, hdr->ramdisk_addr) < 0) {
+               printf("[-] Failed to patch shellcode.\n");
+               return 1;
+       }
+
+       /* Ramdisk must be aligned to a page boundary */
+       hdr->kernel_size = ((hdr->kernel_size + page_mask) & ~page_mask) + hdr->ramdisk_size;
+
+       /* Guarantee 16-byte alignment */
+       offset = tgt->check_sigs & 0xf;
+
+       hdr->ramdisk_addr = tgt->check_sigs - offset;
+
+       if (tgt->lg) {
+               fake_size = page_size;
+               hdr->ramdisk_size = page_size;
+       }
+       else {
+               fake_size = 0x200;
+               hdr->ramdisk_size = 0;
+       }
+
+       /* Write the image header */
+       if (write(ofd, orig, page_size) != page_size) {
+               printf("[-] Failed to write header to output file.\n");
+               return 1;
+       }
+
+       page_kernel_size = (orig_kernel_size + page_mask) & ~page_mask;
+
+       /* Write the kernel */
+       if (write(ofd, orig + page_size, page_kernel_size) != page_kernel_size) {
+               printf("[-] Failed to write kernel to output file.\n");
+               return 1;
+       }
+
+       page_ramdisk_size = (orig_ramdisk_size + page_mask) & ~page_mask;
+
+       /* Write the ramdisk */
+       if (write(ofd, orig + page_size + page_kernel_size, page_ramdisk_size) != page_ramdisk_size) {
+               printf("[-] Failed to write ramdisk to output file.\n");
+               return 1;
+       }
+
+       /* Write fake_size bytes of original code to the output */
+       buf = malloc(fake_size);
+       if (!buf) {
+               printf("[-] Out of memory.\n");
+               return 1;
+       }
+
+       lseek(aboot_fd, tgt->check_sigs - aboot_base - offset, SEEK_SET);
+       read(aboot_fd, buf, fake_size);
+
+       if (write(ofd, buf, fake_size) != fake_size) {
+               printf("[-] Failed to write original aboot code to output file.\n");
+               return 1;
+       }
+
+       /* Save this position for later */
+       pos = lseek(ofd, 0, SEEK_CUR);
+
+       /* Write the device tree if needed */
+       if (hdr->dt_size) {
+
+               printf("[+] Writing device tree.\n");
+
+               if (write(ofd, orig + page_size + page_kernel_size + page_ramdisk_size, hdr->dt_size) != hdr->dt_size) {
+                       printf("[-] Failed to write device tree to output file.\n");
+                       return 1;
+               }
+       }
+
+       lseek(ofd, pos - (fake_size - offset), SEEK_SET);
+
+       /* Write the patch */
+       if (write(ofd, patch, sizeof(patch)) != sizeof(patch)) {
+               printf("[-] Failed to write patch to output file.\n");
+               return 1;
+       }
+
+       close(ifd);
+       close(ofd);
+       close(aboot_fd);
+
+       printf("[+] Output file written to %s\n", out_image);
+
+       return 0;
+}
diff --git a/loki_tool/loki_unlok.c b/loki_tool/loki_unlok.c
new file mode 100644 (file)
index 0000000..f2e685a
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * loki_unlok
+ *
+ * A utility to revert the changes made by loki_patch.
+ *
+ * by Dan Rosenberg (@djrbliss)
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "loki.h"
+
+static unsigned char patch[] = PATCH;
+
+/* Find the original address of the ramdisk, which
+ * was embedded in the shellcode. */
+int find_ramdisk_addr(void *img, int sz)
+{
+
+       int i, ramdisk = 0;
+
+       for (i = 0; i < sz - (sizeof(patch) - 9); i++) {
+               if (!memcmp((char *)img + i, patch, sizeof(patch)-9)) {
+                       ramdisk = *(int *)(img + i + sizeof(patch) - 5);
+                       break;
+               }
+       }
+
+       return ramdisk;
+}
+
+int loki_unlok(const char* in_image, const char* out_image)
+{
+       int ifd, ofd;
+       unsigned int orig_ramdisk_size, orig_kernel_size, orig_ramdisk_addr;
+       unsigned int page_kernel_size, page_ramdisk_size, page_size, page_mask, fake_size;
+       void *orig;
+       struct stat st;
+       struct boot_img_hdr *hdr;
+       struct loki_hdr *loki_hdr;
+
+       ifd = open(in_image, O_RDONLY);
+       if (ifd < 0) {
+               printf("[-] Failed to open %s for reading.\n", in_image);
+               return 1;
+       }
+
+       ofd = open(out_image, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (ofd < 0) {
+               printf("[-] Failed to open %s for writing.\n", out_image);
+               return 1;
+       }
+
+       /* Map the original boot/recovery image */
+       if (fstat(ifd, &st)) {
+               printf("[-] fstat() failed.\n");
+               return 1;
+       }
+
+       orig = mmap(0, (st.st_size + 0x2000 + 0xfff) & ~0xfff, PROT_READ|PROT_WRITE, MAP_PRIVATE, ifd, 0);
+       if (orig == MAP_FAILED) {
+               printf("[-] Failed to mmap input file.\n");
+               return 1;
+       }
+
+       hdr = orig;
+       loki_hdr = orig + 0x400;
+
+       if (memcmp(loki_hdr->magic, "LOKI", 4)) {
+               printf("[-] Input file is not a Loki image.\n");
+
+               /* Copy the entire file to the output transparently */
+               if (write(ofd, orig, st.st_size) != st.st_size) {
+                       printf("[-] Failed to copy Loki image.\n");
+                       return 1;
+               }
+
+               printf("[+] Copied Loki image to %s.\n", out_image);
+
+               return 0;
+       }
+
+       page_size = hdr->page_size;
+       page_mask = hdr->page_size - 1;
+
+       /* Infer the size of the fake block based on the newer ramdisk address */
+       if (hdr->ramdisk_addr > 0x88f00000 || hdr->ramdisk_addr < 0xfa00000)
+               fake_size = page_size;
+       else
+               fake_size = 0x200;
+
+       orig_ramdisk_addr = find_ramdisk_addr(orig, st.st_size);
+       if (orig_ramdisk_addr == 0) {
+               printf("[-] Failed to find original ramdisk address.\n");
+               return 1;
+       }
+
+       /* Restore the original header values */
+       hdr->ramdisk_addr = orig_ramdisk_addr;
+       hdr->kernel_size = orig_kernel_size = loki_hdr->orig_kernel_size;
+       hdr->ramdisk_size = orig_ramdisk_size = loki_hdr->orig_ramdisk_size;
+
+       /* Erase the loki header */
+       memset(loki_hdr, 0, sizeof(*loki_hdr));
+
+       /* Write the image header */
+       if (write(ofd, orig, page_size) != page_size) {
+               printf("[-] Failed to write header to output file.\n");
+               return 1;
+       }
+
+       page_kernel_size = (orig_kernel_size + page_mask) & ~page_mask;
+
+       /* Write the kernel */
+       if (write(ofd, orig + page_size, page_kernel_size) != page_kernel_size) {
+               printf("[-] Failed to write kernel to output file.\n");
+               return 1;
+       }
+
+       page_ramdisk_size = (orig_ramdisk_size + page_mask) & ~page_mask;
+
+       /* Write the ramdisk */
+       if (write(ofd, orig + page_size + page_kernel_size, page_ramdisk_size) != page_ramdisk_size) {
+               printf("[-] Failed to write ramdisk to output file.\n");
+               return 1;
+       }
+
+       /* Write the device tree if needed */
+       if (hdr->dt_size) {
+
+               printf("[+] Writing device tree.\n");
+
+               /* Skip an additional fake_size (page_size of 0x200) bytes */
+               if (write(ofd, orig + page_size + page_kernel_size + page_ramdisk_size + fake_size, hdr->dt_size) != hdr->dt_size) {
+                       printf("[-] Failed to write device tree to output file.\n");
+                       return 1;
+               }
+       }
+
+       close(ifd);
+       close(ofd);
+
+       printf("[+] Output file written to %s\n", out_image);
+
+       return 0;
+}
diff --git a/loki_tool/main.c b/loki_tool/main.c
new file mode 100644 (file)
index 0000000..1d3d9d3
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * loki_patch
+ *
+ * A utility to patch unsigned boot and recovery images to make
+ * them suitable for booting on the AT&T/Verizon Samsung
+ * Galaxy S4, Galaxy Stellar, and various locked LG devices
+ *
+ * by Dan Rosenberg (@djrbliss)
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "loki.h"
+
+static int print_help(const char* cmd) {
+    printf("Usage\n");
+    printf("> Patch partition file image:\n");
+    printf("%s [patch] [boot|recovery] [aboot.img] [in.img] [out.lok]\n", cmd);
+    printf("\n");
+    printf("> Flash loki image to boot|recovery:\n");
+    printf("%s [flash] [boot|recovery] [in.lok]\n", cmd);
+    printf("\n");
+    printf("> Find offset from aboot image:\n");
+    printf("%s [find] [aboot.img]\n", cmd);
+    printf("\n");
+    printf("> Revert Loki patching:\n");
+    printf("%s [unlok] [in.lok] [out.img]\n", cmd);
+    printf("\n");
+    return 1;
+}
+
+int main(int argc, char **argv) {
+    printf("Loki tool v%s\n", VERSION);
+
+    if (argc == 6 && strcmp(argv[1], "patch") == 0) {
+        // argv[2]: partition_label
+        // argv[3]: aboot_image
+        // argv[4]: in_image
+        // argv[5]: out_image
+        return loki_patch(argv[2], argv[3], argv[4], argv[5]);
+    } else if (argc == 4 && strcmp(argv[1], "flash") == 0) {
+        // argv[2]: partition_label
+        // argv[3]: loki_image
+        return loki_flash(argv[2], argv[3]);
+    } else if (argc == 3 && strcmp(argv[1], "find") == 0) {
+        // argv[2]: aboot_image
+        return loki_find(argv[2]);
+    } else if (argc == 4 && strcmp(argv[1], "unlok") == 0) {
+        // argv[2]: in_image
+        // argv[3]: out_image
+        return loki_unlok(argv[2], argv[3]);
+    }
+
+    return print_help(argv[0]);
+}
index afa218250796ea31089ebd0380cb4ae44497a720..6601f51415c23aa60291bcb2a7a692eacd7eebb7 100644 (file)
@@ -60,10 +60,19 @@ dtimage: $(INSTALLED_DTIMAGE_TARGET)
 endif
 endif
 
+ifeq ($(strip $(TAGET_NEEDS_LOKI)),true)
+LOKI_TOOL := loki_tool
+else
+LOKI_TOOL := echo
+TARGET_LOKI_ABOOT_IMAGE :=
+endif
+
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(BOOTIMAGE_EXTRA_DEPS)
        $(call pretty,"Target boot image: $@")
        $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
        $(hide) echo -n "SEANDROIDENFORCE" >> $@
+       $(hide) $(LOKI_TOOL) patch boot $(TARGET_LOKI_ABOOT_IMAGE) $@ $@.lok
+       $(hide) cp $@.lok $@ || true
        $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
        @echo "Made boot image: $@"
 
@@ -71,5 +80,7 @@ $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTIMG) $(recovery_ramdisk) $(recovery_k
        @echo "----- Making recovery image ------"
        $(hide) $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@ --id > $(RECOVERYIMAGE_ID_FILE)
        $(hide) echo -n "SEANDROIDENFORCE" >> $@
+       $(hide) $(LOKI_TOOL) patch recovery $(TARGET_LOKI_ABOOT_IMAGE) $@ $@.lok
+       $(hide) cp $@.lok $@ || true
        $(hide) $(call assert-max-image-size,$@,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw)
        @echo "Made recovery image: $@"