Android应用安全防护的基本策略

混淆机制

代码混淆

在反编译apk后,看到的代码类名、方法名已经代码格式看起来不像正常的Android项目代码,这就是经过混淆的代码。

资源混淆

资源混淆的功能具体可看微信团队的开源项目:https://github.com/shwenzhang/AndResGuard。

对于资源混淆,在获取到资源对于的int类型值后,可在/res/values/public.xml找到对应的一个name项的值,然后通过这个name的值可以在相应的xml中查找到对应的资源。

签名保护

Android中每个应用都有一个唯一的签名。

为了防止应用被二次打包,可以在程序入口处添加签名验证,从而防止运行被修改的程序。

手动注册native方法

在Android中,当程序Java层运行System.loadLibrary(“jnitest”);这行代码后,程序会去载入jnitest.so文件。与此同时,产生一个Load事件,这个事件触发后,程序默认会在载入的.so文件的函数列表中查找JNI_OnLoad函数并执行(卸载时触发UnLoad事件,并查找JNI_OnUnload函数执行。JNI_OnLoad和JNI_OnUnload函数并不强制要求用户去实现)。一般情况下,在C组件中的JNI_OnLoad函数用来实现给VM注册接口,以方便VM可以快速地找到Java代码需要调用的C函数。

应用层的Java类通过VM而调用native函数,这个搜索的过程可能较为漫长。C组件开发者可以将本地函数向VM进行注册,从而加快后续调用native函数的效率。

一般而言,对应native层的函数名是:Java_类名_方法名

显示注册需要在native层的代码中调用如下三个函数:

(*env)->RegisterNatives(env, clazz, methods, methodsLenght):这个函数用于手动手动注册native方法。clazz就是需要注册native方法的那个类,methods是一个结构体包含name(Java中函数的名字)、signature(用字符串描述了函数的参数和返回值)、fnPtr(函数指针,指向C函数)。

jint JNI_OnLoad(JavaVM* vm, void* reserved):在这个函数里面执行上面的注册函数。

void JNI_OnUnload(JavaVM* vm, void* reserved)

反调试检测

ptrace机制有一个特点:如果一个进程被调试了,在它进程的status文件中有一个字段TracerPid会记录调试者的进程id值。通过判断这个TracerPid的值是否为0,就可以判断是否被调试中,从而实现反调试。

Android中常用权限分析

辅助功能权限

Android提供了辅助(Accessibility)功能和服务帮助用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。辅助功能实质上就是监听应用窗口变化和事件。

使用辅助功能的时候必须声明以下权限:

1
2
3
4
5
6
7
8
9
<application>
<service android:name=".MyAcessibilityService"
android:label="@string/accessibility_service_label">
<intent-filter>
<action android:name="android.accessibilityservice.AcessibilityService" />
</intent-filter>
</service>
<uses-permisson android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>
</application>

在代码中直接打开相应的Intent跳转到授权界面:

1
2
Intent accIntent = new Intent(Setting.ACTION_ACCESSIBILITY_SERVICE);
startActivity(accIntent);

存在的风险:可以监听设备当前的窗口变化,分析当前应用的View结构之后模拟点击:

  • 模拟一些社交App的登录窗口页面,这样用户是无感知的。
  • 当监听到当前应用是社交App,而且是聊天记录页面时,就可以得知当前聊天记录。
  • 分析设备中的应用情况,在用户不知情的情况下,模拟点击任何一个应用进行操作,从而获取信息。

设备管理权限

作用类似于iPhone中的“查找我的手机”功能,可以快速定位以及擦除设备数据等。另外还可以防止被卸载。

存在的风险:申请这个权限后,设备相当于是给申请者管理了,他们可以修改设备的锁屏密码、擦除数据等。

通知栏管理权限

这个功能专门用于监听设备的通知栏消息。

存在的风险:与辅助功能一样,申请这个权限后用户设备的通知栏消息就会被它接管。

VPN开发权限

存在的风险:如果申请了这个权限,就代表这个设备的网络访问消息会被申请者接管。

Android中的run-as命令

run-as命令的作用是可以用root身份运行命令,但是必须携带指定应用包名参数,而且这个应用是debug模式的。即开发中如果想看自己应用的沙盒数据(/data/data/xxx/),在设备没有root的情况下,可以使用这个命令进行查看。

命令分析和使用

使用run-as [packagename]后即可进入相应的目录:/data/data/packagename/。

Android中命令一般都在/system/bin和/system/xbin目录下,对应的源码都在Android源码目录下的/system/core下。

run-as命令运行是有诸多限制的:

  • 运行命令的用户id只能是shell和root用户

  • 应用的安装必须合法

  • 应用的uid必须合法

  • 应用是否为debug模式

Linux中的setuid和setgid概念

Linux/Unix下的可执行文件一旦被设置了setuid标记,使用该可执行程序的进程就将拥有该执行文件的所有者权限。

Android中setuid和setgid的使用场景

zygote降级处理

zygote启动后,会进入一个轮询使用socket来监听ActivityManagerService发来的消息,比如应用的启动,zygote就fork出一个进程,开始运转。因为zygote是root用户的,所以zygote通过setuid、setgid进行降级处理,避免所有fork出来的程序都是root权限。

su工具原理

Android手机的root原理是:一个普通进程通过执行su,从而获得一个具有root权限的进程。通过配合superuser这款app,可以在需要时对应用进行授权,从而避免了root权限的滥用。

run-as命令

run-as命令本身的uid是root,gid是shell。

run-as命令的作用

在Android中要调试一个程序,首先这个程序必须是debug模式的,也就是在AndroidManifest.xml中设置的属性,所以一般是修改XML中的debug属性,然后才进行代码关联调试。

Android中的allowBackup属性

allowBackup属性介绍

Android API Level 8及以上Android系统提供了位应用程序数据的备份和恢复功能,此功能的开关由应用程序中AndroidManifest.xml文件中的allowBackup属性值决定,其默认为true,可通过adb backupadb restore命令对应用数据进行备份和恢复。

这个属性的安全风险源于adb backup命令容许任何一个能够打开USB调试开关的人从Android手机中复制应用数据到外设,一旦应用数据被备份后,所有应用数据都可被用户读取;adb restore容许用户指定一个回复的数据,来源来恢复应用程序数据的创建。

Android中的签名机制

基本概念

数据摘要

数据摘要其实是一种算法,就是对一个数据源进行一个算法操作之后得到一个摘要,也叫作数据指纹。

消息摘要算法是一种能产生特殊输出格式的算法,其原理是根据一定的运算规则对原始数据进行某种形式的消息提取,提取出的信息就称为原始数据的数据摘要。

签名文件和证书文件

签名文件和证书文件是成对出现的,二者不可分离。

jarsign和signapk工具

这两个是Android中的签名工具。jarsign是用于jar签名,signapk是用于apk签名。

Android中的签名流程

signapk的源码位于:Android源码中的 com/android/signapk/sign.java。

Android签名apk后,会有一个META-INF目录,这里有三个文件:MANIFEST.MF、CERT.SF、CERT.RSA。

MANIFEST.MF

这个文件主要包括apk中所有文件的数据摘要内容。

CERT.SF

这个文件先对之前的MANIFEST.MF整个内容做一个SHA1方法哦SHA1-Digest-Manifest字段中。

然后逐条计算MANIFEST.MF文件中每一个块的SHA1,并进过BASE64编码后,记录在CERT.SF同名块中,属性名是SHA1-Digest。

CERT.RSA

这个文件就是对前面CERT.SF文件做签名操作后的结果,也就是前面提到的签名文件。