当前位置: 代码迷 >> Android >> Android native反调试模式及使用IDA绕过反调试
  详细解决方案

Android native反调试模式及使用IDA绕过反调试

热度:87   发布时间:2016-04-24 11:46:14.0
Android native反调试方式及使用IDA绕过反调试

    0x00

     为了避免我们的so文件被动态分析,我们通常在so中加入一些反调试代码,常见的Android native反调试方法有以下几种。

     1、直接调用ptrace(PTRACE_TRACEME, 0, 0, 0),参考Android Native反调试。

     2、根据上面说的/proc/$pid/status中TracerPid行显示调试程序的pid的原理, 可以写一个方法检查下这个值, 如果!=0就退出程序。参考Android Native反调试,用JNI实现APK的反调试。

     3、检查代码执行的间隔时间,参考Android应用方法隐藏及反调试技术浅析的0×03反调试初探。

     4、检测手机上的一些硬件信息,判断是否在调试器中,参考Android应用方法隐藏及反调试技术浅析0×03反调试初探。


   0x01

    那么我们如何过掉这些反调试呢?

    我们以阿里比赛第二题为例,参考安卓动态调试七种武器之孔雀翎 – Ida Pro。

    我们讲解两种方式:

    1、Ida Patch so

    2、Ida动态修改内存数据和寄存器数值

  

    我们首先讲解Ida Patch so,有几处都可以patch。我们从易到难依次讲解。

    第一处:

    我们在JNI_ONLOAD下断点,如下图:


     依次单步执行到BLX R7


    我们发现当执行完这步后,我们的ida就退出了,说明反调试代码是从这个入口进入执行的。那么我们只要把这个入口给NOP掉,就可以绕过反调试了。

    Patch so就是修改so中的二进制代码,然后再重新签名生成新的apk。Patch so,需要修改的本地so中的代码,而不是内存中的,所以我们需要通过上图内存中指令地址减去so在内存中的基地址来获取这条指令在本地so文件中的偏移。那么so在内存中的基地址怎么获取呢?按Crtl+s。


     我们看到libcrackme.so的基地址是AB732000,用BLX R7的地址AB733C58减去AB732000,等于1C58。

     然后我们双开ida,在另一个ida中打开libcrackme.so,按G,然后输入1C58,果然我们调到了BLX R7的位置,如下图:


    下面就要把这行代码NOP,可以修改为00 00 00 00,也可以修改为00 00 A0 E1。


    修改后点击右键,applay change。然后重新签名生成apk,再次运行apk,ida调试时就没有反调试的干扰了。


    第二处:

    我们按F7进入BLX R7的内部执行,如下图:


    是创建了一个线程去执行反调试,这里有个小技巧,如果我们想回到刚才的函数BLX R7,怎么办呢?选择寄存器LR,然后点击右键选择Jump即可,同理选择PC是跳到当前的位置。


    这个线程执行的函数体是sub_AB7336A4,如下图:


    sub_AB7336A4函数体如下:


   这个方法循环执行sub_AB73330C。我们进入sub_AB73330C,怀疑这里就是真正检查是否处于调试状态的地方,但是代码经过了严重的混淆,所以找不到反调试的代码。

   那怎么办呢?在0x00中我们谈到了常见的反调试代码,最常见的是第二种方式,第二种方式检查的过程中会调用fopen,所以我们在libc的fopen方法下断点,来是哪个函数调用的fopen,基本上就可以断定这个函数是反调试代码。

   

   首先我们需要找到fopen的位置,按Alt+T,然后输入fopen关键字,如下图:


    找到fopen后,代码是这样的:


   此时按P,就可以变成代码形式。如下图,在fopen处下断点。

   点击F9,继续运行,我们看到程序停在fopen处,此时LR就是刚刚我们谈到的sub_AB73330C,如下图:


   所以我们可以确定sub_AB73330C就是进行反调试的代码。我们可以看到这个函数是被sub_AB7336A4调用的。


    点击右侧的CODE XREF:sub_AB7336A4就能进入到调用sub_AB73330C的地方。在sub_AB7336A4函数上按F5,就能看到对应的C语言代码。如下:


    可见程序是在sub_AB73330C循环检测是否被反调试的。

    此时我们可以用和第一处一样的方式,找到本地so中对应的方法,然后Patch so,Nop掉对应的方法,然后重新签名,重新运行。


    第三处:

    其实第三处和第二处原理是一样的,只不过这里不使用Nop了,sub_AB73330C开始和结束的汇编代码如下:

    开始时:



    结束时:


     所以我们可以把AB733310的代码修改为AB73363C处的代码,不执行任何操作,直接返回。


   0x02

    Ida动态修改内存数据和寄存器数值

    我们看到反调试方法第二点,代码如下:

void be_attached_check(){    try    {        const int bufsize = 1024;        char filename[bufsize];        char line[bufsize];        int pid = getpid();        sprintf(filename, "/proc/%d/status", pid);        FILE* fd = fopen(filename, "r");        if (fd != nullptr)        {            while (fgets(line, bufsize, fd))            {                if (strncmp(line, "TracerPid", 9) == 0)                {                    int statue = atoi(&line[10]);                    LOGD("%s", line);                    if (statue != 0)                    {                        LOGD("be attached !! kill %d", pid);                        fclose(fd);                        int ret = kill(pid, SIGKILL);                    }                    break;                }            }            fclose(fd);        } else        {            LOGD("open %s fail...", filename);        }    } catch (...)    {    }}
     我们发现该程序会用fopen ()打开/proc/[pid]/status这个文件,随后会用fgets()和strcmp()来比较,于是我们在strcmp()处下个断点,然后让hex view的数据与R0同步。每次点击继续,我们都会看到strstr传入的参数。当传入的参数变为TracerPid:XXXX的时候我们停一下。因为在正常情况下,TracerPid的值应该是0。但是当被调试的时候就会变成调试器的pid。

    我们在strcmp下断点:


    程序会在此处断下,当我们发现R0地址中的内容为TracerPid:XXXX时,我们停一下,如下图:


    R0的里面存的地址是AB731B25,里面的内容为,如下图:


   我们可以通过修改内存值的方式来过掉这一次反调试。


   把TracerPid改为0,如下图:


    然后点击Apply changes。这样就可以过掉这次反调试。我们在前面也看到了,程序是在一个循环中进行反调试检查,所以这样的方试只是过了其中一次反调试。

   不推荐使用这种方式,最好使用Patch So的方式。

  相关解决方案