Reddit

Reverse-engineered 8BitDo firmware encryption

8BitDo 펌웨어 암호화 역공학

GD32 기반 8BitDo 제품군의 펌웨어 암호화 방식을 분석하고 복호화·재암호화 도구를 공개했습니다. 여러 제품에서 같은 키스트림을 재사용한 탓에 암호문끼리 비교해 평문을 복구할 수 있었고, 분석자는 해당 암호화가 적용된 펌웨어 데이터 전체를 해독했습니다.

AI 요약

8cryptdo는 GD32 기반 8BitDo 제품 여러 종에 쓰인 펌웨어 암호화 방식을 문서화하고, 펌웨어를 복호화하거나 다시 암호화하는 도구를 제공합니다. 사용자 정의 펌웨어 제작에 활용할 가능성은 있지만, 저장소에는 공식 펌웨어 파일이 포함되지 않습니다. 오래된 펌웨어 파일은 fwupd/8bitdo-firmware 저장소에서 찾을 수 있습니다.

헤더와 체이닝 레이어

펌웨어 .dat 파일 앞부분에는 28바이트 평문 헤더가 있습니다. 처음 12바이트는 little-endian 형식의 버전, 적재 주소로 보이는 값, 페이로드 길이를 담습니다. 예를 들어 SN30 v2.07 파일에서는 버전 207, 주소 0x08003400, 페이로드 길이 99,328바이트가 확인됩니다. 최신 펌웨어 일부에는 알 수 없는 32비트 값이 추가되고 나머지 헤더 영역은 0으로 채워집니다.

일부 파일의 암호문에는 각 32비트 워드를 다음 워드와 섞는 체이닝 레이어도 있습니다. 현재 워드와 직전 워드를 오른쪽으로 3비트 회전한 값을 XOR하며, 128워드마다 체이닝을 초기화합니다. 첫 워드는 이전 워드의 영향을 받지 않습니다. 이 레이어에는 키가 없으므로 보안을 더하지 않습니다. 제거하면 키스트림만 남은 중간 데이터가 드러납니다.

키스트림 재사용으로 평문 복구

서로 다른 펌웨어에서 체이닝 레이어를 제거한 뒤 결과를 XOR하면 긴 0 구간과 읽을 수 있는 구조가 나타납니다. 같은 키스트림을 재사용했다면 두 암호문을 XOR한 결과에서 키스트림이 상쇄되고, 두 평문을 XOR한 값만 남습니다. 여러 메시지에 같은 키스트림을 쓰는 many-time pad의 취약점입니다. 분석자는 8BitDo가 제품 하나에 한정하지 않고 여러 제품에서 키스트림을 공유한 점을 이용했습니다.

평문의 일부를 추측하면 해당 위치의 키스트림을 알아낼 수 있습니다. 특히 펌웨어 버전마다 위치가 달라진 ARM 명령어를 맞춰 비교했습니다. 한 버전에서 확인한 코드와 키스트림 위치를 다른 버전의 같은 코드에 대응시켜 새 키스트림 값을 얻었습니다. 이렇게 알려진 키스트림 워드 수를 약 1,500개에서 수천 개로 늘렸고, 펌웨어의 약 18%를 읽을 수 있었습니다.

블록 키 공식과 복구 범위

추가 분석에서 키스트림은 128워드 블록마다 간단한 공식으로 생성되는 점을 발견했습니다. 블록 번호와 위치에 따라 증가하는 카운터를 만들고, 블록 키를 위치마다 18비트씩 회전한 마스크와 XOR합니다. 따라서 블록 키를 하나만 알아내면 해당 블록의 128개 워드 전체를 복구할 수 있습니다.

블록 키는 256개 항목으로 이뤄진 시드 테이블에서 16비트 값 두 개를 가져와 결합합니다. 테이블 값은 부트로더에 저장된 상수일 가능성이 있지만, 문서에서는 이를 확정하지 않습니다. 블록 번호에 0xF9를 XOR하면 두 블록이 같은 테이블 값 두 개를 순서를 바꿔 사용합니다. 이 미러 관계를 이용하면 블록 키의 두 16비트 절반을 함께 추측하고, 결과가 문자열이나 ARM 명령어로 읽히는지 확인할 수 있습니다. 분석자는 이를 통해 이 암호화가 적용된 펌웨어 데이터 전체를 복구했습니다.

이 암호화 방식은 2020년 전후 제품 대부분과 SN30 Pro 등 GD32 기반 최신 제품에도 쓰이는 것으로 보입니다. M30과 Zero 2처럼 여러 섹션으로 구성된 일부 기기는 다른 암호화 방식을 쓰는 펌웨어 섹션도 있습니다. 다른 SoC를 쓰는 신형 기기는 더 강한 암호화를 사용하는 것으로 보이며, 제품 간 방식 공유 여부는 추가 조사가 필요합니다.

원문: GitHub / 번역·요약: Trawling