1

[原创]CTF2017第6题分析

xwtwho 2017-6-13 10:53 1255

之前看这题的要求是要android 5.1以上系统,手上没这种手机,PCarm模拟器又慢,准备放弃的,后来发现竟然能在我的华为3C (android 4.2)上运行。

1. 静态分析

ApkIDE反编译,直接浏览MainActivity.smali,流程很清晰。

invoke-static {v0}, Lcom/miss/rfchen/utils;->check(Ljava/lang/String;)Z

整个验证逻辑就是调用so中的check完成的.

lib中的librf-chen.so拖到IDA中,可以直接索引check函数


简单浏览了下,发现一些字符串相关的操作,估计跟逻辑有关.

.text:0000295C                 STRH.W          R0, [SP,#arg_22]

.text:00002960                 PUSH.W          {R4-R10,LR}

 

.text:000029C6                 STRH.W          R1, [SP,#arg_24]

.text:000029CA                 MOVS            R1, #0x75

 

.text:00002A32                 STRH.W          R1, [SP,#arg_26]

.text:00002A36                 MOVS            R1, #0x33

 

.text:00002A9A                 SUB.W           R1, R1, #1

.text:00002A9E                 STRH.W          R1, [SP,#arg_28]

.text:00002AA2                 MOVS            R1, #0x43

 

.text:00002B0A                 STRH.W          R1, [SP,#arg_2A]

.text:00002B0E                 PUSH.W          {R4-R10,LR}

 

后面代码混淆比较多,准备动态调试了.

 

这里本来想修改这里的跳转逻辑验证下的,发现只要smali文件有修改,就打包不回去了,看到android.support下有个包名是符号,估计跟这个有关,具体不了解,考虑到时间关系,没继续研究这个了,后面有空了再看看.

smali不行,但是修改AndroidManifest.xml可以,直接加上android:debuggable="true",当然手上有修改参数自己刷过机的调试环境的,可以不用这步.

 

2.  动态调试

先挂起app  (具体的调试步骤,论坛网上都有很详细的介绍):

adb shell am start -D -n com.miss.rfchen/.MainActivity

然后使用IDA附加,跟踪check函数,相比PC,这个又带混淆,调试手段还是少很多,感觉就是单步,然后配合静态IDA看流程,能跳过的函数先跳过,先弄清楚大致流程.

首先是前面拼接了一个字符串JPyjup3eCyJjlkV6DmSmGHQ=,先记录下来。

 

base: 776A4000

librf_chen.so:776A76C4 BLX             R3

librf_chen.so:776A76C6 PUSH.W          {R4-R10,LR}

librf_chen.so:776A76CA POP.W           {R4-R10,LR}

librf_chen.so:776A76CE B               unk_776A76F8   这里r0:指向输入的字符串

 

后面可以逐步看到对输入字符串的处理,到下面函数,通过看逻辑发现就是base64,

.text:00005AFC my_base64                               ; CODE XREF: sub_1A188+9Ep

.text:00005AFC                 PUSH.W          {R4-R10,LR}

.text:00005B00                 ADD             R7, SP, #0xC

.text:00005B02                 MOV             R9, R0

.text:00005B04                 MOVW            R0, #0x5556

.text:00005B08                 MOV             R10, R1

.text:00005B0A                 MOVT.W          R0, #0x5555

.text:00005B0E                 SMMUL.W         R0, R10, R0

.text:00005B12                 ADD.W           R0, R0, R0,LSR#31

.text:00005B16                 ADD.W           R1, R0, R0,LSL#1

.text:00005B1A                 SUB.W           R1, R10, R1

.text:00005B1E                 CMP             R1, #0

 

然后就是结果和之前的JPyjup3eCyJjlkV6DmSmGHQ=这个串比较.

 

之前为了快速弄清楚流程,有的函数跳过了,到base64这,发现输入字符串不是app上输入的原始字符串,看来是之前又有变换操作,不过这里发现长度和原始串是一致的.

后面就单独跟踪了base64之前的操作,大概就是查表,

.text:000047EA                 ADD             R10, R4

.text:000047EC                 UXTB.W          R7, R10

.text:000047F0                 LDR.W           R8, [R6,R7,LSL#2] ; r6: 0x100int的表,查表操作

.text:000047F4                 STR.W           R8, [R6,R5,LSL#2]

.text:000047F8                 STR.W           R4, [R6,R7,LSL#2]

.text:000047FC                 ADD             R4, R8

.text:000047FE                 UXTB            R4, R4

.text:00004800                 LDRB.W          R5, [R11] ; 读取app中的输入串,单个字符

.text:00004804                 LDR.W           R4, [R6,R4,LSL#2]

.text:00004808                 EOR.W           R4, R4, R5

.text:0000480C                 STRB            R4, [R3]

 

这里涉及到混淆,后来又跟踪了后续处理,基本确定是rc4了,然后就是回溯表的生成,发现8字节的输入key:

70 D6 CB 74 40 B2 A8 72

 

base: 77461000

librf_chen.so:7747B0BC POP.W           {R0,R4,R5,R7,LR}

librf_chen.so:7747B0C0 ADD.W           R1, R1, #1

librf_chen.so:7747B0C4 SUB.W           R1, R1, #1

librf_chen.so:7747B0C8 BL              unk_774665E4   生成rc4

librf_chen.so:7747B0CC MOV             R0, R4

librf_chen.so:7747B0CE MOV             R1, R6

librf_chen.so:7747B0D0 MOV             R2, R9

 

不过这里遇到个问题,我用程序生成的表和这个不同,已经intbyte后的,考虑比较晚了,第二天还上班,没跟踪具体逻辑了,直接把表数据扣出来了。

 

整体流程就是:

对输入串rc4,然后base64,再和目标串JPyjup3eCyJjlkV6DmSmGHQ=比较。

 

那解密流程就是反过来了:

先对JPyjup3eCyJjlkV6DmSmGHQ=  base64解码,然后rc4解密.

 

3. 程序解密实现

 

先对JPyjup3eCyJjlkV6DmSmGHQ=  base64解码(可以用工具,我这里是node.js:

 

    var str2 = new Buffer('JPyjup3eCyJjlkV6DmSmGHQ=', 'base64').toString('hex');

console.log(str2);

 

 

输出:

24fca3ba9dde0b226396457a0e64a61874

 

这个就是rc4加密后的结果了,现在对这个rc4解密.

unsigned char key_[0x100]=

{

    0xB7,0x6B,

    0xA6,0xDC,0x11,0x5B,    0x05,0x5A,0x78,0x00,

    0x74,0xB2,0x43,0x30,    0x01,0xBE,0xED,0x26,

    0x09,0xC7,0x31,0x45,    0xCE,0xCF,0x91,0x70,

    0xC6,0xB1,0x5E,0x61,    0x55,0x15,0xBD,0x1A,

    0x4B,0xC5,0xF6,0x22,    0xA2,0x72,0xFF,0x42,

    0xF3,0x6A,0x8F,0x87,    0x02,0xDF,0x6F,0xB0,

    0xBA,0xA4,0x85,0xEA,    0x8E,0x32,0xA1,0x2D,

    0xEF,0xAA,0xE3,0xA3,    0xBF,0x2C,0xE0,0x47,

    0x3D,0x08,0xB9,0xCC,    0x7D,0x7F,0x6D,0x7A,

    0x12,0x1C,0xDB,0x94,    0xFC,0xC0,0xAF,0x0C,

    0x62,0x14,0x59,0x24,    0xE8,0x4C,0xCB,0xCD,

    0x60,0x39,0xC9,0x3A,    0x98,0x77,0xE2,0xAB,

    0xB8,0xD7,0x3C,0x69,    0x53,0x1F,0xCA,0x88,

    0x51,0x4D,0x8A,0x36,    0xE7,0xAE,0xD2,0xD1,

    0xFB,0x8B,0x5D,0x0D,    0xA8,0x8D,0x0A,0x10,

    0x82,0xD4,0x0E,0x2F,    0x57,0x5C,0x20,0x3E,

    0xD0,0xB6,0xBC,0x33,    0x4E,0x90,0x17,0xF2,

    0xB5,0x03,0x79,0x99,    0x2B,0xFD,0xAD,0xA5,

    0x04,0x16,0x40,0x3B,    0x07,0x9E,0xDD,0x48,

    0x2E,0x25,0x27,0xA7,    0x89,0xF9,0x95,0x68,

    0x1B,0x6E,0x84,0xF7,    0xC4,0xC8,0x71,0x6C,

    0x86,0x29,0xB3,0xEC,    0x92,0x80,0x4F,0x41,

    0xB4,0xD8,0x58,0x1D,    0xD6,0x44,0x49,0x81,

    0x3F,0x67,0x96,0x35,    0xAC,0x21,0xE9,0x64,

    0x56,0x19,0x9B,0xD9,    0xC3,0xBB,0x4A,0xD3,

    0xE6,0xE4,0x9D,0xD5,    0x8C,0x06,0x83,0x7C,

    0x9C,0xC1,0xFA,0x93,    0x97,0x76,0xDA,0x38,

    0x73,0x18,0x28,0x13,    0xF4,0x0F,0x23,0x52,

    0x9A,0x7B,0xA9,0xF8,    0x9F,0x50,0x65,0xF1,

    0x34,0xEE,0xA0,0x37,    0x46,0xE5,0x5F,0x75,

    0x7E,0xC2,0xEB,0x66,    0xDE,0xFE,0xF5,0x0B,

    0x2A,0xF0,0x63,0xE1,    0x1E,0x54

};

void RC4Transform(char* output, const char* input, int len)

{

    int i = 0, j = 0;

    for (int k = 0; k < len; k++)

    {

        i = (i + 1) % 256;

        j = (j + key_[i]) % 256;

        swap(key_[i], key_[j]);

        unsigned char subkey = key_[(key_[i] + key_[j]) % 256];

        output[k] = subkey ^ input[k];

    }

}

int TestNo6()

{

         char cOut[0x50] = { 0 };

 

         char cIn[] = { 0x24,0xfc,0xa3,0xba,0x9d,0xde,0x0b,0x22,0x63,0x96,0x45,0x7a,0x0e,0x64,0xa6,0x18,0x74 };

         RC4Transform(cOut, cIn, sizeof(cIn));

 

 

         OutPutMessage2(0, cOut);

 

         return 0;

}

 

 

运行结果:

madebyericky94528

 




快讯:[看雪招聘]十八年来,看雪平台输出了大量安全人才,影响三代安全人才!

最新回复 (0)
返回