重定向已经运行进程的标准输出到文件的办法(通过ptrace注入代码到其他进程并运行)

重定向已经运行进程的标准输出到文件的办法(通过ptrace注入代码到其他进程并运行)


上次看到CSDN里面有人问这个问题,我觉得用ptrace这个系统调用是可以实现的。前两天在IBM developworks看到中兴工程师写的一篇文章是用ptrace来挂钩系统调用然后获取已运行进程的网络包的,他文章里面把程序之间通讯用的网络包分离出来然后在wireshrark中分析。通过这篇文档,证实我之前的想法应该也是可行的。最近工作偷懒,就试试这个吧。

1、重定向标准输出,用dup2系统调用,dup2(file,1)把一个打开的文件描述符的复制到标准输出1去应该就可以了吧。参考“Understanding+The+Linux+Kernel+3rd.pdf” 一书关于管道使用那一章举的ls| more 例子。
2、ptrace这个系统调用功能很强大,可以用来调试进程,挂钩系统调用函数,等待。Windows上面的read/writeprocessmemory以及hook api技术都可以通过这个来实现。调试器也都可以使用这个来做了,不知道GDB是不是也是用ptrace来实现的? 
   我们用重定向标准输出,就用ptrace注入一段调试代码到目标进程,然后在里面调用dup2等几个函数就可以了。
3、ptrace也有一个缺陷,就是如果要attach的进程如果本来就有父进程的话,那么被我们用ptrace处理之后,他原本的父进程是没有办法再受到来自被attach进程的消息了。好像也没有办法恢复。自己看一下ptrace的文档吧,不知道我理解错了没有。反正如果目标进程有父进程的话,用这个办法难免有些影响,实际运用的时候要考虑一下。
4、代码大多从“Playing with ptrace, Part II http://www.linuxjournal.com/node/6210/print”这篇文章里面复制过来,可以去看看原文关于ptrace的用法说名吧。只是我把他本来打印“hello world”的汇编代码改为调用两个系统调用的了。
   调用Linux系统调用通过int $80的80号中断来进行,调用时,其中eax存放系统中断号码,ebx ,ecx等等依次为传给系统调用的参数。系统调用返回时eax为返回值。可以参考一下“Professional+Assembly+Language.pdf” 一书关于在汇编中使用文件系统调用来操作文件的例子。
   其实,好像说新的cpu可以通过sysenter指令来进入系统调用的,那个性能要比int 80要好些。大家可以试试吧。我对汇编不是很熟悉就不试了。我在virtuabox + ubuntu 10.04上面测试int $80还是可以正常工作的,可能现在系统对两种办法都还支持吧。


代码如下:
----------test.c---------------------------------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <asm/ptrace.h>
#include <string.h>

const int long_size = sizeof(long);
void getdata(pid_t child, long addr,
             char *str, int len)
{   char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    }data;
    i = 0;
    j = len / long_size;
    laddr = str;
    while(i < j) {
        data.val = ptrace(PTRACE_PEEKDATA, child,
                          addr + i * 4, NULL);
        memcpy(laddr, data.chars, long_size);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        data.val = ptrace(PTRACE_PEEKDATA, child,
                          addr + i * 4, NULL);
        memcpy(laddr, data.chars, j);
    }
    str[len] = '\0';
}
void putdata(pid_t child, long addr,
             char *str, int len)
{   char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    }data;
    i = 0;
    j = len / long_size;
    laddr = str;
    while(i < j) {
        memcpy(data.chars, laddr, long_size);
        ptrace(PTRACE_POKEDATA, child,
               addr + i * 4, data.val);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        memcpy(data.chars, laddr, j);
        ptrace(PTRACE_POKEDATA, child,
               addr + i * 4, data.val);
    }
}


int main(int argc, char *argv[])
{

//   int log;
//   log = open("/tmp/widebright.txt",O_CREAT|O_RDWR|O_TRUNC, S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP| S_IROTH|S_IWOTH );
//printf ("result =%d\n",log);
//exit(0);
// if (log != -1){
//       int result =dup2(log,1); //重定向标准输出
       //if (result == -1) 
       // printf ("dup2 return failed\n");
       //printf ("result =%d\n", write (log, "failed\n", 5));
// }

   //printf("ddddd,hahahha\n");
   
// close (log);

/*

__asm__ (
"jmp forward\n\t"
"backward:\n\t"
         "popl   %esi #get path string address \n\t"
         "movl   $5, %eax #sys_open number arch/x86/kernel/syscall_table_32.S\n\t"
         "movl   %esi, %ebx\n\t"
         "movl   $0x242, %ecx\n\t"
         "movl   $0x1b6, %edx #all has read and write permissions\n\t"
         "int    $0x80\n\t"
         "test   %eax,%eax\n\t"
         "js end\n\t"
         "mov    %eax , %ebx # parm 1\n\t"
         "movl   $63, %eax #sys_dup2 number arch/x86/kernel/syscall_table_32.S\n\t"
         "movl   $1, %ecx     # parm 2\n\t "
         "int    $0x80\n\t"
"#jmp end\n\t"
"end:\n\t"
         "int3\n\t"
"forward:\n\t"
         "call   backward\n\t"
         ".string \"/tmp/widebright.txt\"\n\t"
"#end:\n\t"
);

(gdb) x/66xb backward-2
0x8048c10 <main+53>: 0xeb 0x27 0x5e 0xb8 0x05 0x00 0x00 0x00
0x8048c18 <main+61>: 0x89 0xf3 0xb9 0x42 0x02 0x00 0x00 0xba
0x8048c20 <main+69>: 0xb6 0x01 0x00 0x00 0xcd 0x80 0x85 0xc0
0x8048c28 <main+77>: 0x78 0x0e 0x89 0xc3 0xb8 0x3f 0x00 0x00
0x8048c30 <main+85>: 0x00 0xb9 0x01 0x00 0x00 0x00 0xcd 0x80
0x8048c38 <end>: 0xcc 0xe8 0xd4 0xff 0xff 0xff 0x2f 0x74
0x8048c40 <forward+7>: 0x6d 0x70 0x2f 0x77 0x69 0x64 0x65 0x62
0x8048c48 <forward+15>: 0x72 0x69 0x67 0x68 0x74 0x2e 0x74 0x78
0x8048c50 <forward+23>: 0x74 0x00


*/

//printf("ddddd,hahahha\n");
// return 0;

    pid_t traced_process;
    struct pt_regs regs, newregs;
    long ins;
    const int len = 66;
    char insertcode[] =
{0xeb, 0x27, 0x5e, 0xb8, 0x05, 0x00, 0x00, 0x00,
0x89, 0xf3, 0xb9, 0x42, 0x02, 0x00, 0x00, 0xba,
0xb6, 0x01, 0x00, 0x00, 0xcd, 0x80, 0x85, 0xc0,
0x78, 0x0e, 0x89, 0xc3, 0xb8, 0x3f, 0x00, 0x00,
0x00, 0xb9, 0x01, 0x00, 0x00, 0x00, 0xcd, 0x80,
0xcc, 0xe8, 0xd4, 0xff, 0xff, 0xff, 0x2f, 0x74,
0x6d, 0x70, 0x2f, 0x77, 0x69, 0x64, 0x65, 0x62,
0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x74, 0x78,
0x74, 0x00};

    char backup[len];
    if(argc != 2) {
        printf("Usage: %s <pid to be traced>\n",
               argv[0], argv[1]);
        exit(1);
    }
    traced_process = atoi(argv[1]);
    ptrace(PTRACE_ATTACH, traced_process,
           NULL, NULL);
    wait(NULL); //等待attach成功。目标进程被断下
    ptrace(PTRACE_GETREGS, traced_process,
           NULL, &regs); //获取目标进程当前的寄存器状态
    getdata(traced_process, regs.eip, backup, len);
    putdata(traced_process, regs.eip,
            insertcode, len); //注入我们的指令到当前运行的位置。
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &regs);
    ptrace(PTRACE_CONT, traced_process,
           NULL, NULL); //继续运行
    wait(NULL); //等待我们汇编代码里面的int3 调试断点。
    printf("The process stopped, Putting back "
           "the original instructions\n");

    ptrace(PTRACE_GETREGS, traced_process,
           NULL, &newregs);
   // printf("\n%ld-%ld\n",regs.eip,newregs.eip);
   // sleep(10);
    putdata(traced_process, regs.eip, backup, len); //还原原本的代码
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &regs);
    printf("Letting it continue with "
           "original flow\n");
    ptrace(PTRACE_DETACH, traced_process,
           NULL, NULL);
    return 0;


}
-------------写一个简单的测试例子 test2.c-------------------- 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


int main(void) 
{
   int i = 0;
   while (i < 20000000)
   {                    
      printf("current number is %d\n",i);
      i++;
      sleep(5);         
   }

}
------------------------------------------

测试一下。
gcc test2.c -o test.exe
桌面$ ./test.exe 
current number is 0
current number is 1
current number is 2
current number is 3
current number is 4
current number is 5
current number is 6
current number is 7
current number is 8
current number is 9
current number is 10
current number is 11
current number is 12
current number is 13
current number is 14
^C

-----------
桌面$ ps -ef |grep test.exe
1000      4609 3554 0 10:51 pts/0    00:00:00 ./test.exe
1000      4667 4295 0 10:52 pts/1    00:00:00 grep --color=auto test.exe
桌面$ ./a.out 4609
The process stopped, Putting back the original instructions
Letting it continue with original flow
桌面$ cat /tmp/widebright.txt
current number is 15
桌面$ cat /tmp/widebright.txt
current number is 15
current number is 16
桌面$ cat /tmp/widebright.txt
current number is 15
current number is 16


可以看到test。exe的标准输出确实被我们重定向到这个/tmp/widebright.txt文件里面去了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值