【移动安全】程序代码安全测试方法
2023-1-29 00:2:57 Author: 白帽子(查看原文) 阅读量:22 收藏

本小节将结合APP代码安全的运行环境、防反编译、防篡改、防调试、防注入5个过程中涉及的安全问题 进行测评,判定APP是否符合安全要求。如果符合,则本项测试结果为“通过”;否则为“不通过”,同时给出本项安全的修复建议,让开发者做到提前防护。

1.1运行环境

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.2防反编译

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.3防篡改

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.4防调试

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.5防注入

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余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246484&idx=2&sn=bb30a8fb0b317e2dabbc699d306a5d52&chksm=82ea56fdb59ddfebcd6f66260b6fd90068133ccfb540f3b752eb879a6e5b64509f9f3dd49d14#rd
如有侵权请联系:admin#unsafe.sh