Easy Keygen

Reversing the serial routine and reimplementing it as a keygen that produces a valid serial for any name.

2026.08.18 Reversing original post

취미로 푸는 워게임 풀이기 때문에, 전문성이나 깊이 있는 이해도가 떨어질 수 있음. 개인 기록용으로 올리는 거기 때문에, 전문성을 원하는 거면 다른 보안 블로그의 풀이를 찾아보기 바람.

파일은 두 가지가 있다. 하나는 실행 파일이고 하나는 워게임의 instruction 을 담은 노트이다.

Figure 1

Figure 2

Reversing.kr 에서 Easy Keygen 문제 파일을 다운로드한 뒤, x32dbg 에서 실행 파일을 연다. 좌측 Symbols(또는 Modules/Functions) 창을 확인하면 main(또는 엔트리포인트 → main 으로 이어지는 함수)과 여러 DLL 함수들이 보인다. 이 문제의 목표는 시리얼 키에 맞는 네임을 확인하는 루틴 분석이므로, main(혹은 main 으로 추정되는 함수)을 더블클릭하여 디스어셈블리 뷰로 이동한다.

Figure 3

Figure 4

Strings(A2)를 열고 프로그램 실행 시 화면에 출력될 것 같은 문자열을 찾아 들어가면, 첫 번째로 input name 을 받고 어떠한 계산을 끝낸 후 밑에 input serial 을 받는 걸 볼 수 있다. 그 밑에서, 계산에서 만들어진 값을 시리얼 넘버와 비교한다.

그 사이 코드들을 분석해보면 입력한 이름을 한 글자(1 Byte)씩 이용해서 시리얼 번호를 생성하고, 입력한 이름의 길이만큼 반복문을 반복하는 것을 알 수 있다. 즉 프로그램이 실행되는 방법은:

이름 입력
  → 이름을 가지고 연산해 시리얼 키 생성
  → 시리얼 번호 입력
  → if (시리얼 넘버 == 생성된 값) → Correct
     if (시리얼 넘버 != 생성된 값) → Wrong

좀 더 자세히 계산을 분석하기 위해 메모장에 어셈블리어를 붙였다.

Figure 5

위의 3개 줄을 보면 입력한 문자열(이름)을 한 바이트씩 XOR 연산을 하는데, 0x10, 0x20, 0x30 을 돌아가면서 사용하는 걸 알 수 있다. 계속 어셈블리어를 디버깅해보자. call easy_keygen.401150 이 함수를 집중적으로 보면,

0040108E | 51          | push ecx
0040108F | 50          | push eax
00401090 | 8D4C24 7C   | lea  ecx, dword ptr ss:[esp+7C]
00401094 | 68 54084000 | push easy_keygen.408054     | 408054:"%s%02X"
00401099 | 51          | push ecx
0040109A | E8 B1000000 | call easy_keygen.401150

easy_keygen.401150 함수는 sprintf 계열 함수로, 기존 시리얼 문자열(0x19FE6C)에 XOR 결과값을 %02X 형식의 16진수 문자열로 누적하여 붙이는 역할을 한다.

이제 분석한 정보를 가지고 파이썬 코드를 짜서 네임을 얻을 수 있다. 아래처럼 어떠한 네임이 주어졌을 때 맞는 시리얼 넘버를 내놓도록 구현했다.

Figure 6

aaaa 를 줘서 루프를 돌렸을 때 시리얼 키 값이 71415171 나오는 것을 알 수 있다. 이러한 것들을 반복해본다. 그럼 aaaaaaaa7141517141517141, 그리고 abcd71425374 나오는 걸 알 수 있다. 이러한 정보들을 가지고 Gemini 를 돌려보면 이러한 코드를 얻을 수 있다:

def serial_to_name(serial_hex: str) -> str:
    serial_hex = serial_hex.strip()
    if len(serial_hex) % 2 != 0:
        raise ValueError("Invalid serial length (must be even number of hex chars).")

    key = [0x10, 0x20, 0x30]
    out = []
    for i in range(0, len(serial_hex), 2):
        b = int(serial_hex[i:i + 2], 16)
        k = key[(i // 2) % 3]
        out.append(chr(b ^ k))
    return "".join(out)

시리얼 5B134977135E7D13 를 위 로직으로 풀면:

Name = K3yg3nm3