1. root 检测
public class Root_check {
private static String LOG_TAG = "Wooo";
public static void checkRoot() {
try {
/* 源码 adb.c 内
int adb_main(int is_daemon)
{
......
property_get("ro.secure", value, "");
if (strcmp(value, "1") == 0) {
// don't run as root if ro.secure is set...
secure = 1;
......
}
if (secure) {
......
*/
Object obj = utils.invokeStaticMethod("android.os.SystemProperties", "get", new Class[]{String.class}, new Object[]{"ro.secure"}); // ro.secure service.adb.root
Log.i("Wooo", "checkRoot -> " + obj);
if (obj != null) {
if (obj.equals("1")) {
Log.i("Wooo", "checkRoot may not root");
}
if (obj.equals("0")) {
Log.i("Wooo", "checkRoot mast rooted");
}
}
checkRelease();
checkSUfile();
// checkRootWhichSU(); // 执行 which su
} catch (Exception e) {
e.printStackTrace();
}
}
private static void checkRelease() {
String buildTags = Build.TAGS;
Log.i("Wooo", "cheeckRelease tag is : " + buildTags);
if (buildTags != null && buildTags.contains("test-keys")) {
Log.i("Wooo", "cheeckRelease -> debug");
}
if (buildTags != null && buildTags.contains("release-keys")) {
Log.i("Wooo", "cheeckRelease -> release");
}
}
private static void checkSUfile() {
// "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su"
String file[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/", "/su/bin/"};
for (int i = 0; i < file.length; i++) {
String sNm = file[i] + "su";
File f = new File(sNm);
if (f.exists()) {
Log.i("Wooo", "checkRoot " + sNm + " file exists");
} else {
Log.i("Wooo", "checkRoot " + sNm + " file no exists");
}
}
}
public static boolean checkRootWhichSU() {
String[] strCmd = new String[] {"/system/xbin/which","su"};
ArrayList<String> execResult = executeCommand(strCmd);
if (execResult != null){
Log.i("Wooo","execResult="+execResult.toString());
return true;
}else{
Log.i("Wooo","execResult=null");
return false;
}
}
private static ArrayList<String> executeCommand(String[] shellCmd){ // 执行 linux 的 shell 命令
String line = null;
ArrayList<String> fullResponse = new ArrayList<String>();
Process localProcess = null;
try {
Log.i(LOG_TAG,"to shell exec which for find su :");
localProcess = Runtime.getRuntime().exec(shellCmd);
} catch (Exception e) {
return null;
}
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
try {
while ((line = in.readLine()) != null) {
Log.i(LOG_TAG,"–> Line received: " + line);
fullResponse.add(line);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.i(LOG_TAG,"–> Full response was: " + fullResponse);
return fullResponse;
}
}
2. Xposed 检测
public class Xposed_check {
private static String TAG = "Wooo Xposed";
private static StringBuffer sb = new StringBuffer();
// https://blog.csdn.net/jiangwei0910410003/article/details/80037971
// https://www.52pojie.cn/thread-691584-1-1.html
// https://tech.meituan.com/android_anti_hooking.html
// https://segmentfault.com/a/1190000009976827
public static void checkXposed(Context ctx) {
checkCache();
checkJarClass();
checkJarFile();
disableHooks();
checkMaps();
checkPackage(ctx);
checkException();
}
private static void checkPackage(Context ctx) {
PackageManager packageManager = ctx.getPackageManager();
List<ApplicationInfo> applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : applicationInfoList) {
if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
Log.i(TAG, "found xposed package installed");
}
}
}
private static void checkException() {
try {
throw new Exception("xppp");
} catch (Exception e) {
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge")) { // stackTraceElement.getMethodName()
Log.i(TAG, "found exception of xposed");
}
}
}
}
/*
bool is_xposed()
{
bool rel = false;
FILE *fp = NULL;
char* filepath = "/proc/self/maps";
...
string xp_name = "XposedBridge.jar";
fp = fopen(filepath,"r"))
while (!feof(fp))
{
fgets(strLine,BUFFER_SIZE,fp);
origin_str = strLine;
str = trim(origin_str);
if (contain(str,xp_name))
{
rel = true; //检测到Xposed模块
break;
}
}
...
}
*/
private static void checkMaps() {
String jarName = "XposedBridge.jar";
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
while (true) {
String str = bufferedReader.readLine();
if (str == null) {
break;
}
if (str.endsWith("jar")) {
if (str.contains(jarName)) {
Log.i(TAG, "proc/pid/maps find Xposed.jar -> " + str);
}
}
// if (str.contains("hack|inject|hook|call")) { // 检测 maps 内的关键字
//
// }
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void checkCache() {
ClassLoader cl = ClassLoader.getSystemClassLoader();
String xpHelper = "de.robv.android.xposed.XposedHelpers";
Log.i(TAG, "checkCache IN");
try {
Object XPHelpers = cl.loadClass(xpHelper).newInstance(); // 在 nexus6 的 7.1 系统上获取失败,抛出异常
if (XPHelpers != null) {
filterField(XPHelpers, "fieldCache");
filterField(XPHelpers, "methodCache");
filterField(XPHelpers, "constructorCache");
} else {
Log.i(TAG, "cannot find Xposed framework");
}
Log.i(TAG, "cache content -> " + sb.length() + " -> " + sb);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void filterField(Object xpHelper, String cacheName) {
try {
Field f = xpHelper.getClass().getDeclaredField(cacheName);
f.setAccessible(true);
HashMap caMap = (HashMap)f.get(xpHelper);
if (caMap == null) {
return;
}
Set caSet = caMap.keySet();
if (caSet.isEmpty()) {
return;
}
Log.i(TAG, "filter -> " + cacheName + " , size -> " + caSet.size());
Iterator iterator = caSet.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
Log.i(TAG, "filter key -> " + key);
if (key == null) {
continue;
}
key = key.toLowerCase();
if (key.length() <= 0) {
continue;
}
if (key.startsWith("android.support")) {
continue;
}
if (key.startsWith("javax.")) {
continue;
}
if (key.startsWith("android.webkit")) {
continue;
}
if (key.startsWith("java.util")) {
continue;
}
if (key.startsWith("android.widget")) {
continue;
}
if (key.startsWith("sun.")) {
continue;
}
sb.append(key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void checkJarFile() {
File f = new File("/system/framework/XposedBridge.jar");
if (f.exists()) {
Log.i(TAG, "system may installed Xposed find jar file");
} else {
Log.i(TAG, "system not install Xposed cannot find jar file");
}
}
private static void checkJarClass() {
try {
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class clazz = cl.loadClass("de.robv.android.xposed.XposedBridge");
if (clazz != null) {
Log.i(TAG, "system installed Xposed Class");
} else {
Log.i(TAG, "system not install Xposed Class");
}
} catch (Exception e) {
e.printStackTrace();
}
}
// disable
private static void disableHooks() {
Object obj2 = utils.getStaticFieldOjbectCL("de.robv.android.xposed.XposedBridge", "disableHooks");
Log.i(TAG, "disableHooks seted -> " + obj2);
Log.i(TAG, "disableHooks seted ");
utils.setStaticOjbectCL("de.robv.android.xposed.XposedBridge", "disableHooks", true);
Object obj = utils.getStaticFieldOjbectCL("de.robv.android.xposed.XposedBridge", "disableHooks");
Log.i(TAG, "disableHooks seted -> " + obj);
}
}
3. Cydia 检测
public class Cydia_check {
private static String TAG = "Wooo Cydia";
public static void checkCydia() {
checkMaps();
}
/* 当检测到有对应的 so 文件后,然后根据路径去获取对应的函数地址,如果能获取,说明被加载。有 9 个导出函数。
void* dlopen = lookup_symbol("/data/app-lib/com.saurik.substrate-2/libsubstrate-dvm.so", "MSJavaHookMethod");
void* lookup_symbol(char* libraryname,char* symbolname)
{
void *imagehandle = dlopen(libraryname, RTLD_GLOBAL | RTLD_NOW);
if (imagehandle != NULL){
void * sym = dlsym(imagehandle, symbolname);
if (sym != NULL){
return sym; //发现Cydia Substrate相关模块
}
...
}
*/
private static void checkMaps() {
String so1 = "libsubstrate.so";
String so2 = "libsubstrate-dvm.so";
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
while (true) {
String str = bufferedReader.readLine();
if (str == null) {
break;
}
if (str.endsWith("so")) {
if (str.contains(so1)) {
Log.i(TAG, "proc/pid/maps find libsubstrate.so -> " + str);
}
if (str.contains(so2)) {
Log.i(TAG, "proc/pid/maps find libsubstrate_dvm.so -> " + str);
}
}
// if (str.contains("hack|inject|hook|call")) { // 检测 maps 内的关键字
//
// }
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 第二种方法:通过检测/proc/self/maps下的加载so库列表得到各个库文件绝度路径,通过fopen函数将so库的内容以16进制读进来放在内存里面进行规则比对,采用字符串模糊查找来检测是否命中黑名单中的方法特征码。
// 参考美团:https://tech.meituan.com/android_anti_hooking.html
}