작성자 : 박진범
메일 : jinb.park7@gmail.com
http://blog.daum.net/_blog/ProfileView.do?blogid=0YW8F&totalcnt=152
:) Goal
- ARM / x86 의 함수 호출, 함수 리턴 할 때의 차이점에 대해 이해한다.
- 그러한 차이점이, 왜 ARM 에서 ROP 공격을 더 어렵게 만드는지 (x86 에 비해) 이해한다.
:) ARM / x86 에서의 함수 호출 방식 비교
- x86
- 명령어 : call
- 동작방식 : call 명령어 하나가 실행될 때, 내부적으로 아래와 같이 수행된다.
(1) push return address to stack. (stack pointer)
(2) mov dest, (%rip)
- 즉, 돌아올 return address 를 stack 에 저장하고, 실제 함수로 점프하는 것이다.
- ARM
- 명령어 : bl (branch with link)
- 동작방식 : bl 명령어 하나가 실행될 때, 내부적으로 아래와 같이 수행된다.
(1) mov lr, =next instruction (pc)
(2) mov pc, =dest
- 즉, 돌아올 return address 를 stack 이 아닌, LR register 에 저장하고, 실제 함수로 점프한다.
:) ARM / x86 에서의 함수 리턴 방식 비교 (ret 명령)
- x86
- 동작방식 : ret 명령어 하나가 실행될 때, 내부적으로 아래와 같이 수행된다.
(1) pop return address from stack (stack pointer += 8)
(2) mov return_address, (%rip)
- 즉, 함수 호출 시에 stack 에 저장했던 return address 를 다시 stack 에서 꺼낸 후, 그 주소로 복귀한다.
- ARM
- 동작방식 : ret 명령어 하나가 실행될 때, 내부적으로 아래와 같이 수행된다.
(1) mov pc, lr
- 즉, 함수 호출 시에 LR 레지스터에 저장했던 return address 를,, 해당 레지스터에서 바로 읽은 후, 그 주소로 복귀한다.
:) 왜 ARM 에서 ROP 공격이 x86 에서보다 더 어려울까??
- 2가지 이유를 설명하는데, 1번 이유가, 실제 함수 호출/리턴 방식의 차이 때문에 발생하는 것이다.
1. ARM 에서,, 함수 리턴 처리부분에 코드가 더 많기 때문에.
- ARM 과 x86 에서 함수 리턴 부분을 보자.
- x86
400577: c3 retq
- 공격자는 단순히 ROP payload 로 400577 을 사용하면 된다.
- 여기서 중요한 건, retq 에서는 stack pointer 외에 다른 어떠한 레지스터도 변경하지 않는다는 것이다.
따라서, 전체 ROP payload 모두 수행되는 동안, 프로세스가 crash 없이 성공할 확률이 높다.
- ARM
400a14: a8c27bfd ldp x29, x30, [sp],#32
400a18: d65f03c0 ret
- 공격자는 400a14 를 ROP payload 로 사용하면 된다.
- ret 명령만 부르면 안되는 이유는, ARM 에서는 ret 명령어 처리될 때 LR 레지스터에 있는 주소로 복귀하기 때문에,
공격자는 fake stack 을 구성하여 return address 를 넣어두고,
그 return address 가 LR 레지스터로 복사하는 구문이 있어야지만, ret 명령으로 그 주소로 점프할 수 있다.
- 여기서 가장 중요한 점은,
x29 ==> frame pointer (stack frame pointer. x86 의 rbp 와 같은 역할을 함)
x30 ==> link register (return address 담기는 주소)
x86 에서는 stack pointer 만 바뀌었는데, ARM 에서는
stack pointer, frame pointer, link register ==> 3개의 레지스터가 바뀐다는 것이다.
- 특히 frame pointer 가 바뀌는 것이 결정적이다. frame pointer 가 바뀌게 되면,
현재 ROP gadget 실행은 성공하더라도, 다음 ROP gadget 실행될 때 변경된 frame pointer 때문에,
프로세스가 crash 발생할 확률이 굉장히 높아진다. 따라서 공격이 실패할 확률이 굉장히 높아지는 것이다.
2. CPU Instruction Set 의 구조적인 차이 때문.
- x86 은 CISC. 즉, 명령어마다 길이가 다를 수 있다.
- ARM 은 RISC. 즉, 명령어마다 길이가 모두 4바이트로 동일하다.
- 이 말은, x86 은 명령어의 중간으로 점프하게 되면, 그 명령어는 원래와 전혀 다른 명령어로 해석 가능해진다는 얘기가 된다.
따라서, x86 에서는 gadget 을 찾을 때, 1byte 단위로 탐색이 가능하기 때문에, 더 많은 gadget 을 찾을 수 있다.
- 반대로, ARM 에서는 gadget 을 찾을 때, 반드시 4byte 단위로만 탐색이 가능하기 때문에, 훨씬 더 제한된 gadget 만 찾을 수 있다.
* 추가적으로, ARM 에서 x86 보다 stack pivoting 이 더 어려운 것도 중요한 이유이다.
x86 의 경우 xchg 라는 명령어를 통한 stack pivoting 이 가능한데,
ARM 에선 stack pivoting 을 위해 활용할 만한 간단한 명령어가 없기 때문이다.
:) 그러면 ARM exploit 에서 주로 활용하는 공격 방식은??
- 위와 같은 어려움들 때문에, ARM exploit 에서 ROP 는 잘 활용되지 않는다. 대신에,
- arbitrary memory write 취약점을 활용하거나,
- JOP (Jump Oriented Programming) 를 활용한다.
'기본개념' 카테고리의 다른 글
QEMU 파일전송 (0) | 2020.01.05 |
---|---|
[ROP] Ret2ZP on ARM (like RTL on x86) (0) | 2020.01.05 |
StackFrame (0) | 2020.01.01 |
[ARM Aseembly basics Part 1 to 7] - v1.0 (0) | 2019.12.25 |
CMP와 TEST 비교 (0) | 2019.12.24 |