Introduction à Abyss
Abyss représente la solution ultime pour l'interception d'appels système au niveau applicatif sur la plateforme Android, sans nécessiter de root, de déverrouillage ou de flashage. En tant que composant final d'un cadre d'interception complet couvrant toutes les couches d'une application, Abyss se concentre spécifiquement sur l'interception des instructions SVC du système.
Ce framework a été développé pour répondre aux besoins de nos produits de virtualisation devant fonctionner sur des téléphones Android standards. Abyss constitue la couche la plus basse de notre architecture d'interception, gérant les appels système au niveau le plus profond.
Concepts fondamentaux
Tracee : Le processus cible auquel ptrace est attaché, généralement le processus de l'application cible.
Tracer : Le processus qui utilise ptrace pour contrôler d'autres processus et gérer les appels système.
Notre framework exploite le composant Provider d'Android pour démarrer un service de traitement d'interception. Une fois lancé, ce service crée un thread indépendant qui boucle en continu pour traiter tous les rappels d'appels système interceptés. La version actuelle étant une démonstration de faisabilité avec journalisation simple, la logique métier peut être étendue selon les besoins spécifiques.
Pour une intégration métier plus complexe, une approche multi-thread pourrait être envisagée pour améliorer la stabilité. Bien que le passage au multi-thread introduise une certaine surcharge avec un gain de performance limité, il améliore efffectivement la stabilité en empêchant le blocage de tous les processus de l'application en cas de traitmeent long.
Flux de traitement
Le processus d'attachement d'un processus d'application (tracee) suit le schéma suivant :
- Le processus tracer démarre via un composant Provider
- Un thread de travail est créé pour effectuer l'attachement
- Le processus tracee est attaché à l'aide de ptrace
- Le thread de travail traite les événements système interceptés
L'utilisation de fork() permet au thread de travail d'effectuer l'attachement. ptrace impose des restrictions strictes : seul le thread ayant effectué l'attachement a le droit d'opérer sur les registres du tracee correspondant.
Gestion des appels système
Mécanisme d'ignorage des bibliothèques
Afin d'optimiser les performances, certains appels système issus de bibliothèques spécifiques comme libc.so sont ignorés. La fonction find_libc_exec_maps() identifie l'intervalle d'adresses mémoire de libc.so dans les maps, et les appels système à filtrer sont définis comme suit :
//enable_syscall_filtering()
FilteredSysnum internal_sysnums[] = {
{ PR_ptrace, FILTER_SYSEXIT },
{ PR_wait4, FILTER_SYSEXIT },
{ PR_waitpid, FILTER_SYSEXIT },
{ PR_execve, FILTER_SYSEXIT },
{ PR_execveat, FILTER_SYSEXIT },
{PR_readlinkat, FILTER_SYSEXIT}, //non traité pour l'instant
};
La fonction set_seccomp_filters configure des filtres eBPF pour les appels système en fonction de l'architecture. Les instructions eBPF pour différentes architectures sont combinées, formant un programme dont la structure pseudo-codée est la suivante :
for (chaque architecture) {
debut_section_arch;
for (chaque appel système de l'architecture courante)
ajouter_trace_appel_système;
fin_section_arch;
}
finaliser_filtre_programme;
debut_section_arch;// instructions eBPF spécifiques à l'architecture, y compris le filtrage de libc
ajouter_trace_appel_système;// ajouter l'instruction eBPF pour l'appel système à traiter
fin_section_arch;// instructions eBPF finales (signification: correspondance à l'appel système)
finaliser_filtre_programme;// instructions eBPF finales, tuant le thread en cas de situation anormale
Le filtre eBPF est finalement appliqué avec l'instruction suivante :
status = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program);
PR_ptrace
Étant donné qu'un tracee ne peut avoir qu'un seul tracer, le système PR_ptrace nécessite un traitement spécial lorsque l'application utilise déjà ptrace. Avant l'entrée dans l'appel système, celui-ci est remplacé par PR_void pour éviter l'exécution réelle de ptrace. Après la sortie de l'appel système, une simulation des différentes opérations ptrace est effectuée, notamment pour PTRACE_ATTACH, PTRACE_TRACEME, PTRACE_SYSCALL, PTRACE_CONT, PTRACE_SETOPTIONS, PTRACE_GETEVENTMSG, etc.
La gestion complète de ptrace est complexe en raison de la multitude de requêtes possibles.
PR_wait4 et PR_waitpid
Ces appels système sont utilisés en conjonction avec PR_ptrace. Si le tracee actuel n'est pas un tracer, les appels sont transmis directement au système. Si le premier paramètre de wait n'est pas -1, le système vérifie si le thread en attente existe dans l'ensemble et s'il est un tracee du thread actuellement traité. Dans le cas contraire, l'appel est transmis au système.
Le traitement consiste à remplacer l'appel système par PR_void avant son exécution, puis à simuler la logique de wait dans le tracer après la sortie de l'appel. Cette simulation implique de parcourir les tracees du tracer actuel pour détecter les événements nécessitant un traitement, puis de remplir les registres et de réveiller le tracer en cours de traitement.
PR_execve et PR_execveat
Lorsque l'option USE_LOADER_EXE est activée, ces appels remplacent les programmes natifs par un loader fixe pour le chargement des programmes.
Journalisation de l'interception
Les journaux d'interception fournissent des détails sur chaque appel système traité, y compris les identifiants de processus, les numéros d'appels système, les chemins de fichiers et les résultats d'exécution. Voici un exemple de journalisation :
E INTERCEPT/SYS: vpid 2: got event 7057f
E INTERCEPT: vpid 2,secomp_enabled 0,
E INTERCEPT/SYS: (null) info: vpid 2: sysenter start: openat(0xffffff9c, 0xb4000073c72fcd60, 0x0, 0x0, 0xb4000073c72fcd88, 0xb4000073c72fcde8) = 0xffffff9c [0x7367d45e80, 0]
E INTERCEPT/SYS: vpid 2: open path:/system/fonts/NotoSansMalayalamUI-VF.ttf
E INTERCEPT/SYS: syscall_number:216
E INTERCEPT/SYS: vpid 2,openat: /system/fonts/NotoSansMalayalamUI-VF.ttf
E INTERCEPT/SYS: (null) info: vpid 2: sysenter end: openat(0xffffff9c, 0xb4000073c72fcd60, 0x0, 0x0, 0xb4000073c72fcd88, 0xb4000073c72fcde8) = 0xffffff9c [0x7367d45e80, 0]
E INTERCEPT/SYS: vpid 2: open path:/system/fonts/NotoSansMalayalamUI-VF.ttf
E INTERCEPT/SYS: (null) info: vpid 2: restarted using 7, signal 0, tracee pid 32222,app_pid 32162
E/INTERCEPT/SYS: (null) info: vpid 3: sysenter start: close(0x90, 0x0, 0x7492d0d088, 0x6, 0x73b7b82860, 0x73b7b82880) = 0x90 [0x73633faae0, 0]
E/INTERCEPT/SYS: syscall_number:41
E/INTERCEPT/SYSW: noting to do,sn:41
E/INTERCEPT/SYS: (null) info: vpid 3: sysenter end: close(0x90, 0x0, 0x7492d0d088, 0x6, 0x73b7b82860, 0x73b7b82880) = 0x90 [0x73633faae0, 0]
E/INTERCEPT/SYS: (null) info: vpid 3: restarted using 7, signal 0, tracee pid 32223,app_pid 32162
E INTERCEPT/SYS: vpid 3: got event 7057f
Modules additionnels
Étant donné que notre framework ajoute un processus de traitement à l'application originale et utilise ptrace sur ses processus, une solution de masquage de ce processus supplémentaire et des traces d'attachement est nécessaire pour éviter les conflits avec les modules de détection d'applications. Cette solution inclut également la simulation complète des appels ptrace effectués par l'application elle-même.
Le module d'évasion contre la détection d'applications est traité séparément et fera l'objet d'un article dédié.