aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordcd <dcd1182@gmail.com>2014-11-19 17:27:26 -0600
committerDan Pasanen <dan.pasanen@gmail.com>2014-11-19 19:47:19 -0600
commit49dbe36e25c7c0432b05fd20dd015b2346f2fca0 (patch)
tree30d2e72cf6fa8841e91aff319e9e18c6b1b80dd5
parentb2efbc6b810a123baca5d713a98e0f306b5a0665 (diff)
jflteatt: de-commonize loki
Change-Id: I6aaabb2f87858936d7ef9581a722dac41d139a48
-rw-r--r--BoardConfig.mk3
-rw-r--r--device.mk5
-rw-r--r--releasetools/Android.mk51
-rw-r--r--releasetools/LICENSE25
-rw-r--r--releasetools/loki-recovery.sh48
-rw-r--r--releasetools/loki.h88
-rw-r--r--releasetools/loki.sh28
-rw-r--r--releasetools/loki_bootloaders2
-rw-r--r--releasetools/loki_find.c91
-rw-r--r--releasetools/loki_flash.c145
-rw-r--r--releasetools/loki_main.c56
-rw-r--r--releasetools/loki_patch.c670
-rw-r--r--releasetools/loki_unlok.c152
-rw-r--r--releasetools/releasetools.py31
14 files changed, 1390 insertions, 5 deletions
diff --git a/BoardConfig.mk b/BoardConfig.mk
index b8dc5f2..a4e08dd 100644
--- a/BoardConfig.mk
+++ b/BoardConfig.mk
@@ -30,5 +30,4 @@ include device/samsung/jf-common/BoardConfigCommon.mk
TARGET_OTA_ASSERT_DEVICE := jflteatt,jflte
# loki
-TARGET_RELEASETOOLS_EXTENSIONS := device/samsung/jf-common/releasetools
-
+TARGET_RELEASETOOLS_EXTENSIONS := device/samsung/jflteatt/releasetools
diff --git a/device.mk b/device.mk
index 326573d..364e3bf 100644
--- a/device.mk
+++ b/device.mk
@@ -18,11 +18,10 @@
$(call inherit-product-if-exists, vendor/samsung/jf-gsm-common/jf-gsm-common-vendor.mk)
PRODUCT_PACKAGES += \
- loki_patch \
- loki_flash \
+ loki_tool \
loki.sh \
loki_bootloaders \
- unlocked_bootloaders
+ reovery-transform.sh
## device overlays
DEVICE_PACKAGE_OVERLAYS += device/samsung/jflteatt/overlay
diff --git a/releasetools/Android.mk b/releasetools/Android.mk
new file mode 100644
index 0000000..24be951
--- /dev/null
+++ b/releasetools/Android.mk
@@ -0,0 +1,51 @@
+#
+# Copyright (C) 2013 The CyanogenMod Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+ifneq ($(filter jflteatt,$(TARGET_DEVICE)),)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := loki_flash.c loki_patch.c loki_find.c loki_unlok.c loki_main.c
+LOCAL_MODULE := loki_tool
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_PATH := $(TARGET_OUT)/bin
+LOCAL_STATIC_LIBRARIES := libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := loki.sh
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc
+LOCAL_SRC_FILES := loki.sh
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := loki_bootloaders
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc
+LOCAL_SRC_FILES := loki_bootloaders
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := recovery-transform.sh
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc
+LOCAL_SRC_FILES := loki-recovery.sh
+include $(BUILD_PREBUILT)
+
+endif
diff --git a/releasetools/LICENSE b/releasetools/LICENSE
new file mode 100644
index 0000000..dcdee17
--- /dev/null
+++ b/releasetools/LICENSE
@@ -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/releasetools/loki-recovery.sh b/releasetools/loki-recovery.sh
new file mode 100644
index 0000000..0ff3d68
--- /dev/null
+++ b/releasetools/loki-recovery.sh
@@ -0,0 +1,48 @@
+#!/system/bin/sh
+#
+# This leverages the loki_patch utility created by djrbliss
+# See here for more information on loki: https://github.com/djrbliss/loki
+#
+
+RECSIZE=$1
+RECSHA1=$2
+BOOTSIZE=$3
+BOOTSHA1=$4
+
+egrep -q -f /system/etc/loki_bootloaders /proc/cmdline
+if [ $? -eq 0 ]; then
+ need_lok=1
+ export C=/data/local/tmp/loki_tmpdir
+ rm -rf $C
+ mkdir -p $C
+ dd if=/dev/block/platform/msm_sdcc.1/by-name/recovery of=$C/recovery.lok
+ /system/bin/loki_tool unlok $C/recovery.lok $C/recovery
+else
+ export C=/dev/block/platform/msm_sdcc.1/by-name/
+fi
+
+if ! applypatch -c EMMC:$C/recovery:$RECSIZE:$RECSHA1; then
+ if [ $need_lok -eq 1 ]; then
+ log -t recovery "recovery is outdated. unloki-ing all the things"
+ dd if=/dev/block/platform/msm_sdcc.1/by-name/boot of=$C/boot.lok
+ dd if=/dev/block/platform/msm_sdcc.1/by-name/aboot of=$C/aboot.img
+ /system/bin/loki_tool unlok $C/boot.lok $C/boot
+ fi
+
+ log -t recovery "Installing new recovery image"
+ applypatch -b /system/etc/recovery-resource.dat EMMC:$C/boot:$BOOTSIZE:$BOOTSHA1 EMMC:$C/recovery $RECSHA1 $RECSIZE $BOOTSHA1:/system/recovery-from-boot.p || exit 1
+
+ if [ $need_lok -eq 1 ]; then
+ /system/bin/loki_tool patch recovery $C/aboot.img $C/recovery $C/recovery.lok || exit 1
+ /system/bin/loki_tool flash recovery $C/recovery.lok || exit 1
+ fi
+
+else
+ log -t recovery "Recovery image already installed"
+fi
+
+if [ $need_lok -eq 1 ]; then
+ rm -rf $C
+fi
+
+exit 0
diff --git a/releasetools/loki.h b/releasetools/loki.h
new file mode 100644
index 0000000..c1d6b3e
--- /dev/null
+++ b/releasetools/loki.h
@@ -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/releasetools/loki.sh b/releasetools/loki.sh
new file mode 100644
index 0000000..4102077
--- /dev/null
+++ b/releasetools/loki.sh
@@ -0,0 +1,28 @@
+#!/sbin/sh
+#
+# This leverages the loki_patch utility created by djrbliss which allows us
+# to bypass the bootloader checks on jfltevzw and jflteatt
+# See here for more information on loki: https://github.com/djrbliss/loki
+#
+#
+# Run loki patch on boot.img for locked bootloaders, found in loki_bootloaders
+#
+
+egrep -q -f /system/etc/loki_bootloaders /proc/cmdline
+if [ $? -eq 0 ];then
+ echo '[*] Locked bootloader version detected.'
+ export C=/tmp/loki_tmpdir
+ mkdir -p $C
+ dd if=/dev/block/platform/msm_sdcc.1/by-name/aboot of=$C/aboot.img
+ echo '[*] Patching boot.img to with loki.'
+ /system/bin/loki_tool patch boot $C/aboot.img /tmp/boot.img $C/boot.lok || exit 1
+ echo '[*] Flashing modified boot.img to device.'
+ /system/bin/loki_tool flash boot $C/boot.lok || exit 1
+ rm -rf $C
+else
+ echo '[*] Unlocked bootloader version detected.'
+ echo '[*] Flashing unmodified boot.img to device.'
+ dd if=/tmp/boot.img of=/dev/block/platform/msm_sdcc.1/by-name/boot || exit 1
+fi
+
+exit 0
diff --git a/releasetools/loki_bootloaders b/releasetools/loki_bootloaders
new file mode 100644
index 0000000..ca099cc
--- /dev/null
+++ b/releasetools/loki_bootloaders
@@ -0,0 +1,2 @@
+# Verizon SGS4
+bootloader=I545VRUAMDK
diff --git a/releasetools/loki_find.c b/releasetools/loki_find.c
new file mode 100644
index 0000000..151a309
--- /dev/null
+++ b/releasetools/loki_find.c
@@ -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/releasetools/loki_flash.c b/releasetools/loki_flash.c
new file mode 100644
index 0000000..16bdd4d
--- /dev/null
+++ b/releasetools/loki_flash.c
@@ -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/releasetools/loki_main.c b/releasetools/loki_main.c
new file mode 100644
index 0000000..1d3d9d3
--- /dev/null
+++ b/releasetools/loki_main.c
@@ -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]);
+}
diff --git a/releasetools/loki_patch.c b/releasetools/loki_patch.c
new file mode 100644
index 0000000..abb9f8d
--- /dev/null
+++ b/releasetools/loki_patch.c
@@ -0,0 +1,670 @@
+/*
+ * 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 = "DoCoMo",
+ .device = "LG Optimus G",
+ .build = "L01E20b",
+ .check_sigs = 0x88F10E48,
+ .hdr = 0x88F54418,
+ .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,
+ },
+};
+
+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/releasetools/loki_unlok.c b/releasetools/loki_unlok.c
new file mode 100644
index 0000000..f2e685a
--- /dev/null
+++ b/releasetools/loki_unlok.c
@@ -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/releasetools/releasetools.py b/releasetools/releasetools.py
new file mode 100644
index 0000000..d14e032
--- /dev/null
+++ b/releasetools/releasetools.py
@@ -0,0 +1,31 @@
+# Copyright (C) 2012 The Android Open Source Project
+# Copyright (C) 2013 The CyanogenMod Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+#
+# This leverages the loki_patch utility created by djrbliss which allows us
+# to bypass the bootloader checks on jfltevzw and jflteatt
+# See here for more information on loki: https://github.com/djrbliss/loki
+#
+
+"""Custom OTA commands for jf devices with locked bootloaders"""
+
+def FullOTA_InstallEnd(info):
+ info.script.script = [cmd for cmd in info.script.script if not "boot.img" in cmd]
+ info.script.script = [cmd for cmd in info.script.script if not "show_progress(0.100000, 0);" in cmd]
+ info.script.Mount("/system")
+ info.script.AppendExtra('package_extract_file("boot.img", "/tmp/boot.img");')
+ info.script.AppendExtra('assert(run_program("/sbin/sh", "/system/etc/loki.sh") == 0);')
+ info.script.Unmount("/system")