#include <sys/mman.h> // For mmap(2) #include <sys/stat.h> // For stat(2) #include <unistd.h> // For everything else #include <fcntl.h> // O_RDONLY #include <stdio.h> // printf! #include <CoreFoundation/CoreFoundation.h> #include <mach-o/loader.h> // struct mach_header #include "machlib.h" /** * * A simple program to home in on XNU's system call table. * Coded specifically for iOS kernels. Seeks XNU version string * and signature of beginning of system call table. Then dumps * all system calls. Can work on the kernel proper, or the kernel cache. * * * System call names auto-generated from iOS's <sys/syscall.h> * (/Developer/Platforms/iPhoneOS.platform/DeviceSupport/Latest/Symbols/usr/include/sys) * * can also be generated from OS X's <sys/syscall.h>, with minor tweaks (e.g. include * ledger, pid_shutdown_sockets, etc..) * * Note, that just because a syscall is present, doesn't imply it's implemented - * System calls can either point to nosys, or can be stubs returning an error code, * as is the case with audit syscalls (350-359), among others. * * Tested on iOS 2.0 through 7.1 * * 03/20/14: Updated to dump sysctls, code cleaned, more messy code added * * @TODO: * - Port to ARMv8/x86_64, * - Create a companion .dSYM * * Coded by Jonathan Levin, info@newosxbook.com * **/ # char *syscall_names[] = { "syscall", "exit", "fork", "read", "write", "open", "close", "wait4", "8 old creat", "link", "unlink", "11 old execv", "chdir", "fchdir", "mknod", "chmod", "chown", "17 old break", "getfsstat", "19 old lseek", "getpid", "21 old mount", "22 old umount", "setuid", "getuid", "geteuid", "ptrace", "recvmsg", "sendmsg", "recvfrom", "accept", "getpeername", "getsockname", "access", "chflags", "fchflags", "sync", "kill", "38 old stat", "getppid", "40 old lstat", "dup", "pipe", "getegid", "profil", "45 old ktrace", "sigaction", "getgid", "sigprocmask", "getlogin", "setlogin", "acct", "sigpending", "sigaltstack", "ioctl", "reboot", "revoke", "symlink", "readlink", "execve", "umask", "chroot", "62 old fstat", "63 used internally , reserved", "64 old getpagesize", "msync", "vfork", "67 old vread", "68 old vwrite", "69 old sbrk", "70 old sstk", "71 old mmap", "72 old vadvise", "munmap", "mprotect", "madvise", "76 old vhangup", "77 old vlimit", "mincore", "getgroups", "setgroups", "getpgrp", "setpgid", "setitimer", "84 old wait", "swapon", "getitimer", "87 old gethostname", "88 old sethostname", "getdtablesize", "dup2", "91 old getdopt", "fcntl", "select", "94 old setdopt", "fsync", "setpriority", "socket", "connect", "99 old accept", "getpriority", "101 old send", "102 old recv", "103 old sigreturn", "bind", "setsockopt", "listen", "107 old vtimes", "108 old sigvec", "109 old sigblock", "110 old sigsetmask", "sigsuspend", "112 old sigstack", "113 old recvmsg", "114 old sendmsg", "115 old vtrace", "gettimeofday", "getrusage", "getsockopt", "119 old resuba", "readv", "writev", "settimeofday", "fchown", "fchmod", "125 old recvfrom", "setreuid", "setregid", "rename", "129 old truncate", "130 old ftruncate", "flock", "mkfifo", "sendto", "shutdown", "socketpair", "mkdir", "rmdir", "utimes", "futimes", "adjtime", "141 old getpeername", "gethostuuid", "143 old sethostid", "144 old getrlimit", "145 old setrlimit", "146 old killpg", "setsid", "148 old setquota", "149 old qquota", "150 old getsockname", "getpgid", "setprivexec", "pread", "pwrite", "nfssvc", "156 old getdirentries", "statfs", "fstatfs", "unmount", "160 old async_daemon", "getfh", "162 old getdomainname", "163 old setdomainname", "164", "quotactl", "166 old exportfs", "mount", "168 old ustat", "csops", "csops_audittoken", "171 old wait3", "172 old rpause", "waitid", "174 old getdents", "175 old gc_control", "add_profil", "177", "178", "179", "kdebug_trace", "setgid", "setegid", "seteuid", "sigreturn", "chud", "186", "fdatasync", "stat", "fstat", "lstat", "pathconf", "fpathconf", "193", "getrlimit", "setrlimit", "getdirentries", "mmap", "198 __syscall", "lseek", "truncate", "ftruncate", "__sysctl", "mlock", "munlock", "undelete", "ATsocket", "ATgetmsg", "ATputmsg", "ATPsndreq", "ATPsndrsp", "ATPgetreq", "ATPgetrsp", "213 Reserved for AppleTalk", "214", "215", "mkcomplex", "statv", "lstatv", "fstatv", "getattrlist", "setattrlist", "getdirentriesattr", "exchangedata", "224 old checkuseraccess / fsgetpath ( which moved to 427 )", "searchfs", "delete", "copyfile", "fgetattrlist", "fsetattrlist", "poll", "watchevent", "waitevent", "modwatch", "getxattr", "fgetxattr", "setxattr", "fsetxattr", "removexattr", "fremovexattr", "listxattr", "flistxattr", "fsctl", "initgroups", "posix_spawn", "ffsctl", "246", "nfsclnt", "fhopen", "249", "minherit", "semsys", "msgsys", "shmsys", "semctl", "semget", "semop", "257", "msgctl", "msgget", "msgsnd", "msgrcv", "shmat", "shmctl", "shmdt", "shmget", "shm_open", "shm_unlink", "sem_open", "sem_close", "sem_unlink", "sem_wait", "sem_trywait", "sem_post", "sem_getvalue", "sem_init", "sem_destroy", "open_extended", "umask_extended", "stat_extended", "lstat_extended", "fstat_extended", "chmod_extended", "fchmod_extended", "access_extended", "settid", "gettid", "setsgroups", "getsgroups", "setwgroups", "getwgroups", "mkfifo_extended", "mkdir_extended", "identitysvc", "shared_region_check_np", "shared_region_map_np", "vm_pressure_monitor", "psynch_rw_longrdlock", "psynch_rw_yieldwrlock", "psynch_rw_downgrade", "psynch_rw_upgrade", "psynch_mutexwait", "psynch_mutexdrop", "psynch_cvbroad", "psynch_cvsignal", "psynch_cvwait", "psynch_rw_rdlock", "psynch_rw_wrlock", "psynch_rw_unlock", "psynch_rw_unlock2", "getsid", "settid_with_pid", "psynch_cvclrprepost", "aio_fsync", "aio_return", "aio_suspend", "aio_cancel", "aio_error", "aio_read", "aio_write", "lio_listio", "321 old __pthread_cond_wait", "iopolicysys", "process_policy", "mlockall", "munlockall", "326", "issetugid", "__pthread_kill", "__pthread_sigmask", "__sigwait", "__disable_threadsignal", "__pthread_markcancel", "__pthread_canceled", "__semwait_signal", "335 old utrace", "proc_info", "sendfile", "stat64", "fstat64", "lstat64", "stat64_extended", "lstat64_extended", "fstat64_extended", "getdirentries64", "statfs64", "fstatfs64", "getfsstat64", "__pthread_chdir", "__pthread_fchdir", "audit", "auditon", "352", "getauid", "setauid", "getaudit", "setaudit", "getaudit_addr", "setaudit_addr", "auditctl", "bsdthread_create", "bsdthread_terminate", "kqueue", "kevent", "lchown", "stack_snapshot", "bsdthread_register", "workq_open", "workq_kernreturn", "kevent64", "__old_semwait_signal", "__old_semwait_signal_nocancel", "thread_selfid", "ledger", "374", "375", "376", "377", "378", "379", "__mac_execve", "__mac_syscall", "__mac_get_file", "__mac_set_file", "__mac_get_link", "__mac_set_link", "__mac_get_proc", "__mac_set_proc", "__mac_get_fd", "__mac_set_fd", "__mac_get_pid", "__mac_get_lcid", "__mac_get_lctx", "__mac_set_lctx", "setlcid", "getlcid", "read_nocancel", "write_nocancel", "open_nocancel", "close_nocancel", "wait4_nocancel", "recvmsg_nocancel", "sendmsg_nocancel", "recvfrom_nocancel", "accept_nocancel", "msync_nocancel", "fcntl_nocancel", "select_nocancel", "fsync_nocancel", "connect_nocancel", "sigsuspend_nocancel", "readv_nocancel", "writev_nocancel", "sendto_nocancel", "pread_nocancel", "pwrite_nocancel", "waitid_nocancel", "poll_nocancel", "msgsnd_nocancel", "msgrcv_nocancel", "sem_wait_nocancel", "aio_suspend_nocancel", "__sigwait_nocancel", "__semwait_signal_nocancel", "__mac_mount", "__mac_get_mount", "__mac_getfsstat", "fsgetpath", "audit_session_self", "audit_session_join", "fileport_makeport", "fileport_makefd", "audit_session_port","pid_suspend", "pid_resume", "pid_hibernate", "pid_shutdown_sockets", "437 old shared_region_slide_np", "shared_region_map_and_slide_np" , "kas_info", "memorystatus_control", "guarded_open_np","guarded_close_np", "guarded_kqueue_np", "change_fdguard_np", "old __proc_suppress", "proc_rlimit_control", "proc_connectx", "proc_disconnectx", "proc_peeloff", "proc_socket_delegate", "proc_telemetry", "proc_uuid_policy", // 452 "memorystatus_get_level", // 453 NULL }; // That MOV PC,R9 always gives it away , now.. const char *ARMExcVector = "\x09\xf0\xa0\xe1\xfe\xff\xff\xea"; const char * mach_syscall_name_table[128] = { /* 0 */ "kern_invalid", /* 1 */ "kern_invalid", /* 2 */ "kern_invalid", /* 3 */ "kern_invalid", /* 4 */ "kern_invalid", /* 5 */ "kern_invalid", /* 6 */ "kern_invalid", /* 7 */ "kern_invalid", /* 8 */ "kern_invalid", /* 9 */ "kern_invalid", /* 10 */ "_kernelrpc_mach_vm_allocate_trap", // OS X : "kern_invalid", /* 11 */ "_kernelrpc_vm_allocate_trap", // OS X : "kern_invalid", /* 12 */ "_kernelrpc_mach_vm_deallocate_trap", // OS X: "kern_invalid", /* 13 */ "_kernelrpc_vm_deallocate_trap" , // "kern_invalid", /* 14 */ "_kernelrpc_mach_vm_protect_trap", //"kern_invalid", /* 15 */ "_kernelrpc_vm_protect_trap", // kern_invalid", /* 16 */ "_kernelrpc_mach_port_allocate_trap", //"kern_invalid", /* 17 */ "_kernelrpc_mach_port_destroy_trap" ,//"kern_invalid", /* 18 */ "_kernelrpc_mach_port_deallocate_trap", // "kern_invalid", /* 19 */ "_kernelrpc_mach_port_mod_refs_trap", //"kern_invalid", /* 20 */ "_kernelrpc_mach_port_move_member_trap", //"kern_invalid", /* 21 */ "_kernelrpc_mach_port_insert_right_trap", //"kern_invalid", /* 22 */ "_kernelrpc_mach_port_insert_member_trap", // "kern_invalid", /* 23 */ "_kernelrpc_mach_port_extract_member_trap", // "kern_invalid", /* 24 */ "kern_invalid", /* 25 */ "kern_invalid", /* 26 */ "mach_reply_port", /* 27 */ "thread_self_trap", /* 28 */ "task_self_trap", /* 29 */ "host_self_trap", /* 30 */ "kern_invalid", /* 31 */ "mach_msg_trap", /* 32 */ "mach_msg_overwrite_trap", /* 33 */ "semaphore_signal_trap", /* 34 */ "semaphore_signal_all_trap", /* 35 */ "semaphore_signal_thread_trap", /* 36 */ "semaphore_wait_trap", /* 37 */ "semaphore_wait_signal_trap", /* 38 */ "semaphore_timedwait_trap", /* 39 */ "semaphore_timedwait_signal_trap", /* 40 */ "kern_invalid", /* 41 */ "kern_invalid", /* 42 */ "kern_invalid", /* 43 */ "map_fd", /* 44 */ "task_name_for_pid", /* 45 */ "task_for_pid", /* 46 */ "pid_for_task", /* 47 */ "kern_invalid", /* 48 */ "macx_swapon", /* 49 */ "macx_swapoff", /* 50 */ "kern_invalid", /* 51 */ "macx_triggers", /* 52 */ "macx_backing_store_suspend", /* 53 */ "macx_backing_store_recovery", /* 54 */ "kern_invalid", /* 55 */ "kern_invalid", /* 56 */ "kern_invalid", /* 57 */ "kern_invalid", /* 58 */ "pfz_exit", /* 59 */ "swtch_pri", /* 60 */ "swtch", /* 61 */ "thread_switch", /* 62 */ "clock_sleep_trap", /* 63 */ "kern_invalid", /* traps 64 - 95 reserved (debo) */ /* 64 */ "kern_invalid", /* 65 */ "kern_invalid", /* 66 */ "kern_invalid", /* 67 */ "kern_invalid", /* 68 */ "kern_invalid", /* 69 */ "kern_invalid", /* 70 */ "kern_invalid", /* 71 */ "kern_invalid", /* 72 */ "kern_invalid", /* 73 */ "kern_invalid", /* 74 */ "kern_invalid", /* 75 */ "kern_invalid", /* 76 */ "kern_invalid", /* 77 */ "kern_invalid", /* 78 */ "kern_invalid", /* 79 */ "kern_invalid", /* 80 */ "kern_invalid", /* 81 */ "kern_invalid", /* 82 */ "kern_invalid", /* 83 */ "kern_invalid", /* 84 */ "kern_invalid", /* 85 */ "kern_invalid", /* 86 */ "kern_invalid", /* 87 */ "kern_invalid", /* 88 */ "kern_invalid", /* 89 */ "mach_timebase_info_trap", /* 90 */ "mach_wait_until_trap", /* 91 */ "mk_timer_create_trap", /* 92 */ "mk_timer_destroy_trap", /* 93 */ "mk_timer_arm_trap", /* 94 */ "mk_timer_cancel_trap", /* 95 */ "kern_invalid", /* traps 64 - 95 reserved (debo) */ /* 96 */ "kern_invalid", /* 97 */ "kern_invalid", /* 98 */ "kern_invalid", /* 99 */ "kern_invalid", /* traps 100-107 reserved for iokit (esb) */ /* 100 */ "kern_invalid", /* 100 */ //"iokit_user_client_trap", /* 101 */ "kern_invalid", /* 102 */ "kern_invalid", /* 103 */ "kern_invalid", /* 104 */ "kern_invalid", /* 105 */ "kern_invalid", /* 106 */ "kern_invalid", /* 107 */ "kern_invalid", /* traps 108-127 unused */ /* 108 */ "kern_invalid", /* 109 */ "kern_invalid", /* 110 */ "kern_invalid", /* 111 */ "kern_invalid", /* 112 */ "kern_invalid", /* 113 */ "kern_invalid", /* 114 */ "kern_invalid", /* 115 */ "kern_invalid", /* 116 */ "kern_invalid", /* 117 */ "kern_invalid", /* 118 */ "kern_invalid", /* 119 */ "kern_invalid", /* 120 */ "kern_invalid", /* 121 */ "kern_invalid", /* 122 */ "kern_invalid", /* 123 */ "kern_invalid", /* 124 */ "kern_invalid", /* 125 */ "kern_invalid", /* 126 */ "kern_invalid", /* 127 */ "kern_invalid", }; #define XNUSIG "SourceCache/xnu/xnu-" #define SYS_MAXSYSCALL 443 #define SYS_MAXSYSCALL_7 454 #define SIG1 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" #define SIG1_SUF "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x04\x00\x00\x00" #define SIG2 "\x00\x00\x00\x00" \ "\x00\x00\x00\x00" \ "\x01\x00\x00\x00" \ "\x1C\x00\x00\x00" \ "\x00\x00\x00\x00" #define SIG1_2423_ONWARDS "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x00\x00" "\x00\x00\x00\x00" #define SIG2_2423_ONWARDS "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x01\x00\x04\x00" void dumpMachTraps(char *mach) { if (mach) printf ("Kern invalid should be %p. Ignoring those\n", *((int *) &mach[4])); int i; for (i = 0; i < 128; i++) { int thumb = 0; int addr = * ((int *) (mach + 4 + 8*i)); if (addr == *((int *) (mach + 4))) continue; if ((addr % 4) == 1) { addr--; thumb++; } if ((addr % 4) == -3) { addr--; thumb++; } if (addr % 4) { thumb = "?"; } printf ("%3d %-40s %x %s\n", i, mach_syscall_name_table[i], addr, (thumb? "T": "-")); } // end for < 128 .. } // dumpMachTraps int g_Verbose = 0; char *MachOLookupSymbolAtAddress(uint64_t, unsigned char *File); int doKext (char *mmapped) { return 1; } // doKext void printDictionaryAsXML(CFMutableDictionaryRef dict) { CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFPropertyListRef)dict); if (xml) { write(1, CFDataGetBytePtr(xml), CFDataGetLength(xml)); printf("done\n"); CFRelease(xml); } printf("..\n"); } void doKexts(char *mmapped) { int kexts = 0; // To do the kexts, we load the dictionary of PRELINK_INFO char *kextPrelinkInfo = (char *) malloc(1000000); CFDictionaryRef dict; char *kextNamePtr; char *kextLoadAddr; char kextName[256]; char loadAddr[16]; char *temp = kextPrelinkInfo; char *loadAddrPtr; char *prelinkAddr; extern char *g_SegName; g_SegName = "__PRELINK_INFO"; void *seg = MachOGetSection("__PRELINK_INFO"); kextPrelinkInfo = (char *) (mmapped + MachOGetSegmentOffset(seg)); temp = kextPrelinkInfo; kextNamePtr = strstr(temp,"CFBundleName</key>"); // This is EXTREMELY quick and dirty, but I can't find a way to load a CFDictionary // directly from XML data, so it will do for now.. while (kextNamePtr) { temp = strstr(kextNamePtr, "</string>"); prelinkAddr = strstr(kextNamePtr, "_PrelinkExecutableLoadAddr"); loadAddrPtr = strstr(prelinkAddr, "0x"); // overflow, etc.. memset(kextName, '\0', 256); strncpy (kextName, kextNamePtr + 26, temp - kextNamePtr - 26); // temp = strstr(loadAddrPtr, "</integer>"); strncpy (loadAddr, loadAddrPtr, 10); loadAddr[9]='\0'; printf("%s: %s ", loadAddr, kextName); temp += 10; kextNamePtr = strstr(temp, "CFBundleIdentifier"); if (kextNamePtr) { temp = strstr(kextNamePtr,"</string>"); memset(kextName,'\0',256); strncpy(kextName, kextNamePtr + 32, temp - kextNamePtr - 32); printf ("(%s)\n", kextName); } kextNamePtr = strstr(temp,"CFBundleName</key>"); kexts++; } printf("Got %d kexts. done\n", kexts); } struct sysctl_oid { uint32_t ptr_oid_parent; uint32_t ptr_oid_link; int oid_number; int oid_kind; uint32_t oid_arg1; int oid_arg2; uint32_t ptr_oid_name; uint32_t ptr_oid_handler; uint32_t ptr_oid_fmt; uint32_t ptr_oid_descr; /* offsetof() field / long description */ int oid_version; int oid_refcnt; }; char *sysctlName (char *mmapped, uint32_t sysctlPtr) { char *name = malloc(1024); name[0] = '\0'; uint32_t sysCtlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr); if (sysCtlOffsetInFile == -1) { strcat (name, "?"); return (name); } struct sysctl_oid *sysctl = (mmapped + sysCtlOffsetInFile); char *parent = MachOLookupSymbolAtAddress(sysctl->ptr_oid_parent, mmapped); if (parent) { if (strncmp(parent, "_sysctl__",9) ==0) { strcpy(name,parent+9); int i =0; while (i < strlen(name)) { if (name[i] == '_') name[i] = '.'; i++; if (strncmp(name +i, "children",7) == 0) name[i-1] = '\0'; //will fall out } } else strcpy(name, parent); strcat(name, "."); } else { char parentAddr[16]; sprintf (parentAddr,"0x%x", sysctl->ptr_oid_parent); strcpy(name, parentAddr); strcat(name,"."); } uint32_t sysctlNameOffsetInFile = MachOGetFileOffsetOfAddr (sysctl->ptr_oid_name); if (sysctlNameOffsetInFile == -1) {strcat (name,"?"); return (name);} strcat (name, mmapped + sysctlNameOffsetInFile); return (name); } //sysctlName void doSysctls(char *mmapped) { // assume section 32 for now.. struct section *sec = MachOGetSection ("__DATA.__sysctl_set"); if (sec) { int numsysctls = sec->size /sizeof(uint32_t); int s = 0; printf ("Dumping sysctl_set from 0x%x (offset in file: 0x%x), %x sysctls follow:\n", sec->addr,sec->offset, numsysctls); for (s = 0 ; s < numsysctls; s++) { uint32_t sysctlPtr = *((uint32_t *)(mmapped + sec->offset+ s * sizeof(uint32_t))); uint32_t sysctlOffsetInFile = MachOGetFileOffsetOfAddr (sysctlPtr); printf ("0x%x: ", sysctlPtr , sysctlOffsetInFile); // sanity check, anyone? if (sysctlOffsetInFile > sec->offset + sec->size) { printf("(outside __sysctl_set)\n"); continue;}; struct sysctl_oid *sysctl = (mmapped + sysctlOffsetInFile); uint32_t sysctlDescInFile = MachOGetFileOffsetOfAddr (sysctl->ptr_oid_descr); uint32_t sysctlFormatInFile = MachOGetFileOffsetOfAddr (sysctl->ptr_oid_fmt); char *sysctlFormat = "?"; if (sysctlFormatInFile != -1) { sysctlFormat = mmapped + sysctlFormatInFile;} printf ("%s\tDescription: %s\n\t\tHandler: 0x%x\n\t\tFormat: %s\n\t\tParent: %x\n\t\tArg1: %x\n\t\tArg2: %x\n", sysctlName(mmapped,sysctlPtr), mmapped + sysctlDescInFile, sysctl->ptr_oid_handler, sysctlFormat, sysctl->ptr_oid_parent, sysctl->oid_arg1, sysctl->oid_arg2); } } } // doSysctls int main (int argc, char **argv) { int ios7 = 0; int fd; char *mmapped; int rc; struct stat stbuf; int filesize; char *filename = argv[1]; struct mach_header *mh; int i,j ; int magic; char *sysent = NULL; char *mach = NULL; char *xnuSig = NULL; int showUNIX = 0, showMach = 0; int suppressEnosys = 1; int showVersion = 0; int showKexts = 0; int showSysctls = 0; if (!filename) { fprintf (stderr,"Usage: joker [-ask] _filename_\n", argv[0]); fprintf (stderr," _filename_ should be a decrypted iOS kernelcache. Tested on 3.x-4.x-5.x-7.0\n"); fprintf (stderr," -m: dump UNIX Syscalls and Mach Traps\n"); fprintf (stderr," -a: dump everything\n"); fprintf (stderr," -k: dump kexts\n"); fprintf (stderr," -s: dump sysctls\n"); fprintf (stderr, "Stable version (no symbolification/etc here yet)\n"); exit(0);} if (filename[0] == '-') { showVersion = (filename[1] == 'v' ? 1 : 0 ) ; filename = argv[2]; }; if (strcmp (argv[1], "-k") ==0 ) { showKexts = 1; filename = argv[2]; showUNIX =0; showMach = 0;}; if (strcmp (argv[1], "-s") ==0 ) { showSysctls = 1; filename = argv[2]; showUNIX =0; showMach = 0;}; if (strcmp (argv[1], "-a") ==0 ) { showSysctls = 1; showKexts=1;filename = argv[2]; showUNIX =showMach = 1;}; if (strcmp (argv[1], "-m") ==0 ) { showMach = showUNIX = 1; filename = argv[2];}; rc = stat(filename, &stbuf); if (rc == -1) { perror (filename); exit (1); } filesize = stbuf.st_size; fd = open (filename, O_RDONLY); if (fd < 0) { perror ("open"); exit(2);} mmapped = mmap(NULL, filesize, // size_t len, PROT_READ, // int prot, MAP_SHARED | MAP_FILE, // int flags, fd, // int fd, 0); // off_t offset); if (!mmapped) { perror ("mmap"); exit(3);} processFile(mmapped,filesize, CPU_TYPE_ARM, 0, 0); struct source_version_command *svc = (struct source_version_command *) findLoadCommand (mmapped, LC_SOURCE_VERSION); if (svc) fprintf (stdout, "%-25s%ld.%d.%d.%d.%d\n", "Source Version:", (long) ((svc->version) >> 40), (int) (svc->version >> 30) & 0x000003FF , (int) (svc->version >> 20) & 0x000003FF, (int) (svc->version >> 10) & 0x000003FF, (int) (svc->version) & 0x000003FF); if (svc && (svc->version >> 40) >= 2423) { fprintf(stdout, "This is iOS 7.x, or later\n"); ios7 = 1; } mh = (struct mach_header *) (mmapped); switch (mh->magic) { case 0xFEEDFACE: /* Good, this is a Mach-O */ if (mh->cputype == 12) /* ARM */ { // This is an ARM binary. Good. } break; case 0xbebafeca: fprintf (stderr, "This is an Intel FAT binary, but I can't handle these yet\n"); exit(5); default: fprintf(stderr, "I have no idea how to handle a file with a magic of %p\n", magic); exit(6); } //printf ("Entry point is 0x%llx..", getEntryPoint()); for (i = 0; i < filesize-50; i++) { if (!xnuSig && memcmp(&mmapped[i], XNUSIG, strlen(XNUSIG)) == 0) { /* Could actually get the version from LC_SOURCE_VERSION... */ char buf[80]; xnuSig = mmapped + i + strlen(XNUSIG); memset(buf, '\0', 80); strncpy (buf, xnuSig, 40); // The signature we get is from a panic, with the full path to the // xnu sources. Remove the "/" following the XNU version. Because the // memory is mmap(2)ed read only, we have to copy this first. char *temp = strstr(buf, "/"); if (temp) { *temp = '\0'; } xnuSig = buf; if (showVersion) { printf ("This is XNU %s\n", xnuSig); exit(0); } } if (memcmp(&mmapped[i], ARMExcVector, 8) == 0) { if (showUNIX) printf("ARM Exception Vector is at file offset @0x%x (Addr: 0x%x)\n", i-28, findAddressOfOffset(i-28)); } if (memcmp(&mmapped[i], SIG1, 20) == 0) { if (memcmp(&mmapped[i+24], SIG1_SUF, 16) == 0) { if (showUNIX) printf ("Sysent offset in file (for patching purposes): %p\n",i-8,0x80041000+(i -8)); sysent = mmapped + i - 24 ; // if (xnuSig) break; } } if ( (memcmp(&mmapped[i], SIG1_2423_ONWARDS, 16) == 0) && (memcmp(&mmapped[i+20], SIG2_2423_ONWARDS, 16) ==0) && (memcmp(&mmapped[i+40], SIG1_2423_ONWARDS, 16) ==0)) { if (showUNIX) printf ("Sysent offset in file (for patching purposes): %p\n",i-8,0x80041000+(i -8)); sysent = mmapped + i - 24 ; // if (xnuSig) break; } if (showMach) { if (! mach && (memcmp(&mmapped[i], &mmapped[i+40], 40 ) == 0) && (memcmp(&mmapped[i], &mmapped[i+32], 32 ) == 0) && (memcmp(&mmapped[i], &mmapped[i+24], 24 ) == 0) && (memcmp(&mmapped[i], &mmapped[i+16], 16) == 0) && (memcmp(&mmapped[i], &mmapped[i+24], 24) == 0) && (memcmp(&mmapped[i], &mmapped[i+8], 8 ) == 0) && ( (!*((int *) &mmapped[i])) && *((int *) &mmapped[i+4])) ) { printf ("mach_trap_table offset in file/memory (for patching purposes): 0x%x/%p\n", i,findAddressOfOffset(i)); mach = &mmapped[i]; dumpMachTraps (mach); } } // end showMach } // end for i.. if (!xnuSig) { fprintf (stderr, "This doesn't seem to be a kernel!\n"); exit (7);} if (showUNIX && sysent) { if (memcmp(&mmapped[i], "syscall\0exit", 12) == 0) { // syscall_names = &mmapped[i]; printf ("Syscall names are @%x\n", i); } if (suppressEnosys) { int enosys = * ((int *) (sysent + 20 + 24*4)); printf ("Suppressing enosys (%p)\n", enosys); } for (i = 0; i< (ios7 ? SYS_MAXSYSCALL_7 : SYS_MAXSYSCALL); i++) { int suppress = 0; int thumb = 0; int jump = (ios7? 20 : 24); int addr = * ((int *) (sysent + 20 + jump*i)); if (addr == *((int *)(sysent + 20 + jump * 8))) suppress =1; if ((addr % 4) == 1) { addr--; thumb++; } if ((addr % 4) == -3) { addr--; thumb++; } if (!suppress) printf ("%d. %-20s %x %s\n", i,syscall_names[i], addr, (thumb? "T": "-")); // skip to next post null byte - unfortunately wont work due to optimizations // putting some of the system call name strings elsewhere (in their first appearance // in the binary) // for (; *syscall_names; syscall_names++); // syscall_names++; } } // showUNIX // Do KEXTs void *seg = MachOGetSection("__DATA.__const"); if (!seg) { fprintf(stderr,"Unable to find const section. This shouldn't be happening.. continuting anyway, but can't look for sysent/mach_trap_table\n"); } else { } _kexts: if (showKexts) doKexts(mmapped); _sysctls: if (showSysctls) doSysctls(mmapped); }