目录
JPDA(Java Platform Debugger Architecture)编辑
-
什么是 Android 逆向开发?
-
Android 逆向开发是指对已发布的 Android 应用进行分析和破解,以了解应用程序的内部工作原理,获取应用程序的敏感信息,或者修改应用程序的行为。
-
Android基础知识介绍
-
apk包文件结构
Android应用 Java编写的,利用Android SDK编译代码,并并且把所有的数据和资源文件打包成一个APK(Android Package)文件
这是一个后缀名为.apk的压缩文件,apk文件中包含了一个Anddroid应用程序的所有内容,是Android平台用于安装应用程序的文件。
apk就是一个压缩包,解开这个apk包我们可以看到以下的结构::
作用介绍如下:
-
assets目录:存放需要打包到apk的静态文件,该目录与res目录不同之处在于assets自录支持任意深度的子目录,我们的开发者可以根据自己的需求来任意部署文件夹的架构,而且res目录下的的文件会在.R文件中生成与其对应的资源ID,assets不会自动生成对应的id,访问的时候需要AssetManager类
-
lib目录:该目录用来存放应用程序所依赖的native库文件,native库一般是用C/C++进行编写的,这里的lib库可能包含4种箱类型,根据CPU型号的不同,我们大体可以分为ARM,ARM-V7a,MIPS,X86,分别对应着ARM架构,ARM-V7架构,MIPS架构和X86架构,这些so库在apk包中构成如下图:
其中,不同的CPU架构对应着不同的目录,每个目录中可以存放非常多的对应版本的so库,而且这个目录的结构固定,用户只能按照这个目录来存放自己的so库。目前市场上使用的移动终端大多是基于ARM或者ARM-v7a架构的。从厂家上来分是有三种,arm,x86,MIPS,arm系列是绝大多数手机上使用的,x86主要是运用在平板上,而MIPS,基本上就没见过。
-
res目录:res是resource的缩写,这个目录存放资源文件牛,存在这个文件夹下的所有文件都会映射到Android工程的R文件中,生成对应的ID,访问的时候直接使用资源ID即R.id.filename,res文件夹下可以包含多个文件夹,其中anim存放动画文件;drawable目录存放图像资源;layout目录存放布局文件;values目录存放一些特值,colors.xml存放color颜色值,dimens.xml定义尺寸值,string.xml定义字符串的值,styles.xxml定义样式对象;xml文件夹存放任意xml文件,在运行时可以通过Resources.getXML()读取;raw是可以直接复制到设备中的任意文件,他们无需编译。
-
META-INF目录:保存应用的签名信息,签名信息可以验证apk文件的完整性。AndroidSDK在打包apk时会计算apk 中所有文件的完整性,并且把这些完整性保存到META-INF文件夹下,应用程序存在安装的时候首先会根据META-INF文件夹校验apk的完整性,这样就可以保证apk中的每一个文件都不能被算改。以此来确保apk应用程序不被恶意修改或者病毒感染,有利于确保Android应用的完整性和系统的安全性。META-INF目录下包含的文件有CERT.RSA, CERT.DSA, CERT.SF 和 MANIFEST.MF,其中CERT.RSA是开发者利用私钥对APK进行签名的签名文件,CERT.SF,MANIFEST.MF记录了文件中文件的SHA-1哈希值,
-
AndroidManifest.xml:是Android应用程序的配置文件,是一个用来描述Android应用整体资讯的设定文件,简单来证说,相当于Android应用向Android系统自我介绍的配置文件,Android系统可以根据这个自我介绍完整地了解apk应用程序的资讯,每个Android应用程序都必须包含一个AndroidManifest.xml文件,且它的名字是固定的,不能修改。在开发Android应用程序的时候一般都把代码中的每一个Activity,Service,Provider和Receeiver在AndroidManifest.xml中注册,只有这样系统才能启动对应的组件,另外这个文件还包含一些权限声明以及使用的SDK版本信息等等。程序打包时,会把AndroidManifest.xml进行简单的编译,便于Android系统识别,编译之后的格式是AXML格式,如下图所示
-
classes.dex:传统的Java程序,首先先把文件编译成class文件,字节码都保存在了class文件中,Java虚拟机可以通过解释且执行这些class文件。然而Dalvik虚拟机是在Java虚拟机进行了优化,执行的是Dalvik字节码,而这些Dalvik字节码就是由、Java字节码转换而来的。一般来说,Android应用在打包的时候通过Android SDK中的dx工具将Java字节码转换为 Dalvik字节码,Dx工具可以对多个class文件进行合并,重组和优化,通过这些操作,可以达到减小体积,缩短运行时间的目的。
-
resources.arsc:用来记录资源文件和资源ID之间的映射关系,用来根据资源ID寻找资源。Android的开发是分模块的,res目录专门用来存放资源文件,当在代码中需要调用资源文件时,只需要调用用findviewbyld()就可以得到资源文件,每当在res文件夹下放一个文件,aapt就会自动生成对应的ID保存在.R文件,我们调用这这个ID就可以,但是只有这个ID还不够,.R文件只是保证编译程序不报错,实际上在程序运行时,系统要根据ID去寻找对应的资源路径,而resources.arsc文件就是用来记录这些和资源文件位置对应关系的文件。
-
Android系统目录介绍
-
data/data目录:存放用户apk数据的目录,每个apk都有自己的目录,以包名命名就是在data/data目录下,会产生一个跟package一样的目录,这是一个私有目录,app只能访问各自的目录,除非root 权限
-
data/app目录:用户安装的app存放在这个目录下
-
data/local/tmp:临时目录,权限比较大
-
system/app目录:存放系统自带的app
-
system/lib目录、system/lib64:存放app用到的so文件
-
system/bin目录:存放shell命令
-
system/framework目录:Andriod系统所用到的框架,如一些jar文件,XposedBridge.jar
-
sd卡目录:不管手机有没有存储卡都会有这个目录,app操作sd卡目录需要申请权限/sdcard->/storage/self/primary/mnt/sdcaard/storage/emulated/0
-
jadx
-
jadx的简介
jadx是一款使用广泛的反编译工具,可以一键把apk文件还原成Java代码,使用起来很简单,功能强大,还具有一些附加功能可以辅助代码追查。GitHub地址为:https://github.com/skylot/jadx。
主要具有处下几个功能:
-
除了反编译apk文件,还可以反编译jar、class、dex、aar等文件和zip文件中的Dalvik字节码
-
解码AndroidManifest.xml文件和一些来自 resources.arsc中的资源文件
-
一些apk文件在打包过程中增加了Java代码的混淆机制,对比化jadx提供反混淆的支持。
jadx本身是一个命令行工具,仅仅通过jadx这个命令就可以反编译一个apk文件。除此之外,它也有配套的图形化界面工具(jadx-gui),这个使用起来更加方便,能直接以图形界面的方式打开一个apk文件。同时,jadx-gui对反编译后得到的java代码和其他资源文牛增加了高亮支持(就像在IDE中打开这些内容一样),还具有快速定位、引用搜索、全文搜索等功能。所以,我们往往直接使用jadx-gui完成一些反编译操作。
-
jadx的命令
使用jadx的命令执行文件的反编译操作,主要是指定一些输入参数和输出参数,这些参数的设置细节直接参考参数说明即可。运行jadx -h命令(以管理员身份运行cmd窗口,并且进入到jadx解压的目录),查看jadx命令的用法,如下图所示:
可以看到,参数<input files>就是输入文件的路径,其他参数如-d可以指定反编译后输出文件的路径,可以指定不解析资源文件(能够提升整体反编译的速度)。使用下面的命令对已经下载好的scrapee-app5.apk文件进行反编译:
jadx scrape-app5.apk -d scrape-app5
运行完毕后,本地会生成一个scrape-app5文件夹,还原效果还是比较理想的,就这样,我们只需要一条简单的命令就完成了对apk文件的反编译,其中Java代码的逻辑一览无遗。
-
jadx-gui的使用方法
jadx-gui是一个图形界面工具,它就像一个IDE,支持很多方便快捷的交互式操作(例如把一个apk文件拖到jadx-gui后,它会直接打开这个软件,之后高亮显示反编译后的代码),以及代码搜索、定位等。
-
启动和反编译
在Windows下直接双击 jadx-gui.bat,jadx-gui便启动了,这是我们可以看到下图所示的界面
可以通过文件路径打开apk,也可以直接将apk文件拖到jadx-gui的窗口中,还可以从菜单栏中的文件-->打开文件调出资源管理器来打开apk文件。文件打开之后,稍等片刻,反编译就完成了,这时看到的界面如下图所示
从界面的左侧可以发现,反编译后的Java源代码以一个个包的形式组织在一起,另外还有资源文件,其中包括图片文件、布局文件和AndroidManifest.xml文件(内含apk文件的基本信息)等。在左侧展开想要查看的包,右侧就会出现对应的Java源代码,如下图所示::
保存为Gradle项目
我们也可以把反编译后的文件另存为Gradle项目,Gradle项项目就是开发版本的Andriod项目,如下图所示
导出后的项目目录结构和我们在jadx-gui界面里看到的结构基本一致,这个项目是可以被Android Studio工具打开的,打开的界面如下图所示:
打开之后的代码一般没办法直接运行,因为毕竟整个项目是反编译出来的,不太可能完全还原出开发版本的Andriod项目。如果你对Andriod开发比较了解,可以尝试修改一下源码和Gradle配置,是可以使项目正常运行的,即使不能运行也没有太大关系,我们的目的并不是运行这个代码,而是分析其中的逻辑,所以要把目光聚焦在查找和定位目标方法与逻辑定义上,Android Studio能够帮我们更方便地完成这些操作,当然jadx-gui也提供了查找和定位的相关功能,现在我们回到jadx-gui,了解一下它的其他常见用法。
-
文本搜索
在源代码中搜索/api/movie字符串,可以使用jadx-gui提供的的搜索功能,打开菜单栏里的导航-->搜索文本,如下图所示:
这时jadx-gui会显示一个搜索框,如下图所示,在搜索文本:下方填入/api/movie,同时可以从类名、方法名、变量名和代码中选择搜索位置,自行勾选即可,下方会显示搜索结果。
可以看到,搜索到了一处包含/api/movie字符串的位置,可以依次看一下这两处的内容,先选中搜索结果,然后点击转到按钮即可跳转到对应的代码处,如下图所示:
-
查找方法的声明
鼠标右键单击任意方法名,会打开一个菜单,选择 跳到声明,如下图所示:
-
反编译
-
原理
说到反编译, 先来看下正向编译, 如上图, 正向编译是
java -> class -> dex -> apk
反编译和正向编译稍有不同, 反编译可以分成两类:
java <- smali <- dex <- apk
这种方法是将dex文件转为smali, smali是Dalvik虚拟机的汇编语言, 可以用来动态调试程序.
java <- class/jar <- dex <- apk
这种方法中是将Dalvik字节码转化为等价的Java字节码, 然后用丰富的java分析工具分析源码.
-
如何反反编译(即对抗反编译):
阅读反编译工具源码查找缺陷
压力测试找反编译工具bug(下载很多apk, 写个脚本调用ApkTool反编译这些apk, ApkTool因为某些bug无法反编译某个apk, 这时我们就通过压力测试找到了ApkTool的bug, 将发现的这个应用到我们的apk中, 即可保护我们的apk免受ApkTool反编译)
-
如何反反反编译呢:
阅读反编译源码修复缺陷
-
工具
上图的反编译工具走的java <- class/jar <- dex <- apk路线, 即先把apk里的dex找到, 然后使用Enjarify/dex2jar/classyshark/jadx反编译得到jar包, 然后使用jd-gui/CFR/Procyon阅读jar包里的java源码. 这些工具各有优缺点, 我们一般选择dex2jar+jd-gui, 相比其他工具, jd-gui虽然很久不更新了, 但是支持跳转, 方便查看代码. 特别说明下Bytecode-Viewer, 其是Procyon的一个前端, 同时集成了很多其他工具, 功能强大.
看下上图, 这些工具走的是java <- class/jar <- dex <- apk路线. 将dex文件转化为smali汇编, 然后直接阅读smali汇编语言, 或者smali再转为java(这里没有强大的工具, 可能经常无法成功转化).
最常用反编译工具
从上图可以看到有很多反编译工具, 最常用的是dex2jar+jd-gui和ApkTool.
jd-gui不仅有不错的界面, 最关键的是支持类之间的跳转, 在混淆后的代码中跳转可以大大方便我们查看.
ApkTool隐隐有无冕之王的声势, 可以反编译代码和资源, 修改后可以重编译成apk, 在Android Studio下使用smalidea插件还可以完成无源码调试, 十分强大.
-
静态分析
-
原理
动态分析主要基于下面两个工具:
JPDA(Java Platform Debugger Architecture)
JPDA分为三层, 分别是JVMTI,JDWP,JDI.
JVMTI(Java Virtual Machine Tool Interface)是一套由虚拟机直接提供的 native接口,通过这些接口,开发人员不仅调试在该虚拟机上运行的 Java程序,还能查看它们运行的状态,设置回调函数,控制某些环境变量,从而优化程序性能。
JDWP(Java Debug Wire Protocol)是一个为 Java调试而设计的一个通讯交互协议,它定义了调试器和被调试程序之间传递的信息的格式。
JDI(Java Debug Interface)提供 Java API 来远程控制被调试虚拟机
Android调试模型是一种JPDA框架的具体实现
有两点主要区别:
-
JVM TI适配了Android设备特有的Dalvik虚拟机/ART虚拟机
-
JDWP的实现支持ADB和Socket两种通信方式
ptrace(process trace)
ptrace()
提供了跟踪和调试的功能。它允许一个进程(跟踪进程tracer)去控制另外一个进程(被跟踪进程tracee)。
tracer可以观察和控制tracee的运行,可以查看和改变tracee的内存和寄存器。它主要用来实现断点调试和系统调用跟踪。
tracer流程一般如下:
其中PTRACE_ATTACH/PTRACE_GETREGS/PTRACE_POKETEXT/PTRACE_SETREGS/PTRACE_DETACH定义如下:
PTRACE_ATTACH,表示附加到指定远程进程;
PTRACE_DETACH,表示从指定远程进程分离
PTRACE_GETREGS,表示读取远程进程当前寄存器环境
PTRACE_SETREGS,表示设置远程进程的寄存器环境
PTRACE_CONT,表示使远程进程继续运行
PTRACE_PEEKTEXT,从远程进程指定内存地址读取一个word大小的数据
PTRACE_POKETEXT,往远程进程指定内存地址写入一个word大小的数据
ptrace是*nix系统上最常用的系统调用之一, 常见的gdb调试也是通过它实现的.
检测ptrace
当我们使用ptrace方式跟踪一个进程时,目标进程会记录自己被谁跟踪,可以查看/proc/pid/status来确认. 所以apk里为了防止被逆向, 一般都会新开一个线程, 对status做检测, 如果TracerPid不为0, 立刻退出apk.
正常情况
被ptrace时
反动态分析:
检查是否有调试
Debug.isDebuggerConnected();
针对ptrace, 检查TracerPid是否为0
检测是否在模拟器
getprop不同(虚拟机和真机的环境变量不同,
比如虚拟机的ro.kernel.qemu=1而真机没有这个属性)
反反动态分析:
对抗反调试
java层:smali代码注释掉
native层 (nop掉so文件或内存中指令, 断点fopen/fget并修改内存)
Android程序是否可调试:
开启调试:
1.下载mprop, 注入init进程, 修改内存中属性值
./mprop ro.debuggable 1
2.重启adbd
stop;start
tip:
说到android:debuggable这个属性, 想到另一个属性android:allowBackup.
android:allowBackup默认为true, 一定要显式设置android:allowBackup=false.
否则adb backup/adb restore备份恢复数据
微信6.0以前未设置此属性,可以备份恢复数据
-
工具
这里特别推荐下VirtualXposed, 其基于VirtualApp和epic, 将Xposed安装到VirtualApp中, 可以不用root权限就使用Xposed, 而且安装插件后重启极快.
Frida是一个DBI工具, 使用其进行动态分析时, 被分析进程的TracerPid仍为0. 下图是Frida原理, 其最初建立连接时通过ptrace向相关进程注入代码, 其后使用其特有的通道来通信, 如下图. Frida-Gadget支持Android下非root和iOS下非越狱的逆向.
IDA家喻户晓, 其支持dex和so的动态分析, 尤其是asm->c的转化, 可以大大方便分析.
radare是一个比IDA还要强大的工具, 其起源是调查取证, 不过目前支持数不胜数的功能. 但是其学习曲线比Vim还要陡峭
工具地址:
https://forum.xda-developers.com/showthread.php?t=3034811
https://github.com/android-hacker/VirtualXposed
https://github.com/frida/frida
https://www.hex-rays.com/products/ida/
https://github.com/radare/radare2
https://www.megabeets.net/a-journey-into-radare-2-part-1/
-
实例
无源码动态调试smali代码
可以将apk用ApkTool反编译后, 使用AndroidStudio+smalidea插件来调试apk.
分享一个小tip, 如何让程序暂停在启动界面.
因为反逆向代码一般在Application的onCreate或更早就执行, 如果等到程序运行到MainActivity再attach进程, 时机就太晚了.
可以用如下命令让app停在等待debug界面:
等待debug一次: adb shell am set-debug-app -w com.oncealong.sample
一次debug不一定能解决问题,多次调试则在所难免,如果每次调试都执行上述语句, 稍显啰嗦, 那么此时可以执行下述语句:
一直等待debug: adb shell am set-debug-app -w --persistent com.oncealong.sample
待debug完毕, 使用下述语句取消打开app时的等待.
取消等待debug: adb shell am clear-debug-app
这里的示例不再展开, 只说明这种方法和其效果, 对其感兴趣可以看下述链接.
IDA动态调试
IDA动态调试可以获得内存中的信息, 比如在dvmDexFileOpenPartial函数上加断点, 然后执行IDA脚本直接把内存中的dex拷贝出来以脱壳. 详情见Android应用安全防护和逆向分析相关章节. 这里也不做详细介绍,
只用下图展示IDA的强大.
https://blog.csdn.net/jltxgcy/article/details/50600241
https://blog.csdn.net/qq1084283172/article/details/46872937
VirtualXposed hook java
VirtualXposed可以hook java, 相比Xposed安装插件需要重启手机, VirtualXposed只用重启下Xposed程序, 如果前者重启手机耗时1min, 后者重启Xposed程序只用1s不到. 对于一些简单的hook或者逆向, 或者验证Xposed插件逻辑, 这里强烈推荐VirtualXposed. 不过Xposed只支持hook java层, 如果需要hook native层, 可以使用下一个工具Frida.
参考地址:https://github.com/android-hacker/VirtualXposed
https://github.com/ac-pm/Inspeckage
http://www.cnblogs.com/lkislam/p/4859959.html
Frida
Frida支持java/native层的hook. 而且Frida支持脚本, 这样可以更方便的复现结果.
比如Frida的这个Android示例. 将下面的代码放到一个py脚本中, 随时运行都可以获得结果. 不像IDA还需要恢复现场.
重编译
4.1 原理
反重编译:
运行时检查签名(signatures比较长,hash后比较)
运行时校验保护(校验classes.dex的md5)
反反重编译:
查关键函数, 注释掉或nop掉
如果到这一步, 光靠本地的检测基本无效, 可以考虑在http请求时加入对apk签名的检查, 如果不合法就不返回数据. 但是这样无法阻止app被非法本地运行, 逆向者也可以通过抓包正常apk的请求来模拟正常请求. 不过这样可以进一步提高破解门槛.
-
Docker
5.1 原理
与逆向工具高内聚,与外界系统低耦合
在Linux下, Docker性能不错, 还可以使用VNC连接桌面.
# pull image
docker pull cryptax/android-re:latest
# run locally interactive
docker run -it --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix
cryptax/android-re:latest /bin/bash
# run through ssh or VNC
docker run -d -p SSH_PORT:22 -p VNC_PORT:5900 cryptax/android-re
## sample: docker run -d --privileged -p 5900:5900 -p 5022:22
cryptax/android-re
ssh -X -p SSH_PORT root@127.0.0.1
## sample: ssh -p 5022 -X root@127.0.0.1 #password: rootpass
vncviewer HOST::VNC_PORT
##vncviewer 127.0.0.1::5900
工具地址: