Q20. 이 프로그램은 Key파일을 필요로 하는 프로그램이다. 'Cracked by: CodeEngn!' 문구가 출력되도록 하려면 crackme3.key 파일 안의 데이터는 무엇이 되어야 하는가 Ex) 41424344454647 (정답이 여러 개 있는 문제로 인증 시 맞지 않다고 나올 경우 Contact로 연락 주시면 확인해 드리겠습니다)

바로 20.exe를 실행해 보면 아무 화면도 출력되지 않는다.

텍스트를 확인해 보면 Cracked by: 이후에 빈칸은 보이지만 아무 내용도 출력되지 않는다는 것을 확인했다.

401016 ~ 401002D 주소를 보면 CreateFileA함수를 볼 수 있다.
이때 함수의 FileName의 값으로 CRACKME3.KEY를 인자값으로 받는 것을 확인했다.
문제에서 key 파일이 필요하다고 했었다.
메모장을 통해 새로운 key파일을 생성해야 한다.
CRACKME3.KEY 파일 생성 방법
: 메모장을 통해 빈 .txt파일을 만든 후 이름과 확장자를 바꿔주면 된다.
이때 이름을 CRACKME3.KEY로 저장하면 된다.
+) 추가 사항
CreateFileA이라는 함수를 실행하는데 받는 인자 값들 중 “Mode” 인자 값을 주목해야 한다.
Mode값에 따라 CreateFileA의 동작이 조금 달라지는데,
이 프로그램처럼 Mode 3(OPEN_EXISTING)이 입력될 경우에는 CreateFile함수의 조건을 만족하는 파일이 있을 경우에만 파일이 실행되도록 동작하기 때문이다.


이 ReadFile함수를 통해 18byte를 읽는 것을 알 수 있었다.
추가적으로 만약 파일이 18byte를 갖지 못한다면 반응하지 않는다고 한다.
따라서 생성해 둔 CRACKME3.KEY 파일에 123456789123456789를 입력 후 저장하여 다시 실행한다.

위 과정을 수행하면 다음과 같이 CRACKME3.KEY 파일에 존재하는 18byte의 데이터를 디버거에서 확인이 가능했다.

401074주소에서 [그림 6]에서 확인할 수 있는 18Byte에 대해 401311주소를 호출해서 다음 명령어들이 수행되므로
해당 401311주소 알고리즘을 분석해야 했다.
401311 분석

401315 ESP+4에 ESI에는 입력받은 문자열의 전체가 들어간다.
401319 BL의 초기값으로 0x41이 들어가는 것을 확인한다.
40131D AL과 BL을 XOR연산 진행한다. 이 결과는 EAX에 저장된다.
40131F 입력받은 전체 문자열에서 AL에는 한 글자씩 들어간다.
401324 EAX와 더한 값을 4020F9에 저장한다.
40132A AL=0이면 반복 루틴 탈출한다.
401330 BL=4F면 반복 루틴 탈출한다.

401324주소를 거치게 되면 CRACKME3.KEY에 저장되었던 첫 글자 31(1)이70(p)으로 변경됨을 확인해 볼 수 있었다.
따라서 401311 분석한 내용을 정리해 보면 다음과 같다.
- BL은 **41(’A’)~ 4F(’O’)**까지 변하는데 마지막의 4F의 값이면 루프를 빠져나오게 되므로 총 14번(0x41~ 0x4E까지)의 변화가 이루어진다.
- 덤프창에서 전체 KEY파일의 내용 중 앞 14바이트까지 변경됨을 볼 수 있었다.
- 40133B 주소의 RETN을 만나면 4020F9 주소에 루틴을 거쳐 저장한 값을 반환한다.

401311에서 빠져나온 후 더 분석을 진행해 보면
401079 주소에서 앞서 루틴을 거쳐 연산결과의 합이 저장된 값[4020F9]과 0x12345678을 XOR연산 진행한다는 것을 알 수 있었다.
이 XOR연산 값을 다시 [4020F9]에 저장한다.
이 다음 40108B에서 40133C 를 호출하는 것을 볼 수 있었다. 따라서 40133C도 분석을 해야 한다.
40133C 분석

40133C에서 402008을 ESI에 저장한다. (Stack창의 ESP+4값 확인)
401340에서는 ESI와 0E를 더한다.
여기서 0E는 0xE(’15’)를 의미하며, 최종적으로 반환되는 값은 EAX에 저장되는 [402008 + 0E]이다.
ReadFile에서 읽어온 바이트 중 15번째 바이트부터 시작한다는 의미이다.

다시 40133C를 빠져나와서 더 분석해 보면,
401093에서 EAX값과 4020F9의 값을 비교한다.
동일하지 않다면 40109F의 분기점을 만나 401037로 넘어가게 된다.
401311에서 연산을 진행할 때 앞의 14개의 바이트만 연산하고 나머지 4개의 바이트는 연산하지 않았는데,
그 나머지 4개의 HEX값이 40133C를 지나면 EAX로 들어오고 4020F9와 비교하고 분기한다는 의미라고 한다.

4010A1에서 40210E를 인자로, 401346함수를 실행하고,
13번째 글자 지점에 특정 문구(”-Cracked!!”) 를 저장하는 동작을 수행한다.

이후에 집중해서 볼 구간은 401188의 CMP AL, 1의 결과에 따라 성공 분기점이 갈리게 된다.
성공 - 40118C , 실패 - 4011A3
따라서 다시 전체적으로 정리한다면
정리
- 총 18바이트 크기의 “CRACKME3.KEY”라는 파일명을 가진 파일이 필요로 한다.
- “CRACKME3.KEY”파일의 0x0 ~ 0xD의 바이트가 401311 함수를 거쳤을 때 “CodeEngn”라는 문자열을 출력할 수 있는 값이어야 한다.
- 401311 함수 이후에 저장된 [4020F9] 값과 “0x12345678”와 XOR 한 값이 “CRACKME3.KEY”의 0xE ~ 0x12바이트와 같아야 한다.
알아두면 좋을 점
1. A XOR B = C 일 때, C XOR B = A 가 성립된다.
2. 해당 알고리즘을 분석해보면 AL XOR BL = 0 일때 반복문이 종료된다.
이 정리된 식에 따라 코드를 작성한다면 다음과 같다.
xor_data = "A" # 0x41
input_data = [] # 저장해야 할 data 값
last_char =
# ASCII 변환을 위한 함수 정의
def hex_to_ascii(hex_string):
return bytes.fromhex(hex_string).decode('ascii')
# 1. 고정된 출력 문자열 사용
text = "CodeEngn"
print(f"Using output string: {text}")
# 2. 입력한 글자들 XOR한 값 저장
for i in range(len(text)):
trans_data = ord(text[i]) ^ (ord(xor_data) + i)
input_data.append(hex(trans_data))
last_char += ord(text[i]) # 모든 문자 아스키 코드 합
# 3. 14문자 중 부족한 나머지 채우기
i = len(text)
while i < 14:
input_data.append(hex(ord(xor_data) + i))
i += 1
# 4. 0x12345678과 XOR 연산
last_char ^= int('0x12345678', 16)
# 5. 마지막 자리(15~18) 연산: last_char를 1바이트씩 분할
for _ in range(4):
input_data.append(hex(last_char % 0x100))
last_char = last_char // 0x100
# 6. 결과 출력
print("Data must be...")
print(input_data)
# 7. 0x 접두사 제거하고 대문자로 변환하여 출력
result = []
for hex_val in input_data:
# 0x 제거하고 대문자로 변환, 2자리로 맞춤
clean_hex = hex_val[2:].upper().zfill(2)
result.append(clean_hex)
print("Final result:")
print(' '.join(result))
# 8. ascii 코드 변환
hex_string = (' '.join(result))
ascii_result = hex_to_ascii(hex_string)
print(f"Hex to ASCII is : ")
print(ascii_result)
결과
Using output string: CodeEngn
Data must be...
['0x2', '0x2d', '0x27', '0x21', '0x0', '0x28', '0x20', '0x26', '0x49', '0x4a', '0x4b', '0x4c', '0x4d', '0x4e', '0x7b', '0x55', '0x34', '0x12']
Final result:
02 2D 27 21 00 28 20 26 49 4A 4B 4C 4D 4E 7B 55 34 12
Hex to ASCII is :
-'!( &IJKLMN{U4
** Process exited - Return Code: 0 **
Press Enter to exit terminal

복호화 코드 결과로 나온 -'!( &IJKLMN{U4 를 HxD프로그램을 통해 초반에 생성해 둔 CRACKME3.KEY 파일을 패치하면 된다.

성공적으로 패치를 진행한 후 실행해 보면 다음과 같은 창이 출력된다.
정답 : 022D272100282026494A4B4C4D4E7B553412
이번 문제는 처음 접해보는 형식의 문제여서 꽤나 헤맸었다.
최대한 쉽게 이해할 수 있도록 값의 출처들을 수집하고 의미를 파악해 가며 작성했다.
출처 1 : [CodeEngn] Basic RCE L20 문제 풀이
[CodeEngn] Basic RCE L20 문제 풀이
문제 이 프로그램은 Key파일을 필요로 하는 프로그램이다. 'Cracked by: CodeEngn!' 문구가 출력 되도록 하려면 crackme3.key 파일안의 데이터는 무엇이 되어야 하는가 Ex) 41424344454647 (정답이 여러개 있는
sec-kero.tistory.com
codeengn-challenges-writeups/RCE_Basic/L20/RCE Basic L20 Report [areiong].pdf at master · codeengn/codeengn-challenges-writeups
Top Writeups for CodeEngn Challenges. Contribute to codeengn/codeengn-challenges-writeups development by creating an account on GitHub.
github.com
'Reversing > CodeEngn' 카테고리의 다른 글
| [CodeEngn] RCE mobile L01 풀이 (0) | 2025.09.06 |
|---|---|
| [CodeEngn] RCE advance L01 풀이 (0) | 2025.09.06 |
| [CodeEngn] RCE basic L19 풀이 (0) | 2025.09.05 |
| [CodeEngn] RCE basic L18 풀이 (0) | 2025.09.05 |
| [CodeEngn] RCE basic L17 풀이 (0) | 2025.09.04 |