CTF安卓逆向练习第五弹

CTF安卓逆向练习第五弹

写在前面的话:这次练习的是看雪2017CTF的第六题,涉及的知识点比较多,包括花指令,加密,编码等,自己并没有做出来,看了许多大佬的wp,自己跟着跑了一遍,期间遇到了许多问题,现在整理一下,看样子要学习的东西还是挺多的:-)

基本概述

还是最基本的,先看一下题,如图,就是一个简单的app,然后获取注册码。
这里写图片描述

java层分析

还是先看java层,打开jeb,拖进去,然后发现加入了混淆,这些方法的名字也是醉了,不过也不太影响我们进行分析。
小tip:在这些乱码的名字处点击n,然后就可以将这些奇葩的名字换位自己输入的熟悉的名字了,这样分析会比较容易些
这里写图片描述
分析onCreate,发现点击按钮之后onClick会调用方法,而这个方法,会调用utils类中的一个方法,仔细一看发现用到了native,因此我们知道关键代码check函数都是隐藏在so文件中了,接下来我们要仔细分析so文件了。
这里写图片描述

ida初步静态分析

接下来打开ida开始静态分析,跳转到check函数处,然后点击F5查看一下伪代码,这个时候发现了奇怪的事情,就是这个函数在跳转,接下来跟着跳转走,发现一个函数套着另一个函数,不断的在跳转,而且这些函数本身并没有什么实际意义,因此想到了花指令,这里应该是遇到了花指令。所以接下来我们要做的事情就是编写idc脚本,进行去花。
这里写图片描述

idc去花脚本

这里的去花脚本由题目的作者提供。基本思路就是在我们多看几个花指令之后就会发现这些花指令有相同之处,大部分分为两类,一类是下面这种形式:

.text:00002A80 B1 B5                       PUSH            {R0,R4,R5,R7,LR}
.text:00002A82 82 B0                       SUB             SP, SP, #8
                          //中间是一些其他指令...
.text:00002A9A A1 F1 01 01                 SUB.W           R1, R1, #1

还有一种格式是:

PUSH.W {R4-R10,LR}
POP.W {R4-R10,LR}

因此去花脚本的思想就是遍历整个函数,然后对比去花指令,如果程序指令和去花指令相同,则用00进行替换,即替换成MOV R0,R0这种形式,去花脚本如下:

// Byte:Get value of program byte
// HiddeAreas - address ranges which can be replaced by their descriptions
//success HideArea(long start,long end, string description, string header, string footer, long color);
//PatchByte:Change value of a program byte
//atoa:Convert address value to a string Return address in the form 'seg000:1234' (the same as in line prefixes)
#include <idc.idc>

static main()
{
    auto i,pos,size,JMP_SIZE,FLOWER1_SIZE,FLOWER2_SIZE;
    pos=0x286C; //START
    size=0x1A000;//SIZE
    JMP_SIZE = 0x40;
    FLOWER1_SIZE = 0x1e;
    FLOWER2_SIZE = 0x8;


    for ( i=0; i < size;i++ ) {
        //PATCH JMPS
        if (
            (Byte(pos)==0x13)&&(Byte(pos+1)==0xe0)&&(Byte(pos+2)==0xbd)&&
            (Byte(pos+3)==0xe8)&& (Byte(pos+4)==0xf0)&&(Byte(pos+5)==0x47))
        {
            for(i=0;i<JMP_SIZE;i++)
            {
                PatchByte(pos+i,0x0);//change
            }
            HideArea(pos,pos+JMP_SIZE,atoa(pos),atoa(pos),atoa(pos+JMP_SIZE),-1);
            continue;
        }

        // PATCH FLOWER1
        //.text:00002A80 B1 B5                       PUSH            {R0,R4,R5,R7,LR}
        //.text:00002A82 82 B0                       SUB             SP, SP, #8
        //.text:00002A84 12 46                       MOV             R2, R2
        //.text:00002A86 02 B0                       ADD             SP, SP, #8
        //.text:00002A88 00 F1 01 00                 ADD.W           R0, R0, #1
        //.text:00002A8C A0 F1 01 00                 SUB.W           R0, R0, #1
        //.text:00002A90 1B 46                       MOV             R3, R3
        //.text:00002A92 BD E8 B1 40                 POP.W           {R0,R4,R5,R7,LR}
        //.text:00002A96 01 F1 01 01                 ADD.W           R1, R1, #1
        //.text:00002A9A A1 F1 01 01                 SUB.W           R1, R1, #1

        if (
            (Byte(pos)==0xb1)&&(Byte(pos+1)==0xb5)&&(Byte(pos+2)==0x82)&&(Byte(pos+3)==0xb0)&&
            (Byte(pos+0x1a)==0xa1)&&(Byte(pos+0x1b)==0xf1)&&(Byte(pos+0x1c)==0x01)&&(Byte(pos+0x1d)==0x01))
        {
            for(i=0;i<FLOWER1_SIZE;i++)
            {
                PatchByte(pos+i,0x0);
            }
            HideArea(pos,pos+FLOWER1_SIZE,atoa(pos),atoa(pos),atoa(pos+FLOWER1_SIZE),-1);
            continue;
        }

        //PATCH FLOWER2
        // "PUSH.W {R4-R10,LR}"
        // "POP.W {R4-R10,LR}"

        if (
            (Byte(pos)==0x2d)&&(Byte(pos+1)==0xe9)&&(Byte(pos+2)==0xf0)&&(Byte(pos+3)==0x47)&&
            (Byte(pos+4)==0xbd)&&(Byte(pos+5)==0xe8)&&(Byte(pos+6)==0xf0)&&(Byte(pos+7)==0x47))
        {
            for(i=0;i<FLOWER2_SIZE;i++)
            {
                PatchByte(pos +i,0x0);
            }
            HideArea(pos,pos+FLOWER2_SIZE,atoa(pos),atoa(pos),atoa(pos+FLOWER2_SIZE),-1);
            continue;
        }
        pos++;
    }

    Message("\n" + "DE-FLOWERS FINISH BY Ericky\n");
}

脚本写的十分清晰,然后接下来我们在ida中运行脚本,在File->Script file中选中脚本文件即可,等一段时间就会运行完毕。运行完毕后我们马上点击F5,发现去花脚本没什么用,还是原先函数跳转的样子。这个时候我们发现在IDA View和Hex View窗口都可以看到相应花指令的更改,花指令已经被更改了,那么为什么F5的时候还是识别不了?原因就是IDA将我们去花后的代码块还是识别成了函数,因此我们就要将已经去花但是IDA自动误认为是函数的地方将IDA的函数标识删去,具体方法是在左侧function窗口中,将我们check函数到check函数的实际结尾处JNI_Onload之间的所有函数标识都删掉,右键delete function即可。
这里写图片描述
至于如何确定JNI_Onload就是函数的结尾,可以看一下check函数开头有个push入栈的操作,而结尾JNI_Onload的前面函数的结尾处有个pop出栈的操作刚好相对应,而且通过查看中间函数的内容也可以知道,他们里面的内容都是我们去花后的指令MOV R0,R0;删除的时候,有一个函数sub_286c不用删除,因为通过前面的有花指令的F5我们知道这个286c是return的一个值,是不满足判断语句时走的另一条路,看一下这个函数的结构发现是一个死循环。实际上在分析过这个apk之后,我们就会知道,这里限制了一个调试次数,当调试次数超过6次的时候就会陷入这个死循环中。因此我们把这个286c的函数加上函数的结尾,在edit->function->set function end
这里写图片描述
这个时候我们的check函数的结尾还是之前的值,因此我们要把这个结尾往后挪,挪到之前分析过的3C3C处。这里的操作是要把check函数标志的头删除,然后重新create,再在3c3c处加function的end。几个操作都在edit->function中。
这里写图片描述
这里写图片描述
最后,去花完成,在check处点击F5,出现伪代码,开始静态分析。

去花后IDA静态分析

下面来分析去花后的代码,可以看到逻辑结果清楚了许多,还是从下往上看,走return=1的分支,发现一个if的判断语句,要两个相等,这种结构一看就觉得是我们要找的flag。然后继续看,byte_20020字符串是通过上面的一个循环得到的,v14是通过sub_19DA8得到的,所以可以在这两处下断点查看。
这里写图片描述
先分析上面字符串循环那里,可以看到在28F2处字符串初始化,在这里下断点
这里写图片描述
找到这个循环的终点36B4,下断点,这样在动态调试的时候,我们就能在这里通过查看寄存器的值,得到字符串了
这里写图片描述
接下来分析sub_19DA8函数,这是一个比较重要的加密及编码的函数,F5之后,还是从下往上看,找到几个重要的位置,下断点
这里写图片描述
只是看这些静态的代码可能并不能完全理解这个函数的算法,因此要动态调试起来

IDA动态调试

首先这个apk和上一篇一样,用了防动态调试技术,因此还是要从JNI_Onload处下断点调试,具体方法可以看上一篇,这里就不再概述了。调试好之后,走到之前下断点的字符串循环处,在R12寄存器中可以找到我们要的字符串“JPyjup3eCyJjlkV6DmSmGHQ=”
这里写图片描述
然后看这个字符串,后面有个“=”号,立刻就可以想到base64编码,因为base64编码的特点就是用=号进行空位填补,然后在之前找到的19DAB函数中的几个关键点中,发现函数5AFC就是base64编码函数。所以我们大概知道我们输入的密码要经过base64编码之后再和字符串JPyjup3eCyJjlkV6DmSmGHQ=进行比较从而完成验证,当然并不是直接进行base64编码的,还需进行一步操作。在翻看19DAB函数的时候,发现另一个关键处,函数sub_467E正是RC4加密算法,因此可以得出输入是先经过RC4加密,再用base64进行编码。分析到这里,我们继续动态调试,在运行完sub_1A31C函数之后,我们可以得到秘钥:“19931012”
这里写图片描述
至此,我们已经能将这道题解出了,即把“JPyjup3eCyJjlkV6DmSmGHQ=”进行RC4解密(带base64解码),其中秘钥为“19931012”。在网上有好多在线解的工具,找一个即可
这里写图片描述
得到注册码“madebyericky94528”,试了一下,成功!
这里写图片描述

关键点

做完这道题之后,有几个关键的点需要我们进一步的思考和练习。

  1. 花指令的识别:需要我们能有效的识别花指令,同时对函数的真正开始和结尾处进行辨别,这里用到push和pop的出栈入栈进行分辨;
  2. base64编码函数的识别:一是了解各种编码的特点,看论坛上各个大神的wp的时候人家都是一下子就想到是base64编码。二是了解base64编码算法及其源程序,看到代码能知道算法。
  3. RC4加密函数的识别:还是经验的原因,要了解RC4加密算法,源代码及其逆向后的代码。

写在结尾的话:逆向的时候好多都是根据经验进行判断的,因此还需加强对编码、加密算法的了解,有条件的,最好自己找个源代码编译,再重新逆向,掌握算法结构。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值