동작 원리

이 문서는 LTBox의 기술적인 배경을 조금 더 자세히 설명한다. 도구를 쓰는 데 반드시 읽어야 하는 것은 아니지만, 각 작업에서 무슨 일이 일어나는지 이해하는 데 도움이 된다.

핵심: 신뢰되는 테스트 키

Android Verified Boot(AVB)는 부팅 체인을 보호한다. 서명된 이미지(vbmeta, boot, vendor_boot 등)에는 각각 서명이 들어 있고, 부트로더는 그 서명이 자신이 신뢰하는 공개 키와 일치할 때만 이미지를 실행한다. 잠긴 기기에서 수정된 이미지를 플래싱할 수 없는 이유가 이것이다.

문제가 되는 레노버 태블릿들은 AVB 신뢰 루트로 AOSP 테스트 키를 신뢰하는 부트로더(ABL)를 탑재하고 있다. 이 키 쌍은 비밀이 아니다. 대응하는 개인 키가 공개된 AOSP 소스 트리 (external/avb/test/data/testkey_rsa4096.pem)에 그대로 들어 있기 때문이다. 개인 키가 공개되어 있으므로 누구나 잠긴 부트로더가 진짜라고 받아들이는 이미지에 서명할 수 있다.

LTBox는 이 사실 하나에 기반한다. 이미지를 수정한 뒤에는 잠긴 부트로더가 결과물을 계속 신뢰하도록 AVB 메타데이터를 다시 만든다. vbmeta와 서명된 부트 이미지는 AOSP 테스트 키로 다시 서명하고, 해시만 있는 이미지(vendor_boot 등)에는 새 AVB 해시 푸터를 붙인다. LTBox는 avbtool-rs에 내장된 테스트 키 사양(testkey_rsa2048 / testkey_rsa4096)을 쓰므로 외부 키 파일이 필요 없다.

지역 변환

레노버는 같은 하드웨어를 중국(PRC) 모델과 글로벌(ROW) 모델로 판매한다. 두 빌드는 vendor_boot.img 안에 저장된 지역 표시자에서 차이가 난다.

LTBox는 다음과 같이 지역을 변환한다.

  1. 표시자를 찾는다. vendor_boot.img에서 지역 태그 .PRC / IPRC(중국) 또는 .ROW / IROW(글로벌)를 스캔해 이미지의 현재 지역을 판별한다. 이미 목표 지역과 같으면 아무것도 바꾸지 않는다.
  2. 바이트를 패치한다. 표시자를 목표 지역으로 바꾼다(예: .PRC.ROW).
  3. vendor_boot 해시를 다시 계산한다. 바이트가 바뀌면 원래 AVB 해시가 깨지므로, 패치된 이미지의 AVB 해시 푸터를 다시 계산한다.
  4. vbmeta를 다시 만든다. vbmeta.img는 다른 파티션의 해시를 담고 있으므로 함께 다시 만들어야 한다. LTBox는 원래 vbmeta의 디스크립터를 유지하고 vendor_boot에 해당하는 값만 패치된 이미지의 값으로 교체한 뒤, 테스트 키로 vbmeta에 다시 서명한다.

그 결과 부트로더가 계속 받아들이는, 새 지역용 vendor_boot.img + vbmeta.img 쌍이 만들어진다.

v3.1.4부터는 대상 펌웨어에서는 테스트 키 ABL 취약점이 이미 수정되었지만 기기에 설치된 펌웨어에서는 아직 수정되지 않은 경우에도 동작한다. LTBox는 기기의 현재 abl을 백업하고, 지역을 변환하고, 필요한 AVB 체인 파티션만 AOSP 테스트 키로 다시 서명한 뒤 펌웨어를 플래싱하고, 다시 서명한 파티션을 덮어쓴 다음 백업해 둔 abl을 복원한다. 기기는 테스트 키를 신뢰하는 부트로더를 유지하므로, 변환된 펌웨어도 잠긴 기기에서 부팅될 수 있다.

루팅

루팅은 부트 이미지에 루트 제공자를 주입하고 다시 서명하는 작업이다. LTBox는 제공자마다 알맞은 이미지와 방식을 고른다.

  • KernelSU / KernelSU Next / SukiSU / ReSukiSU — LKM(로더블 커널 모듈) 방식을 쓴다. LTBox는 init_boot.img를 패치해 램디스크의 init 바이너리를 KernelSU 부트스트랩으로 교체하고, 순정 커널이 부팅 시 로드하도록 kernelsu.ko 모듈을 배치한다.
  • Magisk(및 포크) — LTBox가 Magisk APK에서 패치 페이로드를 추출해 init_boot.img에 적용한다. Magisk 앱이 기기에서 부트 이미지를 패치하는 방식과 같다.
  • APatch / FolkPatch — 커널 자체를 패치한다. LTBox는 boot.img에서 커널을 추출해 kptools로 패치하고(슈퍼키와 커널 패치 모듈 추가), 다시 패킹한다.

KernelSU 계열에는 선택적인 GKI 모드도 있다. LKM 램디스크 경로 대신, 사용자가 제공하는 커널 패키지(AnyKernel3 zip)로 boot.img의 커널을 교체한다.

패치가 끝나면 이미지를 다시 패킹하고 AVB 테스트 키로 다시 서명한다. 부트로더가 그 서명을 신뢰하므로 기기는 수정된 이미지로 부팅된다. 언루팅은 루팅 전에 LTBox가 백업해 둔 순정 부트 이미지를 그대로 되돌린다.

롤백 보호 우회

AVB에는 롤백 인덱스도 있다. 값이 올라가기만 하는 숫자로, 부트로더는 기기가 이미 커밋한 값보다 낮은 롤백 인덱스를 가진 이미지의 부팅을 거부한다. 다운그레이드를 막는 것이 바로 이 장치다.

롤백 인덱스는 두 파티션을 대상으로 기록되는데, LTBox는 기기 전체에 하나의 값을 쓰지 않고 bootvbmeta_system에 대해 각각 하한값을 따로 구한다. 각 이미지는 자기 위치의 하한값과만 비교한다. 처리 과정은 다음과 같다.

  1. 기기의 하한값을 읽는다. 어디에서 읽는지는 플래싱을 시작한 상태에 따라 다르다.

    • EDL에서 시작하면 두 슬롯 모두에서 bootvbmeta_system을 덤프해 위치별로 가장 큰 인덱스를 취한다. 두 슬롯에 서로 다른 이미지가 들어 있을 수 있기 때문이다.
    • 부트로더 모드에서는 LTBox가 stored_rollback_index:N 항목을 읽어 분류한다. 관련된 두 위치 중 낮은 쪽이 vbmeta_system, 높은 쪽이 boot이다.

    순정이 아닌 값이 하나도 없으면 기기에 하한값이 없다는 뜻이므로 패치가 필요 없다.

  2. 비교하고 패치한다. 각 이미지의 롤백 인덱스를 자기 위치의 하한값과 비교한다. 확인 화면에 표시되는 세 가지 모드가 있다.

    • 켜기 — 대상 이미지를 항상 기기의 하한값으로 갱신한다.
    • 자동 — 이미지의 인덱스가 자기 위치의 하한값보다 낮을 때만 갱신한다. 그대로 두면 거부되는 경우다.
    • 끄기 — 롤백 인덱스를 그대로 둔다.
  3. 갱신한다. 패치가 필요하면 해당 이미지를 자기 위치의 하한값으로 다시 서명하거나 새 해시 푸터를 붙인다. 그러면 부트로더가 더 이상 그 이미지를 "예전" 빌드로 보지 않는다.

플래싱 파이프라인

기기에 대한 모든 쓰기는 퀄컴 EDL(Emergency Download, 9008이라고도 한다) 모드를 거친다. 전체 플래싱은 다음 단계로 진행된다.

  1. EDL 진입. LTBox는 기기가 이미 EDL에 있는지, ADB 또는 부트로더 모드로 연결되었는지 확인한다. 이미 EDL이면 그대로 진행하고, ADB나 부트로더 모드면 EDL로 재부팅한다. 어느 쪽도 아니면 사용자가 직접 EDL로 진입할 때까지 기다린다.
  2. 프로그래머 업로드. EDL에서 기기는 퀄컴 Sahara 프로토콜로 통신한다. LTBox는 펌웨어의 프로그래머(xbl_s_devprg/prog_firehose 로더)를 기기 메모리에 업로드한다.
  3. 파티션 읽기·쓰기. 이후 프로그래머는 Firehose 프로토콜로 통신하고, LTBox는 이를 통해 이름이나 LUN 기준으로 파티션을 지우고 쓰고 읽는다.
  4. 리셋. 플래싱이 끝나면 LTBox가 기기를 리셋해 새 펌웨어로 부팅시킨다.

LTBox가 AVB 메타데이터를 다시 만들기 때문에(해시 푸터 재계산과 신뢰되는 테스트 키로의 vbmeta 서명), 잠긴 부트로더에서 파티션이 바뀌었는데도 기기는 정상적으로 부팅된다.