일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- tracing
- Linux custom packer
- linux thread
- uftrace
- TLS
- on stack replacement
- 난독화
- Linux packer
- LLVM 난독화
- v8 optimizing
- on-stack replacement
- initial-exec
- OSR
- pinpoint
- linux debugging
- Injection
- so inject
- v8 tracing
- apm
- pthread
- Android
- LLVM Obfuscator
- android inject
- 안티디버깅
- tracerpid
- LLVM
- Obfuscator
- anti debugging
- thread local storage
- custom packer
- Today
- Total
Why should I know this?
Android 보안솔루션의 기법 깨기 본문
검증 가능한 영역으로 로직을 가져오려는 노력을 게을리하지 않았으면 좋겠다.보안 솔루션은 절대로 자신들의 역량으로 검증 불가능한 영역의 코드를 호출해서는 안된다.
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의 구조 정도는 암기하고 분석/조작 가능한 프로그램 정도는 만들 수 있어야 한다.
'Knowledge > Android' 카테고리의 다른 글
Dexopt, Dex2oat 실행 옵션 (0) | 2020.03.15 |
---|---|
ARMv7 에서 Cacheflush를 사용할 때 주의점 (0) | 2020.02.22 |
Android에서 모든 Activity를 후킹하는 가장 깔끔한 방법. (2) | 2019.02.18 |
Android의 시작부터 디버깅하기 JNI_OnLoad (0) | 2019.02.18 |
Android AOSP 빌드하여 기기에 올리기 (0) | 2019.02.18 |