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下断点,如下图:

Android native反调试方式及使用IDA绕过反调试_第1张图片

     依次单步执行到BLX R7

Android native反调试方式及使用IDA绕过反调试_第2张图片

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

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

Android native反调试方式及使用IDA绕过反调试_第3张图片

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

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

Android native反调试方式及使用IDA绕过反调试_第4张图片

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

Android native反调试方式及使用IDA绕过反调试_第5张图片

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


    第二处:

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

Android native反调试方式及使用IDA绕过反调试_第6张图片

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

Android native反调试方式及使用IDA绕过反调试_第7张图片

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

Android native反调试方式及使用IDA绕过反调试_第8张图片

    sub_AB7336A4函数体如下:

Android native反调试方式及使用IDA绕过反调试_第9张图片

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

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

   

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

Android native反调试方式及使用IDA绕过反调试_第10张图片

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

Android native反调试方式及使用IDA绕过反调试_第11张图片

   此时按P,就可以变成代码形式。如下图,在fopen处下断点。
Android native反调试方式及使用IDA绕过反调试_第12张图片

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


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

Android native反调试方式及使用IDA绕过反调试_第13张图片

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

Android native反调试方式及使用IDA绕过反调试_第14张图片

    可见程序是在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下断点:

Android native反调试方式及使用IDA绕过反调试_第15张图片

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

Android native反调试方式及使用IDA绕过反调试_第16张图片

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

Android native反调试方式及使用IDA绕过反调试_第17张图片

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


   把TracerPid改为0,如下图:


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

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

你可能感兴趣的:(Android,Security)