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

- Delphi로 만들어졌다는 것을 알 수 있음.


< 일련번호를 담은 파일을 생성하는 문제 >
- 어떤 이름의 파일을 생성해야하는 지 알아내야함.
- 생성한 파일을 규칙에 맞도록 변경.
함수 호출 규약 (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 |