Why should I know this?

Android 파헤치기 - Dex 메모리 로드 본문

Knowledge/Android

Android 파헤치기 - Dex 메모리 로드

die4taoam 2019. 2. 18. 23:08

0. 개요

이 글의 내용은 DEX를 메모리에서 로드하는 기법을 다루고 있습니다. 제가 2018년 고안하였으며 이 글은 2019년에 글을 작성되었습니다. 그간 비공개로 해놨으며, github를 통해 다음 프로젝트처럼 동일 기법이 오픈소스로 공개되어 있는것을 확인되어 공개로 전환합니다.

https://github.com/yongyecc/dexshellerInMemory

 

GitHub - yongyecc/dexshellerInMemory: android APK一键DEX加固脚本(内存加载DEX)

android APK一键DEX加固脚本(内存加载DEX). Contribute to yongyecc/dexshellerInMemory development by creating an account on GitHub.

github.com

 

1. DEX란?

Android에서 사용하는 DEX라는 파일 포맷은 Java로 짜여진 코드가 컴파일되어 번역된 Bytecode들과 해당 Bytecode들의 구조에 대한 설계를 함께 포함하고 있다. DEX와 함께 Android에서 사용되는 각종 리소스들은 APK라는 확장자를 가진 파일로 Export되며 개발자들은 이를 Android Market에 등록하여 App을 출시하게 된다.

 

 

1-1. 동기

문제는 DEX가 말 그대로 설계도이기 때문에 관련 지식이 가진 사람들은 이를 풀어 소스코드로 복원하는게 크게 어렵지 않은 작업이다.

 

 

 

 

때문에 DEX를 숨겨놓고 싶다는 요구가 생기게 되는 것은 자연스러운 일이다.

 

 // manipulate point of dex at cookie!!  
 void* pDexInMemory = getDexFromAsset(env, assetManager);
 /********************************************************
 * Proof of concept - check possibility of executing dex original instructions.
 *
 **************************************************************/ 
 // allocate new DexFile pointer 
 DexFile* pCopiedDexFile = (DexFile*) malloc(sizeof(DexFile)); 
 memcpy(pCopiedDexFile, orig_pDexFile, sizeof(DexFile)); 
 // Original DexFile Header have located at 0 position.  
 DexHeader *pCopiedDexFileHeader = (DexHeader *) pDexInMemory; 
 pCopiedDexFile->pHeader = pCopiedDexFileHeader; 
 // ODEX Header  pCopiedDexFile->pOptHeader = NULL; 
 /* 
 * dalvik/vm/analysis/RegisterMap.cpp:841
 * const void
 * dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx, u4* pNumMaps) 
 * 
 * when dalvikvm optimize dex to make optimized dex(ODEX), then it will create RegisterMapPool 
 * to save Class, Method offset. after that, when dalvikvm need to find class or method then 
 * it will reference here first, to save time. 
 * this mechanism is very similar like how to use pResClases by dalvikvm. 
 * 
 * if this job does not made correctly. 
 * you will meet below error message: 
 * E/dalvikvm( 5172): bad class index (1151 vs 1) 
 * E/dalvikvm( 5172): VM aborting 
 * F/libc ( 5172): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 5172 (ssloader_sample) 
 **/ 
 pCopiedDexFile->pRegisterMapPool = NULL; 
 // string  pCopiedDexFile->pStringIds = (DexStringId *)((uintptr_t)pDexInMemory + pCopiedDexFileHeader->stringIdsOff); 
 // type  pCopiedDexFile->pTypeIds = (DexTypeId *)((uintptr_t)pDexInMemory + pCopiedDexFileHeader->typeIdsOff); 
 // field  pCopiedDexFile->pFieldIds = (DexFieldId *)((uintptr_t)pDexInMemory + pCopiedDexFileHeader->fieldIdsOff); 
 // method  pCopiedDexFile->pMethodIds = (DexMethodId *)((uintptr_t)pDexInMemory + pCopiedDexFileHeader->methodIdsOff); 
 // proto  pCopiedDexFile->pProtoIds = (DexProtoId *)((uintptr_t)pDexInMemory + pCopiedDexFileHeader->protoIdsOff); 
 // classdef  pCopiedDexFile->pClassDefs = (DexClassDef *)((uintptr_t)pDexInMemory + pCopiedDexFileHeader->classDefsOff); 
 // move base to our dex  pCopiedDexFile->baseAddr = (u1 *)pDexInMemory;

 

 

그래서 DEX를 xor하여 간단한 암호화를 해서 asset에 보관한 뒤에 App이 구동되는 Runtime에 해당 DEX를 메모리에 풀어놓고 cookie를 변경하여 동적으로 DEX를 풀어놓는 방식으로 DEX를 보호할 수 있는 코드를 짜봤다.

 

 

이 과정은 실제 Android에서 Dex를 적재하는 과정을 고스란히 가져다가 복붙해놓은 것이므로, 관련 소스코드를 분석하면 쉽게 이해할 수 있을 것이다.

 

 /******************************************************************************************** 
 * Proof of concept - check possibility of modifying Dex signature. * 
 ******************************************************************************************/ 
 const char* new_signature = "entwork"; 
 char *dex_magic = (char*)((DexHeader *)pCopiedDexFile->pHeader)->magic; strncpy(dex_magic, new_signature, 8);

또한 DEX의 signature 'dex'를 검색하는 방식으로 찾을 수 없게 signature를 변경하는 것도 잊지 말기를...

 

 

 

 

 

 

 

 

 

 

Comments