Mafia의 진실
Mafia의 진실 2010.02.10

낚시성 제목에 방문하신 분께는 죄송합니다. 이 글은 오래전부터 twitter 사용자들을 대상으로 퍼져나가고 있는 'Mobster world'라는 온라인 MMORPG 게임과 관련된 글입니다. 트위터 사용자 분들 중에는 위와 유..

iPhoto 슬라이드 쇼

트위터에 질문이 하나 올라와 짧게 작성해봤습니다. iPhoto에서 슬라이드 쇼 생성하는 방법입니다. 1. 먼저 사진 메뉴에서 슬라이드 쇼에 추가할 사진을 선택합니다. 사진 선택은 Command 키와 마우스를 이용합니다. 2...

노키아에서 동작하는 Mac OS X 10.3

핀란드에 사는 Toni Nikkanen이라는 분이 자신의 Nokia N900 모델에 Mac OS 10.3 Panther를 설치하고 실행시키는데 성공했다고 합니다. Toni의 블로그에 가보니 Mac OS X 말고도 Windows..

개요
악성코드나 패커 등의 프로그램에서 안티 리버싱 테크닉으로 많이 사용되는 것중 TLS callback이라는 것이 있습니다. 오늘 이야기할 주제는 바로 이 TLS callback에 관한 것입니다.

한마디로 TLS callback은 쓰레드가 생성될 때 로더에 의해서 실행되는 코드입니다. 보통 디버거를 이용하여 프로그램을 오픈하면 엔트리 포인트에서 프로그램이 멈추는데, TLS callback은 프로그램의 엔트리 포인트 이전에 실행되어 디버거로 분석하기가 쉽지 않습니다.(물론 몰랐을때 이야기입니다. TLS callback을 우회하거나 분석하는 것은 사실 매우 쉽습니다.) 악성코드 또는 패커와 같은 프로그램은 이러한 사실을 이용하여 TLS callback에 디버거 탐지 루틴이나 언패킹 루틴을 등록해 둡니다. TLS callback에 대해서 지식이 없는 리버서라면 귀신이 곡할 노릇아니겠습니까. 분석하려고 열면 죽고, 열면 죽고 또는 OEP를 찾아야하는데 도대체 어디서 언패킹을 하는지 알 수가 없다든지..

어쨌든 오늘은 이 녀석에 대해서 알아보도록 하죠.

TLS(Thread Local Storage)
사실 TLS callback을 이용한 안티 리버싱 테크닉을 이해하기 위해서 TLS 자체에 대한 이해가 필요한 것은 아닙니다. 단지 TLS callback 함수가 쓰레드가 생성될 때 실행된다는 것, ntdll.dll에 있는 LdrpRunInitializeRoutines()가 TLS callback으로 등록된 코드를 실행시킨다는 것 쯤을 기억하면 됩니다. 뭐 하지만 늘 그렇듯이 TLS callback을 공부하면서 덤으로 TLS 섹션에 대해서도 알아보는 것이 좋을 것 같습니다.  

먼저 첨부된 예제 프로그램을 OllyDbg를 이용하여 열어 보겠습니다.  

사용자 삽입 이미지

            [그림 1] sample.exe를 ollydbg로 오픈한 직후의 모습


[그림 1]에서 확인할 수 있는 것처럼 sample.exe를 디버거로 오픈한 직후 디버기(sample.exe)가 종료되어 정상적인 디버깅이 힘들게 되었습니다.  디버거가 디버기를 로드하고 브레이크하기 전에 실행되는 함수가 있다는 의미죠. 이에 대한 해답은 TLS 섹션에 있습니다.  TLS는 Thread Local Storage의 약자로 쓰레드별 전역 변수 공간 쯤으로 이해하면 됩니다. 보통 동일 프로세스 안에 존재하는 모든 쓰레드들은 전역 변수를 공유하지만 경우에 따라서 쓰레드간 전역 변수를 구분한 필요가 있기 때문에 고안된 공간입니다. TLS에 관련된 정보는 PE 구조 내의 Data Directory 테이블에서 찾을 수 있습니다. [그림 2]는 LordPE를 이용하여 sample.exe의 Data Directory 테이블을 살펴본 결과 입니다. TLS 테이블의 RVA값이 3060 이고 사이즈가 24bytes임을 알 수 있습니다.


사용자 삽입 이미지
                           [그림 2] sample.exe의  Data Directory 테이블

PE 파일 내에서 TLS 테이블의 내용을 확인하기 위해서는 파일 내 오프셋 값을 알아야 합니다.(사실 LordPE에서 H 버튼을 누르면 해당 테이블의 내용을 확인할 수 있습니다. 하지만 주소를 직접 계산해 보도록 하죠. PE에 대한 지식을 조금이라도 늘리는 것이 좋지 않겠습니까? )  불행하게도 TLS 테이블의 파일 내 오프셋에 대한 직접적인 정보는 PE 파일 내에 존재하지 않습니다. 이러한 이유로 TLS 테이블의 오프셋 값을 알아내기 위해서는 약간의 번거로운 계산이 필요합니다. 계산 방법은 아래와 같습니다.
  1. Data Directory 테이블에서 TLS 테이블의 RVA값을 확인한다.
  2. 섹션 테이블에서 TLS 테이블이 속한 섹션을 확인하고 섹션의 오프셋 값을 확인한다.
  3. 다음과 같이 계산한다.

     TLS 테이블 오프셋 = TLS 테이블이 속한 섹션의 오프셋
                              + (TLS 테이블의 RVA - TLS 테이블이속한 섹션의 RVA)


 [그림 3]은 LordPE를 이용하여 sample.exe의 섹션 테이블을 살펴본 결과 입니다.
사용자 삽입 이미지
       [그림 3] sample.exe의 섹션 테이블

.data 섹션의 RVA값이 3000입니다. 앞서 살펴본 TLS 테이블의 RVA값은 3060이었습니다. 따라서 TLS 테이블은 .data 섹션에 존재함을 알 수 있습니다. 일반적인 경우 TLS 테이블은 보통 .rdata 섹션에 존재하지만 예제 프로그램은 제가 직접 TLS 테이블을 구성한 것이라 컴파일러가 생성한 것과는 조금 차이가 있습니다. 어쨌든 RVA값이 3000인 경우 파일 내 오프셋이 800이므로 RVA값이 3060인 경우 파일내 오프셋은 860( 3060 - 3000 + 800 )이 될 것입니다.  이제 Hex 에디터를 이용하여 sample.exe를 열어보겠습니다. 앞서 잠깐 이야기한 것처럼 LordPE에서 "H" 버튼을 눌러도 됩니다.
TLS 테이블은 아래와 같이 정의되어 있습니다.

   typedef IMAGE_TLS_DIRECTORY32
   {
         DWORD    StartAddressOfRawData;
         DWORD    EndAddressOfRawData;
         DWORD    AddressOfIndex;
         DWORD    AddressOfCallBacks;
         DWORD    SizeOfZeroFill;
         DWORD    Characteristics;
   } IMAGE_TLS_DIRECTORY32;

위 구조체 중에서 4번째 멤버인 AddressOfCallbacks는 TLS callback 함수들의 주소를 담고 있는 벡터 테이블의 주소입니다. 예제 프로그램을 통해서 좀 더 자세하게 살펴보겠습니다. 먼저 sample.exe의 파일 내 오프셋 860 번지로 이동해 보겠습니다.
 
사용자 삽입 이미지
         [그림 4] sample.exe의 TLS 테이블

[그림 4]에서 볼 수 있듯이 AddressOfCallbacks 값이 00403084 입니다. RVA나 오프셋이 아닌 가상 주소가 그대로 사용된다는 사실을 알아두세용~. 이미 언급한대로 AddressOfCallbacks에 저장되어 있는 주소 값은  TLS callback 함수의 주소를 저장하고 있는 벡터 테이블을 가르킵니다. 따라서 TLS callback 함수의 주소를 알아내려면 00403084번지의 내용을 확인해 보아야 합니다. 00403084번지는 파일 내 오프셋이 884 입니다. sample.exe의 Image Base가 00400000이므로 00403084 번지의 RVA는 3084가 되고, 앞서 TLS 테이블의 시작 주소의 RVA 3060의 오프셋이 860이었음을 생각해보면 어렵지 않게 계산할 수 있을 것입니다. [그림 4]를 보면 오프셋 884에는 0040101B가 저장되어 있음을 확인할 수 있습니다. 바로 이 주소가 TLS callback 함수의 주소입니다.  IDA로 열어서 확인해 보겠습니다.


사용자 삽입 이미지

대충봐도 IsDebuggerPresent가 보이죠? 디버거 탐지 루틴이 구현되어 있음을 아실 수 있을 거라 믿습니다. sample.exe는 TLS callback 함수를 가지고 있으며, 그 안에는 디버거 탐지 루틴이 구현되어 있습니다. 따라서 아무 생각없이 디버거로 열면 정상적으로 디버깅할 수가 없는 것이지요. 설명 목적 상 디버거 탐지 루틴이 매우 직접적이고 단순하지만 PEB를 직접 접근하는 방식을 이용하거나 다른 방식의 디버거 탐지 루틴을 추가하고, 가비지 코드 많이 넣어주고, 발견되었을 때도 단순히 종료가 아니고 엉뚱한 주소로 점프하게 만들어 디버거가 코드를 잘못 해석하게 만들어 준다면 초보 리버서로서는 당황할 수 밖에 없겠죠.
 

TLS callback 디버깅 & 분석 방법

TLS callback 함수를 분석하거나 디버깅하는 방법은 매우 간단합니다.
IDA를 이용하여 실행시키기 전에 TLS callback 함수를 분석할 수도 있으며,
분석 결과 TLS callback 함수 내에 디버거 탐지 루틴 등 실행되지 않아도 분석에는 지장이 없는
(오히려 실행되지 않는 것이 더 좋은) 코드들이 구현되어 있다면 TLS 테이블 내의 AddressOfCallbacks의 값을 00000000으로 변경하여 우회하는 것도 가능합니다.

디버깅하는 방법도 역시 그리 어렵지 않습니다. TLS callback 함수가 실행되기 전에 브레이크가 걸리도록 디버거를 설정한 다음 TLS callback 함수의 시작점에 브레이크 포인트를 걸어 분석을 시작하면 됩니다. 보다 구체적인 방법은 아래와 같습니다.

TLS callback 함수 주소 알아내기 

TLS callback 함수의 주소는 앞에 설명한 방법으로 알아낼 수 있습니다. 또 다른 방법으로는 IDA를 이용하는 것인데, 개인적으로는 강의할 때가 아니면 후자의 방법을 사용합니다. IDA는 똑똑하여 TLS callback 함수를 식별해 낼 줄 알거든요. IDA로 분석할 파일을 오픈한 후 Ctrl+E(Entrypoint)를 눌러보면 쉽게 TLS callback 함수의 주소를 식별해 낼 수 있습니다. [그림 5]에 예제가 있습니다.       

사용자 삽입 이미지

                     [그림 5] IDA를 이용한 TLS callback 함수 식별



TLS callback 함수 디버깅
WinDbg를 이용하는 경우 TLS callback이 구현되어 있는 경우 TLS callback 호출 전에 알아서 멈춥니다. 멈추면 TLS callback 함수의 시작점에 브레이크 포인트를 설정하고 F9를 누르거나 'g(go)' 명령을 이용하여 분석을 시작하면 됩니다.

OllyDbg를 이용하는 경우 디버깅 옵션을 변경해 주어야 합니다.  아래와 같이 OllyDbg의 디버깅 옵션에서 Events 탭을 선택한 후 최초 정지 지점을 System Breakpoint로 설정해 주면 됩니다.

사용자 삽입 이미지


맺음말
지금까지 TLS callback 함수에 대해서 간단히 알아보았습니다. 두서없이 쓴 글이지만 리버싱을 공부하시는 분들에게 도움이 되었으면 합니다. 혹 내용 중에 잘못된 점이 있으면 zer0one@xstone.org로 이메일 주시거나 댓글 부탁드리겠습니다. 즐핵~

Posted by zesrever

BLOG main image
Slow but Steady, Broad and Deep ... by zesrever

공지사항

카테고리

분류 전체보기 (44)
Digital Forensics (4)
Reverse Engineering (21)
Vulnerability (2)
Secure Coding (0)
Book Story (1)
Digital Life (7)
My Life (7)
세미나자료 (1)
개인용 (0)
Musics (0)
Total : 248,586
Today : 57 Yesterday : 48