注册 登录
自由的生活_软路由 返回首页

心想事成的个人空间 https://bbs.routerclub.com/?681 [收藏] [复制] [分享] [RSS]

日志

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

已有 253 次阅读2009-4-20 11:46

应用程序在异常发生的时候产生的内存转储文件,即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系统,不过其他平台上的实现亦能由此原理炮制出来,请有需求者自己。

参考资料:
1. Building ptrace injecting shellcod
2. 用core dump和错误自动重启技术提高软件可用性
3. 段错误bug的调试
4. C和汇编混合编程

路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|Archiver|手机版|小黑屋|软路由 ( 渝ICP备15001194号-1|渝公网安备 50011602500124号 )

GMT+8, 2024-5-9 22:48 , Processed in 0.043828 second(s), 5 queries , Gzip On, Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2023 Discuz! Team.

返回顶部