├── Makefile ├── README └── ksymhunter.c /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | LD = gcc 4 | RM = rm -f 5 | CFLAGS = -g -Wall 6 | LDFLAGS = 7 | 8 | PROG = ksymhunter 9 | OBJS = ksymhunter.o 10 | 11 | all: $(PROG) 12 | 13 | $(PROG): $(OBJS) 14 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) 15 | 16 | %.o: %.c 17 | $(CC) $(CFLAGS) -c $< 18 | 19 | clean: 20 | $(RM) $(PROG) $(OBJS) 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ksymhunter.c 2 | 3 | Jon Oberheide 4 | http://jon.oberheide.org 5 | 6 | Routines for hunting down kernel symbols from from kallsyms, 7 | System.map, vmlinux, vmlinuz, and remote symbol servers. 8 | 9 | Example: 10 | 11 | $ ./ksymhunter prepare_kernel_cred 12 | [+] trying to resolve prepare_kernel_cred... 13 | [+] resolved prepare_kernel_cred using /boot/System.map-2.6.38-gentoo 14 | [+] resolved prepare_kernel_cred to 0xffffffff81061060 15 | 16 | $ ./ksymhunter commit_creds 17 | [+] trying to resolve commit_creds... 18 | [+] resolved commit_creds using /boot/System.map-2.6.38-gentoo 19 | [+] resolved commit_creds to 0xffffffff81060dc0 20 | -------------------------------------------------------------------------------- /ksymhunter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ksymhunter.c 3 | * 4 | * Jon Oberheide 5 | * http://jon.oberheide.org 6 | * 7 | * Routines for hunting down kernel symbols from from kallsyms, 8 | * System.map, vmlinux, vmlinuz, and remote symbol servers. 9 | * 10 | * System.map parsing adapted from spender's enlightenment. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | unsigned long try_sysmap(char *name, char *path); 32 | unsigned long try_vmlinux(char *name, char *path); 33 | unsigned long try_vmlinuz(char *name, char *path); 34 | unsigned long try_remote(char *name, char *path); 35 | 36 | #define SOURCE(FP, FMT, ARGS) { .fp = FP, .fmt = FMT, .args = ARGS } 37 | 38 | #define SYSMAP(FMT, ARGS) SOURCE(try_sysmap, FMT, ARGS) 39 | #define SYSMAP_0(FMT) SYSMAP(FMT, 0) 40 | #define SYSMAP_1(FMT) SYSMAP(FMT, 1) 41 | #define SYSMAP_2(FMT) SYSMAP(FMT, 2) 42 | 43 | #define VMLINUX(FMT, ARGS) SOURCE(try_vmlinux, FMT, ARGS) 44 | #define VMLINUX_0(FMT) VMLINUX(FMT, 0) 45 | #define VMLINUX_1(FMT) VMLINUX(FMT, 1) 46 | #define VMLINUX_2(FMT) VMLINUX(FMT, 2) 47 | 48 | #define VMLINUZ(FMT, ARGS) SOURCE(try_vmlinuz, FMT, ARGS) 49 | #define VMLINUZ_0(FMT) VMLINUZ(FMT, 0) 50 | #define VMLINUZ_1(FMT) VMLINUZ(FMT, 1) 51 | #define VMLINUZ_2(FMT) VMLINUZ(FMT, 2) 52 | 53 | #define REMOTE(FMT, ARGS) SOURCE(try_remote, FMT, ARGS) 54 | #define REMOTE_0(FMT) REMOTE(FMT, 0) 55 | 56 | #define REMOTE_HOST "kernelvulns.org" 57 | #define REMOTE_PORT "80" 58 | 59 | struct source { 60 | int args; 61 | char *fmt; 62 | unsigned long (*fp) (char *, char *); 63 | }; 64 | 65 | struct source sources[] = { 66 | SYSMAP_0("/proc/kallsyms"), 67 | SYSMAP_0("/proc/ksyms"), 68 | SYSMAP_1("/boot/System.map-%s"), 69 | SYSMAP_2("/boot/System.map-genkernel-%s-%s"), 70 | SYSMAP_1("/System.map-%s"), 71 | SYSMAP_2("/System.map-genkernel-%s-%s"), 72 | SYSMAP_1("/usr/src/linux-%s/System.map"), 73 | SYSMAP_1("/lib/modules/%s/System.map"), 74 | SYSMAP_0("/boot/System.map"), 75 | SYSMAP_0("/System.map"), 76 | SYSMAP_0("/usr/src/linux/System.map"), 77 | VMLINUX_1("/boot/vmlinux-%s"), 78 | VMLINUX_1("/boot/vmlinux-%s.debug"), 79 | VMLINUX_1("/boot/.debug/vmlinux-%s"), 80 | VMLINUX_1("/boot/.debug/vmlinux-%s.debug"), 81 | VMLINUX_1("/lib/modules/%s/vmlinux"), 82 | VMLINUX_1("/lib/modules/%s/vmlinux.debug"), 83 | VMLINUX_1("/lib/modules/%s/.debug/vmlinux"), 84 | VMLINUX_1("/lib/modules/%s/.debug/vmlinux.debug"), 85 | VMLINUX_1("/usr/lib/debug/lib/modules/%s/vmlinux"), 86 | VMLINUX_1("/usr/lib/debug/lib/modules/%s/vmlinux.debug"), 87 | VMLINUX_1("/usr/lib/debug/boot/vmlinux-%s"), 88 | VMLINUX_1("/usr/lib/debug/boot/vmlinux-%s.debug"), 89 | VMLINUX_1("/usr/lib/debug/vmlinux-%s"), 90 | VMLINUX_1("/usr/lib/debug/vmlinux-%s.debug"), 91 | VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/lib/modules/%s/vmlinux"), 92 | VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/lib/modules/%s/vmlinux.debug"), 93 | VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/boot/vmlinux-%s"), 94 | VMLINUX_1("/var/cache/abrt-di/usr/lib/debug/boot/vmlinux-%s.debug"), 95 | VMLINUX_1("/usr/src/linux-%s/vmlinux"), 96 | VMLINUX_0("/usr/src/linux/vmlinux"), 97 | VMLINUX_0("/boot/vmlinux"), 98 | VMLINUZ_1("/boot/vmlinuz-%s"), 99 | VMLINUZ_2("/boot/kernel-genkernel-%s-%s"), 100 | VMLINUZ_1("/vmlinuz-%s"), 101 | VMLINUZ_2("/kernel-genkernel-%s-%s"), 102 | VMLINUZ_1("/usr/src/linux-%s/arch/x86/boot/bzImage"), 103 | VMLINUZ_0("/boot/vmlinuz"), 104 | VMLINUZ_0("/vmlinuz"), 105 | VMLINUZ_0("/usr/src/linux/arch/x86/boot/bzImage"), 106 | REMOTE_0(REMOTE_HOST), 107 | }; 108 | 109 | unsigned long 110 | try_sysmap(char *name, char *path) 111 | { 112 | FILE *f; 113 | unsigned long addr; 114 | char dummy, sname[512]; 115 | int ret = 0, oldstyle = 0; 116 | struct utsname ver; 117 | 118 | f = fopen(path, "r"); 119 | if (!f) { 120 | return 0; 121 | } 122 | 123 | uname(&ver); 124 | if (strncmp(ver.release, "2.6", 3)) { 125 | oldstyle = 1; 126 | } 127 | 128 | while (ret != EOF) { 129 | if (!oldstyle) { 130 | ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname); 131 | } else { 132 | ret = fscanf(f, "%p %s\n", (void **) &addr, sname); 133 | if (ret == 2) { 134 | char *p; 135 | if (strstr(sname, "_O/") || strstr(sname, "_S.")) { 136 | continue; 137 | } 138 | p = strrchr(sname, '_'); 139 | if (p > ((char *) sname + 5) && !strncmp(p - 3, "smp", 3)) { 140 | p = p - 4; 141 | while (p > (char *) sname && *(p - 1) == '_') { 142 | p--; 143 | } 144 | *p = '\0'; 145 | } 146 | } 147 | } 148 | if (ret == 0) { 149 | fscanf(f, "%s\n", sname); 150 | continue; 151 | } 152 | if (!strcmp(name, sname)) { 153 | fclose(f); 154 | return addr; 155 | } 156 | } 157 | 158 | fclose(f); 159 | return 0; 160 | } 161 | 162 | unsigned long 163 | try_vmlinux(char *name, char *path) 164 | { 165 | char cmd[512]; 166 | char *tmpfile = ".sysmap"; 167 | unsigned long addr; 168 | 169 | snprintf(cmd, sizeof(cmd), "nm %s &> %s", path, tmpfile); 170 | system(cmd); 171 | addr = try_sysmap(name, tmpfile); 172 | unlink(tmpfile); 173 | 174 | return addr; 175 | } 176 | 177 | unsigned long 178 | try_vmlinuz(char *name, char *path) 179 | { 180 | FILE *fp; 181 | void *mem; 182 | char *token, cmd[1024], out[1024]; 183 | unsigned long *ptr, rodata, curr = 0, prev = 0; 184 | int i, fd, ret, ctr, off, num_syms; 185 | struct stat sb; 186 | 187 | unsigned long kallsyms_num_syms; 188 | unsigned long *kallsyms_addresses; 189 | unsigned long *kallsyms_markers; 190 | uint8_t *kallsyms_names; 191 | uint8_t *kallsyms_token_table; 192 | uint16_t *kallsyms_token_index; 193 | 194 | char *tmpfile = ".vmlinuz"; 195 | char *madness_1 = "for pos in `tr \"\037\213\010\nxy\" \"\nxy=\" < \"%s\" | grep -abo \"^xy\"`; do pos=${pos%%:*}; tail -c+$pos \"%s\" | gunzip > %s 2> /dev/null; break; done"; 196 | char *madness_2 = "readelf -S %s | grep \"\\.rodata\" | awk '{print $6}'"; 197 | 198 | ret = stat(path, &sb); 199 | if (ret == -1) { 200 | return 0; 201 | } 202 | 203 | snprintf(cmd, sizeof(cmd), madness_1, path, path, tmpfile); 204 | system(cmd); 205 | 206 | ret = stat(tmpfile, &sb); 207 | if (ret == -1) { 208 | return 0; 209 | } 210 | 211 | snprintf(cmd, sizeof(cmd), madness_2, tmpfile); 212 | 213 | fp = popen(cmd, "r"); 214 | if (!fp) { 215 | return 0; 216 | } 217 | fgets(out, sizeof(out), fp); 218 | pclose(fp); 219 | 220 | rodata = strtoul(out, NULL, 16); 221 | 222 | fd = open(tmpfile, O_RDONLY); 223 | if (fd == -1) { 224 | return 0; 225 | } 226 | 227 | ret = fstat(fd, &sb); 228 | if (ret == -1) { 229 | return 0; 230 | } 231 | 232 | mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 233 | if (mem == MAP_FAILED) { 234 | return 0; 235 | } 236 | 237 | ptr = mem + rodata; 238 | 239 | for (ctr = 0; ctr < 5000; ++ctr, ++ptr) { 240 | prev = curr; 241 | curr = *ptr; 242 | if (prev > curr) { 243 | ctr = 0; 244 | } 245 | } 246 | 247 | for (; prev <= curr; ++ptr) { 248 | prev = curr; 249 | curr = *ptr; 250 | } 251 | 252 | num_syms = curr; 253 | kallsyms_num_syms = (unsigned long) (ptr - 1); 254 | kallsyms_addresses = (unsigned long *) (kallsyms_num_syms - (num_syms * sizeof(unsigned long))); 255 | kallsyms_names = (uint8_t *) (kallsyms_num_syms + (1 * sizeof(unsigned long))); 256 | 257 | for (ptr = (unsigned long *) kallsyms_names; *ptr != 0; ++ptr) { } 258 | 259 | kallsyms_markers = ptr; 260 | kallsyms_token_table = (uint8_t *) (kallsyms_markers + (((num_syms + 255) / 256))); 261 | token = (char *) kallsyms_token_table; 262 | 263 | for (i = 0; i < 256; ++i) { 264 | token += strlen(token) + 1; 265 | } 266 | 267 | kallsyms_token_index = (uint16_t *) ((unsigned long) (token + sizeof(unsigned long)) & ~(sizeof(unsigned long) - 1)); 268 | 269 | for (i = 0, off = 0; i < kallsyms_num_syms; i++) { 270 | char buf[128]; 271 | char *result = buf; 272 | int len, skipped_first = 0; 273 | uint8_t *tptr, *data; 274 | 275 | data = &kallsyms_names[off]; 276 | len = *data; 277 | data++; 278 | off += len + 1; 279 | 280 | while (len) { 281 | tptr = &kallsyms_token_table[kallsyms_token_index[*data]]; 282 | data++; 283 | len--; 284 | while (*tptr) { 285 | if (skipped_first) { 286 | *result = *tptr; 287 | result++; 288 | } else { 289 | skipped_first = 1; 290 | } 291 | tptr++; 292 | } 293 | } 294 | *result = '\0'; 295 | 296 | if (strcmp(buf, name) == 0) { 297 | return kallsyms_addresses[i]; 298 | } 299 | } 300 | 301 | close(fd); 302 | munmap(mem, sb.st_size); 303 | unlink(tmpfile); 304 | 305 | return 0; 306 | } 307 | 308 | unsigned long 309 | try_remote(char *name, char *path) 310 | { 311 | int ret, sock; 312 | struct addrinfo *result; 313 | struct addrinfo hints; 314 | unsigned long addr; 315 | struct utsname ver; 316 | char msg[512]; 317 | 318 | uname(&ver); 319 | 320 | memset(&hints, 0, sizeof(hints)); 321 | hints.ai_socktype = SOCK_STREAM; 322 | hints.ai_family = AF_INET; 323 | 324 | ret = getaddrinfo(REMOTE_HOST, REMOTE_PORT, &hints, &result); 325 | if (ret != 0) { 326 | return 0; 327 | } 328 | 329 | sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 330 | if (sock == -1) { 331 | return 0; 332 | } 333 | 334 | ret = connect(sock, result->ai_addr, result->ai_addrlen); 335 | if (ret == -1) { 336 | close(sock); 337 | return 0; 338 | } 339 | 340 | snprintf(msg, sizeof(msg), "%s|%s|%s", ver.machine, ver.release, name); 341 | 342 | ret = send(sock, msg, strlen(msg), 0); 343 | if (ret != strlen(msg)) { 344 | close(sock); 345 | return 0; 346 | } 347 | 348 | ret = recv(sock, &addr, sizeof(addr), 0); 349 | if (ret != sizeof(addr)) { 350 | close(sock); 351 | return 0; 352 | } 353 | 354 | close(sock); 355 | return addr; 356 | } 357 | 358 | unsigned long 359 | ksymhunter(char *name) 360 | { 361 | char path[512]; 362 | struct source *source; 363 | struct utsname ver; 364 | unsigned long addr; 365 | int i, count; 366 | 367 | uname(&ver); 368 | 369 | count = sizeof(sources) / sizeof(struct source); 370 | for (i = 0; i < count; ++i) { 371 | source = &sources[i]; 372 | 373 | if (source->args == 0) { 374 | snprintf(path, sizeof(path), source->fmt, ""); 375 | } else if (source->args == 1) { 376 | snprintf(path, sizeof(path), source->fmt, ver.release); 377 | } else if (source->args == 2) { 378 | snprintf(path, sizeof(path), source->fmt, ver.machine, ver.release); 379 | } 380 | 381 | addr = source->fp(name, path); 382 | if (addr) { 383 | printf("[+] resolved %s using %s\n", name, path); 384 | return addr; 385 | } 386 | } 387 | 388 | return 0; 389 | } 390 | 391 | int 392 | main(int argc, char *argv[]) 393 | { 394 | char *symbol; 395 | unsigned long addr; 396 | 397 | if (argc < 2) { 398 | printf("usage: %s symbol_name\n", argv[0]); 399 | exit(1); 400 | } 401 | 402 | symbol = argv[1]; 403 | 404 | printf("[+] trying to resolve %s...\n", symbol); 405 | 406 | addr = ksymhunter(symbol); 407 | if (!addr) { 408 | printf("[-] failed to resolve %s\n", symbol); 409 | exit(1); 410 | } 411 | 412 | printf("[+] resolved %s to 0x%lx\n", symbol, addr); 413 | 414 | return 0; 415 | } 416 | --------------------------------------------------------------------------------