Skip to content

Commit

Permalink
make it better
Browse files Browse the repository at this point in the history
  • Loading branch information
comex committed Dec 16, 2010
1 parent 879d9ad commit 4b1d25e
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 23 deletions.
6 changes: 3 additions & 3 deletions Makefile
@@ -1,9 +1,9 @@
all: white_loader

GCC ?= /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -arch armv7 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.1.sdk/ -mapcs-frame -fomit-frame-pointer -mthumb -fno-builtin-printf -fno-builtin-memset
GCC ?= /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -arch armv7 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.2.sdk/ -mapcs-frame -fomit-frame-pointer -mthumb -fno-builtin-printf -fno-builtin-memset
CFLAGS ?= -g3 -std=gnu99 -Os -Wall -Wno-parentheses -Werror
ifneq ($(strip $(wildcard /usr/include/CommonCrypto)),)
CFLAGS += -DIMG3_SUPPORT
CFLAGS += -DIMG3_SUPPORT -dead_strip
endif
AR ?= ar

Expand All @@ -14,7 +14,7 @@ libdata.a: common.o binary.o running_kernel.o link.o find.o cc.o lzss.o
$(AR) rcs $@ $^

white_loader: libdata.a white_loader.o
$(GCC) -o $@ white_loader.o -L. -ldata
$(GCC) $(CFLAGS) -o $@ white_loader.o -L. -ldata
ifneq ($(shell which lipo),)
bash -c 'if [ -n "`lipo -info $@ | grep arm`" ]; then ldid -Sent.plist $@; fi'
endif
Expand Down
149 changes: 134 additions & 15 deletions binary.c
Expand Up @@ -4,6 +4,7 @@
#include "nlist.h"
#include "fat.h"
#include "dyld_cache_format.h"
#include "find.h"

const int desired_cputype = 12; // ARM
const int desired_cpusubtype = 0; // v7=9, v6=6
Expand Down Expand Up @@ -268,6 +269,10 @@ void b_load_macho(struct binary *binary, const char *path, bool rw) {
b_prange_load_macho(binary, load_file(path, rw, NULL), path);
}

void b_fd_load_macho(struct binary *binary, int fd, bool rw) {
b_prange_load_macho(binary, load_fd(fd, rw), "(fd)");
}

range_t b_macho_segrange(const struct binary *binary, const char *segname) {
if(binary->last_seg && !strncmp(binary->last_seg->segname, segname, 16)) {
return (range_t) {binary, binary->last_seg->vmaddr, binary->last_seg->filesize};
Expand Down Expand Up @@ -410,16 +415,22 @@ uint32_t b_allocate_from_macho_fd(int fd) {
}


static addr_t find_vm_pageout(const struct binary *binary);

void b_inject_into_macho_fd(const struct binary *binary, int fd) {
off_t off = lseek(fd, 0, SEEK_SET);
addr_t vm_pageout = 0;
prange_t vm_pageout_pr = {0, 0};
range_t vm_pageout_off_range = {NULL, 0, 0};

off_t seg_off = lseek(fd, 0, SEEK_END);
struct mach_header *hdr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(hdr == MAP_FAILED) edie("could not mmap hdr in read/write mode");
if(hdr->sizeofcmds > 0x1000 || hdr->sizeofcmds + sizeof(struct mach_header) > 0x1000) {
die("too many commands");
}

struct segment_command *newseg = (void *) ((char *) (hdr + 1) + hdr->sizeofcmds);
off_t newseg_off = sizeof(struct mach_header) + hdr->sizeofcmds;
off_t header_off = sizeof(struct mach_header) + hdr->sizeofcmds;

CMD_ITERATE(binary->mach_hdr, cmd) {
if(cmd->cmd == LC_SEGMENT) {
Expand All @@ -428,37 +439,139 @@ void b_inject_into_macho_fd(const struct binary *binary, int fd) {
die("too many sections");
}
size_t size = sizeof(struct segment_command) + seg->nsects * sizeof(struct section);
if(newseg_off + size > 0x1000) {
if(size != seg->cmdsize) {
die("inconsistent cmdsize");
}

if(header_off + size > 0x1000) {
die("not enough space");
}

hdr->ncmds++;
hdr->sizeofcmds += size;

memcpy(newseg, seg, size);

newseg->fileoff = (uint32_t) off;
seg_off = (seg_off + 0xfff) & ~0xfff;

newseg->fileoff = (uint32_t) seg_off;
prange_t pr = rangeconv((range_t) {binary, seg->vmaddr, seg->filesize});
if(pwrite(fd, pr.start, pr.size, off) != pr.size) {
if(pwrite(fd, pr.start, pr.size, seg_off) != pr.size) {
die("couldn't write additional segment");
}

seg_off += pr.size;

newseg = (void *) ((char *)newseg + size);
newseg_off += size;
header_off += size;

struct section *sections = (void *) (seg + 1);
for(int i = 0; i < seg->nsects; i++) {
struct section *sect = &sections[i];
if((sect->flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS) {
// ldr r3, [pc]; bx r3
uint16_t part0[] = {0x4b00, 0x4718};

// push {lr}; ldr r3, [pc, #4]; blx r3; b next
// (the address of the init func)
//
uint16_t part1[] = {0xb500, 0x4b01, 0x4798, 0xe001};
// (bytes_to_move bytes of stuff)
// pop {r3}; mov lr, r3
static const uint16_t part2[] = {0xbc08, 0x469e};
// ldr r3, foo; bx r3
static const uint16_t part3[] = {0x4b00, 0x4718};

printf("note: constructor functions are present; hacking vm_pageout\n");
uint32_t bytes_to_move = 12; // don't cut the MRC in two!
addr_t vm_pageout = find_vm_pageout(kern);
if(!vm_pageout) {
struct binary kern;
b_init(&kern);
b_fd_load_macho(&kern, fd, false);
vm_pageout = find_vm_pageout(&kern);
vm_pageout_pr = rangeconv((range_t) {&kern, vm_pageout & ~1, bytes_to_move});
vm_pageout_off_range = range_to_off_range((range_t) {&kern, vm_pageout & ~1, sizeof(part0) + sizeof(uint32_t)});
}
// allocate a new segment for the stub
// push {lr}; ldr r3, [pc, #4]; blx r3; b next
static const uint16_t part1[] = {0xb500, 0x4b01, 0x4798, 0xe001};
// (bytes_to_move bytes of stuff)
// pop {r3}; mov lr, r3; ldr r3, foo; bx r3
static const uint16_t part2[] = {0xbc08, 0x469e, 0x4b00, 0x4718};
if(newseg_off + size > 0x1000) {
die("not enough space");
}

prange_t pr = rangeconv((range_t) {binary, sect->addr, sect->size});


uint32_t stub_size = (sizeof(part1) + 4) * (pr.size / 4) + sizeof(part2) + bytes_to_move + sizeof(part3) + 4;

if(!(vm_pageout & 1)) {
die("vm_pageout is not thumb");
}


size = sizeof(struct segment_command);
if(header_off + size > 0x1000) {
die("not enough space");
}

seg_off = (seg_off + 0xfff) & ~0xfff;

hdr->ncmds++;
hdr->sizeofcmds += sizeof(struct segment_command);

newseg->cmd = LC_SEGMENT;
newseg->cmdsize = sizeof(struct segment_command);
memset(newseg->segname, 0, 16);
strcpy(newseg->segname, "__CRAP");
newseg->vmaddr = b_allocate_from_macho_fd(fd);
newseg->vmsize = stub_size;
newseg->fileoff = (uint32_t) seg_off;
newseg->filesize = stub_size;
newseg->maxprot = newseg->initprot = PROT_READ | PROT_EXEC;
newseg->nsects = 0;
newseg->flags = 0;

lseek(fd, seg_off, SEEK_SET);

seg_off += pr.size;

while(pr.size > 0) {
if(write(fd, part1, sizeof(part1)) != sizeof(part1) ||
write(fd, pr.start, 4) != 4) {
edie("couldn't write part1");
}
pr.start = (uint32_t *) pr.start + 1;
pr.size -= 4;
part1[0] = 0x46c0;
}

if(write(fd, part2, sizeof(part2)) != sizeof(part2)) {
edie("couldn't write part2");
}

if(write(fd, vm_pageout_pr.start, bytes_to_move) != bytes_to_move) {
edie("couldn't write moved bytes");
}

if(write(fd, part3, sizeof(part3)) != sizeof(part3)) {
edie("couldn't write part3");
}

uint32_t new_addr = vm_pageout + bytes_to_move;

if(write(fd, &new_addr, sizeof(new_addr)) != sizeof(new_addr)) {
edie("couldn't write new_addr");
}

lseek(fd, vm_pageout_off_range.start, SEEK_SET);

if(write(fd, part0, sizeof(part0)) != sizeof(part0)) {
edie("couldn't write part0");
}

new_addr = newseg->vmaddr | 1;

if(write(fd, &new_addr, sizeof(new_addr)) != sizeof(new_addr)) {
edie("couldn't write new_addr 2");
}

newseg = (void *) ((char *)newseg + size);
header_off += size;
}
// ZEROFILL is okay because iBoot always zeroes vmsize - filesize
}
Expand All @@ -467,3 +580,9 @@ void b_inject_into_macho_fd(const struct binary *binary, int fd) {

munmap(hdr, 0x1000);
}

static addr_t find_vm_pageout(const struct binary *binary) {
range_t text = b_macho_segrange(binary, "__TEXT");
const char *string = "\"vm_pageout_iothread_external";
return find_bof(text, find_int32(text, find_bytes(text, string, strlen(string), 1, true), true), true) | 1;
}
3 changes: 2 additions & 1 deletion binary.h
Expand Up @@ -42,7 +42,7 @@ __attribute__((const)) prange_t rangeconv(range_t range);
// offset -> buffer
__attribute__((const)) prange_t rangeconv_off(range_t range);
// address -> offset
__attribute__((const)) addr_t range_to_off(range_t range);
__attribute__((const)) range_t range_to_off_range(range_t range);

void b_init(struct binary *binary);

Expand All @@ -54,6 +54,7 @@ void b_dyldcache_load_macho(struct binary *binary, const char *filename);

void b_macho_load_symbols(struct binary *binary);
void b_load_macho(struct binary *binary, const char *path, bool rw);
void b_fd_load_macho(struct binary *binary, int fd, bool rw);
void b_prange_load_macho(struct binary *binary, prange_t range, const char *name);

__attribute__((pure)) range_t b_macho_segrange(const struct binary *binary, const char *segname);
Expand Down
6 changes: 5 additions & 1 deletion common.c
Expand Up @@ -73,6 +73,11 @@ prange_t load_file(const char *filename, bool rw, mode_t *mode) {
if(fd == -1) {
edie("could not open");
}
return load_fd(fd, rw);
#undef _arg
}

prange_t load_fd(int fd, bool rw) {
off_t end = lseek(fd, 0, SEEK_END);
if(sizeof(off_t) > sizeof(size_t) && end > (off_t) SIZE_MAX) {
die("too big: %lld", (long long) end);
Expand All @@ -82,7 +87,6 @@ prange_t load_file(const char *filename, bool rw, mode_t *mode) {
edie("could not mmap buf");
}
return (prange_t) {buf, (size_t) end};
#undef _arg
}

void store_file(prange_t range, const char *filename, mode_t mode) {
Expand Down
2 changes: 2 additions & 0 deletions common.h
Expand Up @@ -40,6 +40,8 @@ bool is_valid_range(prange_t range);
prange_t parse_hex_string(const char *string);

prange_t load_file(const char *filename, bool rw, mode_t *mode);
prange_t load_fd(int fd, bool rw);

void store_file(prange_t range, const char *filename, mode_t mode);

uint32_t parse_hex_uint32(char *string);
15 changes: 12 additions & 3 deletions find.c
@@ -1,7 +1,7 @@
#include "find.h"
#include "binary.h"

static addr_t find_data_int(range_t range, int16_t *buf, ssize_t pattern_size, size_t offset, int align, bool must_find, const char *name) {
static addr_t find_data_raw(range_t range, int16_t *buf, ssize_t pattern_size, size_t offset, int align, bool must_find, const char *name) {
int8_t table[256];
for(int c = 0; c < 256; c++) {
table[c] = pattern_size;
Expand Down Expand Up @@ -92,7 +92,7 @@ addr_t find_data(range_t range, char *to_find, int align, bool must_find) {
if(offset == -1) {
die("pattern [%s] doesn't have an offset", to_find);
}
addr_t result = find_data_int(range, buf, pattern_size, offset, align, must_find, to_find);
addr_t result = find_data_raw(range, buf, pattern_size, offset, align, must_find, to_find);
#ifdef PROFILING
clock_t b = clock();
printf("find_data [%s] took %d/%d\n", to_find, (int)(b - a), (int)CLOCKS_PER_SEC);
Expand All @@ -107,11 +107,20 @@ addr_t find_string(range_t range, const char *string, int align, bool must_find)
for(unsigned int i = 0; i < len; i++) {
buf[i+1] = (uint8_t) string[i];
}
addr_t result = find_data_int(range, buf, len, 1, align, must_find, string);
addr_t result = find_data_raw(range, buf, len + 2, 1, align, must_find, string);
free(buf);
return result;
}

addr_t find_bytes(range_t range, const char *bytes, size_t len, int align, bool must_find) {
int16_t *buf = malloc(sizeof(int16_t) * (len + 2));
for(unsigned int i = 0; i < len; i++) {
buf[i] = (uint8_t) bytes[i];
}
addr_t result = find_data_raw(range, buf, len, 0, align, must_find, "bytes");
free(buf);
return result;
}
addr_t find_int32(range_t range, uint32_t number, bool must_find) {
prange_t pr = rangeconv(range);
char *start = pr.start;
Expand Down
1 change: 1 addition & 0 deletions find.h
Expand Up @@ -6,6 +6,7 @@ struct binary;
// Specify align as 0 if you only expect to find it at one place anyway.
addr_t find_data(range_t range, char *to_find, int align, bool must_find);
addr_t find_string(range_t range, const char *string, int align, bool must_find);
addr_t find_bytes(range_t range, const char *bytes, size_t len, int align, bool must_find);
addr_t find_int32(range_t range, uint32_t number, bool must_find);

// lol what is this
Expand Down

0 comments on commit 4b1d25e

Please sign in to comment.