윈도우 API&메뉴얼 언패킹&안티디버깅 정리(CodeEngn)
1. CodeEngn Basic RCE 문제에서 사용된 윈도우 API 정리해서 write-up 작성
-> API의 역할(또는 기능), 인자값, 반환값을 중심으로 작성할 것
2. 문제 내에서 패킹된 프로그램을 메뉴얼 언패킹(수동 언패킹)으로 다시 해보기
-> UPX로 패킹된 프로그램 1개, AsPack 으로 패킹된 프로그램 1개
-> 패킹 개념, 패커 종류, 패킹하는 이유도 함께 write-up 에 작성
3. 안티 디버깅 기법 5개 조사해서 정리
순 서
CodeEngn Basic RCE 문제(윈도우 API 정리 & UPX/ASPack언패킹)
◇ 문제별 윈도우 API(Ctrl+F)
- GetDriveTypeA
- vbaStrCmp, vbaStrComp
- SetConsoleTextAttribute
- timeGetTime
- CreateFileA, ReadFile
◇ 공통 윈도우 API
- Sleep
- MessageBox
패킹
◇ 패킹 개념/패커 종류/패킹 사용 이유
안티디버깅
◇ 5가지 기법
CodeEngn Basic RCE 문제(윈도우 API 정리 & UPX/ASPack 언패킹)
Base RCE L01(GetDriveTypeA)
사용된 윈도우 API : GetDriveTypeA
○ GetDriveTypeA
RootPathName 은 드라이브의 루트 디렉토리의 이름을 가져오는 거고 매개변수가 없을 시에 현재 디렉토리의 루트 디렉토리를 가져온다.
함수를 호출하기 이전에! 00402094 주소에서 RootPathName 으로 정의된 "문자열(c:\)" 을 가져온다.
그 뒤에 드라이브의 종류를 확인한뒤에 EAX에 결과를 반환한다.
GetDriveTypeA 함수의 호출 이후 레지스터의 변화이다.
CD-ROM 으로 인식하기 위해선
'5' 로 리턴값이 나와야 함을 알 수 있다.
근데 이게 만약에 CD-ROM 반환값이라도 CMP 분기 조작이나 값 수정이 아니라면
성공 메시지를 볼 수 없긴 하다.
Base RCE L02(패킹 X, Win API X)
Basic RCE L03(__vbaStrCmp)
004028BA : PUSH DWORD PTR SS : [EBP-58]
004028BD : PUSH 03.00401DDC
004028C2 : CALL <JMP.&MSVBVM50.__vbaStrCmp>
이 부분에서 입력 한 값과 키값으로 보이는 문자열 "2G83G35Hs2" 를 밀어넣고 vbaStrCmp 함수를 호출하여 비교한다.
입력 값이 들어가 있음을 볼 수 있다.
함수 호출 전 / 호출 후(임의 값) / 호출 후(플래그)
비교 문자열이 서로 다름 → 1
비교 문자열이 서로 같음 → 0
으로 반환되는 것을 확인할 수 있다.
그리고 또 재미난게
vbaStrcmp 와 vbaStrcomp 다른 건줄 알았는데,
함수 호출 따라가 보니까 저렇게 다시 StrComp 함수를 호출한다 ㅋㅋㅋㅋㅋ.(함수 인자로도 스택 프레임에 쌓은거 그대로 넣고)
StrComp 함수 원형을 보면
StrComp(string1, string2 [,compare])
로 쓰이고 [,compare] 부분은 비교 옵션인데 아래와 같다.
Basic RCE L04(패킹 X, IsDebuggerPresent)
프로그램 실행 화면
Sleep 함수는 공통 윈도우 API 부분에서 설명할거라 생략하고
IsDebuggerPresent 함수를 보면
(함수)IsDebuggerPresent : Debugger 실행 유무를 체크하고 실행 중일때 eax 값이 '1'로 반환되어 나옴
함수의 원형은 BOOL 함수로 디버깅의 유/무 를 판단해서 1 or 0 결과 값이 나오는걸 확인 할 수 있다.
함수의 원리가 궁금해져서 찾아봤는데 오... 알아야될 내용들이 많다.
MOV EAX, DWORD PTR FS:[30] :
MOVZX EAX, BYTE PTR DS:[EAX+2] :
이 두개 명령어가 다인데 여기서 중요한게.
PTR FS:[30] 의 의미이다.
(32bit 기준으로) FS 세그먼트 레지스터는 일반적으로 TEB(Thread Environment Block) 이라고 하는 구조체를 가리키고,
이 구조체는 현재 실행중인 스레드에 대한 정보를 담고 있다. API 함수를 호출 하지 않고 정보를 얻기 위한 상태창 정도로 생각하면 된다.
그리고 TED 의 0x30 은 PEB(Process Environment Block) 을 가리키는데
PEB 구조체 정보는 아래와 같다.
여기서 EAX+2로 BeingDebugged 변수에 접근하여 값을 확인한다.
(MOVZX 는 MOV 명령어와 다르게 값 데이터 형태가 일치하지 않아도 집어넣고 빈공간을 0 으로 채워서 일단 넣는다.)
* 다른 방식으로 디버깅의 동작 유무를 확인할 줄 알았는데, 프로그램 실행 때부터 정해지는 프로세스의 정보로 확인할줄은 몰랐다. 신기하다.
Basic RCE L05(UPX 패킹, Win API X)
UPX 패킹 실습을 이 문제로 다시 진행해보겠다.
명령어 | Operand | 설명 |
PUSHAD | PUSHAD | EAX → ECX → EBX → ESP → EBP → ESI → EDI 레지스터 순으로 스택에 PUSH 한다. (전체 레지스터 백업용) |
POPAD | POPAD | 스택에 들어있는 값을 레지스터들(EAX, ECX, EBX...)로 POP한다. |
PUAHAD/POPAD 동작을 확인할 수 있고,
referenced String 전체가 알 수 없는 문자들로 덮혀 있었다.
정확한 패킹 정보를 알아보기 위해 정적 분석용 툴 PEID 를 사용하였다.
* PEID : 파일 정보 및 패킹 유형 확인
화면을 보면 UPX 형식으로 패킹 되어있다는걸 알 수 있고, 언패킹 하는 과정을 거쳐야 되는데.
수동 언패킹 과정으로 진행을 해보겠다.
실행 압축/해제를 하는 UPX의 형태는
PUSHAD 로 레지스터의 현재 상태를 모두 저장, POPAD로 레지스터 값 복원 이후 압축 해제된 프로그램의 OEP에서 다시 돌아가게 된다.
'레지스터 값을 복원시킨 뒤 실행하게 된다' 만 기억하고 넘어가자.
이런식으로 POPAD 이후에 JMP 문을 볼수 있는데 들어가보면 프로그램의 시작부분을 찾을 수 있다.
수동 언패킹의 필요한 준비물은 Ollydbg, PEID, Ollydump, LordPE 를 준비한다.
시작점을 찍고 OllyDump를 사용하여 돌려본다.
여기서 시작점을 어떻게 찾아요! 하는 의문이 들 수 있는데,
1. PUSHAD - 패킹 코드 - POPAD 의 형태라서 PUSHAD 아래로 내려서 찾던지,
2. ESP 레지스터에 [Follow Dump] → [Hardware, on acess] 로 ESP 의 접근 이 바뀌는 시점을 BP 걸어서 찾아도 된다.
(해보니까 2번이 편하다.)
OllyDump 로 생성한 파일을 LordPE로 다시 빌드하고 나면, 아래처럼 언패킹된 문자열을 확인 할 수 있다.
(LordPE를 하는 이유는 이건 돌아가는 프로그램의 주소부분까지 중간에 싹둑! 하고 편집해버린 거기 때문에 재조정 과정이 필요하다. 그래서 재 빌드를 해줘야 정상적으로 돌아간다. 안하면 실행주소가 이상하다고 튕긴다.)
Basic RCE L06(UPX 패킹(생략), Win API X)
Basic RCE L07(패킹 X, GetVolumeInformation, IstrcatA, IstrcmpiA)
참조 문자열에 성공 했을때로 보이는 곳을 따라 들어가본다.
이번 문제에 쓰인 윈도우 API 들은 3가지다.
GetVolumeInformationA : 지정된 루트 디렉토리와 연결된 파일 시스템 및 볼륨을 검색한다.
IstrcatA : 한 문자열(IpString1)에 다른 문자열(IpString2)을 추가한다.
IstrcmpiA : 두 개의 문자열을 비교한다.
GetVolumn 을 지나고 나서 Windows 가 찍힌걸 확인 할 수 있다.(내 루트 디렉토리)
0040225C 가 GetVolumeInformationA 함수를 보면 VolumeNameBuffer 부분인데 C드라이브의 이름이 들어가 있는걸 볼 수 있다.
IstracatA 함수 까지 거치면 추가된 문자열이 적힌 VolnumeNameBuffe의 주소가! EAX에 들어간걸 확인할 수 있다.
이후 IstracmpiA 를 통해서 인자 string1, string2 의 값을 비교해
값을 같으면 0, 크거나 작으면 양수/음수를 반환한다.
Basic RCE L08(UPX 패킹(생략), Win API : X)
Basic RCE L09(UPX 패킹, Win API X)
UPX 패킹 중에서 StolenByte 라는 개념이 추가로 들어간 문제라서 이것도 추가로 정리하면 좋을거 같아 넣었다.
StolenByte
안티 디버깅 기법중 하나로 UPX 패킹된 프로그램에서 원본 코드의 일부를 가져와
OEP로 건너뛰어 실행되기 이전에 숨긴다.
* 그대로 OllyDump 를 통해서 JMP문 이후의 부분을 편집하면 원본 코드의 일부가 유실된 상태로 만들어 지기 때문에 정상적인 실행이 되지 않는다.
그래서 OEP를 찾아 Dump&Build 과정을 거친 프로그램을 실행시키면
위와 같이 정상 실행이 안된다.
나중에 MessageBox 함수를 같이 보겠지만 코드를 보면 POPAD 이후에
PUSH 0 / PUSH 09.00402000 / PUSH 09.00402012 으로 MessageBox 함수의 인자값을 넣어 줌을 알 수 있다.
언패킹한 09.1exe 파일의 OEP 위에(새로 언패킹/빌드 한 프로그램은 OEP 기준으로 실행이 되게 짰으니)
빠져있는 3개의 인자를 넣어 주면 된다.
1) 나중에 값을 위에 붙여주고 OEP 시작점을 바꿔서 실행시켜도 되고
2) 미리 넣어준 상태로 새로 덤프를 떠서 생성해도 된다.
Basic RCE L10(ASPack 패킹, Win API X)
ASPack 방식으로 패킹 되어 있다는걸 확인 했고,
PUSHADD
ESP 레지스터의 주소에 Hardware Breakpoint 방법으로 PUSHAD 이후에 값이 변경되는 부분을 찾아서
PUSH 10.00445834 / RETN 으로
OEP 함수의 주소(445834)를 찾았다.
덤프를 뜨고 난 뒤에 실행시켜 보면 위의 오류를 볼 수 있는데,
잘라서 만든 실행 파일의 IAT(Import Address Table) 이 정상적이지 않아서 생긴 문제이다.
UPX 패킹 문제들은 Ollydump 자체의 Rebuild Import 기능으로 됐지만 해당 문제에서는 뭔가 꼬인거 같다.
그래서 ImportREC 라는 툴을 사용하여 다시 IAT 정보를 맞춰 주었다.
Tool 사용법에 있어서 중요한건.
1) 돌아가고 있는 원본 파일에 붙여서 IAT 정보를 받아온다는거(OEP 값만 입력하고 AutoSearch 눌러라)
2) 원본 파일에서 받은 해당 정보를 만들어 놓은 덤프 파일(IAT 오류 뜨는)에 덮어 씌워 줘야 한다는 점.
그럼 이렇게 언패킹된 파일을 Ollydbg로 오류없이 확인할 수 있다.
Basic RCE L11(UPX 패킹(생략), Win API X)
Basic RCE L12(패킹 X, Win API X)
Basic RCE L13(패킹 X, Win API X)
Basic RCE L14(UPX 패킹(생략), Win API X)
Basic RCE L15(패킹 X, Win API X)
Basic RCE L16(패킹 X, SetConsoleTextAttribute)


SetConsoleTextAttribute 함수의 원형은 이렇다.
BOOL 함수라서 반환값은 성공 여부만 판단하는거라 넘어가고
더 찾아보니까 콘솔에서의 색상, 배경색을 조정하기 위함이였다.
예로 SetConsoleTextAttribute(GetStdHandle (HANDLE), 색상);
쓰이는데 함수에 WORD 크기로 받는 wAttributes 변수를 통해 색상 정보를 저장한다.
Basic RCE L17(패킹 X, Win API(생략)
Basic RCE L18(패킹 X, Win API(생략))
Basic RCE L19(UPX 패킹, Sleep, timeGetTime)


UPX 패킹된 프로그램으로 몇 초 뒤에 자동으로 종료되는걸 확인했다.
언패킹된 파일을 돌렸을 때는 해당 문구가 뜨면서 바로 종료된다.
이 문제에서도 IsDebuggerPresent 함수가 있어 분기문을 넘기기 위해 JMP 조건을 패치한채로 진행했다.
timeGetTime : 시스템 시간을 밀리초 단위로 변환한다.
eax에 timeGetTime 함수의 반환값(ms) 가 담긴걸 확인할 수 있다.
* 그 뒤로는 프로그램 실행시 시스템 시간을 저장하고, 그 뒤에 시스템 시간을 다시 저장하여
두개의 차이로 프로그램 실행 시간을 계산한다.
계산된 값을 지정되어 있는 [EBX+4] 의 값과 지속적으로 비교하여 제한시간을 설정한 방식이라고 볼 수 있다.
Basic RCE L20(패킹 X, CreateFileA, ReadFile)


패킹은 되어있지 않았다.
우선 CreateFileA, ReadFile 두 개의 함수가 보인다.
CreateFileA 함수의 원형을 보면, 들어가는 인자들이 많은데
파일 이름, 접근 권한, 공유 모드, 보안 속성, 동작 조건 등의 속성이다.
해당 문제에서는 FileName(IpFileName), Mode(dwCreationDisposition) 속성에 집중해서 보면 된다.
FileName은 CRACKME3.KEY 로 지정되어 있고, Mode는 3으로 OPEN_EXISTING, 즉 파일이 있을 경우에만 실행된다.
ReadFile 함수의 원형을 보면
CreateFileA를 통해 가져온 파일의 제어권(HANDLE) 로
파일에서 읽은 데이터를 수신하는 버퍼를 가르키는 Buffer(IpBuffer)
파일에서 읽을 최대 바이트 수 BytesToRead(nNumberBytetsToRead)
위의 BytesToRead 값을 받는 변수에 대한 포인터 pBytesRead(IpNumberOfBytesRead)
로 정리할 수 있고,
이 문제에서는 CRACKME3.KEY 라는 파일에서 18 Byte 만큼의 값을 읽어온다는걸 알 수 있다.
음... 파일 생성, 입출력 관련한 함수들이 보인다.
명칭/인자값들을 보면 무슨 동작인지는 짐작은 가는데 정확히는 모르니까 찾아보자.
CreateFileA : 파일 혹은 오브젝트를 생성하거나 여는 동작을 한다
ReadFile : 파일 데이터를 읽는 함수이다. CreateFile 함수를 사용하여 파일의 핸들(제어권) 을 받아와야 한다.
그리고 CreateFileA 속성들을 보면 Mode = OPEN_EXISTING 이 있는데 파일이 존재할 경우에만 연다는 옵션이다.
ReadFile 속성에서도 0x12=18 Byte를 읽어온다는걸 알 수 있다.
문제 공통 윈도우 API(Sleep, MessageBox)
Sleep
제한 시간이 경과할 때까지 실행을 일시 중단하는 함수이다.
ms(1/1000초) 를 입력받아 동작한다.
MessageBox(Basic 18번 문제)
문제를 풀면서 정말 많이본..
우선 인자가 4개가 필요하고(우린 StolenByte 문제에서도 경험한 적이 있다.)
표기할 메세지가 들어가 있는 Text(IpText)
대화 상자의 제목이 들어 있는 Title(IpCaption) ← 해당 변수가 NULL 이면 기본 제목이 Error로 들어간다.
반환값은 메뉴 항목들중 어떤 선택(확인, 예, 무시, 등의 상태)을 했는지 체크하기 위한 값이 저장된다.
물론 해당 문제에서는 확인란 밖에 없어서 그대로 EAX는 1 로 유지가 된다.
패킹
일반적인 압축(Zip, RAR, 7z) 와는 다르게 '실행 압축' 이라는 특징을 가진다.
압축의 특징을 가져 크기를 줄이면서 해제하지 않고도 바로 실행 시킬수 있고,
그 용도에 따라서
단순 파일 압축을 하는 컴프레싱(Compressing) / 파일을 암호화 하여 분석을 어렵게 하는 목적의 프로텍팅(Protecting)으로 구분된다.
패커 종류
평범한 실행 파일(PE)를 만들어 내는 UPX, ASPack 같은 패커들이 있고,
원본파일을 변경하고, PE헤더를 훼손시켜 패킹하는 UPack, PESpin, NSAnti 같은 패커들이 있다.
(원본 파일 내용 변경 및 PE헤더 변질 때문에 VirusTotal 에서도 악성파일로 진단되는거 같다.)
패킹을 하는 이유
1) 데이터 압축을 통한 프로그램 크기 줄이기
2) 보안상의 이유로 프로그램의 코드 노출 최소화를 위해
3) 악성 프로그램의 경우 코드를 암호화하여 분석을 어렵게 하고, 용량을 줄여 배포에 용이하게 하기 위해
로 정리할 수 있다.
안티디버깅
우선 안티 리버싱 종류 중 하나인 안티 디버깅은
디버깅이 시작하는 시점에 동작하는 Static 형태와
디버깅 도중에 수시로 동작하는 Dynamic 형태로 구분할 수 있다.
디버거의 동작 원리를 역이용하여 디버거 실행 여부를 판단하는 Dynamic 형태의 안티 디버깅 기법이 더 어렵다고 한다.
안티 디버깅 기법 중 5가지
[Static] BeingDebugged(PEB), FindWindow(Using Normal APi)
[Dynamic] INT 3(Break Points), timeGetTime(Timing Check), Calculate Checksum(Patching Detection)
를 정리해본다.
[Static] BeingDebugged(PEB)
위쪽에서 WinAPI 를 다루면서 봤지만
PEB 구조체의 BeingDebugged 의 값으로 디버깅 여부를 판단한다. 우리는 IsDebuggerPresent() 함수를 통해 이 값을 참조 했었다.
우회 방법으로는 해당 값을 0으로 변조하거나 분기를 건너뛰는 방식으로 가능하다.
추가로 특정 값을 확인하는 방식으로 유사하게
PEB 위치에서 0x68 에 존재하는
NTGlobalFlag 값을 확인(디버깅 중일때 0x70, 아닐 때 0)
하는 방법도 있다.
ProcessHeap을 이용한 방법도 마찬가지.
[static] FindWindow(Using Normal API)
시스템안에 돌아가고 있는 프로그램의 이름을 검사하여 찾아내는 형식이다.
상용 디버거들은 각자의 이름(Title)과 Class를 가지고 돌아가고 있어서 쉽게 찾을 수 있다.
nProtect 나 다른 은행 앱들에서 가끔 "디버거 툴이 발견되었습니다 끄고 사용해주세요! " 등의 문구가 뜨는걸 확인할 수 있는데, 레지스트리 값 조회나 이런 FindWindow 함수를 사용해서 식별한게 아닐까 개인적으로 고런 생각이 든다.
[Dynamic] INT 3(Break Points)
리버싱 문제 풀다가 INT 3 INT 3 INT 3 INt 3 이런 명령어를 본적이 있을 것이다.
INT 3 은 디버거 프로그램이 사용하는 인터럽트 명령어로 소프트웨어 BP 를 설정할 때 발생한다.
OPCODE 로는 0xCC 인데 그런 0XCC를 주기적으로 검색해 디버거의 실행 여부(BP 설정 여부)를 체크한다.
[Dynamic]timeGetTime(Timing Check)
디버깅 과정 중의 시간 지연이 발생하는 것을 이용한 기법이다.
timeGetTime, GetttickCOunt 등의 함수를 이용하여 시간을 체크하는데,
BP 같은 예외처리 인터럽트가 많이 들어간 디버깅은 어쩔 수 없이 원본 프로그램의 시간과 실행 시간이 차이가 나게 된다.
[Dynamic]Calculate Checksum(Patching Detection)
'Canary Word' 로 알려진 버퍼 오버플로우의 스택 가드 기법과 형태가 비슷하다.
특정 코드 영역의 값을 저장해놓고 실행 중간에 값을 비교하여 변조 유/무를 판단하는 방식이다.
구현 방법이 굉장히 다양해서
함수 코드들의 Hash 값을 미리 구해놓은 다음 실행 도중 Hash 값 변조를 확인하는 방법(Hash Checking)도 있고
코드와 코드 사이의 값들을 저장해놓고 비교하여 인터럽트나 값 변조의 가능성을 판단하는 방법도 있다.
참고) 아래는 Static/Dynamic 안티 디버깅의 차이와 대표적 기법들을 정리해놓은 표인데 정말 정리가 잘 되어 있다.
◇ 기법 5가지 정리
참고)
사이트
빡공팟 4주차 과제_리버싱 워게임 문제 풀이 및 write-up(Codeengn) - https://claude97.tistory.com/
(내 글이지만 참고 했으니까 적는게 맞는거 같다)
Win32 API에 대한 프로그래밍 참조 - https://learn.microsoft.com/ko-kr/windows/win32/api/
(역시 공식사이트 document는 최고다.)
TEB 및 PEB을 활용하는 루틴 - https://sanseolab.tistory.com/47
SanseoLab :: 패커들 분석 - https://sanseolab.tistory.com/10
UPX Manual Unpacking - https://forensic-wetware.tistory.com/7
리버싱 도구(PEiD,BinText,Winhex), UPX UnPacking - https://blog.naver.com/brostone35/221558984782
Ollydbg를 이용한 UPX 언패킹 - https://cleverdj.tistory.com/79