initial commit
[surgeon.git] / surgeon.c
blobeb20f1809a8a1cffef18c01d18ff253a5a3bf6da
1 /*
2 * Copyright (C) 2010 gonzoj
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <dlfcn.h>
19 #include <errno.h>
20 #include <getopt.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/ptrace.h>
24 #include <unistd.h>
26 #include "elf_utils.h"
27 #include "proc_utils.h"
28 #include "ptrace_utils.h"
29 #include "stubs.h"
30 #include "types.h"
32 pid_t pid = 0;
34 char *module_to_load = NULL;
36 char action = 'i';
38 char *libc_path = NULL;
40 Elf32_Off malloc_off = 0;
41 Elf32_Off free_off = 0;
42 Elf32_Off dlopen_off = 0;
43 Elf32_Off dlclose_off = 0;
45 int
46 resolve_symbol_generic(vaddr_t base, const char *symbol, vaddr_t *resolved)
48 if (elf_is_valid(base))
50 Elf32_Ehdr ehdr;
51 if (elf_read_ehdr(base, ehdr))
53 if (ehdr.e_type == ET_DYN)
55 vaddr_t phdr_table = base + ehdr.e_phoff;
56 int i;
57 for (i = 0; i < ehdr.e_phnum * ehdr.e_phentsize; i
58 += ehdr.e_phentsize)
60 Elf32_Phdr phdr;
61 if (elf_read_phdr(phdr_table + i, phdr))
63 if (phdr.p_type == PT_DYNAMIC)
65 vaddr_t dyn_section = base + phdr.p_offset;
66 Elf32_Off dyn_off = 0;
67 vaddr_t symtab = 0, strtab = 0;
68 long nchain = 0;
69 Elf32_Dyn dyn;
72 if (!elf_read_dyn(dyn_section + dyn_off, dyn))
74 break;
76 dyn_off += sizeof(Elf32_Dyn);
77 if (dyn.d_tag == DT_SYMTAB)
79 symtab
80 = ((vaddr_t) dyn.d_un.d_ptr < base) ? base
81 + dyn.d_un.d_ptr
82 : (vaddr_t) dyn.d_un.d_ptr;
84 else if (dyn.d_tag == DT_STRTAB)
86 strtab
87 = ((vaddr_t) dyn.d_un.d_ptr < base) ? base
88 + dyn.d_un.d_ptr
89 : (vaddr_t) dyn.d_un.d_ptr;
91 else if (dyn.d_tag == DT_HASH)
93 vaddr_t hash = ((vaddr_t) dyn.d_un.d_ptr
94 < base) ? base + dyn.d_un.d_ptr
95 : (vaddr_t) dyn.d_un.d_ptr;
96 if (!elf_read(hash + sizeof(Elf32_Word),
97 &nchain, sizeof(Elf32_Word)))
99 break;
103 while (dyn.d_tag != DT_NULL && !(symtab && strtab
104 && nchain));
105 if (symtab && strtab && nchain)
107 int j;
108 for (j = 0; j < nchain * sizeof(Elf32_Sym); j
109 += sizeof(Elf32_Sym))
111 Elf32_Sym sym;
112 if (elf_read_sym(symtab + j, sym))
114 if (sym.st_name != 0)
116 char *sym_name = elf_read_string(
117 strtab + sym.st_name);
118 if (sym_name)
120 if (strcmp(symbol, sym_name) == 0)
122 *resolved
123 = ((vaddr_t) sym.st_value
124 < base) ? base
125 + sym.st_value
126 : (vaddr_t) sym.st_value;
127 return 1;
140 return 0;
144 resolve_symbol_dumb(const char *object, const char *symbol, vaddr_t *resolved)
146 char *cmd = (char *) malloc(strlen("readelf -s ") + strlen(object) + strlen(
147 " | grep ") + strlen(symbol) + 1);
148 if (cmd == NULL)
150 printf("err: %s\n", strerror(errno));
151 return 0;
153 sprintf(cmd, "readelf -s %s | grep %s", object, symbol);
154 FILE *output = popen(cmd, "r");
155 if (output == NULL)
157 printf("err: %s\n", strerror(errno));
158 free(cmd);
159 return 0;
161 char *line;
162 while ((line = read_line(output)))
164 if (strstr(line, "GLOBAL"))
166 Elf32_Off off;
167 strtok(line, ":");
168 char *tmp = strtok(NULL, " ");
169 if (tmp)
171 sscanf(tmp, "%x", &off);
172 strtok(NULL, " ");
173 strtok(NULL, " ");
174 strtok(NULL, " ");
175 strtok(NULL, " ");
176 strtok(NULL, " ");
177 char *sym = strtok(NULL, "@");
178 if (sym)
180 if (strcmp(symbol, sym) == 0)
182 vaddr_t base;
183 if (proc_get_object_base(object, &base))
185 *resolved = base + off;
186 pclose(output);
187 free(cmd);
188 return 1;
195 pclose(output);
196 free(cmd);
197 return 0;
201 load_module(const char *module, vaddr_t patch, vaddr_t sym__libc_dlopen_mode,
202 vaddr_t sym_malloc, vaddr_t sym_free)
204 long success = 0;
206 struct user_regs_struct regs_backup;
207 struct user_regs_struct regs;
209 if (ptrace(PTRACE_GETREGS, pid, NULL, &regs_backup) == -1)
211 printf("err: %s\n", strerror(errno));
212 return 0;
214 memcpy(&regs, &regs_backup, sizeof(struct user_regs_struct));
216 long *patch_backup;
217 size_t patch_size = ALIGN_SIZE(call_malloc_STUB_size)
218 + ALIGN_SIZE(call_free_STUB_size)
219 + ALIGN_SIZE(call_libc_dlopen_mode_STUB_size);
220 if ((patch_backup = ptrace_read(patch, patch_size)) == NULL)
222 printf("err: could not backup original code\n");
223 return 0;
225 vaddr_t free_stub = patch + ALIGN_SIZE(call_malloc_STUB_size);
226 vaddr_t libc_dlopen_mode_stub = free_stub + ALIGN_SIZE(call_free_STUB_size);
227 if (!ptrace_write(patch, call_malloc_STUB, call_malloc_STUB_size))
229 printf("err: could not patch in malloc stub\n");
230 return 0;
232 if (!ptrace_write(free_stub, call_free_STUB, call_free_STUB_size))
234 printf("err: could not patch in free stub\n");
235 goto backup;
237 if (!ptrace_write(libc_dlopen_mode_stub, call_libc_dlopen_mode_STUB,
238 call_libc_dlopen_mode_STUB_size))
240 printf("err: could not patch in __libc_dlopen_mode stub\n");
241 goto backup;
244 regs.eax = (long) sym_malloc;
245 regs.ecx = (long) ALIGN_SIZE(strlen(module) + 1);
246 vaddr_t module_string = ptrace_call(patch, &regs);
247 if (module_string == NULL)
249 printf("err: allocating memory for module name failed\n");
250 goto backup;
252 if (!ptrace_write(module_string, (void *) module, strlen(module) + 1))
254 printf("err: failed to write module name\n");
255 goto cleanup;
258 regs.eax = (long) sym__libc_dlopen_mode;
259 regs.ecx = (long) RTLD_LAZY | 0x80000000;
260 regs.edx = (long) module_string;
261 success = (long) ptrace_call(libc_dlopen_mode_stub, &regs);
263 cleanup: regs.eax = (long) sym_free;
264 regs.ecx = (long) module_string;
265 ptrace_call(free_stub, &regs);
267 backup: if (!ptrace_write(patch, patch_backup, patch_size))
269 printf("err: failed to restore original code\n");
270 success = 0;
272 if (ptrace(PTRACE_SETREGS, pid, NULL, &regs_backup) == -1)
274 printf("err: %s\n", strerror(errno));
275 return 0;
278 return success;
282 unload_module(const char *module, vaddr_t patch, vaddr_t sym__libc_dlclose,
283 vaddr_t sym__libc_dlopen_mode, vaddr_t sym_malloc, vaddr_t sym_free)
285 struct user_regs_struct regs_backup;
286 struct user_regs_struct regs;
288 if (ptrace(PTRACE_GETREGS, pid, NULL, &regs_backup) == -1)
290 printf("err: %s\n", strerror(errno));
291 return 0;
293 memcpy(&regs, &regs_backup, sizeof(struct user_regs_struct));
295 long *patch_backup;
296 size_t patch_size = ALIGN_SIZE(call_malloc_STUB_size)
297 + ALIGN_SIZE(call_free_STUB_size)
298 + ALIGN_SIZE(call_libc_dlopen_mode_STUB_size)
299 + ALIGN_SIZE(call_libc_dlclose_STUB_size);
300 if ((patch_backup = ptrace_read(patch, patch_size)) == NULL)
302 printf("err: could not backup original code\n");
303 return 0;
305 vaddr_t free_stub = patch + ALIGN_SIZE(call_malloc_STUB_size);
306 vaddr_t libc_dlopen_mode_stub = free_stub + ALIGN_SIZE(call_free_STUB_size);
307 vaddr_t libc_dlclose_stub = libc_dlopen_mode_stub
308 + ALIGN_SIZE(call_libc_dlopen_mode_STUB_size);
309 if (!ptrace_write(patch, call_malloc_STUB, call_malloc_STUB_size))
311 printf("err: could not patch in malloc stub\n");
312 return 0;
314 if (!ptrace_write(free_stub, call_free_STUB, call_free_STUB_size))
316 printf("err: could not patch in free stub\n");
317 goto backup;
319 if (!ptrace_write(libc_dlopen_mode_stub, call_libc_dlopen_mode_STUB,
320 call_libc_dlopen_mode_STUB_size))
322 printf("err: could not patch in __libc_dlopen_mode stub\n");
323 goto backup;
325 if (!ptrace_write(libc_dlclose_stub, call_libc_dlclose_STUB,
326 call_libc_dlclose_STUB_size))
328 printf("err: could not patch in __libc_dlclose stub\n");
329 goto backup;
332 regs.eax = (long) sym_malloc;
333 regs.ecx = (long) ALIGN_SIZE(strlen(module) + 1);
334 vaddr_t module_string = ptrace_call(patch, &regs);
335 if (module_string == NULL)
337 printf("err: could not allocate memory for module name\n");
338 goto backup;
340 if (!ptrace_write(module_string, (void *) module, strlen(module) + 1))
342 printf("err: failed to write module name\n");
343 goto cleanup;
346 regs.eax = (long) sym__libc_dlopen_mode;
347 regs.ecx = (long) RTLD_LAZY | 0x80000000;
348 regs.edx = (long) module_string;
349 void *handle = ptrace_call(libc_dlopen_mode_stub, &regs);
350 if (handle == NULL)
352 printf("err: could not get a handle for %s\n", module);
353 goto cleanup;
356 long success = 1;
357 vaddr_t base;
358 while (proc_get_object_base(module, &base))
360 regs.eax = (long) sym__libc_dlclose;
361 regs.ecx = (long) handle;
362 if (ptrace_call(libc_dlclose_stub, &regs))
364 printf("err: __libc_dlclose returned with error\n");
365 success = 0;
366 break;
370 cleanup: regs.eax = (long) sym_free;
371 regs.ecx = (long) module_string;
372 ptrace_call(free_stub, &regs);
374 backup: if (!ptrace_write(patch, patch_backup, patch_size))
376 success = 0;
378 if (ptrace(PTRACE_SETREGS, pid, NULL, &regs_backup) == -1)
380 printf("err: %s\n", strerror(errno));
381 return 0;
384 return success;
388 parse_arguments(int argc, char **argv)
390 struct option long_options[] =
392 { "libc", required_argument, 0, 'a' },
393 { "malloc", required_argument, 0, 'b' },
394 { "free", required_argument, 0, 'c' },
395 { "dlopen", required_argument, 0, 'd' },
396 { "dlclose", required_argument, 0, 'e' },
397 { 0 } };
398 while (optind < argc)
400 int index = -1;
401 int result = getopt_long(argc, argv, "hiu", long_options, &index);
402 if (result == -1)
404 break;
406 switch (result)
408 case 'a':
409 libc_path = (char *) malloc(strlen(optarg) + 1);
410 if (libc_path == NULL)
412 printf("err: %s\n", strerror(errno));
413 return 0;
415 strcpy(libc_path, optarg);
416 break;
417 case 'b':
418 if (!sscanf(optarg, "%lx", (long unsigned *) &malloc_off))
420 return 0;
422 break;
423 case 'c':
424 if (!sscanf(optarg, "%lx", (long unsigned *) &free_off))
426 return 0;
428 break;
429 case 'd':
430 if (!sscanf(optarg, "%lx", (long unsigned *) &dlopen_off))
432 return 0;
434 break;
435 case 'e':
436 if (!sscanf(optarg, "%lx", (long unsigned *) &dlclose_off))
438 return 0;
440 break;
441 case 'h':
442 return 0;
443 case 'i':
444 case 'u':
445 action = result;
446 break;
447 case '?':
448 printf("err: unknown parameter\n");
449 return 0;
450 case ':':
451 printf("err: missing argument\n");
452 return 0;
455 if (argc - optind < 2)
457 return 0;
459 if (!sscanf(argv[optind++], "%i", &pid))
461 return 0;
463 module_to_load = (char *) malloc(strlen(argv[optind]) + 1);
464 strcpy(module_to_load, argv[optind++]);
465 return 1;
468 void
469 usage()
471 printf("\n");
472 printf("usage:\n");
473 printf(" surgeon [OPTION]... PID MODULE\n");
474 printf("\n");
475 printf(" OPTIONS:\n");
476 printf(" -h display usage\n");
477 printf(" -i inject MODULE into process PID (default)\n");
478 printf(" -u unload MODULE from process PID\n");
479 printf(" --libc=PATH set location of libc to PATH\n");
480 printf(" --malloc=OFFSET set offset for symbol malloc to OFFSET\n");
481 printf(" --free=OFFSET set offset for symbol free to OFFSET\n");
482 printf(
483 " --dlopen=OFFSET set offset for symbol __libc_dlopen_mode to OFFSET\n");
484 printf(
485 " --dlclose=OFFSET set offset for symbol __libc_dlclose to OFFSET\n");
486 printf("\n");
490 main(int argc, char **argv)
492 if (!parse_arguments(argc, argv))
494 usage();
495 exit(EXIT_FAILURE);
498 if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1)
500 printf("err: could not attach to process %i (%s)\n", pid, strerror(errno));
501 exit(EXIT_FAILURE);
504 vaddr_t libc_base = 0, patch = 0, p_malloc = 0, p_free = 0, p_dlopen = 0,
505 p_dlclose = 0;
507 if (libc_path)
509 proc_get_object_base(libc_path, &libc_base);
511 else
513 proc_get_object_base("libc-", &libc_base);
516 if (libc_base)
518 if (libc_path == NULL)
520 libc_path = proc_get_object_path(libc_base);
522 if (malloc_off)
524 p_malloc = libc_base + malloc_off;
526 else if (!resolve_symbol_generic(libc_base, "malloc", &p_malloc))
528 if (libc_path)
530 resolve_symbol_dumb(libc_path, "malloc", &p_malloc);
533 if (free_off)
535 p_free = libc_base + free_off;
537 else if (!resolve_symbol_generic(libc_base, "free", &p_free))
539 if (libc_path)
541 resolve_symbol_dumb(libc_path, "free", &p_free);
544 if (dlopen_off)
546 p_dlopen = libc_base + dlopen_off;
548 else if (!resolve_symbol_generic(libc_base, "__libc_dlopen_mode",
549 &p_dlopen))
551 if (libc_path)
553 resolve_symbol_dumb(libc_path, "__libc_dlopen_mode", &p_dlopen);
556 if (dlclose_off && action == 'u')
558 p_dlclose = libc_base + dlclose_off;
560 else if (!resolve_symbol_generic(libc_base, "__libc_dlclose", &p_dlclose)
561 && action == 'u')
563 if (libc_path)
565 resolve_symbol_dumb(libc_path, "__libc_dlclose", &p_dlclose);
569 else
571 vaddr_t addr;
572 while (proc_iterate_addrspace(&addr))
574 if (!p_malloc)
576 resolve_symbol_generic(addr, "malloc", &p_malloc);
578 if (!p_free)
580 resolve_symbol_generic(addr, "free", &p_free);
582 if (!p_dlopen)
584 resolve_symbol_generic(addr, "__libc_dlopen_mode", &p_dlopen);
586 if (action == 'u' && !p_dlclose)
588 resolve_symbol_generic(addr, "__libc_dlclose", &p_dlclose);
593 char *bin = proc_get_executable();
594 if (bin)
596 proc_get_object_base(bin, &patch);
599 if (!(patch && p_malloc && p_free && p_dlopen && (action == 'i' || p_dlclose)))
601 printf("err: could not resolve all necessary symbols\n");
602 ptrace(PTRACE_DETACH, pid, NULL, NULL);
603 exit(EXIT_FAILURE);
605 else
607 int success;
608 switch (action)
610 case 'i':
611 printf("injecting module %s into process %i\n", module_to_load, pid);
612 success
613 = load_module(module_to_load, patch, p_dlopen, p_malloc, p_free);
614 if (!success)
616 printf("err: failed to load module %s\n", module_to_load);
618 else
620 printf("successfully injected module %s\n", module_to_load);
622 break;
623 case 'u':
624 printf("unloading module %s from process %i\n", module_to_load, pid);
625 success = unload_module(module_to_load, patch, p_dlclose, p_dlopen,
626 p_malloc, p_free);
627 if (!success)
629 printf("err: failed to unload module %s\n", module_to_load);
631 else
633 printf("successfully unloaded module %s\n", module_to_load);
635 break;
637 if (!success)
639 ptrace(PTRACE_DETACH, pid, NULL, NULL);
640 exit(EXIT_FAILURE);
644 ptrace(PTRACE_DETACH, pid, NULL, NULL);
645 exit(EXIT_SUCCESS);