Notice
Recent Posts
Recent Comments
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Today
Total
관리 메뉴

9oat's LAB

함수 주소에 대한 궁금증 본문

Study/System

함수 주소에 대한 궁금증

90at 2017. 5. 20. 02:43

BOF 문제를 풀이하면서 생겼었던 궁금증에 대해 연구해보고자 한다.


함수가 한 번 호출되고 나면 함수 호출 메커니즘에 의해 GOT에는 함수의 라이브러리 주소가 저장된다고 알고있다. 

그래서 그 주소가 해당 함수의 실제 주소겠거니 생각했는데 gdb의 print로 함수 주소를 출력했을 땐 이와 다른 주소가 출력된다.


[사진 1] 이 글을 쓰는 이유.jpg


보통 exploit payload를 구성할 땐 print로 exploit에 사용할 함수의 주소를 출력해서 그 주소를 활용했었는데 이 주소가 got에 저장되는 라이브러리 함수 주소와 다르다니.. 

프로그램이 실행되는 중 함수를 호출할 때는 해당 함수의 PLT 주소를 call한다. 이것은 GOT에 저장된 주소를 이용한다는 것과 같기 때문에 실제로 프로그램이 동작하면서 함수 호출에 이용하는 주소는 GOT에 저장된 주소를 이용할 것이다. 그렇다면 print 명령어를 이용했을 때 출력되는 이 주소는 어떤 주소일까?


먼저 두 주소가 어느 영역에 해당하는지 살펴보면


[사진 2] 메모리 영역 확인


0xf7e05000 ~ 0xf7fb4000 의 /lib/i386-linux-gnu/libc-2.23.so 영역에 존재한다.일단 둘 다 라이브러리 함수 주소란 건데..

그리고 두 주소들이 내부에서 어떻게 동작하는지 확인해보면 다음과 같다.


[사진 3] GOT에 저장된 함수 주소의 동작


[사진 4] print로 출력되는 함수 주소의 동작


동작들을 언뜻 살펴보면 GOT에 저장된 함수 주소의 내부동작이 좀 더 내가 생각했던 strcpy의 동작과 비슷한 것 같다.  두 개의 인자를 가지는 strcpy의 특징으로 미루어볼 때 처음 두 줄에 [esp+0x4] [esp+0x8]를 이용하는 게 인자를 참조하는 것처럼 보이기 때문. [esp+0x4]와 [esp+0x8]이 인자를 참조하는 것이라면 esp가 가리키고 있는 건 아마 return address가 아닐까?


뭐 이것은 추측이고 두 주소가 왜 다른지, 각각 주소들이 의미하는 것에 대해 제대로 알아봐야한다.

그런데..


[사진 5] 이건 왜 주소가 같지?..


이것저것 시도해보다가 알게 된 사실로, 모든 함수가 그런 건 아닌 것 같다. 어째 점점 더 어려워지는 것 같은데..


vmmap을 통해 메모리 영역을 확인했을 때 0xf7e05000 부터 0xf7fb8000가 라이브러리 영역이였다. 

이 라이브러리 영역을 쭉 살펴봤는데 어떤 부분들은 [사진 3] 과 같이 형식없는 어셈블리 코드들로 이뤄져있는 반면, 어떤 부분들은 [사진 4] 와 같이 함수의 형태를 띠고있었다. (프롤로그 혹은 에필로그가 존재)


만약 라이브러리 영역이 또 어떤 영역들로 나뉘어져 있다고 가정하면, 두 개의 주소는 다른 영역을 가리키고 있는 것으로 보이기 때문에 관점을 조금 바꿔 라이브러리 영역의 구조 혹은 동작방식에 대해 조사한다면 두 주소의 차이에 대해 알 수 있을지도 모른다.

(일단 확실한 건 gdb의 print 명령어로 알 수 있는 함수의 주소는 무조건 함수 형태를 띤 영역을 가리킨다.)


하지만 라이브러리 영역의 구조나 동작방식은 검색을 못하는 건지 제대로 찾질 못해 정체되어 있었다. 그래서 다른 방향으로 접근했는데 그게 gnu-indirect-function이다. [사진 1] [사진 5]의 차이점은 strcpy는 gnu-indirect-function이고 printf는 그렇지 않다는 점이다. 혹시 이것 때문에 strcpy는 2개의 주소를 가지나 해서 찾아봤다. 한글로 된 정보는 하나도 없고.. 그러던 중 어떤 글을 발견했는데


[사진 6] 오 이거 느낌있어


'간접', '문자열 함수와 같은'이 눈에 띄었다. 이 때문에 gnu-indirect-function이 원인이라고 생각했다. 이것 저것 print 해보니 문자열 관련 함수(strcpy, memcmp 등)들은 gnu-indirect-function이 붙어있었고 GOT 주소와 print했을 때 주소가 달랐다. 그 이외의 함수들(printf, fgets 등)은 단순히 text variable 이였고 GOT 주소와 print했을 때 주소가 일치했다.


이전에 동작과정을 따라가 볼려고 라이브러리 함수 주소에 breakpoint를 설정하고 실행했더니 주소를 못찾는다며 실행되지 않았었다.

그래서 라이브러리 영역에는 breakpoint를 설정할 수 없구나 했었는데 main+0에 breakpoint를 설정한 후 바이너리를 실행시킨 다음 라이브러리 영역에 breakpoint를 설정하고 continue 했더니 제대로 breakpoint가 설정됐다. 생각해보니 바이너리 실행 전에는 라이브러리 영역이 적재되지 않으니 당연한 거였다..뭐 어쨌든 이런 방법으로 라이브러리 함수 주소에 breakpoint를 설정할 수 있게 되었다.


gnu-indirect-function과 일반 text variable 함수의 동작 차이를 알기 위해서 하나씩 디버깅 해봤다.

디버깅 과정은 print로 출력되는 함수의 주소에 breakpoint를 생성하여 ni로 한 줄씩 실행했다. 

먼저 gnu-indirect-function인 strcpy로 진행했다. strcpy의 print 주소인 0xf7e79e20에 breakpoint를 걸고 continue 하면


[사진 7] strcpy에 breakpoint


break가 걸리는 걸 볼 수 있는데, 이는 실행되는 도중에 어쨌든 이 주소로 접근을 했다는 게 된다. 즉 일단 이 주소는 PLT주소나 GOT에 저장되는 라이브러리 주소와 별개로 strcpy를 호출하기 위해 이용되는 주소라는 것. 여기서부터 쭉쭉 ni로 진행한다.

그러다 ret을 만나기 직전에 strcpy의 destination 주소에 값이 복사되었는지 확인해봤다.


[사진 8] destination 주소는 0xffffce98이다.


정상적으로 strcpy가 실행됐다면 0xffffce98에 "AAAA"가 복사됐어야 했다. 하지만 그렇지 않은 걸 보면 이 주소는 제대로 된 strcpy가 아니라는 것을 알 수 있다.(print 했을 때 주소 = 실제 함수 X)


[사진 9] 0xf7e79e20(strcpy) ret 이후


ret하면 main이 아닌 다른 곳이다. 바로 /lib/ld-linux.so.2 영역. 왜 이 영역인지는 모르겠지만 일단 지금은 넘어간다. 아마 라이브러리 함수를 호출하는 어떤 단계인 것처럼 보인다.(위의 call *%eax 때문에 strcpy(0xf7e79e20)가 호출됐을 것이기 때문) 이 영역은 라이브러리 함수 영역이 아니므로 strcpy와 무관하다 생각하여 따로 destination 주소의 값을 체크하지 않고 쭉 진행하였다. 진행하다 보면 ret 하게되는데 또 /lib/ld-linux.so.2 영역이다. 거기서 한 번 더 ret하게되면..


[사진 10] !?


다시 라이브러리 영역으로 돌아오게 된다. 여기엔 낯익은 주소와 낯익은 어셈코드가.. 아까 확인한 GOT에 저장됐던 strcpy의 주소다. (그리고 mov 2줄은 예상대로 인자를 참조하는 코드였다.)

아마 지금까지의 과정은 여기에 도착하기 위함이였던 듯 하다. ni로 쭉 실행하다보면 여러 CMP문을 만나게 되는데 그 중 하나에 걸려 점프하게되고


[사진 11] Destination에 값이 복사되었다.


점프한 영역에서 한 줄씩 실행하다 보면 strcpy가 실행된다는 걸 알 수 있다. 그리고 ret하면 main으로 돌아온다.


이 과정은 gnu-indirect-function의 과정이고, 일반 text variable 함수의 경우엔 /lib/ld-linux.so.2 영역을 거치지 않고 처음 라이브러리 영역에서 함수가 동작하고 바로 main으로 ret하였다. 


이로써 정리를 할 수 있게 됐다.


gnu-indirect-function은 print 했을 때 간접주소를 출력하고, 간접주소는 /lib/ld-linux.so.2 영역을 거쳐 해당 함수의 실제 라이브러리 영역(GOT에 저장되는)으로 점프하게 된다. 즉 이 특성의 함수는 PLT, GOT, 간접주소 3가지의 주소를 갖게되는 것. 그리고 이것은 라이브러리 영역에 간접주소 영역([사진 4])과 실제 함수 실행 코드 영역([사진 3])이 구분된다고 생각할 수 있다.(물론 gnu-indirect-function의 경우에)


Ubuntu 16.04 / gcc 5.4.0 

Comments