본문 바로가기

Reversing/Introduction to reversing

abex crackme 3

프로그램 동작 방식

  • Detect It Easy 프로그램을 통해서 PE 구조를 살펴볼 수 있음
  • 프로그램을 분석하기 전에 가장 먼저 알아야 할 것은 어떤 언어로 개발되었고, 패킹이 되었는지 알아보는 것.
  • Delphi로 만들어졌다는 것을 알 수 있음.

 

실행 화면
 
 

 

오류창
 
 

< 일련번호를 담은 파일을 생성하는 문제 >

  1. 어떤 이름의 파일을 생성해야하는 지 알아내야함.
  2. 생성한 파일을 규칙에 맞도록 변경.

 

함수 호출 규약 (Calling Convention)

: 함수를 호출할 때 인자를 전달하는 방식이나, 함수 실행이 끝나고 스택을 정리하는 방식에 대한 약속

cdecl
인자가 오른쪽에서 왼쪽으로 순서대로 스택으로 전달된다. 함수가 종료될 때 호출자가 피호출자의 스택 프레임을 정리한다.
stdcall
인자가 오른쪽에서 왼쪽으로 순서대로 스택으로 전달된다, 함수가 종료될 때 피호출자가 스스로 스택 프레임을 정리한다.
fastcall
인자가 오른쪽에서 왼쪽으로 순서대로 레지스터를 사용해서 전달된다. 레지스터로 전달되기 때문에 별도로 스택을 정리할 필요가 없다.

 

[ cdecl 방식 ]

① 인자는 오른쪽에 있는 2부터 callee함수에 전달된다.

  • 스택에 있는 값을 사용할 때는 입력한 순서와 반대로 가장 나중에 입력한 값부터 사용할 수 있다.
  • 스택에 넣을 때는 1 → 2 순서로 넣었지만, 스택에서 꺼낼 때는 반대로 2 → 1 순서로 나오기 때문에 2부터 callee 함수에 전달된다.

② callee 함수 (피호출자)가 종료되면, caller 함수 (호출자)가 사용한 스택을 정리해야한다.

  • 4byte 2개 인자를 스택에 넣었기 때문에 총 8byte를 정리해야한다.
  • ADD ESP 8 명령어를 통해서 현재 스택의 위치를 8byte만큼 증가시켜준다.

 

[ stdcall 방식 ]

③ 인자는 오른쪽에 있는 2부터 callee함수에 전달된다.

  • 스택에 넣을 때는 1 → 2로 넣었지만, 꺼낼 때는 2부터 사용할 수 있다.

④ callee함수가 종료되면서 callee 함수 스스로 스택 영역 8byte를 정리한다.

 

[ fastcall 방식 ]

⑤ 레지스터에 인자를 할당할 때는 왼쪽에 있는 1부터 순서대로 할당된다.

⑥ callee함수 내부로 들어가서 레지스터에 들어가 있는 값을 사용할 때는 먼저 스택에 복사한 다음에 연산 할 때는 스택에 복사된 값을 사용한다.

 

부호 있는 숫자의 표현

윈도우에서 음수를 표현할 때 2의 보수 방식으로 표현한다.

  • 즉, 이진수에 대해 NOT 연산을 한 다음에 1을 더해준다.

 

 

프로그램 구조 분석

 

 

00401000 ~ 0040101B

PUSH 0
PUSH 0
PUSH OFFSET 00402000   ; "abex 3rd crackme"
PUSH OFFSET 00402012   ; "Click OK to check for the keyfile."
PUSH 0
CALL DWORD PTR DS:[<&USER32.MessageBoxA>]

 

MessageBoxA 호출

이 부분은 사용자에게 메시지 박스를 띄우는 코드

  • 00402012: 메시지 내용 "Click OK to check for the keyfile."
  • 00402000: 메시지 제목 "abex 3rd crackme"
  • MB_OK | MB_DEFBUTTON1 | MB_APPLMODAL 플래그 사용됨
  • 사용자에게 키파일이 존재하는지 확인하겠다고 알리는 용도입니다.

 

 

0040101C ~ 0040102A

PUSH 0                    ; hTemplateFile = NULL
PUSH 80                   ; dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL
PUSH 3                    ; dwCreationDisposition = OPEN_EXISTING
PUSH 0                    ; lpSecurityAttributes = NULL
PUSH 0                    ; dwShareMode = 0
PUSH 80000000             ; dwDesiredAccess = GENERIC_READ
PUSH OFFSET 00402069      ; lpFileName = "abex.l2c"
CALL DWORD PTR DS:[<&KERNEL32.CreateFileA>]
 

CreateFileA 호출

  • 파일 혹은 오브젝트를 생성하거나 열 수 있는 함수
  • 핸들을 받아와 다른 함수들을 이용해서 열기나 쓰기 등을 할 수 있다.
  • "abex.l2c"라는 키파일이 존재하는지 확인하는 코드.
  • GENERIC_READ, OPEN_EXISTING 옵션으로 열기 시도

 

 

0040102F

MOV DWORD PTR DS:[4020CA], EAX // 4020CA ; CALL DWORD PTR DS:[<&KERNEL32.CreateFileA>]
  • CreateFileA의 반환값(EAX)을 메모리에 저장합니다.
  • 파일이 존재하면 EAX에 유효한 핸들 값이 들어감, 일반적으로 0xFFFFFFFF(-1)보다 크며, Windows 내부에서 파일을 식별하는 용도로 사용된다.
  • 파일이 없으면 INVALID_HANDLE_VALUE= 0xFFFFFFFF(-1)이 반환

 

 

00401034

CMP EAX, -1
  • 파일이 존재하지 않으면 ( = EAX의 값이 1 )이면 “Hmmmm, I can’t find the file!” 메세지를 보여주는 부분으로 넘어간다.
  • 파일이 존재하면, 유효한 파일인지 검증하는 부분으로 이동한다.

 

< 파일이 존재 >

 

00401048

PUSH 0                          ; lpFileSizeHigh = NULL
PUSH DWORD PTR DS:[4020CA]     ; hFile = 이전에 CreateFileA로 얻은 핸들
CALL DWORD PTR DS:[<&KERNEL32.GetFileSize>]
  • GetFileSize(hFile, NULL) 호출
  • 즉, "abex.l2c" 파일의 크기를 구하려는 시도
  • 반환값은 EAX에 들어가며, 이 크기를 기준으로 조건 판단

 

 

00401050

CMP EAX, 12                    ; 파일 크기가 12인가?

 

 

00401053

JNE SHORT 00401060            ; 크기가 12가 아니면 → 실패 처리로 점프
  • JNE = Jump if Not Equal
  • 파일 크기가 정확히 12가 아니라면, "유효하지 않은 키파일" 경고창으로 점프함
  • 파일 크기가 12라면 "Yep, keyfile found!"라는 메시지 박스를 띄움.

 

파일 크기가 12 (16진수) → 10진수 18

 

문제 해결

  • 파일 크기를 9byte로 만들면, 일단 파일 이름은 abex.l2c이기 때문에 파일 이름을 검사하는 CreateFileA() 함수 부분은 통과할 것이다.

 

  • 파일 유효성을 검증하는 부분에 Break Point를 설정한다.
  • 프로그램을 시작하면 Break Point에서 실행이 멈춘다.
  • Break Point에서 실행이 멈춘다는 것은 이전 주소 명령어까지 실행이 완료되었다는 의미.

 

  • 이전 주소를 확인해보면 EAX에 9가 들어있음을 알 수 있다.



'Reversing > Introduction to reversing' 카테고리의 다른 글

abex crackme 5  (0) 2025.05.05
abex crackme 4  (0) 2025.05.05
abex crackme 2  (0) 2025.05.05
abex crackme 1  (2) 2025.05.05
Introduction to reversing - Chapter 01  (0) 2025.05.05