Try Attack/Reverse Engineering[basic]

[crackme3] abex' 3rd crackme 풀이 및 복원

D4tai1 2018. 11. 8.

1. [abex' 3rd crackme] 풀이시연

1) 실행파일 실행

 [1] 파일 열기

  ▶ 첫 번째 메세지박스는 키파일을 체크 후 확인을 클릭하라고 한다.



  ▶ 두 번째 메세지박스는 파일을 찾을 수 없다고 한다.


2) 개발언어 및 링커 확인


 [1] Pascal계열의 Delphi로 작성됨을 알 수 있다.

 [2] 32비트 실행파일임을 알 수 있다.

 [3] Terbo 링커를 사용하여 링킹한 것을 알 수 있다.

 [4] 헤더타입이 PE라고 적힌 것이 확인되어 Windows용 실행파일임을 알 수 있다.

 [5] ImageBase(기준주소)가 0x400000임을 알 수 있다 

 [6] EntryPoint(시작주소)가 기준주소로부터 0x1000 만큼 떨어진 곳임을 알 수 있다.

 [7] 기타 시그니쳐 및 옵션 세부사항은 직접 하나씩 확인해보면 된다.

 [8] 필자는 Detect It Easy 2.0 툴을 사용하였다.


3) 상상

 [1] 1)에서 실행해본 결과 특정파일 이름이 있는지 확인하는 조건문이 있을 것으로 추측된다.

 [2] 의사코드

MessageBox(핸들번호, "Click OK to check for the keyfile.", "abex' 3rd crackme", MB_OK);

if (특정파일이 있다면?) {

//참인 경우? 실행할 내용

//실행할 내용을 예측해보면 파일을 열기 때문에 WinAPI라면 OPENFILENAME을 사용할 것으로 추측된다.

} else {

MessageBox(핸들번호, "Hmmmmm, I can't find the file!", "Error", MB_OK);

}

종료..

 


 

4) 디버거 툴을 사용한 분석

 [1] Main함수 실행

 

  ▶ 아까 기준주소와 상대적인 상대주소를 얻었으므로 더해서 메인함수의 시작주소가 0x401000임을 알 수 있다,

  ▶ 디버거프로그램으로 F9를 누르면 자동으로 브레이크포인트가 있는 것으로 체크해서 멈춘다.

  ▶ 프로그램을 시작하자마자 MessageBoxA( )함수가 보인다.

  ▶ 0x401000에서 0을 PUSH하는 것으로 보아 OK버튼만 있는 메세지박스가 실행될 것으로 보인다.

  ▶ OK를 누르고 난 뒤 CreateFileA( ) 함수가 보인다.


 [2] CreateFileA( ) API 검색

HANDLE CreateFileA(
   LPCSTR                        lpFileName,            

             //파일이나 장치명 = "abex.l2c"
   DWORD                       dwDesiredAccess,    

             //요청한 access = 0x80000000은 읽기권한을 부여한다.
   DWORD                       dwShareMode,        

             //공유모드 = 0x0은 다른 프로세스에서 사용하지 않는다.

   LPSECURITY_ATTRIBUTES  lpSecurityAttributes,    

            //보안속성 = 0x0은 보안구조체를 가리키지 않는다.
   DWORD                       dwCreationDisposition,

            //기능 = 0x3은 파일이나 장치가 있으면 열고 없으면 ERROR_FILE_NOT_FOUND(2)를 확인한다.
   DWORD                       dwFlagsAndAttributes,    

            //플래그 = 0x80은 특별한속성x
   HANDLE                       hTemplateFile        

            // 기존에 열린파일의 핸들 = 0x0은 참고하지 않는다는 의미이다.
);

  ▶ CreateFileA( ) 함수를 호출 전에 스택에 push한 내용을 대입해서 비교해본다.
  ▶ 종합해보면 [abex' 3rd crackme.exe]파일이 있는 곳에 [abex.l2c]파일을 읽으려고 한다.

  ▶ 이후 내용은 전부 조건문에 따른 메세지박스를 호출하는 것으로 보인다.


 

5) 풀이

 [1] 시작

  ▶ 먼저 "abex' 3rd crackme" 타이틀바, "Click OK to check for the keyfile." 내용, OK버튼이 있는 메세지박스를 띄운다.

 

 [2] CreateFileA( ) 함수 호출

  ▶ "abex.l2c"라는 파일을 읽기위해 열 수 없다면 eax에 -1을 반환한다.

  ▶ eax가 -1("abex.l2c"라는 파일이 없다면)이면 0x401075로 jmp한다.

  ▶ 만약 파일이 있다면 그에 따른 핸들 값을 0x4020CA에 저장한다.

  ▶ 처음 실행하면 [3]으로 이동하고 [3]에서 깨달음을 얻으면 [4]로 이동한다.

 

 [3] 0x401075 내용

  ▶ "Error!" 타이틀바, "Hmmmmm, I can't find the file!" 내용, OK버튼이 있는 메세지박스를 띄운다.

  ▶ OK버튼을 누르면 0x401088에서 프로세스종료 함수를 호출한다.

  ▶ 즉, 나는 파일을 찾을 수 없다는 메세지를 띄우고 종료하게 된다.

  ▶ 여기서 "abex' 3rd crackme.exe"와 같은 위치에 "abex.l2c"파일을 만들어주어야겠다는 생각이 든다.

 

 [4] 0x401039 내용

  ▶ CreateFileA( )함수에서 반환받은 핸들 값을 첫 번째 파라미터에 넣고 GetFileSize( ) 함수를 실행한다.

  ▶ 현재는 NULL로 적혀있지만 실제로 실행하면 0xBC값을 가지고 GetFileSize( ) 함수를 실행한다.

  ▶ 함수 종료 후 파일 사이즈(바이트)를 eax에 반환한다.

  ▶ eax가 0x12(10진수 18)이 아니면 0x401060으로 jmp한다.

  ▶ 즉, CreateFileA( ) 함수에서 열은 "abex.l2c"파일의 사이즈가 18바이트가 아니면 0x401060으로 jmp한다.

  ▶ 처음 실행하면 [5]로 이동하고 깨달음을 얻었다면 [6]으로 이동한다.

 

 [5] 0x401060 내용

  ▶ "Error" 타이틀바, "The found file is not a valid keyfile!" 내용, OK버튼이 있는 메세지박스를 띄운다.

  ▶ OK버튼을 누르면 0x401088에서 프로세스종료 함수를 호출한다.

  ▶ 즉, 찾는 파일의 키파일이 아니라는 메세지를 띄우고 종료한다.

  ▶ 여기서 "abex.l2c"파일의 사이즈를 18바이트를 지정해주어야겠다는 생각이 든다.

 

 [6] 0x40104B 내용

  ▶ "Well done!" 타이틀바, "Yep, keyfile found!" 내용, OK버튼이 있는 메세지박스를 띄운다.

  ▶ OK버튼을 누르면 0x401088에서 프로세스종료 함수를 호출한다.

  ▶ 키파일을 찾았다는 메세지를 띄우고 종료한다.

 

 [7] 파일생성

  ▶ 위 분석결과로 유추했을거라고 생각한다.

  ▶ 파일명이 "abex.l2c"인 파일을 [abex' 3rd crackme.exe] 파일과 동일경로에 저장한다.

  ▶ 아무내용이나 18바이트만큼 입력 후 저장한다.

  ▶ [abex' 3rd crackme.exe]을 실행한다.

 


6) 원본소스 가복원

<<Event_start>>

USER32.MessageBoxA(NULL,"Click OK to check for the keyfile.","abex' 3rd crackme", MB_OK);


<<Event_OK>>

eax = KERNEL32.CreateFileA(

"abex.l2c", GENERIC_READ, 0, NULL, OPEN_EXISTING, NORMAL, NULL

);


int imsi = eax;


if (eax == -1) {

USER32.MessageBoxA(NULL, "Hmmmmm, I can't find the file!", "Error", MB_OK);

KERNEL32.ExitProcess();

}

else {

eax = KERNEL32.GetFileSize(imsi, NULL);

if (eax != 0x12) {

USER32.MessageBoxA(NULL, "The found file is not a valid keyfile!", "Error", MB_OK);

KERNEL32.ExitProcess();

}

else {

USER32.MessageBoxA(NULL, "Yep, keyfile found!", "Well done!", MB_OK);

KERNEL32.ExitProcess();

}

}

KERNEL32.ExitProcess();


7) C언어 코드로 복구

#include<stdio.h>
#include<Windows.h>

int main() {
	int eax;

	MessageBoxA(NULL, "Click OK to check for the keyfile.", "abex' 3rd crackme", MB_OK);
	eax = CreateFileA(
		"abex.l2c", GENERIC_READ, 0, NULL, OPEN_EXISTING, NULL, NULL
	);

	if (eax == -1) {
		MessageBoxA(NULL, "Hmmmmm, I can't find the file!", "Error", MB_OK);
	}
	else {
		eax = GetFileSize(eax, NULL);

		if (eax != 0x12) {
			MessageBoxA(NULL, "The found file is not a valid keyfile!", "Error", MB_OK);
		}
		else {
			MessageBoxA(NULL, "Yep, keyfile found!", "Well done!", MB_OK);
		}
	}

	return 0;
}


8) 복구한 C언어 코드로 실행






댓글