Why should I know this?

Android 보안솔루션의 기법 깨기 본문

Knowledge/Android

Android 보안솔루션의 기법 깨기

die4taoam 2019. 2. 22. 19:40



보안 솔루션은 절대로 자신들의 역량으로 검증 불가능한 영역의 코드를 호출해서는 안된다.
검증 가능한 영역으로 로직을 가져오려는 노력을 게을리하지 않았으면 좋겠다.



1. Shared Object 암호화 은닉 후 Runtime에 복호화하여 로드하기

Android 보안솔루션 중에는 shared object를 로드하는 경로를 변경하는 경우가 있다.

System.loadLibrary("library_name");

Android에서 라이브러리를 로드하는 방법은 위처럼 System.loadLibrary를 사용하거나 뭐 딴 방법을 쓰거나 여러가지 있다. 특정 보안솔루션들은 shared object가 공격자에게 노출되면 위험하다는 생각을 하는 것 같다.


그래서  이들은(N사) shared object를 실행 전에 임의의 경로에 복호화해놓고 로드하도록 만들었다. 나는 이게 도무지 이해가 안된다. 왜냐면 보안솔루션의 특성상 원본의 실행에 영향을 미쳐서는 안되기 때문에 Java Interface에 맞춰 결국 dvmLoadNativeCode 함수를 호출하게 된다.



보안솔루션이 자신들이 검증할 수 없는 영역의 코드를 호출한다는 것은 곧 뭐다?


318bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
319        char** detail)
320{
321    SharedLib* pEntry;
322    void* handle;
323    bool verbose;
324
325    /* reduce noise by not chattering about system libraries */
326    verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
327    verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
328
329    if (verbose)
330        ALOGD("Trying to load lib %s %p", pathName, classLoader);
331
332    *detail = NULL;
333

/app 이하의 경로가 아닌 경우 무조건 shared object를 백업하도록 코드를 변경하면 so를 확보할 수 있다.

- AOSP를 새로 빌드

- dvmLoadNativeCode를 후킹


같은 방법도 있겠지만,

사실 Android에서 실행가능한 이미지를 로드할 수 있는 경로는 딱 한 곳 뿐이기 때문에 해당 폴더에 새 파일이 생기면 백업하도록 무한루프 프로그램을 작성해도 된다. 이는 대다수 DEX를 복호화 파여 파일로 떨구는 모든 보안 솔루션에게도 DEX를 확보하기 위해 사용 가능한 방법이다.


2. DEX 암호화 후 Runtime에 복호화 로드

말이 나온 김에 DEX를 암호화 하고 Runtime에 복호화 하여 로드하는 경우 DEX를 확보하는 방법도 살펴보자. 위의 경우와 마찬가지로 DEX를 로드하는 메소드는 정해져 있고, 실질적으로 DEX를 pointing하는 cookie는 Native에서 openDexFile로 생성된다.


151static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
152    JValue* pResult)
153{
154    StringObject* sourceNameObj = (StringObject*) args[0];
155    StringObject* outputNameObj = (StringObject*) args[1];
156    DexOrJar* pDexOrJar = NULL;
157    JarFile* pJarFile;
158    RawDexFile* pRawDexFile;
159    char* sourceName;
160    char* outputName;

그러므로 이곳에서 arg로 넘어오는 DexFile을 백업하도록 AOSP를 빌드하면 확실히 DEX를 확보할 수 있다.




3. 원본 DEX에 Class를 변경하여 실행간 무결성 확보하는 방식

Bytecode는 디컴파일이 매우 쉽다는 특성이 있다. 이 때문에 일부 보안 솔루션에서는 Bytecode를 디컴파일 한 후 자신들이 작성한 Class등을 주입하고 원본 프로그램이 extends하는 Activity등을 자신들의 Class(ex : SecureActivity)로 변경하는 경우가 있다. 이런 방식을 취하는 경우 원본 APK에 Shared Object를 추가해놓고 해당 Shared-Object와 통신을 통해 실행 무결성을 확보하는 작업을 하기 마련이다.


이 역시도 간단히 우회 가능한다.

방법은 DEX에서 각 클래스는 extends하기 위한 클래스를 저장하는 필드가 있는데 이 필드를 바꾸면 된다.




4. 디컴파일 방지 기법

모 솔루션의 경우 Apktool을 이용한 디컴파일을 방지하기 위해 depth가 깊은 상속 클래스를 주입한다. 이유는, apktool이 디컴파일하면서 class의 상속을 최대 255 deps까지만 취하기 때문이며 그 이상 deps가 있을 경우 에러를 발생시키게 된다. 단순히 이런 경우에는 해당 class를 dex에서 제거하는 등의 방식으로 에러를 발생시키지 않도록 만들면 된다. class shark와 같은 프로그램으로 열어보면 이런 기법을 취했는지 쉬이 확인 할 수 있다.


윈도우에서 프로그램을 리버싱을 하기 위해 PE 구조 정도는 꿰차고 있어야 하는게 당연하듯, 안드로이드 앱을 리버싱하고자 한다면 DEX의 구조 정도는 암기하고 분석/조작 가능한 프로그램 정도는 만들 수 있어야 한다.






Comments