본문 바로가기
Mobile App/Android

[안드로이드] 앱 위변조 방지 (루팅, 탈옥, 패킷탈취, 디버깅툴 탐지 등)

by Jman 2024. 4. 15.

회사에서 상반기 하반기를 나누어 사내 보안팀이 '모의해킹' 팀을 고용하여 회사 내 자체 서비스 App을 모의해킹 한다.

그에 맞춰 이번에 작성하게 될 주제가 선정되었다.

 

사실 아래에 여러가지 방법들을 소개해줄 예정이지만,

가장 좋은 건 보안업체 SDK 를 사용하여 보안취약점 대응을 하는게 좋다. (우리 회사는 당연히 외부 라이브러리 보안 SDK 사용 중)

 

우리 회사에서 사용하고 있는 보안 관련 SDK 업체는 라온시큐어, 시큐센, 스틸리언 이 있다.

그 외에 다른 회사들도 있으니 보안은 보안업체에 맞겨 대응을 하고 우리 안드로이드 개발자는 고객을 위한 서비스에 집중을 하면 된다.

 

위와 같은 보안 관련 SDK 없이 어떤 방법으로 앱 보안 취약점에 대응할 수 있을지 몇 가지 소개하겠다.

사실 아래 방법들은 검색하면 잘 알려져 있고, 그냥 전체적으로 정리 했다고 생각하면 된다.

 

TL;DR

1. 루팅 탐지

2. 'Su' 루트 실행 권한 있는지 확인

3. 루팅 우회 탐지(프리다)

4. 디버깅 툴 탐지

5. 타 스토어에서 설치된 앱 체크

 

 

 

#1. 루팅 탐지

루팅된 기기에서 사용되는 바이너리 파일 경로에 루팅 탐지 관련 몇 가지 텍스트를 확인해보는 방법이다.

 

/system/build.prop 파일에 test-keys 값이 존재하는 지 확인

private static Boolean checkTestKey() {
    String tag = android.os.Build.TAGS;
    return tag != null && tag.contains("test-keys");
}

 

바이너리 파일 경로에 루팅탐지 관련 텍스트 확인

    /**
     * 루팅된 기기에서 사용되는 바이너리 파일 경로
     */
    private static final String[] binaryPaths = {
            "/data/local/",
            "/data/local/bin/",
            "/data/local/xbin/",
            "/sbin/",
            "/su/bin/",
            "/system/bin/",
            "/system/bin/.ext/",
            "/system/bin/failsafe/",
            "/system/sd/xbin/",
            "/system/usr/we-need-root/",
            "/system/xbin/",
            "/system/app/Superuser.apk",
            "/cache",
            "/data",
            "/dev"
    };
    

    private static Boolean checkForBinary(String fileName) {
        for (String path : binaryPaths) {
            File f = new File(path, fileName);
            if (f.exists()) {
                return true;
            }
        }
        return false;
    }
    
	/**
     * 비지박스 루팅 탐지
     */
    private static Boolean checkForBusyBoxBinary() {
        return checkForBinary("busybox");
    }

    /**
     * Su 루팅 탐지
     */
    private static Boolean checkForSuBinary() {
        return checkForBinary("su");
    }

    /**
     * 매지스크 루팅 탐지
     */
    private static Boolean checkForMagiskBinary() {
        return checkForBinary("magisk");
    }

 

 

#2. 'Su' 루트 실행 권한이 있는지 확인

앱을 루팅하게 되면, Su 실행 권한이 등록이 되어있다.

private static Boolean checkSuExecAuthority() {
    Process process = null;
    try {
        process = Runtime.getRuntime().exec(new String[]{"/system/xbin/which", "su"});
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = in.readLine();
        process.destroy();
        return line != null;
    } catch (Exception e) {
        if (process != null) {
            process.destroy();
        }
        return false;
    }
}

 

 

#3. 루팅 우회 탐지(프리다) 확인

탐지 우회 Hooking 을 하게 되는 경우를 막는 로직이다.

/**
 * 프리다 탐지 (탐지 우회 Hooking 막기)
 */
private static boolean checkFrida() {
    int pid = android.os.Process.myPid();

    try {
        String mapsFileContents = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mapsFileContents = new String(Files.readAllBytes(Paths.get("/proc/"+pid+"/maps")), Charset.defaultCharset());
        }

        if (mapsFileContents == null) return false;

        return mapsFileContents.contains("frida");
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

 

 

#4. 디버깅 툴 탐지

 /**
  * 디버깅 툴 탐지
  */
private static boolean checkDebugger() {
    int pid = android.os.Process.myPid();
    String status = "proc/" + pid + "/status";
    File file = new File(status);

    try {
        FileReader fr = new FileReader(file);
        BufferedReader br = new BufferedReader(fr);

        String line = br.readLine();
        while (line != null) {
            if (line.contains("TracerPid")) {
                String[] split = line.split("\t");
                int tracerPid = Integer.parseInt(split[1]);
                if (tracerPid != 0) { // '디버깅 툴' 존재.
                    return true;
                }
            }
            line = br.readLine();
        }
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    return false;
}

 

 

#5. 타 스토어에서 앱 설치 확인

구글 스토어에만 올렸는데, 내 앱을 다른 누군가 타 스토어에 올려 악용하는 사례가 있으니 확인한다.

public static List<String> registeredStores = new ArrayList<String>() {
    {
        add("com.android.vending"); // 구글스토어   
    }
};


String installer = getPackageManager().getInstallerPackageName(getPackageName());

if (!registeredStores.contains(installer)) {
    showAlertMessage(getString(R.string.dialog_forgery));
    return;
}

 

추가로 다양한 스토어 패키지 명을 정리하자면 아래와 같다.

각종 스토어 패키지명

1. Google Play Store (구글 플레이 스토어)

: com.android.vending

 

2. ONE Store (원스토어)

: SKT / com.skt.skaf.A000Z00040

: KT / com.kt.olleh.storefront

: LG U+ / android.lgt.appstore 또는 comlguplus.appstore

 

3. Galaxy Apps (갤럭시 앱스)

: com.sec.android.app.samsungapps

 

4. Samsung Smart Switch (삼성 스마트 스위치)

: com.sec.android.easyMover.Agent

 

5. Android Package Installer

: com.google.android.packageinstaller

 

6. Samsung Mate Agent

: com.samsung.android.mateagent