TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 24 READS

내 전동킥보드를 리버스 엔지니어링하고 펌웨어를 Rust로 다시 쓰기까지

내 전동킥보드를 리버스 엔지니어링하고 펌웨어를 Rust로 다시 쓰기까지
SOURCE IMAGE · HACKER NEWS

고가의 전자기기를 소유하는 소비자일수록 '내 기기를 내가 원하는 대로 다룰 수 있는가'라는 질문에 부딪히곤 한다. 한 개발자는 자신이 구매한 이그렛(Egret) GT 전동킥보드를 대상으로 이 질문을 끝까지 밀어붙였다. 100km 주행거리와 큰 타이어, 그리고 속도·주행모드·배터리·주행거리를 표시하는 320x480 LCD 디스플레이를 갖춘 이 제품을 그는 하드웨어와 펌웨어 수준까지 분해해 분석했고, 최종적으로 디스플레이 유닛용 커스텀 펌웨어를 직접 작성하는 데까지 이르렀다. 이 사례는 단순한 취미 개조를 넘어, 상용 임베디드 기기의 내부 통신 구조와 보안 설계가 실무에서 어떻게 다뤄지는지를 보여주는 구체적인 관찰 기록이다.

출발점은 사소한 불편이었다. 전원을 켜면서 키패드의 '아래' 버튼을 누르면 펌웨어 업데이트 모드에 진입하는데, 여기서 버튼을 눌러 빠져나가면 PIN 입력 없이 곧바로 주행 모드로 진입할 수 있었다. 물리 잠금장치로 별도 보관한다 해도, 인증 절차가 이렇게 우회된다는 사실은 마음에 걸릴 수밖에 없다. 그는 먼저 원격 잠금 해제와 설정 변경, 배터리 확인을 지원하는 모바일 앱의 블루투스 핸들러를 살펴봤지만, 곧 흥미를 잃고 디스플레이의 USB-C 포트로 눈을 돌렸다.

충전 포트에 숨어 있던 CAN 버스

제조사는 이 USB-C 포트가 휴대폰 충전 전용이라고 명시했다. 실제로 데이터 핀을 연결해도 디스플레이는 USB 호스트나 디바이스로 동작하지 않았다. 그러나 USB-C 브레이크아웃 보드를 구해 오실로스코프로 각 핀을 짚어본 결과, 두 개의 핀이 CAN 버스로 쓰이고 있었다. 표준을 상당히 벗어난 설계다. 그는 ESP32-C6, SN65HVD230 트랜시버, MCP2515 컨트롤러를 엮어 급조한 CAN 로거로 부팅 시점의 메시지를 기록했다. 버스가 워낙 시끄러워서, egui로 만든 도구로 시간축 대비 메시지 ID를 점으로 찍어 시각화하니 어떤 것이 명령·응답·주기적 데이터인지 한눈에 구분됐다.

이 과정에서 스로틀, 주행모드, 모터 속도를 담은 핵심 메시지들이 드러났다. 예컨대 0x300은 디스플레이가 컨트롤러로 보내는 주행모드와 헤드라이트 상태 메시지로, 특정 바이트를 5a 대신 a5로 바꿔 보내면 컨트롤러가 리셋됐다. 0x306은 스로틀 위치와 방향지시등, 속도 제한을 담았는데, 스로틀 값이 9비트 부호 없는 정수로 전송되는 등 관례에서 벗어난 인코딩이 곳곳에 있었다. 다만 이 단계에서는 펌웨어에 접근할 수 없어, 모터를 원격 제어하는 정도의 장난 외에는 더 나아갈 수 없었고 프로젝트는 잠시 멈췄다.

교체 부품이 열어준 펌웨어 분석

돌파구는 몇 달 뒤 모터 컨트롤러와 디스플레이 유닛의 교체 부품을 온라인에서 구매할 수 있다는 사실을 발견하면서 열렸다. 원래 기기를 손상시키지 않고도 마음껏 뜯어볼 대상이 생긴 것이다. 컨트롤러는 나사 머리가 뭉개지고 포팅 컴파운드로 채워져 있어 분해가 까다로웠지만, 컴파운드가 무른 편이라 긁어낼 수 있었다. 다행히 부품 표면의 마킹이 지워지지 않은 채였고, MCU가 STM32F103 클론인 APM32E103 계열임을 확인했다. 기판 뒷면의 네 개 패드는 SWD 포트로 추정됐고, OpenOCD로 플래시와 부팅 직후 RAM을 덤프한 뒤 Ghidra로 분석에 들어갔다.

덤프한 펌웨어에서는 흥미로운 설계들이 나왔다. 컨트롤러 MCU에는 부트로더, '업데이터', 메인 애플리케이션 세 개가 각각 다른 주소에 올라가 있었고, 부트로더와 업데이터 모두 서로 다른 방식으로 CAN 버스를 통한 펌웨어 갱신 기능을 갖고 있었다. 애플리케이션 길이를 4·8바이트 정수가 아니라 10진수를 아스키 문자열로 저장하고, 그 뒤를 공백 문자로 채운 다음 \r\n으로 끝맺는 식의 특이한 구현도 있었다. 다만 컨트롤러 코드의 대부분은 FOC 모터 제어였고, 디스플레이가 유효한 스로틀 값을 일정 시간 보내지 않으면 정지하는 등 나름의 안전장치가 있었기에, 그는 안전에 직결된 이 부분을 건드리지 않기로 하고 디스플레이 유닛으로 방향을 틀었다.

암호화 없는 업데이트, 그리고 커스텀 펌웨어

디스플레이 유닛은 2mm 두께의 사출 성형 케이스라 분해에 훨씬 큰 노력이 들었지만, 기판에는 미사용 핀 헤더와 여러 MCU가 있었다. SWD로 메인 MCU 펌웨어를, 별도 헤더로 SPI 플래시를 덤프했는데 후자에는 GUI 비트맵 이미지만 담겨 있었다. 초기에는 Ghidra 버전의 버그로 THUMB 명령을 쓰는 함수 포인터가 제대로 태깅되지 않아, 콜백 테이블 구조로 짜인 펌웨어에서 호출 지점을 찾기 어려웠다. 그러나 CAN 핸들러 테이블을 스캔하는 함수를 우연히 발견하면서 각 메시지에 대응하는 코드를 빠르게 짚어낼 수 있었다. 제조사 제공 라이브러리를 LTO 없이 컴파일한 덕에 디컴파일 결과가 원본 소스와 거의 일치해, GPIO 핀과 주변장치 설정을 온전히 복원하는 작업도 수월했다. 남겨진 디버그 메뉴는 버튼 상태 enum을 채우는 단서가 됐다.

마지막 관문은 실사용 디스플레이에 자기 펌웨어를 올리기 위한 업데이트 절차의 해석이었다. 뜯어놓은 유닛이야 디버그 프로브로 바로 플래시하면 되지만, 정상 기기는 CAN을 통한 갱신 과정을 거쳐야 했다. 다행히 이 과정에는 암호화가 전혀 없었다. 업데이트는 ID 0x384 핸들러에서 시작되며, 64바이트 청크를 9개의 CAN 프레임에 나눠 보내고 각 청크는 CRC-16-CCITT로 검증된다. 첫 청크는 펌웨어 데이터가 아니라 파일 이름과 길이 문자열이며, 부트로더는 전원 투입 시 항상 업데이트 패킷을 확인하기 때문에 애플리케이션이 망가져도 복구가 가능하다. 다만 업데이트가 애플리케이션 이미지를 직접 덮어쓰는 방식이라, 실패하면 디스플레이가 벽돌이 된다.

이 기록이 한국의 임베디드·보안 실무자에게 주는 함의는 분명하다. '충전 전용'이라던 포트가 사실은 비표준 CAN 버스였고, 인증 우회 경로와 암호화 없는 펌웨어 업데이트가 함께 존재했다는 점은, 상용 기기의 마케팅 설명과 실제 설계 사이의 간극을 그대로 보여준다. 부트로더에 갱신 경로를 상시 열어두는 설계는 복구성 측면에서는 장점이지만, 서명·검증이 없다면 그대로 공격 표면이 된다. 물론 이 분석은 저자 개인의 기기와 별도로 구입한 교체 부품을 대상으로 한 것이며, 안전에 직결된 모터 제어 펌웨어에는 손대지 않았다는 신중함도 함께 읽어둘 대목이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://bensimms.moe/reverse-engineering-scooter/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...