Modifier ptrace pour contourner l'anti-débogage (Android10)

1. Introduction à ptrace sous Android 10

 
   1. Suivi de l'emplacement du code source

      Dans Android10, le chemin du fichier de définition de la fonction ptrace est:

 bionic/libc/include/sys/ptrace.h

    Le prototype de fonction est défini comme suit:

long ptrace(int __request, ...);

        Le chemin d'accès au fichier de la fonction ptrace est:

 bionic/libc/bionic/ptrace.cpp

          

    Le code d'implémentation de la fonction est le suivant:

long ptrace (int req, ...) {

  bool is_peek = (req == PTRACE_PEEKUSR || req == PTRACE_PEEKTEXT || req == PTRACE_PEEKDATA);  long peek_result;
  va_list args;  va_start(args, req);  pid_t pid = va_arg(args, pid_t);  void* addr = va_arg(args, void*);  void* data;  if (is_peek) {
   
       data = &peek_result;  } else {
   
       data = va_arg(args, void*);  }  va_end(args);  //最终是调用__ptrace函数  long result = __ptrace(req, pid, addr, data);  if (is_peek && result == 0) {
   
       return peek_result;  }  return result;}

    A partir du code d'implémentation ptrace, nous pouvons voir que la fonction __ptrace est finalement appelée. Après le traçage du code, la fonction __ptrace est implémentée via l'assemblage de différentes plates-formes et appelle finalement l'appel système __NR_ptrace.

   Chemin du fichier de code d'implémentation de la plate-forme Android arm __ptrace:

bionic/libc/arch-arm/syscalls/__ptrace.S

 

Le code d'implémentation est le suivant:

#include <private/bionic_asm.h>
ENTRY(__ptrace)    mov     ip, r7    .cfi_register r7, ip    ldr     r7, =__NR_ptrace    swi     #0    mov     r7, ip    .cfi_restore r7    cmn     r0, #(MAX_ERRNO + 1)    bxls    lr    neg     r0, r0    b       __set_errno_internalEND(__ptrace)

      

      Chemin du fichier de code d'implémentation __ptrace de la plate-forme Android arm64:

               

 bionic/libc/arch-arm64/syscalls/__ptrace.S

               

      Le code d'implémentation est le suivant:

#include <private/bionic_asm.h>
ENTRY(__ptrace)    mov     x8, __NR_ptrace    svc     #0
    cmn     x0, #(MAX_ERRNO + 1)    cneg    x0, x0, hi    b.hi    __set_errno_internal
    retEND(__ptrace).hidden __ptrace

2. Fonction  

     ptrace peut permettre à un processus de surveiller et contrôler l'exécution d'un autre processus, et de modifier la mémoire et les registres du processus surveillé. Il est principalement utilisé pour le débogage des points d'arrêt du débogueur, le suivi des appels système, etc. Par exemple, l'outil strace fourni par le système Android peut suivre tous les appels système exécutés par l'application, comme indiqué ci-dessous:

image

   Dans la protection des applications Android, ptrace est largement utilisé pour l'anti-débogage. Un processus ne peut être ptrace qu'une seule fois. Si la méthode ptrace est appelée en premier, cela peut empêcher les autres de déboguer notre programme. C'est la fosse préventive légendaire. Par exemple, appelez le code suivant dans JNI_onLoad pour occuper le trou en premier:

 

    if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
   
           LOGD("I am ptraced");        exit(0);    }else{
   
           LOGD("ptrace success");    }

   Vous pouvez également utiliser un processus parent ptrace multi-processus pour détecter s'il est débogué, le code référencé en ligne est le suivant:

static void anti_ptrace(){
   
       pid_t child;    child = fork();    if (child)    {
   
           //等待子进程退出        wait(NULL);    }    else    {
   
           // 获取父进程的pid        pid_t parent = getppid();        LOGD("ptrace attach start");        // ptrace附加父进程        if (ptrace(PTRACE_ATTACH, parent, 0, 0) < 0)        {
   
               LOGD("ptrace attach failure");            //attach父进程失败,说明被调试了,进入死循环,也可以考虑用杀进程的方式杀掉父进程            //kill(parent,SIGKILL);            while(1);        }        LOGD("ptrace attach success");        //说明没有被调试,附加成功        // 释放附加的进程        ptrace(PTRACE_DETACH, parent, 0, 0);        // 结束当前子进程        exit(0);    }}

Deux, modification du code source

      

     Pour les méthodes anti-débogage ci-dessus, nous pouvons modifier la logique ptrace dans le code source comme suit:

long ptrace (int req, ...) {

  bool is_peek = (req == PTRACE_PEEKUSR || req == PTRACE_PEEKTEXT || req == PTRACE_PEEKDATA);  long peek_result;
  va_list args;  va_start(args, req);  pid_t pid = va_arg(args, pid_t);  void* addr = va_arg(args, void*);  void* data;  if (is_peek) {
   
       data = &peek_result;  } else {
   
       data = va_arg(args, void*);  }  va_end(args);

  ///ADD START  int caller_uid=getuid();    //int caller_pid=getpid();  //caller_uid>10000说明是普通App调用  if(caller_uid>10000)  {
   
             if(req ==PTRACE_TRACEME)     {
   
           //自己ptrace自己,直接返回成功        return 0;     }   if(req==PTRACE_ATTACH)   {
   
          int caller_ppid=getppid();     if(caller_ppid==pid)     {
   
           //如果是子进程ptrace父进程,直接返回成功        return 0;     }         //TODO 也可以通过读取/proc/$pid/status获取进程的uid,如果uid和当前调用ptrace的uid一致,也直接返回成功            }  }  ///ADD END  long result = __ptrace(req, pid, addr, data);  if (is_peek && result == 0) {
   
       return peek_result;  }  return result;}

   Une fois la modification terminée, exécutez la commande suivante pour compiler le test flash:

source build/envsetup.shbreakfast oneplus3brunch  oneplus3

Trois, l'expansion

    Étant donné que la fonction ptrace est facile à détourner, de nombreuses applications utilisent la fonction ptrace en interne et n'appelleront pas directement ptrace. Au lieu de cela, utilisez syscall en interne ou utilisez l'assembly en ligne pour implémenter la fonction d'appel de ptrace. Par conséquent, le module ptrace hook du noyau peut être développé pour prendre en compte divers appels ptrace. À propos de l'interception des appels système du noyau, vous pouvez vous référer à cet article: https://www.anquanke.com/post/id/85375.

 

Suivez le compte public WeChat pour obtenir plus d'articles connexes:

image

Je suppose que tu aimes

Origine blog.csdn.net/u011426115/article/details/113194084
conseillé
Classement