내 블루투스 이어버드의 볼륨 조절하기
(blog.ornx.net)- Tozo T6 이어버드의 페어링·연결·해제 시스템 사운드가 너무 커서, 펌웨어 안의 오디오 파일 게인을 직접 낮추는 방식으로 해결함
- 칩셋은 Airoha AB1562 계열로 추정됐고, AirReps156X 앱을 통해 진단 정보와 수정 펌웨어를 올릴 수 있는 가능성을 확인함
- Tozo 앱의 업데이트 확인 트래픽을 mitmproxy로 가로채
/api/v1/getOtaVersionV3응답에서 이어버드 펌웨어 바이너리 링크를 확보함 - 펌웨어는 좌우 이어버드용 FotaPackage 2개와 FileSystemImage 2개로 구성됐고, 수정 대상인 파일시스템 이미지 안에 mp3 파일들이 원본 그대로 들어 있었음
mp3gain으로 mp3를 재인코딩하거나 길이를 바꾸지 않고 -19.5dB만 낮춘 뒤 이미지 내부 바이트를 교체해 플래싱했고, 기기는 정상 동작하며 소리가 훨씬 작아짐
너무 큰 시스템 사운드와 초기 가정
- Tozo T6 이어버드는 페어링, 연결, 연결 해제 때마다 소리를 재생했고, 이 소리가 사용자가 선호하는 수준보다 너무 컸음
- 이퀄라이저에서 전체 대역을 몇 dB 낮추는 방식으로는 해결되지 않았고, Tozo에 이메일로 문의했지만 회사는 할 수 있는 일이 없다고 답함
- 목표는 기기에서 실행되는 펌웨어를 수정해 해당 소리 파일의 볼륨을 낮추는 것이었음
- 처음에는 몇 가지 전제를 세우고 접근함
- 온라인에서 기기용 펌웨어 바이너리를 구할 수 있음
- 펌웨어가 ELF 같은 이해하기 쉬운 바이너리 구조일 수 있음
- 오디오 파일이 펌웨어 안에 포함돼 있고, 오프셋과 길이를 알면 수정 가능함
- 오디오가 PCM 같은 단순한 형식일 수 있음
- 수정한 펌웨어를 기기 또는 칩셋용 도구로 플래싱할 수 있음
- 실제로는 여러 가정이 빗나갔고, 리버스 엔지니어링 자체보다 프록시 같은 분석 인프라 설정과 우회 경로 탐색에 많은 시간이 들어감
기기와 칩셋 식별
- 저가 전자기기에는 보통 여러 주체와 계층이 얽혀 있음
- 제품을 브랜드로 판매하는 벤더는 이 경우 Tozo임
- 펌웨어를 실행하는 중심 하드웨어인 칩셋이 있음
- 칩셋은 ARM, MIPS 같은 기반 기술에서 파생된 ISA를 사용할 수 있음
- 추가 코프로세서나 하드웨어 인터페이스용 기능이 통합될 수 있음
- Tozo Android 앱을 디스어셈블하면서 Airoha SDK와 특정 칩 모델 참조, 기기와 통신하는 기본 기능들을 발견함
- Reddit의 AirPods 복제품 관련 커뮤니티
/r/airreps에서 진행 방향에 대한 정보를 얻었고, AirReps156X 앱도 확인함 - AirReps156X 앱은 Airoha SDK를 사용하며 Airoha 기기 진단 정보를 제공할 수 있었음
- 해당 앱에 기기를 연결하자 진단 문자열
QW_1562U_SDK1.5.1이 표시됐고, 이를 근거로 기기 칩셋을 Airoha AB1562 시리즈로 판단함 - AirReps156X 앱에는 새 펌웨어를 플래싱하는 기능도 있어, 수정 펌웨어를 기기에 올릴 수 있는 핵심 조건이 충족됨
Tozo 앱 트래픽에서 펌웨어 URL 찾기
- Tozo 앱은 이어버드에 연결하면 현재 펌웨어 버전과 최신 여부를 표시함
- 앱이 서버와 통신해 최신 펌웨어 정보를 확인한다고 보고, 업데이트 체크 과정에서 실제 펌웨어 파일 URL을 찾기로 함
- 디컴파일된 코드를 끝까지 읽는 정적 분석 대신, 네트워크 요청을 직접 보는 동적 분석을 선택함
- 무선 NIC,
hostapd,mitmproxy로 가로채기 프록시를 구성함 - Tozo APK는
apktool과uber apk signer로 패치해 사용자 CA 저장소의 mitmproxy TLS 인증서를 신뢰하도록 바꿈- Android 앱은 기본적으로 시스템 CA 저장소만 보는 경우가 많음
- APK 패치를 통해 사용자 CA 저장소도 사용하게 만들고, 다시 서명해 Android에서 실행되도록 함
- 프록시 구성에는 AP 설정,
iptables로 80/443 트래픽을 mitmproxy 포트로 리다이렉트하는 작업, NAT 설정이 포함됨 - 앱이 펌웨어 버전 옆에 “current”를 표시할 때
/api/v1/getOtaVersionV3엔드포인트로 요청을 보냈고, 응답에 필요한 펌웨어 bin 링크들이 들어 있었음
펌웨어 파일 구조와 분석
- 확보한 펌웨어는 총 4개 파일로 구성됨
- 좌우 이어버드 각각에 대한
FotaPackage - 좌우 이어버드 각각에 대한
FileSystemImage
- 좌우 이어버드 각각에 대한
- 두 파일시스템 이미지는 동일해, 고유 파일은 좌우용 FotaPackage 2개와 파일시스템 이미지 1개, 총 3개였음
file,strings,hexdump,binwalk로 포맷과 내장 파일을 확인하려고 함- 파일시스템 이미지에서는 일부 파일명 문자열이 보였지만,
binwalk는 기대한 mp3 파일을 찾아내지 못함 - mp3는 명확한 magic number나 footer가 없어 임의의 바이너리에서 오프셋과 길이를 확정하기 어려웠음
- 시작은
0xFFFF또는0xFFFE일 수 있음 - 둘 다 파일 식별자로 충분히 독특하지 않음
- 시작은
- 파일시스템 이미지 구조를 알면 각 파일의 시작과 끝을 알 수 있으리라 보고, 이미지 포맷을 이해하는 쪽으로 방향을 잡음
엔트로피 분석과 ROFS
- 엔트로피 분석은 파일의 어느 부분이 상수, 랜덤 노이즈, ASCII 텍스트에 가까운지와 전환 지점을 시각화하는 데 유용함
- 파일시스템 이미지는 구조가 보이는 형태였지만, FotaPackage 파일은 압축 또는 암호화된 것으로 보였음
- 좌우 FotaPackage는 헤더 일부만 드문드문 다르고, 본문은 거의 동일하다가 끝부분 약 7KB에서 완전히 달라짐
- 이 차이가 정확히 무엇을 뜻하는지는 확인하지 못했지만, 불투명한 변환이 적용돼 있어 큰 노력 없이는 의미 있는 정보를 얻기 어렵다고 판단함
- 파일시스템 이미지는 ASCII 문자열
ROFS로 시작함 ROFS에 대한 공개 문서나 일치하는 포맷 정보를 찾지 못했고, 이후 발견한 Airoha SDK에는 이 이미지를 읽는 인터페이스 구현이 포함돼 있었음- 한때 FotaPackage를 해독하는 경로를 시도했지만, SDK가 펌웨어를 전송 전에 변환하지 않는다는 점을 확인하는 데 그쳤고 성과는 없었음
mp3를 재인코딩하지 않고 낮추기
- 파일이 mp3라는 사실은 처음에는 위험 요소였음
- mp3 인코더에는 많은 옵션이 있고, 알 수 없는 디코더가 특정 합법적 파일을 처리하지 못할 수 있음
- 연결 직후 재생되는 오디오가 문제를 일으키면 기기가 다시 연결되기 전에 충돌할 수 있어 복구 불가능한 상태가 될 수 있음
- mp3를 다시 인코딩하면 파일 길이가 바뀔 수 있고, 그러면 파일시스템 이미지 안의 길이 정보까지 정확히 수정해야 할 가능성이 있었음
- 다행히 mp3는 재인코딩, 길이 변경, 메타데이터 변경 없이 게인을 조정할 수 있었음
- 이는 JPEG를 재인코딩하지 않고 회전할 수 있는 것처럼, 내부 데이터 구조의 일부만 수정하는 방식에 가까움
SDK에서 얻은 결정적 단서
- 칩셋 이름으로 검색해 Airoha SDK 사본을 찾았고, 그 안에 기기에서 들리는 것과 같은
.mp3파일들이 포함돼 있었음 - 간단한 Python 프로그램
bincontains.py를 작성해 어떤 파일이 다른 바이너리 안에 그대로 포함돼 있는지 확인함 - SDK의 mp3 파일들이 파일시스템 이미지에 원본 그대로 들어 있음을 검증함
- 압축되지 않았음
- 블록으로 쪼개져 있지 않았음
- 따라서 이미지 안의 오프셋과 길이를 계산할 수 있었음
- ROFS 관련 SDK 코드를 간단히 살펴봤고, 체크섬 존재를 강하게 시사하는 심볼은 보이지 않았음
- 이 시점에서 추가 리버스 엔지니어링 없이 수정에 필요한 조건이 갖춰짐
- 펌웨어 파일과 플래싱 방법이 있음
- mp3 파일의 이미지 내 위치와 길이를 알 수 있음
- mp3 게인은 길이 변경 없이 조정 가능함
- 파일 내부 바이트 범위만 바꿔도 파일시스템 메타데이터가 깨지지 않는다고 가정함
파일시스템 이미지 수정과 플래싱
- Bash 스크립트는 SDK의 mp3 파일들을 순회하면서 파일시스템 이미지 안에 포함된 파일을 찾음
- 포함된 mp3를 임시 파일로 복사한 뒤
mp3gain으로 게인을 낮춤 - 사용한 조정값은 -19.5dB였음
- 수정된 mp3의 크기가 원본과 같은지 확인한 뒤,
dd로 파일시스템 이미지의 해당 오프셋에 바이트를 덮어씀 - 최종 펌웨어 이미지는 바이너리 diff에서 예상대로 몇 바이트만 달라짐
- 수정된 펌웨어를 기기에 플래싱했고, 기기는 정상 동작했으며 시스템 사운드는 시작 전보다 훨씬 조용해졌음
결과와 한계
- 펌웨어 암호화를 해독하거나
ROFS파일시스템 포맷 전체를 이해할 필요는 없었음 - 실제로 많은 리버스 엔지니어링 시간은 최종 해결에 직접 필요하지 않은 우회 경로에 쓰임
- 시스템 사운드 볼륨 조절이 기기의 기본 기능이었다면 이런 수정은 필요하지 않았음
- 오디오를 재생하는 기기라면 기기에서 나는 모든 소리에 적용되는 볼륨 제어가 UI 차원에서 제공되는 편이 더 적절함
- 이 경우에는 펌웨어 이미지 내부 mp3의 게인만 낮추는 방식으로 충분한 우회책을 만들 수 있었음
댓글과 토론
Hacker News 의견들
-
내 블루투스 수면 마스크도 누가 이렇게 고쳐줬으면 좋겠음
전반적으로 꽤 좋은데, 배터리가 부족하거나 꺼질 때가 되면 그걸 최대 음량으로 알려줌
수면 마스크에서 그럼- 그거 사실 꽤 웃김. 처음 알았을 때 엄청 화났을 모습이 상상됨
비슷하게 결함 있는 알람시계를 쓴 적이 있는데, MSF 무선 시각 동기화 기능이 들어 있었음
그런데 MSF 시각 신호와 다시 동기화할 때마다 알람과 같은 소리를 2~3초 동안 냈고, 끌 수도 없었음
항상 새벽 3시쯤 같은 끔찍한 시간에 울려서, 결국 열어 보고 MSF 안테나를 잘라냈고 시계가 항상 조금 틀리다는 사실을 알면서 더 잘 잠 - 내 블루투스 이어 클립은 배터리 부족 경고를 할 때 이렇게 동작함: 현재 재생 중인 오디오를 끄고, 극적인 침묵을 가진 뒤, “BATTERY LOW. PLEASE CHARGE NOW.”라고 말하고, 다시 한 번 극적인 침묵을 가진 뒤 정상 동작으로 돌아감
그동안 통화 상대가 몇 초간 무슨 말을 했는지 다시 따라잡으려 하지만 잘 안 됨
이 알림은 아무것도 안 하는 것보다 훨씬 나쁨. 경고가 없을 때 최악은 소리가 끊겨서 상대 말을 놓치는 것인데, 이어 클립이 바로 그 효과를 더 빠른 일정으로 일부러 만들어냄
기존 오디오 흐름에 눈에 거슬리지 않는 삐 소리 패턴 같은 식으로 섞지 못할 이유가 없음. 1초도 안 걸리면서, 막으려는 문제를 스스로 만들지도 않을 것임
상상하기 어려운 나쁜 블루투스 동작으로는, 음성 채팅을 쓰면 일반 컴퓨터 오디오가 전부 사라지는 것도 있음. 음성 채팅이 마이크 입력을 쓰면 블루투스 장치가 “헤드셋” 모드로 바뀌고, 이 모드는 스테레오를 모노로 바꾸며 오디오 입력을 제공 중이거나 제공할 가능성이 있는 동안 허용되는 유일한 출력이 됨
오디오 입력을 쓰지 않는 앱들은 더 이상 존재하지 않는 블루투스 헤드폰 장치로 계속 재생하려 해서 모두 소리를 낼 수 없게 됨
왜 장치 모드가 여러 개여야 하는지 모르겠음. 가족과 이야기한다는 부작용으로 기능을 잃고 싶을 이유가 없음. 마이크가 켜질 가능성이 있다는 이유로 양쪽 귀에 다른 오디오 신호를 동시에 재생하는 게 뭐가 그렇게 어려운지 모르겠음. 비블루투스 장치는 이걸 처리하고, 심지어 주목할 기능으로도 취급되지 않음. “마이크를 쓰는 동안 헤드폰이 꺼지지 않는다”가 왜 특별해야 하나? - 결국 3.5mm 잭 케이블이 달린 베개 스피커를 쓰게 됨. 블루투스에서 겪던 이런 문제를 피할 수 있음
- 그게 뭘 하는 장치임? 자려고 할 때 왜 블루투스 연결이 필요한지 궁금함
- Futurama의 NNY 도시 시계가 생각남. 컷신에서 최대 음량으로 “THE TIME IS FOUR AM”이라고 외침
- 그거 사실 꽤 웃김. 처음 알았을 때 엄청 화났을 모습이 상상됨
-
멋짐! 끝까지 해낸 원 작성자 대단함
큰 소리 이어버드 얘기가 나온 김에, 내겐 반대 문제가 있을지도 모르겠음. 러닝머신에서 Bose 운동용 이어버드를 편안하고 보수적인 음량이라고 생각하는 수준으로 쓰는데, iPhone은 음량이 너무 높아서 청력을 망치고 있다는 알림을 줌
휴대폰이 맞는 걸까? 그렇다면 귀 건강을 위해 즐거움을 조금 희생할 의향은 있음. 하지만 그럴듯한 다른 가설도 있음. 이 이어버드는 같은 음량 설정에서 내가 써 본 다른 제품보다 실제 물리 음량이 뚜렷하게 낮아서, Apple의 게으른 모델링이 내가 받는 것 같은 잘못된 알림을 만들 수 있음
Apple이 제품 모델과 음량 설정을 실제 물리 음량에 매핑하는 데이터베이스를 만들었다면 칭찬하고 싶음. 하지만 알림과 기능 설명에 세부 정보가 전혀 없어서 신뢰가 가지 않고, Apple이 대학 과제 수준 모델을 실제 제품에 넣었다는 이유만으로 운동을 더 별로로 만들고 싶지는 않음
이 알림을 뒷받침하는 데이터 과학이 제대로 된 건지 아는 사람이 있나?- iOS에서 Android의 딱 두 가지가 그리운데, 둘 다 블루투스가 덜 구리게 동작하는 것과 관련 있음
첫째, iOS에서는 내 블루투스 이어버드의 최소 음량이 너무 큼. 써 본 모든 서드파티 헤드폰에서 그랬고, 온라인에서는 10년째 불평이 이어짐. EU가 고치라고 법까지 통과시켰지만, 스포일러: 효과 없었음
제발 UI의 최소 음량은 하드웨어 음량 정수 1에 매핑되어야 함
둘째, 서드파티 앱이 자동차 블루투스 미디어 탐색 메뉴에 음악이나 팟캐스트를 노출할 수 없음. Android에서는 가능함
그래서 Android에서는 차의 조그휠로 팟캐스트를 듣고 Tidal을 스트리밍할 수 있지만, iOS에서는 안 됨
다른 블루투스 불만도 있음. 왜 내 Apple Watch는 자동차 스테레오를 블랙리스트에 넣는가? iOS 버전 N과 N-1의 블루투스는 정말 버그가 많음 - 내 휴대폰도, Android 5이고 솔직히 거의 안 써서 고장 날 때까지 새것이 필요 없는데, 재부팅 뒤 처음으로 차에 블루투스로 연결할 때마다 같은 짓을 함
그냥 명백히 멍청한 기능임. 루팅만 할 수 있으면 어딘가 설정 파일에 있는 그 스위치를 끌 수 있을 텐데, Samsung Galaxy J1 (2016)에서는 한 번도 성공하지 못함 - 운영체제가 반대쪽에서 몇 dB가 나오는지 알지는 못할 것 같음
내 Bose도 블루투스 칩셋에 따라 다르게 동작함. Linux에서는 뭐라도 제대로 들으려면 음량을 150%로 해야 함 - Apple이 그런 매핑을 만들었다 해도 출시되는 순간부터 구식 데이터가 됨
하지만 그런 일을 했다고 믿을 이유는 없음. 헤드폰 제조사가 블루투스 연결 시 dB 범위를 보고해서 그런 기능을 가능하게 한다면 흥미롭겠지만, 그런 건 들어본 적 없음. 3.5mm 잭과 달리 블루투스라면 그런 기능을 가능하게 할 수 있는 영역이긴 함 - 소음 제거 헤드폰/이어버드가 이 문제를 해결해줌
소음 제거를 쓰면 음량을 20% 아래로 유지하면서도 귀를 혹사하지 않는 편안한 청취가 가능함
내 경우 몇 년 전 긴 운동 후 귀 통증이 생기기 시작해서 Apple 알림이 아마 맞다고 봄. 소음 제거를 쓰고 나서는 완전히 사라졌음
- iOS에서 Android의 딱 두 가지가 그리운데, 둘 다 블루투스가 덜 구리게 동작하는 것과 관련 있음
-
이런 작업 정말 좋음. 갑자기 이 이어폰 모델이 꽤 흥미롭게 느껴짐
덧붙이면, 블루투스 장치가 내는 시스템 소리는 제품 차이를 가장 강하게 만드는 요소 중 하나임. 어떤 건 완전히 끔찍함: https://youtu.be/J2wPsH64JEM
그런데 리뷰나 제품 페이지에서 그 제품이 어떤 소리를 내는지 알려주는 경우를 본 적이 없음. 하루에도 여러 번 들어야 하고, 끌 방법도 없는데 말임
이 소리를 바꿀 수 있게 하는 것만으로도 꽤 쉬운 차별화가 될 것 같음- 내 AfterShokz의 재생/일시정지 삐 소리를 줄일 수 있으면 좋겠음
귀마개를 끼고 시끄러운 작업장에 있을 때는 완벽한데, 조용한 사무실에서 집중하려고 음악을 작게 틀어두면 불편하고 깜짝 놀라게 됨
왜 시스템 소리가 음량 설정을 따르지 않는지 이해가 안 됨
- 내 AfterShokz의 재생/일시정지 삐 소리를 줄일 수 있으면 좋겠음
-
휴대폰이 연결된 헤드셋이 스피커 세트, 인이어 모니터, 골전도인지 등을 알 수 있으면 좋겠음
일반 스피커 세트를 써서 집안 어디서든 들리게 일부러 크게 틀고 싶을 때 “음량이 너무 큼” 알림은 짜증남
골전도 헤드폰은 잘 들으려면 꽤 큰 음량이 필요해서 두 배로 짜증남 -
이게 펌웨어 암호화가 없는 Airoha 대상이라 다행임
궁금하면 펌웨어 형식용 010 Editor 템플릿도 있음
https://github.com/ramikg/airoha-firmware-parser -
실력은 인정하지만, 파일 재생 음량을 조금 바꾸는 것처럼 기본적인 일을 하는 데 이렇게까지 노력이 필요하다는 사실은 아쉬움
도구가 원하는 대로 동작하게 만드는 데 이 정도 수고가 필요해서는 안 될 것 같음 -
“이해할 만하다”가 아님. 제품값을 냈고, 이건 고쳐야 하는 제품 문제임
-
이 글을 읽고 중고 Tozo T6를 샀는데, 겉보기에는 꽤 최근에 제조된 것 같았지만 재현할 수 없었음
공식 Tozo 앱이 헤드셋을 인식하지도 못했고, AAC 지원으로 식별되는 Airoha 칩셋 사용 여부도 확인할 수 없었음. 내 것은 SBC만 지원함
가짜를 샀거나, 작성자가 산 뒤 내부 변경이 있었던 것 같음
일부 오디오 파일은 인터넷에 있는 부분적인 Airoha SDK에 포함된 것과 같게 들리지만, 다른 새로운 음성 파일도 재생함
이 결과를 독립적으로 검증하거나 가지고 놀고 싶다면 가짜 AirPods가 더 나은 경로일 수 있음 -
크고 나쁜 시스템 소리에 대해 더 많은 사람이 불평했으면 좋겠음
-
내 Sony WH-1000XM4도 정확히 같은 문제가 있는데, Sony는 펌웨어 페이로드를 암호화하고 장치에서 복호화하는 것 같음
거의 분해해서 전부 덤프하고 탐색해볼 뻔했지만, 손이 떨려서 망가뜨릴 가능성이 너무 큼
해킹 가능한 소음 제거 헤드셋이라면 꽤 큰돈을 낼 의향이 있음