[Android] Xposed: Как скрыть наличие Xposed/Cydia
@webware
Привет,
Данная статья является логическим продолжением "Android/Xposed/Cydia: Ищем root и то, что может скрыть его", где я в прошлый раз рассказал как можно задетектить наличие root прав на девайсе. Сейчас рассмотрим то, как скрывать это для тех, кому это не нужно знать.
Все основные и часто используемые методы сокрытия su давно изучены и описаны, в том числе и кодом (хорошим примером является проект от Matt Joseph (devadvance) именуемый "rootcloak").
Но я хочу отдельно обратить ваше внимание на методе, который я описал в предыдущей статье и назвал "Xposed or Cydia". Его суть -- это поиск подстрок xposed или substrate в стектрейсе.
Первым шагом нам нужно составить список способов, как можно получить Stack Trace.
Да, как оказалось способов больше чем один и мы нашли 4 способа (если кто знает еще, велкам в комментарии)
getStackTraceвjava.lang.ThreadgetAllStackTracesвjava.lang.ThreadgetStackTraceвjava.lang.ThrowablegetStackTraceStringandroid.util.Log
Дальше нужно понять как мы будем хукать методы. На самом деле в инфицированном Zygote это не так то и сложно сделать, а Xposed Framework вообще облегчает эту задачу (огромным плюсом у этого фреймворка явлется понятная и обширная документация, которая укажет нам на findAndHookMethod)
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
Остается дело за малым -- написать код, который будет отлавливать все обращения в stack trace и вычищать из него все упоминания о xposed.
Покажу на примере getAllStackTraces в java.lang.Thread
findAndHookMethod("java.lang.Thread", lpparam.classLoader, "getAllStackTraces", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Map<Thread, StackTraceElement[]> services = (Map<Thread, StackTraceElement[]>)param.getResult();
Map<Thread, StackTraceElement[]> newTh = new HashMap<>();
for (Thread th : services.keySet()) {
List<StackTraceElement> stackTraceElementsList = new LinkedList<>(Arrays.asList((StackTraceElement[])services.get(th)));
// XposedBridge.log("---BEFORE=== " + stackTraceElementsList.toString());
Iterator<StackTraceElement> iter = stackTraceElementsList.iterator();
StackTraceElement stackTraceElement;
while (iter.hasNext()) {
stackTraceElement = iter.next();
if (stackTraceElement == null) {
continue;
}
if (stackTraceElement.getClassName().contains("xposed") ||
(stackTraceElement.getFileName() != null &&
stackTraceElement.getFileName().toLowerCase().contains("xposed"))) {
iter.remove();
}
}
// XposedBridge.log("===AFTER=== " + stackTraceElementsList.toString());
newTh.put(th, stackTraceElementsList.toArray(new StackTraceElement[stackTraceElementsList.size()]));
}
param.setResult(newTh);
}
});
Все остальные способы ±похожи, но для того, чтобы понять суть решения этого более, чем достаточно.
Как вы уже поняли, Xposed это очень сильный инструмент, который позволяет сделать очень многое. Если у вас остались вопросы, велкам в комментарии.