Anti-Reversing Code [4/4]

문서 작성일 : 2008년 12월 19일
최종 수정일 : 2008년 12월 19일

Hong10님께서 작성하신 “Anti Reversing Code” 큰 주제를 바탕으로 4개로 나눠 작성하였습니다.

5.3 Hardware Break Point
이왕 인터럽터 나 SEH에 대해서 나온 만큼 브레이크 포인트에 대한 안티 리버싱 기술에 대하여 살펴 보겠습니다. 사실 위에서 언급한 Debugger interrupts만으로도 소프트 브레이크 포인터에 대한 탐지가 가능합니다. 물론 언급한 내용외에 코드를 체크하거나 체크섬을 검사하여 탐지를 할수 있습니다. 이번 장에서는 Hardware Break Point를 어떻게 탐지하며 우회를 할 것 인가에 대하여 다루겠습니다. 시작 하기에 앞서 먼저 디버그 레지스터리에 대한 이해가 필요합니다.

디버그 레지스터는 총 7개가 구성이 됩니다. (DR0~DR7) 또한 DR4와 DR5는 사용되 지 않고 있으며 DR0~DR3 까지는 브레이크 포인트가 걸린 주소값을 담는 용도 입니다. 그래서 하드웨어 브레이크 포인트 가 4개 밖에 존재 하지 않습니다.
이러한 하드웨어적인 방식은 DR0~DR3중 하나에 브레이크 포인트로 사용될 주소로 설정하여 준 후 DR7 레지스터에 브레이크가 발생하게 될 조건을 명시하게 됩니다. 좀더 중요한 DR7 레지스터리에 알아봅시다. 아래 출처에서 설명 되어 있어서 긁어 오듯이 설명 하겠습니다.

출처

http://slaxcore.tistory.com/entry/하드웨어-브레이크-포인트

-탐지

DR7은 Debug Control Register 입니다. 브레이크 포인터 타입 및 활성화 여부 브레이크 포인트 길이 등이 저장 됩니다. 아래는 위 그림에서 RW/0~RW/3 의 설정값.

다음 그림은 LEN0~LEN3에 설정되는 브레이크 포인터 Data에 대한 길이 값이 저장됩니다.
 

또한 (L0~L3,G0~G3)은 새로운 브레이크 포인터에 대한 설정값등이 설정됩니다. 앞서 SEH에 대하여 간략하게 설명드렸습니다. 좀더 깊게 이해 하기 위하여 정덕영님의 Windows 구조와 원리 p162에 나와 있는 글로써 인용을 하겠습니다.
구조화 된 예외 처리(Structed Exception Handling)

 프로세스가 메모리와 각종 자원에 대한 구별을 가진다면 스레드는 목적한 코드를 일정시간 동안 수행하는 실행 단위로 구별할 수 있으며,각각의 스레드가 가지는 독립적인 요소중 하나는 에러 핸들러이다.
각각의 스레드는 자신이 목적하는 코드를 수행하게 되며, 만약 이코드가 수행하던 중 예외적인 상황이 발생하게 되면 운영체제와 컴파일러는 이 예외적 상황을 처리할 수 있는 기회를 스레드에게 제공 하고 있다.C나 C++와 같은 컴파일러에서는 우리에게 _try,_except,catch,thro와 같은 키워드를 제공함으로써 예외적 상황에 대해 프로그래머가 처리할 수 있는 기회를 제공해 주고 있다. 하지만 여기서 간과해서는 안되는 일은 이는 컴파일만으로는 되지 않는다는 점이다.
즉, 어떠한 스레드에서 잘못된 메모리를 참조하는 것과 같은 행위를 하게 되면 마이크로프로세서에서는 예외가 발생하게 되며,이때 이 예외를 어떻게 처리하는지는 OS가 해주어야 하는 몫이다.Windows에서는 이러한 예외가 발생하면 그 예외를 발생시킨 스레드가 그에 대한 에러 처리를 할 수 있도록 해주고 있으며,이를 구조화된 예외처리라고 부른다.

간단한 SEH 처리 과정을 코드와 더불어 설명하겠습니다.
SimpleSEH.cpp

 #include <windows.h>
ULONG G_nValid;
EXCEPTION_DISPOSITION __cdecl except_handler( //예외 처리
 struct _EXCEPTION_RECORD *ExceptionRecord,
 void *EstablisherFrame,
 struct _CONTEXT *ContextRecord,
 void *DisatcherContext
 )
{
 ContextRecord->Eax = (ULONG)&G_nValid;
 return ExceptionContinueExecution;
}
int main(int argc,char *argv[])
{
 ULONG nHandler = (ULONG)except_handler;
 PCHAR pTest = (PCHAR)0;
 __asm //SEH 설치
 {
   push nHandler //에러 핸들링을 수행할 함수 주소
   push FS:[0]
   mov FS:[0],ESP
 }
 __asm
 {
   mov eax,0
   mov [eax],’a’ //SEH 발생 잘못된 메모리 참조
 }
 __asm //SEH 제거
 {
   mov eax,[ESP]
   mov FS:[0],EAX
   add esp,8
 }
 return 0;

위의 코드는 사용자가 만든 SEH핸들러에 대한 처리를 정의하고 새로운 SHE를 발생시키기 위하여 0의 주소값을 가리키게 하여 SHE를 발생 시키는 코드입니다. SEH설치 부분을 먼저 살펴 보겠습니다.

현재 ESP 값이 기존의 FS:[0]값으로 가리키게 됩니다.이과정은 우리가 직접 넣은 push 명령으로 넣은 데이터들은 실제 Windows에서 EXCEPTOIN_REGISTRATION_RECORD라는 구조체로 정의하고 있는 데이터입니다. 구조체의 정의는 이렇습니다.

 Typedef struct
EXCEPTIONREGISTRATIONRECORD
{
DWORD prev_structure;  //이전에 설치된 Exception handler
DWORD ExceptionHandler; //해당 에러 핸들러
}

이런식으로 싱글리스트 형태로 이전의 Exception handler가 다음의 Exception handler를 가리키게 됩니다. 다음으로는 에러 처리에 해당하는 코드를 살펴보겠습니다. 코드를 보면 에러가 발생한 시점의 Eax 값을 전역변수 G_nValid에 담아서 ExceptionContinueExcecution을 리턴하고 있습니다. 이값은 0값으로 VC헤더 파일인 EXCPT.H에 정의 되어 있습니다.

 Typedef enum _EXCEPTION_DISPOSITION{
ExceptionContinueExcution, //0
ExceptionContinueSearch, //1
ExceptionNestedException,//2
ExceptoinCollidedUnwind//3
}

이러한 값은 악성 코드들에 많이 이용 됩니다. 보통 악성코드들은 앞서 살펴본 디버거 레지스터리 값을 바꿀 때 사용되어 지는 ContextRecord 을 이용하여 디버거 레지스트리의 값을 바꾸어 여러가지에 응용하며 또한 ExceptionContinuExecution(0) 값을 리턴함으로써 에러가 발생할 당시의 Register을 바꾸어 줄 수 있는데 보통  EIP 값을 수정하여 에러가 발생한 시점에 악성코드를 실행한 뒤(exception handler) EIP를 수정하여 보통의 OEP를 가장하는 기법을 이용합니다.
(실제로 디버거를 이용하여 디버깅을 할 때 OEP) 다음은 구조체 CONTEXT의 일부인 디버거 레지스터리(Context Record 구조체중 디버거 레지스터리)를 정의 하고 있는 부분입니다.

Hardware Break Point 탐지에 필요한 개념을 이해 하였으면 아래의 코드로 어떻게 탐지를 할수 있는지 애기를 해보겠습니다.

 .386
      .model flat, stdcall
      option casemap :none   ; case sensitive
      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc
      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib
    .data
       KoreaSecurity db “Korea Security”,0h
       Protect db “보호하고 싶은가?”,0h
       DbgNotFoundTitle db “Debugger status:”,0h
       DbgFoundTitle db “Debugger status:”,0h
       DbgNotFoundText db “Debugger hardware bpx not found!”,0h
       DbgFoundText db “Debugger hardware bpx found!”,0h
    .data?
OrgEbp   dd ?
OrgEsp   dd ?
SaveEip  dd ?
    .code
start:
; Setup SEH
MOV EAX,offset @Exit
MOV DWORD PTR[OrgEbp],EAX ;EAX에 @Exit함수주소를 담고 있음
MOV DWORD PTR[SaveEip],EBP; 현재 EBP를 SaveEip라고 담고 있음.
ASSUME FS : NOTHING
PUSH offset @DetectHardwareBPX ;Exception handler
PUSH FS:[0]
MOV DWORD PTR[OrgEsp],ESP ;현재 스택을 OrgEsp에 담고 있음.
MOV  FS:[0], ESP  ; 위에서 설명한 과정
; Fire SEH
XOR EAX,EAX ;EAX값을 0 초기화
XCHG DWORD PTR DS:[EAX],EAX ;0이라는 주소값 참조 exception발생
CALL @Protected;Hardware Break Point 로부터 보호 하고 싶은 영역
@Exit:
POP FS:[0]
ADD ESP,4
PUSH 0
CALL ExitProcess
@Protected:
  PUSH 30h
  PUSH offset KoreaSecurity
  PUSH offset Protect
  PUSH 0
  CALL MessageBox
@DetectHardwareBPX:
PUSH EBP;핸들러가 발생할 때 의 처리
MOV EBP,ESP
MOV EAX,DWORD PTR SS:[EBP+10h] ;Context Record값 세번째인자
; Restore ESP, EBP, EIP
MOV EBX,DWORD PTR[OrgEbp] ;@exit 함수 주소
MOV DWORD PTR DS:[EAX+0B8h],EBX;Context Record의 Ebp값
MOV EBX,DWORD PTR[OrgEsp];fs[0]과 esp가 바뀌기전의 esp값
MOV DWORD PTR DS:[EAX+0C4h],EBX;Context Record의 esp값
MOV EBX,DWORD PTR[SaveEip];
MOV DWORD PTR DS:[EAX+0B4h],EBX;context Record의 eip값
; Check DRx registers
CMP DWORD PTR DS:[EAX+4h],0
JNE @hardware_bpx_found
CMP DWORD PTR DS:[EAX+8h],0
JNE @hardware_bpx_found
CMP DWORD PTR DS:[EAX+0Ch],0
JNE @hardware_bpx_found
CMP DWORD PTR DS:[EAX+10h],0
JNE @hardware_bpx_found
PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox
  @hbpx_exit:
MOV EAX,0 ;위에서 설명한 ExceptionContinuExecution(0) 값
LEAVE
RET
  @hardware_bpx_found:
PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox
JMP @hbpx_exit
end start

다소 이때까지 본 코드보다는 길어 보이지만 복잡하지는 않다.앞전에 설명한대로 SHE를 설치하고 SEH처리에서는 디버거 레지스터리를 검사하며 Context Record값중 ebp,esp,eip를 변경하여 다시금 변경된 레지스티리 값을 복구하는 과정이다.
이를 올리로 본 화면은 이렇다. 아래 그림에서 보다시피 보호 하고 싶은 영역에 하드웨어 브레이크 포인트를 설정하였다. 확인하는 방법은 올리에서 Debug  Hardware Breakpoints를 선택하면 된다.


5.4 Hardware Break Point 우회
앞선 설명을 다 이해 했다면 우회하는 방법은 간단합니다.디버깅 체크루틴에서 강제적으로 디버거 레지스터리 값을 0으로 만들어 주거나 혹은 Exception을 유발 시키는 코드에 대해서 NOP을 처리해 해당 루틴이 실행하지 않도록 합니다.혹은 올리 플러그인중 PhantOm 이라는 것을 이용해 Drx 체크옵션을 주면 우회(PhantOm 플러그인을 이용한 우회)를 합니다.

다음은 Protect 영역에 Hardware break Point 부분입니다.

F9를 눌러 run을 하여 보자. Hardware break Point 발견을 한 부분 입니다.
 

6.1 Garbage Code & JunkCode
지금부터 설명한 안티 리버싱 방법은 앞선 기술과 달리 리버서들을 심리적으로 짜증나고 지치게 만드는 기법입니다. 단지 보호 될 코드를 분석할 때 시간을 끄는 용도로 사용됩니다. 두 개념은 다소 다른 의미를 지니고 있습니다. 먼저 Garbage코드는 코드중간에 의미없는 코드들을 집어 넣어 리버서들의 집중력(?) 과 혼란을 가중 시켜 버리며 Junkcode는 가령 디버거는 코드를 디어셈 할 때 정의된 OPCODE에 의해 디어셈을 화면에 DISPLAY합니다. 그
것을 착안하여 실행하지 않도록 하는 OPCODE를 중간에 넣으므로써 전혀 엉뚱한 디어셈 코드를 출력함으로써 리버서를 방해 합니다. 앞서 작성한 SHE를 유발 시키는 코드를 보호될 코드 영역으로 정하고 Garbage 코드를 삽입 해 올리로 살펴보겠습니다.

 보호되어야 할 코드
XOR EAX,EAX
XCHG DWORD PTR DS:[EAX],EAX

위와 같은 코드가 보호 되어야 할 것이라면 중간에 Garbage코드(프로그램에 상관없이 의미 없는 코드)를 삽입 합니다.

 Garbage코드 삽입
PUSH EAX
MOV EBX,3
POP EAX
SUB EBX,3
XOR EAX,EAX
MOV EAX,0DEADh
SHR EAX,4
XCHG DWORD PTR DS:[EAX],EAX

간단하게 적용한 모습입니다. 물론 저정도 코드는 중간에 삽입된게 의미가 없다는걸 한눈에 알아 볼수 있지만 이러한 코드들이 무더기로 중간중간에 삽입이 되어 있다면 골치가 아플 것입니다.

다음은 ollydbg에서 Garbage코드가 삽입된 화면을 보여주고 있습니다.

다음은 Junk Code를 삽입 하여 보겠습니다. 여기에 해당하는 내용은 제스리버님의 홈페이지의 테스트를 참고로 하였습니다. 코드는 아래와 같습니다.

 #include <windows.h>
int main(int argc,char *argv[])
{
 __asm
 {
  jmp here+1;
  here:
   __emit 0xe9//__emit은 뒤에 바이트를 코드에 포함
  mov eax,1
 }
 return 0;
}

코드를 보면 중간에 박아서 디버거가 제대로된 코드를 뿌려주지 못하도록 하고 있습니다.

다음은 ollydbg로 junk code 삽입된것을 확인한 화면입니다.

MOV EAX,1 디어셈 코드는 온데 간데 보이질 않고 덩그러니 JMP코드가 자리 잡고 있습니다. 그런데 JMP하는 주소값이 이상하네요.머 저정도야 금방 눈치채겠지만 그래도 모르는 상태에서 본다면 다소 의아해 할 것 입니다. 해당 옵코드 0xe9 를 0x90(NOP)으로 처리한다면 원래 OP CODE인 mov eax,1을 복원 할 수 있습니다. 다음은 해당 코드를 복원 한 모습입니다. NOP으로 바꾼뒤 올리에서 Ctrl+a 혹은 마우스 우클릭시 Analysis  Alnalse code 을 클릭하면 OPCODE를 재분석 하게 됩니다.

다음은 Junk Code 패치한 화면입니다. 

여기까지 “Anti-Reversing Code” 에 대한 설명을 마치겠습니다.^^)

참고사이트 및 문서
EDIT PLUS 를 이용한 MASM 환경 구축
http://mysilpir.net/entry/EditPlus-Assembly-%EC%84%A4%EC%A0%95-MASM
ANTI REVERSE
http://zesrever.xstone.org/
http://slaxcore.tistory.com
http://beist.org/research/public/artofunpacking/artofunpacking.pdf
http://openrce.org
정덕영님의 윈도우 구조와 원리
THX to zersrever,slaxcore,ashine,ap0x,정덕영FROM Hong10

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤