[안드로이드 모의해킹] Magisk 루팅 원리와 기능

사전지식

패스트부트(Fastboot)

부트로더 언락 등의 작업을 수행할 수 있으며 system, boot, recovery 등 다양한 영역의 펌웨어를 교체작업할 수 있는 엔지니어링 프로토콜이다. 부트로더가 허용하는 경우에만 시스템을 플래싱할 수 있으며, 부트로더는 기본적으로 잠겨 있다. 부트로더를 잠금해제 할 수는 있지만 해제하는 시점에 개인정보보호를 위해 저장된 사용자 데이터는 모두 삭제된다.
삼성 단말기에서는 패스트부트라는 용어 대신 다운로드 모드(download mode)라는 명칭을 사용한다.


Magisk 동작원리

 

systemless working

boot.img에 'init' 파일이 빌드되어 있기 때문에 boot.img 파일을 수정하는 것이 필요하며, /system 경로의 파일은 수정할 필요가 없다. /system을 리플래싱(re-flashing)하는 것보다 부팅 이미지를 리플래시하는 것이 덜 번거로우므로 단말의 OTA 업데이트를 진행할 때도 이 방법을 더 많이 사용한다.

 

시스템으로서의 루트(System-as-Root, SAR)

최신 안드로이드 단말기에서는 '시스템으로서의 루트'(System-as-Root, SAR)를 사용하여 부팅한다. 이러한 경우, 부팅 시 램디스크(ramdisk)를 boot에서 로드하는 대신 system에서 로드한다. 이로써, [system.img]/init 이미지를 Magisk의 init으로 대체해야 한다. 또한, Magisk는 /init.rc를 수정하고 자체 파일을 /root/sbin 폴더에 위치시킨다. 이러한 접근 방식은 system.img를 수정하지만, Magisk는 시스템 파티션을 직접 수정하지 않는다.

 

안드로이드 A/B 시스템은 정상 부팅 중 skip_initramfs 옵션을 사용하는 경우, 'boot.img'에 복구용 'ramdisk'가 포함되어 있으므로 커널 명령행의 부트로더에서 전달된다. 따라서 Magisk는 항상 커널 바이너리를 skip_initramfs로 패치하고, boot.img 내부의 복구 ramdisk에 Magisk init 바이너리를 배치한다. 부팅 시 커널이 리커버리로 부팅하는 경우, 사용자가 의도적으로 복구 모드로 부팅할때 skip_initramfs가 없으면 Magisk init은 리커버리 초기화를 실행하고, 그렇지 않으면 Magisk init에 의해 다음을 과정을 거친다.

 

① system.img가 /system_root에 마운트
② ramdisk의 내용이 '/'에 복사
③ 이전에 존재하는 리소스 정리
④ rootfs가 /에 파일을 추가/수정
⑤ /system_root/system이 /system에 바인딩되어 마운트
⑥ 마지막으로 [/system]/init이 실행

A/B 시스템(seamless system)
안드로이드 A/B 시스템은 업데이트 매커니즘에 따라 시스템을 구분한 것이다. OTA(Over-The-Air) 업데이트 중에도 작동 가능한 부팅 시스템을 디스크에 남아 있도록 하여 단말의 비활성 시간(downtime)을 최소화한다.
https://source.android.com/docs/core/ota/ab?hl=ko

추가적으로 안드로이드 12부터는 가상 A/B 시스템을 사용한다.
https://source.android.com/docs/core/ota/virtual_ab?hl=ko

 

안드로이드 Q에서의 변경사항은 /system/ 경로에 마운트되지만, /init, /init.rc, /sbin 등 추가/수정할 파일은 바인드 마운트로 겹쳐진다.

A/B 시스템이 아닌 루트 단말에서는 boot.img에 ramdisk가 없기 때문에, 시스템리스 접근 방식을 유지하려면 Magisk를 설치하여 ramdisk를 복구해야 한다. 이는 안드로이드 단말의 부팅 및 루팅 프로세스에서 중요한 역할을 한다.

 

magisk hide

v24.0부터는 지원하지 않는 기능

루팅의 또다른 과제는 앱이 기기가 루팅되었는지 알 수 없도록 Magisk의 존재를 숨기는 것이다. 많은 앱이 루팅된 단말에서 앱이 실행되는 것을 원하지 않기 때문에 앱이 동작을 멈출 수 있다. 구글은 루팅의 주요 피해자 중 하나였기 때문에 GMS(Play 서비스) 프로세스로 실행되고 앱(자체 Google Pay 포함)과 개발자에게 기기가 현재 변조되지 않은 상태임을 알려주는 Play Protect의 일부로 SafetyNet을 도입했다.

 

루팅은 단말의 최고 권한을 가진 상태이면서 다양한 상태로 존재할 수 있다. 다른 상태로는 검증되지 않은 부팅(un-Verified), 잠금 해제된 부트로더(unlocked bootloader), CTS 미인증, 커스텀롬, 디버깅 가능한 빌드, SELinux 허용, ADB 허용(디버그 모드), 일부 우회 속성(properties), Lucky Patcher, Xposed 등이 있다. Magisk는 추가 모듈을 사용하여 이러한 탐지 로직의 대부분을 항상 우회하도록 하지만 앱이 모듈에서 사용하지 않는 다른 Android API를 사용하거나 루팅 시 특성을 포함하고 있는 시스템 내 일부 파일을 직접 읽을 수도 있다.

 

구글의 세이프넷에서 자신의 존재를 숨기는 것 외에도 Magisk는 바인드 마운트와 마운트 네임스페이스를 사용하여 사용자가 모든 앱에서 루트(su 바이너리 및 기타 Magisk 관련 파일)를 숨길 수 있도록 한다. 이를 위해서는 새로 포크(fork)된 앱의 VM을 지속적으로 감시해야 한다.

 

그러나 주로 /proc 또는 기타 파일 시스템에서 Magisk의 존재를 감지하는 새로운 기술이 개발되고 있음에 따라 앱에서 루팅된 디바이스를 실제로 숨기는 것은 어려운 작업이 되고 있다. 따라서 수정 사항을 감지하지 못하도록 숨기는 것을 적절히 지원하기 위해 여러 가지 기발한 방법이 사용된다. Magisk는 부팅 과정에서 자신의 존재 및 흔적을 모두 제거하려고 시도한다.

 

시스템 속성 초기화(resetprop)

일반적으로 시스템 속성은 init 프로세스에 의해서만 업데이트되고 루트 프로세스가 아닌 프로세스에 대해서는 읽기 전용으로만 업데이트되도록 설계되어 있다. root를 사용하면 setprop과 같은 명령을 사용하여 property_service(init에서 호스팅)에 요청을 보내 속성을 변경할 수 있지만 읽기 전용 프롭(ro.build.product와 같이 ro로 시작하는 속성)을 변경하고 속성을 삭제하는 것은 여전히 금지된다.

 

'resetprop'은 안드로이드 오픈소스 프로젝트(AOSP)에서 시스템 속성과 관련된 소스 코드를 추출하여 구현하고, property_service를 거치지 않고 프로퍼티 영역 또는 prop_area를 직접 수정할 수 있도록 패치되었다. property_service를 우회하기 때문에 몇 가지 주의 사항이 존재한다:

 

  • 속성 변경이 property_service를 거치지 않으면 *.rc 스크립트에 등록된 속성:foo=bar 동작이 트리거되지 않는다. resetprop의 기본 설정 속성 동작은 이벤트를 트리거하는 setprop과 일치한다.(먼저 속성을 삭제한 다음 property_service를 통해 설정하는 방식으로 구현됨). 이 특별한 동작이 필요한 경우 -n 플래그를 사용하여 비활성화할 수 있다.
  • 퍼시스턴트 속성(persist.sys.usb.config와 같이 persist.로 시작하는 속성)은 prop_area/data/property에 모두 저장된다. 기본적으로 속성을 삭제해도 영구 저장소에서 제거되지 않으므로 다음 재부팅 후 속성이 복원되며, 속성을 읽어도 영구 저장소에서 읽지 않으므로 getprop의 동작에 따라 영구 저장소에서 읽지 않는다. 플래그가 -p인 경우, prop을 삭제하면 prop_area/data/property 모두에서 prop이 제거되고, prop을 읽으면 prop_area와 영구 저장소 모두에서 읽혀진다.

 

Magisk가 제공하는 추가 기능

  • fstab 수정을 통한 dm-verity 비활성화 및 /data 암호화
  • restprop 도구를 이용한 읽기 전용 속성 비활성화, magiskboot 도구를 이용한 boot.img 수정
    magiskpolicy를 이용한 SELinux 수정

 

출처

  1. https://android.stackexchange.com/questions/213167/how-does-magisk-work
  2. https://topjohnwu.github.io/Magisk/details.html#resetprop
반응형