diff options
| author | dcd <dcd1182@gmail.com> | 2014-11-19 17:27:26 -0600 |
|---|---|---|
| committer | Dan Pasanen <dan.pasanen@gmail.com> | 2014-11-19 19:47:19 -0600 |
| commit | 49dbe36e25c7c0432b05fd20dd015b2346f2fca0 (patch) | |
| tree | 30d2e72cf6fa8841e91aff319e9e18c6b1b80dd5 | |
| parent | b2efbc6b810a123baca5d713a98e0f306b5a0665 (diff) | |
jflteatt: de-commonize loki
Change-Id: I6aaabb2f87858936d7ef9581a722dac41d139a48
| -rw-r--r-- | BoardConfig.mk | 3 | ||||
| -rw-r--r-- | device.mk | 5 | ||||
| -rw-r--r-- | releasetools/Android.mk | 51 | ||||
| -rw-r--r-- | releasetools/LICENSE | 25 | ||||
| -rw-r--r-- | releasetools/loki-recovery.sh | 48 | ||||
| -rw-r--r-- | releasetools/loki.h | 88 | ||||
| -rw-r--r-- | releasetools/loki.sh | 28 | ||||
| -rw-r--r-- | releasetools/loki_bootloaders | 2 | ||||
| -rw-r--r-- | releasetools/loki_find.c | 91 | ||||
| -rw-r--r-- | releasetools/loki_flash.c | 145 | ||||
| -rw-r--r-- | releasetools/loki_main.c | 56 | ||||
| -rw-r--r-- | releasetools/loki_patch.c | 670 | ||||
| -rw-r--r-- | releasetools/loki_unlok.c | 152 | ||||
| -rw-r--r-- | releasetools/releasetools.py | 31 |
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 @@ -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") |
