├── elfize.sh ├── README.md └── kfinder.py /elfize.sh: -------------------------------------------------------------------------------- 1 | for vmlinuz in `ls $1/vmlinuz-*` 2 | do 3 | echo ":: $vmlinuz" 4 | off=`sudo od -A d -t x1 $vmlinuz | grep '1f 8b 08 00' | cut -d " " -f1` 5 | vmoffset=`echo "$off + 12" | bc` 6 | echo -ne "\t - offset: $vmoffset\n" 7 | output="$1/vmlinux-`basename $vmlinuz`" 8 | dd if=$vmlinuz bs=1 skip=$vmoffset | zcat > $1"/"$output 9 | readelf -p __ksymtab_strings $output 10 | echo 11 | done 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | === USAGE === 2 | 3 | emdel -> time python kfinder.py 4 | [-] Usage: kfinder.py [symbol] 5 | 6 | symbol is not mandatory. In the case it is not provided, `kfinder` runs 7 | in recursive mode and carves out all the possible symbols. 8 | 9 | === HOW === 10 | 11 | This is possible by parsing the `__ksymtab_strings` and `__ksymtab` of 12 | the Linux kernel image (aka vmlinux). `kfinder` analyzes the physical 13 | memory dump, locates the kernel .text segment and from there the 14 | two interesting sections: `__ksymtab_strings` and `__ksymtab`. 15 | 16 | 17 | === EXAMPLES === 18 | 19 | 20 | 08:38:53 emdel -> python kfinder.py /home/emdel/Downloads/fmem_1.6-0/vm-ksfinder-fmem.raw init_task 21 | ... 22 | ... 23 | Page: b8349000 24 | - ei_class: 64bit format 25 | - ABI: System V 26 | - e_type: shared 27 | - e_machine x86_64 28 | Page: b9d77000 29 | - ei_class: 64bit format 30 | - ABI: System V 31 | - e_type: relocatable 32 | - e_machine x86_64 33 | 34 | :: Architecture identified: x86_64 35 | :: init_task found at offset: 0x1b8243f 36 | :: __ksymtab_strings found at offset: 0x1b82436 37 | :: Symbol init_task found at offset: 0x01b82436 38 | :: Symbol Virtual Address: 0xffffffff81b82436 39 | :: Packing the symbol_va 40 | :: __ksymtab offset guess: 0x01a8243c 41 | :: symbol_va packed found at 0x01b5aa40 42 | :: init_task at 0xffffffff81c1d4e0 43 | 44 | 45 | Old example - x86_32 bit only (missing x86_64 support and the check to identify 46 | the architecture): 47 | 48 | emdel -> time python kfinder.py kernelexp.ram init_task 49 | :: __ksymtab_strings found at offset: 0x017e172d 50 | :: Symbol init_task found at offset: 0x017e1724 51 | :: Symbol Virtual Address: 0xc17e1724 52 | :: Packing the symbol_va 53 | :: __ksymtab offset guess: 0x016e172c 54 | :: symbol_va packed found at 0x017d2060 55 | :: init_task at 0xc180b020 56 | 57 | real 0m3.813s 58 | user 0m3.632s 59 | sys 0m0.172s 60 | 61 | 62 | 63 | emdel -> cat System.map-3.2.0-23-generic-pae | grep -w init_task 64 | c180b020 D init_task 65 | 66 | 67 | emdel -> time python kfinder.py kernelexp.ram > carved_sysmap.log 68 | 69 | real 12m18.355s 70 | user 12m17.326s 71 | sys 0m0.372s 72 | 73 | 74 | emdel -> cat System.map-3.2.0-23-generic-pae | wc -l 75 | 69247 76 | 77 | 78 | emdel -> cat carved_sysmap.log | wc -l 79 | 6334 80 | 81 | 82 | emdel -> head carved_sysmap.log 83 | :: __ksymtab_strings found at offset: 0x017e172d 84 | [+] Retrived 6333 symbols 85 | c180b020 ? init_task 86 | c180c460 ? loops_per_jiffy 87 | c193e004 ? reset_devices 88 | c18742c0 ? system_state 89 | c180c5c0 ? init_uts_ns 90 | c1003580 ? populate_rootfs_wait 91 | c17fdf20 ? x86_hyper_xen_hvm 92 | c1003f70 ? xen_hvm_need_lapic 93 | 94 | 95 | === LIMITATIONS === 96 | 97 | It has been tested on very few memory dumps. 98 | I used 'fmem' to dump the memory. 99 | I tried with 'Lime' in the raw format, but 100 | there are some offset issues. Contact me if 101 | you want to discuss about it. 102 | 103 | 104 | Happy hacking, 105 | 106 | 107 | emdel 108 | -------------------------------------------------------------------------------- /kfinder.py: -------------------------------------------------------------------------------- 1 | # 2 | # Simple POC to retrive kernel symbols from a 3 | # physical memory dump. 4 | # 5 | # Mariano Graziano - graziano@eurecom.fr 6 | # 7 | 8 | # 9 | # References: 10 | # - https://github.com/0xAX/linux-insides/blob/master/Booting/linux-bootstrap-4.md 11 | # - Linux kernel source code 12 | # - https://github.com/psviderski/volatility-android/blob/master/volatility/plugins/linux/auto_ksymbol.py 13 | # 14 | # The last link is basically the same idea/approach applied to Android. I discovered it 15 | # few days ago. Really cool work, it's a pity his thesis is not available in 16 | # english (only in russian). 17 | # 18 | # Thanks: 19 | # Andrew Case and Enrico Perla 20 | # 21 | # 22 | # XXD Manual Debugging: 23 | # 24 | # cat System.map-3.2.0-23-generic-pae | grep -i dns_query 25 | # c1574e30 T dns_query 26 | # c17d0fc0 r __ksymtab_dns_query 27 | # c17dc4ec r __kcrctab_dns_query 28 | # c17fde42 r __kstrtab_dns_query 29 | # 30 | # 21:42:06 emdel -> xxd kernelexp.ram | grep -ni dns_query -A 1 -B 1 31 | # 1450737-1622f00: 6174 6500 646e 735f 7265 736f 6c76 6572 ate.dns_resolver 32 | # 1450738:1622f10: 2e64 6562 7567 0064 6e73 5f71 7565 7279 .debug.dns_query 33 | # 1450739-1622f20: 0000 0000 0000 00c1 0000 00c1 db00 00c1 ................ 34 | #-- 35 | # 1572324-17fde30: 5f6e 6574 5f73 7973 6374 6c5f 7461 626c _net_sysctl_tabl 36 | # 1572325:17fde40: 6500 646e 735f 7175 6572 7900 6b6c 6973 e.dns_query.klis 37 | # 1572326-17fde50: 745f 6e65 7874 006b 6c69 7374 5f69 7465 t_next.klist_ite 38 | # 39 | # c17fde42 --> 42de7fc1 40 | # 41 | # 21:44:25 emdel -> xxd kernelexp.ram | grep -ni "42de 7fc1" 42 | # 1464144:16574f0: f0dd 7fc1 0cde 7fc1 28de 7fc1 42de 7fc1 ........(...B... 43 | # 1560829:17d0fc0: [[304e 57c1]] 42de 7fc1 30e4 34c1 621c 7fc1 0NW.B...0.4.b... 44 | # 45 | 46 | 47 | import mmap, sys, struct, os, string 48 | from collections import OrderedDict 49 | 50 | 51 | PAGE_SIZE = 4096 52 | ELF_MAGIC = 0x7F454C46 53 | x64 = 0 54 | x86 = 0 55 | is_x86 = False 56 | is_x64 = False 57 | THRESHOLD = 20 58 | 59 | ei_class_table = { 60 | 0x01: "32bit format", 61 | 0x02: "64bit format" 62 | } 63 | 64 | ei_osabi_table = { 65 | 0x00: "System V", 66 | 0x01: "HP-UX", 67 | 0x02: "NetBSD", 68 | 0x03: "Linix", 69 | 0x06: "Solaris", 70 | 0x07: "AIX", 71 | 0x08: "IRIX", 72 | 0x09: "FreeBSD", 73 | 0x0C: "OpenBSD", 74 | 0x0D: "OpenVMS", 75 | 0x0E: "NonStop Kernel", 76 | 0x0F: "AROS", 77 | 0x10: "Fenix OS", 78 | 0x11: "CloudABI", 79 | 0x53: "Sortix" 80 | } 81 | 82 | e_type_table = { 83 | 0x01: "relocatable", 84 | 0x02: "executable", 85 | 0x03: "shared", 86 | 0x04: "core" 87 | } 88 | 89 | e_machine_table = { 90 | 0x00: "No specific instruction set", 91 | 0x02: "SPARC", 92 | 0x03: "x86", 93 | 0x08: "MIPS", 94 | 0x14: "PowerPC", 95 | 0x28: "ARM", 96 | 0x2A: "SuperH", 97 | 0x32: "IA-64", 98 | 0x3E: "x86_64", 99 | 0xB7: "AArch64" 100 | } 101 | 102 | def lookup(m, ksymtab_pa, symbol_addr_raw): 103 | for addr in xrange(ksymtab_pa, 0x3000000, 0x04): 104 | m.seek(addr) 105 | if is_x86: 106 | raw = m.read(4) 107 | elif is_x64: 108 | raw = m.read(8) 109 | if raw == symbol_addr_raw: 110 | if is_x86: 111 | symbol_offset = (addr - 4) 112 | elif is_x64: 113 | symbol_offset = (addr - 8) 114 | if len(sys.argv) > 2: 115 | print ":: symbol_va packed found at 0x%08x" % symbol_offset 116 | m.seek(symbol_offset) 117 | if is_x86: 118 | addr_raw = m.read(4) 119 | symbol_addr = struct.unpack("", "[symbol]") 150 | sys.exit(1) 151 | 152 | # Open the memory dump 153 | try: 154 | fd = os.open(sys.argv[1], os.O_RDONLY) 155 | except: 156 | print "[-] Error: fopen.\n" 157 | sys.exit(1) 158 | 159 | # mmap the dump 160 | try: 161 | m = mmap.mmap(fd, 0, mmap.MAP_PRIVATE, mmap.PROT_READ) 162 | except: 163 | print "[-] Error: memmap.\n" 164 | sys.exit(1) 165 | 166 | # Scan for ELF files and get the architecture 167 | print "Size: " , m.size() 168 | for page in range(0, m.size(), PAGE_SIZE): 169 | m.seek(page) 170 | raw = m.read(4) 171 | value = struct.unpack(">L", raw)[0] 172 | if value == ELF_MAGIC: 173 | print "Page: %x" % page 174 | raw = m.read(1) 175 | ei_class = struct.unpack(" THRESHOLD and x86 > THRESHOLD: 207 | print "Something went wrong." 208 | os.close(fd) 209 | m.close() 210 | sys.exit(1) 211 | elif x64 > THRESHOLD: 212 | print "\n:: Architecture identified: x86_64" 213 | is_x64 = True 214 | elif x86 > THRESHOLD: 215 | print "\n:: Architecture identified: x86" 216 | is_x86 = True 217 | 218 | if is_x86: 219 | ksymtab_strings = find_ksymtab_strings(m, 0x01000000, 0x02000000) 220 | elif is_x64: 221 | ksymtab_strings = find_ksymtab_strings(m, 0x100000, 0x03000000) 222 | 223 | if not ksymtab_strings: 224 | print ":: __ksymtab_strings not found..." 225 | os.close(fd) 226 | m.close() 227 | sys.exit(1) 228 | 229 | symbol_names = OrderedDict() 230 | 231 | # TODO: Make it modular 232 | candidate_value = "" 233 | prev_addr = 0 234 | prev_val = "" 235 | for addr in range(ksymtab_strings - len("init_task"), 0x03000000, 0x01): 236 | m.seek(addr) 237 | raw = m.read(1) 238 | if raw != "\x00": 239 | candidate_value += raw 240 | continue 241 | # Recursive mode. Build necessary data structures 242 | kaddr = addr - (len(candidate_value) + 1) 243 | if len(sys.argv) == 2: 244 | final_check = string.ascii_letters + "_" + string.digits 245 | if all(c in final_check for c in candidate_value) and len(candidate_value) >= 2: 246 | if len(symbol_names.keys()) == 0: 247 | symbol_names[hex(kaddr).strip("L")] = candidate_value 248 | else: 249 | if prev_addr in symbol_names.keys(): 250 | if hex(addr - (len(candidate_value) + 1) - (len(symbol_names[prev_addr]) + 1)).strip("L") in symbol_names.keys(): 251 | symbol_names[hex(kaddr).strip("L")] = candidate_value 252 | # Single lookup mode 253 | elif sys.argv[2] == candidate_value: 254 | symbol_pa = addr - len(sys.argv[2]) 255 | print ":: Symbol %s found at offset: 0x%08x" % (sys.argv[2], symbol_pa) 256 | #print len(candidate_value), len(sys.argv[2]) 257 | if is_x86: 258 | symbol_va = symbol_pa + 0xC0000000 259 | elif is_x64: 260 | symbol_va = symbol_pa + 0xFFFFFFFF80000000 261 | print ":: Symbol Virtual Address: 0x%08x" % symbol_va 262 | break 263 | prev_addr = hex(kaddr).strip("L") 264 | prev_val = candidate_value 265 | candidate_value = "" 266 | 267 | # Single lookup mode 268 | if len(sys.argv) > 2: 269 | print ":: Packing the symbol_va" 270 | if is_x86: 271 | symbol_addr_raw = struct.pack("> 2 << 2 275 | print ":: __ksymtab offset guess: 0x%08x" % ksymtab_pa 276 | symbol = lookup(m, ksymtab_pa, symbol_addr_raw) 277 | print ":: %s at 0x%08x" % (sys.argv[2], symbol) 278 | else: 279 | system_map = OrderedDict() 280 | print "[+] Retrived %d symbols" % len(symbol_names.keys()) 281 | print symbol_names 282 | ksymtab_pa = (ksymtab_strings - 0x100000) >> 2 << 2 283 | for k, v in symbol_names.items(): 284 | if is_x86: 285 | symbol_va = (int(k, 16) + 1) + 0xC0000000 286 | symbol_addr_raw = struct.pack("