├── README.md ├── kernelSymbolFinder.c ├── kernelSymbolFinder.h ├── lzssdec.cpp └── lzssdec.hpp /README.md: -------------------------------------------------------------------------------- 1 | # kernelSymbolFinder 2 | Get kernel symbols on device. No jailbreak required (note: unslid addresses) 3 | 4 | # Setup 5 | 6 | - Include the files, call initWithKernelCache() with a copy of the compressed kernelcache **that is stored on a location you have permission to write** 7 | - Call find_symbol() as you wish 8 | - You'll be left with a .dec file on the same location which you might want to delete later 9 | 10 | ------------------ 11 | 12 | Thanks to Willem Hengeveld for lzssdec and MachOView as an awesome tool! 13 | -------------------------------------------------------------------------------- /kernelSymbolFinder.c: -------------------------------------------------------------------------------- 1 | // 2 | // kernelSymbolFinder.c 3 | // KernelSymbolFinder 4 | // 5 | // Created by Jake James on 8/21/18. 6 | // Copyright © 2018 Jake James. All rights reserved. 7 | // 8 | 9 | #include "kernelSymbolFinder.h" 10 | 11 | #define SWAP32(p) __builtin_bswap32(p) 12 | 13 | static FILE *file; 14 | uint32_t offset = 0; 15 | 16 | static void *load_bytes(FILE *obj_file, off_t offset, uint32_t size) { 17 | void *buf = calloc(1, size); 18 | fseek(obj_file, offset, SEEK_SET); 19 | fread(buf, size, 1, obj_file); 20 | return buf; 21 | } 22 | 23 | uint32_t find_macho_header() { 24 | uint32_t off = 0; 25 | uint32_t *magic = load_bytes(file, off, sizeof(uint32_t)); 26 | while ((*magic & ~1) != 0xFEEDFACE) { 27 | off++; 28 | magic = load_bytes(file, off, sizeof(uint32_t)); 29 | } 30 | return off - 1; 31 | } 32 | 33 | uint64_t find_symbol(const char *symbol, bool verbose) { 34 | 35 | //----This will store symbol address----// 36 | uint64_t addr = 0; 37 | 38 | //----This variable will hold the binary location as we move on through reading it----// 39 | size_t offset = 0; 40 | size_t sym_offset = 0; 41 | int ncmds = 0; 42 | struct load_command *cmd = NULL; 43 | uint32_t *magic = load_bytes(file, offset, sizeof(uint32_t)); //at offset 0 we have the magic number 44 | if (verbose) printf("[i] MAGIC = 0x%x\n", *magic); 45 | 46 | //----64bit magic number----// 47 | if (*magic == 0xFEEDFACF) { 48 | 49 | if (verbose) printf("[i] 64bit binary\n"); 50 | 51 | struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64)); 52 | ncmds = mh64->ncmds; 53 | free(mh64); 54 | 55 | offset += sizeof(struct mach_header_64); 56 | 57 | if (verbose) printf("[i] %d LOAD COMMANDS\n", ncmds); 58 | for (int i = 0; i < ncmds; i++) { 59 | cmd = load_bytes(file, offset, sizeof(struct load_command)); 60 | if (verbose) printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd); 61 | 62 | if (cmd->cmd == LC_SYMTAB) { 63 | if (verbose) printf("[+] Found LC_SYMTAB command!\n"); 64 | struct symtab_command *symtab = load_bytes(file, offset, cmd->cmdsize); 65 | if (verbose) printf("\t[i] %d symbols\n", symtab->nsyms); 66 | if (verbose) printf("\t[i] Symbol table at 0x%x\n", symtab->symoff); 67 | 68 | for (int i = 0; i < symtab->nsyms; i++) { 69 | struct symbol *sym = load_bytes(file, symtab->symoff + sym_offset, sizeof(struct symbol)); 70 | 71 | int symlen = 0; 72 | int sym_str_addr = sym->table_index + symtab->stroff; 73 | uint8_t *byte = load_bytes(file, sym_str_addr+symlen, 1); 74 | 75 | //strings end with 0 so that's how we know it's over 76 | while (*byte != 0) { 77 | free(byte); 78 | symlen++; 79 | byte = load_bytes(file, sym_str_addr+symlen, 1); 80 | } 81 | free(byte); 82 | 83 | char *sym_name = load_bytes(file, sym_str_addr, symlen + 1); 84 | if (verbose) printf("\t%s: 0x%llx\n", sym_name, sym->address); 85 | if (!strcmp(sym_name, symbol)) { 86 | addr = sym->address; 87 | if (!verbose) return addr; 88 | } 89 | free(sym_name); 90 | sym_offset += sizeof(struct symbol); 91 | free(sym); 92 | } 93 | 94 | free(symtab); 95 | free(cmd); 96 | break; 97 | } 98 | 99 | offset += cmd->cmdsize; 100 | free(cmd); 101 | } 102 | } 103 | //----32bit magic number----// 104 | else if (*magic == 0xFEEDFACE) { 105 | 106 | if (verbose) printf("[i] 32bit binary\n"); 107 | 108 | struct mach_header *mh = load_bytes(file, offset, sizeof(struct mach_header)); 109 | ncmds = mh->ncmds; 110 | free(mh); 111 | 112 | offset += sizeof(struct mach_header); 113 | 114 | if (verbose) printf("[i] %d LOAD COMMANDS\n", ncmds); 115 | for (int i = 0; i < ncmds; i++) { 116 | cmd = load_bytes(file, offset, sizeof(struct load_command)); 117 | if (verbose) printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd); 118 | offset += cmd->cmdsize; 119 | 120 | if (cmd->cmd == LC_SYMTAB) { 121 | if (verbose) printf("[+] Found LC_SYMTAB command!\n"); 122 | struct symtab_command *symtab = load_bytes(file, offset, cmd->cmdsize); 123 | if (verbose) printf("\t[i] %d symbols\n", symtab->nsyms); 124 | if (verbose) printf("\t[i] Symbol table at 0x%x\n", symtab->symoff); 125 | 126 | for (int i = 0; i < symtab->nsyms; i++) { 127 | struct symbol *sym = load_bytes(file, symtab->symoff + sym_offset, sizeof(struct symbol)); 128 | 129 | int symlen = 0; 130 | int sym_str_addr = sym->table_index + symtab->stroff; 131 | uint8_t *byte = load_bytes(file, sym_str_addr+symlen, 1); 132 | 133 | while (*byte != 0) { 134 | free(byte); 135 | symlen++; 136 | byte = load_bytes(file, sym_str_addr+symlen, 1); 137 | } 138 | free(byte); 139 | 140 | char *sym_name = load_bytes(file, sym_str_addr, symlen + 1); 141 | if (verbose) printf("\t%s: 0x%llx\n", sym_name, sym->address); 142 | if (!strcmp(sym_name, symbol)) { 143 | addr = sym->address; 144 | if (!verbose) return addr; 145 | } 146 | free(sym_name); 147 | sym_offset += sizeof(struct symbol); 148 | free(sym); 149 | } 150 | 151 | free(symtab); 152 | free(cmd); 153 | break; 154 | } 155 | offset += cmd->cmdsize; 156 | free(cmd); 157 | } 158 | } 159 | else { 160 | if (verbose) printf("[!] Unrecognized file\n"); 161 | return -1; 162 | } 163 | return addr; 164 | } 165 | 166 | int initWithKernelCache(const char *kernelcache) { 167 | file = fopen(kernelcache, "rb"); 168 | offset = find_macho_header(); 169 | if (!offset) { 170 | printf("[-] offset = 0; this isn't a macho, right?\n"); 171 | return -1; 172 | } 173 | 174 | printf("[i] Mach-o header at 0x%X\n", offset); 175 | 176 | char strOff[128]; // I don't think a string of the offset can ever be bigger than 128 bytes 177 | sprintf(strOff, "0x%X", offset); 178 | char *args[5] = { strdup("lzssdec"), strdup("-o"), strdup(strOff), strdup(kernelcache), strcat(strdup(kernelcache), ".dec")}; 179 | 180 | if (lzssdec(5, (char **)args)) { 181 | printf("[-] Failed decompression, this is lzss right?\n"); 182 | return -1; 183 | } 184 | else printf("[+] Decompressed kernelcache!\n"); 185 | fclose(file); 186 | file = fopen(strcat(strdup(kernelcache), ".dec"), "rb"); 187 | return 0; 188 | } 189 | -------------------------------------------------------------------------------- /kernelSymbolFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // kernelSymbolFinder.h 3 | // KernelSymbolFinder 4 | // 5 | // Created by Jake James on 8/21/18. 6 | // Copyright © 2018 Jake James. All rights reserved. 7 | // 8 | 9 | #import "lzssdec.hpp" 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #import 18 | #import 19 | 20 | 21 | // dunno if the built-in headers have something like this but I couldn't find any so DIY :) 22 | struct symbol { 23 | uint32_t table_index; 24 | uint8_t type; 25 | uint8_t section_index; 26 | uint16_t description; 27 | uint64_t address; 28 | }; 29 | 30 | uint32_t find_macho_header(void); 31 | uint64_t find_symbol(const char *symbol, bool verbose); 32 | int initWithKernelCache(const char *kernelcache); 33 | -------------------------------------------------------------------------------- /lzssdec.cpp: -------------------------------------------------------------------------------- 1 | // (C)2009 Willem Hengeveld itsme@xs4all.nl 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // streaming version of the lzss algorithm, as defined in BootX-75/bootx.tproj/sl.subproj/lzss.c 10 | // you can use lzssdec in a filter, like: 11 | // 12 | // cat file.lzss | lzssdec > file.decompressed 13 | // 14 | static int g_debug= 0; 15 | 16 | class lzssdecompress 17 | { 18 | enum { COPYFROMDICT, EXPECTINGFLAG, PROCESSFLAGBIT, EXPECTING2NDBYTE }; 19 | int _state; 20 | uint8_t _flags; 21 | int _bitnr; 22 | uint8_t *_src, *_srcend; 23 | uint8_t *_dst, *_dstend; 24 | uint8_t _firstbyte; 25 | 26 | uint8_t *_dict; 27 | 28 | int _dictsize; 29 | int _maxmatch; 30 | int _copythreshold; 31 | 32 | int _dictptr; 33 | 34 | int _copyptr; 35 | int _copycount; 36 | 37 | int _inputoffset; 38 | int _outputoffset; 39 | public: 40 | lzssdecompress() 41 | { 42 | _maxmatch= 18; // 4 bit size + threshold 43 | _dictsize= 4096; // 12 bit size 44 | _copythreshold= 3; // 0 == copy 3 bytes 45 | _dict= new uint8_t[_dictsize+_maxmatch-1]; 46 | 47 | reset(); 48 | } 49 | ~lzssdecompress() 50 | { 51 | delete[] _dict; 52 | _dict= 0; _dictsize= 0; 53 | } 54 | void reset() 55 | { 56 | _state=EXPECTINGFLAG; 57 | _flags= 0; _bitnr= 0; 58 | _src=_srcend=_dst=_dstend=0; 59 | memset(_dict, ' ', _dictsize+_maxmatch-1); 60 | _dictptr= _dictsize-_maxmatch; 61 | _inputoffset= 0; 62 | _outputoffset= 0; 63 | _firstbyte= 0; 64 | _copyptr= 0; 65 | _copycount= 0; 66 | } 67 | void decompress(uint8_t *dst, uint32_t dstlen, uint32_t *pdstused, uint8_t *src, uint32_t srclen, uint32_t *psrcused) 68 | { 69 | _src= src; _srcend= src+srclen; 70 | _dst= dst; _dstend= dst+dstlen; 71 | 72 | while (_src<_srcend && _dst<_dstend) 73 | { 74 | switch(_state) 75 | { 76 | case EXPECTINGFLAG: 77 | if (g_debug) fprintf(stderr, "%08x,%08x: flag: %02x\n", _inputoffset, _outputoffset, *_src); 78 | _flags= *_src++; 79 | _inputoffset++; 80 | _bitnr= 0; 81 | _state= PROCESSFLAGBIT; 82 | break; 83 | case PROCESSFLAGBIT: 84 | if (_flags&1) { 85 | if (g_debug) fprintf(stderr, "%08x,%08x: bit%d: %03x copybyte %02x\n", _inputoffset, _outputoffset, _bitnr, _dictptr, *_src); 86 | addtodict(*_dst++ = *_src++); 87 | _inputoffset++; 88 | _outputoffset++; 89 | nextflagbit(); 90 | } 91 | else { 92 | _firstbyte= *_src++; 93 | _inputoffset++; 94 | _state= EXPECTING2NDBYTE; 95 | } 96 | break; 97 | case EXPECTING2NDBYTE: 98 | { 99 | uint8_t secondbyte= *_src++; 100 | _inputoffset++; 101 | setcounter(_firstbyte, secondbyte); 102 | if (g_debug) fprintf(stderr, "%08x,%08x: bit%d: %03x %02x %02x : copy %d bytes from %03x", _inputoffset-2, _outputoffset, _bitnr, _dictptr, _firstbyte, secondbyte, _copycount, _copyptr); 103 | if (g_debug) dumpcopydata(); 104 | _state= COPYFROMDICT; 105 | } 106 | break; 107 | case COPYFROMDICT: 108 | copyfromdict(); 109 | break; 110 | } 111 | } 112 | if (g_debug) fprintf(stderr, "decompress state= %d, copy: 0x%x, 0x%x\n", _state, _copyptr, _copycount); 113 | if (pdstused) *pdstused= _dst-dst; 114 | if (psrcused) *psrcused= _src-src; 115 | } 116 | void flush(uint8_t *dst, uint32_t dstlen, uint32_t *pdstused) 117 | { 118 | if (g_debug) fprintf(stderr, "flash before state= %d, copy: 0x%x, 0x%x\n", _state, _copyptr, _copycount); 119 | _src= _srcend= NULL; 120 | _dst= dst; _dstend= dst+dstlen; 121 | 122 | if (_state==COPYFROMDICT) 123 | copyfromdict(); 124 | 125 | if (pdstused) *pdstused= _dst-dst; 126 | if (g_debug) fprintf(stderr, "flash after state= %d, copy: 0x%x, 0x%x\n", _state, _copyptr, _copycount); 127 | } 128 | void copyfromdict() 129 | { 130 | while (_dst<_dstend && _copycount) 131 | { 132 | addtodict(*_dst++ = _dict[_copyptr++]); 133 | _outputoffset++; 134 | _copycount--; 135 | _copyptr= _copyptr&(_dictsize-1); 136 | } 137 | if (_copycount==0) 138 | nextflagbit(); 139 | } 140 | void dumpcopydata() 141 | { 142 | // note: we are printing incorrect data, if _copyptr == _dictptr-1 143 | for (int i=0 ; i<_copycount ; i++) 144 | fprintf(stderr, " %02x", _dict[(_copyptr+i)&(_dictsize-1)]); 145 | fprintf(stderr, "\n"); 146 | } 147 | void addtodict(uint8_t c) 148 | { 149 | _dict[_dictptr++]= c; 150 | _dictptr = _dictptr&(_dictsize-1); 151 | } 152 | void nextflagbit() 153 | { 154 | _bitnr++; 155 | _flags>>=1; 156 | _state = _bitnr==8 ? EXPECTINGFLAG : PROCESSFLAGBIT; 157 | } 158 | void setcounter(uint8_t first, uint8_t second) 159 | { 160 | _copyptr= first | ((second&0xf0)<<4); 161 | _copycount= _copythreshold + (second&0xf); 162 | } 163 | }; 164 | 165 | void usage(int argc,char**argv) 166 | { 167 | char *name = NULL; 168 | name = strrchr(argv[0], '/'); 169 | fprintf(stderr, "Usage: %s [-d] [-o OFFSET] \n",(name ? name + 1: argv[0])); 170 | } 171 | extern "C" int lzssdec(int argc,char**argv) 172 | { 173 | FILE *readFrom = NULL; 174 | FILE *outputDir = NULL; 175 | 176 | // _setmode(fileno(stdin),O_BINARY); 177 | // _setmode(fileno(stdout),O_BINARY); 178 | 179 | #define HANDLEULOPTION(var, type) (argv[i][2] ? var= (type)strtoul(argv[i]+2, 0, 0) : i+1 bytes 222 | while (skipbytes && !feof(readFrom)) { 223 | int nr= fread(ibuf, 1, std::min(skipbytes,(uint32_t)CHUNK), readFrom); 224 | skipbytes -= nr; 225 | } 226 | 227 | while (!feof(readFrom)) 228 | { 229 | size_t nr= fread(ibuf, 1, CHUNK, readFrom); 230 | if (nr==0) { 231 | perror("read"); 232 | return 1; 233 | } 234 | if (nr==0) 235 | break; 236 | 237 | size_t srcp= 0; 238 | while (srcp 0x%x\n", srcused, dstused); 249 | } 250 | } 251 | if (g_debug) fprintf(stderr, "done reading\n"); 252 | uint32_t dstused; 253 | lzss.flush(obuf, CHUNK, &dstused); 254 | size_t nw= fwrite(obuf, 1, dstused, outputDir); 255 | if (nw 13 | 14 | int lzssdec(int argc,char**argv); 15 | 16 | #endif /* lzssdec_hpp */ 17 | --------------------------------------------------------------------------------