[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, ®s); 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, ®s); 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, ®s); 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