drizzleDumper的原理分析和使用说明
2016-12-10 23:34
399 查看
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53561622
在前面的博客中已经介绍了Android的脱壳工具DexExtractor的原理和使用说明,接下来就来分析一下另一个Android的脱壳工具drizzleDumper的原理和使用说明。drizzleDumper脱壳工具的作者是Drizzle.Risk,他是在strazzere大神的android-unpacker脱壳工具的基础上修改过来的drizzleDumper,他在完成drizzleDumper脱壳工具的时候,对某数字加固、ijiami、bangbang加固进行了脱壳测试,效果比较理想。drizzleDumper脱壳工具是一款基于内存特征搜索的dex文件dump脱壳工具。
一、drizzleDumper脱壳工具的相关链接和讨论:
github地址:https://github.com/DrizzleRisk/drizzleDumper#drizzledumper
freebuf地址:http://www.freebuf.com/sectool/105147.html
看雪地址:http://bbs.pediy.com/showthread.php?goto=nextoldest&nojs=1&t=213174
android-unpacker地址:https://github.com/strazzere/android-unpacker/tree/master/native-unpacker
二、drizzleDumper脱壳工具的原理分析(见代码的注释):
drizzleDumper工作的原理是root环境下,通过ptrace附加需要脱壳的apk进程,然后在脱壳的apk进程的内存中进行dex文件的特征搜索,当搜索到dex文件时,进行dex文件的内存dump。
drizzleDumper.h头文件
drizzleDumper.c实现文件
drizzleDumper的编译配置文件Android.mk
三、drizzleDumper的使用说明
关于drizzleDumper的使用,作者已经在freebuf的文章中已经讲的很详细了,具体的修改的地方也指出来了。
![](https://img-blog.csdn.net/20161210225851217?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUVExMDg0MjgzMTcy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
四、下面就使用nexcus
5的已经root的真机进行drizzleDumper的脱壳实战(以com.qihoo.freewifi为例):
在cmd控制台的条件下,执行cd命令进入到存放drizzleDumper的文件夹,然后将drizzleDumper文件推送到android手机的/data/local/tmp文件夹下并赋予可执行权限,然后根据每种android加固的特点,选择需要脱壳的apk和drizzleDumper运行的先后顺序,调整能够脱壳成功的过程。这里使用的com.qihoo.freewifi为例,先运行com.qihoo.freewifi程序,然后adb
shell条件下su提权,执行drizzleDumper的脱壳操作,等待2秒。
![](https://img-blog.csdn.net/20161210232555101?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUVExMDg0MjgzMTcy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
说明:对脱壳是否成功,这个估计有一定的概率性,主要的目的是学习工具作者的脱壳思想和方法,自己去实践,不管怎样谢谢工具的作者Drizzle.Risk,代码中有理解错误的地方希望大牛不吝赐。
编译好的drizzleDumper文件和代码的打包下载地址:http://download.csdn.net/detail/qq1084283172/9707768。
参考网址:
http://www.freebuf.com/sectool/105147.html https://github.com/DrizzleRisk/drizzleDumper
在前面的博客中已经介绍了Android的脱壳工具DexExtractor的原理和使用说明,接下来就来分析一下另一个Android的脱壳工具drizzleDumper的原理和使用说明。drizzleDumper脱壳工具的作者是Drizzle.Risk,他是在strazzere大神的android-unpacker脱壳工具的基础上修改过来的drizzleDumper,他在完成drizzleDumper脱壳工具的时候,对某数字加固、ijiami、bangbang加固进行了脱壳测试,效果比较理想。drizzleDumper脱壳工具是一款基于内存特征搜索的dex文件dump脱壳工具。
一、drizzleDumper脱壳工具的相关链接和讨论:
github地址:https://github.com/DrizzleRisk/drizzleDumper#drizzledumper
freebuf地址:http://www.freebuf.com/sectool/105147.html
看雪地址:http://bbs.pediy.com/showthread.php?goto=nextoldest&nojs=1&t=213174
android-unpacker地址:https://github.com/strazzere/android-unpacker/tree/master/native-unpacker
二、drizzleDumper脱壳工具的原理分析(见代码的注释):
drizzleDumper工作的原理是root环境下,通过ptrace附加需要脱壳的apk进程,然后在脱壳的apk进程的内存中进行dex文件的特征搜索,当搜索到dex文件时,进行dex文件的内存dump。
drizzleDumper.h头文件
/* * drizzleDumper Code By Drizzle.Risk * file: drizzleDumper.h */ #include <stdlib.h> #include <stdio.h> #include <dirent.h> #include <fcntl.h> #include <unistd.h> #include <stdarg.h> #include <string.h> #include <errno.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <linux/user.h> #ifdef HAVE_STDINT_H #include <stdint.h> /* C99 */ typedef uint8_t u1; typedef uint16_t u2; typedef uint32_t u4; typedef uint64_t u8; typedef int8_t s1; typedef int16_t s2; typedef int32_t s4; typedef int64_t s8; #else typedef unsigned char u1; typedef unsigned short u2; typedef unsigned int u4; typedef unsigned long long u8; typedef signed char s1; typedef signed short s2; typedef signed int s4; typedef signed long long s8; #endif /* * define kSHA1DigestLen */ enum { kSHA1DigestLen = 20, kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 }; /* * define DexHeader */ typedef struct DexHeader { u1 magic[8]; /* includes version number */ u4 checksum; /* adler32 checksum */ u1 signature[kSHA1DigestLen]; /* SHA-1 hash */ u4 fileSize; /* length of entire file */ u4 headerSize; /* offset to start of next section */ u4 endianTag; u4 linkSize; u4 linkOff; u4 mapOff; u4 stringIdsSize; u4 stringIdsOff; u4 typeIdsSize; u4 typeIdsOff; u4 protoIdsSize; u4 protoIdsOff; u4 fieldIdsSize; u4 fieldIdsOff; u4 methodIdsSize; u4 methodIdsOff; u4 classDefsSize; u4 classDefsOff; u4 dataSize; u4 dataOff; } DexHeader; //#define ORIG_EAX 11 static const char* static_safe_location = "/data/local/tmp/"; static const char* suffix = "_dumped_"; typedef struct { uint32_t start; uint32_t end; } memory_region; uint32_t get_clone_pid(uint32_t service_pid); uint32_t get_process_pid(const char* target_package_name); char *determine_filter(uint32_t clone_pid, int memory_fd); int find_magic_memory(uint32_t clone_pid, int memory_fd, memory_region *memory ,const char* file_name); int peek_memory(int memory_file, uint32_t address); int dump_memory(const char *buffer , int len , char each_filename[]); int attach_get_memory(uint32_t pid);
drizzleDumper.c实现文件
/* * drizzleDumper Code By Drizzle.Risk * file: drizzleDumper.c */ #include "drizzleDumper.h" // 主函数main int main(int argc, char *argv[]) { printf("[>>>] This is drizzleDumper [<<<]\n"); printf("[>>>] code by Drizzle [<<<]\n"); printf("[>>>] 2016.05 [<<<]\n"); // 脱壳工具drizzleDumper在工作的实收需要3个参数(需要脱壳的apk的package_name、脱壳等待的时间wait_times(s)) if(argc <= 1) { printf("[*] Useage : ./drizzleDumper package_name wait_times(s)\n[*] The wait_times(s) means how long between the two Scans, default 0s \n[*] if successed, you can find the dex file in /data/local/tmp\n[*] Good Luck!\n"); return 0; } // 由于脱壳的原理是基于进程的ptrace,需要有root权限 if(getuid() != 0) { printf("[*] Device Not root!\n"); return -1; } double wait_times = 0.01; // 脱壳工具drizzleDumper在工作的实收需要3个参数(需要脱壳的apk的package_name、脱壳等待的时间wait_times(s)) if(argc >= 3) { // 获取加固脱壳的等待时间 wait_times = strtod(argv[2], NULL); printf("[*] The wait_times is %ss\n", argv[2]); } // 获取需要被脱壳的加固apk的包名 char *package_name = argv[1]; printf("[*] Try to Find %s\n", package_name); uint32_t pid = -1; int i = 0; int mem_file; uint32_t clone_pid; char *extra_filter; char *dumped_file_name; // 进入循环 while(1) { // 休眠等待一段时间 sleep(wait_times); pid = -1; // 获取加固需要被脱壳的apk的进程pid pid = get_process_pid(package_name); // 判断获取的进程pid是否有效 if(pid < 1 || pid == -1) { continue; } printf("[*] pid is %d\n", pid); // 获取进程pid的一个线程tid,方便后面进行ptrace附加 clone_pid = get_clone_pid(pid); if(clone_pid <= 0) { continue; } printf("[*] clone pid is %d\n", clone_pid); memory_region memory; printf("[*] ptrace [clone_pid] %d\n", clone_pid); // 对指定pid进程的克隆即tid进程ptrace附加,获取指定pid进程的内存模块基址 mem_file = attach_get_memory(clone_pid); // 对获取到的内存有效数据的进行校验3次即最多进行3次脱壳尝试 if(mem_file == -10201) { continue; } else if(mem_file == -20402) { //continue; } else if(mem_file == -30903) { //continue } /**** *static const char* static_safe_location = "/data/local/tmp/"; *static const char* suffix = "_dumped_"; ****/ // 申请内存空间保存内存dump出来的dex文件的名称 dumped_file_name = malloc(strlen(static_safe_location) + strlen(package_name) + strlen(suffix)); // 格式化生成存dump出来的dex文件的名称 sprintf(dumped_file_name, "%s%s%s", static_safe_location, package_name, suffix); printf("[*] Scanning dex ...\n"); // 通过ptrace附件目标pid进程,在目标进程的pid中进行dex文件的搜索然后进行内存dump if(find_magic_memory(clone_pid, mem_file, &memory, dumped_file_name) <= 0) { printf("[*] The magic was Not Found!\n"); ptrace(PTRACE_DETACH, clone_pid, NULL, 0); close(mem_file); continue; } else { // dex的内存dump成功,跳出循环 close(mem_file); ptrace(PTRACE_DETACH, clone_pid, NULL, 0); break; } } printf("[*] Done.\n\n"); return 1; } // 获取指定进程的一个线程tid uint32_t get_clone_pid(uint32_t service_pid) { DIR *service_pid_dir; char service_pid_directory[1024]; // 格式化字符串 sprintf(service_pid_directory, "/proc/%d/task/", service_pid); // 查询指定进程的pid的线程TID的信息 if((service_pid_dir = opendir(service_pid_directory)) == NULL) { return -1; } struct dirent* directory_entry = NULL; struct dirent* last_entry = NULL; // 获取指定pid进程的线程TID while((directory_entry = readdir(service_pid_dir)) != NULL) { last_entry = directory_entry; } if(last_entry == NULL) return -1; closedir(service_pid_dir); // 返回获取到的指定pid的线程tid return atoi(last_entry->d_name); } // 通过运行的apk的名称的获取进程的pid uint32_t get_process_pid(const char *target_package_name) { char self_pid[10]; sprintf(self_pid, "%u", getpid()); DIR *proc = NULL; if((proc = opendir("/proc")) == NULL) return -1; struct dirent *directory_entry = NULL; while((directory_entry = readdir(proc)) != NULL) { if (directory_entry == NULL) return -1; if (strcmp(directory_entry->d_name, "self") == 0 || strcmp(directory_entry->d_name, self_pid) == 0) continue; char cmdline[1024]; snprintf(cmdline, sizeof(cmdline), "/proc/%s/cmdline", directory_entry->d_name); FILE *cmdline_file = NULL; if((cmdline_file = fopen(cmdline, "r")) == NULL) continue; char process_name[1024]; fscanf(cmdline_file, "%s", process_name); fclose(cmdline_file); if(strcmp(process_name, target_package_name) == 0) { closedir(proc); return atoi(directory_entry->d_name); } } closedir(proc); return -1; } // 在目标进程的内存空间中进行dex文件的搜索 int find_magic_memory(uint32_t clone_pid, int memory_fd, memory_region *memory , const char *file_name) { int ret = 0; char maps[2048]; // 格式化字符串得到/proc/pid/maps snprintf(maps, sizeof(maps), "/proc/%d/maps", clone_pid); FILE *maps_file = NULL; // 打开文件/proc/pid/maps,获取指定pid进程的内存分布信息 if((maps_file = fopen(maps, "r")) == NULL) { printf(" [+] fopen %s Error \n" , maps); return -1; } char mem_line[1024]; // 循环读取文件/proc/pid/maps中的pid进程的每一条内存分布信息 while(fscanf(maps_file, "%[^\n]\n", mem_line) >= 0) { char mem_address_start[10]={0}; char mem_address_end[10]={0}; char mem_info[1024]={0}; // 解析pid进程的的内存分布信息--内存分布起始地址、内存分布结束地址等 sscanf(mem_line, "%8[^-]-%8[^ ]%*s%*s%*s%*s%s", mem_address_start, mem_address_end, mem_info); memset(mem_line , 0 ,1024); // 获取内存分布起始地址的大小 uint32_t mem_start = strtoul(mem_address_start, NULL, 16); memory->start = mem_start; // 获取内存分布结束地址的大小 memory->end = strtoul(mem_address_end, NULL, 16); // 获取实际的内存区间大小 int len = memory->end - memory->start; // 过滤掉不符合条件的内存分布区间 if(len <= 10000) {//too small continue; } else if(len >= 150000000) {//too big continue; } char each_filename[254] = {0}; char randstr[10] = {0}; sprintf(randstr ,"%d", rand()%9999); // 拼接字符串得到dump的dex文件的生成名称 strncpy(each_filename , file_name , 200); //防溢出 strncat(each_filename , randstr , 10); strncat(each_filename , ".dex" , 4); // 先将pid进程内存文件句柄的指针置文件开头 lseek64(memory_fd , 0 , SEEK_SET); // 设置pid进程内存文件句柄的指针为内存分布起始地址 off_t r1 = lseek64(memory_fd , memory->start , SEEK_SET); if(r1 == -1) { //do nothing } else { // 根据内存分布区间的大小申请内存空间 char *buffer = malloc(len); // 读取pid进程的指定区域的内存数据 ssize_t readlen = read(memory_fd, buffer, len); printf("meminfo: %s ,len: %d ,readlen: %d, start: %x\n", mem_info, len, readlen, memory->start); // 对读取的内存分布区域的数据进行dex文件的扫描和查找 if(buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') { free(buffer); continue; } // 查找到dex文件所在的内存区域 if(buffer[0] == 'd' && buffer[1] == 'e' && buffer[2] == 'x' && buffer[3] == '\n' && buffer[4] == '0' && buffer[5] == '3') { printf(" [+] find dex, len : %d , info : %s\n" , readlen , mem_info); DexHeader header; char real_lenstr[10]={0}; // 获取内存区域中dex文件的文件头信息 memcpy(&header , buffer ,sizeof(DexHeader)); sprintf(real_lenstr , "%x" , header.fileSize); // 通过dex文件头信息,获取到整个dex文件的大小 long real_lennum = strtol(real_lenstr , NULL, 16); printf(" [+] This dex's fileSize: %d\n", real_lennum); // 对dex文件所在的内存区域进行内存dump if(dump_memory(buffer , len , each_filename) == 1) { // 打印dump的dex文件的名称 printf(" [+] dex dump into %s\n", each_filename); free(buffer); continue; } else { printf(" [+] dex dump error \n"); } } free(buffer); } // 前面的内存方法搜索没有查找dex文件的内存,尝试下面的内存+8位置进行搜索 // 具体什么原因没太明白?? lseek64(memory_fd , 0 , SEEK_SET); //保险,先归零 r1 = lseek64(memory_fd , memory->start + 8 , SEEK_SET); //不用 pread,因为pread用的是lseek if(r1 == -1) { continue; } else { char *buffer = malloc(len); ssize_t readlen = read(memory_fd, buffer, len); if(buffer[0] == 'd' && buffer[1] == 'e' && buffer[2] == 'x' && buffer[3] == '\n' && buffer[4] == '0' && buffer[5] == '3') { printf(" [+] Find dex! memory len : %d \n" , readlen); DexHeader header; char real_lenstr[10]={0}; // 获取内存dex文件的文件头信息 memcpy(&header , buffer ,sizeof(DexHeader)); sprintf(real_lenstr , "%x" , header.fileSize); // 通过dex文件头信息,获取到整个dex文件的大小 long real_lennum = strtol(real_lenstr , NULL, 16); printf(" [+] This dex's fileSize: %d\n", real_lennum); // 对dex文件所在的内存区域进行内存dump if(dump_memory(buffer , len , each_filename) == 1) { printf(" [+] dex dump into %s\n", each_filename); free(buffer); continue; //如果本次成功了,就不尝试其他方法了 } else { printf(" [+] dex dump error \n"); } } free(buffer); } } fclose(maps_file); return ret; } // 从内存中dump数据到文件中 int dump_memory(const char *buffer , int len , char each_filename[]) { int ret = -1; // 创建文件 FILE *dump = fopen(each_filename, "wb"); // 将需要dump的内存数据写入到/data/local/tmp文件路径下 if(fwrite(buffer, len, 1, dump) != 1) { ret = -1; } else { ret = 1; } fclose(dump); return ret; } // 获取指定附加pid进程的内存模块基址 int attach_get_memory(uint32_t pid) { char mem[1024]; bzero(mem,1024); // 格式化字符串得到字符串/proc/pid/mem snprintf(mem, sizeof(mem), "/proc/%d/mem", pid); int ret = -1; int mem_file; // 尝试ptrace附加目标pid进程 ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL); // 对ptrace附加目标pid进程的操作结果进行判断 if (0 != ret) { int err = errno; //这时获取errno if(err == 1) //EPERM { return -30903; //代表已经被跟踪或无法跟踪 } else { return -10201; //其他错误(进程不存在或非法操作) } } else { // ptrace附加目标进程pid成功,获取指定pid进程的内存模块基址 // 获取其它进程的内存模块基址,需要root权限 if(!(mem_file = open(mem, O_RDONLY))) { return -20402; //打开错误 } } return mem_file; }
drizzleDumper的编译配置文件Android.mk
LOCAL_PATH := $(call my-dir) TARGET_PIE := true NDK_APP_PIE := true include $(CLEAR_VARS) # 需要编译的源码文件 LOCAL_SRC_FILES := \ drizzleDumper.c LOCAL_C_INCLUDE := \ drizzleDumper.h \ definitions.h LOCAL_MODULE := drizzleDumper LOCAL_MODULE_TAGS := optional # Allow execution on android-16+ # 支持PIE LOCAL_CFLAGS += -fPIE LOCAL_LDFLAGS += -fPIE -pie # 编译生成可执行ELF文件 include $(BUILD_EXECUTABLE) include $(call all-makefiles-under,$(LOCAL_PATH))
三、drizzleDumper的使用说明
关于drizzleDumper的使用,作者已经在freebuf的文章中已经讲的很详细了,具体的修改的地方也指出来了。
四、下面就使用nexcus
5的已经root的真机进行drizzleDumper的脱壳实战(以com.qihoo.freewifi为例):
在cmd控制台的条件下,执行cd命令进入到存放drizzleDumper的文件夹,然后将drizzleDumper文件推送到android手机的/data/local/tmp文件夹下并赋予可执行权限,然后根据每种android加固的特点,选择需要脱壳的apk和drizzleDumper运行的先后顺序,调整能够脱壳成功的过程。这里使用的com.qihoo.freewifi为例,先运行com.qihoo.freewifi程序,然后adb
shell条件下su提权,执行drizzleDumper的脱壳操作,等待2秒。
cd xxxxx/drizzleDumper adb push drizzleDumper /data/local/tmp adb shell chmod 0777 /data/local/tmp/drizzleDumper adb shell #进入androd系统的shell su #获取root权限 ./data/local/tmp/drizzleDumper com.qihoo.freewifi 2 #执行脱壳操作
说明:对脱壳是否成功,这个估计有一定的概率性,主要的目的是学习工具作者的脱壳思想和方法,自己去实践,不管怎样谢谢工具的作者Drizzle.Risk,代码中有理解错误的地方希望大牛不吝赐。
编译好的drizzleDumper文件和代码的打包下载地址:http://download.csdn.net/detail/qq1084283172/9707768。
参考网址:
http://www.freebuf.com/sectool/105147.html https://github.com/DrizzleRisk/drizzleDumper
相关文章推荐
- drizzleDumper的原理分析和使用说明
- DexExtractor的原理分析和使用说明
- Nmap使用说明及原理分析
- DexHunter的原理分析和使用说明(一)
- DexHunter的原理分析和使用说明(二)
- 免费动态域名解析软件nat123技术性原理分析及使用方法说明
- 静态分析工具PMD使用说明 (文章来源: Java Eye)
- OAuth认证协议原理分析及使用方法
- RBAC原理介绍及kasai使用分析
- 编译原理之LL(1)文法分析(使用分析表)
- mysql 数据库中索引原理分析说明
- Oauth认证协议原理分析及使用方法
- 在 JSP/Servlet 中使用 Bean 自动属性填充机制(含原理分析)
- JNI技术实践小结--原理分析和详细步骤截图说明
- 【华磊随笔】善于使用工具(1)-使用IE8开发人员工具和FireBug分析Jquery FlexiGrid的实现原理
- ARP欺骗攻击原理深入说明分析
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- 使用JAVA发送HTTP请求(Http Request),返回HTTP响应(Http Response)内容,代码程序例子及原理说明
- 步进电机原理及使用说明