기본개념

[펌] ARM / x86 에서의 함수 호출 /리턴방식 비교

우와해커 2020. 1. 1. 20:59

작성자 : 박진범

메일 : 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