본문 바로가기
System/Linux

ctors, dtors 영역

by bbolmin 2012. 5. 10.

포맷스트링 버그에 이용하는 .dtors 영역에 대해서 알아 보겠습니다.

 

GNU Compiler(gcc)는 컴파일 할 때 .ctors, .dtors 두 segment를 생성합니다. (gcc의 속성)

( ctors - constructor,  dtors - destructor )

 

이 두 영역의 특징은

 .ctors 속성의 함수는 main() 전에 실행되고

 .dtors 속성의 함수는 main() 종료 후에 실행 된다는 것입니다.

그러므로 main() 종료 후에 .dtors 속성의 함수가 실행되는 것을 이용해서 그 부분을 쉘 코드가 있는 주소 값으로 덮음으로써 쉘 코드가 실행되게 할 수 있다는 것입니다.

 

포맷 스트링 버그 - FSB (Format String Bug) 기본 연습하기 <- 에서는 포맷스트링 버그를 이용해서 .dtors를 에 쉘 코드가 있는 주소를 덮었습니다.  또 RedHat 버전일 때는 .data 영역이 .dtors보다 아래에 있어서 bof를 통해 덮어쓸 수도 있다고 합니다.

 

.data 영역의 위치를 간단히 확인해보면 아래와 같습니다. ( readelf -S (실행 파일) )

1. 아래는 Red Hat에서 (해커스쿨의 FTZ입니다.)

 

.data영역 위쪽에 .ctors나 .dtors가 있는 것을 확인할 수 있습니다. (따라서 .data영역에서 bof를 통해 .dtors를 덮을 수 있었습니다.)

 

2. 이번에는 fedora core3에서 확인해본 결과입니다.

.data를 .ctors나 .dtors보다 높은 곳에 위치시킨 것을 확인할 수 있습니다. (.data에서 bof로 .dtors를 덮을 수 없음; - 포인터를 조작하면 가능할 수 있겠지만)

 

 


 

그럼 ( ctors - constructor,  dtors - destructor )에 대해서 좀 더 알아보겠습니다.

 

위에서 ( 이 두 영역의 특징은 .ctors 속성의 함수는 main() 전에 실행되고 .dtors 속성의 함수는 main() 종료 후에 실행 된다는 것입니다. ) 라고 했는데 여기서 말하는 속성의 함수는 __attribute__((constructor)) 또는 __attribute__((destructor))를 사용해서 정의한 함수를 말합니다.

 

그럼 __attribute__((constructor))와 __attribute_((destructor))를 이용한 간단한 예제를 보겠습니다.

 

위와 같이 프로그램을 만들어서 실행시켜보면 예상대로 test_ctors() -> main()  -> test_dtors()  순서로 함수가 수행되는 것을 알 수 있습니다.  (main전에 constructor 수행, main 종료 후에 destructor 수행)

 

 

이제 위의 프로그램 gdb로 좀 더 살펴봅시다.

아래는 우분투 11.04에서 확인해본 결과입니다.

ctor와 dtor부분을 살펴본 것으로 LIST의 첫 부분(LIST)은 0xffffffff이고 마지막 부분(END)은 0x00000000로 끝나는 것이 기본적인 구조라는 것을 알 수 있습니다.

그런데 제가 본 문서에서는 LIST와 END 사이에 attribute로 추가했던 함수를 가리키는 부분이 있었습니다.

 

그래서 아래는 RedHat (역시 해커스쿨의 FTZ입니다.)에서 확인해본 결과입니다.

 

LIST부분과 END부분에 각각 아까 attribute로 선언했던 test_ctors함수와 test_dtors함수를 가리키는 부분이 있습니다.

 

그래서 vm에 있던 fedora11에서도 확인해보니 LIST+4에 함수 부분이 제대로 있었습니다.

 

또 백트랙5에서도 확인해보니 제대로 있습니다.

우분투의 특징인지 아니면 페도라도 높은 버전을 사용하면 이렇게 바뀌는지 .. 앞으로 공부하면서 왜 그런지 알아 보도록 하겠습니다.

 

그래서 위의 우분투는 좀 다르게 나왔지만 구조를 정리해보면

( ctor는 dtor와 같은 형태일 것이므로 dtor만 쓰겠습니다. )

 __DTOR_LIST__

 __DTOR_LIST__+4

 __DTOR_END__

가 될 것입니다. ( 만약 attribute로 추가한 함수가 2개라면 __DTOR_LIST__+8까지 있을 것이고 함수가 더있다면 계속해서 추가 되겠죠 )

 

그리고 attribute로 추가해준 함수가 없다면 단순하게

 __DTOR_LIST__

 __DTOR_END__

의 구조를 가지게 될 것입니다.

 

여기서 우리가 포맷스트링 버그를 할때 .dtors영역에 +4 해준 곳을 덮는데 따로 attribute로 추가해준 함수가 있다면 그 함수의 주소를 덮어 씌우게 될 것이고 추가해준 함수가 없다면 __DTOR_END__를 덮어 씌우는 것이 될 것입니다.

 

왜 __DTOR_END__를 덮어도 실행되는지 궁긍하시면  http://x82.inetcop.org/h0me/papers/FC_exploit/ctors_dtors.txt 문서의  _do_global_dtors_aux()가 동작하는 부분을 보고 알 수 있을 것입니다.

 

지금 까지 ctors와 dtors의 역할과 간단한 구조 (LIST - 함수들 - END)를 알아보았습니다.

 

REF : http://x82.inetcop.org/h0me/papers/FC_exploit/ctors_dtors.txt