mkbootimg: Add Loki support

* 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
tirimbino
Nolen Johnson 5 years ago
parent 7ffb495501
commit 802fd001d4
  1. 5
      Android.mk
  2. 17
      loki_tool/Android.mk
  3. 25
      loki_tool/LICENSE.txt
  4. 30
      loki_tool/Makefile
  5. 81
      loki_tool/README.txt
  6. 88
      loki_tool/loki.h
  7. 91
      loki_tool/loki_find.c
  8. 145
      loki_tool/loki_flash.c
  9. 694
      loki_tool/loki_patch.c
  10. 152
      loki_tool/loki_unlok.c
  11. 56
      loki_tool/main.c
  12. 11
      mkbootimg.mk

@ -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

@ -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)

@ -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.

@ -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

@ -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!

@ -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_

@ -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;
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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]);
}

@ -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: $@"

Loading…
Cancel
Save