본문 바로가기

INTERTLUDE/리버싱 스터디

CodeEngn Advance RCE 3

CodeEngn Advance RCE 3

Q. Name이 CodeEngn일 때 Serial은 무엇인가


 

해당 문제를 풀기 위해, 03.exe  문제 파일을 다운로드하여 주었다.

 7-Zip File Manager로 압축을 풀고 DIE로 파일 정보를 확인해보려고 한다.

 

 

DIE를 통해서, 해당 파일이 UPX로 패킹이 되어 있지 않다는 사실을 알 수 있다.

만일 패킹이 되어 있다면 언패킹 과정을 거쳐야 하지만, 그렇지 않기에 Immunity Debugger로 가서 바로 파일을 열어주었다

 

F9키를 눌러 프로그램을 실행하면 익숙한 이러한 화면이 나타난다. 

  • Name 입력칸
  • Serial 입력칸

문제에서 Name이 CodeEngn이라고 알려 주었기 때문에, 

Name 입력 칸에는 CodeEngn으로, Serial number는 모르기에 임의로 1357로 입력하였다. 

 

Name, Serial를 입력하고 Check 버튼을 누르면 실패 메시지가 뜬다는 것을 확인하였다.  

--> 즉, 프로그램 내부 어딘가에서 Name과 Serial을 비교하거나,

    Serial만을 특정 값과 비교하는 로직이 존재한다는 의미로 파악해볼 수 있었다. 

이후에 Immunity Debugger에서 Search → All referenced text strings로 들어가면
성공 및 실패 문자열이 나열된 문자열 테이블을 쉽게 확인할 수 있었다.

 

문자열들과 함께, 의심스러운 숫자 두 개가 위치함을 확인할 수 있었다. 

단순히 봐도 "3265754874"가 시리얼로 직접 쓰일 가능성이 매우 높다고 생각하여서, 
이제 실제 비교 구문을 찾기 위해 이 숫자 주변의 코드 흐름을 따라가 보았다.

문자열 테이블 근처로 이동해 디버거를 통해 흐름을 따라가면 다음과 같은 구간이 나온다.

  • GetDlgItemTextA : 사용자가 입력한 Serial을 버퍼에 가져온다.
  • wsprintfA("%u") : 입력된 문자열을 unsigned int 형태의 문자열로 재정렬한다.
  • lstrcmpA(String1, String2) :                                                                                                                                    String1 = 내가 입력한 값 "1357"                                                                                                                              String2 = 프로그램 내부의 "3265754874"                                                                                                                          두 문자열이 서로 다르기 때문에 lstrcmpA는 0이 아닌 값, 즉 EAX = 1을 반환하게 된다.                                      

(lstrcmpA는 문자열이 동일할 때만 0을 반환하고,
서로 다를 경우 다르면 EAX ≠ 0 (1이나 -1) 을 반환하는 구조이기 때문이다.)                                                                               

이제 EAX 값이 1이라는 점을 기반으로 Serial 검사 이후의 흐름을 더 자세히 분석해 보았다.

Serial 비교 직후 코드: CDQ / IDIV

CDQ
IDIV EAX

 

CDQ, IDIV 명령어를 처음 보았기에, 이에 대해 알고 넘어가고자 하였다. 

 

Q. IDIV 명령는 무엇을 의미하는 것일까? 

더보기
더보기
더보기

여기서 IDIV는 ‘부호 있는(signed) 정수 나눗셈’ 명령어이다.
32비트 모드에서는 EDX:EAX(상위 32비트:하위 32비트)를 피제수로 보고,
IDIV의 피연산자를 나누는 값(제수)으로 사용한다.

 

그전에 CDQ가 먼저 실행되는데,
CDQ는 EAX 레지스터의 최상위 비트(부호 비트)를 그대로 EDX로 확장해 주는 명령어이다.
즉, EAX 값의 부호에 맞게 EDX:EAX를 64비트 정수 형태로 만들어,
IDIV가 바로 사용할 수 있도록 준비하는 역할을 하는 것이다. 

이제 이 구조를 Serial 일치할 때 / 일치하지 않을 때의 상황에 각각 대입해 보면 흐름이 두 개로 나눠지게 된다. 

 

1. Serial 일치하지 않을 때의 흐름

  • lstrcmpA → EAX = 1
  • CDQ → EDX:EAX = 0x00000000 00000001
  • IDIV EAX → 1/1 = 1
  • 정상 연산
  • 아래 실패 메시지 코드 실행 → 실패 처리(Fail)
  • 즉, Serial이 틀리면 정상 루틴을 타고 실패 메시지가 뜨게 된다.

2. Serial 일치할 때의 흐름

  • lstrcmpA → EAX = 0
  • CDQ → EDX:EAX = 0
  • IDIV EAX → 0으로 나누기 발생 → Divide-by-zero exception

Q. Divide-by-zero exception(0 나누기 예외) 란? 

IDIV EAX 명령은 EDX:EAX 값을 제수(EAX)로 나누는 부호 있는 정수 나눗셈이다.
이때 제수인 EAX가 0이면 “0으로 나누기(0 ÷ 0)” 연산이 되어 CPU가 처리할 수 없는 상황이 된다.


따라서 CPU는 즉시 Divide-by-zero Exception(0 나누기 예외)을 발생시키며,
이 예외가 발생한 순간 정상 코드 흐름은 더 이상 진행되지 않고 즉시 중단된다.

 

여기서 중요한 점은:

예외가 발생하면 정상 흐름이 끊기고,
CPU가 자동으로 ‘등록된 예외 처리기(SEH)’로 이동한다는 것이다.

Windows OS는 내부적으로 다음 순서로 움직인다:

  1. 현재 스레드에 등록된 SEH 체인을 확인
  2. 가장 최근에 등록된 핸들러 주소를 찾음
  3. 그 핸들러로 프로그램 흐름을 강제로 이동
    = 즉, “예외 발생 → OS가 SEH로 보내줌 → SEH 코드가 실행됨”

 

예외 발생 시 SEH가 실행된다는 것을 확인하기 위해 프로그램 시작 부분(OEP 근처)을 다시 살펴보았다.

프로그램 시작 부분을 확인해 보니, FS:[0] 값을 이용해 SEH 체인에 예외 핸들러를 등록하는 전형적인 SEH 설치 패턴이 존재했다.

 

즉,

  • 프로그램은 실행 초기에 SEH를 등록해두고
  • 예외가 발생하면 OS가 이 SEH로 흐름을 옮기도록 설계되어 있는 것이다. 

그리고 이 프로그램에서 등록된 SEH의 실제 주소는 00401392임을 알 수 있었다.

 

해당 주소로 이동해 보니,
그곳에는 아래와 같은 성공 메시지(MessageBoxA)가 존재했다.

  • Title : "You succeeded..."
  • Text : "Yes, you see the Good Boy Message :)"

즉, SEH 내부가 바로 성공 루틴이었던 것을 알 수 있었다. 

 

** 핵심 구조 요약

  1. Serial 비교
  2. Serial이 맞아서 EAX=0 
  3. IDIV EAX → 무조건 0 나누기 에러
  4. CPU 흐름이 OS로 넘어감
  5. OS가 SEH로 점프
  6. SEH 내부에서 Success Message 출력
더보기
더보기
더보기

🔸 Serial 틀림

→ EAX = 1
→ IDIV 1 → 정상 연산
→ 아래 코드 계속 실행
→ 실패 메시지 출력
→ Fail

 

🔸 Serial 맞음

→ EAX = 0
→ IDIV 0 → Divide-by-zero Exception
→ CPU가 정상 코드 실행 중단
→ OS가 등록된 SEH(00401392)로 점프
→ SEH 내부에서 Success 메시지 출력
→ Good Boy!

즉, 정답 Serial을 넣어야 예외가 발생하고, 예외가 발생해야 SEH로 진입하며, SEH에 성공 코드가 들어 있다.

정상 흐름을 타면 오히려 실패하고, 예외 흐름을 타야 성공한다는 독특한 구조라는 것을 살펴볼 수 있었다. 

  • 정답 Serial "3265754874"를 입력하면 IDIV에서 0 나누기 예외가 발생하고, CPU가 등록된 SEH 핸들러로 이동하여
    Good Boy 메시지를 출력하게 된다.

정답 Serial : 3265754874

문제 해결 성-공! 

 

 

++ 일반적인 프로그램의 흐름과 비교 

 

A. 일반 Name/Serial 프로그램

(1) Serial 비교
(2) 맞으면 → 성공 메시지
(3) 틀리면 → 실패 메시지

즉, 성공 코드와 실패 코드는 모두 정상 흐름 안에 있다.

 

B.  이번 CrackMe의 특수 구조

(1) Serial 비교
      ├ 틀림 → EAX=1 → IDIV 1 → 정상 → 실패 메시지
      └ 맞음 → EAX=0 → IDIV 0 → 예외 발생

(2) 예외 발생 → 정상 흐름 중단 → SEH로 이동

(3) SEH 내부 = 성공 메시지 출력

즉, Serial이 맞아야 예외가 발생하고, 예외가 발생해야만 성공 코드에 도달할 수 있는 것이다. 

'INTERTLUDE > 리버싱 스터디' 카테고리의 다른 글

[Reversing.Kr] Music Player  (0) 2025.11.29
Codeengn Basic RCE 9  (0) 2025.11.26
Codeengn Basic RCE 11  (0) 2025.11.26
Codeengn Basic RCE 17  (0) 2025.11.20
Codeengn Basic RCE 13  (0) 2025.11.18