죽은 하드웨어의 봉인을 뜯다: SGI O2 PROM 리버스 엔지니어링 분석


엔지니어링의 세계에서 ‘레거시(Legacy)‘는 종종 기피 대상 1호입니다. 하지만 어떤 이들에게 레거시는 단순한 고물이 아니라, 해결되지 않은 퍼즐이자 정복해야 할 에베레스트입니다.

최근 Hacker News에서 꽤 흥미로운 글을 하나 발견했습니다. 90년대 워크스테이션의 아이콘이었던 Silicon Graphics(SGI) O2 의 펌웨어를 리버스 엔지니어링하여, 20년 넘게 불가능했던 CPU 업그레이드의 길을 연 이야기입니다. 오늘은 이 ‘디지털 고고학’의 과정을 기술적인 관점에서 뜯어보고자 합니다.

왜 하필 2026년에 SGI O2인가?

SGI O2는 독특한 디자인과 당시로서는 혁신적인 그래픽 성능으로 유명했습니다. 하지만 이 기계에는 오랜 숙원이 하나 있었습니다. 바로 900 MHz RM7900 CPU 업그레이드입니다. 하드웨어적으로는 장착이 가능했지만, 부팅을 담당하는 PROM(Programmable Read-Only Memory) 펌웨어가 이 CPU를 인식하지 못해 부팅이 불가능했기 때문입니다.

SGI는 이미 역사 속으로 사라졌고, 소스 코드는 유실되었습니다. 남은 건 바이너리 덩어리뿐. Matt라는 엔지니어는 이 난제를 해결하기 위해 ip32prom-decompiler라는 도구를 직접 만들기로 결심합니다. 단순히 어셈블리를 읽는 것을 넘어, 비트 단위까지 완벽하게 동일한 바이너리로 재조립 가능한 소스 코드 를 복원하는 것이 목표였습니다. (이 대목에서 솔직히 좀 전율이 일었습니다. 이건 그냥 해킹이 아니라 ‘복원’이니까요.)

1. 첫 번째 장벽: 이게 코드인가 데이터인가?

가장 먼저 부딪힌 문제는 바이너리의 구조 파악이었습니다. objdump로 덤프를 떠봤지만, 의미를 알 수 없는 데이터들이 난무했습니다.

0: 10000011 b 0x48
4: 00000000 nop
8: 53484452 beql k0,t0,0x11154  <-- 수상한 명령어

0x53484452는 명령어가 아니었습니다. ASCII로 변환해보니 “SHDR”. 즉, Section Header의 매직 넘버였죠. 저자는 이 패턴을 분석하여 바이너리가 여러 개의 섹션(sloader, env, post1, firmware 등)으로 나뉘어 있다는 것을 밝혀냅니다.

재미있는 점은 각 섹션 끝에 붙어있는 ‘가짜 명령어’들이었습니다. 알고 보니 이것들은 명령어처럼 보이는 Checksum 값이었습니다. 모든 워드(Word)를 더했을 때 0이 되도록 만드는 2의 보수 체크섬 방식이었죠. 90년대 펌웨어 특유의 투박하지만 효율적인 방식입니다.

2. 시각화: 데이터의 모양을 보다

제가 이 프로젝트에서 가장 감탄한 부분은 ‘시각화’를 통한 접근법입니다. 저자는 바이너리 구조가 한눈에 들어오지 않자, 이를 픽셀 데이터로 변환해 이미지로 만들어버립니다.

  • 빨간색: 코드
  • 파란색: 헤더 및 체크섬
  • 검은색: NOP (0x00000000)

이 이미지를 통해 코드 덩어리 사이에 숨겨진 문자열(녹색)이나, 접근 불가능한 데드 코드(Dead Code)들을 직관적으로 파악해냈습니다. 복잡한 시스템을 분석할 때 로그만 들여다볼 것이 아니라, 이렇게 데이터를 시각화해보는 접근은 시니어 엔지니어라면 꼭 벤치마킹할 만한 태도입니다.

3. VMA(Virtual Memory Address)의 함정

리버스 엔지니어링의 꽃은 역시 메모리 주소 매핑입니다. 초기에 디컴파일러가 코드를 제대로 찾지 못한 이유는 절대 점프(Jump) 명령어 때문이었습니다.

MIPS 아키텍처에서 jal(Jump and Link) 명령어는 타겟 주소의 상위 4비트를 현재 PC(Program Counter)에서 가져옵니다. 즉, 코드가 메모리의 어디에 로드되느냐에 따라 점프 타겟이 달라집니다. 저자는 See MIPS Run이라는 책을 통해 MIPS CPU가 리셋 시 0xBFC0.0000에서 시작한다는 사실을 알아내고, 이를 기준으로 오프셋을 조정하여 코드의 흐름을 찾아냈습니다.

더 골치 아픈 건 firmware 섹션이었습니다. 이 섹션은 0xBFC...가 아니라 0x81000000 (kseg0 영역)에 로드되어 실행된다는 것을 jal 타겟 주소 패턴 분석을 통해 역추적해냈습니다. 주소 하나만 틀려도 엉뚱한 곳을 가리키는 포인터 지옥에서, 이런 단서들을 조합해 퍼즐을 맞추는 과정은 실로 집요함 그 자체입니다.

4. ELF 포맷의 재발견

분석 후반부에 version 섹션의 헤더에서 낯익은 패턴이 발견됩니다. 0x7f454c46. 네, 바로 ELF 매직 넘버 (\x7fELF)입니다.

알고 보니 SGI 엔지니어들은 표준 ELF 바이너리를 컴파일한 뒤, 자신들만의 커스텀 헤더(SHDR)를 덧씌워 PROM에 구워 넣었던 것입니다. 심지어 ELF 헤더의 패딩 바이트 공간에 자신들의 커스텀 데이터를 끼워 넣는 꼼수(?)까지 부렸더군요. 30년 전 선배 엔지니어들의 “돌아가기만 하면 되지” 정신을 엿본 것 같아 묘한 동질감이 느껴졌습니다.

Hacker News의 반응과 나의 생각

이 글이 Hacker News에 올라오자, 반응은 뜨거웠습니다. 특히 “Ghidra를 써보지 그랬냐” 는 코멘트가 많았습니다. 저자도 인정했듯이, 요즘 같으면 NSA가 공개한 Ghidra 같은 강력한 툴을 쓰면 훨씬 수월했을 겁니다. 하지만 저자는 Capstone 라이브러리를 이용해 직접 파서를 짰습니다.

저는 오히려 이 ‘바퀴를 다시 발명하는’ 과정에 박수를 보내고 싶습니다. 툴에 의존하지 않고 맨땅에 헤딩을 했기에 MIPS 아키텍처의 부팅 시퀀스, 캐시 초기화, 섹션 구조를 뼛속까지 이해할 수 있었을 것입니다. Principal 레벨로 성장하려면 가끔은 이런 밑바닥 탐험이 필수적입니다.

한 유저는 이를 “PC 월드에서의 BIOS 모딩”에 비유했고, 또 다른 유저는 SGI 하드웨어 에뮬레이션(MAME)의 어려움을 토로하기도 했습니다. 결국 이런 하드코어한 분석 데이터가 쌓여야 에뮬레이터의 정확도도 올라가고, 레거시 하드웨어의 수명도 연장됩니다.

결론: 엔지니어링의 본질

이 프로젝트의 결과물인 ip32prom-decompiler는 이제 누구나 SGI O2의 펌웨어를 수정하여 재조립할 수 있게 해줍니다. 900 MHz CPU 업그레이드가 가능해진 것은 덤이고, 하드웨어의 초기화 과정을 완벽하게 문서화했다는 점이 진짜 가치입니다.

우리는 매일 추상화된 고수준 언어와 클라우드 인프라 위에서 일합니다. 하지만 그 아래에는 여전히 비트와 바이트, 레지스터와 메모리 주소가 얽혀 돌아가고 있습니다. 가끔은 이렇게 깊은 심연을 들여다보는 것이 엔지니어로서의 ‘야성’을 깨우는 데 큰 도움이 됩니다.

여러분의 서랍 속에 잠자고 있는 옛날 기계는 무엇인가요? 이번 주말엔 먼지를 털고 헥스 에디터(Hex Editor)를 한번 켜보는 건 어떨까요?