27.安卓逆向2-frida hook技术-frida-dump(使用firda脚本下载dex文件)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
内容参考于:图灵Python学院
工具下载:
链接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwd=zy89
提取码:zy89
复制这段内容后打开百度网盘手机App,操作更方便哦
上一个内容:26.安卓逆向2-frida hook技术-解密响应
app使用java语言开发的,但是有些加密算法为了增加破解难度它会使用java中的jini协议使用c/c++语言来实现加密算法,c/c++编译完之后的文件就是os文件
再来看一个app,它的请求里有一个sign加密字段
然后右击选择 Copy cURL Request
然后百度搜索curl转Python 在线工具,把上方复制的curl转成Python去调用,然后会返回430说参数错误,这个愿意就是里面有加密参数(签名)没有去获取或加密参数已过期,导致请求被服务器拦截了,相同的请求,我们不认识的数据,然后还会变化的一般就是签名,这种的需要逆向分析出加密方式,我们自己使用代码模拟一个然后再去调用请求,这个app的签名是 sign 然后接下来就去源码中找sign
然后这个app也需要砸壳,之前使用的是frida-dexdump进行的砸壳,这次用个不一样的,使用脚本文件砸壳,它的原理是利用安卓系统的libart.so从内存里查询有没有加载dex文件
开源脚本:https://github.com/lasting-yang/frida_dump/blob/master/dump_dex.js
/*** 获取当前进程的名称(包名)* @returns {string} 进程名称,如果失败则返回"-1"*/ function get_self_process_name() {// libc.so是C标准库的共享库,包含了基本的文件操作函数// open函数用于打开文件,返回文件描述符var openPtr = Module.getExportByName('libc.so', 'open');var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);// read函数用于从文件描述符读取数据var readPtr = Module.getExportByName("libc.so", "read");var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]);// close函数用于关闭文件描述符,释放资源var closePtr = Module.getExportByName('libc.so', 'close');var close = new NativeFunction(closePtr, 'int', ['int']);// 读取/proc/self/cmdline文件获取当前进程名var path = Memory.allocUtf8String("/proc/self/cmdline");var fd = open(path, 0);if (fd != -1) {var buffer = Memory.alloc(0x1000);var result = read(fd, buffer, 0x1000);close(fd);result = ptr(buffer).readCString();return result;}return "-1"; }/*** 创建目录(如果不存在)* @param {string} path - 要创建的目录路径*/ function mkdir(path) {// libc.so提供的mkdir函数用于创建目录// 第二个参数755表示目录权限(rwxr-xr-x)var mkdirPtr = Module.getExportByName('libc.so', 'mkdir');var mkdir = new NativeFunction(mkdirPtr, 'int', ['pointer', 'int']);// opendir函数用于打开目录,检查目录是否已存在var opendirPtr = Module.getExportByName('libc.so', 'opendir');var opendir = new NativeFunction(opendirPtr, 'pointer', ['pointer']);// closedir函数用于关闭目录句柄var closedirPtr = Module.getExportByName('libc.so', 'closedir');var closedir = new NativeFunction(closedirPtr, 'int', ['pointer']);var cPath = Memory.allocUtf8String(path);var dir = opendir(cPath);if (dir != 0) {closedir(dir);return 0; // 目录已存在}mkdir(cPath, 755); // 创建目录chmod(path); // 确保目录权限正确 }/*** 修改文件或目录的权限* @param {string} path - 要修改权限的文件或目录路径*/ function chmod(path) {// libc.so提供的chmod函数用于修改文件权限var chmodPtr = Module.getExportByName('libc.so', 'chmod');var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']);var cPath = Memory.allocUtf8String(path);chmod(cPath, 755); // 设置为rwxr-xr-x权限 }/*** 从内存中提取Dex文件*/ function dump_dex() {// libart.so是Android Runtime的核心库// 负责Java/Kotlin代码的执行、类加载、内存管理等底层操作var libart = Process.findModuleByName("libart.so");var addr_DefineClass = null;// 枚举libart.so中所有导出的符号(函数和变量)var symbols = libart.enumerateSymbols();for (var index = 0; index < symbols.length; index++) {var symbol = symbols[index];var symbol_name = symbol.name;// 查找ClassLinker::DefineClass函数// 这个函数在类加载时被调用,传入DexFile对象if (symbol_name.indexOf("ClassLinker") >= 0 &&symbol_name.indexOf("DefineClass") >= 0 &&symbol_name.indexOf("Thread") >= 0 &&symbol_name.indexOf("DexFile") >= 0) {console.log(symbol_name, symbol.address);addr_DefineClass = symbol.address;}}var dex_maps = {}; // 存储已提取的Dex文件,避免重复var dex_count = 1;console.log("[DefineClass:]", addr_DefineClass);if (addr_DefineClass) {// 使用Frida的Interceptor拦截DefineClass函数调用// 当函数被调用时,我们可以获取其参数和返回值Interceptor.attach(addr_DefineClass, {onEnter: function(args) {// args[5]是DexFile对象指针(根据函数签名确定)var dex_file = args[5];// 解析DexFile对象的内存布局// DexFile对象包含Dex文件的基地址和大小信息var base = ptr(dex_file).add(Process.pointerSize).readPointer();var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();if (dex_maps[base] == undefined) {dex_maps[base] = size;var magic = ptr(base).readCString();if (magic.indexOf("dex") == 0) { // 验证Dex文件魔数var process_name = get_self_process_name();if (process_name != "-1") {/**创建以包名命名的目录存储提取的Dex文件要注意一定要找权限高的目录(可读可写,不知道怎么找就跟着笔记中用的目录来),否则会下载dex失败*/var dex_dir_path = "/data/data/" + process_name + "/files/dump_dex_" + process_name;mkdir(dex_dir_path);var dex_path = dex_dir_path + "/class" + (dex_count == 1 ? "" : dex_count) + ".dex";console.log("[find dex]:", dex_path);// 使用JavaScript File API将内存中的Dex内容写入文件var fd = new File(dex_path, "wb");if (fd && fd != null) {dex_count++;var dex_buffer = ptr(base).readByteArray(size);fd.write(dex_buffer);fd.flush();fd.close();console.log("[dump dex]:", dex_path);}}}}},onLeave: function(retval) {}});} } dump_dex()
效果图:
![]()
安卓目录里面
![]()
MT管理器
![]()
这个开源脚本还有下载os文件的方式
![]()
然后使用脚本砸壳玩后默认放到/data目录里,在手机里,还需要拷贝到电脑上,然后这个data目录它没有权限,所以需要先把dex文件移动到一个有权限的位置,使用mv命令移动文件到 /sdcard/Download/ 目录中,这个可以改一下脚本代码中 dex_dir_path 变量的值,要注意一定要找权限高的目录 ,否则会下载dex失败
mv dump_dex_com.xxxx.news /sdcard/Download/
然后使用adb命令把目录下载到电脑上
adb pull /sdcard/Download/dump_dex_com.xxx.news ./
执行命令
效果图:
然后全选拖到jadx中可以分析了
到这就脱壳完毕,现在有两种脱壳方式,推荐用frida-dexdump,因为它不需要手动把dex文件放到电脑上,比较省事