Lab06-03

 

1. 서브루틴 sub_401040이 수행하는 것은 무엇인가?

 

 

 

위 코드에서 확인할 수 있듯이 sub_401030은 sub_401130이라는 새로운 함수를 호출하고 있다.

 

 

2. 서브루틴 sub_401130이 필요로 하는 매개변수는 무엇인가?

 

 

서브루틴 sub_401130은 var_8과 argv라는 두 개의 파라미터 값을 받는다는 것을 확인할 수 있다.

argv의 주소는 argv[0]과 동일하기 때문에 프로그램의 이름을 나타낸다고 볼 수 있다.

var_8이라는 인자에는 AL 레지스터 값을 할당한다. 즉, sub_401040의 리턴 값이 var_8이다.

 

 

3. 해당 함수(sub_401130)에서 switch문을 사용하여 할 수 있는 기능은 무엇인가?

 

 

sub_401130 함수는 switch 구문을 통해 에러메세지 출력, 파일 삭제, 디렉토리 생성, 레지스트리 값 설정, 파일 복사, 100초간 sleep 등의 기능을 한다.

- case 97 : C:\\Temp 디렉토리 생성

- case 98 : C:\\Temp 디렉토리에 IpExistingFileName을 cc.exe라는 이름으로 파일 복사

- case 99 : C:\\Temp\\cc.exe 파일 삭제

- case 100 : HKEY_LOCAL_MACHINE의 Run 레지스트리 오픈, RegSetValueEx API를 이용하여 C:\\Temp\\cc.exe 악성코드를 자동 실행 레지스트리에 등록

- case 101 : 100초간 sleep

- default : 에러메세지 출력

 

 

4. 해당 악성 프로그램에 호스트 기반에 대한 지표는 무엇인가?

 

호스트 기반의 행위는 ① Software\Microsoft\Windows\CurrentVersion\Run 레지스트리 값 변경과 C:\Temp\cc.exe 경로의 파일 생성(복사)으로 볼 수 있다.

 

 

5. 해당 악성 프로그램의 목적은?

 

switch 구문을 이용한 파일 생성, 삭제, 복사, 레지스트리 값 변경, sleep 등이 주 목적이다.

 

Lab06-04

 

1. Lab 6-3과 비교했을 때 main에서 변경된 부분들은 무엇인가?

 

 

Lab 6-3과 비교했을 때 Lab 6-4는 반복문 loop가 추가되었다.

위쪽 블록에서 cmp [ebp+var_C], 1440 부분이 있는데, 총 1440번의 반복문을 수행한다는 의미이고,

이 반복문 안에는 aSuccessParesedC 및 sub_401150 함수가 실행된다.

 

 

2. Lab 6-3과 비교했을 때 sub_401040에서 변경된 부분은 무엇인가?

 

 

먼저, 메인 함수를 확인해보면 for문이 추가된 것을 확인할 수 있다. 총 1440번의 for문을 실행한다.

 

 

파일을 분석하다 보면 HTML 파싱 함수는 sub_401040( ) 함수임을 확인할 수 있다. 하지만, 기존의 함수들과 비교해서 sub_401040( ) 함수는 인자값을 받는다는 점에서 다르다.

sub_401040( ) 함수 내부를 보면 sprintf( ) 함수로 입력된 "Internet Explorer 7.50/pma%d" 문자열은 &szAgent 안에 저장되고, 이 szAgent는 InternetOpenA( ) API의 첫 번째 인자인 User-Agent에 들어가게 된다.

 

 

3. 해당 악성 프로그램에서 네트워크 기반에 대한 지표는 무엇인가?

 

악성코드를 살펴보면 User-Agent를 사용한다는 것을 알 수 있는데 이는 User-Agent의 대표적인 예시인 브라우저를 사용한다는 것과 같은 의미로 이해할 수 있다. 따라서 이는 네트워크 기반 악성코드라고 볼 수 있다. 

 

 

4. 해당 악성 프로그램의 목적은?

 

인터넷 연결 여부를 확인하고, User-Agent로 서버에서 브라우저를 감시하여 사용자가 인터넷을 사용하는지를 관리하는 목적이고, 이전 파일과 비슷하게 HTML 파싱도 존재한다.

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week04_DLL Injection, DLL Ejection  (0) 2021.11.13
Week04_Dreamhack rev-basic-7, rev-basic-8  (0) 2021.11.13
Week03_Dreamhack rev-basic-4, rev-basic-5, rev-basic-6  (0) 2021.10.02
Week03_IAT, EAT  (0) 2021.10.02
Week02_패킹&UPX  (0) 2021.09.25

1. DLL(Dynamic Link Library)이란?

 

→ 동적 링크 라이브러리

→ 컴파일 시 함수의 코드가 실행 파일에 복사되는 것이 아닌 실행 중 라이브러리에 있는 함수를 호출

 

- 컴파일러 : 함수의 실제 주소가 저장될 위치만 준비 후 CALL

- PE로더 : 준비한 위치에 실제 함수의 주소를 입력

 

→ 자주 사용되는 표준 함수를 미리 만들어서 모아 놓은 파일

 

장점 : 응용프로그램의 모듈화 및 쉬운 기능 업데이터, 중복 코드 사용의 감소로 인한 적은 리소스 사용 등

 

- DLL 호출 방법

1) 암시적(Implicit linking)

→ 실행 파일 자체에 어떤 dll의 어떤 함수를 사용할지에 대한 정보를 포함시킨 후 운영체제가 프로그램 실행 시 해당 함수들을 초기화한 후 이용

2) 명시적(Explicit linking)

→ 프로그램이 실행 중일 때 API를 이용하여 DLL 파일이 있는지 검사하고 동적으로 원하는 함수만 불러와서 사용

 

2. DLL Injection

 

 

→ 다른 프로세스에 특정 dll 파일을 강제로 삽입시키는 것

→ 다른 프로세스에게 LoadLibrary( ) API를 호출하도록 명령하여 원하는 dll을 Loading시키는 것 

 

※ 사용 목적

- LoadLibrary() API를 이용하여 어떤 dll을 로딩시키면 dllmain()함수가 실행됨

- 강제 삽입된 dll의 DllMain() 함수가 실행되면 해당 프로세스의 메모리에 대한 접근 권한을 갖기 때문에 사용자가 원하는 다양한 일을 수행할 수 있음

→ 악의적인 용도로 사용될 가능성이 있음  

 

 

1. 제어권 얻기 - OpenProcess()

→ 주입할 대상 프로세스에 대한 handle을 얻는다.

 

 

OpenProcess() API를 이용하여 notepad.exe 프로세스의 handle을 얻는다.

 

 

2. DLL 경로를 프로세스에 기록 - VirtualAllocEX(), WriteProcessMemory()

→ 대상 프로세스 내의 DLL 경로를 넣기 위한 메모리를 할당하고, 할당된 메모리에 DLL 경로를 기록한다.

 

 

notepad.exe에 로딩할 DLL 파일의 경로를 알려주기 위해 VirtualAllocEx() API를 이용하여 메모리를 할당한다.

 

 

WriteProcessMemory() API를 이용하여 DLL 경로를 써준다.

 

 

3. DLL 로드 작업 - LoadLibraryW()

→ 원하는 DLL을 로드한다.

 

 

notepad.exe 프로세스가 LoadLibraryW() API를 이용하여 myhack.dll을 불러내야 하기 때문에 LoadLibraryW() API 주소를 알아내야 한다. 

→ 실제로는 운영체제에서 kernel32.dll은 프로세스마다 같은 주소에 로딩되므로 InjectDll 프로세스에 임포트된 LoadLibraryW()와 notepad 프로세스에 임포트된 LoadLibraryW() 주소는 동일하다.

 

 

4. 원격 스레드 생성을 통한 프로세스에서 로드 - CreateRemoteThread(), NtCreateThreadEx()

→ 대상 프로세스에 Thread를 생성하고, LoadLibaryW() 함수를 실행시킨다.

 

 

CreateRemoteThread()는 다른 프로세스에게 스레드를 실행시키도록 하는 함수이다.

이를 이용하여 notepad.exe가 LoadLibraryW() API를 호출하도록 한다.

 

CreateRemoteThread() API 구조

 

→ IpStartAddress는 스레드 함수 주소, IpParameter는 스레드 파라미터 주소이다.

→ 이 주소들은 대상 프로세스(hProcess)의 메모리 내의 공간의 주소여야 한다.

 

3. DLL Ejection

 

→ 다른 프로세스에 특정 DLL 파일을 강제로 빼내는 것

→ 다른 프로세스에게 FreeLibrary() API를 스스로 호출하도록 명령하는 것

 

 

1. 프로세스에 로딩된 DLL 정보 구하기 - CreateToolhelp32Snapshot()

→ 프로세스에 로딩된 DLL의 정보를 얻는다.

 

 

CreateToolhelp32Snapshot() API를 통해 프로세스에 로딩된 DLL의 정보를 얻을 수 있다.

 

 

2. 대상 프로세스 핸들 구하기 - OpenProcess()

→ 프로세스 ID를 이용하여 대상 프로세스의 프로세스 핸들을 구한다. 추후에 이를 이용하여 CreateRemoteThread() API를 호출한다.

 

 

 

3. FreeLibrary() API 구하기 - GetModuleHandle(), GetProcAddress()

→ hModule(모듈핸들)로 DLL의 핸들 값을 받아온다.

 

 

모든 프로세스에서 FreeLibrary()의 주소는 동일하기 때문에 EjectionDll.exe에서 로딩된 API의 주소를 불러오고 있다.

 

 

4. 대상 프로세스에 스레드를 실행 - CreateRemoteThread(), NtCreateThreadEx()

→ 외부 프로세스에 스레드 함수(FreeLibrary)를 실행시킨다.

 

 

스레드 함수로 FreeLibrary()를 지정하고 Ejection할 DLL의 주소를 넘겨주면 대상 프로세스에서는 FreeLibrary() API가 호출된다.

 

 

 

※ 출처

https://fistki.tistory.com/6

https://fistki.tistory.com/9

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week05_Lab06-03, Lab06-04  (0) 2021.11.20
Week04_Dreamhack rev-basic-7, rev-basic-8  (0) 2021.11.13
Week03_Dreamhack rev-basic-4, rev-basic-5, rev-basic-6  (0) 2021.10.02
Week03_IAT, EAT  (0) 2021.10.02
Week02_패킹&UPX  (0) 2021.09.25

Dreamhack rev-basic-7

 

먼저 문제 파일을 x64dbg로 열어보았다.

 

 

위와 같이 "Input: " 문구가 출력되는 부분에 breakpoint를 걸어준 후 함수 내부로 들어가 보았다.

 

 

함수 내부에서 위와 같은 코드들을 발견할 수 있었다.

여기서 test eax, eax를 통해 eax가 0인지 아닌지 판단하는 부분을 찾아볼 수 있는데,

이때 eax 값이 0이면 Wrong을 출력하고, 아니면 Correct를 출력한다는 것을 예상할 수 있다.

 

 

실행파일에 출력되는 Input: 문구에 아무 값이나 입력해준 후 correct와 wrong을 판단하기 위해 호출되는 함수 내부에 들어가보면 위와 같은 코드를 확인할 수 있다.

 

 

이 부분의 코드를 보면,

rsp의 인덱스 값을 eax에 저장해준 후 eax와 7을 and 연산해주고 있다.

또한 rcx에 인덱스 값을 저장해준 후 이 값을 rsp+8에 저장해ㅜㄴ다.

다음으로 내가 임의로 입력했던 asdf가 rdx에 저장되고, eax의 하위 4바이트가 ecx에 저장된다.

 

 

여기에서 rol al,cl을 해주는 부분이 있는데,

rol 명령어는 왼쪽 회전, 즉 각 비트를 왼쪽으로 시프트해주는 것을 뜻하는 명령어이다.

예를 들어 al=10000000일 때 rol al,1을 하게 되면 al=00000001이 된다.

 

즉, eax의 하위 4바이트를 ecx의 하위 4바이트만큼 rol해준 후 eax와 인덱스 값을 xor하여 eax에 저장한다.

 

해당 배열은 [7FF727523000]에서 확인할 수 있다.

 

 

해당 위치로 가 보면 위와 같이 저장되어 있는 값들을 확인할 수 있다.

 

이때 (p[i] rol (i&7))^i의 값이 e[i]가 되도록 하는 값을 찾아주면 된다.

예를 들어 x52의 경우 (01010010 rol (0&7))^0 을 해주면 0x52=R이 나오게 된다.

 

이와 같이 해당 배열의 값을 찾아주면 "Roll_the_left!_Roll_the_right!" 라는 플래그를 얻을 수 있다.

 

 

위와 같이 Correct가 잘 출력되는 것을 확인할 수 있다.

 

 

Dreamhack rev-basic-8

 

이 문제도 역시 x64dbg를 이용하여 파일을 확인해보았다.

 

 

위 문제와 마찬가지로 메인 부분에 breakpoint를 걸어주었다.

 

 

함수 내부로 들어가 보니 마찬가지로 Correct와 Wrong을 결정해주는 분기문이 있었다.

 

 

또한, 분기문 확인을 위해 실행되는 함수의 내부를 들어가 보면 위와 같은 코드를 확인할 수 있다.

 

 

여기서 이 부분을 보면 eax 부분에 0xFB를 곱하고 0xFF를 AND 연산해주는 과정이 있다.

 

데이터를 확인하기 위해 해당 부분(7FF6BCC03000)으로 가 보았다.

 

 

메모리 덤프에서 위와 같은 값들을 확인할 수 있었다.

 

 

아까 코드에서 볼 수 있었듯이 0xFB를 곱한 후 곱한 결과를 0xFF로 나눈 나머지를 구했기 때문에 해당하는 값을 찾아주기 위해 brute force로 값을 구하는 코드를 작성해주었다.

이를 실행한 결과 "Did_y0u_brute_force?"라는 플래그를 얻어낼 수 있었다.

 

 

위와 같이 Correct가 잘 출력되는 것을 확인할 수 있다.

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week05_Lab06-03, Lab06-04  (0) 2021.11.20
Week04_DLL Injection, DLL Ejection  (0) 2021.11.13
Week03_Dreamhack rev-basic-4, rev-basic-5, rev-basic-6  (0) 2021.10.02
Week03_IAT, EAT  (0) 2021.10.02
Week02_패킹&UPX  (0) 2021.09.25

Dreamhack rev-basic-4

 

먼저 x64dbg를 이용하여 주어진 문제 파일을 열어보았다.

 

 

위와 같이 "input: " 부분이 출력되는 구간을 찾았고, 이 부분이 메인 함수와 관련 있을 것이라 생각했기 때문에 breakpoint를 걸어주었다. 

 

 

F7 키를 이용하여 breakpoint를 걸었던 call 함수 내부로 들어가 보니 위와 같은 코드를 확인할 수 있었다.

여기서 test eax, eax를 통해 eax가 0인지 아닌지 판단하는 부분을 찾아볼 수 있는데,

이때 eax 값이 0이면 Wrong을 출력하고, 아니면 Correct를 출력한다는 것을 예상할 수 있다.

 

 

위 부분에서 계속 진행을 하다가 "input: " 부분에 아무 문자나 입력한 후 엔터를 눌러주었더니 위와 같은 코드로 이동했다.

 

 

위 부분의 코드를 하나씩 살펴보면,

00007FF760361031 주소 부분에서 sar eax, 4의 의미는 eax=eax>>4, 즉 eax에 있는 입력 값을 오른쪽으로 4비트 shift 해주는 것을 의미한다.

또한, 00007FF760361041 주소 부분에서 shl ecx,4의 의미는 ecx=ecx<<4, 즉 ecx에 있는 입력 값을 왼쪽으로 4비트 shift 해주는 것을 의미한다.

다음으로 00007FF760361044 주소 부분에서 and ecx,F0을 해주고 있기 때문에 5-8번째 비트 값만 남도록 설정되므로 최종 값은 ecx eax 즉, 원래는 eax ecx 형태였던 것이 뒤바뀐 순서로 나오게 된다.

이를 덤프의 7FF760363000에 있는 값과 비교해주고 있기 때문에 이 주소로 이동해보았다.

 

 

해당 주소로 가보니 위와 같은 값들을 확인할 수 있었다.

따라서 아까 위의 코드에서 봤던 연산을 반대로 수행해주면 값을 알아낼 수 있을 것이다.

즉, 0x24는 0010 0100이다. 이때 0010은 ecx의 값, 0100은 eax의 값이 된다.

ecx는 왼쪽으로 4bit shift 해주었기 때문에 하위 4비트,였을 것이고, eax는 오른쪽으로 4bit shift 해주었기 때문에 상위 4비트였을 것이다.

따라서 원래의 입력값은 0100 0010 = 0x42가 되고, 0x42를 아스키코드 값으로 변환해주면 B가 된다.

 

이와 같은 방법으로 위 덤프에 있는 값들을 모두 수행해주면 "Br1ll1ant_bit_dr1bble_<<_>>"라는 값이 나온다.

 

 

따라서 위와 같이 답을 입력해주면 Correct 문구가 잘 출력되는 것을 확인할 수 있다.

 

Dreamhack rev-basic-5

 

 

이 문제도 역시 x64dbg를 사용했고, 위와 같이 메인 함수 부분이라고 생각되는 부분에 breakpoint를 걸어주었다.

 

 

F7키를 사용하여 함수 내부로 들어가 보니 마찬가지로 사용자의 입력 값에 따라 test를 해주어 Correct와 Wrong 문구를 출력해주는 분기문을 확인할 수 있었다.

 

 

위와 같이 "input : " 부분에 아무 값을 입력했더니 위와 같은 코드로 넘어가는 것을 볼 수 있었고, 이후에 F8키로 계속 코드를 진행시켜보았다.

 

 

이때 위 부분의 코드를 자세히 살펴보자.

00007FF6BC4E1031 주소 부분에서 eax에 입력된 값을 넣어주고,

00007FF6BC4E1034 주소 부분에서 inc ecx, 즉 ecx 값을 1씩 증가시켜준다.

00007FF6BC4E1042 주소 부분에서는 eax와 ecx 값을 더해준 값을 eax에 저장한다.

그리고 나서 덤프의 00007FF6BC4E3000에 있는 값과 비교해주고 있다/

 

00007FF6BC4E3000에 있는 값을 확인해 보기 위해 이 주소로 이동했다.

 

 

해당 주소에는 위와 같은 값이 저장되어 있었다.

 

즉, 0과 가장 마지막으로 입력한 문자의 hex 값을 더한 값이 4C가 되기 때문에 가장 마지막 입력값의 hex 값은 4C이다.

다음으로 4C와 마지막에서 두번째 입력한 문자의 hex 값을 더한 값이 98이 되어야 하기 때문에 마지막에서 두번째 입력값은 98-4C=4C, 즉 이를 아스키코드값으로 변환하면 L이 된다. 

 

이와 같은 방법으로 모두 연산을 진행해주면 "All_l1fe_3nds_w1th_NULL"라는 문자열이 나온다.

 

 

위와 같이 Correct가 잘 출력되는 것을 확인할 수 있다.

 

Dreamhack rev-basic-6

 

 

이 문제도 역시 메인 함수를 찾아 breakpoint를 걸어 주었다.

 

 

breakpoint를 걸어준 부분의 함수 내부로 들어가 보니 위와 같이 "Input : " 값을 받아 값을 비교해준 후 Correct와 Wrong 문구를 출력해주는 부분이 있었다.

 

 

마찬가지로 한 단계씩 코드를 진행해 보며 확인해 보았고, 

00007FF7D787116E 주소 부분에서 F7키를 눌러 내부로 들어가보았다.

 

 

이도 마찬가지로 한 단계씩 코드를 진행시켜 보았고, 다음 코드를 확인해보자.

 

 

00007FF7D7871024 주소 부분에서는 rax에 0을 넣어주었다.

00007FF7D7871028 주소 부분에서는 rcx에 사용자의 입력 값을 넣어주었다.

00007FF7D787102D 주소 부분에서는 eax에 rcx 값과 rax 값을 더해준 값을 넣어주었다.

여기서 첫번째 반복문의 경우에는 rax 값이 0이므로 입력값의 가장 첫번째 문자가 들어가게 된다.

00007FF7D7871031 주소 부분에서는 rcx에 7FF7D7873020이라는 주소를 넣어주었다.

 

 

이때 00007FF7D7871038 주소 부분에서는 eax에 rcx 값과 rax 값을 더한 주소에 해당되는 값을 넣어준다.

즉, 위에서 내가 입력했던 값이 asdf였고, 가장 첫번째 문자인 a는 0x61이므로 7FF7D7873020 + 61 = 7FF7D7873081 주소에 해당되는 값인 EF가 eax에 들어가게 된다.

 

00007FF7D787103C 주소 부분에서는 rcx에 반복문을 돈 횟수를 넣어준 후, rdx에 7FF7D7873000의 주소를 넣어준다.

 

 

덤프를 통해 확인해보면 00007FF7D7873000에 있는 값은 위와 같다.

 

00007FF7D7871047 주소 부분에서는 ecx에 rdx 값과 rcx 값을 더해준 값을 넣어준다.

00007FF7D787104B 주소 부분에서는 eax와 ecx 값을 비교해준 후 같으면 반복문을 다시 돌고, 아니면 Wrong 문구를 출력한다.

 

 

즉, 7FF7D7873020와 입력값 중 첫 번째 문자의 hex 값을 더해준 주소에 해당되는 값이 00007FF7D7873000에 있는 값과 같아야 하므로 덤프에서 그 값을 찾아주면 00007FF7D7873072에 있는 것을 확인할 수 있다.

따라서 첫 번째 입력값은 00007FF7D7873072-00007FF7D7873020=0x52이므로 이를 아스키코드로 변환하면 R이 된다.

 

위와 같은 과정을 반복해주면 "Replac3_the_w0rld"라는 값을 얻을 수 있다.

 

 

따라서 이 값을 입력해보면 위와 같이 Correct가 잘 출력되는 것을 확인할 수 있다.

 

 

 

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week04_DLL Injection, DLL Ejection  (0) 2021.11.13
Week04_Dreamhack rev-basic-7, rev-basic-8  (0) 2021.11.13
Week03_IAT, EAT  (0) 2021.10.02
Week02_패킹&UPX  (0) 2021.09.25
Week02_Dreamhack rev-basic-2, rev-basic-3  (0) 2021.09.25

1. IAT(Import Address Table)

 

→ 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지 기술한 테이블

 

▶ DLL (Dynamic Linked Library)

 

→ '동적 연결 라이브러리'

→ 32bit Windows 환경을 제대로 지원하기 위해서는 기본적으로 매우 많은 라이브러리 함수를 사용해야 한다.

→ 여러 프로그램이 동시에 실행되어야 하는 상황에서 모든 프로그램마다 위와 같이 동일한 라이브러리가 포함되어 실행된다면 심각한 메모리 낭비를 불러오게 된다.

 

▷ DLL 로딩 방식

 

1) Explicit Linking

: 프로그램 내에서 사용되는 순간에 로딩하고, 사용이 끝나면 메모리에서 해제시키는 방법

2) Implicit Linking

: 프로그램을 시작할 때 같이 로딩되어 프로그램을 종료할 때 메모리에서 해제되는 방법

 

★ IAT는 Implicit Linking에 대한 메커니즘을 제공하는 역할을 한다.

 

▷ DLL 장점

 

- 프로그램 내에 라이브러리를 포함시키지 않고 별도의 파일로 구성하여 필요할 때마다 불러 사용할 수 있다.

- 일단 한 번 로딩된 DLL의 코드와 리소스는 Memory Mapping 기술로 여러 프로세스에서 공유하여 사용할 수 있다.

- 라이브러리가 업데이트 되었을 때 해당 DLL 파일만 교체하면 되기 때문에 쉽고 편하다.

 

 

▶ IAT(Import Address Table)

 

▷ VA, RVA, RAW

1) VA(Virtual Address)

: 프로세스 가상 메모리의 절대주소, 즉 실제 메모리에 로딩되는 주소

2) RVA(Relative Virtual Address)

: ImageBase로부터의 상대 주소(메모리에 로딩된 상태)

3) RAW 

: 디스크 상의 파일에서의 주소, 즉 PE 파일이 로딩되기 전 File Offset

 

★ RAW = RVA - VirtualAddress + PointerToRawData

 

▷ IID(IMAGE_IMPORT_DESCRIPTOR) 구조체

 

출처 : Microsoft Platform SDK - winnt.h

 

☆ 구조체 중요 멤버

 

1) OriginalFirstThunk

: INT(Import Name Table)의 주소(RVA)

2) Name

: Library 이름 문자열의 주소(RVA)

3) FirstThunk

: IAT(Import Address Table)의 주소(RVA)

 

▷ notepad.exe의 kernel32.dll에 대한 IMAGE_IMPORT_DESCRIPTOR 구조

 

출처 : https://foxtrotin.tistory.com/331

 

▷ PE Loader가 Import 함수 주소를 IAT에 입력하는 순서

 

1) IID의 Name 멤버를 읽어서 라이브러리의 이름 문자열("kernel32.dll")을 얻는다.

2) 해당 라이브러리("kernel32.dll")를 로딩한다.

3) IID의 OriginalFirstThunk 멤버를 읽어서 INT 주소를 얻는다.

4) INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME주소(RVA)를 얻는다.

5) IMAGE_IMPORT_BY_NAME 의 Hint(ordinal) 또는 Name 항목을 이용하여 해당 함수("GetCurrentThreadId")의 시작 주소를 얻는다.

6) IID의 FirstThunk(IAT) 멤버를 읽어서 IAT의 주소를 얻는다.

7) 해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.

8) INT가 끝날 때까지 (NULL을 만날때 까지) 4~7의 과정을 반복한다.

 

 

▶ notepad.exe를 이용한 IAT(Import Address Table) 실습

 

먼저 IMAGE_OPTIONAL_HEADER의 DataDirectory 구조체의 배열 정보를 확인해야 한다.

 

 

위에서 볼 수 있듯이 RVA 주소는 32B9B0, Size는 190임을 확인할 수 있다.

 

 

실제 해당 주소에 가 보면 RVA와 Size 값을 볼 수 있다.

 

이제 RVA 값을 알기 때문에 RWA(offset)을 구할 수 있다.

RAW = RVA - VirtualAddress + PointToRawData 이므로

RAW = 32B9B0 - 1000 + 0400 = 32ADB0

 

 

위 그림에서 블록 부분이 IID의 구조체 배열이다. (.text)

 

1) Name

 

 

2E746578 - 1000 + 0400 = 2E745B78

 

2. EAT(Export Address Table)

 

→ DLL 자신이 서비스하는 함수에 대한 정보를 기술해 놓은 테이블

→ 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 핵심 메커니즘

→ 즉, 라이브러리가 가진 함수를 다른 프로그램에서 사용할 수 있도록 하는 것

 

▶ EAT(Export Address Table)

 

notepad.exe의 kernel32.dll에 대한 IMAGE_EXPORT_DIRECTORY 구조

 

 

▷ IMAGE_EXPORT_DIRECTORY 구조체

 

출처 : Microsoft Platform SDK - winnt.h

 

☆ 구조체 중요 멤버

 

1) NumberOfFunctions 

: 실제 Export 함수 개수

2) NumberOfNames 

: Export 함수 중에서 이름을 가지는 함수 개수 (NumberOfFunctions)

3) AddressOfFunctions

: Export 함수 주소 배열 (배열의 원소 개수 = NumberOfFunctions)

4) AddressOfNames

: 함수 이름 주소 배열 (배열의 원소 개수 = NumberOfNames)

5) AddressOfNameOrdinals

: Ordinal 주소 배열 (배열의 원소 개수 = NumberOfNames)

 

▷ EAT 동작 원리

 

1) AddressOfNames 멤버를 이용해 ‘함수 이름 배열’로 이동한다.

2) ‘함수 이름 배열'은 문자열 주소가 저장되어 있어 문자열 비교를 통해 원하는 함수 이름을 찾는다.

3) IID의 OriginalFirstThunk 멤버를 읽어 INT 주소를 얻는다.

4) INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻는다.

5) IMAGE_IMPORT_BY_NAME의 Hint(ordinal) 또는 Name 항목을 이용해 해당 함수의 시작 주소를 얻는다

→ GetProcAddress(“GetCurrentThreadld”)

6) IID의 FirstThunk(IAT) 멤버를 읽어 IAT 주소를 얻는다.

7) 해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.

8) INT가 끝날 때까지(NULL을 만날 때까지) 위에서 4~7 과정을 반복한다.

 

 

▶ notepad.exe를 이용한 EAT(Export Address Table) 실습

 

 

 

 

 

 

1. 패킹(Packing)

 

1) 정의

 

- 실행압축

- 실행(PE) 파일을 대상으로 파일 내부에 압축해제코드를 포함하고 있어 실행 시 메모리에서 압축을 해제 시킨 후 실행시키는 기술

- 코드 분석을 통한 프로그램의 악용을 막는 목적으로 활용

 

2) 종류

 

 Compressor

- 파일의 사이즈를 줄여 배포를 쉽게 하기 위해 사용하는 packer

- 악성코드에서 많이 활용

- Petite, ASPack, MEW, FSG, UPX 

 

 Protector

- 원본 코드를 보호하기 위해 사용하는 packer

- 주로 상용 프로그램이나 온라인 게임 파일을 패킹할 때 많이 활용

- 코드 가상화 기법과 코드 난독화 기법을 많이 활용

- Themida, Yoda, ASProtect, armadilo 

 

3) 패킹을 하는 이유

 

- 데이터 압축

- 악성코드에서는 작은 용량으로 빠르고 많이 퍼지게 하며 분석이 불가능하게 하기 위한 목적

- 데이터 보호

- 취약하게 나타날 수 있는 중요정보를 포함한 어플리케이션에 대한 노출 최소화

 

4) 패킹된 파일의 특징

 

- Section Name이 일반적이지 않거나 패커의 이름을 가짐

- Code Section은 보통 첫번째 Section이지만, 패킹 시 첫째가 아닐 수 있음

- Code Section은 보통 EP를 가리키므로 MEM_Excute권한을 가지지만, 패킹 시 그렇지 않을 수 있음

- Unpacking하면 Packing된 데이터가 저장되어 있던 세션이 비어있거나 Raw Size Virtual Size의 차이가 지나치게 큰 세션이 존재

 

2. UPX를 이용한 패킹 실습 (notepad.exe)

 

먼저 UPX(UPX307w) 패커를 이용했고, notepad(32bit).exe로 패킹을 진행했다. 

 

 

위와 같이 UPX가 실행되는 것을 확인할 수 있다.

 

 

위와 같은 명령어를 입력하여 notepad_org.exe (notepad 원본) 파일을 notepad_upx.exe라는 이름으로 패킹했다.

Packed 1 file 이라는 문구가 떴으므로 패킹에 성공한 것을 알 수 있다.

 

 

위와 같이 해당 파일에 잘 저장된 것을 확인할 수 있고, 원본에 비해 패킹된 파일의 용량이 적어진 것을 확인할 수 있다.

 

 

notepad_org.exe

 

notepad_upx.exe

 

PEiD라는 툴을 이용하여 두 파일을 비교해보면 section부터 다르다는 것을 확인할 수 있다.

원본 파일은 .text이고, 패킹된 파일은 UPX1으로 확인된다.

 

notepad_org.exe

 

notepad_upx.exe

 

PEview로도 두 파일을 비교해보면 위와 같이 역시 구조가 다른 것을 확인할 수 있다.

 

3. tool 없이 언패킹하기

 

 

툴 없이 언패킹을 진행하기 위해 x32dbg를 이용하여 패킹된 파일을 열어보았다.

 

 

Ctrl+F8을 눌러 step over을 하다보면 반복되는 LOOP 아래 코드를 찾을 수 있다.

메모리의 변화가 나타나는 것을 통해 무언가 압축을 해제하여 데이터를 쓰고 있다는 것을 알 수 있다.

 

 

위와 같이 010141A3을 보면 pop esi를 찾을 수 있고, 이곳에 breakpoint를 걸어준 후 F9를 누르면 압축 해제가 된다.

 

 

압축 해제 후 F8로 실행하다 보면 위와 같은 LOOP를 만나게 된다.

이는 cmp 명령어로 값을 계속 비교하며 무결성 체크를 해주는 역할을 한다.

 

 

다시 F8로 진행을 하다 보면 위와 같이 IAT 테이블을 만드는 작업을 하는 LOOP를 발견할 수 있다.

 

 

위의 LOOP를 탈출한 후 좀 더 진행시켜보면 popad 명령어를 통해 스택을 정리하고 jmp 하는 코드를 발견할 수 있다.

이는 UPX에서 사용하던 스택 정보를 원래대로 돌리기 위한 작업이다.

 

 

점프를 하고 난 후의 코드를 확인해보면 위와 같은 코드를 확인할 수 있는데, 이는 패킹 전의 코드와 똑같은 것을 확인할 수 있다.

 

notepad_upx.exe

 

notepad_org.exe

 

 

 

 

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week03_Dreamhack rev-basic-4, rev-basic-5, rev-basic-6  (0) 2021.10.02
Week03_IAT, EAT  (0) 2021.10.02
Week02_Dreamhack rev-basic-2, rev-basic-3  (0) 2021.09.25
Week02_PE 파일 구조  (0) 2021.09.25
Week01_abex' crackme #1  (0) 2021.09.11

Dreamhack rev-basic-2

 

문제 파일을 x64dbg로 열어보았다.

 

 

먼저 메인 함수를 찾기 위해 F8키로 계속 확인해보다가 cmd 창에 "input: "이라는 문구가 뜨는 함수 호출 부분에 breakpoint를 걸어주었다.

 

 

위와 같이 메인 함수를 찾을 수 있었고, input : 부분에 값을 입력하면 rsp+20 스택에 저장되고 

test eax, eax를 통해 eax가 0인지 아닌지 판단하는 부분을 찾아볼 수 있는데,

이때 eax 값이 0이면 Wrong을 출력하고, 아니면 Correct를 출력한다는 것을 예상할 수 있다.

 

 

위와 같이 아무거나 입력하면 Wrong이 출력되는 것을 확인할 수 있다.

 

 

IDA로 코드를 확인해보았더니 위와 같았고, 140001000 부분의 주소로 가보았다.

 

 

위 코드를 보면 배열 aC에 들어 있는 값을 4byte씩 넘어가면서 비교하는 것을 확인할 수 있다.

 

 

위와 같이 aC 배열을 확인해 보면 "Comp4re_the_arr4y"가 저장되어 있는 것을 확인할 수 있다.

 

 

위와 같이 Correct가 잘 출력되는 것을 확인할 수 있다.

 

 

Dreamhack rev-basic-3

 

이 문제도 역시 x64dbg로 문제 파일을 열어보았다.

 

 

마찬가지로 사용자에게 입력받는 함수가 출력되는 함수에 breakpoint를 걸어주었다.

 

 

함수 내부를 확인해보았더니 위와 같이 입력받은 값을 test 명령어로 비교해준 후 Correct나 Wrong을 출력하도록 해주는 코드가 있었다.

 

 

함수의 소스코드를 확인해보기 위해 IDA로 열어보았고 위와 같은 코드를 확인할 수 있었다.

 

위 코드에서 byte_7FF767A93000[i] != (i ^ *(unsigend __int8 *)(a1 + 1)) + 2 *i) 라는 식을 확인할 수 있는데,

이 부분에서 7FF767A93000라는 주소에 들어 있는 값과 i와 사용자가 입력한 값을 xor 해준 후 2*i를 더해준 값이 같은지 다른지 비교해준다.

 

이때 xor의 특징을 보면 A^B=C일때 C^B=A이므로 

사용자 입력값^i=7FF767A93000주소값이면 7FF767A93000주소값^i=사용자 입력값이 될 것이다.

 

 

위와 같이 7FF767A93000주소값에는 위와 같이 값이 저장되어 있었다.

0x49 0x60 0x67 0x74 0x63 0x67 0x42 0x66 0x80 0x78 0x69 0x7B 0x99 0x6D 0x88 0x68 0x94 0x9F 0x8D 0x4D 0xA5 0x9D 0x45

이제 위 값들을 이용하여 사용자 값을 알아내기 위한 계산 과정이 필요하다.

i값은 0부터 23까지 반복되고 배열[i] - (2*i) ^ i를 해준 후 해당 hex값을 아스키코드로 바꿔주면 사용자가 입력해야 하는 값을 알아낼 수 있다.

예를 들어 첫번째 값인 0x49 - (2*0) ^ 0 = 0x49 -> 아스키코드값 : I이다.

 

이렇게 모든 값을 변환해주면 "I_am_X0_xo_Xor_eXcit1ng"가 나온다.

 

 

위와 같이 Correct가 잘 출력되는 것을 확인할 수 있다.

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week03_IAT, EAT  (0) 2021.10.02
Week02_패킹&UPX  (0) 2021.09.25
Week02_PE 파일 구조  (0) 2021.09.25
Week01_abex' crackme #1  (0) 2021.09.11
Week01_Dreamhack rev-basic-0, rev-basic-1  (0) 2021.09.11

PE File Header

 

 

위는 PEView를 이용하여 notepad.exe 파일을 실행한 모습이다.

 

1. Dos Header

 

 

Dos Header는 현재 해석된 정보를 보여준다.

IMAGE_DOS_HEADER를 클릭하면 위와 같은 모습이 나타나고 시작주소인 00000000부터 0000003C까지 각각의 주소값에 해당하는 정보가 보여지고 있다.

또한, Value로 나오는 값은 MZ 하나밖에 없는 것을 확인할 수 있는데, 이는 아스키코드로 해석되어 나타나는 값으로 PE파일의 시작 부분에 관련된 정보이다.

또한, 맨 밑에 0000003C라는 주소를 보면 Offset to New EXE Header라는 내용을 확인할 수 있는데, 이는 EXE 헤더가 시작하는 지점에 대한 정보이다. , Data의 값인 000000D8에 해당하는 offset부터 실제 EXE 헤더가 시작된다는 의미이다.

 

 

e_magic값은 PE 파일의 시작 부분의 시그니처이다. , 여기서는 4D 5A (MZ)라는 Raw Data 값을 확인할 수 있다.

e_lfanew값은 exe 헤더의 시작 주소를 가리킨다. 여기서 e_lfanew값은 0000003C였고, 해당 부분을 가보면 F8 00 00 00 인 것을 확인할 수 있다. 이를 뒤집어서 읽은 000000F8이라는 주소를 가보면 50 45 라는 값을 확인할 수 있고, 이에 해당하는 Value값을 보면 PE인 것을 확인할 수 있다.

 

 

2. Dos Stub Code

 

 

위는 MZPE 사이에 있는 DOS Stub 영역으로 윈도우 프로그램을 cmd창에서 실행할 때 발생하는 경고 메세지이다.

 

 

3. NT Header

 

 

위는 시그니처 정보에 해당한다.

실제 시작 주소였던 000000F8을 볼 수 있고 DWORD이기 때문에 50 45 00 00 이라는 4개의 값을 지정해준다.

또한, 이를 데이터로 읽을 때에는 뒤집어서 00004550으로 읽을 수 있고, 이를 아스키코드와 매핑한 Value값은 PE임을 확인할 수 있다.

 

 

NT Header는 파일에 대한 추가적인 유용한 정보들을 포함한다.

① Machine(CPU) : 8664 - 파일이 동작할 수 있는 CPU 종류를 의미한다.

② Number Of Sections : PE 파일을 구성하는 섹션의 수를 의미한다.

③ Time Date Stamp : PE 파일이 만들어진 시간을 의미한다.

④ Size of Optional Header : Optional Header의 크기를 의미한다.

⑤ Characteristics : 현재 파일의 형식을 의미한다. 0002는 실행가능한 파일임을 의미한다.

 

 

4. Optional Header

 

 

① Magic : 현재 사용하는 시스템이 몇 비트인지 확인하는 항목이다.

② Size of Code : 코드 영역의 크기를 확인하는 항목이다.

③ Address Of Entry Point : 프로그램이 시작되는 코드의 주소를 RVA 값으로 저장한다.

④ Image Base : PE 파일이 메모리에 로드되는 시작 주소를 의미한다.

 

 

5. Section Header

 

→ Section Header는 각 섹션의 속성을 정의한 것으로 프로그램의 안정성을 위하여 섹션마다 엑세스 권한이 달라야 한다.

 

 

① Name : 섹션 이름을 의미한다.

② Virtual Size : 메모리 섹션의 크기 정보를 의미한다.

③ RVA : 메모리 섹션의 시작 주소를 의미한다.

④ Size of Raw Data : 파일에서 섹션이 차지하는 크기를 의미한다.

⑤ Pointer to Raw Data : 파일에서 섹션의 시작 주소를 의미한다.

⑥ Characteristics : 파일이 갖는 속성을 의미한다.

'2021-2 STUDY > Reversing Study' 카테고리의 다른 글

Week02_패킹&UPX  (0) 2021.09.25
Week02_Dreamhack rev-basic-2, rev-basic-3  (0) 2021.09.25
Week01_abex' crackme #1  (0) 2021.09.11
Week01_Dreamhack rev-basic-0, rev-basic-1  (0) 2021.09.11
Week01_32bit/64bit 호출 규약  (0) 2021.09.11

+ Recent posts