MASTG-KNOW-0027: Detecção de Root
No contexto de anti-engenharia reversa, o objetivo da detecção de root é tornar a execução do aplicativo em um dispositivo root um pouco mais difícil, o que por sua vez bloqueia algumas das ferramentas e técnicas que os engenheiros reversos gostam de usar. Como a maioria das outras defesas, a detecção de root não é muito eficaz por si só, mas implementar várias verificações de root espalhadas por todo o aplicativo pode melhorar a eficácia do esquema geral de anti-manipulação.
Para Android, definimos "detecção de root" de forma um pouco mais ampla, incluindo a detecção de ROMs personalizadas, ou seja, determinar se o dispositivo é uma compilação Android padrão ou uma compilação personalizada.
A detecção de root também pode ser implementada por meio de bibliotecas como RootBeer.
Verificações de Existência de Arquivos¶
Talvez o método mais amplamente utilizado de detecção programática seja verificar a existência de arquivos normalmente encontrados em dispositivos root, como arquivos de pacote de aplicativos de root comuns e seus arquivos e diretórios associados, incluindo os seguintes:
/system/app/Superuser.apk
/system/etc/init.d/99SuperSUDaemon
/dev/com.koushikdutta.superuser.daemon/
/system/xbin/daemonsu
O código de detecção também frequentemente procura por binários que geralmente são instalados após o dispositivo ter sido root. Essas buscas incluem verificar a presença do busybox e tentar abrir o binário su em diferentes localizações:
/sbin/su
/system/bin/su
/system/bin/failsafe/su
/system/xbin/su
/system/xbin/busybox
/system/sd/xbin/su
/data/local/su
/data/local/xbin/su
/data/local/bin/su
Verificar se su está no PATH também funciona:
public static boolean checkRoot(){
for(String pathDir : System.getenv("PATH").split(":")){
if(new File(pathDir, "su").exists()) {
return true;
}
}
return false;
}
As verificações de arquivos podem ser facilmente implementadas tanto em Java quanto em código nativo. O seguinte exemplo JNI (adaptado de rootinspector) usa a chamada de sistema stat para recuperar informações sobre um arquivo e retorna "1" se o arquivo existir.
jboolean Java_com_example_statfile(JNIEnv * env, jobject this, jstring filepath) {
jboolean fileExists = 0;
jboolean isCopy;
const char * path = (*env)->GetStringUTFChars(env, filepath, &isCopy);
struct stat fileattrib;
if (stat(path, &fileattrib) < 0) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NATIVE: stat error: [%s]", strerror(errno));
} else
{
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NATIVE: stat success, access perms: [%d]", fileattrib.st_mode);
return 1;
}
return 0;
}
Execução de Comandos Privilegiados¶
Outra maneira de determinar se su existe é tentar executá-lo através do método Runtime.getRuntime.exec. Uma IOException será lançada se su não estiver no PATH. O mesmo método pode ser usado para verificar outros programas frequentemente encontrados em dispositivos root, como busybox e os links simbólicos que normalmente apontam para ele.
Verificação de Processos em Execução¶
O Supersu - de longe a ferramenta de root mais popular - executa um daemon de autenticação chamado daemonsu, portanto a presença desse processo é outro sinal de um dispositivo root. Os processos em execução podem ser enumerados com as APIs ActivityManager.getRunningAppProcesses e manager.getRunningServices, o comando ps e navegando pelo diretório /proc. Segue um exemplo implementado em rootinspector:
public boolean checkRunningProcesses() {
boolean returnValue = false;
// Obtém processos de aplicativos atualmente em execução
List<RunningServiceInfo> list = manager.getRunningServices(300);
if(list != null){
String tempName;
for(int i=0;i<list.size();++i){
tempName = list.get(i).process;
if(tempName.contains("supersu") || tempName.contains("superuser")){
returnValue = true;
}
}
}
return returnValue;
}
Verificação de Pacotes de Aplicativos Instalados¶
Você pode usar o gerenciador de pacotes do Android para obter uma lista de pacotes instalados. Os seguintes nomes de pacote pertencem a ferramentas de root populares:
eu.chainfire.supersu
com.noshufou.android.su
com.koushikdutta.superuser
com.zachspong.temprootremovejb
com.ramdroid.appquarantine
com.topjohnwu.magisk
Verificação de Partições Graváveis e Diretórios do Sistema¶
Permissões incomuns em diretórios do sistema podem indicar um dispositivo personalizado ou root. Embora os diretórios do sistema e de dados normalmente sejam montados como somente leitura, às vezes você os encontrará montados como leitura-gravação quando o dispositivo está root. Procure por esses sistemas de arquivos montados com a flag "rw" ou tente criar um arquivo nos diretórios de dados.
Verificação de Compilações Android Personalizadas¶
Verificar sinais de compilações de teste e ROMs personalizadas também é útil. Uma maneira de fazer isso é verificar a tag BUILD por test-keys, que normalmente indica uma imagem Android personalizada. Verifique a tag BUILD da seguinte forma:
private boolean isTestKeyBuild()
{
String str = Build.TAGS;
if ((str != null) && (str.contains("test-keys")));
for (int i = 1; ; i = 0)
return i;
}
A ausência de certificados Google Over-The-Air (OTA) é outro sinal de uma ROM personalizada: em compilações Android padrão, as atualizações OTA usam certificados públicos do Google.