-
eild(강신일)'s 안티 디버깅의 종류Team $!9N 구성원의 글/Reversing 2020. 1. 20. 22:44
1. 머리말
안녕하세요?
오늘은 다양한 안티 리버싱 기법들에 대해서 포스팅하려 합니다.
프로그램 개발자들은 자신의 개발 기술이 그대로 드러나게 두고 싶어 하지 않습니다.
그래서 리버싱을 막기 위한 기법들이 많이 존재합니다.
오늘은 그 기법들에 대해서 살펴보겠습니다.
(윈도우 PE 파일에 해당하는 내용입니다.)
2. 패킹
준비물
사용 프로그램 : UPX, ollydbg, peviwer
실습 프로그램 : codeengn basic 4
패킹이란?
패킹은 pe 파일의 데이터 압축과 프로택팅을 목적으로 사용됩니다.
packing이라는 뜻답게 선물을 포장하듯 프로그램을 밖에서 한 번에 알아차리지 못하게 포장해서 보관하는 특성이 있습니다.
이렇게 파일을 패킹하게 되면 oep를 찾기가 힘들어집니다.
(oep란 original entry point로 실제 프로그램이 시작하는 지점을 말합니다.)
그래서 보통 디버거를 통해 프로그램을 열면
이런 식으로 oep를 찾기 쉬운 구조입니다.
이런 식으로 육안으로 보기 쉽게 코드가 그대로 나와있다면 금방 oep에 접근할 수 있습니다.
코드가 길지 않음으로 한 줄 한 줄 f8 만 눌러봐도 프로그램이 동작하는 곳을 발견하기 쉬우므로 이렇게 쉽게 oep에 접근할 수 있습니다.
그리고 peviwer로 파일을 보면 IAT가 손쉽게 확인이 되어 어떠한 AIP를 사용하는지도 한눈에 들어오게 됩니다.
전체적인 PE 구조가 한눈에 들어오는 것은 물론이며
이러한 IAT 목록을 확인함으로써 사용되는 API를 한눈에 볼 수가 있습니다.
하지만 upx 같은 툴로 패킹을 진행하게 되면 전혀 다른 모습으로 보이게 됩니다.
upx 경로로 이동하여 upx -1 [파일명]을 입력해줍시다.
패킹이 되고 file size가 줄었습니다.
그럼 이제 어떻게 변화가 되었는지 확인해보도록 하겠습니다.
먼저 peviwer로 보면
section이 전부 upx 패킹이 되었습니다.
그로 인하여 iat를 살펴봐도
IAT에 사용되는 API 목록들이 가려진 것을 확인할 수가 있습니다.
또 디버거로 열어도 OEP를 찾기 난해해진 모습을 확인할 수 있습니다.
누가 봐도 분석하기 싫게끔 변환된 것을 확인할 수 있습니다.
보통 패킹이 진행되면 PUSHAD가 눈에 띄게 됩니다.
그래서 어떠한 패킹은 보통 같은 툴로 패킹과 언패킹이 가능하며 어떠한 패킹을 진행했는지 모를 경우는 분석이 어려워집니다.
(UPX 패킹 같은 경우는 수동으로 반복문을 실행시키어 풀거나 UPX 툴의 -d 옵션으로 풀어 줄 수 있습니다.)
어떤 패킹이 된 지 확인하는 방법
PEiD라는 도구를 이용하면 어떠한 종류의 패커로 패킹이 되어있는지를 확인할 수가 있습니다.
패커 종류 외에도 다양한 정보를 확인할 수 있어서 매우 편리합니다.
3. Static & Dynamic 안티 디버깅
Static 안티 디버깅
Static 안티 디버깅이란 정적인 방법으로 디버깅을 막는 시스템으로 보통 한 번만 우회해주면 넘어갈 수가 있습니다.
Dynamic 한 방법보다는 비교적 쉬운 이유도 1번만 우회하면 된다는 것이 큽니다.
보통 디버거를 사용했는지를 탐지하기 위하여 사용되며 API 후킹이나 디버거 자체 플러그인을 사용하여 우회합니다.
Static 기법에는 많은 종류가 존재합니다.
PEB(Process environment Block)을 이용한 방법들, TEB(Thread Environment Block)을 이용한 방법, 각종 API를 이용한 방법이 있습니다. 그 이외에도 Entry Point 보다 먼저 실행되는 특징을 이용한 TLS Callback 함수를 이용한다든지 Targeting 등.. 너무 많아서 이번 글에 다 담지 못하고 한 가지 예만 보고 넘어가겠습니다.
아까 패킹에서 예시로 사용된 코드엔진 basic4번 문제도 사실 Static 안티 디버깅을 사용하고 있는 파일입니다.
일반 실행 시 아래 사진과 같이 정상이라고 프린트문을 보내고 있는 파일입니다.
하지만 디버깅을 시도하면 정상 실행되지 않고 디버깅 모드에서만 사용되는 알고리즘으로 빠지게 되어 정상적인 디버깅이 불가능합니다.
그 이유는 IsDebuggerPresent()라는 PEB를 참고하는 API 때문입니다.
Dynamic 안티 디버깅
Dynamic 안티 디버깅은 Static 보다 훨씬 번거로우며 우회가 더 까다롭습니다.
Static과 비슷하게 API hooking이나 디버거 플러그인을 사용해서 우회할 수 있으며 디버거 탐지를 통해 막는 static 과는 다르게 내부 코드와 데이터를 숨기기 위해 디버깅 도중에 수시로 나와서 좀 더 까다롭습니다.
SEH를 사용하거나 디버거의 특징을 역이용해서 INT 3등을 써서 Break Points를 이용한 방법, 동적 디버거 특성상 비정상적으로 프로그램을 실행함에 있어서 시간이 흘러가게 됩니다. 그래서 그런 시간 값을 이용한 여러 가지 Timing Check 기법들, TF 값을 1로 조작하여 CPU를 Single Step 모드로 전환시켜서 EXCEPTIONSINGLESTEP 예외를 발생시키는 방법도 있습니다.
또는 자주 디버깅을 위해 분석하는 API의 첫 바이트 코드가 CC(Break Point) 일 때 디버깅을 탐지하는 방법도 있습니다.
4. 난독화
앞에서 알아본 Static & Dynamic 안티 디버깅이란 것도 있지만 이번 난독화 파트만큼은 사실 정해진 형식이 없습니다.
말 그대로 난독화의 의미를 갖고 있는 코드들은 실제 사용되는 코드가 보기 어렵고 복잡하게 짜여 있는 것을 말합니다.
그래서 읽기 어렵게 만든다는 의미로 난독화라고 불립니다.
(그리고 이 파트에 실제 사용되지 않는 코드들이지만 리버서를 실제 코드를 분석하고 있는 것처럼 착각하게 만드는 쓰레기 더미 코드들도 포함시켜서 포스팅하도록 하겠습니다.)
이렇게 일부로 해석하기 힘들게 노리고 만드는 코드들을 가비지 코드(Garbage Code)라고 합니다.
JMP 703B7을
MOV EAX, 17
IMUL EAX, B52
MOV EAX, 60000
MOV ECX, EAX
SUB ECX, A7
JMP [ECX]
처럼 괜히 길게 적어두거나, push & pop 혹은 xor 등을 이용해서 얼핏 보면 레지스트리 값을 복잡하게 연산하는 듯 보이게 착각하게 만들지만 실행 결과는 아무런 레지스트리 값의 변동이 없는 등에 해석하는데 시간만 버리는 코드들을 일부로 넣어서 리버서들을 지치게 만드는 방법입니다.
5. 맺음말
오늘 포스팅은 사실 windows 운영체제 위주의 안티 디버깅 종류에 대해서 알아봤습니다.
하지만 원론적인 방법은 리눅스나 윈도우나 별반 차이가 없습니다.
결국은 디버깅 툴을 탐지해서 예외 처리를 시켜버리던지 실제 코드를 찾기 힘든 곳에 숨겨두거나 직관적으로 시스템의 흐름을 확인할 수 없게 난독화를 시켜버리는 등의 일들은 마찬가지로 일어나기 때문입니다.
그럼 오늘의 포스팅을 마치겠습니다.
'Team $!9N 구성원의 글 > Reversing' 카테고리의 다른 글
eild(강신일)'s 간단한 어셈블리어 디컴파일 (0) 2019.11.27