周一 | 周二 | 周三 | 周四 | 周五 | 周六 | 周天 |
---|---|---|---|---|---|---|
Android系统以Linux系统中的用户权限机制为基础,一个APP对应一个用户,Linux中的用户隔离转变成Android系统中的应用隔离。
Android用户在设备上安装时被赋予独特的用户标识(UID),同时也会为该应用程序下的所有文件与所有的操作都配置相同的权限,只有相同UID的应用程序(前提是签名相同)才能对这些文件资源进行操作,未经授权不能访问。同时,每一个Android应用程序运行时Zygote进程会为其单独生成一个Dalvik虚拟机实例,应用程序运行于各自的Dalvik虚拟机中,这就是Android中的沙箱隔离机制。
运行的字节码不同:
Java程序通过经过相应的编译环节生成对应的.class文件,Java虚拟机再通过.class文件读取相应的Java字节码运行程序。
Dalvik虚拟机运行的Dex文件是通过.class文件打包生成的,核心环节就是Java字节码到Dalvik字节码的转换。
可执行文件体积不同:
Java类文件中的方法如果被其它类引用,相应的方法签名会被复制到其他类文件当中,导致不同的类文件中存在大量相同的常量,造成信息冗余。
Android SDK中的dx工具负责将Java字节码转换成Dalvik字节码,它能够提取所有Java类文件中的常量池,消除其中冗余的信息,再重新组合生成一个新的共享常量池。
架构不同:
Android系统启动后,会首先启动init进程完成初始化工作,然后读取init.rc文件启动Zygote进程。
Zygote进程是Android系统的孵化进程,启动后会初始化Dalvik虚拟机、启动Server进程,等待执行命令。
用户启动一个Android应用时,Server进程会通过Android系统中的Binder方式接收到一条执行请求,随后通过将执行请求通过Socket的方式发送给Zygote进程。
Zygote进程收到执行请求后,会创建出一个Dalvik虚拟机实例来执行应用程序,完成整个启动过程。
Dalvik虚拟机执行应用程序时,首先完成类的装载工作,通过一张全局哈希表来实现装载类的查询存储。
类的装载工作完成之后,使用字节码验证器对所有代码进行验证,校验完成之后调用相关函数在全局哈希表中查找main方法类,并执行。
Dalvik虚拟机使用JIT方式,当程序运行时将Dex可执行文件中的字节码转换成机器码。主要包括两种方式:
Smail中分基本数据类型与引用类型,其具体表现形式如下:
基本数据类型:
L
类型可以表示Java类型中的任何类,在Smail具体表现形式为 "Lpackage/name/ObjectName;" package/name表示类所在包名,ObjectName表示对象名。比如:Ljava/lang/String;
相当于java.lang.String
。
[
类型可以表示所有基本类型的数组。"[I"表示int型的一维数组,相当于Java中的int[]
,多个"["可以表示多维数组。
在Smail中,方法以.method
指令开始,以.end method
指令结束,具体表现形式如下:
.method Lpackage/name/ObjectName;->MethodName(III)Z;
.locals
.prologue
.end method
Lpackage/name/ObjectName
:方法所在类。MethodName
:具体的方法名。(III)
:方法参数。Z
:返回值类型。.prologue
:方法开始处。字段与方法类似,只是将参数与返回值改成字段名与类型。
.field Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
每一个Smail文件对应Java中的一个类,包括内部类。只是内部类的文件名形式为[外部类]$[内部类].smali
,格式如下:
.class < 访问权限> [ 修饰关键字] < 类名>
.super < 父类名>
.source <源文件名>
静态分析是指在不运行代码的情况下,采用词法分析,语法分析等各种技术手段对程序文件进行扫描,从而生成程序的反汇编代码,然后阅读反汇编代码来掌握程序功能的一种技术。
Android Java层静态分析的两种方法:
一种方法是是阅读通过ApkTool等工具对APK文件进行反编译,这一步最重要的就是通过baksmali反编译dex生成smali文件,攻击者在有一定Smali代码阅读能力的基础上,可以得到许多关键信息。
第二种方法就是使用Dex2jar生成反汇编的.jar文件,然后再通过jd-gui来阅读反汇编的Java代码。
目前对Android应用的反汇编以及静态分析,主要使用集成化的工具,这里我使用的是AndroidKiller。
再对Android应用进行逆向攻击时,如果直接反编译,一行一行地阅读反汇编代码,工作量非常大,效率低。为了节省时间,常见的攻击思路如下:
首先通过ApkTool反汇编APK文件,获取AndroidManifet.xml文件,该文件相当于是Android应用的配置文件,包含了整个应用组件的信息,以及一些关键的属性值,比如应用申请的一些权限,应用程序的主入口,包名等。
信息反馈:运行目标程序,从程序运行过程中的反馈信息获取突破口。通常Android应用程序中用到的字符串数据会被存储在string.xml文件中,调用的时候是通过id值进行访问,可以通过直接搜索字符串的id值或者字符串来定位。
特征函数法:与信息反馈类似,Android SDK提供的API,可以为我们提供便利。比如应用程序中弹出的对话框,需要调用Android中的Toast.MakeText().Show()方法,我们可以直接在反汇编后的代码中搜索相应的API,可以找到对应调用的位置。
顺序查看法:从应用启动开始,一行一行的代码分析,也是最耗时最原始的办法,需要攻击者具有非常好的编程基础。
首先运行程序,寻找一个突破口。发现当注册失败时,会有对话框弹出。
然后使用AndroidKiller反汇编攻击程序,进入AndroidManifet.xml文件中。找到对应的主函数入口。
利用应用程序运行时弹出的提示信息“无效注册码”,在项目工程中查找相关内容。能够在String.xml文件中找到“注册成功”的字符信息,很明显这就是我们想要的结果。
查找succeed对应的id值,并检索其调用位置。
根据id值,定位到具体的smali文件中。
使用Dex2jar工具将Smail文件反编译生成Jar文件,再使用jd-gui阅读Jar文件代码,找到关键的Toast函数调用,并进行初步分析:
返回Smali文件中,修改核心的判断逻辑。
重新编译运行:
核心步骤:
危害性:静态分析常常与Android应用程序的重打包相关联,攻击者可以通过插入恶意代码感染合法应用(破解版软件可能存在的安全风险),步骤如下:
由于Java代码被反编译的难度很小,所以针对Java层的代码保护,主要是通过代码混淆来实现,目前常用的是Android Studio自带的混淆插件,只需要配置相关文件就行。
静态修改Smali代码后,攻击者需要重新打包再使用攻击者的私钥对APK进行签名,所以二次打包后的签名信息一定会发生改变,所以可以在代码中添加相应的检测逻辑。
针对静态分析最有效的办法还是加壳,攻击者反汇编后不能直接得到源程序的代码,无从下手。
这里我以Android Studio动态调试为例,源程序为静态分析中的注册验证程序。
首先需要在Android Studio中安装smalidea插件,源zip可以百度搜一下。安装成功后重启。
将Android Killer反编译后生成的Smali文件打包存放在一个文件夹下。
将该文件导入Android Studio工程,选择Import project选项。
选中文件夹,右键Make Directory As -> Source Root命令,让Smali代码加载进来。
执行File->Project Structure命令,选择SDK1.8。
命令行使用am命令,以debug模式启动应用:adb shell am start -D -n com.XXX.XXX包名/.MainActivity,模拟器应用会进入等待调试状态。
打开DDMS查看程序运行的端口。
修改Debug设置中的端口号。
执行Debug命令。
命令行会提示建立Socket连接。
在关键的Equals函数处设置断点。
通过查看变量值,发现一个16位的字符串,直接猜测这就是正确的注册码。
直接尝试使用同样的用户名,注册码填入获得的16位字符串。
核心思路
获取待调试应用程序的反汇编Smali代码。
导入Android Studio之类的调试器中,设置相关参数。
在模拟器中以debug模式启动应用:adb shell am start -D -n -com.XXX.XXX(包名)/.主Activity。
打开DDMS监视器,找到对应的调试端口号。
在调试器Debug Configuration中设置远程调试项目,运行debug,如果DDMS中的小蜘蛛从红色变绿了,表明连接成功。
在调试器中相应的关键代码设置断点,查看相应的变量值,这个过程主要看攻击者自身的分析能力。
最后就是根据调试过程得到的信息,修改Smali代码或者其它手段达到攻击的效果。
危害性:目前针对Android应用程序的动态调试攻击,不仅仅是对反汇编的Smali代码,还有Native层的So文件,通过IDA可以对So文件进行调试。而且目前的应用程序大部分都是经过加固之后才能上架,此时静态分析攻击基本上没有什么效果。所以目前针对Android应用的攻击思路首先是以动态调试,在Dexload函数处下断点,得到源Dex文件开始。
反调试的目的就是为了防止应用程序被第三方的调试器分析,目前反调试的思路主要有三个方向:
调试器状态检测:Android SDK中提供了android.os.Debug.isDebuggerConnected()方法来判断程序是否被调试器调试。同时AndroidManifest.xml文件中,可以查看Application标签中是否有android:debuggable属性,所以可以在代码中检测它的属性值。
调试器端口检测:在使用调试器远程调试程序时,调试服务端会在Android设备上运行,然后等待调试器连接,所以可以根据调试器常用的端口号来检测是否被调试。最简单的方法就是用Socket连接需要检测的端口。
进程状态检测:当应用程序被调试时,进程状态与其正常运行时有不同,可以通过检测进程状态的差异判断应用程序是否被调试。
Android系统中使用的最多的Hook框架就是Xposed框架了,支持Java层的Dalvik Hook和ART Hook,所以主要针对其背后的运行原理进行研究。
Xposed框架的安装需要从网上下载一个Xposed Installer App,按照操作提示完成安装,前提是设备必须经过root,这里我选择的是模拟器。通过安装的脚本可以发现安装Xposed框架后的Android系统Zygote进程被替换,启动的是Xposed版的Zygote进程。
在Android系统启动的时候,Zygote进程加载XposedBridge.jar,将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,这个方法再通过调用handleHookedMethod这个Java方法来调用被劫持的方法转入Hook逻辑。简而言之,就是在应用不知情的情况下修改了被劫持方法的执行逻辑,也就是偷梁换柱。
目标:还是以上一节的注册应用为例,修改CheckSN()方法的执行逻辑,实现攻击。
首先需要安装相应的Xposed框架,效果如下:
随后使用Android Studio新建一个hook工程,新建一个Java类,添加对被攻击应用程序进程的过滤:
public class hooks implements IXposedHookLoadPackage {
final String packageName = "com.droider.crackme0201"; //被劫持的应用包名
@Override
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals(packageName)) {
return;
}
new CheckSNHook(lpparam.classLoader); //如果发现进程中有该应用,则新建一个CheckSNHook类对应用进行hook。
}
}
之后有两种攻击思路:
1、编写核心的CheckSNhook方法,首先找到应用程序CheckSN方法的位置,进行定位。
Class clz = (Class<?>) XposedHelpers.findClass("com.droider.crackme0201.MainActivity", cl); //找到MainActivity类
XposedHelpers.findAndHookMethod(clz,"checkSN",
String.class, String.class,
new XC_MethodHook(){
// 找到需要Hook的方法,并写入hook后的方法逻辑
}
随后在XC_MethodHook中编写afterHookedMethod()方法,将结果置为true。
@Override
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
XposedBridge.log("CheckSN afterHookedMethod called.");
String s1 = (String) param.args[0];
String s2 = (String) param.args[1];
param.setResult(true);super.afterHookedMethod(param);
}
2、使用XC_MethodReplacement,该类配合XposedBridge类的hookAllMethods()方法可以hook所有名为checkSN的方法,并可以设置相应的返回值为true。
final XC_MethodReplacement replacementTrue = XC_MethodReplacement.returnConstant(true);
Class clz = (Class<?>) XposedHelpers.findClass("com.droider.crackme0201.MainActivity", cl);
XposedBridge.hookAllMethods(clz, "checkSN", replacementTrue);
hook模块编写完成后,需要在项目src目录下新建assets目录,再新建一个xposed_init的文本文件,XposedBrige会从该文件找到hook工具类。
同时需要在AndroidManifest.xml文件中添加Xposed模块的声明:
<meta-data android:value="true" android:name="xposedmodule"/>
<meta-data android:value="53" android:name="xposedminversion"/>
<meta-data android:name="xposeddescription" android:value="Crackme0201 checkSN hooker"/>
编译安装好插件之后,在设备的Xposed Installer中启用该框架,重新开机。
运行被攻击程序,随便输入用户名和密码,都会提示注册成功。
核心思路:
危害性:
原文:https://www.cnblogs.com/Gst-Paul/p/12637346.html