프로그래밍/C

execve() argv 포인터 배열 확인

msh1307 2022. 5. 24. 00:26

execve의 시스템 콜 표이다. 

레지스터는 rax, rdi, rsi, rdx순이다. 

rax는 59, rdi에는 파일 이름의 포인터, rsi에는 argv 포인터 배열이 온다. rdx는 시스템 환경 변수가 담긴다는데, 스킵하겠다.

 

execve함수도 어차피 내부적으로는 sys_execve로 구현이 되어있고, 파라미터도 똑같이 받아준다.

/bin/ls를 통해서 한번 어떤 식으로 구현이 되는지 확인을 해보겠다.

다음과 같은 코드를 짜서 gdb로 까보겠다.

1
2
3
4
5
6
7
8
#include<stdio.h>
#include<unistd.h>
int main(){
        char * arr[3]={"/bin//ls","-al",NULL};
        execve(arr[0],arr,NULL);
 
        return 0;
}
 
cs

argv[0]은 항상 자기 자신이 들어가야 되고, argv[argc]는 항상 NULL 이여야 한다. 

여기서 포인터 배열에 NULL을 마지막에 넣어주는 이유는, 포인터 배열의 끝을 나타내기 위해서 쓴다.

나중에 디버거로 까 보면 더 잘 알 수 있다. 

/bin/ls가 아닌 /bin//ls를 넣은 이유는 /bin//sh를 넣고 콜 할 때 8바이트를 맞춰서 널 바이트를 없애기 위해서 넣었다.

쉘 코드 공부하던 도중에 넣은 거라 코드 수정을 안 했다.

/bin/ls 해도 상관없다.

코드를 작성하고 컴파일을 해보겠다.

-g 옵션을 통해서 나중에 gdb로 깔 때 편하도록 추가 정보를 넣도록 했다.

execve를 콜 할 때 어떤 식으로 들어가는지만 확인하면 되니 그쪽에 break를 걸겠다.

break *main+76으로 걸어줬다.

RDI RSI RDX를 확인해보면, 순서대로 다 잘 들어가 있는 것을 확인할 수 있다.

RDI에는 "/bin//ls"가 들어가 있다.

RSI에는 포인터 배열의 주소가 들어가 있다. 

RDX에는 NULL이 들어가있다. 

 

RSI만 한번 확인해보겠다.

아까 NULL을 함수 포인터 배열의 마지막 원소에 넣어줬었는데, 어떤 식으로 되어있는지 확인을 해보겠다.

64비트 환경이라 8바이트씩 3번 읽어왔다. 

아까 배열의 마지막 원소에 입력한 NULL을 확인할 수 있다. 

argv[] 마지막을 NULL로 해줌으로써, 배열의 끝을 나타낸다는 것을 알 수 있다. 

0x0000000008002004에는 "/bin//ls"가 들어있다. 

0x000000000800200d에는 "-al"이 들어있다. 

이를 통해 argv 포인터 배열이 어떤식으로 되어있는지 확인할 수 있다.