64bit ROP & 64bit BOF

2018. 5. 24. 00:28

최근 64bit BOF 및 ROP 문제를 접하게 되었는데, 그 전까지는 x86, 32bit만 다루어봐서 하루종일 삽질을 함과 동시에 대체 "왜 모두 제대로했는데... 익스중에 죽어버리는 거지?!??"하면서 계속해서 원인 파악을 위한 디버깅과 여러가지 시도 등... 굉장히 삽질을 하였지만 결국 왜인지 알 수 없었다.


그래서 64bit 관련해서 ROP, BOF를 찾아보니 32bit와는 꽤나 다른 것을 알 수 있었다..(아...)


먼저 이건 알고있던 사항이지만 32bit와는 다르게 64bit환경에서는 레지스터, 데이터의 크기가 기본 8byte이다.

또 64bit에서는 접근 주소의 범위가 첫 47bit까지이다. 만약 BOF를 일으켜 ret를 조작하려할 때, 0x00007fffffffffff보다 큰 값이 들어오면 에러가 나면서 종료되게 된다.


이제 몰랐지만 이제 알게된 내용. 64bit에서 함수 호출방식이 32bit와 다르다는 것!

32bi의 경우에는 인자가 ret 다음에 위치하게 되어, ebp를 기준으로 이를 참조하는 방식이다.

+----------------------+ |...higher addresses...| +----------------------+ | 1000 | <-- sleep() looks here for its param (on 32-bit) +----------------------+ | [return addr] | <-- where esp will be when sleep() is entered +----------------------+ | [sleep's addr] | <-- return addr of previous function +----------------------+ |...lower addresses....| <-- other data from previous function +----------------------+

And on 64-bit:

+----------------------+
|...higher addresses...|
+----------------------+ <-- sleep()'s param is in rdi, so it's not needed here
|     [return addr]    | <-- where rsp will be when sleep() is entered
+----------------------+
|    [sleep's  addr]   | <-- return addr of previous function
+----------------------+
|...lower addresses....| <-- other data from previous function
+----------------------+

We'll dive into deeper detail of how to set this up and see way more stack diagrams shortly. But let's start from the beginning!


하지만 64bit는 다르다... 64bit에서는 6개짜기의 인자가 레지스터에 들어간다.

첫번째 인자는 rdi 레지스터에 들어가고, 순서는 아래와 같다.


-----register field-----

rdi : 첫번째인자

rsi: 두번째인자

rdx: 세번째인자

rcx: 네번째인자

r8: 다섯번째인자

r9: 여섯번째인자

-------stack field-------

r10(%rsp): 일곱번째인자

r11, 0x8(%rsp): 여덟번째인자

r12 

....


[64BIT CALLING CONVENTION]

- function@plt + rdi + rsi + rdx + rcx



그래서 스택에는 인자가 따로 들어가지않는다.


bof가 나서 프로그램이 죽으면 ret instruction에서 죽게되는데(이걸 몰라서 계속 여기서 죽는데 지금까지 이유를 몰랐다.)

보통은 rsp가 잘못되거나 rsp가 가리키는 값(rip가 되는값)이 잘못되었기 때문인데...


여기서 인자를 32bit처럼 올려주고 실행하려고 하면 당연히 인자는 rdi레지스터부터 가져오므로 잘못된 호출로 죽게된다. (스택은 아무의미가 없다 ㅡㅡ;)


그러므로 함수호출전에 pop rdi 같은 가젯들을 이용해 인자를 넣어주는게 좋다.



+ Recent posts