* 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: I45ef363e05566268c8f24f7e8939a2d785478fbetirimbino
parent
7ffb495501
commit
802fd001d4
@ -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]); |
||||
} |
Loading…
Reference in new issue