安卓逆向笔记-得到360加固的dex文件

借鉴于大佬的教程
https://www.52pojie.cn/thread-685918-1-1.html
https://blog.csdn.net/qing666888/article/details/71307295

安卓逆向笔记-得到360加固dex文件

这个方法很老了,所以部分apk可能没有用
工具
drizzleDumper ,雷电模拟器,androidkiller
附上drizzleDumper源码及工具
自己相尝试编译drizzleDumper的时候要在liunx下编译
链接:https://github.com/iaa-mr/android_unprotect (脱壳.zip就是)

drizzleDumper源码

drizzleDumper编译环境

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 Code By Drizzle.Risk 
 * file: drizzleDumper.h 
 */  
  
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
  
#ifdef HAVE_STDINT_H  
#include     /* 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使用教程

首先将脱壳.zip解压会得到两个drizzleDumper,雷电模拟器用x86就行
然后打开雷电模拟器和androidkiller
Androidkiller 上方可以找到模拟器或者已连接的手机

安卓逆向笔记-得到360加固的dex文件_第1张图片
然后把drizzleDumper移到这个/data/local/tmp文件夹
打开雷电模拟器,安装需要解加固的软件
然后打开系统工具中的终端模拟器
输入 su
cd /data/local/tmp
./drizzleDumper 你需要dumper的包名
输入后回到主界面运行需要dumper的软件
安卓逆向笔记-得到360加固的dex文件_第2张图片
然后就把dex文件解出来了

drizzleDumper后apk修复

此时需要注意oncreat有没有被抽空,如果被抽空就需要手动修复了。
如果没有的话那就是最好的,现在只需要将dex文件反编译成smali替换反编译后的apk里面
然后修改androidmanifest.xml里面

只适用于简单加固的程序,现在的壳基本没用了。。。。。。。。。。

你可能感兴趣的:(安卓逆向笔记)