QuickBASIC 3.0 'Hello World'를 어셈블리어로 해부하다!
QuickBASIC 3.0으로 컴파일된 'Hello World' 바이너리 파일의 리버스 엔지니어링(Reverse Engineering) 과정을 상세히 분석함
DOSBox Debug를 활용하여 실행 흐름(Execution Flow)을 추적하고, BRUN30.EXE 런타임의 동작 방식을 파악함
Ghidra 및 radare2와 같은 디스어셈블러(Disassembler)의 한계와 Spice86, ChatGPT와 같은 도구의 활용성을 제시함
1980년대 DOS 환경의 메모리 관리(Memory Management) 및 컴파일러 최적화(Compiler Optimization) 부재를 지적함
QuickBASIC 컴파일러의 비효율성 분석
분석에 따르면 QuickBASIC 3.0 컴파일러는 최적화(Optimization)에 취약하여, 간단한 'Hello World' 프로그램 실행에 8000개 이상의 어셈블리 명령어가 필요하다. 특히, 런타임 모듈(BRUN30.EXE)을 함께 사용해야 하는 구조적 한계로 인해, 실행 파일 자체는 최소화될 것으로 예상했으나, 실제로는 불필요한 로직과 데이터 처리가 많았다. 이는 BASIC 언어의 특성상 인터프리터 방식(Interpreter)에 가깝게 컴파일되기 때문이며, 1980년대 컴퓨팅 환경의 제약이 반영된 결과이다.
DOSBox Debug를 활용한 실행 흐름 추적
저자는 DOSBox Debug를 사용하여 HELLO.EXE의 실행 흐름을 상세히 추적하고, 각 어셈블리 명령어의 동작을 분석했다. 특히, 인터럽트(Interrupt) 호출 빈도가 높았으며, 화면에 문자를 출력하는 과정에서 INT 10,9번 인터럽트가 사용됨을 확인했다. 또한, DOSBox Debug의 로깅 기능(Logging Function)을 통해 레지스터 값 변화를 면밀히 관찰하고, 프로그램의 메모리 할당 및 해제 과정을 파악했다.
BRUN30.EXE 런타임의 동작 원리
HELLO.EXE는 BRUN30.EXE 런타임의 기능을 활용하기 위해, 런타임 코드를 메모리에 로드하고, 런타임 루틴을 직접 호출하는 방식으로 동작한다. HELLO.EXE는 BRUN30.EXE를 열어 메모리에 복사하고, 런타임의 재배치 테이블(Relocation Table)을 처리하여 포인터를 수정하는 과정을 거친다. 이 과정은 전체 실행 시간의 25%를 차지하며, 1980년대 DOS 환경에서 동적 링크(Dynamic Linking)가 지원되지 않는 한계를 보여준다.
ChatGPT를 활용한 분석 보조
저자는 ChatGPT를 사용하여 리버스 엔지니어링 과정에서 발생한 의문점을 해결하고, 코드의 의미를 파악하는 데 도움을 받았다. ChatGPT는 x86 어셈블리에 대한 깊이 있는 지식을 바탕으로, 정적 디스어셈블리 및 DOSBox의 추적 결과에 대한 적절한 설명을 제공했다. 하지만, ChatGPT의 AI 환각(Hallucination) 위험을 인지하고, 잘못된 정보를 수정하거나 무시하는 등, 비판적인 시각을 유지하며 분석을 진행했다.
Ghidra 및 radare2 디스어셈블러의 한계
Ghidra와 radare2는 HELLO.EXE를 분석하는 데 어려움을 겪었다. 특히, 파일 헤더의 불일치로 인해 디스어셈블러가 제대로 동작하지 않았으며, radare2는 불안정한 동작을 보이기도 했다. 저자는 Spice86 커뮤니티의 도움을 받아 문제를 해결하고, radare2에 패치를 적용하여 분석을 진행했다. 이러한 경험은 리버스 엔지니어링 도구의 한계와, 특정 환경에서의 호환성 문제(Compatibility Issues)를 보여준다.