安卓逆向笔记-得到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 <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使用教程
首先将脱壳.zip解压会得到两个drizzleDumper,雷电模拟器用x86就行
然后打开雷电模拟器和androidkiller
Androidkiller 上方可以找到模拟器或者已连接的手机
然后把drizzleDumper移到这个/data/local/tmp文件夹
打开雷电模拟器,安装需要解加固的软件
然后打开系统工具中的终端模拟器
输入 su
cd /data/local/tmp
./drizzleDumper 你需要dumper的包名
输入后回到主界面运行需要dumper的软件
然后就把dex文件解出来了
drizzleDumper后apk修复
此时需要注意oncreat有没有被抽空,如果被抽空就需要手动修复了。
如果没有的话那就是最好的,现在只需要将dex文件反编译成smali替换反编译后的apk里面
然后修改androidmanifest.xml里面
<application … android name=" 源程序的包名"…>
只适用于简单加固的程序,现在的壳基本没用了。。。。。。。。。。
安卓逆向笔记-得到360加固的dex文件相关推荐
- 安卓逆向笔记--apk加固
安卓逆向笔记–apk加固 资料来源: 浅谈安卓apk加固原理和实现 Android中的Apk的加固(加壳)原理解析和实现 前两个太老了所以具体代码借鉴下面的 Android Apk加壳技术实战详解 一 ...
- 安卓逆向笔记(课外资料)
安卓逆向笔记(课外资料) 只有知识点 分析apk主要组成 1.AndroidMainfest.xml-----配置清单 该文件是每个应用都必须定义和包含的,它描述了用的名字.版本.权限.引用的库文件等 ...
- android 360加固 反编译,[原创]逆向360加固等dex被隐藏的APK
如果遇到apk中的lib文件夹中是这样的 基本没有dex文件可以反编译,这中的dex文件一般都是加密混淆压缩后放在so中啦. 但是软件要想运行就需要解出dex字节码然后加载到手机内存中,这样就可以在软 ...
- 《0基础学安卓逆向》第2集:初始apk文件和smali语法
1.APK文件 apk=android Application PacKage=APKapk文件是什么:是安卓app的安装文件本质:(apk文件其实就是个)zip压缩包 意味着可以用解压缩工具把apk ...
- Android中Apk加固之Dex文件的加密与解密
参考文档:https://developer.android.google.cn/studio/build/multidex.html#keep 1.由文档中可以知道 了解到 在Android5.0之 ...
- Android逆向与安全——360 dex加固与脱壳
前言 现在市面上对APP的安全合规管控越来越严格了,也就要求了APP在上架之前一定要做合规检测和加固处理.对APP就是加固的好处,可以提高APP的安全性,提高APP被逆向分析破解的门槛,同时通过加固保 ...
- 360加固保的dex脱壳方法
博客地址:http://blog.csdn.net/qq1084283172/article/details/53149214 360整体加固classes.dex后的apk程序的特点,以超信1.1. ...
- 安卓逆向007之安卓系统架构
文章目录 Java闲谈 Android系统架构 安卓开发的第一个程序 安卓的四大组件 dalvik字节码 本篇文章主要记录一些概念性的东西 Java闲谈 继续深入学习安卓逆向,建议先去学一下java的 ...
- IO操作Dex文件加密,APK加固项目实战
APK加固原理分析 1.1 APK文件结构 首先让我们先了解一下一个完整的Android应用程序都由哪些文件组成.解压一个apk包,我们可以看到一下的这些文件及文件夹: 每个文件及文件夹的作用如下表所 ...
最新文章
- 适用于AMD ROC GPU的Numba概述
- tomcat 7.0 linux下载,Tomcat 7.0.67 发布下载
- R语言构建随机森林模型错误解决:Error in y - ymean : non-numeric argument to binary operator
- c++ vector嵌套传参
- 手脱UPX(堆栈平衡原理)
- STM32 基础系列教程 49 – Jansson
- Linux视频切片m3u8,使用ffmpeg+nginx使用视频切片播放
- pip install安装php,详述Python、pip、easy_install的安装教程
- linux结束所有任务命令行,Linux基础命令(15)定时任务
- python语言程序设计实践教程实验八答案_清华大学出版社-图书详情-《Java程序设计教程及实验指导》...
- SpringBoot 集成 layering-cache 实现两级缓存调研与实践
- 国内首部《数字货币词典》在新莫干山会议上启动,巴比特智库担任主编单位...
- php微信转发无法显示标题图片,解决微信公众号分享朋友圈不显示标题图片描述的方法...
- 【5G模组】讨论RSSI,RSRP,RSRQ and SINR
- Linux磁盘管理和文件系统
- 手把手教你拥有自己的代码生成器-------->坑居多
- 虚拟机的虚拟化如何开启?
- 条件极值(拉格朗日乘数法)_Simplelife_新浪博客
- python办公自动化ppt_最全总结 | 聊聊 Python 办公自动化之 PPT(下)
- 股票指标SMA EMA WMA...
热门文章
- 车载毫米波雷达系列专题规划和文章目录
- c++ 图片HWC格式转CHW格式
- e470换高分屏_ThinkPadE470笔电(8G内存 256G固态 高分屏 14英寸) 京东5499元
- 思念是美好的,思念是痛心的...
- 通过DeviceIoControl获取U盘或移动硬盘的出品商、制造商和版本号等信息
- Linux 防火墙(一)——基础介绍以及基本扩展模块
- 文献丨GWAS分析菜用大豆可溶性糖含量调控基因
- 计时器(视频的计时:时间码)
- 如何解决Eclipse启动慢?多图、Eclipse详细配置说明
- 分享138个HTML公司企业模板,总有一款适合您