用dumper打开运行程序的core dump功能

原文链接: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 (= 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系统,不过其他平台上的实现亦能由此原 理炮制出来,请有需求者自己。

你可能感兴趣的:(linux,function,struct,kill,null,程序开发)