一、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);
};
});