アンチデバッグをバイパスするようにptraceを変更します(Android10)

1. Android10でのptraceの概要

 
   1.ソースコードの場所の追跡

      Android10では、ptrace関数定義ファイルのパスは次のとおりです。

 bionic/libc/include/sys/ptrace.h

    関数プロトタイプは次のように定義されています。

long ptrace(int __request, ...);

        ptrace関数のファイルパスは次のとおりです。

 bionic/libc/bionic/ptrace.cpp

          

    関数実装コードは次のとおりです。

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;}

    ptrace実装コードから、__ ptrace関数が最終的に呼び出されたことがわかります。コードトレースの後、__ ptrace関数はさまざまなプラットフォームのアセンブリを通じて実装され、最後に__NR_ptraceシステムコールを呼び出します。

   Android ARMプラットフォーム__ptrace実装コードファイルパス:

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

 

実装コードは次のとおりです。

#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)

      

      Android arm64プラットフォーム__ptrace実装コードファイルパス:

               

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

               

      実装コードは次のとおりです。

#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.機能  

     ptraceを使用すると、プロセスは別のプロセスの実行を監視および制御し、監視対象プロセスのメモリとレジスタを変更できます。これは主に、デバッガブレークポイントのデバッグ、システム呼び出しの追跡などに使用されます。たとえば、Androidシステムが提供するstraceツールは、以下に示すように、アプリによって実行されたすべてのシステムコールを追跡できます。

画像

   Androidアプリの保護では、ptraceはアンチデバッグに広く使用されています。プロセスをptraceにできるのは1回だけです。最初にptraceメソッドを呼び出すと、他の人がプログラムをデバッグできなくなる可能性があります。これは伝説的な先制ピットです。たとえば、JNI_onLoadで次のコードを呼び出して、最初に穴を占有します。

 

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

   マルチプロセスのptrace親プロセスを使用して、デバッグされているかどうかを検出することもできます。オンラインで参照されるコードは次のとおりです。

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);    }}

2、ソースコードの変更

      

     上記のアンチデバッグメソッドの場合、ソースコードのptraceロジックを次のように変更できます。

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;}

   変更が完了したら、次のコマンドを実行してフラッシュテストをコンパイルします:

source build/envsetup.shbreakfast oneplus3brunch  oneplus3

三、拡張

    ptrace関数はハイジャックされやすいため、多くのアプリは内部でptrace関数を使用し、ptraceを直接呼び出すことはありません。代わりに、内部でsyscallを使用するか、インラインアセンブリを使用してptraceを呼び出す関数を実装します。したがって、カーネルフックptraceモジュールは、さまざまなptrace呼び出しを考慮に入れるように開発できます。カーネルシステムコールの傍受については、https://www.anquanke.com/post/id/85375の記事を参照してください。

 

その他の関連記事を入手するには、WeChatパブリックアカウントをフォローしてください。

画像

おすすめ

転載: blog.csdn.net/u011426115/article/details/113194084