Notes / Mobile Security
안드로이드 루팅 탐지 우회
test-keys·su/busybox 경로·Superuser·su 실행 가능 여부로 루팅을 판별하는 안드로이드 탐지 로직의 생김새와, 그 판별을 런타임에 뒤집는 우회를 정리합니다.
목차
루팅 탐지는 “이 단말이 루팅됐는가”를 여러 신호로 판별해 앱을 종료시킨다. 신호는 정해져 있으므로, 탐지 함수가 어디서 무엇을 보는지 알면 그 반환을 뒤집어 우회한다.
개념
전형적인 루팅 판별 신호(아래 중 하나라도 걸리면 루팅으로 간주하는 함수가 많다):
Build.TAGS에test-keys포함(커스텀 롬 흔적).su·busybox바이너리가/system/bin,/system/xbin,/sbin,/data/local/...등 경로에 존재./system/app/Superuser.apk존재.Runtime.getRuntime().exec("su")가 예외 없이 실행됨.
이 신호들을 모은 Boolean 함수(예: onCheckOSFalsify())가 하나로 결론 내는 구조라, 그 함수의 반환을 false로 고정하거나 개별 체크(File.exists, exec)를 속이면 통과한다.
if (Build.TAGS.contains("test-keys")) return true;
if (new File("/system/xbin/su").exists()) return true;
Runtime.getRuntime().exec("su"); // 성공하면 루팅
공격·우회 구문
허가된 대상에서만.
판별 함수 자체를 뒤집기 (Frida)
Java.perform(function () {
var C = Java.use("com.example.app.security.RootCheck");
C.onCheckOSFalsify.implementation = function () { return false; };
});
개별 신호 속이기
Java.perform(function () {
// su/busybox 경로 존재 체크 무력화
var File = Java.use("java.io.File");
File.exists.implementation = function () {
var p = this.getAbsolutePath();
if (p.indexOf("su") >= 0 || p.indexOf("busybox") >= 0 ||
p.indexOf("Superuser") >= 0) return false;
return this.exists();
};
// Runtime.exec("su") 차단
var RT = Java.use("java.lang.Runtime");
RT.exec.overload('java.lang.String').implementation = function (cmd) {
if (cmd.indexOf("su") >= 0) throw Java.use("java.io.IOException").$new("blocked");
return this.exec(cmd);
};
});
네이티브·보편 스크립트
탐지가 .so 에 있으면 네이티브 함수 Interceptor 후킹
공개 root-detection bypass 스크립트(codeshare)로 다수 구현 일괄 처리