Linux,  Programming

[Linux] ptrace

Linux, unix 환경에서 process debugging에 사용되는 함수다.

특정 Process의 Register 정보 얻기

특정 process의 register 정보를 ptrace 함수를 통해 얻을 수 있다.

// loop.c
#include <stdio.h>
#include <unistd.h>

int main()
{
    int i=0;
    while(1) {
        printf("%d\n", i++);
        sleep(1);
    }   
    return 0;
}
// ptrace.c
#include <sys/ptrace.h>
#include <sys/user.h>
#include <stdio.h>
#include <stdlib.h>
#include <asm/ptrace.h>

int main(int argc, char *argv[])
{
    struct user_regs_struct regs;
    int ret, pid, i;

    pid = atoi(argv[1]);

    ret = ptrace(PTRACE_ATTACH, pid, 0, 0); 
    printf("return=%d\n", ret);
    ptrace(PTRACE_GETREGS, pid, 0, &regs);
    printf("stack=%p\n", (void*)regs.rsp);

    ptrace(PTRACE_DETACH, pid, 0, 0); 
}

위 코드들은 우선 loop.c 프로세스를 무한히 실행시킬 때 해당 process를 attach하고, register 정보를 얻고 다시 detach하는 ptrace.c 프로그램이다.

$ gcc loop.c -o loop
$ gcc ptrace.c -o ptrace
# session 1
$ ./loop
0
1
2
3
...
# session 2
$ ps -ef | grep loop
shumin   27347 26745  0 15:13 pts/37   00:00:00 ./loop
shumin   27349 27257  0 15:13 pts/38   00:00:00 grep --color=auto loop
$ ./ptrace_1 27347
return=0
stack=0x7ffd46fd5ec8

수행 결과 stack register (rsp) 값을 얻을 수 있었다.

특정 Process의 Stack 정보 얻기

Register 정보를 얻은 것과 마찬가지로 stack의 주소를 알면 내부 값 또한 얻을 수 있다.

#include <sys/ptrace.h> 
#include <sys/user.h>
#include <stdio.h>
#include <stdlib.h>
#include <asm/ptrace.h>

int main(int argc, char *argv[])
{
    struct user_regs_struct regs;
    unsigned int data;
    int ret, pid, i;

    pid = atoi(argv[1]);

    ret = ptrace(PTRACE_ATTACH, pid, 0, 0); 
    printf("return=%d\n", ret);
    ptrace(PTRACE_GETREGS, pid, 0, &regs);
    printf("stack=%p\n", (void*)regs.rsp);

    for(i=0; i<20; i++)
    {   
        data = ptrace(PTRACE_PEEKDATA, pid, regs.rsp+i*4, 0); 
        printf("%08x\n", data);
    }   
    ptrace(PTRACE_DETACH, pid, 0, 0); 
}
$ ps -ef | grep loop
shumin   27350 26745  0 15:13 pts/37   00:00:00 ./loop
shumin   27426 27257  0 15:19 pts/38   00:00:00 grep --color=auto loop
$ ./ptrace_2 27350
return=0
stack=0x7ffc6736e858
54a3d67a
00007fa5
00000000
00000000
376ed6d4
00000000
00000001
00000000
92617500
4c0da659
54d5ab40
00007fa5
00000000
00000000
6736e8b0
00007ffc
2a2f36bf
000055df
6736e990
00007ffc

코드를 보면 printf("stack=%p\n", (void*)regs.rsp)를 통해 stack 주소를 얻은 것을 바탕으로 4 bytes 단위의 데이터를 접근하는 코드다. data = ptrace(PTRACE_PEEKDATA, pid, regs.rsp+i*4, 0)

특정 Process의 Stack 값 변경

우리가 stack의 특정 주소위치의 값을 변경시킬 수 있다.

// loop.c
#include <stdio.h>
#include <unistd.h>

int main()
{
    int i=0;
    char str[] = "Hello";
    while(1) {
        printf("%d %s\n", i++, str);
        sleep(1);
    }   
    return 0;
}

위 코드를 수행하면 지속적으로 Hello라는 문자열을 출력할 것이다. 여기서 값을 변경하기 위해선 문자열이 stack에 올라와야 하는데, 따라서 char str[] = "Hello"라는 내부 변수로 초기화한다. 따라서 이 때 해당 stack 위치를 알아내서 변경하면 출력되는 값이 바뀔 것이다.

// ptrace.c
#include <sys/ptrace.h>
#include <sys/user.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <asm/ptrace.h>

int main(int argc, char *argv[])
{
    struct user_regs_struct regs;
    unsigned int data;
    unsigned char data2[4];
    int ret, pid, i, j;

    pid = atoi(argv[1]);

    ret = ptrace(PTRACE_ATTACH, pid, 0, 0);
    printf("return=%d\n", ret);
    ptrace(PTRACE_GETREGS, pid, 0, &regs);
    printf("stack=%p\n", (void*)regs.rsp);
    for(i=0; i<300; i++)
    {
        data = ptrace(PTRACE_PEEKDATA, pid, regs.rsp+i*4, 0);
        memcpy(data2, &data, 4 );
        printf("%08x : ", (unsigned int)regs.rsp+i*4);
        for(j=0; j<4; j++){
            if(isprint(data2[j]))
                printf("%c ", data2[j]);
            else
                printf(". ");
        }
        printf("%08x\n", data);
    }
    //   ptrace(PTRACE_POKEDATA, pid, 0x7ffcdd70d240 , 0x41414141);
    ptrace(PTRACE_DETACH, pid, 0, 0);
}
$ ps -ef | grep loop
shumin   27576 26745  0 15:49 pts/37   00:00:00 ./loop
shumin   27578 27257  0 15:49 pts/38   00:00:00 grep --color=auto loop
$ ./ptrace_3 27576
return=0
stack=0x7ffffefff3d8
fefff3d8 z . . f 66fd067a
fefff3dc . . . . 00007ff7
fefff3e0 . . . . 00000000
fefff3e4 . . . . 00000000
fefff3e8 . . . 3 33d8c4e7
fefff3ec . . . . 00000000
fefff3f0 X . . . fefff458
fefff3f4 . . . . 00007fff
fefff3f8 . . . . a4c9aa00
fefff3fc . . . V 56cbfbd8
fefff400 . . . . 00000001
fefff404 . . . . 00000000
fefff408 . . . . 00000000
fefff40c . . . . 00000000
fefff410 @ . . . fefff440
fefff414 . . . . 00007fff
fefff418 . . . S 538cd6df
fefff41c ( V . . 00005628
fefff420 . . . S 538cd6f0
fefff424 ( V . . 00005628
fefff428 . . . S 538cd580
fefff42c W . . . 00000057
fefff430   . H e 6548f520
fefff434 l l o . 006f6c6c
fefff438 . . . . a4c9aa00
fefff43c . . . V 56cbfbd8

위 결과를 보면 fefff430 주소에 Hello가 있는 것을 볼 수 있다. 정확한 주소는 앞에 7fff까지 포함한 0x7ffffefff432가 될 것이다. 이를 ptrace 함수의 PTRACE_POKEDATA 옵션으로 내부 값을 “AAAA”로 변경하면 다음과 같이 된다.

$ ./ptrace_3 27576
355 Hello
356 Hello
357 Hello
358 Hello
359 AAAA
360 AAAA

Leave a Reply

Your email address will not be published. Required fields are marked *