-榮-

Advanced 안티 디버깅 본문

리버싱 핵심원리

Advanced 안티 디버깅

xii.xxv 2024. 1. 10. 15:29

1. 가비지 코드

의미 없는 코드를 대량으로 추가시켜 디버깅을 힘들게 만드는 기법

중간 중간에 진짜 의미 있는 고드를 섞어 버리거나 다른 안티 디버깅 기법을 혼합해서 사용하면 디버깅이 매우 힘들다.

정교하고 복잡한 형태의 가비지 코드는 수많은 조건 분기와 끝없이 이어지는 함수 호출로 인하여 건너뛰기도 쉽지 않다.

 


 

2. Breaking Code Alignment

IA32-Instruction을 잘 이해한 상태에서 정교하게 어셈블리 프로그래밍을 하면, 디버거의 디스어셈블 과정에 혼란을 줄 수 있다. 

즉, 디스어셈블리 코드가 깨진 것처럼 보인다.

코드 사이에 (정교하게 계산된) 불필요한 코드를 삽입시켜 디스어셈블리 코드의 가독성을 떨어뜨리는 것이 ‘Breaking Code Alignment’ 기법

사람이 파악하기 어렵게 뒤죽박죽 섞어 놓은 코드를 난도화 코드(Obfuscated Code)

가비지 코드와 Breaking Code Alignment 기법을 잘 조합하여 사용하면 훌륭한 Obfuscated Code를 생성할 수 있다. 

 


 

3. Encryption / Decryption

암호화/복호화 기법은 프로그램의 코드와 데이터를 숨기기 위해 패커/프로텍터에서 자주 사용되는 기법

정상적인 코드를 암호화시키는 행위를 인코딩(Encoding), 암호화된 코드를 복호화시키는 행위를 디코딩(Decoding)이라고 한다.

 

-1. 간단한 디코딩 코드

디코딩의 경우 프로그램 내에서 복호화 과정을 완료해야 정산적인 코드가 나타난다.

안티 덤프 기법의 하나로써 복호화한 정상 코드를 실행시킨 이후에 다시 암호화하는 기법이 있다. 실행 중인 프로세스의 메모리 코드를 덤프해도 암호화된 코드만 보이게 된다.

 

-2. 복잡한 디코딩 코드

복잡한 디코딩 코드의 경우 가비지 코드가 포함되는 등의 형태로 복잡한 디코딩 코드가 이루어진다.

트레이싱을 하다 보면 패턴이 눈에 들어오고 이 코드가 디코딩 루프라는 것을 알게 되면서 유효 명령어를 뽑아낼 수 있다.

 

-3. 특수한 경우 - 코드 재조합

어떤 프로텍터들은 코드의 가독성을 떨어뜨리고 트레이싱을 어렵게 하기 위해서 실행코드를 실시간으로 재조합하는 경우가 있다.

코드 실행 직전 코드에서 레지스리에 접근하여 SUB/ADD/XOR 등의 명령어를 사용하여 이후의 코드를 변경한다. 변경된 코드. 즉, 새로운 코드를 재생성하여 실행한다.

다른 장점은 사용자가 만약 디코딩되는 위치에 software BP를 설치했을 때, 실행 에러를 발생킨다. (BP가 설치된 영역은 CC로 패치가 되어 전혀 다른 계산 결과가 나오기 때문 - OllyDbg의 경우, Software BP가 설치된 주소의 내용을 보호해서 새로운 값으로 쓰지 못하게 한다.)

 


 

4. Stolen Bytes(Remove OEP)

원본 코드의 일부(주로 OEP코드)를 패커/프로텍터가 생성한 메모리 영역으로 옮겨서 실행

장점은 프로세스 메모리 덤프 시 OEP 코드의 일부분이 없어 정상 실행 불가(Anti-Dump 기법)

Stolen Bytes가 적용된 파일을 다른 패커/프로텍터로 압축 시 리버서에게 혼란을 준다. (언팩의 성공여부를 의심하게 됨)

프로텍터의 종류에 따라 다양한 형태의 실행이 존재한다. Stolen Bytes 실행 후 메모리에서 제거하는 경우도 있다.(메모리 할당 - Stolen Bytes 코드 복호화 - 실행 - 메모리 해제)

 


 

5. API 리다이렉션(Redirection)

디버깅 시 흐름을 대략적으로 빨리 파악할 수 있는 좋은 방법은 주요 Win32 API(파일, 레지스트리, 프로세스, 네트워크 등)에 BP를 설치하는 것

BP 설치 후 실행하면 BP를 설치한 API가 호출된 후 멈춘다. 이때 스택에는 리턴주소가 저장되어 그 주소부터 디버깅을 하면 효과적으로 핵심 코드에 다가갈 수 있다.

이런 디버깅 노하우를 회피하는 기법이 API Redirection이다.

프로텍터는 주요 API들의 전체코드 혹은 일부를 다른 메모리 영역으로 복사 그리고 보호 대상 프로그램의 코드를 분석하여 해당 API를 호출하는 코드를 패치시켜 자신이 복사해둔 API코드가 실행되도록 한다. 따라서 원본 API 주소에 BP를 설치해도 소용이 없다.(추가적인 Anti-Dump기법)

 - 다른 메모리로 복사해 둔 코드는 같은 결국 같은 API를 가리킨다.(dll파일의 API 주소를 가리킴)

또한 Opcode ‘FF15’로 간접호출을 하던 원래완 다르게 ‘8E’로 직접호출을 하면서 남은 1바이트가 뒤로 밀리게 된다.(Breaking Code Alignment 기법이 이루어짐)

※ ASProtect의 난독화 코드 생성기에 의해서 그때그때 새로운 가비지 코드가 생성돼서 주소로 이동할 때마다 코드의 모양이 달라진다. 이렇게 같은 결과를 내는 다른 모양의 코드를 다형성 코드(Polymorphic Code)라고 한다.

API Redirction 기법은 구조상 API Hooking과 비슷한 면이 많다. 둘의 차이점은 목적. API Redirection은 디버깅을 어렵게 만드는 것이 목적이고, API 후킹은 API 호출 전/후에 추가적인 기능을 제공하는 것이 목적

 


 

6. Debug Blocker(Self Debugging)

자신을 디버깅 모드로 실행시키는 기법

동일한 프로세스를 부모/자식 구조로 실행하는 것으로 디버거/디버기 관계가 되는 것

파일이 실행되면 자신의 실행 파일을 찾아서 DEBUG 모드로 실행

자기 자신을 자식 프로세스로 실행시키는 Self Creating 기법의 발전 형태.
Self Creating 기법에서 실제 원본 코드의 실행은 자식 프로세스에서 담당, 부모 프로세스는 자식 프로세스의 생성, 메모리(코드/데이터) 변경, EP 주소 변경 등의 작업을 수행
  → 부모 프로세스만을 디버깅해서는 OEP로 갈 수 없다.
  → 디버거를 자식 프로세스에 Attach시키는 디버깅 방법으로 무력화된다는 단점이 있으며, 이 단점을 보완한 것이 Debug Blocker 기법이다.

Debug Blocker 기법의 장점은 두 가지(목적 관점에서)

1. 디버깅 방지

  • 실제 원본 코드를 실행하는 자식 프로세는 이미 디버깅 중이므로 원칙적으로 다른 디버거를 Attach할 수 없다. (디버깅하는 방법은 디버깅 실습 참고)

2. 자식(Debuggee) 프로세스를 제어(Control) 할 수 있다.

  • 디버거-디버기 관계에서 디버거는 막강한 권한을 가지고 있다. 즉, 디버기 프로세스의 예외를 처리하고 실행 흐름을 제어하는 등의 작업을 수행할 수 있다. 이로 인해 디버깅이 까다로워진다.
  • → 디버기에 발생항 예외에 대한 우선 처리권은 디버거에게 있다. 따라서 자식(Debuggee) 프로세스를 디버깅하기 위해서는 기존 디버거의 연결을 끊어야 하는데, 그러면 자식 프로세스는 정상적인 실행이 불가능해진다.
Debug Blocker 기법에서 발전된 ‘Nanomite’ 기법
1. 디버기 프로세스의 내부 코드에서 Jcc 명령어를 전부 INT3(0xCC) 명령어(SWBP) 또는 기타 예외를 발생시키는 코드로 패치한다.
2. 패치시킨 Jcc 명령어의 실제 주소 위치와 점프할 주소를 디버거 내부에 테이블 형태로 가지고 있다가 기버기에서 패치된 명령이 실행되면 예외가 발생하여 디버거에게 제어권이 넘어오면, 예외가 발생한 주소를 이용해 테이블에서 점프해야 할 주소를 얻어서 디버기에게 알려준다.
 
‘Nanomite’ 기법을 제대로 디버깅하기 위해서는 패치된 코드를 원본과 똑같이 복구시켜야 된다.
일일이 수작업하기에는 너무 힘들기에 복구 작업의 많은 부분을 자동화해야 된다. 어느 정도의 프로그래밍 센스와 노하우가 필요하며 따라서 디버깅 난이도가 매우 높다.