热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用Frida对APP脱壳

上一篇讲到了用工具FDex2进行脱壳,但有一些会脱不到,这篇用Frida​进行脱壳。加壳apk运行流程:app启动后--壳dex先加载

上一篇讲到了用工具FDex2进行脱壳,但有一些会脱不到,这篇用Frida​进行脱壳。

加壳apk运行流程:app启动后-->壳dex先加载起来-->把源classes.dex读出来-->解密源classes.dex-->把源classes.dex给加载进内存-->源dex运行起来

以下两篇文章都对dex进行了详解

Dex文件格式详解 https://www.jianshu.com/p/f7f0a712ddfe

ART 加载dex文件 https://www.jianshu.com/p/f81242ad8cb7


dex文件结构


数据名称

解释

header

dex文件头部,记录整个dex文件的相关属性

string_ids

字符串数据索引,记录了每个字符串在数据区的偏移量

type_ids

类似数据索引,记录了每个类型的字符串索引

proto_ids

原型数据索引,记录了方法声明的字符串,返回类型字符串,参数列表

field_ids

字段数据索引,记录了所属类,类型以及方法名

method_ids

类方法索引,记录方法所属类名,方法声明以及方法名等信息

class_defs

类定义数据索引,记录指定类各类信息,包括接口,超类,类数据偏移量

data

数据区,保存了各个类的真是数据

link_data

连接数据区

 


header

简单记录了dex文件的一些基本信息,以及大致的数据分布。长度固定为0x70,其中每一项信息所占用的内存空间也是固定的,好处是虚拟机在处理dex时不用考虑dex文件的多样性


字段名称

偏移值长度说明

magic

0x0

8

魔数字段,值为"dex\n035\0"(固定的)

checksum

0x8

4

校验码

signature

0xc

20

sha-1签名

file_size

0x20

4

dex文件总长度

......

......

.....

......

字段太多就不都展示出来,可以看到file_size这个就是我们要找的dex文件,因为源dex解密后会加载进内存,所以我们去Hook加载Dex的函数,把Dex从内存中dump出来。

下面这个函数就是把解密后的源dex加载进内存:

DexFile::OpenMemory(const uint8_t* base,

  size_t size,

  const std::string& location,

  uint32_t location_checksum,

  MemMap* mem_map,//nullptr const OatDexFile* oat_dex_file,

  std::string* error_msg)

OpenMemory()是在安卓系统/system/lib/libart.so里面,然后我们先把这个so文件拉到电脑用IDA打开

image.png

打开IDA选择静态调试,打开libart.so这个文件

image.png

image.png

这些就是so文件里面的函数,点击这个框,按ctrl+f进行搜索,输入OpenMemory这个函数,右键进行编辑,

_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_

这个字符串就是OpenMemory函数在内存中对外的方法名,我们打开IDA就是为了找这个方法名。

image.png

 

现在来写脚本:

import frida
import syspackage = "com.iCitySuzhou.suzhou001"open_memory_6 = "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"
#OpenMemory 在libart.so中 art虚拟机(安卓5) davlink虚拟机(安卓4)
#Hook OpenMemory的导出方法名
#用IDA 打开libart.so 查看OpenMemory的导出方法名
#OpenMemory的第一个参数是dex文件在内存中的起始位置
#根据dex文件格式 从起始位置开始 第32个字节 是该dex文件的大小
#知道dex起始位置和整个文件大小,只需要把这段内存dum出来即可
#适用于 安卓 6 7 8 9#文件的起始位置 文件的大小 知道了文件的结束位置def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)src = """
//找so文件某个方法地址,找openMemory的内存地址
var openMemory_address = Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_");
send('openMemory address:'+openMemory_address)//hook openMemory地址
Interceptor.attach(openMemory_address, {//一进入openMemory,就会调用onEnter方法onEnter: function (args) {//dex文件的起始位置var dex_begin_address = args[1]//dex文件的前8个字节是magic字段 看dex的文件格式说明//打印magic(会显示 "dex 035") 三个字符 可以验证是否为dex文件 console.log("magic : " + Memory.readUtf8String(dex_begin_address))//把地址转换成整型(十进制) 再加32 //因为dex文件的第32个字节处存放的是 dex文件的大小var address = parseInt(dex_begin_address, 16) + 0x20//把address地址指向的内存值读出来 该值就是dex的文件大小//ptr(address)转换的原因是 frida只接受 NativePointer类型指针var dex_size = Memory.readInt(ptr(address))console.log("dex_size :" + dex_size)//frida写文件 把内存中的数据 写到本地var timestamp = new Date().getTime();var file = new File("/data/data/%s/" + timestamp + ".dex", "wb")//Memory.readByteArray(begin, length)//把内存里的数据读出来,从begin开始读,取length长度file.write(Memory.readByteArray(dex_begin_address, dex_size))file.flush()file.close()send("dex begin address:"+parseInt(dex_begin_address,16))send("dex file size:"+dex_size)},onLeave: function (retval) {if (retval.toInt32() > 0) {}}
});
"""%(package)print("dex 导出目录为: /data/data/%s"%(package))
device = frida.get_usb_device()
pid = device.spawn(package)
session = device.attach(pid)
script = session.create_script(src)
script.on("message" , on_message)
script.load()
device.resume(pid)
sys.stdin.read()

先把frida-server运行起来

image.png

再做端口转发到PC,Windows运行

adb forward tcp:27043 tcp:27043
adb forward tcp:27042 tcp:27042

image.png

hook需要app运行,先把app打开,再运行脚本,

image.png

我们去手机/data/data/com.iCitySuzhou.suzhou001目录下查看,会多了几个dex文件

image.png

把这些dex文件拉到电脑用jadx打开看下,一个个查看后,找到了源dex

image.png

到这里用frida脱壳也成功了。。。


推荐阅读
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • 打开文件管理器_【教程】模组管理器3.1食用指南
    文编:byakko最近有部分小伙伴反应还不会使用unity模组管理器,现在我就给大家讲一下unity模组管理器——从下载到使用。完整视频版以下是无WiF ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • Ubuntu安装常用软件详细步骤
    目录1.GoogleChrome浏览器2.搜狗拼音输入法3.Pycharm4.Clion5.其他软件1.GoogleChrome浏览器通过直接下载安装GoogleChro ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
author-avatar
xiaojin
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有