Codeengn Basic RCE 9
Q. StolenByte를 구하시오 Ex) 75156A0068352040
해당 문제를 읽었는데, 처음 보는 개념인 Stolenbyte가 등장하였다. Stolenbyte가 무엇인지 모르겠어서 이에 대해 알고 문제 풀기를 시작하고자 한다.
- Stolenbyte란 무엇일까? 말 그대로 “훔쳐진 바이트”라는 뜻으로,
원래 실행 파일의 초기 코드(OEP 근처)에 있어야 할 바이트가 패킹 과정에서 디스크에서는 사라졌다가,
프로그램이 실제 실행될 때 메모리에서 다시 복구되는 코드 조각을 의미한다.
Q. 왜 이러한 훔쳐진 바이트가 생길까?
UPX와 같은 패커는 프로그램을 압축하기 위해 내부 섹션 구조를 재배치한다.
이 과정에서 아래와 같은 동작이 일어난다.
- 원래 OEP 앞뒤의 코드 일부가 디스크에서 제거됨
- 실행 시 먼저 언패킹 루틴(PUSHAD → 언패킹 → POPAD) 이 수행됨
- 패킹되기 전 OEP의 코드 일부(예: PUSH, MOV 등)가
스택이나 특정 메모리 위치에 몰래 저장됨 - 언패킹이 거의 끝나는 시점에서
이 “원래 코드”를 POPAD 아래 ~ JMP OEP 사이에서 다시 복원함
디스크에는 없고, 실행 중 메모리에서만 다시 나타나는 “원래 코드” → StolenByte
** UPX의 전형적 언패킹 루틴 구조는 다음과 같다:
PUSHAD
[ 압축해제 루프 / 복원 코드 ]
POPAD
[ StolenByte 복구 구간 ]
JMP OEP
여기서 중요한 점은:
- 패킹되기 전 원래 OEP에 있던 코드 일부를
- 패커가 스택(PUSH 명령어들) 또는 특정 메모리 영역에 먼저 몰래 저장해 두었다가
- 언패킹 루틴이 모두 끝나기 직전에 다시 복원해서 실행하도록 만들어 놓는다는 것이다.
- 여기서 POPAD 바로 뒤가 언패킹이 끝나는 지점이고,
POPAD ~ JMP OEP 사이가 바로 StolenByte가 복구되는 구간이라고 기억하면 된다.
해당 문제를 풀기 위해, 09.exe 문제 파일을 다운로드하여 주었다.
7-Zip File Manager로 압축을 풀고 DIE로 파일 정보를 확인해보려고 한다.

압축을 푼 다음에, DIE로 확인해 보니 파일이 패킹이 되어 있음을 알 수 있다.
그래서 upx-4.2.4-win64 폴더 내에서, CMD를 열고 언패킹 명령어를 입력하였다.
** 언패킹 명령어 : upx -d -o (저장할 파일명) (원본 파일명)

- 패킹 파일 / 언패킹 파일 실행 결과 비교


- 원본 09.exe 실행 시
→ 제목: abex' 3rd crackme
→ 내용: Click OK to check for the keyfile.
라는 정상 메시지 박스가 뜬다. - unpacked_09.exe 실행 시
→ 메시지 박스는 뜨지만
→ 내용 부분이?↑P 䧪^? t卞 4Lᄑ 같이 깨져서 나온다.
즉, 언패킹 된 EXE에서 메시지박스에 전달돼야 할 문자열 관련 코드가 제대로 복구되지 못했다는 뜻이다.
문제에서 말하는 StolenByte가 바로 이 부분과 관련되어 있을 것이라고 추측할 수 있다.
- 디버거로 UPX 언패킹 루틴 관찰하기 Immunity Debugger로 09.exe를 열어 UPX 언패킹 루틴을 확인하였다.

코드를 따라가다 보면, UPX의 전형적인 구조인 형태를 볼 수 있다.
004071F0 60 PUSHAD
0040736C 58 POP EAX
0040736D 61 POPAD
이때 우리가 특히 주목해야 할 구간은:
POPAD 바로 아래 ~ JMP 직전이 StolenByte 의심 구간
이 자리에서 원래 OEP에서 수행됐어야 하는 코드가 복구되기 때문이다.

실제로 09.exe의 POPAD 인근 코드를 보면 다음과 같다.
0040736C 58 POP EAX
0040736D 61 POPAD
0040736E 6A 00 PUSH 0
00407370 68 00204000 PUSH 09.00402000 ; ASCII "abex' 3rd crackme"
00407375 68 12204000 PUSH 09.00402012 ; ASCII "Click OK to check for the keyfile."
이를 통해 알 수 있는 사실은,
- POPAD 바로 아래에 PUSH 0, PUSH 00402000, PUSH 00402012 가 연달아 나온다는 것과,
- 00402000, 00402012 주소를 따라가 보면 문자열이 있다는 것이다.
- 00402000 → "abex' 3rd crackme"
- 00402012 → "Click OK to check for the keyfile."
- 즉, 메시지박스(MessageBoxA) 호출을 위한 인자들이 PUSH되고 있는 것이라는 사실을 파악할 수 있었다.
MessageBoxA의 함수는 다음 인자를 받는다.
int MessageBoxA(
HWND hWnd, // 윈도우 핸들
LPCSTR lpText, // 메시지 내용
LPCSTR lpCaption,// 메시지 제목
UINT uType // 버튼/아이콘 타입
);
PUSH는 오른쪽 인자부터 역순으로 들어가므로:
PUSH uType
PUSH lpCaption
PUSH lpText
PUSH hWnd
CALL MessageBoxA
다음과 같다.
우리가 본 코드를 여기에 대입해 보면,
0040736E 6A 00 PUSH 0 ; uType = 0 (MB_OK)
00407370 68 00204000 PUSH 09.00402000 ; lpCaption = "abex' 3rd crackme"
00407375 68 12204000 PUSH 09.00402012 ; lpText = "Click OK to check for the keyfile."
각 주소(00402000, 00402012)를 따라가보면 실제 문자열이 존재한다.
즉, 이 3개의 PUSH가 원래 OEP에서 실행됐어야 하는 메시지박스 인자 세팅 코드임을 알 수 있다.
다음으로는 언패킹 된 exe 코드도 살펴보기 위해서,
unpacked_09.exe를 디버거로 열면, 엔트리포인트(OEP) 00401000 부근의 코드가 다음처럼 보인다.

00401000 90 NOP
00401001 90 NOP
...
(총 12바이트 정도의 NOP)
...
0040100C 6A 00 PUSH 0
0040100E E8 ... CALL MessageBoxA
...
OEP 위치에 NOP 12개가 깔려 있다는 것은
:: 최소 12바이트 분량의 “원래 코드”가 디스크에서 사라졌다는 뜻이고
- NOP 12바이트 = 사라진 원래 코드 12바이트
- POPAD 아래 PUSH 3줄의 크기도 12바이트
(2 + 5 + 5 = 12)
따라서 NOP로 사라진 12바이트 = POPAD 아래의 PUSH 3줄 = StolenByte 라는 결론이 성립한다.
- 최종 StolenByte 계산
- 이제 POPAD 이후의 세 줄 PUSH 명령을 바이트 단위로 적어보면 된다.
바이트만 쭉 이어 쓰면: 6A 00 68 00 20 40 00 68 12 20 40 00
공백을 제거하면 6A0068002040006812204000
이 값이 바로 CodeEngn Basic RCE 9의 정답인 StolenByte이다.


문제 해결 성-공
'INTERTLUDE > 리버싱 스터디' 카테고리의 다른 글
| [Reversing.Kr] Music Player (0) | 2025.11.29 |
|---|---|
| CodeEngn Advance RCE 3 (1) | 2025.11.28 |
| Codeengn Basic RCE 11 (0) | 2025.11.26 |
| Codeengn Basic RCE 17 (0) | 2025.11.20 |
| Codeengn Basic RCE 13 (0) | 2025.11.18 |