사례 연구 · DX-Ball 1.07

실행 파일로부터
DX-Ball 재구성하기

목표는 1996년에 나온 Windows 게임을 유지보수 가능한 C 코드로 재구성하는 것입니다. REA는 에이전트가 원본 함수를 하나씩 분석하도록 돕습니다.

Windows i386 · C · REA

REA로 DXBALL.EXE의 함수 0x00406400을 분석합니다. 벽돌 충돌 함수는 20 + 30 × tile_x를 전달합니다. 명령어는 x를 읽고 1.5625를 곱한 뒤 500.0을 빼고 pan_scale을 적용해 정수를 반환합니다. C 코드는 원본 x86과 3,205개 사례를 비교하고, VC4.0 컴파일러 재현으로 63바이트 일치를 확인합니다.
함수 하나를 실행 파일에서 검증된 C 코드로 복구합니다. REA는 명령어, 호출 함수, 데이터 읽기 결과를 제공하고 재구성 프로젝트가 구현과 테스트를 맡습니다. 그림 열기
55
유지보수 중인 C 함수
45,380
원본 코드와의 비교
33
컴파일된 바이트가 일치하는 함수

프로젝트 체크포인트: 2026년 10월 7일. 진행 기록 · 테스트 범위

지금까지의 프로젝트

복구된 코드는 보드와 리소스 처리, 벽돌 충돌, 애니메이션, 파티클, 보너스 생성을 다룹니다. 현재 빌드는 분석 유틸리티와 라이브러리를 제공합니다. 플레이 가능한 게임을 위해 공·패들의 물리, 파워업 처리, Windows 통합 작업이 계속되고 있습니다.

왼쪽/오른쪽 사운드 위치 계산 찾기

벽돌에 맞으면 가로 위치가 사운드 코드로 전달됩니다. 이 화면 좌표가 어떻게 좌우 패닝 값으로 바뀌는지 복구하는 조사입니다.

코딩 에이전트

REA로 DX-Ball의 사운드 패닝 계산 방식을 찾아줘. 계산을 설명하고 코드도 보여줘.

로컬 실행 파일을 준비한 상태에서 쓰는 코딩 에이전트용 프롬프트입니다. 아래 조사에서 답변의 근거가 된 REA 질의와 증거를 볼 수 있습니다.

에이전트는 REA로 함수를 살펴보고, 벽돌 충돌 처리 함수가 이 함수를 어떻게 호출하는지 추적한 뒤 실행 파일의 상수를 읽었습니다. 이 결과로 입력과 계산 방법을 확인하고 아래의 C 함수를 작성했습니다.

0x406400의 첫 함수 분석 결과에는 FUN_00406400(void)을 선언하고 __ftol()를 호출하는 의사 코드가 포함되어 있었습니다. 명령어 보기에는 스택 입력과 부동 소수점 연산이 나타났습니다. 이 불일치는 에이전트가 조사를 계속할 구체적인 근거가 되었습니다.

첫 디컴파일 결과
REA 결과 · 의사 코드 발췌
longlong FUN_00406400(void)
{
  longlong lVar1;

  lVar1 = __ftol();
  return lVar1;
}

출력에는 변환 함수 호출이 나타납니다. 아래의 명령어를 읽으면 그 호출에 전달되는 입력과 계산 과정을 알 수 있습니다.

REA로 답을 찾은 세 가지 질문

  1. 누락된 입력 복구

    에이전트 → REA · analyze_function
    {"procedure": "0x406400"}
    REA → 에이전트 · 명령어 발췌문
    0x406409: MOV EAX, dword ptr [EBP + 0x8]
    0x40640c: MOV dword ptr [EBP + -0xc], EAX
    0x40640f: FILD dword ptr [EBP + -0xc]

    함수 결과에는 의사 코드, 호출 함수와 함께 명령어가 포함됩니다. 여기서는 [EBP+8]이 디컴파일 결과에 빠진 정수 입력을 드러냅니다.

  2. 호출 함수를 따라 입력 의미 확인

    에이전트 → REA · analyze_function
    {"procedure": "0x411f40"}
    REA → 에이전트 · 호출 함수 발췌
    0x411f5b: ADD EAX, 0x14
    0x411f5e: PUSH EAX
    0x411f5f: CALL 0x00406400

    REA는 첫 결과에서 이 호출 함수를 식별했습니다. 앞선 명령어는 tile_x에 30을 곱하고 20을 더해 벽돌의 화면 좌표 20 + 30 × tile_x을 구합니다.

  3. 메모리 주소 뒤에 있는 값 읽기

    에이전트→ REA · 두 read_bytes 요청
    {"address": "0x420068", "length": 16}
    {"address": "0x4210a0", "length": 8}
    REA 바이트 분석 결과 · 배정밀도 부동소수점으로 해석
    000000000000f93f → 1.5625
    0000000000407f40 → 500.0
    000000000000f03f → 1.0

    반환된 바이트로 연산 상수 두 개와 초기 패닝 배율을 확인합니다. 이제 에이전트는 C 수식에 필요한 값을 모두 갖췄습니다.

REA 분석 결과에서 C 코드로

아래 다섯 단계를 선택해 보세요. 각 단계에 해당하는 원본 명령어와 이를 바탕으로 작성한 C 코드가 강조됩니다.

원본 x86 · 발췌
0x406409: MOV EAX, dword ptr [EBP + 0x8]
0x40640c: MOV dword ptr [EBP + -0xc], EAX
0x40640f: FILD dword ptr [EBP + -0xc]
0x406415: FMUL double ptr [0x00420068]
0x40641e: FSUB double ptr [0x00420070]
0x406427: FMUL double ptr [0x004210a0]
0x406430: CALL 0x0041678c
0x40643e: RET
재구성한 C 코드
DxBallInt dxball_screen_pan(DxBallInt x)
{
    double pan;
    pan = (double)x;
    pan = pan * 1.5625;
    pan = pan - 500.0;
    pan = pan * dxball_pan_scale;
    return (DxBallInt)pan;
}

01 · 입력을 읽습니다. REA의 함수 결과에 있는 명령어 뷰에서는 [EBP+8]를 읽고 FILD로 정수를 로드합니다. REA로 호출 함수를 추적하면 C 매개변수 x가 화면 좌표임을 확인할 수 있습니다.

어셈블리 발췌문은 입력, 연산, 반환 명령어를 골라 보여줍니다. 아래 전체 목록에는 중간 저장과 함수의 준비·정리 명령도 포함됩니다.

전체 어셈블리와 근거 자료
REA 결과 · 0x406400–0x40643e
0x406400: PUSH EBP
0x406401: MOV EBP, ESP
0x406403: SUB ESP, 0xc
0x406406: PUSH EBX
0x406407: PUSH ESI
0x406408: PUSH EDI
0x406409: MOV EAX, dword ptr [EBP + 0x8]
0x40640c: MOV dword ptr [EBP + -0xc], EAX
0x40640f: FILD dword ptr [EBP + -0xc]
0x406412: FST double ptr [EBP + -0x8]
0x406415: FMUL double ptr [0x00420068]
0x40641b: FST double ptr [EBP + -0x8]
0x40641e: FSUB double ptr [0x00420070]
0x406424: FST double ptr [EBP + -0x8]
0x406427: FMUL double ptr [0x004210a0]
0x40642d: FST double ptr [EBP + -0x8]
0x406430: CALL 0x0041678c
0x406435: JMP 0x0040643a
0x40643a: POP EDI
0x40643b: POP ESI
0x40643c: POP EBX
0x40643d: LEAVE
0x40643e: RET

호출하는 함수가 제공하는 것

0x411f40의 벽돌 충돌 함수는 타일 좌표에 30을 곱하고 20을 더한 뒤, 호출 전에 스택에 넣습니다:

0x411f50: MOV EAX, dword ptr [EBP + 0x8]
0x411f53: ADD EAX, EAX
0x411f55: LEA EAX, [EAX + EAX*0x2]
0x411f58: LEA EAX, [EAX + EAX*0x4]
0x411f5b: ADD EAX, 0x14
0x411f5e: PUSH EAX
0x411f5f: CALL 0x00406400
0x411f64: ADD ESP, 0x4

데이터 읽기로 확인한 값

REA가 읽은 바이트를 리틀 엔디언 배정밀도 부동소수점 값으로 디코딩
주소 값 용도
0x420068 1.5625 첫 번째 곱셈
0x420070 500.0 빼기
0x4210a0 1.0의 초깃값 저장된 패닝 배율

해시로 고정한 DX-Ball 1.07 대상을 REA 4.1.0으로 분석한 기록입니다. 연결된 조사 메모에 Evidence ID가 보존돼 있습니다.

유지보수 중인 소스 · 조사 메모와 증거 참조

복구한 함수 검증

복구한 함수는 두 검사를 통과합니다. 반환값이 원본과 일치하고, 컴파일된 바이트도 실행 파일과 일치합니다.

동작 검증 사례 3,205개

테스트는 원래 x86 함수를 실행하고 그 반환값을 유지보수 중인 C 코드의 반환값과 비교합니다. 팬 스케일 0, 0.5, 1, 20, −1의 다섯 가지 값에 대해 0부터 640까지의 모든 정수 위치를 검사합니다.

바이트 63개 일치

C 함수는 버전이 고정된 VC4.0 도구 모음으로 컴파일됩니다. 검토된 재배치를 적용하고 참조된 상수를 확인하면 컴파일러 재현 결과가 함수 전체와 일치합니다.

배율이 1일 때 복구한 계산은 왼쪽 끝에서 −500, 중앙에서 0, 오른쪽 끝에서 500을 반환합니다. 다음 오디오 작업은 이 값을 게임의 사운드 백엔드와 연결하는 것입니다.

컴파일러 재현 세부 사항 읽기

조사 이어가기

파티클 갱신, 폭발 대기열, 보너스 생성에도 같은 방법을 사용합니다. 관련 함수를 살펴 상태와 의존 관계를 복구한 뒤 구현을 원본과 비교합니다.

사운드 패닝 함수를 직접 분석하려면 DX-Ball 프로젝트의 설정 지침을 따르고 일치하는 원본 파일을 준비하세요. 그런 다음 실행하세요:

체크아웃한 DX-Ball 폴더에서
scripts/rea function original/DXBALL.EXE 0x00406400 \
  --snapshot .analysis/rea/dxball.snapshot.json --json

상단