annyoung

TLS callback 악성코드 우회 및 분석 본문

분석생활

TLS callback 악성코드 우회 및 분석

nopsled 2015. 5. 19. 02:59

몇일전 메일로 분석 문의가 들어와서 급하게 써본다..


TLS callback을 어떻게 bypass를 하냐는 문의였는데 솔직히 TLS callback(), IsDebuggerPresent()를 사용해서 안티 디버깅 하는건 나도 몰랐던 부분인데 덕분에 공부도 하고 좋았던 것 같다.


TLS callback을 사용하는 악성코드를 어떻게 우회를 하냐며 여러 샘플을 보내주셨는데 한두개 정도 우회 하는 방법에 대해서 설명하려고 한다.



  • 샘플1, loa.exe

 FileName

 loa.exe

 MD5

 B3677D1BA7DD15C842D0A0DA4778719A

 SHA-1

 C7C94FFA1DD05CF8F29E9B88FB034E58D4691609

 Pack

 VMProtect v.2.07

[표1] TLS callback을 사용하는 샘플 정보


해당 샘플은 TLS callback과 VMProtect를 사용한다.

 - TLS callback에 요점을 두고 진행 하였으므로 VMProtect의 안티 디버깅에 대해서는 언급하지 않고 진행함.




     A. PEview를 이용하여 직접 PE 오프셋 값을 구하는 법

     B. LordPE를 이용하여 Tls Table의 H버튼을 클릭하여 자동으로 오프셋 값을 구하는 법

     C. IDA를 이용하여 간단하게 구하는 법


다음과 같이 TLS callback의 함수 내용을 알아내는 방법이 세 가지 방법이 있다. 이 글을 보는 사람들의 기호에 맞게 사용하면 되지만, PE 공부 한다고 치고 A 루트로 쓸 예정이다. (B, C가 궁금하신 분들은 덧글이나 이메일로 연락 부탁드립니다.)



1-1. PE 오프셋 값을 구하여 TLS Table의 내용 확인 방법



  1. Data Directory 테이블에서 TLS 테이블의 RVA값을 확인한다. (RVA = 48028, Size=32Byte)



  2. 섹션 테이블에서 TLS 테이블이 속한 섹션을 확인하고 섹션의 오프셋 값을 확인한다.

     RVA=3B000, Pinter to Raw Data=1E00



  3. 다음과 같이 계산한다.
     TLS 테이블 오프셋 = TLS 테이블이 속한 섹션의 오프셋 

                              + (TLS 테이블의 RVA - TLS 테이블이속한 섹션의 RVA) 

     즉, 48028 - (3B000 + 1E00) = EE28


이런 계산 필요없이 간단하게 TLS Directory를 보고 Start Address of Raw Data의 offset을 보아도 된다.

A루트는 위와 같이 계산하여 TLS Table Offset을 구하면 된다.



Offset EE28부터 TLS Table의 Size인 32Byte만큼 계산하면 EE28~EE47(32Byte)까지가 TLS Table이다.





4Byte씩 끊어서 [Start Address of Raw Data], [End Address of Raw Data], [Address of Index], [Address of Callbacks]로 나뉩니다. 이 중에서도 [Address of Callback]의 Address를 보면 448040이다. 이제 TLS callback의 함수 내용을 보기 위해 처음에 구했던 TLS 테이블의 RVA는 48028이 였고 Offset = EE28임으로 448040의 Offset은 EE40.




Offset EE40은 00453645이므로 00453646부터 분석하면 된다.





1-2. Ollydbg Debugging opitons 설정

                      


[그림1,2] Debugging options 수정

시작하기 전 Debugging options의 Make first pause at을 수정해준다. 해당 옵션은 디버거의 설정을 건들지 않는 이상 2번째 [Entry point of main module]로 설정되어 있다. 또한, TLS callback을 사용하므로 EP보다 먼저 실행되기 때문에 [Entry point of main module]을 사용할 필요가 없다. 그러므로 [System breakpoint]를 선택해준다.




[그림3] 로드 시킨 후 assembly

RETN에서 시작되게 되는데 Ctrl+G를 눌러 1-1를 통해서 TLS callback fuction의 Address인 00453646에서 분석을 시작하면 된다.







1-3. 코드 분석

- 다른 어셈블리어들은 뒷전으로 해두고 간단하게 핵심 부분만 정리하였다.

0045363B   F73B             IDIV DWORD PTR DS:[EBX]

0045363D   50               PUSH EAX

0045363E   8B10             MOV EDX,DWORD PTR DS:[EAX]

00453640   FE               ???                                      ; Unknown command

00453641   68 40282BF1      PUSH F12B2840

00453646  ^E9 F977FFFF      JMP loa.0044AE44

0045364B   60               PUSHAD

0045364C   881424           MOV BYTE PTR SS:[ESP],DL

0045364F   E8 D559FFFF      CALL loa.00449029

00453654   8D6424 04        LEA ESP,DWORD PTR SS:[ESP+4]

00453658  ^0F83 D780FEFF    JNB loa.0043B735

TLS callback의 첫 시작이다.

0044AE44로 JMP시키는데 TLS callback fuction으로 넘어가는 것이다.


필요없는 내용은 넘어가고 IsDebuggerPresent()에 Breakpoint를 걸고 시작하였다.



5A48A0D3   33F6             XOR ESI,ESI

5A48A0D5   46               INC ESI

5A48A0D6   8935 701B4B5A    MOV DWORD PTR DS:[5A4B1B70],ESI

5A48A0DC   FF15 C011485A    CALL DWORD PTR DS:[5A4811C0]             ; kernel32.IsDebuggerPresent

5A48A0E2   85C0             TEST EAX,EAX

5A48A0E4   0F85 617F0100    JNZ 5A4A204B

5A48A0EA   5E               POP ESI

5A48A0EB  ^E9 42FDFFFF      JMP 5A489E32

5A48A0F0   90               NOP

5A48A0F1   90               NOP

IsDebuggerPresent()에 Breakpoint를 걸은 후 RET을 통해 백트레이싱 한 후 어셈블리어를 봤는데 대략적인 소스코드는 다음과 같다.



if(IsDebuggerPresent==0)

{

     JNZ 5A4A204B // 디버깅 되고 있지 않다면 5A4A204B에서 시작.

}

JMP 5A489E32 // 디버깅 되고 있다면 위 조건문을 실행하지않고 5A489E32로 JMP.

JMP 5A489E32 부분은 Terminated되겠고, 5A4A204B로 가면 악성 행위를 위해 Main으로 가게 된다.





디버깅하는 중이므로 IsDebuggerPresent() 실행 후 EAX의 값이 1이 되었다. 즉, 디버깅 중이라고 이야기 해주는 중이다.


해당 안티디버깅을 우회하려면 EAX나, EFLAGS의 ZF를 0으로 수정한다.

해당 안티 디버깅을 우회하려면 EFLAGS의 ZF를 수정하여 주거나, EAX를 수정해주면 된다. 필자의 경우, EAX가 1로 Set되어 0으로 바꿔주어 우회 하였다. 위와 같이 말이다.


IsDebuggerPresent API말고도, 다른 API를 사용하여 샌드박스를 탐지하는 경우가 있으므로, 꼭 IsDebuggerPresent만으로 안티 디버깅했다고 생각하진 말자.

Comments