内容纲要

drizzleDumper

源码分析

main函数

首先检查设备是否root

 if(getuid() != 0) 
  {
    printf("[*]  Device Not root!\n");
    return -1;
  }

获取指定进程的pid

pid = get_process_pid(package_name);

    //find process
      if(pid < 1 || pid == -1)
      {
          continue;
      }
      printf("[*]  pid is %d\n", pid);

get_process_pid

通过遍历proc目录下的目录(目录名为进程的pid),得到每个目录下的cmdline(运行时的命令),来确认当前目录为指定包名的进程,从而得到pid

uint32_t get_process_pid(const char *target_package_name)
{
//获取自己的pid
  char self_pid[10];
  sprintf(self_pid, "%u", getpid());

  DIR *proc = NULL;
//得到proc目录
  if((proc = opendir("/proc")) == NULL)
    return -1;

  struct dirent *directory_entry = NULL;
  while((directory_entry = readdir(proc)) != NULL)
  {

    if (directory_entry == NULL)
      return -1;
    //如果当前目录名(pid)为self或自己的pid则继续遍历
    if (strcmp(directory_entry->d_name, "self") == 0 || strcmp(directory_entry->d_name, self_pid) == 0)
        continue;

      char cmdline[1024];
      //  /proc/[pid]/cmdline: 获得该进程启动时的命令
      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);
        //如果为指定包名,则确认该pid为该包名的pid
      if(strcmp(process_name, target_package_name) == 0)
      {
           closedir(proc);
         return atoi(directory_entry->d_name);
      }
    }

    closedir(proc);
    return -1;
}

得到一个cloned的进程id

//find cloned process
      clone_pid = get_clone_pid(pid);
      if(clone_pid <= 0) 
      {
        continue;
      }
      printf("[*]  clone pid is %d\n", clone_pid);

get_clone_pid

遍历/proc/[pid]/task(目录内为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);

  if((service_pid_dir = opendir(service_pid_directory)) == NULL)
  {
    return -1;
  }

  struct dirent* directory_entry = NULL;
  struct dirent* last_entry = NULL;
//得到/proc/[pid]/task目录下的最后一个文件(名字即为tid)
  while((directory_entry = readdir(service_pid_dir)) != NULL)
  {
    last_entry = directory_entry;
  }

  if(last_entry == NULL)
    return -1;

  closedir(service_pid_dir);

  return atoi(last_entry->d_name);
}

ptraceAttach cloned pid

     memory_region memory;
          //ptrace cloned process
          printf("[*]  ptrace [clone_pid] %d\n", clone_pid);
          mem_file = attach_get_memory(clone_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);

  // Attach to process so we can peek/dump
  int ret = -1;
  /*****!!!!!!!!!!!*****/
  ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
  int mem_file;

  if (0 != ret)
  {
      int err = errno;  //这时获取errno
      if(err == 1) //EPERM
      {
          return -30903;    //代表已经被跟踪或无法跟踪
      }
      else
      {
          return -10201;    //其他错误(进程不存在或非法操作)
      }
  }
  else
  {
      if(!(mem_file = open(mem, O_RDONLY)))
      {
        return -20402;      //打开错误
      }
  }
  return mem_file;
}

扫描进程

    // static_safe_location = "/data/local/tmp/"
        // suffix = "_dumped_"
      dumped_file_name = malloc(strlen(static_safe_location) + strlen(package_name) + strlen(suffix));
      sprintf(dumped_file_name, "%s%s%s", static_safe_location, package_name, suffix);
      printf("[*]  Scanning dex ...\n");
      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
      {
        /*
         * Successed & exit
         */
         close(mem_file);
     ptrace(PTRACE_DETACH, clone_pid, NULL, 0);
     break;
      }

find_magic_memory

获取/proc/[pid]/maps内容(进程内存映射信息),对每一块(文件中的每行)地址进行分析,如果文件的magic number是dex03(dex的magic number),则进行内存提取

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;
  if((maps_file = fopen(maps, "r")) == NULL)
  {
    printf(" [+] fopen %s Error  \n" , maps);
    return -1;
  }

   char mem_line[1024];
   // 对maps信息一行一行读取
   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};
    // 得到内存起始地址、结束地址和内存权限信息
    sscanf(mem_line, "%8[^-]-%8[^ ]%*s%*s%*s%*s%s", mem_address_start, mem_address_end,mem_info);
    // 清空初始
    memset(mem_line , 0 ,1024);
    // 获得数字部分,以16进制形式
    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 );

      strncpy(each_filename , file_name , 200); //防溢出
      strncat(each_filename , randstr , 10);
      strncat(each_filename , ".dex" , 4);

       lseek64(memory_fd , 0 , SEEK_SET);   //保险,先归零
       off_t r1 = lseek64(memory_fd , memory->start , SEEK_SET);
       if(r1 == -1)
       {
           //do nothing
       }
       else{
            char *buffer = malloc(len);
            ssize_t readlen = read(memory_fd, buffer, len);
            printf("meminfo: %s ,len: %d ,readlen: %d, start: %x\n",mem_info, len, readlen, memory->start);
            //判断magic number
            if(buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F')
            {
                free(buffer);

                continue;
            }
            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};
              memcpy(&header , buffer ,sizeof(DexHeader));
              sprintf(real_lenstr , "%x" , header.fileSize);
              long real_lennum = strtol(real_lenstr , NULL, 16);
              printf(" [+] This dex's fileSize: %d\n", real_lennum);

              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);
       }

       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};
              memcpy(&header , buffer ,sizeof(DexHeader));
              sprintf(real_lenstr , "%x" , header.fileSize);
              long real_lennum = strtol(real_lenstr , NULL, 16);
              printf(" [+] This dex's fileSize: %d\n", real_lennum);

            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;
}

待解决问题

cloned pid是啥情况