本小节将结合APP代码安全的运行环境、防反编译、防篡改、防调试、防注入5个过程中涉及的安全问题 进行测评,判定APP是否符合安全要求。如果符合,则本项测试结果为“通过”;否则为“不通过”,同时给出本项安全的修复建议,让开发者做到提前防护。
1、Android root环境检测
检测目的
检测APP运行是否对Android ROOT环境运行检测。
检测方法与步骤
(1)将被测APP安装在Android ROOT的运行环境中;
(2)运行APP,确认是否能够正常运行并提示用户。
测试方法1:反编译APP源代码,查看是否存在检测root运行环境的代码。通常要在root设备上找到su文件,文件检测是最常见的检测方法,包括检查busybox并尝试打开su文件,具体操作如下。
(1)查看系统是否是测试版
我们可以查看发布的系统版本,是test-keys(测试版),还是release-keys(正式版)。
可以先在adb shell中运行下命令查看:
这个返回结果“release-keys”,代表此系统是正式版。
在代码中的检测方法如下:
public static boolean checkDeviceDebuggable() {
String buildTags = android.os.Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
Log.i(LOG_TAG, "buildTags=" + buildTags);
return true;
}
return false;
}
若是非官方发布版,很可能是完全root的版本,存在使用风险。
(2)检查是否存在Superuser.apk
Superuser.apk是一个被广泛使用的用来root安卓设备的软件,所以可以检查这个app是否存在。检测方法如下:
public static boolean checkSuperuserApk() {
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
Log.i(LOG_TAG,"/system/app/Superuser.apk exist");
return true;
}
} catch (Exception e) {
}
return false;
}
(3)检查su命令
su是Linux下切换用户的命令,在使用时不带参数,就是切换到超级用户。如下如图所示:
通常我们获取root权限,就是使用su命令来实现的,所以可以检查这个命令是否存在。这样,系统就会在PATH路径中搜索su,如果找到,就会执行,执行成功后,就是获取到真正的超级权限了。
public static synchronized boolean checkGetRootAuth() {
Process process = null;
DataOutputStream os = null;
try {
Log.i(LOG_TAG, "to exec su");
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes("exit\n");
os.flush();
int exitValue = process.waitFor();
Log.i(LOG_TAG, "exitValue=" + exitValue);
if (exitValue == 0) {
return true;
} else {
return false;
}
} catch (Exception e) {
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
+ e.getMessage());
return false;
} finally {
try {
if (os != null) {
os.close();
}
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这种检测su的方法应该是最靠谱的,不过,也有个问题,就是在已经root的设备上,会弹出提示框,请求给app开启root权限。
测试方法2:在真机中安装并运行APP,查看APP是否对root运行环境进行检测,是否提示用户APP在不安全的环境下运行,让运行选择,root环境检测结果如下:
检测结论
在步骤(1)后,如果APP如法安装,或在步骤(2)后,如果APP在root系统环境中运行,在root环境提示,则本项检测结果为“通过”,否则为“不通过”。
修复建议
通过多种方法检测root环境,如果发现APP在root环境中运行,弹窗提示用户APP在不安全环境下运行,或者禁止安装和运行。
2.Android模拟器环境检测
检测目的
检测APP运行时,是否对Android模拟器环境运行检测。
检测方法与步骤
(1)将被测APP安装在Android模拟器中;
(2)运行APP,确认是否能够正常运行并提示用户。
在模拟器中安装并运行APP,一旦发现运行环境是模拟器,将提示用户请勿在模拟器、虚拟机中运行APP。
检测结论
在步骤(1)后,如果APP无法安装,或在步骤(2)后,APP在模拟器上无法运行,并存在模拟器检测提示,则本项测试结果为“通过”,否则为“不通过”。
修复建议
APP运行时对模拟器环境进行检测,如果发现在模拟器环境中运行,弹窗提示用户APP在不安全环境下运行,或者禁止在模拟器中安装或者运行。
3.Android挂钩框架环境检测
检测目的
检测APP是否存在逆向框架检测机制。
检测方法与步骤
(1)将被测APP安装在装有逆向框架的环境中;
(2)运行APP,确认是否能够正常运行并提示用户。
测试方法1:反编译APP源代码,查看在APP代码中是否存在检测逆向框架的代码。用packageManager类检测包名来判断是否安装了逆向框架。
if(item.packageName.equals("de.robv.android.xposed.installer")){
Log.d("HookTest","检测到您的设备已安装exposed框架");
}
if(item.packageName.equals("com.saurik.substrate")){
Log.d("HookTest","检测到您的设备已安装substrate框架");
}
测试方法2:在已安装逆向框架的手机上安装并运行APP,查看是否提示用户APP在不安全环境下运行。开启xpoesd如果打不开APP或者无法正常使用 ,就是做了xpoesd检测。
检测结论
在步骤(1)后,如果APP无法安装,或在步骤(2)后,如果存在逆向框架的检测提示,则本项测试结果为“通过”,否则为“不通过”。
修复建议
在APP代码中实现检测逆向框架的机制,一旦检测到手机安装了逆向框架,弹出提示告知用户运行环境不安全,让用户自己选择是否继续操作。
1.反编译工具检测
检测目的
检测APP是否可以防止反编译工具,是否具有防逆向保护措施。
检测方法与步骤
(1)通过反编译工具对apk文件进行反编译,查看是否具有防逆向保护措施。
JADX是一款非常好用的反编译的工具jadx。jadx的功能非常强大,可以满足日常反编译需求。jadx优点:
图形化的界面。
拖拽式的操作。
反编译输出java代码。
导出Gradle工程。
将apk文件拖到jadx中,如果APP没有防逆向保护措施,就会出现反编译的源代码,如下图所示。
(2)通过IDAPro等反汇编工具对动态库so文件进行反汇编,查看APP是否具有防反汇编的能力。
为了提高APP的安全性,通常会将核心功能放在Native层实现,因此,防止动态库反汇编也是非常有必要的,常用的防范方法是将so文件导出函数加密。
加密前动态库so反汇编:
检测结论
在步骤(2)后,如果APP的dex文件和so文件无法正常反编译或者APP经过加固处理,则本项测试结果为“通过”,否则为“不通过”。
修复建议
对APP文件结构进行变形或加密,让反编译工具无法识别,或者对APP文件进行加固处理。
2.代码混淆检测
检测目的
检测APP反编译后的源代码是否经过混淆处理。
检测方法与步骤
通过反编译工具对APP进行反编译,查看代码中的类、字段和方法是否经过混淆处理。
检测结论
如果反编译后的源代码的类、字段和方法使用a、b、c、d等无意义的字符重命名,如下图,则本项测试结果为“通过”,否则为“不通过”。
修复建议
对APP源代码进行混淆。
3.混淆强度检测
检测目的
检测APP反编译后源代码的混淆强度,查看是否能够有效地保护代码安全。
检测方法与步骤
(1)检测dex文件代码中所有的类名、函数名、字段、方法,是否都经过混淆处理,例如反编译后无法正常识别java层函数功能。
(2)检测so文件中所有的类名、函数名、字段、方法,是否都进行了混淆处理,例如反汇编后无法正常识别native层函数功能。
检测结论
在步骤(2)后,如果反编译后代码能够识别出APP函数的功能,如下图所示,则本项测试结果为“不通过”,否则为“通过”。
修复建议
这对dex文件和so文件的类名、函数名、字段、方法进行高强度混淆。
4.关键代码(敏感段落和数据保护)检测
检测目的
检测APP是否对关键代码和数据实施有效的保护措施,是否暴露业务逻辑。
检测方法与步骤
通过反编译工具对APP进行反编译,结合mainfest.xml配置文件,份子APP注册,登录、支付过程、加密过程、数据通信等关键功能代码,查看相关代码逻辑是否有明显的暴露。
对APP进行反编译后,如果APP没有对关键代码和数据进行保护,相关业务字符串将会以明文显示,容易暴露业务逻辑,如下图所示:
检测结论
如果关键代码未暴露,并且关键数据经过加密和隐藏保护处理,则本项测试结果为“不通过”,否则为“通过”。
修复建议
将APP关键代码进行隐藏、混淆、加壳等处理,从而无法逆向出重要的代码信息。
1.程序文件防篡改检测
检测目的
检测APP启动时是否进行了完整性的校验,是否对客户端代码、资源文件进行修改,是否具有防篡改机制。
检测方法与步骤
(1)使用反编译工具AndroidKiller对目标文件进行反编译。用AndroidKiller打开目标文件,可自动反编译apk。
(2)修改相关代码,篡改AndroidManifest.xml、assets文件、res文件配置文件等。例如,修改login.html文件,将登录按钮,改为登录test,如下图所示:
(3)点击android-编译按钮,重新打包签名后再运行APP,查看结果。
检测结论
在步骤(3)后,如果APP签名能够正常进行,则本项测试结果为“不通过”,否则为“通过”。
修复建议
采用完整性校验技术对安装包进行校验,校验的对象包括原包中代码、资源文件、配置文件等所有文件,一旦校验失败,立即退出。
2.内存数据防篡改检测
检测目的
检测APP运行时,内存中的关键代码和敏感数据是否能被篡改。
检测方法与步骤
(1)将被测APP安装到被测的移动智能终端上,并与服务器端进行连接;
(2)动态分析调试代码逻辑,修改APP运行期间内存中的数据,查看运行结果,比如修改登录逻辑、任意密码是否能够登录成功。
检测结论
在步骤(2)后,如果在APP运行期间,能够动态篡改内存中的敏感数据,或者修改数据能够改变代码逻辑,则本项测试结果为“不通过”,否则为“通过”。
修复建议
增加内存保护措施,防止攻击者修改内存数据达到不正当的目的。
1.调试工具防护检测
检测目的
检测APP是否可以利用动态调试工具加载调试。
检测方法与步骤
(1)安装并运行APP,通过动态调试工具加载调试,查看是否可以正常调试。
例如,通过IDAPro动态调试APP的dex文件,查看是否存在调试工具防护功能,调试方法如下图所示。
(2)解压测试APP,把classes.dex拖到IDA Pro中,点击“OK”。
(3)接着,选择“Debugger”->“Debugger Options”->“Set specific options”,根据Androidmainfest文件找到该apk的入口
Activity“com.ecar.driver.ui.SplashActivity”进行设置,如图所示,设置好后点击“OK”。
(4)选择com.ecar.driver.ui.SplashActivity中的某一函数出下断点。
(5)在IDA中点击F9快捷键执行程序,程序会自动断到刚才下断点的地方。
注意,根Android官方文档,想调试apk中的dex代码,必须满足下面两个条件之一。
根据APP的manifest来进行,如果application项中包含了Android:debuggable="true",具有android:debuggable="true"属性,那么该apk就处于可调试状态。
当VM从framework中启动,系统属性ro.debuggable为1时,即可对系统中所有APP进行调试,也就是default.prop中ro.debuggbable的值为1.
检测结论
在步骤(1)后,如果APP能够被调试工具调试,则本项测试结果为“不通过”,否则为“通过”。
修复建议
增加反调试工具功能,通过判定查看TracerPid是否有数值,一旦发现TracerPid为0,就退出进程。
2.内存防护检测
检测目的
检测APP是否具有内存防护功能,例如防内存转储。
检测方法与步骤
启动APP,使用ps命令查看进程PID,使用gdb -p 命令挂载APP进程,再使用gcore指令转储内存。
如果内存有防护,那么转储将会失败。如果在内存转储过程中出现大量的警告,可以忽略警告,警告只是内存权限问题,不是内存转储防护。
检测结论
如果能够内存转储成功,生成corefile文件,则本项测试结果为“不通过”,否则为“通过”。
检测目的
通过监控/proc/pid/mem与/proc/pid/pagemep来防止内存转储。
1.进程保护检测
检测目的
检测APP进程空间是否可以被注入第三方动态库so文件。
检测方法与步骤
(1)将被测APP安装到被测移动智能测试终端,并启动应用进程;
(2)通过注入工具或脚本,将第三方动态库文件注入APP的进程空间,查看第三方so文件是否在进程的内存空间中。
检测结论
在步骤(2)后,如果第三方动态库能够注入目标进程空间,则本项测试结果为“不通过”,否则为“通过”。
修复建议
增加ptrace函数的检测功能,使第三方无法使用该函数附件进程。修改linker汇总的dlopen函数,防止第三方金慈宁宫so加载。定时检测APP加载的第三方so库,如果发现是被注入的so文件,程序进程立即报异常。空间是否可以被注入第三方动态库so文件。
检测方法与步骤
(1)将被测APP安装到被测移动智能测试终端,并启动应用进程;
(2)通过注入工具或脚本,将第三方动态库文件注入APP的进程空间,查看第三方so文件是否在进程的内存空间中。
检测结论
在步骤(2)后,如果第三方动态库能够注入目标进程空间,则本项测试结果为“不通过”,否则为“通过”。
修复建议
增加ptrace函数的检测功能,使第三方无法使用该函数附件进程。修改linker汇总的dlopen函数,防止第三方金慈宁宫so加载。定时检测APP加载的第三方so库,如果发现是被注入的so文件,程序进程立即报异常。
E
N
D
关
于
我
们
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。