动态加载型壳
- 即第一代壳
- 其发展时期正是从 Android 4.4 向 Android 5.0 迈进的从 Dalvik 虚拟机向 ART 虚拟机转型时期
- 这一时期的软件壳,早期版本主要针对 Dalvik 虚拟机的实现,特点是对本地 APK 中的数据加密,运行时在内存中解密
缓存脱壳法
- 动态加载型壳用 DexClassLoader 方式将加密后的 DEX 在内存中解密后动态加载,但一些软件壳没有处理 DEX 优化时缓存的路径,最终使得系统执行 dexopt 命令对加载的 DEX 进行优化时,将优化结果放到默认的
/data/dalvik-cache
目录,因此只要将此目录下的 ODEX 取出,进行一次 deodex 操作,即可完成脱壳
内存 Dump 脱壳法
-
因第一代壳在内存中完全解密,可从内存中 Dump 要解密 APK 的内存,完成脱壳
-
脱壳工具:
android-unpacker
-
其核心是通过
find_magic_memory()
读取/proc/pid/maps
内存映射表,找到 DEX 所在的内存起始位置,通过dump_memory()
将内存 Dumpint find_magic_memory(uint32_t clone_pid, int memory_fd, memory_region* memory[], char* extra_filter) { char maps[1024]; snprintf(maps, sizeof(maps), "/proc/%d/maps", clone_pid); FILE* maps_file = NULL; if ((maps_file = fopen(maps, "r")) == NULL) return -1; char mem_line[1024]; int found = 0; while (fscanf(maps_file, "%[^\n]\n", mem_line) >= 0) { if (extra_filter != NULL && strstr(mem_line, extra_filter) == NULL) continue; if (extra_filter == NULL && (strstr(mem_line, "/") != NULL || strstr(mem_line, "[") != NULL)) continue; char mem_address_start[10]; char mem_address_end[10]; sscanf(mem_line, "%8[^-]-%8[^ ]", mem_address_start, mem_address_end); uint64_t mem_start = strtoul(mem_address_start, NULL, 16); off64_t offset = peek_memory(memory_fd, mem_start); if (extra_filter != NULL && offset < 0) offset = 0; if (offset >= 0) { memory[found] = malloc(sizeof(memory_region)); memory[found]->start = mem_start + start; memory[found++]->end = strtoull(mem_address_end, NULL, 16); } else if (offset == -99) { // No offset found } else { perror(" [!] Error peeking at memory "); } } fclose(maps_file); return found; } int dump_memory(char* class_path, int memory_fd, memory_region* memory, const char* file_name, int ignore_class_path) { int ret