您的位置:

App检测全方位剖析

一、App检测设备

为了保障软件的安全性,开发者需要对使用环境进行检测,其中设备型号、操作系统版本、是否越狱等都是非常重要的因素。

在iOS设备上,可以使用UIDevice类获取各种硬件和软件信息,如设备类型、iOS版本、是否越狱等。在Android平台上,常用的设备检测方法是检测是否具有root权限,若有则认为该设备不安全。


// iOS设备检测示例代码
- (BOOL)isJailBroken {
    BOOL jailBroken = NO;
    NSString *cydiaPath = @"/Applications/Cydia.app";
    NSString *aptPath = @"/private/var/lib/apt/";
    if ([[NSFileManager defaultManager] fileExistsAtPath:cydiaPath]) {
        jailBroken = YES;
    }
    if ([[NSFileManager defaultManager] fileExistsAtPath:aptPath]) {
        jailBroken = YES;
    }
    return jailBroken;
}


// Android设备检测示例代码
private boolean isRoot() {
    Process process = null;
    try {
        process = Runtime.getRuntime().exec("/system/xbin/which su");
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String suPath = in.readLine();
        if (suPath != null) {
            return true;
        }
        return false;
    } catch (Throwable e) {
        return false;
    } finally {
        if (process != null) {
            process.destroy();
        }
    }
}

二、App检测Root不让使用

检测到设备已经root后,有一些应用会强制退出,或者拒绝继续运行。

这里介绍两种比较常见的检测方式:一种是检测root文件系统的一些文件,如 /system/bin/su、/su/bin/su、/magisk,如果文件存在则认为设备已经被root。另一种方式是使用Xposed框架,Hook掉进程和文件,从而可以绕过检测。


// 检测文件是否存在,判断设备是否已root
private boolean isRoot() {
    String[] paths = {"/system/bin/su", "/system/usr/su", "/system/xbin/su",
     "/system/su", "/sbin/su", "/vendor/bin/su", "/su/bin/su", "/magisk"};
    for (String path : paths) {
        if (new File(path).exists()) {
            return true;
        }
    }
    return false;
}


// 使用Xposed框架,绕过Root检测示例代码
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable{
    if (lpparam.packageName.equals("com.app")) {
        findAndHookMethod("android.os.SystemProperties", lpparam.classLoader, "get", String.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                if ("ro.build.selinux".equals(param.args[0])) {
                    param.setResult("Permissive");
                }
                super.beforeHookedMethod(param);
            }
        });
    }
}

三、App检测代理

在网络安全中,代理被广泛使用,以保护用户的隐私和安全。但是有些应用会禁止使用代理,此时我们需要进行代理检测,确保代理不被应用检测到。

常用的检测方式是检测当前应用的网络请求是否经过了代理,以及检测当前设备是否开启了系统级代理。如果经过代理,则直接拦截请求,否则直接发送请求。


// 检测代理是否开启示例代码
private boolean isProxyEnabled() {
    String proxyHost = System.getProperty("http.proxyHost");
    String proxyPort = System.getProperty("http.proxyPort");
    return !TextUtils.isEmpty(proxyHost) && !TextUtils.isEmpty(proxyPort);
}


// 设置系统级代理示例代码
public void setProxy() {
    System.setProperty("http.proxyHost", "192.168.1.1");
    System.setProperty("http.proxyPort", "8888");
}

四、App检测平台

有些应用只开放了特定平台的下载和使用,例如iOS应用只能在iOS设备上下载。为了安装和使用这些应用程序,我们可以通过模拟平台信息的方式来绕过平台检测,让应用检测到我们的设备为目标平台设备。


// Java代码模拟iOS设备
System.setProperty("http.agent", "Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us)\n" +
        " AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10");


// Android代码模拟iOS设备
WebView webView = new WebView(context);
WebSettings settings = webView.getSettings();
settings.setUserAgentString("Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us)\n" +
        " AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10");

五、App检测不到NFC

NFC作为近场通讯技术,现在已经被广泛应用于移动设备支付、门禁卡等领域,但是有些应用会进行NFC检测,如果检测到设备不支持NFC,则会禁止应用使用。

针对这种情况,我们可以通过hook掉检测函数,绕过NFC检测。


// Hook掉NFC检测函数示例代码
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable{
    if (lpparam.packageName.equals("com.app")) {
        findAndHookMethod("com.app.NFCManager", lpparam.classLoader, "checkNFC", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                return true;
            }
        });
    }
}

六、App检测WiFi连接

在有些应用中,需要在特定的WiFi环境下使用,例如内部WiFi。此时如果WiFi连接不上,则应用会直接崩溃或强制退出。

解决这个问题的方法比较简单,只需要将设备的WiFi模块关闭,或者截获应用检测WiFi的函数,使其直接返回有连接的状态。


// 关闭WiFi模块示例代码
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(false);


// Hook掉WiFi检测函数示例代码
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
    if (lpparam.packageName.equals("com.app")) {
        XposedHelpers.findAndHookMethod("com.app.WifiUtils", lpparam.classLoader,
        "isWifiConnected", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                return true;
            }
        });
    }
}

七、App检测投屏怎么办

有些应用会检测设备是否进行了投屏操作,如果发现投屏则会强制退出或者提示不允许投屏。

针对这种情况,我们可以使用Xposed插件来hook掉投屏检测函数,或者使用投屏模块来伪装不进行投屏操作。


// Hook掉投屏检测函数示例代码
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable{
    if (lpparam.packageName.equals("com.app")) {
        findAndHookMethod("com.app.CastManager", lpparam.classLoader, "isCasting", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                return false;
            }
        });
    }
}

八、App检测代理不能抓包

在进行移动应用安全测试时,我们需要使用抓包工具来分析应用的数据流程,但是有些应用会禁止使用代理抓包工具,因此我们需要使用代理绕过工具来绕过该检测。


// 使用Frida抓包绕过代理检测示例代码
var MyClass = Java.use('com.app.MyClass');
MyClass.getHttp = function (str) {
    Java.use("java.net.Proxy").$init(Java.use("java.net.Proxy$Type").DIRECT,
                                      Java.use("java.net.InetSocketAddress").$new("127.0.0.1", 8888));
    return this.getHttpNoProxy(str);
}

九、App检测Frida原理

Frida是一款非常强大的动态分析工具,可以帮助我们分析应用的数据流程、函数调用、加密算法等。但是有些应用会对Frida进行检测,如果检测到Frida则会强制退出。

为了绕过这个检测,我们可以通过hook掉检测函数、反射修改值、修改寄存器等方法来绕过Frida检测。


// 使用Frida hook掉Frida检测函数
var MyClass = Java.use('com.app.MyClass');
MyClass.checkFrida = function () {
    return false;
}


// 使用Frida反射修改Frida检测值示例代码
Java.use("com.app.MyClass").__staticInitializer__.overload().implementation = function() {
    var MyClass = Java.use("com.app.MyClass");
    var fild = MyClass.class.getDeclaredField("isFrida");
    fild.setAccessible(true);
    fild.set(null, false);
}

十、App检测抓包闪退

在进行应用抓包时,有时会遇到抓包软件崩溃或者强制退出的情况,这种情况往往是由抓包软件的检测或者应用的检测导致的。

解决这种问题的方法比较简单,我们可以使用一些抓包模块,或者通过hook掉检测函数和调用函数等方法来绕过检测。


// 使用Xposed hook掉crash检测函数示例代码
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
    if (lpparam.packageName.equals("com.app")) {
        XposedHelpers.findAndHookMethod("com.app.CrashManager", lpparam.classLoader,
                "checkCrash", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                return false;
            }
        });
    }
}


// 使用Frida防止应用检测抓包模块
Java.perform(function() {
    var System = Java.use('java.lang.System');
    System.loadLibrary.implementation = function(library) {
        try {
            if (library.includes('charles')) {
                return false;
            }
        } catch(e) {}
        return this.loadLibrary.call(this, library);
    };
});