原文链接:http://blog.chinaunix.net/u3/116920/showart_2285785.html
用dumper打开运行程序的core dump功能
应用程序在异常发生的时候产生的内存转储文件,即core文件,对于应用程序开发人员调试有随机或者是很难重现的bug的应用程序有重要的意义。无奈,当 前很多的Linux发行版为了防止core文件占用过多的磁盘空间,或者说是污染系统,core dump功能默认都是关闭的,你可以通过ulimit -c查看你正在使用的发行版的情况:
xiaosuo@gentux dumper $ ulimit -c 0 |
诚然,这些core文件对于普 通用户来说,确实意义不大,默认关闭core dump功能也是无可厚非的。
如果应用程序因为某个不可恢复的bug最终退出,那么我们也 不要奢求它能给我们留下什么bug的蛛丝马迹,除非你打开core dump功能。实际上,有的时候,bug也许并不导致程序的异常退出,而是进入了某个微妙的状态(比如死锁),表现出来的情况就是行为的异常,如果此时应 用程序运行的操作系统上有gdb,或者可以安装gdb,那么你很幸运,你能够进行在线的调试;否则,面对这样一个可能千载难逢的bug重现现场,也许就只 有望洋兴叹的份了!的确,有的时候strace就能帮我们了解一些情况,但是信息仍旧比较有限:只能显示和系统调用有关的信息。也许,你已经懊悔或者是抱 怨为什么不默认打开core dump的功能,但是牢骚除了把气氛变得更糟外,并不能实际解决什么问题,倒不如想想如何补救。
也许我们可 以向正处于异常的进程植入一段打开core dump功能的代码,然后通知它去执行植入的代码,最后我们就可以通过向他发送SIGSEGV信号来产生我们所需要的core文件了。
搜 罗了一些资料,并试验了多次后,终于完成了这个叫做dumper的小程序,它能够打开运行着的进程的core dump功能;如果用户需要,它还可以在等待3s后,向异常程序发送SIGSEGV,令其产生core文件。
dumper.c:
#include <stdio.h> #include <sys/ptrace.h> #include <sys/user.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h>
#ifndef __WALL #define __WALL 0 #endif void enable_core_dump(void); int inject(pid_t pid, const char *shellcode, int size) { long ptr; int i; struct user_regs_struct data; if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { perror("Attach"); return -1; } /* wait for the stopping of the target process */ if (waitpid(pid, NULL, __WALL) == -1) { perror("waitpid"); return -1; } if (ptrace(PTRACE_GETREGS, pid, NULL, &data) == -1) { perror("Getregs"); return -1; } /* save the return address, since we use jmp to call the * function instead of call instruction */ data.esp -= sizeof(long); if (ptrace(PTRACE_POKETEXT, pid, data.esp, data.eip) == -1) { perror("Poketext"); return -1; } /* transfer the shellcode to the target process */ if (size < 0) size = strlen(shellcode); ptr = data.eip = data.esp - size - 1024; for (i = 0; i < size; i += sizeof(long)) { if (ptrace(PTRACE_POKETEXT, pid, ptr, *((long*)(shellcode + i))) == -1) { perror("Poktext"); return -1; } ptr += sizeof(long); } /* set the instruction counter */ data.eip += 2; /* skip the two instructions: nop */ if (ptrace(PTRACE_SETREGS, pid, NULL, &data) == -1) { perror("Setregs"); return -1; } /* detach the target process and let it run... */ if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) { perror("Detach"); return -1; } return 0; } int main(int argc, char *argv[]) { pid_t pid; if (argc < 2) { printf("Usage: %s pid [-k]/n", argv[0]); return -1; } pid = atoi(argv[1]); printf("Start injecting(%d)...", pid); if (inject(pid, (const char *)enable_core_dump, 0x2c) != 0) { printf("Failed/n"); return -1; } printf("OK/n"); /* sleep for a moment. When waken up, the core dump of the target * process should be enabled. */ if (argc > 2 && strcmp(argv[2], "-k") == 0) { sleep(3); kill(pid, SIGSEGV); } return 0; } |
enable_core_dump_i386.S
/* * call the function: setrlimit(RLIMIT_CORE, {-1, -1}); * after calling that, the process is allowed to save * the core dump file if exception, such as SIGSEGV, occurs. */ /* XXX: * Something important to do before the exploitation is to put two nops bytes * before the shellcode. Reason is simple : if ptrace has interrupted a syscall * being executed, the kernel will subtract two bytes from eip after the * PTRACE_DETACH to restart the syscall. */ .globl enable_core_dump enable_core_dump: nop nop pusha push %ebp mov %esp, %ebp sub $8, %esp movl $-1, -8(%ebp) movl $-1, -4(%ebp) lea -8(%ebp), %eax mov %eax, %ecx xor %ebx, %ebx mov $4, %bl xor %eax, %eax mov $75, %al int $0x80 leave popa ret nop /* padding byte */ nop nop |
编译方法如下:
xiaosuo@gentux dumper $ gcc dumper.c enable_core_dump_i386.S -o dumper |
使 用起来比较简单,只要给出要打开core dump功能的进程号即可,如果还跟有-k参数,它还负责给目标进程发送SIGSEGV令其退出,并产生core dump文件。比如,需要打开进程号是14091的进程的core dump功能:
xiaosuo@gentux dumper $ ./dumper 14091 Start injecting(14091)...OK |
如果想立即使其退出并生成core 文件:
xiaosuo@gentux dumper $ ./dumper 14091 -k Start injecting(14091)...OK |
14091 进程将退出,并产生core文件:
段 错误 (core dumped) xiaosuo@gentux dumper $ ls core.14091 core.14091 |
代 码上的注释已经比较完备了,这里就不再赘述,如果哪里不明白,可参考文后的参考资料。
注意:以上程序只适用于32bit的x86系统,不过其他平台上的实现亦能由此原 理炮制出来,请有需求者自己。