Skip to content

Instantly share code, notes, and snippets.

@hduarte
Last active January 26, 2017 22:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hduarte/d18ecbb9b3c4c0ea0eecd80047c13fcd to your computer and use it in GitHub Desktop.
Save hduarte/d18ecbb9b3c4c0ea0eecd80047c13fcd to your computer and use it in GitHub Desktop.
auto offset finder for Ian Beer's iOS 10.1.1 exploit
## What's this?
This simple tool allows you to automatically find the two needed offsets for Ian Beer's iOS 10.1.1 exploit (https://bugs.chromium.org/p/project-zero/issues/detail?id=965#c2).
The tool doesn't support 32-bit offsets or AMFI offsets, just _allproc and _kernproc offsets.
Implementing an auto search for those other ones would be quite complex and time consuming. Just remember that if your device is 64-bit, and you are running the exploit on a recent version (<+ 10.1.1) you should do fine with just the two basic offsets.
## How to use
Download this gist, the cd into the folder and make. Then simply run './off_finder [kernelcache_path]', where kernelcache_path is the actual on-disk path of an uncompressed kernelcache, extracted from an IPSW firmware file.
Once your offsets have been found, just insert them into the provided offsets.c file (more detailed instructions are inside the file itself). Finally, just replace the original offsets.c file (in the exploit folder) with your new one, build everything from Xcode and run on your iDevice.
## Notes
Please report any problems you encounter to me. Twitter: @jndok
//
// machoman.c
// machoman
//
// Created by jndok on 14/05/16.
// Copyright © 2016 jndok. All rights reserved.
//
#include "machoman.h"
macho_map_t *map_macho_with_path(const char *path, int mode)
{
if (!path) return NULL;
if (access(path, R_OK) == -1) return NULL;
int32_t fd = open(path, mode);
if (fd < 0) {
return NULL;
}
struct stat st;
if(fstat(fd, &st) != 0)
goto fail;
macho_map_t *map = (macho_map_t *)malloc(sizeof(macho_map_t));
if((map->map_data = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, (mode == O_RDONLY) ? MAP_PRIVATE : MAP_SHARED, fd, 0)) == MAP_FAILED)
goto fail;
map->map_magic = MACHO_MAP_MAGIC;
map->map_size = (mach_vm_size_t)st.st_size;
map->unique_id = (uint32_t)(((uint64_t)map << 32) >> 32);
return map;
fail:
close(fd);
return NULL;
}
void free_macho_map(macho_map_t *map)
{
if (!is_valid_macho_map(map)) {
return;
}
munmap(map->map_data, map->map_size);
free(map);
}
__attribute__((always_inline))
boolean_t is_valid_macho_file(const char *path)
{
if (!path) return FALSE;
if (access(path, R_OK) == -1) return FALSE;
int32_t fd = open(path, O_RDONLY);
if (fd < 0)
return FALSE;
uint32_t magic = 0;
if (read(fd, (void*)&magic, sizeof(uint32_t)) == -1)
return FALSE;
if ((magic == MH_MAGIC) || (magic == MH_MAGIC_64))
return TRUE;
else
return FALSE;
}
__attribute__((always_inline))
boolean_t is_valid_macho_map(macho_map_t *map)
{
if (!map) return FALSE;
if (!map->map_data) FALSE;
if (map->map_magic != MACHO_MAP_MAGIC) return FALSE;
return TRUE;
}
__attribute__((always_inline))
struct mach_header_64 *get_mach_header_64(macho_map_t *map)
{
if (!is_valid_macho_map(map)) return NULL;
return (struct mach_header_64*)(map->map_data);
}
__attribute__((always_inline))
struct load_command **find_all_load_commands(struct mach_header_64 *mh)
{
struct load_command **all_lcmds = (struct load_command **)malloc(sizeof(struct load_command *) * mh->ncmds);
struct load_command *lcmd = (struct load_command *)(mh + 1);
for (uint32_t i=0; i<mh->ncmds; i++, lcmd += (lcmd->cmdsize / sizeof(struct load_command))) {
all_lcmds[i] = lcmd;
}
return all_lcmds;
}
__attribute__((always_inline))
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t lc)
{
struct load_command *lcmd = (struct load_command *)(mh + 1);
for (uint32_t i=0; i<mh->ncmds; i++, lcmd += (lcmd->cmdsize / sizeof(struct load_command))) {
if (lcmd->cmd == lc)
return lcmd;
}
return NULL;
}
__attribute__((always_inline))
struct segment_command_64 *find_segment_command64(struct mach_header_64 *mh, const char *segname)
{
struct load_command *lcmd = (struct load_command *)(mh + 1);
for (uint32_t i=0; i<mh->ncmds; i++, lcmd += (lcmd->cmdsize / sizeof(struct load_command))) {
if (lcmd->cmd == LC_SEGMENT_64) {
struct segment_command_64 *seg64 = (struct segment_command_64*)(lcmd);
if (strcmp(seg64->segname, segname) == 0)
return seg64;
}
}
return NULL;
}
__attribute__((always_inline))
struct section_64 *find_section64(struct segment_command_64 *seg64, const char *sectname)
{
struct section_64 *sect64 = (struct section_64 *)(seg64 + 1);
for (uint32_t i=0; i<seg64->nsects; i++, sect64++) {
if (strcmp(sect64->sectname, sectname) == 0)
return sect64;
}
return NULL;
}
__attribute__((always_inline))
struct symtab_command *find_symtab_command(struct mach_header_64 *mh)
{
return (struct symtab_command *)find_load_command(mh, LC_SYMTAB);
}
__attribute__((always_inline))
struct dysymtab_command *find_dysymtab_command(struct mach_header_64 *mh)
{
return (struct dysymtab_command *)find_load_command(mh, LC_DYSYMTAB);
}
/*
* == libmachoman v0.1.0 ==
*
* A simple library providing all you need
* for generic Mach-O parsing.
* I found myself rewriting this fucking code
* in every project, so I finally decided to
* do it right, once and for all!
*
*/
//
// machoman.h
// machoman
//
// Created by jndok on 26/05/16.
// Copyright © 2016 jndok. All rights reserved.
//
#ifndef machoman_h
#define machoman_h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach-o/nlist.h>
#define MACHO_MAP_MAGIC 0xDEADC0DE
#define MACHO_MAP_SLIDE_OFFSET(map, off) ((uint64_t)(map->map_data) + (uint64_t)off)
#define MACHO_MAP_UNSLIDE_OFFSET(map, off) ((uint64_t)off > (uint64_t)(map->map_data)) ? ((uint64_t)off - (uint64_t)(map->map_data)) : ((uint64_t)off)
enum {
MMRC_ErrGen = 1
};
typedef struct macho_map {
uint32_t map_magic;
void *map_data;
mach_vm_size_t map_size;
uint32_t unique_id;
} macho_map_t;
macho_map_t *map_macho_with_path(const char *path, int mode);
void free_macho_map(macho_map_t *map);
__attribute__((always_inline)) boolean_t is_valid_macho_file(const char *path); /* before you map */
__attribute__((always_inline)) boolean_t is_valid_macho_map(macho_map_t *map);
__attribute__((always_inline)) struct mach_header_64 *get_mach_header_64(macho_map_t *map);
__attribute__((always_inline)) struct load_command **find_all_load_commands(struct mach_header_64 *mh);
__attribute__((always_inline)) struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t lc);
__attribute__((always_inline)) struct segment_command_64 *find_segment_command64(struct mach_header_64 *mh, const char *segname);
__attribute__((always_inline)) struct section_64 *find_section64(struct segment_command_64 *seg64, const char *sectname);
__attribute__((always_inline)) struct symtab_command *find_symtab_command(struct mach_header_64 *mh);
__attribute__((always_inline)) struct dysymtab_command *find_dysymtab_command(struct mach_header_64 *mh);
#endif /* machoman_h */
//
// main.c
// off_finder
//
// Created by jndok on 17/12/16.
// Copyright © 2016 jndok. All rights reserved.
//
#define __dbg(...) do { fprintf(stdout, "[+] Debug (_%s): ", __FUNCTION__); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } while (0)
#define __err_and_exit(status, ...) do { fprintf(stderr, "[!] Error: " __VA_ARGS__); fprintf(stderr, "\n"); exit(status); } while (0)
#include <stdio.h>
#include <mach/mach.h>
#include "machoman.h"
#define STP_MASK 0xa9bfffff
#define ADD_IMM_MASK 0x91ffffff
#define MOV_REG_MASK 0xaa1f03ff
#define STR_IMM_UO 0xf93fffff
#define STUR_MASK 0xf81ff3ff
#define LDR_IMM_UO_MASK 0xf97fffff
#define BL_MASK 0x97ffffff
uint32_t mask_array[] = {
STP_MASK,
STP_MASK,
STP_MASK,
STP_MASK,
ADD_IMM_MASK,
MOV_REG_MASK,
MOV_REG_MASK,
STR_IMM_UO,
MOV_REG_MASK,
STR_IMM_UO,
STR_IMM_UO,
STUR_MASK,
LDR_IMM_UO_MASK,
STUR_MASK,
LDR_IMM_UO_MASK,
STUR_MASK,
BL_MASK
};
void *mask_search(uint32_t *start, uint32_t *end) {
boolean_t b = 0;
uint32_t index = 0;
void *addr = NULL;
for (; start < end; start++) {
if (*start == 0)
continue;
if (index == sizeof(mask_array) / sizeof(uint32_t)) {
return addr;
}
if ((*start & mask_array[index]) == *start) {
index++;
if (!b)
addr = start;
b = 1;
} else {
if (b) {
index = 0;
b = 0;
}
}
}
return NULL;
}
uint64_t find_symbol(macho_map_t *map, struct mach_header_64 *mh, const char *s)
{
struct symtab_command *symtab_cmd = find_symtab_command(mh);
if (!symtab_cmd)
__err_and_exit(5, "Unable to find symbol table in specified kernelcache.");
void *symtab = map->map_data + symtab_cmd->symoff;
void *strtab = map->map_data + symtab_cmd->stroff;
struct nlist_64 *entry = (struct nlist_64 *)symtab;
for (uint32_t i = 0; i < symtab_cmd->nsyms; i++, entry++) {
if (strcmp(s, (char *)(strtab + entry->n_un.n_strx)) == 0) {
return entry->n_value;
}
}
return -1;
}
static int32_t extract_signed_bitfield(uint32_t insn, unsigned width, unsigned offset)
{
unsigned shift_l = sizeof (int32_t) * 8 - (offset + width);
unsigned shift_r = sizeof (int32_t) * 8 - width;
return ((int32_t) insn << shift_l) >> shift_r;
}
static int decode_masked_match(uint32_t insn, uint32_t mask, uint32_t pattern)
{
return (insn & mask) == pattern;
}
int aarch64_decode_b(uint32_t insn, int *is_bl, int32_t *offset)
{
/* b 0001 01ii iiii iiii iiii iiii iiii iiii */
/* bl 1001 01ii iiii iiii iiii iiii iiii iiii */
if (decode_masked_match (insn, 0x7c000000, 0x14000000))
{
*is_bl = (insn >> 31) & 0x1;
*offset = extract_signed_bitfield (insn, 26, 0) << 2;
return 1;
}
return 0;
}
int aarch64_decode_adr (uint32_t insn, int *is_adrp, unsigned *rd, int32_t *offset)
{
/* adr 0ii1 0000 iiii iiii iiii iiii iiir rrrr */
/* adrp 1ii1 0000 iiii iiii iiii iiii iiir rrrr */
if (decode_masked_match (insn, 0x1f000000, 0x10000000))
{
uint32_t immlo = (insn >> 29) & 0x3;
int32_t immhi = extract_signed_bitfield (insn, 19, 5) << 2;
*is_adrp = (insn >> 31) & 0x1;
*rd = (insn >> 0) & 0x1f;
if (*is_adrp)
{
/* The ADRP instruction has an offset with a -/+ 4GB range,
encoded as (immhi:immlo * 4096). */
*offset = (immhi | immlo) * 4096;
}
else
*offset = (immhi | immlo);
return 1;
}
return 0;
}
int aarch64_decode_add (uint32_t insn, uint32_t *offset)
{
/* x00x 0001 SSii iiii iiii iinn nnnd dddd */
*offset = (insn << 10) >> 20;
return 1;
}
int main(int argc, const char * argv[]) {
if (argc < 2) {
printf("usage: ./off_finder [kernelcache_path]\n");
return -1;
}
const char *KERNELCACHE_PATH = argv[1];
__dbg("Mapping kernelcache in memory...");
macho_map_t *map = map_macho_with_path(KERNELCACHE_PATH, O_RDONLY);
if (!map)
__err_and_exit(1, "Unable to map kernelcache (\'%s\') in memory.", KERNELCACHE_PATH);
struct mach_header_64 *mh = (struct mach_header_64 *)map->map_data;
if (mh->magic != MH_MAGIC_64) {
void *p = (void *)map->map_data;
void *end = p + (128 * sizeof(uint32_t));
for (; p < end; p++) {
if (*(uint32_t *)p == MH_MAGIC_64) {
printf("[!] Warning: This is a compressed kernelcache. Starting offset is: %#x.\n", (uint32_t)(p - map->map_data - 1));
printf("Please download and compile lzssdec (http://nah6.com/~itsme/cvs-xdadevtools/iphone/tools/lzssdec.cpp) to uncompress this kernelcache before use with this tool.\n");
printf("Once downloaded and compiled, run: \'lzssdec -o %#x < %s > kernelcache.decompressed\'\nThen run this tool on the output file.\n", (uint32_t)(p - map->map_data - 1), KERNELCACHE_PATH);
return 0;
}
}
__err_and_exit(6, "Invalid kernelcache.");
return 0;
}
struct segment_command_64 *text_cmd = find_segment_command64(mh, SEG_TEXT);
if (!text_cmd)
__err_and_exit(2, "Unable to find __TEXT segment in specified kernelcache.");
struct segment_command_64 *text_exec_cmd = find_segment_command64(mh, "__TEXT_EXEC");
if (!text_exec_cmd)
__err_and_exit(3, "Unable to find __TEXT_EXEC segment in specified kernelcache.");
void *start = map->map_data + text_exec_cmd->fileoff;
void *end = start + text_exec_cmd->filesize;
void *pgrp_add_addr_map = mask_search(start, end);
if (!pgrp_add_addr_map)
__err_and_exit(4, "Unable to find _pgrp_add in map.");
uint64_t panic_addr = find_symbol(map, mh, "_panic");
if (panic_addr == -1)
__err_and_exit(6, "Unable to find _panic address.");
uint64_t pgrp_add_addr = text_cmd->vmaddr + (pgrp_add_addr_map - map->map_data);
uint32_t *p = pgrp_add_addr_map;
uint32_t index = 0;
while (1) {
if (index > (1000 * sizeof(uint32_t)))
break;
if ((*p & BL_MASK) == *p) {
int is_bl = 0;
int32_t off = 0;
aarch64_decode_b(*p, &is_bl, &off);
if (is_bl) {
if ((pgrp_add_addr + off + index) == panic_addr)
break;
}
}
index+=4;
p++;
}
uint64_t final = 0;
uint32_t *end_func = pgrp_add_addr_map + index;
for (; end_func > (uint32_t *)pgrp_add_addr_map; end_func--, index--) {
int is_adrp = 0;
unsigned int rd = 0;
int32_t off = 0;
if (aarch64_decode_adr(*end_func, &is_adrp, &rd, &off)) {
if (is_adrp) {
if (rd == 8) {
uint64_t base_page = trunc_page(((uint64_t)(((void *)end_func - map->map_data) + text_cmd->vmaddr) + off));
uint32_t off_add = 0;
aarch64_decode_add(*(end_func+1), &off_add);
final = base_page + off_add;
break;
}
}
}
}
uint64_t allproc_off = final - text_cmd->vmaddr;
uint64_t kernproc_off = find_symbol(map, mh, "_kernproc") - text_cmd->vmaddr;
uint64_t rootvnode_off = find_symbol(map, mh, "_rootvnode") - text_cmd->vmaddr;
__dbg("All done! Now open the included \'offsets.c\' file, write your two offsets in the \'init_custom()\' function, and replace the file in the original exploit code. Then run and it should work!\n");
printf("_allproc: %#llx\n", allproc_off);
printf("_kernproc: %#llx\n", kernproc_off);
printf("_rootvnode: %#llx\n", rootvnode_off);
return 0;
}
all:
clang main.c machoman.c -o off_finder
clean:
rm -rf off_finder
/*
* ++ HOW TO USE YOUR CUSTOM OFFSETS ++
* After running the tool, you will get two offsets (allproc and kernproc)
* Simply copy those two hex values and paste them in the init_custom function().
* By default they have a value of 0x0. Overwrite that with your values.
*
* Once done, save the file and replace the original offsets.c with this one.
* Then go back in Xcode and run the exploit on your device.
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/utsname.h>
#include "offsets.h"
// offsets from the main kernel 0xfeedfacf
uint64_t allproc_offset;
uint64_t kernproc_offset;
// offsets in struct proc
uint64_t struct_proc_p_pid_offset;
uint64_t struct_proc_task_offset;
uint64_t struct_proc_p_uthlist_offset;
uint64_t struct_proc_p_ucred_offset;
uint64_t struct_proc_p_comm_offset;
// offsets in struct kauth_cred
uint64_t struct_kauth_cred_cr_ref_offset;
// offsets in struct uthread
uint64_t struct_uthread_uu_ucred_offset;
uint64_t struct_uthread_uu_list_offset;
// offsets in struct task
uint64_t struct_task_ref_count_offset;
uint64_t struct_task_itk_space_offset;
// offsets in struct ipc_space
uint64_t struct_ipc_space_is_table_offset;
// offsets in struct ipc_port
uint64_t struct_ipc_port_ip_kobject_offset;
/*** EDIT THIS FUNCTION WITH YOUR OFFSETS! ***/
void init_custom()
{
printf("setting custom offsets!\n");
allproc_offset = 0x0;
kernproc_offset = 0x0;
struct_proc_p_pid_offset = 0x10;
struct_proc_task_offset = 0x18;
struct_proc_p_uthlist_offset = 0x98;
struct_proc_p_ucred_offset = 0x100;
struct_proc_p_comm_offset = 0x26c;
struct_kauth_cred_cr_ref_offset = 0x10;
struct_uthread_uu_ucred_offset = 0x168;
struct_uthread_uu_list_offset = 0x170;
struct_task_ref_count_offset = 0x10;
struct_task_itk_space_offset = 0x300;
struct_ipc_space_is_table_offset = 0x20;
struct_ipc_port_ip_kobject_offset = 0x68;
}
/***********************************************/
void init_ipad_mini_2_10_1_1_14b100() {
printf("setting offsets for iPad mini 2 10.1.1\n");
allproc_offset = 0x5A4128;
kernproc_offset = 0x5AA0E0;
struct_proc_p_pid_offset = 0x10;
struct_proc_task_offset = 0x18;
struct_proc_p_uthlist_offset = 0x98;
struct_proc_p_ucred_offset = 0x100;
struct_proc_p_comm_offset = 0x26c;
struct_kauth_cred_cr_ref_offset = 0x10;
struct_uthread_uu_ucred_offset = 0x168;
struct_uthread_uu_list_offset = 0x170;
struct_task_ref_count_offset = 0x10;
struct_task_itk_space_offset = 0x300;
struct_ipc_space_is_table_offset = 0x20;
struct_ipc_port_ip_kobject_offset = 0x68;
}
void init_ipod_touch_6g_10_1_1_14b100() {
printf("setting offsets for iPod touch 6G 10.1.1\n");
allproc_offset = 0x5B4168;
kernproc_offset = 0x5BA0E0;
struct_proc_p_pid_offset = 0x10;
struct_proc_task_offset = 0x18;
struct_proc_p_uthlist_offset = 0x98;
struct_proc_p_ucred_offset = 0x100;
struct_proc_p_comm_offset = 0x26c;
struct_kauth_cred_cr_ref_offset = 0x10;
struct_uthread_uu_ucred_offset = 0x168;
struct_uthread_uu_list_offset = 0x170;
struct_task_ref_count_offset = 0x10;
struct_task_itk_space_offset = 0x300;
struct_ipc_space_is_table_offset = 0x20;
struct_ipc_port_ip_kobject_offset = 0x68;
}
void init_macos_10_12_1() {
printf("setting offsets for MacOS 10.12.1\n");
allproc_offset = 0x8bb490;
kernproc_offset = 0x8BA7D8;
struct_proc_task_offset = 0x18;
struct_proc_p_uthlist_offset = 0x98;
struct_proc_p_ucred_offset = 0xe8;
struct_proc_p_comm_offset = 0x2e4;
struct_kauth_cred_cr_ref_offset = 0x10;
struct_uthread_uu_ucred_offset = 0x168;
struct_uthread_uu_list_offset = 0x170;
struct_task_ref_count_offset = 0x10;
struct_task_itk_space_offset = 0x300;
struct_ipc_space_is_table_offset = 0x18;
struct_ipc_port_ip_kobject_offset = 0x68;
}
void unknown_build() {
printf("This is an unknown kernel build - the offsets are likely to be incorrect and it's very unlikely this exploit will work\n");
printf("You need to find these two kernel symbols:\n");
printf(" allproc\n");
printf(" kernproc\n\n");
printf("and update the code\n");
}
void init_offsets() {
struct utsname u = {0};
int err = uname(&u);
if (err == -1) {
printf("uname failed - what platform is this?\n");
printf("there's no way this will work, but trying anyway!\n");
init_ipad_mini_2_10_1_1_14b100();
return;
}
printf("sysname: %s\n", u.sysname);
printf("nodename: %s\n", u.nodename);
printf("release: %s\n", u.release);
printf("version: %s\n", u.version);
printf("machine: %s\n", u.machine);
printf("[!] Warning: Ignoring original code, skipping to custom offsets function!\n");
init_custom();
#if 0
if (strstr(u.machine, "iPod7,1")) {
// this is an iPod 6G
if (strstr(u.version, "root:xnu-3789.22.3~1/RELEASE_ARM64_T7000")) {
printf("this is a known kernel build for iPod touch 6G - offsets should be okay\n");
} else {
unknown_build();
}
init_ipod_touch_6g_10_1_1_14b100();
return;
}
if (strstr(u.machine, "iPad4,4")) {
// this is an iPad mini 2
if (strstr(u.version, "root:xnu-3789.22.3~1/RELEASE_ARM64_S5L8960X")){
printf("this is a known kernel build for iPad mini 2 - offsets should be okay\n");
} else {
unknown_build();
}
init_ipad_mini_2_10_1_1_14b100();
return;
}
printf("don't recognize this platform\n");
unknown_build();
init_ipad_mini_2_10_1_1_14b100(); // this won't work!
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment