首页
社区
课程
招聘
[原创]2015移动安全挑战赛MSC(第二届)第三题 解题分析
发表于: 2015-10-28 20:19 22222

[原创]2015移动安全挑战赛MSC(第二届)第三题 解题分析

2015-10-28 20:19
22222

难,这题真是难,ida附加,一堆的乱序指令、变形BL和花指令,一不小心就跑飞了,需要大量的体力去恢复,所以时间关系大家都止步于此;不过,此题对我等小鸟来说,很有研究价值;好了,废话不多说,下面看我的分析吧:

正确座位号:L6^a9>z<81Li!*M

一,定位check函数,方法很多,说下,我自己的方法,看下安卓源码,调用jni函数,都是通过 libdvm.so 中的 dvmPlatformInvoke 函数调用的,所以直接hook这个函数,就能打印出check函数;

二,说下乱序指令和变形BL:
3种乱序指令:
1种:
PUSH            {R0,R1,LR}
LDR             R0, XXXXXXXX
BL                                 XXXXXXXX
相当于BL lr + Dword(2 * (lr >> 1) + 4 * r0 + 4) - 1;
2种:
PUSH            {R0,R1,LR}
BL                                 XXXXXXXX
相当于BL lr + Dword(2 * (lr >> 1)) - 1;
这种指令有种变形,直接 BL       模块基地址+0x2A5B4
3种:
SUB             SP, SP, #8
PUSH            {R0,R1,LR}
BL              XXXXXXXX
相当于push {Dword(2 * (lr >> 1) + Dword(2 * (lr >> 1)))};
然后 BL lr + 4 - 1;
后面一般跟 POP    Rx
然后跟
                      CMP        Rx,Rx
  BXX        XXXXXXXX
这种指令,可能重复n多次,比较难以处理,我的方法是RunTo(BXX的PC)那,然后StepOver();

1种变形BL:
BL              XXXXXXXX
相当于BL  Dword(2 * (lr >> 1) + Dword(2 * (lr >> 1)))
这种属于调用函数的变形BL,可以隐藏api函数

这些乱序指令和变形BL,使得单步调试很容易就跑飞了,并且IDA的F5功能也全完失效;不过知道了它的特征,可以写个idc脚本,可以把真实的指令给跑出来,idc脚本见附件。

三,check函数的逆向分析:
1、效验偏移0x1F08,大小0x286DC字节的代码CRC32效验值;
2、比较座位长度是否等于0xF;
3、反调试的检测,通过调用r_debug->r_brk函数触发SIGILL异常;
4、经过2个循环,得到一个0x100字节的表,接着通过raise(0x5)和raise(0xB)抛出SIGTRAP和SIGSEGV异常,然后在SIGSEGV异常的接管函数里会循环和V值进行异或,最后循环和CRC32效验值异或,最后得到一个0x100字节的表;
5、最后得到的0x100字节的表和正确坐标的表比较,相等就验证通过;
说的有点乱,下面看IDC跑出的代码,这里我只贴关键代码:

先看下函数大的流程:
4BEDB510:    PUSH            {R4-R7,LR}
4BEDB512:    ADD             R7, SP, #0xC
4BEDB514:    SUB             SP, SP, #0x5C
.........
4BEDB0DC:    MOV             R2, R3
4BEDAF1C:    BL             sub_4BEDF3C0   //得到座位  R6,#0x24

4BEDAF24:    STR             R0, [R6,#0x24]
4BEDAEC4:    BL             sub_4004112C   //strlen

4BEDAECC:    STR             R0, [R6,#0x14]
4BEDAECE:    ADDS            R4, R0, #1
4BEDB1F0:    MOV             R0, R4
4BEDB1F2:    NOP
4BEDB1F4:    BL             malloc

4BEDB1FC:    MOV             R5, R0
4BEDB1DC:    LDR             R1, [R6,#0x24]
4BEDB1DE:    MOV             R2, R4
4BEDB1E0:    LDR             R4, [R6,#0x18]
4BEDB404:    BL             sub_4BECD86C   //memcpy
......
4BEDB1C8:    BL             lrand48
.........
4BED962C:    BL             sleep
.........
4BED968C:    LDR             R0, [R5]
4BED9640:    BL             sub_4004112C   //strlen

//循环开始
4BEDA370:    LDR             R0, [R5]
.........
4BEDA116:    BLT             unk_4BEDA124   //R2==64 跳出循环
.........
4BEDA1CC:    LDR             R0, [R4]
//循环结束

//循环开始  
4BED982A:    LDR             R0, [R6,#0x18]
.........
4BEDA7D4:    BEQ             unk_4BEDA7E0   //R1=0跳出循环
.........
4BEDAD62:    ADDS            R1, R2, R1     //这里能看到R1 = CRC32 table
........
4BED9BD4:    STR             R1, [R6]
/循环结束 这里计算偏移0x1F08,大小0x286DC字节的代码CRC32效验值

.......
4BED97C8:    STR             R2, [R6,#0x18]    //R2 = R2 ^ 0xFFFFFFFF 得到最后的CRC效验值
//4BED97CA:    push {[0x4BEDB5E0]}  -->1279E//4BED97D8:    POP             {R0}
4BED97DA:    ADD             R0, PC
//4BED9F78:    push {[0x4BEDB5C0]}  -->FFFFFF58//4BED9F84:    POP             {R1}
4BED9EC8:    ADDS            R0, R1, R0
4BED9ECA:    LDR             R0, [R0]
4BED9ECC:    MOVS            R1, #0x80
4BED9F38:    BL             sub_4BEE01F0

4BED9F40:    LDR             R1, [R6,#0x28]
4BED9EDC:    LDR             R1, [R1]
4BEDA0B8:    ADDS            R2, R1, #1
4BED9EA8:    CMP             R0, #0
4BED9EAA:    BEQ             unk_4BED9EB8
4BED9EB8:
4BED9EB8:    MOV             R2, R1
4BEDA900:    STR             R2, [R6,#0x14]
4BEDA902:    MOV             R0, R2        //座位字符串
4BEDA904:    LDR             R1, [R6,#0x18]  //CRC效验值
4BEDA934:    BL             sub_4BECE6F8   //验证座位的关键函数
4BEDA93C:    STR             R0, [R6,#0x18]
4BED9E80:    LDR             R0, [R6,#0x14]
4BED9E82:    NOP
4BED9E84:    BL             free
.............

下面看sub_4BECE6F8 函数

4BED7F14:    PUSH            {R4-R7,LR}
4BED7F16:    ADD             R7, SP, #0xC
4BED7F18:    SUB             SP, SP, #0x114
.......
4BED889C:    STR             R1, [R6,#0x48]  //保存CRC32效验值
4BED89A0:    STR             R0, [R6,#0x4C]  //保存座位地址
.......
4BED3B66:    NOP
4BED3B68:    BL             __aeabi_memset8
.......
4BED3C10:    LDR             R2, [R6,#0x24]
4BED3BE8:    BL             __aeabi_memset8
..........
4BED5130:    LDR             R0, [R6,#0x4C]
4BED5132:    NOP
4BED5134:    BL             sub_4004112C    //strlen
.........
//循环开始
4BED8610:    LDR             R0, [R6,#0x64]
........
4BED6056:    CMP             R2, #0xFF
4BED6038:    MOV             R2, R0
4BED603A:    BGT             unk_4BED6048   //跳出循环
........
4BED6BA6:    LDR             R1, [R0,#0x20]
4BED6D10:    LDR             R0, [R0,#0x5C]
4BED6D12:    NOP
4BED6D14:    BL             sub_4BEE7F78  //R0 = R0 % R1
...........
4BED1384:    STR             R1, [R6,#0x60]
4BED1386:    NOP
//循环结束  这里和KeyTable表生成有关系,后面还有一段循环算法
4BED188A:    CMP             R2, #0x3C     //0x3C是由座位长度算出来的
这里要说下如果长度不等于0xF,就返回,不执行下面的反调试,
4BED17CC:    BNE             unk_4BED17D8  //判断长度是否==0xF

//循环开始
4BED3A38:    STR             R0, [R6,#0x44]
............
4BED780A:    LDR             R1, [R0,#0x20]  //R1 = 长度
4BED77B0:    LDR             R0, [R0,#0x64]
4BED77B2:    NOP
4BED77B4:    BL             sub_4BEE7F78   //R0 = R0 % R1

4BED77BC:    LDR             R1, [R6,#0x4C]
4BED7818:    ADDS            R0, R1, R0    //坐标地址++
........
4BED7E14:    LDR             R0, [R6,#0x24] //取[座位地址]
4BED7E16:    LSRS            R0, R1                //循环右移n&7位
4BED7E18:    STR             R0, [R6,#0x20]
.......
4BED3B2A:    ADDS            R0, #1
//循环结束,这段循环相当于下面的功能,lpString 座位字符串 lpBuffer 最后得到一个0x100字节的表
for (i=0;i<0x100;i++)
{
        lpBuffer[i] = cror(lpString[i%0xF],i&7);
}

4BED17E0:    MOV             R0, R1
//4BED2A94:    push {[0x4BED26BC]}  -->119E6FC1//4BED2AA0:    POP             {R1}//4BED2C90:    CMP             R0, R1//4BED2AB0:    BNE             unk_4BED2ABC
4BED2AC4:    BL             sub_4BECBD90   //获取 r_debug->r_brk 函数
........
4BED5498:    MOV             LR, R0
4BED549A:    MOV             R0, LR
4BED549C:    BL             sub_4BEE95D4   //调用r_debug->r_brk 函数,调试状态下,会触发  SIGILL异常,导致程序终止;
........

//循环开始
4BED637C:    STR             R0, [R6,#0x54]
.....
4BED3C4E:    CMP             R2, #0xFF
....
4BED7CC4:    MOVS            R1, #5
4BED7D80:    LDR             R0, [R3,#0x6C]
4BED7D82:    NOP
4BED7D84:    BL             sub_4BEE7F78   //R0 = R0 % 5
.....
4BED7D58:    ADDS            R0, #1
/循环结束 这里和KeyTable表生成有关系,由于是固定的,直接扣内存
........
4BED28DC:    MOVS            R0, #5
4BED2BDC:    BL             sub_400440E4   //raise(5) SIGTRAP 接管函数 4BEC10A4
........
//循环开始
4BED34DC:    LDR             R0, [R6,#0x64]
..........
4BED27CA:    CMP             R2, #0
4BED2800:    MOV             R2, R0
4BED2780:    BNE             unk_4BED278C   //R2 == 1 跳出循环
..........
4BED343E:    LDR             R1, [R3,#0x1C]  //KeyTable
4BED344C:    ADDS            R1, R1, R2      //R1 =KeyTable + ([KeyTable+n-1]+[KeyTable+n]) & 0xFF
4BED342C:    LDRB            R2, [R1]
4BED3400:    LDR             R0, [R6,#0x20]
4BED3402:    STRB            R2, [R0]        //[KeyTable+n] = [KeyTable+[KeyTable+n]]
4BED31D4:    LDR             R2, [R6,#0x24]
4BED31D6:    STRB            R2, [R1]        //[KeyTable+[KeyTable+n]] = [KeyTable+n] 以上代码 相当于互换值
.............
4BED3394:    LDR             R0, [R6,#0x24]  
4BED3384:    LDR             R1, [R6,#0x18]
4BED3308:    ADDS            R0, R1, R0      //前面对换的值相加
...........
4BED37B4:    LDR             R0, [R6,#0x20]
4BED37B6:    LSRS            R0, R0, #5
4BED3774:    MOVS            R1, #6
4BED3776:    ANDS            R1, R0
4BED3778:    BICS            R3, R0
4BED4B9C:    ORRS            R3, R1
4BED4B9E:    EORS            R3, R2  //相当于 [lpBuffer+ n] 循环向左移动3位
...............
4BED07E4:    LDRB            R1, [R0]   //取 [lpBuffer+ n] 循环向左移动3位 的值
4BED07E6:    LDR             R0, [R6,#0x1C]
4BED07E8:    STRB            R1, [R0]   //[lpBuffer+ n] = [lpBuffer+ n] 循环向左移动3位 的值
4BECFEF4:    MOVS            R2, #0x53
4BED0660:    STR             R2, [R6,#0x24]
4BED04F4:    MVNS            R0, R2
4BED04F6:    MOV             R3, R0
4BECFEE4:    BICS            R3, R1
4BECFEE6:    ANDS            R1, R2
4BECFEC0:    ORRS            R1, R3
4BECFEC2:    LDR             R2, [R6,#0x64]
4BECFEC4:    LDR             R3, [R2,#0x1C] //取KeyTable地址
4BED06F0:    LDR             R2, [R6,#0x28] //取之前相加的值
4BED0600:    ADDS            R3, R3, R2
4BED0602:    LDRB            R3, [R3]       //取 KeyTable[取之前相加的值]
4BED0604:    LDR             R2, [R6,#0x24]  
4BECFEB0:    ANDS            R2, R3
4BED0764:    BICS            R0, R3
4BED0766:    ORRS            R0, R2
4BED0754:    EORS            R0, R1
4BED0756:    STR             R0, [R6,#0x28] //保存 KeyTable[取之前相加的值] ^ [lpBuffer+ n] 循环向左移动3位
4BED0744:    LDR             R1, [R6,#0x1C] //lpBuffer+ n
4BED1AB4:    STRB            R0, [R1]      //[lpBuffer+ n] = KeyTable[取之前相加的值] ^ [lpString + n] 循环向左移动3位
............
4BED0D8A:    STR             R0, [R6,#0x28]
//4BED0D40:    push {[0x4BED21F8]}  -->F04DCF0F//4BED0D4C:    POP             {R0}
4BED0D4E:    NOP
//循环结束 这段循环相当于下面的功能
for (i=0;i<0x100;i++)
{
        if (i == 0xFF)
        {
                j = 0;                       
        }
        else
        {
                j = i+1;                       
        }
        t = szKeyTable[j];
        k = (k + szKeyTable[j]) & 0xFF;               
        szKeyTable[j] = szKeyTable[k];
        szKeyTable[k] = t;       
        sum = (szKeyTable[j] + szKeyTable[k]) & 0xFF;
        t = lpBuffer[i];
        t = crol(t,3);
        t = cror((t  ^ szKeyTable[sum]),i&7);
        lpBuffer[i] = t;
}
...........
4BED45E8:    MOVS            R0, #0xB
4BED45EA:    NOP
4BED45EC:    BL             sub_400440E4   //raise(0xB) SIGSEGV    接管函数 4BEC10A4, 这里会循环和V值进行异或,代码如下:
for (i=0;i<0x100;i++)
{
        lpBuffer[i] = crol((lpBuffer[i] ^ 0x29),i&7);
}
.........
//循环开始
4BED5DB0:    LDR             R0, [R6,#0x64]
........
4BED7BE4:    CMP             R2, #0
4BED7BE6:    BNE             loc_4BED7BF4  //R2=0跳出循环
........
4BED727C:    LDR             R0, [R2,#0x7C]  //取 lpBuffer
4BED71E0:    LDR             R0, [R0]
4BED71E2:    LDR             R3, [R6,#0x48]  //取异或值 CRC32效验值
4BED71E4:    MOV             R1, R3
4BED71D0:    BICS            R1, R0
4BED71D2:    BICS            R0, R3
4BED7114:    ORRS            R0, R1
4BED7116:    LDR             R1, [R2,#0x7C]  
4BED71C0:    STR             R0, [R1] //[lpBuffer] = [lpBuffer] ^ CRC32效验值
......
4BED7958:    STR             R1, [R6,#0x3C]
//循环结束 代码如下:
for (i=0;i<0x100;i=i+4)
{
        *(unsigned int*)(&lpBuffer[i]) = *(unsigned int*)(&lpBuffer[i]) ^ 0x39138e20;
}
..........
4BED478C:    LDRB            R1, [R1]       //lpBuffer
//4BED4858:    push {[0x4BED5A08]}  -->1D68ECA3//4BED4864:    POP             {R2}
4BED48B8:    ADDS            R1, R1, R2
4BED47AC:    LDR             R2, [R3,#0x14]  
4BED47AE:    LDRB            R2, [R2]       //正确的表Result,这里直接扣出来
4BED47B0:    SUBS            R1, R1, R2
//4BED4174:    push {[0x4BED5A0C]}  -->E297135D//4BED4180:    POP             {R2}
4BED4182:    ADDS            R1, R1, R2
4BED4184:    STR             R1, [R6,#0x14]   返回值 = [[R6,#0x50],#0x18] - [[R6,#0x50],#0x14],相当于a和b是否相等
//4BED498C:    push {[0x4BED59FC]}  -->87898A83//4BED4998:    POP             {R3}//4BED3F74:    CMP             R1, R3//4BED3F76:    BEQ             unk_4BED3F84
4BED5708:    MOVS            R0, #1  //R0 = 1
4BED570A:    STR             R0, [R6,#0x38]  //默认设置返回值 1
4BED570C:    MOVS            R0, #0  //R0 = 0
4BED56CC:    LDR             R1, [R6,#0x14]
4BED56CE:    CMP             R1, #0   
4BED56D0:    BNE             unk_4BED56DC   //判断 R1!=0 返回0 否则返回 1
4BED56DC:
4BED5694:    STR             R0, [R6,#0x38]  //设置返回值 0
.......
4BED1820:    ADDS            R0, R6, #7
4BED1822:    ADDS            R0, #0xF1
4BED1E64:    LDRB            R0, [R0]    //取返回值
..........
4BED1E74:    SUBS            R4, R7, #7
4BED1E76:    SUBS            R4, #5
4BED1E38:    MOV             SP, R4
4BED1E3A:    POP             {R4-R7,PC}
---------call end---------
到此整个流程基本走完;

先贴下的加解密算法:

unsigned char g_KeyTable[256] = {
        0xF9, 0x6B, 0xE8, 0x1F, 0x2B, 0xBB, 0x3C, 0x60, 0x4A, 0xCC, 0x8B, 0x32, 0x22, 0x1E, 0x39, 0x73,
        0x36, 0x7E, 0x4C, 0x40, 0x41, 0x38, 0x8F, 0x84, 0x37, 0x4F, 0xFA, 0x0B, 0x8D, 0x96, 0x9A, 0xA9,
        0xEF, 0x30, 0x00, 0xCB, 0x99, 0x95, 0xAC, 0x34, 0x27, 0x5B, 0x54, 0x05, 0xD4, 0xC6, 0x28, 0x63,
        0x18, 0xC2, 0x4B, 0x55, 0x89, 0x7A, 0x49, 0x7F, 0xAF, 0x4D, 0x87, 0x26, 0xE3, 0x47, 0x98, 0x79,
        0xD3, 0x71, 0x14, 0x7D, 0xC1, 0x8C, 0x6D, 0xDA, 0xED, 0xA1, 0x0A, 0x3E, 0xF2, 0x76, 0xB2, 0x56,
        0x7C, 0xEA, 0xBF, 0x90, 0x01, 0x5D, 0xBC, 0xDF, 0x0E, 0xA7, 0x6E, 0x8A, 0x67, 0x6A, 0xAE, 0xE6,
        0xCF, 0xB0, 0x1A, 0xFD, 0x50, 0x66, 0xFF, 0xC5, 0x11, 0xEE, 0x97, 0x85, 0xDC, 0xA4, 0xCE, 0xEC,
        0xC7, 0x15, 0x9E, 0x88, 0x0C, 0x94, 0xBE, 0xAA, 0xC0, 0xAD, 0x53, 0x2C, 0x81, 0x3F, 0x68, 0x80,
        0x12, 0x48, 0x02, 0x83, 0x21, 0x1D, 0xB1, 0x16, 0xDD, 0x10, 0x25, 0xD2, 0xA0, 0x77, 0x74, 0x04,
        0xD6, 0xD1, 0xAB, 0xE1, 0xD7, 0x86, 0x23, 0x51, 0x5C, 0x61, 0xE5, 0xBD, 0xE7, 0x5F, 0xF5, 0xD0,
        0x58, 0x2A, 0x42, 0x62, 0xB6, 0x24, 0x92, 0x46, 0xC8, 0x4E, 0xCA, 0x64, 0xD9, 0x65, 0xBA, 0xB7,
        0x9D, 0xA5, 0xE9, 0xDB, 0xB8, 0xF0, 0x7B, 0x3B, 0xB3, 0xF4, 0xDE, 0x70, 0x44, 0xA6, 0x6C, 0x78,
        0x1B, 0x9F, 0xEB, 0x17, 0xE0, 0x9B, 0xFE, 0x13, 0x3D, 0x52, 0x09, 0x75, 0xCD, 0x03, 0x0D, 0xF7,
        0x3A, 0xE4, 0xC9, 0xD8, 0x20, 0xF3, 0x31, 0x45, 0x82, 0xF6, 0x5A, 0xD5, 0x8E, 0xC3, 0x2E, 0x6F,
        0xC4, 0xA2, 0x57, 0x08, 0xE2, 0x2F, 0x59, 0xF8, 0x72, 0x91, 0xB5, 0x43, 0x1C, 0x5E, 0x35, 0xFC,
        0x06, 0x69, 0xA3, 0xB9, 0xFB, 0xA8, 0xB4, 0x93, 0x2D, 0x33, 0x9C, 0x19, 0x07, 0x0F, 0xF1, 0x29
};

unsigned char g_Result[256] = {
        0x6D, 0xC4, 0xAD, 0x34, 0x7C, 0x64, 0x9C, 0x39, 0x17, 0x17, 0x26, 0xA7, 0xBA, 0x18, 0x28, 0xD2,
        0xD5, 0xDD, 0x27, 0xBA, 0x86, 0x2A, 0x0E, 0x27, 0xC4, 0xEE, 0xC4, 0x31, 0xF7, 0x35, 0x62, 0x14,
        0x6C, 0x41, 0x18, 0xB8, 0x77, 0xFA, 0x5C, 0xF0, 0xA4, 0x20, 0x25, 0x90, 0x16, 0x6C, 0x91, 0x43,
        0x49, 0x08, 0xA4, 0x25, 0x8E, 0x84, 0xA1, 0x47, 0x02, 0x74, 0x65, 0xB6, 0xAF, 0xAE, 0x45, 0xD6,
        0x6D, 0x2F, 0x53, 0x93, 0xEA, 0xF2, 0xD4, 0x88, 0xF3, 0xDB, 0x87, 0x38, 0xE0, 0xFC, 0x7E, 0x1C,
        0x8E, 0x73, 0x82, 0x6D, 0x6A, 0x27, 0xE2, 0xE8, 0x64, 0x27, 0x1B, 0x97, 0x0D, 0x32, 0x05, 0xC9,
        0xC6, 0x7E, 0xBF, 0x9F, 0xB8, 0xDB, 0xCC, 0x85, 0x36, 0xB9, 0xAB, 0xE5, 0xA7, 0xF9, 0x76, 0xFB,
        0x0B, 0x91, 0x71, 0x16, 0x40, 0x49, 0x41, 0x81, 0x4C, 0xC9, 0x92, 0x47, 0x93, 0x3B, 0x52, 0x8C,
        0xA5, 0xE1, 0x66, 0x3A, 0xB0, 0x33, 0x90, 0x86, 0xA5, 0x20, 0x9D, 0xBF, 0xF4, 0x71, 0x96, 0x2D,
        0xAD, 0x52, 0x1D, 0x09, 0xCC, 0x92, 0xC1, 0xE3, 0x73, 0x06, 0xFB, 0x92, 0xA7, 0xF5, 0x38, 0xA7,
        0x1A, 0xFE, 0x09, 0x37, 0x5B, 0xAB, 0xD9, 0x89, 0x96, 0xA2, 0x8A, 0x1A, 0xF6, 0xC9, 0x2B, 0xC6,
        0x8E, 0x35, 0x07, 0x33, 0x87, 0xD1, 0xA7, 0xE3, 0x4C, 0x82, 0x4B, 0x89, 0x03, 0x1A, 0x8E, 0x7F,
        0x7D, 0x85, 0xE4, 0xB5, 0xFB, 0xF9, 0x1E, 0xF6, 0xF7, 0xAC, 0x79, 0x62, 0x4B, 0xB4, 0x01, 0xC4,
        0x99, 0x67, 0xF6, 0x7D, 0x2D, 0xF2, 0xA8, 0xA6, 0x6B, 0x1F, 0x67, 0xE4, 0x59, 0x15, 0xC9, 0xEF,
        0x26, 0x18, 0xC4, 0x2C, 0xD6, 0x54, 0xFE, 0x33, 0xBE, 0xF3, 0x8C, 0xCF, 0xF8, 0x5B, 0x5F, 0x06,
        0x2B, 0xC2, 0x18, 0xB6, 0x80, 0x1D, 0xBA, 0x93, 0xFF, 0x11, 0xE9, 0x2A, 0x5D, 0xA3, 0x95, 0x97
};

unsigned char crol(unsigned char c,unsigned char b)
{  
        unsigned char left=c<<b;  
        unsigned char right=c>>(sizeof(unsigned char)*8-b);
        unsigned char temp=left|right;  
        return temp;  
}  
unsigned char cror(unsigned char c,unsigned char b)
{  
        unsigned char right =c>>b;  
        unsigned char left =c<<(sizeof(unsigned char)*8-b);
        unsigned char temp=left|right;  
        return temp;  
}  

void Encode(unsigned char* lpString,unsigned char* lpBuffer,unsigned char* lpKeyTable)
{
        unsigned char t,k = 0,sum;
        int i,j;
        unsigned char szKeyTable[256] = {NULL};
        memcpy(szKeyTable,lpKeyTable,sizeof(szKeyTable));

        for (i=0;i<0x100;i++)
        {
                lpBuffer[i] = cror(lpString[i%0xF],i&7);
        }

        for (i=0;i<0x100;i++)
        {
                if (i == 0xFF)
                {
                        j = 0;                       
                }
                else
                {
                        j = i+1;                       
                }
                t = szKeyTable[j];
                k = (k + szKeyTable[j]) & 0xFF;               
                szKeyTable[j] = szKeyTable[k];
                szKeyTable[k] = t;       
                sum = (szKeyTable[j] + szKeyTable[k]) & 0xFF;
                t = lpBuffer[i];
                t = crol(t,3);
                t = cror((t  ^ szKeyTable[sum]),i&7);
                lpBuffer[i] = t;
        }
       
        for (i=0;i<0x100;i++)
        {
                lpBuffer[i] = crol((lpBuffer[i] ^ 0x29),i&7);
        }

        for (i=0;i<0x100;i=i+4)
        {
                *(unsigned int*)(&lpBuffer[i]) = *(unsigned int*)(&lpBuffer[i]) ^ 0x39138e20;
        }
}

void Decode(unsigned char* lpString,unsigned char* lpBuffer,unsigned char* lpKeyTable)
{
        unsigned char t,k = 0,sum;
        int i,j;
        unsigned char szKeyTable[256] = {NULL};
        memcpy(szKeyTable,lpKeyTable,sizeof(szKeyTable));       

        for (i=0;i<0x100;i=i+4)
        {
                *(unsigned int*)(&lpBuffer[i]) = *(unsigned int*)(&lpBuffer[i]) ^ 0x39138e20;
        }

        for (i=0;i<0x100;i++)
        {
                lpBuffer[i] = cror(lpBuffer[i],i&7) ^ 0x29;
        }
        memcpy(g_Temp,lpBuffer,256);
        for (i=0;i<0x100;i++)
        {
                if (i == 0xFF)
                {
                        j = 0;                       
                }
                else
                {
                        j = i+1;                       
                }
                t = szKeyTable[j];
                k = (k + szKeyTable[j]) & 0xFF;               
                szKeyTable[j] = szKeyTable[k];
                szKeyTable[k] = t;       
                sum = (szKeyTable[j] + szKeyTable[k]) & 0xFF;
                t = lpBuffer[i];   //73
                t = crol(t,i&7) ^ szKeyTable[sum];  //73 ^ 06
                t = cror(t,3);                //ae
                lpBuffer[i] = t;
        }       
       
        for (i=0;i<0xF;i++)
        {
                lpString[i%0xF] = crol(lpBuffer[i],i&7);               
        }
}

int _tmain(int argc, _TCHAR* argv[])
{
        unsigned char lpBuffer[256] = {NULL};
        unsigned char lpString[16] = {NULL};
        Decode(lpString,g_Result,g_KeyTable);
        printf("座位号:%s\n",lpString);       
}
座位号:L6^a9>z<81Li!*M

四,总结
1,指令恢复,编写idc脚本识别上面的三种乱序指令和一种变形BL,基本上能恢复全部的代码,关键点是对循环和循环跳出的识别,这里,循环我是人工肉眼识别,循环的跳出,我是用idc脚本,记录循环内每个相反跳,然后依次断点,然后跳出,继续idc跑下面代码;
2,CRC32效验值,也相当于反调试检测,由于效验了代码,下的断点会影响CRC32效验值,导致后面异或出的lpBuffer出问题,得到不正确的加解密结果;那问题来了,怎么取这个效验值,idc 脚本dump 出来,然后单独CRC32算出效验值 0x39138e20;
3,长度等于0xF的判断,不等于,下面的代码都得不到执行,无从分析;
4,r_debug->r_brk 反调试检测,这是个啥东西,可以百度一下,ida会修改调试的进程里的r_debug->r_brk函数的指令为非法指令,写程序注入进去会看到r_debug->r_brk:
没调试: 0x70 0x47      BX  LR,
调试下: 0x10 0xDE      异常指令
所以,调试的时候,在调用这个函数,程序就中断退出了;
具体代码看恢复函数下的:复制r_debug--r_brk函数.txt、获取DT_DEBUG地址函数.txt、内存拷贝r_debug--r_brk函数.txt;
获取debug_r_brk代码如下:
void printf_r_debug_r_brk()
{
        void * libc  = dlopen("libdl.so",0);
        LOGI("%x,%s",libc,libc);
        if (libc)dlclose(libc);
        unsigned int t = *(unsigned int*)((unsigned int)libc + 0x98);
        LOGI("%x",t);
        t = *(unsigned int*)((unsigned int)libc + 0xA4);
        LOGI("%s",t);
        t = *(unsigned int*)(t + 0x98);

        while (1)
        {               
                printf("%X,%X\n",*(unsigned int*)t,*(unsigned int*)(t+4));
                if (*(unsigned int*)t == 0x15)
                {
                        LOGI("-------%X",*(unsigned int*)(*(unsigned int*)(t+4) + 8));
                        for(int i=0;i < 16;i++)
                        {
                                LOGI("%02X ",*(unsigned char*)(*(unsigned int*)(*(unsigned int*)(t+4) + 8) + i - 1));
                        }
                        LOGI("-------");
                }
                if (*(unsigned int*)t == 0)break;
                t = t + 8;
        }
}
5,raise(0x5)和raise(0xB) 异常的作用,反调试是一部分,可以隐藏加密函数部分算法到这两个异常的接管函数里面,增加逆向难度;
具体代码看恢复函数下的: SIGTRAP异常接管函数.txt 和 SIGSEGV异常接管函数.txt ;
6,关于那个异或的V值 = 0x29 的来源,这个值,是用idc 脚本跑出来的,跑的时候需要修改CRC32效验值;调试的时候,会看到这个值会变,估计也是保护程序的以一种手段吧;具体怎么来没搞清楚,有研究出来的,请告知,谢谢!
7,附件里有idc脚本和恢复的一些函数,idc、脚本写得有点烂,勉强能用,基地址0x4BEBF000;


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (29)
雪    币: 76
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
楼主威武
2015-10-28 22:21
0
雪    币: 8
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
牛人啊,谢谢楼主分享
2015-10-29 09:07
0
雪    币: 250
活跃值: (65)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
看到这密码就感觉是压轴的·····
2015-10-29 09:42
0
雪    币: 275
活跃值: (320)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
高手就是不一样,这下又可以愉快的研究了
2015-10-29 09:56
0
雪    币: 562
活跃值: (4347)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
顶牛掰的楼主,手下我的膝盖
2015-10-29 09:56
0
雪    币: 107
活跃值: (404)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
牛了个比啊..膜拜了...这个月的膝盖给楼主了......
2015-10-29 10:41
0
雪    币: 60
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
lz好厉害
2015-10-29 15:03
0
雪    币: 190
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
给楼主拜
2015-10-29 15:32
0
雪    币: 29
活跃值: (499)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
10
lz厉害!感谢分享~
2015-10-29 21:37
0
雪    币: 20
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
lz牛逼啊。。
2015-10-29 22:04
0
雪    币: 608
活跃值: (403)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
12
lz请收下我的膝盖!!!
2015-10-30 10:03
0
雪    币: 35
活跃值: (139)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
13
牛逼,感谢分享
2015-10-30 17:09
0
雪    币: 105
活跃值: (211)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
14
碉堡了。
2015-10-30 18:50
0
雪    币: 79
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
楼主太强,IDC脚本代码都写了快1000行
2015-10-31 21:33
0
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
16
mark
2015-11-5 22:50
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
简直是神了,尔等还是回家修炼吧
2015-11-6 16:46
0
雪    币: 21
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
看不懂啊~~~~
2015-11-11 12:04
0
雪    币: 130
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
楼主威武!真的很辛苦
2015-11-24 13:50
0
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
弱弱的问一下,楼主这次挑战赛得了几分呀
2015-11-25 08:48
0
雪    币: 24
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
楼主威武!真的很辛苦,请问r_debug->r_brk函数触发SIGILL异常如何实现?
2016-2-22 00:11
0
雪    币: 407
活跃值: (125)
能力值: ( LV13,RANK:280 )
在线值:
发帖
回帖
粉丝
23
直接 call r_debug->r_brk 函数即可
2016-2-23 10:03
0
雪    币: 112
活跃值: (27)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
24
@EricAzhe

大神您好,请教您一个问题,就开头所说的三种乱序指令:
“--------------------------------------------------------------------
二,说下乱序指令和变形BL:
3种乱序指令:
1种:
PUSH            {R0,R1,LR}
LDR             R0, XXXXXXXX
BL         XXXXXXXX
相当于BL lr + Dword(2 *  (lr >> 1) + 4 * r0 + 4) - 1;
2种:
PUSH            {R0,R1,LR}
BL         XXXXXXXX
相当于BL lr + Dword(2 * (lr >> 1)) - 1;
这种指令有种变形,直接 BL       模块基地址+0x2A5B4
3种:
SUB             SP, SP, #8
PUSH            {R0,R1,LR}
BL              XXXXXXXX
--------------------------------------------------------------------”

我最多能看出堆栈不平衡,但是“相当于BL lr + Dword(2 *  (lr >> 1) + 4 * r0 + 4) - 1;”,这个是怎么转换的?

从右往左压入R0,R1,LR,lr存储着返回地址,“2 *  (lr >> 1) ”-->右移之后再乘2不还是lr吗?

请百忙中抽点时间给我解惑一下可以吗,先谢谢了
2016-4-26 14:50
0
雪    币: 407
活跃值: (125)
能力值: ( LV13,RANK:280 )
在线值:
发帖
回帖
粉丝
25
不是,lr = 0x21 ,2 *  (lr >> 1) = 0x20
2016-5-4 11:37
0
游客
登录 | 注册 方可回帖
返回
//