Be myself :: '분류 전체보기' 카테고리의 글 목록 (10 Page)

달력

42024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

'분류 전체보기'에 해당되는 글 116건

  1. 2014.05.16 PEB를 활용한 안티디버깅
  2. 2014.05.16 WinDbg, xp 연동
  3. 2014.05.15 SEH를 이용한 안티디버깅 1
  4. 2014.05.15 TLS 콜백 안티디버깅
  5. 2014.05.14 CodeEngn 베이직 올클
  6. 2014.05.11 인라인 패치
  7. 2014.05.10 Upack 분석중 정리
  8. 2014.05.09 UPX압축 디버깅
  9. 2014.05.08 IAT와 EAT
  10. 2014.05.08 PE구조

1 개요

PEB를 이용한 안티디버깅 기법을 알아보고 우회법을 파악한다.

2 PEB

PEB(Process Environment Block)은 프로세스에 관한 정보를 담고 있는 구조체이다. 이를 WinDbg상에서 확인해 보자.(window xp sp3환경에서 테스트 했다)

여기서 안티 디버깅에 사용되는 멤버는 BeingDebugged, Ldr, ProcessHeap, NtGlobalFlag 가 있다. 이 각각의 것들을 '리버스코어'에서의 StaAD_PEB.exe예제에서 알아보고 우회방법 까지 다루겠다.

2-1 BeingDebugged

BeingDebugged 멤버를 어떻게 사용하는지 예를 보자.

FS:[18]은 TEB의 주소값이다. TEB:[30]은 PEB의 주소 값이다. PEB[2]가 바로 BeingDebugged멤버인데 이 값은 디버그 중일 때 1이된다. 그렇다면 회피방법 또한 간단하다. PEB[2]의 값을 0으로 만들어 주면 된다.

TEB가 무엇인지 설명을 빠뜨렸는데, 다음과 같은 멤버들을 가진다. 멤버들을 보면 프로그램 시작주소나 PEB(프로세스 정보) 등을 가지는데 TEB는 각 쓰레드 마다 가지는 구조체이다.

 

2-2 PEB.Ldr

이번엔 PEB.Ldr멤버가 어떻게 사용되는지 알아보자. Ldr은 프로세스에 로딩된 Dll의 로딩베이스 주소를 알아낼 때 많이 쓰이는데, 힙 영역에 존재하는 특징을 이용한 것이다. 디버깅시 사용되지 않는 힙영역의 값이 EEFEEEFE로 초기화 되는데 이 특징을 이용한 것이다.

*주의할점)
윈도우 xp이하 적용되는 내용이다. Win 7, 8 등은 안 된다.

위의 그림을 보면, NtCurrentTeb 함수를 CALL EAX로 호출하는데 이 함수는 TEB의 주소값을 리턴한다. 즉, EAX엔 TEB주소값이 들어가게 된다.

자, 위의 루틴을 보면, PEB.Ldr 값에서부터 1씩 증가해 가며 EEFEEEFE값을 가지나 확인하는 루틴이다. 그럼 우회법은 Ldr의 주소값을 참조하여 메모리값을 본 다음 EEFEEEFE라고 쓰여진 부분을 0으로 초기화 시키면 끝이다. 다음과 같이.

 

2-3 Process Heap

이번엔 Process Heap의 flag를 이용해 안티 디버깅을 수행하고 있다. 아까 EBX에 PEB의 주소값이 있었으므로, 401112주소에서 [EBX+18]은 PEB.ProcessHeap을 가리키고, ProcessHeap구조체에서 12(0xC)만큼 떨어진 곳에 flag값이 존재한다. 이 flag값은 디버그 중이 아니면 2의 값을 가지므로 이 값을 2로 하면 우회까지 완성된다.

ProcessHeap구조체는 다음과 같은 멤버들을 가진다. 환경이 xp가 아니라서 멤버들의 위치가 xp에서의 HEAP과 다르다. XP에선 Flags가 0xc에 위치하고 ForceFlags가 0x10에 위치한다.

<출처: http://gogil.kr/24>

다음 ProcessHeap의 ForceFlags값을 이용하는 방법이다. 이 값은 정상 실행중일 때, 0이 된다. 따라서 이 값을 0으로 하면 우회까지 완벽하다.

아까 EDI에 PEB.ProcessHeap값이 있었으므로 [EDI+10]은 ForceFlags의 값이 된다. 401152주소를 보면 ESI이 0인지 아닌지 확인하는 루틴이 있고, 0이면 Not debugging이 뜨도록 되어있다. 우리는 [EDI+10]의 값을 0으로 바꾸면 된다.

2-4 NtGlobalFlags

TEB.NtGlobalFlag 값은 디버깅 중일 때 0x70의 값을 가진다. 따라서 이 값을 0으로 만들어 줘버리면 우회가 가능하다.

아까 EBX에 TEB의 주소 값이 있었으므로 0x68만큼 떨어진 위치에 NtGlobalFlag가 위치하게 된다. 이 값이 0x70인지 확인하는 루틴이 40117E에 있다. 우린 이 값을 0으로 만들면 된다.

'Reversing' 카테고리의 다른 글

치트엔진을 활용한 환세취호전 핵  (2) 2014.07.28
DLL Injection  (1) 2014.05.17
WinDbg, xp 연동  (0) 2014.05.16
SEH를 이용한 안티디버깅  (1) 2014.05.15
TLS 콜백 안티디버깅  (0) 2014.05.15
Posted by flack3r
|

WinDbg, xp 연동

Reversing 2014. 5. 16. 16:13

1 개요

Windbg와 Vmware에서 XP를 연동하는 법을 알아본다.

2 vmware 세팅

1. Edit virtual machine settings를 누른다.

2 Serial Port 를 추가하고 use named pipe 의 마지막에 com_1라 적힌걸 기억하라 WinDbg에서 연결할 때 필요하다.

3 xp세팅

1. 내 컴퓨터-> 속성-> 고급-> [시작 및 복구] 설정 을 누른다

2. 편집을 눌러준다.

3. 위와 같이 제일 마지막 줄에 아래와 같이 추가해준다.
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="XP kernel debug" /fastdetect /debugport=com1 /baudrate=115200

4 windbg 세팅

1. 윈디버그 속성창에서 '대상'에
"C:\Program Files\Windows Kits\8.0\Debuggers\x86\windbg.exe" -k com:port=\\.\pipe\com_1,baud=115200,pipe,reconnect

이런 식으로 입력한다. 프로그램 위치와 버전이 다를 수 있으니 확인하고 적용한다. 핵심은 경로 + 뒤에 옵션을 주는 것이다. 여기서 주의할 점은 \\.\pipe\com_1이 부분에 com_1이라고 아까 vmware설정에서 적었던 이름 그대로 넣어주어야 한다.

2. 이제 Windbg를 실행시킨 후 xp를 실행시키면 된다. 그 후 Ctrl+Break를 누르면 디버깅 상태가 되고 F5나 g를 누르면 running상태가 된다.

'Reversing' 카테고리의 다른 글

DLL Injection  (1) 2014.05.17
PEB를 활용한 안티디버깅  (0) 2014.05.16
SEH를 이용한 안티디버깅  (1) 2014.05.15
TLS 콜백 안티디버깅  (0) 2014.05.15
인라인 패치  (0) 2014.05.11
Posted by flack3r
|

1 개요

SEH(Structured Exception Handler)는 윈도우에서 제공하는 예외처리 매커니즘이다. 이 SEH는 안티 디버깅 기법으로도 사용되는데 SEH가 무엇인지, 어떻게 안티 디버깅으로 사용되는지, 우회법은 무엇인지 등을 알아보자.

2 SEH(프로그래밍으로)

예외 처리의 과정을 알아보자. 우선 프로그램에서 예외가 발생할 경우, OS는 프로세스에게 예외처리를 맡긴다. 하지만, 프로그램이 적절한 예외처리를 하지 못할 경우 OS에서 오류처리를 한답시고 프로그램을 종료시켜 버린다. 이것이 일반적인 예외처리 메커니즘인데 디버깅을 하는 경우, 예외처리 루틴을 디버기(디버그를 수행하는 프로그램)에 맡겨버린다. 이러한 상황을 한번 보자.

다음과 같이 예외처리를 하는 프로그램을 작성하였다.

#include<Windows.h>
#include<stdio.h>
int main(void)
{
	int seh_flag = 0;
	int a = 1;
	int b = 0;
	int result = 0;
	__try{
		result = a / b;
	}

	__except(EXCEPTION_EXECUTE_HANDLER){
		seh_flag = 1;
		printf("normal processing \n");
	}

	if (seh_flag == 0)
	{
		printf("debuge detected \n");
		getchar();
	}
	getchar();
	return 0;
}

0으로 나누는 것은 EXCEPTION_INT_DIVIDE_BY_ZERO예외를 발생 시킨다. 이 것을 그냥 실행했을 때와 올리디버그상에서 실행했을 때를 보자.

<일반 실행> <디버그로 실행>

왜 이렇게 될까? 그것은 앞에서 설명 했듯이 예외 발생 시 디버기에서 예외처리를 담당하기 때문에 프로그램에서의 예외처리는 무시된다. 이는 올리디버거 상에서 확인할 수 있다.

IDIV 즉, 나누는 과정에서 break되고 과정을 살펴 보면, normal processing이라 뜨는 루틴이 생략되는 것을 알 수 있다.

그렇다면 이를 우회하는 방법은 이제 답이 나온 셈이다. 아예 예외가 발생하는 명령어 부분을 고쳐서 디버깅을 다시 하던지, 아님 올리디버거에 예외처리를 디버기(디버깅을 당하는 프로그램)에게 맡기는 옵션을 주면 정상 실행된다.

위 그림과 같이 설정하면 된다.

 

3 SEH(어셈블리로)

아깐, 디버기에서의 예외처리를 이용하여 안티디버깅을 했지만 이번엔, SEH chain에 직접 예외 핸들러를 등록해서 예외처리 루틴에서 안티디버깅을 수행한다.

먼저 SEH를 등록하는 과정을 보자.

처음 PUSH한 것이 예외를 처리하는 핸들러의 주소 값이다 FS:[0]는 TEB의 주소 값인데, NtTib.ExceptionList구조체 멤버의 주소값이기도 하다. TEB에 관한 내용은 다음에 설명하도록 하겠다. 즉 위에서 명령어가 수행되면, 전 FS:[0] (SE핸들러 멤버를 가진 구조체의 주소값 더 자세히는 EXCEPTION_REGISTRATION_RECORD 구조체의 첫 번째 멤버인데 자세한 내용은 구글을 통해 알아 보시길) 를 스택에 넣고 SEH를 등록하는 과정이다.

이후 프로그램 과정에서 다음과 같이 일부로 예외를 발생시킨다.

EAX의 주소값이 0인데 이 부분에 1이라는 값을 넣고 있다. 이제 예외가 발생했으므로 우리가 아까 등록한 예외 핸들러가 실행될 것이다.

어디서 많이 본 명령어 이지 않은가? 0x40105E~ 0x401064명령어를 보면 TEB의 30번째 멤버(PEB)에 접근해 PEB의 BeingDebugged멤버에 접근해 이 값이 1인지 아닌지 판별하는 IsDebuggerPresent() 함수의 내용과 같은 명령어를 수행한다.

여기서 ESI엔 예외가 발생하기 전 pContext값이 들어가게 된다. 예외처리 핸들러의 3번째 매개변수이다. Context엔 예외가 발생하기 전 레지스터나 세그먼트 들의 값이 저장되어 있다. Context는 각 스레드마다 내부적으로 가지고 있다. Context가 존재하는 이유는, 전에 OS kernel관련 포스팅에 게시되어 있는데 멀티스레딩 환경에서 필수적이기 때문이다.

아무튼, Context에서 B8만큼 떨어진 값에는 Eip의 값이 들어 있으므로, 이 값을 변경하면 예외 핸들러루틴이 마무리되면, 설정된 주소값으로 JMP하게 된다. 따라서, 디버깅 중이라면, 40106A로 점프하게 되는게 이 명령어를 보면 Eip를 401023으로 설정하고 있다. 이 값에 해당하는 영역으로 가보자.

예상 했듯이, 디버거가 발견되었다는 메시지박스가 출력된다.

우회방법은 위의 __try, __except방법과 같고 그 후 위의 IsDebuggerPresent() 함수의 우회법을 적용하면 된다.

'Reversing' 카테고리의 다른 글

PEB를 활용한 안티디버깅  (0) 2014.05.16
WinDbg, xp 연동  (0) 2014.05.16
TLS 콜백 안티디버깅  (0) 2014.05.15
인라인 패치  (0) 2014.05.11
Upack 분석중 정리  (0) 2014.05.10
Posted by flack3r
|

1 개요

TLS콜백 함수가 어떻게 안티 디버깅으로 사용되는지 어떻게 우회가 가능한지 살펴 볼 것이다.

 

2 TLS

TLS(Thread Local Storage)는 쓰레드 별로 독립된 메모리 공간이다. 프로세스에서 전역변수나 static변수를 마치 쓰레드의 지역변수처럼 사용하고 싶을 때 사용된다. 하지만 TLS 콜백 함수가 EP코드가 실행되기 전에 동작하는 특징을 이용해 안티디버깅 기법으로도 사용 된다.

일단 TLS콜백함수를 사용하는 소스코드를 살펴 보자.

#include<Windows.h> #pragma comment(linker, "/INCLUDE:__tls_used") //링커설정. tls사용함으로 void NTAPI Tls_Callback(PVOID THandle, DWORD Reason, PVOID Reserved) { if (IsDebuggerPresent()) //IsDebuggerPresent()함수를 이용해 디버깅 중인지 확인 {         MessageBoxA(NULL, "Not allowed Debuging..", "TLSCallback",MB_OK);         ExitProcess(1); } } #pragma data_seg(".CRT$XLX") //TLS콜백 사용시 꼭 필요한 섹션 PIMAGE_TLS_CALLBACK TLS_CALLBACk[] = { Tls_Callback, 0 }; // TLS callback 함수 등록 #pragma data_seg() int main(void) { MessageBoxA(NULL, "Hi There! this is Inj3cti0n ! ", "mainbox", MB_OK); return 0; }

주석처리를 해놓아서 딱히 어려운점은 없을 것 같다. IsDebuggerPresent 함수가 처음 소개되었는데, 이는 PEB에서 3번째 멤버(PEB.BeingDebugged)를 이용해 디버깅중인지 아닌지 확인하는 함수이다. 자세한 내용은 다음에 설명하도록 하겠다.

자 그럼 이렇게 컴파일 된 파일을 실행시켜 보겠다.

잘 된다. 그럼 올리디버거로 열어 보겠다.

열리지 않는다. 자 일단 누군가가 어떠한 파일을 올리디버거로 열었을 때 디버깅이 되지 않는다면 무척 당황스러울 것이다. 그렇다면 필자는 원인이 뭔지 확인하기 위해 제일 먼저 PEview로 확인을 해 볼 것이다. 그래서 확인을 해 봤더니 TLS Table이 있는 것이 아닌가.

그렇다면 이 파일은 TLS Callback을 이용하여 안티 디버깅을 했을 가능성이 많다. 테이블 내용을 살펴 보면 다음과 같다.

여기서 제일 중요한 맴버는 Address of Callbacks이다. 여기서 주의할 점은 Callbacks 복수로 나타나 있다. 그래서 이 값은 배열의 주소 값을 나타내며 여러 개의 TLS Callback 함수를 등록할 수 있다는 것을 암시한다. 이 값은 VA값으로 나타나 있다.

3 TLS callback 디버깅

자 그럼 어떻게 디버깅은 어떻게 할까. EP가 실행되기 전에 디버깅을 해야 하므로 일단, 올리디버거 옵션부터 변경해 준다.

그리고, PEview를 이용하여 TLS 테이블에서 TLS콜백의 시작주소 값을 알아 낸 후 올리디버거에서 브레이크걸고 IsDebuggerPresent함수를 우회하면 된다.

디렉토리에서 TLS함수 배열 주소를 찾은 뒤

Hex에디터에서 함수 시작 값을 찾고

올리디버거에서 BP를 걸고 디버깅해서 우회하면 성공!

 

4 수작업으로 tls추가

지금까지 살펴 보았듯이 PE구조에 TLS table만 추가해 주면 코드를 수정하지 않고 패치가 가능하다. 직접 해보자.

직접 패치 시킬 때, 빈 공간을 잡아야 하기에 빈 섹션을 이용하거나, 마지막 섹션의 크기를 늘리거나, 마지막 섹션을 추가하거나 하는 방법이 있다. 여기서 우린 마지막 섹션의 크기를 200바이트 늘려서 해보도록 하자.


<Image_section_header .rsrc>

Size of Raw Data값을 Hex에디터를 이용해 400으로 수정하고 마지막 부분에 200바이트를 추가 시킨다. 그 다음 Optional_header의 TLS Table값을 설정해 준다.

다음엔, .rsrc섹션에 Characteristics값을 IMAGE_SCN_CNT_CODE, IMAGE_SCN_CNT_MEM_EXECUTE, IMAGE_SCN_CNT_WRITE의 값으로 설정한다

 

마지막으로 IMAGE_TLS_DIRECTORY의 멤버를 참고하여 구성한다.

여기서 RETN 0C를 하는 이유는. TLS callback 함수의 인자의 개수가 3개이기 때문에 보정해 주는 역할을 하는 것이다.

이제 마지막으로 할 일은 FileOffset 9230부분에 명령어를 입력하는 것인데, 이 땐, 올리디버거를 이용해 어셈프로그래밍을 하는 것이 쉽다.

다음과 같이 입력하자.

그런 다음 Copy to executable –selection –Save file 을 선택해 저장한 후 실행 시키면 메시지 박스가 뜬 다음 실행되는 걸 볼 수 있다.

<실행 후 첫 번째 메시지> <두 번째 메시지>

'Reversing' 카테고리의 다른 글

WinDbg, xp 연동  (0) 2014.05.16
SEH를 이용한 안티디버깅  (1) 2014.05.15
인라인 패치  (0) 2014.05.11
Upack 분석중 정리  (0) 2014.05.10
UPX압축 디버깅  (0) 2014.05.09
Posted by flack3r
|

CodeEngn 베이직 올클

Wargame 2014. 5. 14. 20:40

ㅋㅋㅋ..;; 삽질하느라 시간 가고 롤하느라 시간가고 친구가 불러서 놀고.. 3일동안한게 고작 CodeEngn basic all clear..내일부터 초심으로 돌아가서 다시 빡세개 공부해야겠다!

'Wargame' 카테고리의 다른 글

Fc3 dark_eyes  (0) 2014.08.22
Suninatas all clear  (3) 2014.07.25
워게임 현황  (0) 2014.07.01
워게임 사이트  (0) 2014.05.05
BOF원정대 level3  (0) 2014.05.04
Posted by flack3r
|

인라인 패치

Reversing 2014. 5. 11. 13:45

1 개요

인라인 패치를 통해 지금까지 배운 내용들을 총 정리하는 시간을 가지자. 패치를 통해 파일의 내용이 어떻게 가상메모리상에 올라가는지, 가상메모리에 올라간 프로그램이 어떻게 실행 되는지 등의 그림을 그릴 수 있을 것이다. 이 기법은 후킹을 할때도 이용된다. 

2 분석

일단 파일의 내용이 어떻게 가상메모리 상에 올라가고, 연산되는지 전체적인 그림을 그리자
먼저 unpackme#1.aC.exe파일을 실행시켜 보자. 파일은 http://ap0x.jezgra.net/download/patchme_no1.rar 에서 받을 수 있다.

NAG(잔소리)라고 쓴 글자를 바꾸라는 것 같다.

Status의 메시지도 바꾸는 것이 우리의 목표이다.

일단 올리디버거로 디버깅하기 전에 우선, PE구조를 분석하여 이 파일이 가상메모리상에서 어떻게 존재하게 되는지 파악한다. 다음과 같은 그림이 그려진다. (각자 그려보자)

자, 이제 대략적인 메모리 구조가 파악이 됐으니 ollyDbg로 뜯어보면서 확인 이 메모리상에서 어떤 일이 벌어지는지 확인해 보자. 우선 분석을 하다보면 첫 번째, 반복 루틴을 만나게 된다.

EBX에 0x4010F5~0x401249까지의 값에 44의 XOR값으로 연산되고 있다. 여기서 XOR 은 기본적인 암호화, 복호화 연산에 잘 사용된다. 예를들어 a XOR 11로 암호화하고 a XOR 11로 복호화 하는식이다. 그렇다면 위의 값은 44로 위의 주소 값에 존재하는 값을 복호화 하고 있다. 계속 트레이싱 해보자. 두 번재 반복 루틴을 만난다.

EBX의 0x401007~0x401086까지의 값을 7로 복호화 하고있다. 계속 살펴 보자.

세 번째 루틴이다. EBX의 0x4010F5~0x401249 의 값을 11로 다시 복호화 하고 있다. 즉, 0x4010F5~0x401249영역은 첫 번째와 세 번째 루틴에서 두 번 복호화 하고 있다. 두 번 암호화 된 것이다.

자 계속 트레이싱 해보자.

0x401062의 명령어를 보면 0x4010F5~0x401249 영역의 값을 더한 값을 31EB8DB0의 값과 비교하고 있다. 이 것은 이 데이터가 손실되진 않았는지, 변형되진 않았는지 확인하는 CheckSum값이다. 계속 트레이싱 해보자. CALL 0040108A부분을 들어가면 다음과 같은 루틴이 확인된다.

EBX에 0x40124A~0x401280 까지의 값을 17로 복호화 하고 있다.

자, 지금까지의 과정을 정리해 보면 다음과 같다.

1.0x4010F5~0x401249까지의 값에 44의 XOR값으로 연산
2.0x401007~0x401086까지의 값을 7로 복호화
3.0x4010F5~0x401249 의 값을 11로 다시 복호화
4.0x4010F5~0x401249 영역의 값을 더한 값을 31EB8DB0의 값과 비교
5.0x40124A~0x401280 까지의 값을 17로 복호화
6.JMP 0040121E명령어로 OEP로 점프

이를 제일 처음에서 살펴 보았던 가상메모리상의 구조와 함께 이해하자. 그럼 다음과 같은 그림이 그려진다.

(출처: http://www.partner114.com/bbs/board.php?bo_table=B33&wr_id=91)
자 이제 분석이 끝났으니, 어떻게 패치 할까 고민해 보자. 우리는 인라인 패치 방법을 진행하려 한다. 이 방법은 파일의 빈영역에 명령어를 집어 넣고 OEP로 점프하는 부분을 우리가 입력한 패치 코드부분으로 JMP하도록 한다음, 패치를 수행한 후 원래의 OEP로 되돌려 주면 된다. 그럼 일단 빈영역을 확보해야 하는데, 우리는 text section을 이용하도록 하자. 앞에서 살펴 보았듯이 text section은 0x401280 부분에 끝이 나게된다. 그렇다면, 이 부분에 파일이 비었는지 확인을 해야하는데 이는 RVA->RWA변환을 하면 된다. 0x401280(RVA) – 0x1000(Virtual Address) + 400(PointerToRawData) = 680이다. 이부분을 헥스 에디터로 살펴 보자.

공간이 충분한 것 같다. 그럼 이 부분을 올리디버거를 이용해 어셈블리로 명령어를 입력해 파일을 저장한 후, JMP OEP부분을 이 부분으로 바꾸면 패치가 완료 된다.

다음과 같이 명령어를 입력하자.

자 이제 이 파일을 저장하자. 그 다음 OEP로 가는 부분을 찾아보자

명령어가 E9 96 01로 되어있는데 이 부분을 우리가 원하는 주소로 입력하면 된다.

이렇게 변경하면 명령어가 E9 F8 01 이렇게 된다. 이 값을 XOR 7으로 다시 암호화 해서 파일에 값을 적어 넣으면 완성이다. 401083(RVA)->483(RAW)값 이므로 이 위치의 값을 밑의 그림처럼 바꾸면 된다.

저장한 다음, 파일을 실행 시키면 다음과 같이 뜬다.

성공이다.!

'Reversing' 카테고리의 다른 글

SEH를 이용한 안티디버깅  (1) 2014.05.15
TLS 콜백 안티디버깅  (0) 2014.05.15
Upack 분석중 정리  (0) 2014.05.10
UPX압축 디버깅  (0) 2014.05.09
IAT와 EAT  (0) 2014.05.08
Posted by flack3r
|

Upack 분석중 정리

Reversing 2014. 5. 10. 00:24

1. Dos Header에서 file header위치를 dos header에서 바꿀수 있다

2. File Header에서  Optional Header의 크기를 결정 할 수 있다

*여기서 주의할 점은 section헤더의 시작은 

Optional Header의 시작주소 + File Header->sizeofOptioanlHeader값.

4. OptionalHeader에서 NumberOfRvaAndsSizes 값이 바뀔 수 있다. 건드리면

안되는 부분만 제외하면 조작가능

5. Section Header에서도 주요한 것 제외하곤 없어도 됨


'Reversing' 카테고리의 다른 글

TLS 콜백 안티디버깅  (0) 2014.05.15
인라인 패치  (0) 2014.05.11
UPX압축 디버깅  (0) 2014.05.09
IAT와 EAT  (0) 2014.05.08
PE구조  (0) 2014.05.08
Posted by flack3r
|

UPX압축 디버깅

Reversing 2014. 5. 9. 13:41

1 개념

UPX실행 압축된 파일을 뜯어보면서 OEP까지 가는게 목표이다. 한번 읽어보고 직접 실습하면서 연습하자.

2 분석

어떤식으로 압축 알고리즘이 되어 있는지 살피기 위해 일단, PE구조를 살펴 보았다.

IMAGE_SECTION_HEADER UPX0의 구조이다. 여기서 주목할 점은 파일에서의 사이즈가 0이라는 점이다. 하지만 Virtual Size를 보면 10000값을 가진다. 이것이 의미하는 것은 다른 섹션에서 압축해제 알고리즘을 가지고 있어 그 섹션에서 먼저 EP가 실행이 되고 압축해제가 완료되면 원래의 UPX0섹션에서 OEP를 가진다는 것을 유추할 수 있다. 자, 그럼 이제 올리디버거로 이 파일을 까보자.

빠르게 훑어보면 코드섹션에 많은 데이터가 압축되거나 암호화되어 있고, 그 결과로 분석된 코드가 정상적이지 않을 수 있다는 말을 뱉어낸다. 그냥 '예'를 눌러준다.

처음 명령어를 살펴 보면, 먼저, EAX~EDI 레지스터 들을 스택에 push한 다음, ESI와 EDI값을 설정한다. 그 중 ESI와 EDI에 어떤 값이 들어가는지 살펴 보면 1011000값과 1001000값이 들어 있다. 이 값은 각각 아까 PEview에서 보았듯이 UPX1섹션과 UPX0섹션의 시작 값 이였다. 그렇다면 아까 우리가 예상 했듯이 UPX1에서 압축해제 알고리즘이 끝나면 UPX0에 압축해제된 내용이 존재하고, 원래의 OEP로 찾아 갈 수 있을 것 같다.

계속 트레이싱 하다 보면, 다음과 같은 루프를 만날 수 있다.

ECX의 수만큼, EDX의 하위 1바이트 값을 AL에 넣고, AL값을 EDI값(UPX0위치)에 넣는 루틴이다. 값은 NULL을 넣으니, UPX0의 공간을 확보하는 루틴이라 보아도 좋을 것 같다.

그 이후 EDI에 값을 넣는 루틴을 3가지 정도 발견 할 수 있다.

  (첫째)

(둘째)

(셋째)

EAX에 압축해제된 데이터가 존재하고 이 값을 EDI(UPX0)에 넣는 과정이다.

그 다음 만나는 루프는 다음과 같다.

UPX0에서 E8과 E9(CALL, JMP명령어)부분을 찾아 어떤 연산을 수행하는데, 잘은 모르겠지만 점프할 주소를 원래대로 복원시키는 작업인 것 같다. 계속 트레이싱 해보자.

뭔가 많이 본 함수가 보이지 않나? LoadLibraryA-> GetProcAddress가 반복된다. 앞에서 살 펴 본 IAT에 함수 시작값을 입력하는 순서에 나왔었다. 즉 이 반복루틴은 INT에 함수의 시작점을 입력하는 루틴이다. 여기까지 보면 의문스러운 점이 한두가지가 아닐 것이다. 일단 INT에 함수의 시작점을 쓴다는데, 그럼 LoadLibraryA의 시작주소는 어떻게 메모리에 올라가 있는 것인가? GetProcAddress는 또 어떠한가. 그래서 PE Viewer로 찾아보았다.

IMPORT Directory Table이 .rsrc섹션에 존재하고 있었다! 그렇다면 중요한 라이브러리의 함수들은 rsrc섹션에 이미 다 로드 되어 있고, 나머지 함수들이 UPX0섹션의 INT영역에 API주소가 입력되어 진다는 것이다.

자 이제 끝났다. 조금 더 트레이싱 하다보면, 다음과 같은 화면을 볼 수 있다.

JMP로 OEP에 가는 것을 볼 수 있다. 100739D는 UPX0의 영역이다.

'Reversing' 카테고리의 다른 글

인라인 패치  (0) 2014.05.11
Upack 분석중 정리  (0) 2014.05.10
IAT와 EAT  (0) 2014.05.08
PE구조  (0) 2014.05.08
기본 레지스터  (0) 2014.05.04
Posted by flack3r
|

IAT와 EAT

Reversing 2014. 5. 8. 20:17

1 개요

자 이제 IAT와 EAT에 관해서 알아 볼 것이다. 앞 장에서 자세히 설명하지 않고 넘어간 NT Header -> Optional Header -> DataDirectory 멤버의 구조체에서 EXPORT Directory와 IMPORT Directory와 관련된 부분이다. API후킹 방법 중 하나로 활용되므로 꼭 숙지하길 바란다.

IAT(Import Address Table)로 쉽게 말해서 프로그램이 어떤 라이브러리에서, 어떠한 함수를 사용하는지 적은 메모장이라 생각하면 된다.

IAT의 원리를 알기 위해선 DLL에 대한 개념이 필수적이다. DLL이란, 여러 프로세스에서 공유하면서 쓰는 라이브러리인데 멀티태스킹 환경에서는 각 프로세스마다 라이브러리를 가지고 그 용량을 차지하는 것 보다, 메모리에 한번 로딩시켜 프로세스마다 공유시키면 메모리를 더 효율적으로 사용 할 수 있기 때문에 DLL이 탄생하였다.

<출처 http://www.hanbit.co.kr/network/view.html?bi_id=1326>

예를 들어, 위의 프로세스 A의 Page 1이 Frame 5를 가리키고, 프로세스 B의 Page 7도 마찬가지로 Frame 5를 가리킨다. 이것은, DLL을 한번 메모리에 로드 시키고, 그 영역을 프로세스 별로 공유한다는 것을 뜻한다. 여기서 주의할 점은, DLL이 메모리에 어느 곳에 로딩되어 있는지 프로그래밍 할 당시에 확인할 방법이 없다는 것이다. 그래서, IAT라는 공간을 만들어 두고, PE Loader가 이 공간에 DLL이 메모리상 어디에 위치 되어 있는지 쓰는 과정을 거치게 된다.

여기서 또 알 수 있는 점은 DLL이 올라 갈 때, 메모리상에 바로 올라 가는 것이 아니라 프로세스 가상메모리에 먼저 올라 가고, 페이지 테이블에서 물리 메모리에 지정된다는 것이다. IAT와 EAT를 배우면 이 과정이 좀더 자세하게 그림이 그려질 것이다.

2 IAT

자, IMAGE_IMPORT_DESCRIPTOR구조체의 FirstThunk라는 멤버가 IAT를 가리킨다. 그럼, IMAGE_IMPORT_DESCRIPTOR는 어디에서 확인할까? 바로 NT Header-> Optioinal Header-> DataDirectory 에서의 2번째 변수 값이 가리키는 위치에 존재한다. 그럼, IMAGE_IMPORT_DESCRIPTOR 구조체가 어떤 멤버로 구성되어 있는지 확인해 보자.

보통 하나의 프로그램에서 여러 개의 라이브러리를 갖기 때문에, IID는 배열의 형식으로 나타나게 된다. 이 멤버들 중 우리에게 중요한 부분은 주석으로 달린 부분들 이다. OriginalFirstThunk같은 경우는 라이브러리의 함수들의 주소를 가리키고 있는 부분들이다. Name멤버는, 라이브러리의 이름을 뜻한다. FirstThunk멤버는 IAT의 주소값을 가리킨다.

자 이제 어떠한 방식으로 IAT에 함수 주소들이 로딩되는지 알아 볼 텐데 꾀나 복잡하다. 하지만 상식적으로 생각하면 쉽게 와 닿는다. 프로그램에서 라이브러리를 사용 할 경우, 먼저 라이브러리를 로딩시켜야 하지 않겠나? 그럼 필요한 것이 라이브러리의 이름이다.(Name 멤버) 그 다음 필요한 것이 무엇 이겠는가? 우린 지금 라이브러리에 존재하는 함수들의 위치를 IAT에 적으려 하고 있다. 그렇다면 필요한 것은 바로 라이브러리에 존재하는 함수들의 이름이다(INT멤버) 그럼 필요한 것은 모두 구했다. 이제 이 값을 이용해 FirstThunk 를 이용하여 IAT에 함수 주소를 입력하면 끝이다. 이 순서를 좀 더 자세히 표현한 것은 다음과 같다.

1. IDD의 Name멤버를 이용해 라이브러리의 이름을 얻는다.
2. 라이브러리를 로딩시킨다(Loadlibrary).
3. INT의 값을 읽어 IMAGE_IMPORT_BY_NAME구조체들의 주소(RVA)를 얻는다.
4. IMAGE_IMPORT_BY_NAME 의 멤버를 이용하여 함수의 시작주소를 얻는다.
5. FirstThunk멤버를 이용해 IAT에 함수의 시작 주소값을 입력한다.
6. OriginalFirstThunk가 끝날 때 까지 3~5의 과정을 반복한다.

여기서 다루지 않은 부분이 IMAGE_IMPORT_BY_NAME구조체 부분이다.

참고로 INT와 IAT는 NULL로 끝나고 둘의 크기는 반드시 같아야 한다.

3 EAT

EAT(Export Address Table)란 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 과정에서 중요한 역할을 한다. IAT를 공부 할 때, LoadLibrary 함수를 이용해 라이브러리를 로딩시키는 과정에서 일어나는 과정을 설명할 것이다. 우선 IMAGE_EXPORT_DIRECTORY의 구조체를 살펴 보자. 이 구조체 위치는 OptionalHeader->DataDirectory[0]이 가리킨다.

중요한 멤버들은 주석으로 표시를 하였다.
그렇다면 이제 어떤 과정으로 라이브러리의 함수들이 로딩 되는지 살펴 보겠다.

1. AddressOfNames 멤버(RVA값)를 이용해 '함수 이름 배열' 로 간다. 즉, 함수들의 이름을 적어놓은 공간이라 생각하면 된다.
2. '함수 이름 배열'은 문자열 주소가 저장되어 있다. 문자열 비교를 통해 원하는 함수의 이름을 찾는다. 그리고 이 때의 인덱스 값을 구한다.
3. AddressOfNameOrdinals 멤버(RVA)값 을 이용해 'ordinal'배열로 간다.
4. 'ordinal'배열에서 2번에서 구한 인덱스 값을 이용해 ordinal값을 찾는다.
5. AddressOfFunctions을 이용해 EAT로 간다.
6. EAT에서 4번에서 구한 ordinal을 배열 인덱스로 하여 원하는 함수의 시작 주소를 얻는다.

4 정리

EAT와 IAT의 과정을 묶어서 이해 할 필요가 있습니다. 어떤 순서로 되는지 짚어 보겠습니다.

-------------------------<IAT 과정>-------------------------

1. IDD의 Name멤버를 이용해 라이브러리의 이름을 얻는다.
2. 라이브러리를 로딩시킨다(Loadlibrary).
3. INT의 값을 읽어 IMAGE_IMPORT_BY_NAME구조체들의 주소(RVA)를 얻는다.
4. IMAGE_IMPORT_BY_NAME 의 멤버를 이용하여 함수의 시작주소를 얻는다.

-------------------------<EAT 과정>-------------------------

4-1. AddressOfNames 멤버(RVA값)를 이용해 '함수 이름 배열' 로 간다. 즉, 함수들의 이름을 적어놓은 공간이라 생각하면 된다.
4-2. '함수 이름 배열'은 문자열 주소가 저장되어 있다. 문자열 비교를 통해 원하는 함수의 이름을 찾는다. 그리고 이 때의 인덱스 값을 구한다.
4-3. AddressOfNameOrdinals 멤버(RVA)값 을 이용해 'ordinal'배열로 간다.
4-4. 'ordinal'배열에서 2번에서 구한 인덱스 값을 이용해 ordinal값을 찾는다.
4-5. AddressOfFunctions을 이용해 EAT로 간다.
4-6. EAT에서 4번에서 구한 ordinal을 배열 인덱스로 하여 원하는 함수의 시작 주소를 얻는다.
-----------------------------------------------------------------
5. FirstThunk멤버를 이용해 IAT에 함수의 시작 주소값을 입력한다.
6. OriginalFirstThunk가 끝날 때 까지 3~5의 과정을 반복한다.



'Reversing' 카테고리의 다른 글

인라인 패치  (0) 2014.05.11
Upack 분석중 정리  (0) 2014.05.10
UPX압축 디버깅  (0) 2014.05.09
PE구조  (0) 2014.05.08
기본 레지스터  (0) 2014.05.04
Posted by flack3r
|

PE구조

Reversing 2014. 5. 8. 15:50

1 개요

이번엔 PE구조에 대해 살펴보도록 하자. PE구조를 처음 공부할 때는 어디에 쓰이는지 몰라 이걸 왜 공부해야 하는지 잘 모를 것이다. 필자 역시 그러했다. 하지만 공부를 하면 할수록 윈도우 리버싱에서 꼭 알고 가야 할 것이 PE관련 지식들이었다. 간단한 예를 들면 간단한 압축파일의 패치를 할 경우에, 여러가지 방법이 있겠지만, 그 중 쓰지 않는 섹션의 공간에 패치명령어를 입력하는 방법을 쓸 때, PE관련 지식이 없다면 사용 할 수도 없을 것이다. 또한 IAT를 후킹 할 시에도 PE관련 지식이 필수적이다. 또 PE에 관한 지식이 있다면, IDT를 조작하여 DLL인젝션도 가능 하다. 이처럼 여러 가지 기법을 사용함에 있어 PE구조에 관한 지식은 필수라 할 수 있다.

 

2 PE란?

PE란 Portable Executable의 약자로, 윈도우 운영체제에서 사용되는 실행파일 형식이다. 애초에 Portable의 의미대로 여러 운영체제간의 이식성을 좋게 하려는 의도였으나 현실은 윈도우 계열의 운영체제에서만 사용된다. PE를 보고 알 수 있는 점은 참 많다. 프로세스의 각 섹션들이 어떻게 메모리에 적재되는지, 어떤 DLL들이 사용되는지, 필요한 stack과 heap의 크기가 어떤지 등이 모두 적혀져 있다. 참고로 64비트에서의 실행파일은 PE+ 혹은 PE32+라 부른다.

 

3. PE 파일 포맷

위의 그림은 메모장을 Hex에디터로 열어본 것이다. 이 Hex값들을 토대로 PE구조를 살펴 볼 것이다.

3-1 기본 개념

본격적으로 PE구조에 대해 살펴보기 전에, PE구조에 의해 어떻게 파일이 메모리에 적재되는지 살펴 보자.

<출처 reversecore>

그림을 살펴 보면, DOS header에서 Section header까지 PE header라 부르고 그 아랫 부분을 PE body라 부른다. 여기서 주의깊게 봐야 할 점은 header부분은 메모리와 파일의 위치가 같은데, body부분은 위치가 다르다는 것이다. 하지만 그 오프셋(어떤 값의 어떤 위치에서부터의 메모리상에서 위치) 값은 같게된다. VA(Virtual Address)는 프로세스 가상 메모리의 절대주소를 말하며, RVA(Relative Virtual Address)는 오프셋 값을 나타낸다. 따라서 RVA + ImageBase = VA값이 된다. 여기서 또 주목할 점은, 어던 값의 파일에서의 오프셋과 메모리에서의 오프셋은 같으므로 이러한 공식이 성립된다. RAW(파일에서 어떤 메모리의 위치) – PointerToRawData = RVA – VirtualAddress가 성립된다. PointerToRawData란, 데이터에서의 섹션의 시작지점을 나타내고, VirtualAddress란 메모리에서 섹션의 시작주소를 나타낸다. 예를 들어 RVA값이 6000이라면 .text section에 존재하므로 VirtualAddress값은 1000이고, PointerToRawData의 값은 400이 된다. 따라서, RAW(파일에서의 이 값의 위치)는 = 6000 – 1000 + 400 이다. 이 때의 값은 16진수임에 유의해서 계산한다. 이 계산을 자유자재로 할 수 있을 만큼 연습해야 할 것이다.

혹자는 여기까지 읽으면 궁금한 점이 하나 있을 것이다. (필자는 궁금하다.) 과연 PE Format은 누가 어떻게 사용하는 것인가? 이에 대한 답은 윈도우 커널에서 PE loader가 로드 시키는 것이다. 필자의 추측으론, 어떠한 실행파일을 실행 시키면, 시스템 콜을 OS에 전달하고, 이에 대한 처리로써, PE loader를 실행시켜 메모리에 PE구조에 맞게 각 섹션들과 데이터들을 로드시키지 않나 생각한다.

3-2 dos헤더

이제 본격적으로 PE구조에 관한 사항들을 살펴 보자. 먼저 DOS Header이다. 마이크로 소프트는 DOS파일에 대한 호환성을 고려해 PE구조를 디자인하였다. DOS Header의 모습은 어떠한지 살펴 보자.

위의 부분이 DOS Header 부분이다. 이 중 눈 여겨 볼 것은 딱 2가지 부분이다. e_magic 부분과 e_lfanew 부분이다. E_magic은 젤 앞에 4D 5A부분으로 ASCII 값으로 'MZ'이다. 이는 DOS signature 값으로 항상 이 값이 존재 해야 한다. E_lfanew부분은 제일 마지막 E0 00 00 00 부분으로 이 값은 NT Header 구조체를 가리킨다. 그리고 그 다음 ASCII값으로 PE가 나오기 전까지의 부분은 DOS Stub코드 이다. 이 부분은 도스의 호환성을 위해 마련된 자리이므로 없어도 상관없다. 따라서 컴파일러 설정만 조정하면 이 부분을 없앨 수 있다.

3-3 Nt 헤더

NT헤더 구조체가 어떻게 생겼는지 구경해 보자.


<출처:http://blog.naver.com/PostView.nhn?blogId=cor2quard&logNo=150169612062&beginTime=0&jumpingVid=&from=search&redirect=Log&widgetTypeCall=true>

위의 사진에서 보다시피 NT Header는 ASCII값으로 PE라고 적힌 Signature부분과 FileHeader, OptionalHeader부분으로 구성되어 있다. 이제 각각의 경우를 살펴보자. 부분을 살 펴 보자.

3-3-1 Signature

Signature부분은 ASCII코드 값으로 PE를 나타내며, NT Header의 시작을 표현한다.

3-3-2 file header

파일의 개략적인 속성을 나타내는 구조체이다.

<출처 MSDN>

여기서 우리가 주목해야 할 점은 Machine부분과 NumberOfSections, SizeofOptionalHeader, Characteristics 부분이다.

<Machine>
CPU별로 고유한 값을 가진다. Intel x86호환칩은 14C값을 가진다.

<NumberOfSections>
글자 그대로 섹션들의 개수를 나타낸다.

<SizeofOptionalHeader>
글자 그대로 OptionalHeaer의 크기를 나타낸다. OptionalHeader가 무엇인지는 뒤에서 살펴 볼 것이다.

<Characteristics>
파일의 속성을 나타내는 값이다. 실행파일인지 dll파일인지 등의 정보들이 or연산으로 저장되어 있다.

그림에 표시된 부분이 PE signature 와 PE FileHeader 부분이다.

3-3-3 file optional header

어떤 구조체인지 모습을 살펴 보자

제일 밑에 보면 IMAGE_DATA_DIRECTORY가 있는데 이것 또한 어떻게 생겼는지 훑어 보자.

Optional Header에서 중요한 멤버들을 찍어 보겠다.

Magic, AddressOfEntryPoint, ImageBase, SectionAlignment, FileAlignment, SizeOfImage, SizeOfHeaders, Subsystem, NumberOfRvaAndSizes, DataDirectory 부분이다. 하나하나 살펴 보도록 하겠다.

<Magic>
32비트환경인 경우 10B, 64비트인 경우 20B의 값을 가진다.

<AddressOfEntryPoint>
EP(Entry Point)의 RVA값을 가지고 있다.

<ImageBase>
메모리 상에서 PE파일이 로딩되는 시작 값을 가진다. 즉, PE로더는 메모리에 PE파일을 실행 시킬 때, AddressOfEntryPoint + ImageBase의 값을 EIP값으로 지정한다.

<SectionAlignment, FileAlignment>
파일에서 섹션의 최소단위가 FileAlignment이고 메모리에서 섹션의 최소단위가 SectionAlignmnet이다.

<SizeOfImage>
가상메모리 상에서 PE Image가 차지하는 크기이다.

<SizeOfHeader>
PE헤더의 전체 크기를 나타낸다. (파일상에서) 따라서 파일 시작위치에서 SizeOfHeader만큼 떨어진 위치에서 섹션이 시작된다.

<SubSystem>
시스템 드라이버파일인지, 일반 실행 파일인지 구분한다.

<NumberOfRvaAndSizes>
DataDirectory 배열의 개수를 나타낸다.

<DataDirectory>
데이터 디렉토리 배열에 어떤 값들이 정의되어 있는지 살펴보자.

<출처 : http://keybreak.tistory.com/98>

위에 밑줄 그어져 있는 것들을 유심히 잘 보시기 바란다. 이러한 내용들 까지 다 설명 하려면 날이 다 샐 것 같으니 다음 글에서 설명하도록 하겠다. 간단히 설명하자면, 외부에서 사용할 수 있도록 한 함수들이 정의된 부분이 EXPORT부분이고 외부에서 가져와 쓰는 함수들이 정의된 부분이 IMPORT이고 TLS같은 경우 쓰레드의 생성 시 EP시작 전에 시작되는 부분인데, 보통 이 부분에 바이러스 프로그램을 넣거나, 혹은 디버깅이 불가능하게 만들도록 Anti Debugging기법에서도 많이 쓰인다.

지금까지의 값들을 Hex로 확인하면 다음과 같다.

3-4 section header

이제 거의 다왔다. 조금만 더 참고 인내하자.

섹션은, 안정적인 구조를 위해 구현 되었다. 읽고 쓰기 등의 권한을 주어서 관리하는 것이다. 그래서 이러한 값들을 저장할 공간이 필요하게 되었고 섹션헤더 라는 공간을 만들게 되었다.

<출처: MSDN>

여기서 우리가 주목해야 할 멤버는 VirtualSize, VirtualAddress, SizeOfRawData, PointerToRawData, Characteristics정도 이다.

항목

의미

VirtualSize

메모리 상에서 섹션의 크기

VirtualAddress

메모리 상에서 섹션의 시작주소(RVA)값

SizeOfRawData

파일 상에서 섹션의 크기

PointerToRawData

파일 상에서 섹션의 위치

Characteristics

읽기, 쓰기, 실행 등의 설정 값

여기서 VirtualSize값과 SizeOfRawData값이 따로 존재하는지 궁금해 하시는 분이 있을 수 있습니다. 그 이유는 메모리 상에서의 사이즈 값과, 실제 파일에서의 크기가 다를 수 있기 때문입니다. 이는 아까 NT Header -> Optional Header -> FileAlignment, SectionAlignment 에서도 다루었습니다. 이 부분은 RVA -> RAW변환에서 상당히 중요한 개념입니다. 잘 기억하시기 바랍니다.

'Reversing' 카테고리의 다른 글

인라인 패치  (0) 2014.05.11
Upack 분석중 정리  (0) 2014.05.10
UPX압축 디버깅  (0) 2014.05.09
IAT와 EAT  (0) 2014.05.08
기본 레지스터  (0) 2014.05.04
Posted by flack3r
|