x86 call convention & how to ROP in x86
기존에 x86이 스택을 통해 인자를 받는 것을 당연히 알고 있을 것이다.
이번에는 어떻게 받는지 좀 더 자세하게 알아보도록 하자 그리고 나아가 ROP 체인을 만드는 관점에서 알아보자
0x080485f6 <+29>: push $0x400
0x080485fb <+34>: lea -0x44(%ebp),%eax
0x080485fe <+37>: push %eax
0x080485ff <+38>: push $0x0
0x08048601 <+40>: call 0x80483f0 <read@plt>다음은 일반적인 read함수를 부르는 과정이다. 인자가 3개 답게 push를 3번함을 알 수 있다. 그럼 이제 함수 내부에서 볼건데 우리가 함수의 스택을 계산할때 생각해야 하는 건 $rsp와 $rbp의 움직임이다.
지금도 사실 call 함수가 불리는 순간 $rsp 는 0x4만큼 작아진다. RA가 들어가기 때문이다. 그리고 추가적으로 함수의 프롤로그에서 당연히 $rbp 가 push 된다. 따라서 $rsp - 0x8이 된다.

즉, 다음과 같이 스택이 구조 된다는 것이다.
그리고 ROP를 작성할때는 내가 어디를 기준으로 ROP를 작성하는지 계속 생각해야 한다. 예를 들어 read@plt 함수로 ROP를 짠다면

이상태를 가정하고 짜야한다. 왜냐하면
Dump of assembler code for function read@plt:
0x080483f0 <+0>: jmp *0x804a00c
0x080483f6 <+6>: push $0x0
0x080483fb <+11>: jmp 0x80483e0read@plt는 call만 된 상태이지 아직 진짜 Libc 함수로 가서 $rbp 가 push가 도기 전이기 때문이다. 따라서 payload를 짤때
payload += p32(read_plt)
payload += p32("다음 ROP체인")
payload += p32(0)
payload += p32(read_got + 0x100)
payload += p32(8)다음처럼 RA 위치에 다음 ROP 체인 주소를 넣는 것이 일반적이다.
근데.. 생각을 해보면 다음 ROP 체인 주소를 넣는다고 해도 아래 스택 3개를 치울 수 있는 방법이 필요하다. ( 다음 ROP 체인 함수에 필요가 없는 인자니깐 )
이럴때 주로 쓰는 방법중 2가지가 있는데
- main 함수문 그대로 사용하기
- pop을 원하는 갯수 만큼 해주는 가젯 사용
다음과 같다.
1번은
0x080485f6 <+29>: push $0x400
0x080485fb <+34>: lea -0x44(%ebp),%eax
0x080485fe <+37>: push %eax
0x080485ff <+38>: push $0x0
0x08048601 <+40>: call 0x80483f0 <read@plt>
0x08048606 <+45>: add $0xc,%esp다음과 같이 main함수처럼 read 함수를 원래 프로그램의 목적으로 부르는 코드에서는 함수가 끝나고
0x08048606 <+45>: add $0xc,%esp
처럼 스택을 정리를 해준다. 따라서 스택을 관리하기 쉽다.
(아니면 main함수를 아예 처음 부터 실행해도 됨 )
2번은
다음 페이지를 참고하자