프로그램 동작 방식
Nag 창은 프로그램을 실행하거나 버튼을 누를 때 나타나는 오류창



- 이 문제에서 해야하는 것은 Nag 창 1, 2를 없애는 것
콜 스택
Call Stack은 프로그램에서 사용하는 서브루틴에 대한 정보를 저장하기 위한 자료구조
Call Stack을 사용하는 목적은 서브루틴 간 호출 순서를 추적할 수 있는 정보를 저장하는 것

[ 콜 스택 분석 ]
Open file → Run (F9) → Pop up Nag1 → Pasue (F12) → Check Call Stack

[ Called from ] : 호출하는 코드
[ Procedure ] : 호출되는 서브루틴
목적 : ReverseMe.NAGs.exe 모듈에서 Nag 창을 띄우는 코드를 찾는 것

사용자 코드에서 서브루틴을 호출하는 코드를 못 찾을 수도 있음
⇒ 스택 영역을 살펴보아야함.


→ 복귀주소를 표시한 것
스택 영역 아랫부분으로 내려와서 서브루틴을 호출하는 모듈이 ReverseMe.NAGs.exe인 것을 찾아야한다.

( 스택 영역에서 엔터를 누르면 코드 영역으로 넘어옴 )

스택에서 복귀주소로 명시된 주소 0042039F의 윗부분에 CALL <JMP,&MFC42.#2514> 와 같이 스택에 나와있는 서브루틴이 호출된 것을 알 수 있다.
Code Cave
코드를 수정할 때, 새로 입력해야할 코드가 기존 코드보다 크기가 작다면 문제가 없지만, 코드 길이가 크다면 문제가 발생함
Code Cave는 작은 공간에 큰 코드를 집어넣으려고 사용하는 기술

사용하지 않는 코드 영역에 필요한 코드를 입력하고,
수정해야 할 부분에는 새로운 코드가 입력된 메모리로 점프하는 코드를 넣는다.
그리고 코드 영역에 입력된 새로운 코드 마지막 부분에 다음에 실행되어야 할 코드로 점프하는 코드를 넣는다.

TEST EAX,EAX : EAX가 0인지 확인 -> 0이면 ZF = 1
JE SHORT 004023BA : ZF =1 이면 004023BA로 점프 / 1이 아니면 점프 안함.
TEST 명령어 : 뒤에 오는 두 값이 모두 0이면 ZF를 1로 설정함.
ZF = 1이 아니면 (= EAX가 0이 아니면) 004023BA로 점프하지 않고 Nag창을 열어주는 서브루틴을 호출한다.
결론 : EAX 값에 따라서 Nag창을 여는 서브루틴을 호출할지 말지를 결정한다.
첫 번째와 세 번째 열리는 창이 Nag창이고 두 번째 열리는 정상이다.
따라서 창을 두 번째만 열도록 프로그램을 수정하면 된다.

[ Cave Code 내부 로직 ]
- TEST EAX, EAX를 Cave Code로 점프하는 명령어로 대체
- 몇 번째로 열리는 창인지 계산 → Nag 창을 구분
Cave Code : 프로그램 실행 코드 영역 (TEXT 영역)에 들어감
창이 몇 번 열렸는지 저장하는 값: 데이터 영역 (DATA 영역)에 저장됨
📌 이 둘은 모두 PE(Windows 실행 파일) 안에 있고, 메모리에 연속적으로 올라옴.
[ PE 구조 ]
|
TEXT 섹션
|
실제 실행되는 코드가 들어감 (여기 캐이브 코드도 들어갈 수 있음)
|
|
DATA 섹션
|
프로그램이 사용하는 변수, 숫자, 기록이 들어감 (예: 창이 몇 번 열렸는지 저장)
|
PE 파일은 실행될 때 메모리에 한 덩어리로 쭉 로딩돼서, 코드 영역도, 데이터도 같은 주소 공간에 연속적으로 들어감.
메모리 맵
메모리 맵을 보면 운영체제가 PE 파일을 메모리에 로딩에서 어떻게 저장하고 있는지 한눈에 볼 수 있다.
[ Address ] : 메모리에서 시작 주소
[ Size ] : 데이터의 크기
[ Section ] : PE 파일 구성요소
[ Access ] : 메모리 맵에 접근할 수 있는 특성
ex) R E → 읽고 실행 가능
코드를 입력하고 싶다면 R E 속성을 가진 .text 영역에 넣어야함.
데이터를 입력하고 싶다면 R W 속성을 가진 .data 영역에 넣어야 함.


00401000 + 00037000 = 00438000
코드 영역의 주소는 00401000부터 시작해서 00437FFF로 끝난다.

마지막 주소에는 데이터 00 1바이트가 들어가 있기 때문에 주소 00438000까지 코드 영역 데이터가 들어가 있는 것을 확인할 수 있다.
Cabe Code → .text 영역
임시로 사용하기 위해서 저장하는 데이터 → .data 영역
어셈블
코드 영역과 메모리 영역 아랫부분으로 내려오면 0만 반복되는 곳을 찾을 수 있다.
코드 영역에서는 주소 00437D6A에 코드를 넣고
메모리 영역에서는 주소 00445E90에 임시로 사용할 숫자를 넣는다.

[ Keep size ] : 새로 들어가는 명령어가 선택한 명령어 크기를 넘을 수 없다는 것
[ Fill rest wih NOPs ] : 나머지 영역을 NOP 명령어로 채운다는 것
NOP 명령어 : 아무 동작도 하지 않고 다음 명령어를 실행한다는 의미
원래 명령어가 새로 들어가는 명령어보다 큰 경우 나머지 공간에는 NOP 명령어가 들어가야 함
선택된 TEST EAX, EAX를 JMP 00437D6A로 교체


① ADD BYTE PTR DS:[445E90], AL
445E90에 1바이트 단위 덧셈
BYTE PTR DS:[445E90]의 의미는 데이터 영역의 주소 445E90에 있는 바이트 단위의 값을 의미함
EAX는 32바이트 값을 저장하고 있는데, 하위 8비트 = 하위 1 바이트 값을 사용할 때 AL이라고 지정
바이트 단위 연산이기 때문에, EAX 값을 모두 사용하지 않고 ‘1’이 저장된 하위 바이트만 사용
프로그램에서 창을 띄울 때만 EAX가 ‘1’로 설정되기 때문에 명령어를 수행한 결과는 주소 445E90에 몇 번째 띄워지는 창인지 순서가 기록된다
② CMP BYTE PTR DS:[445E90], 0
445E90에 2가 저장되었는지 확인
2가 저장되어있다면 ZF를 1로 설정
두 번째 띄워지는 창 (= 정상적인 창)을 구분하기 위함
③ JNE 004203BA
두 번째 창이 아니면 창을 띄우지 않는 로직으로 이동
두 번째 창일 경우에만 ZF가 1로 설정되기 때문에
첫 번째, 세 번째 창일 경우 주소 004203BA로 이동
004203BA는 창을 띄우지 않고 다음 명령어를 수행하는 부분
④ LEA ECX, [ESP+4C]
점프 명령어 입력을 위해 삭제된 원본 코드 입력
주소 00420377 ( TEST EAX, EAX )에 명령어 JMP 00437D6A를 입력했다
5바이트 코드를 입력하기 위해서 모두 8바이트 코드가 사용됐다.
앞에 4바이트 코드는 비교와 점프 구문이기 때문에 없어져도 상관이 없지만,
뒤에 4바이트 코드는 프로그램에서 필요한 코드다.
따라서 Cave Code 안에 원래 동작을 수행할 수 있도록 그래도 넣어둘 필요가 있다.
⑤ JMP 0042037F
두 번째 창이면 창을 띄우는 로직으로 이동
CMP BYTE PTR DS:[445E90], 0를 수행한 결과가 참인 경우에 해당
두 번째 창을 띄우는 경우면 이제는 창을 띄우는 로직으로 이동하도록 한다.
'Reversing > Introduction to reversing' 카테고리의 다른 글
| abex crackme 5 (0) | 2025.05.05 |
|---|---|
| abex crackme 4 (0) | 2025.05.05 |
| abex crackme 3 (1) | 2025.05.05 |
| abex crackme 2 (0) | 2025.05.05 |
| abex crackme 1 (2) | 2025.05.05 |