首页
社区
课程
招聘
[原创]看雪2016 第二题 CrackMe逆向分析
发表于: 2016-11-5 15:01 5827

[原创]看雪2016 第二题 CrackMe逆向分析

2016-11-5 15:01
5827
使用OD加载crackme,发现有LUA虚拟机,拖入IDA中定位lua_load函数如下
.text:004045FC lua_load_t      proc near               ; CODE XREF: luaL_loadfilex+125p
.text:004045FC                                         ; sub_405A88+23p ...
.text:004045FC
.text:004045FC var_14          = byte ptr -14h
.text:004045FC arg_0           = dword ptr  8
.text:004045FC arg_4           = dword ptr  0Ch
.text:004045FC arg_8           = dword ptr  10h
.text:004045FC arg_C           = dword ptr  14h
.text:004045FC arg_10          = dword ptr  18h
.text:004045FC
.text:004045FC                 push    ebp
.text:004045FD                 mov     ebp, esp
.text:004045FF                 sub     esp, 14h
.text:00404602                 cmp     [ebp+arg_C], 0
.text:00404606                 jnz     short loc_40460F
.text:00404608                 mov     [ebp+arg_C], offset a?_1 ; "?"
.text:0040460F
.text:0040460F loc_40460F:                             ; CODE XREF: lua_load_t+Aj
.text:0040460F                 push    ebx
.text:00404610                 push    esi

OD中下断,根据参数找到buffer,将buffer数据导出,如下:
1B 6C 73 11 00 19 93 0D 0A 1A 0A 04 04 04 08 08
78 56 00 00 00 00 00 00 00 00 00 00 00 28 77 40
01 0E 40 54 61 73 6B 42 65 67 69 6E 2E 6C 73 00
00 00 00 00 00 00 00 00 02 02 07 00 00 00 08 40
....

发现文件头被修改过,将头结构修复如下:
1B 4C 75 61 53 00 19 93 0D 0A 1A 0A 04 04 04 08
08 78 56 00 00 00 00 00 00 00 00 00 00 00 28 77
40 01 0E 40 54 61 73 6B 42 65 67 69 6E 2E 6C 73
00 00 00 00 00 00 00 00 00 02 02 07 00 00 00 08
40
......

使用luadec.exe 工具将上述数据反编译得到,lua5.3的脚本:
g_strRegSn = " "
g_strRegSnToVerify = ""
userRegister = function(strRegSnIn)
  -- function num : 0_0 , upvalues : _ENV
  local iRc = -1
  g_strRegSn = strRegSnIn
  g_strRegSnToVerify = fnGetRegSnToVerify()
  g_strRegSn = fnCalcUserInputRegSnAfterEnc(g_strRegSn)
  if g_strRegSn == g_strRegSnToVerify then
    iRc = 1024
  end
  g_strRegSnToVerify = ""
  return iRc
end

getRegSnAfterCalc = function(strRegSnIn)
  -- function num : 0_1 , upvalues : _ENV
  return g_strRegSn
end

通过分析可知,fnGetRegSnToVerify和fnCalcUserInputRegSnAfterEnc为关键回调C函数,下面定位这两个函数
使用IDA定位luaD_precall
.text:00409DAD luaD_precall    proc near               ; CODE XREF: sub_40A1CF+21p
.text:00409DAD                                         ; sub_40A3F6+50p ...
.text:00409DAD
.text:00409DAD arg_0           = dword ptr  8
.text:00409DAD arg_4           = dword ptr  0Ch
.text:00409DAD arg_8           = word ptr  10h
.text:00409DAD
.text:00409DAD                 push    ebp
.text:00409DAE                 mov     ebp, esp
.text:00409DB0                 push    ebx
.text:00409DB1                 push    esi
.text:00409DB2                 push    edi
.text:00409DB3                 mov     edi, [ebp+arg_4]
.text:00409DB6                 mov     esi, [ebp+arg_0]
.text:00409DB9                 mov     eax, [edi+8]
.text:00409DBC                 and     eax, 3Fh
.text:00409DBF                 cmp     eax, 6

OD中下断跟踪关键跳转
00409F04  |. FF55 0C        CALL DWORD PTR SS:[EBP+C]
00409F07  |. 8B4E 0C        MOV ECX,DWORD PTR DS:[ESI+C]
00409F0A  |. 50             PUSH EAX
00409F0B  |. C1E0 04        SHL EAX,4
首先来到fnGetRegSnToVerify函数
004019A2   . FF7424 04      PUSH DWORD PTR SS:[ESP+4]
004019A6   . E8 F11C0000    CALL CrackMe.0040369C
004019AB   . 85C0           TEST EAX,EAX
004019AD   . 59             POP ECX
004019AE   . 75 13          JNZ SHORT CrackMe.004019C3
004019B0   . 6A 20          PUSH 20
004019B2   . 68 44D24200    PUSH CrackMe.0042D244
004019B7   . FF7424 0C      PUSH DWORD PTR SS:[ESP+C]
004019BB   . E8 50220000    CALL <CrackMe.lua_pushlstring>
004019C0   . 83C4 0C        ADD ESP,0C
004019C3   > 6A 01          PUSH 1
004019C5   . 58             POP EAX
004019C6   . C3             RETN

紧接着是fnCalcUserInputRegSnAfterEnc函数
004019C7 >/. 55             PUSH EBP                                 ;  fnCalcUserInputRegSnAfterEnc
004019C8  |. 8BEC           MOV EBP,ESP
004019CA  |. 51             PUSH ECX
004019CB  |. 51             PUSH ECX
004019CC  |. 8365 F8 00     AND DWORD PTR SS:[EBP-8],0
004019D0  |. 8365 FC 00     AND DWORD PTR SS:[EBP-4],0
004019D4  |. 56             PUSH ESI
004019D5  |. 57             PUSH EDI
004019D6  |. 8B7D 08        MOV EDI,DWORD PTR SS:[EBP+8]
004019D9  |. 57             PUSH EDI
004019DA  |. E8 BD1C0000    CALL CrackMe.0040369C
004019DF  |. 83F8 01        CMP EAX,1
004019E2  |. 59             POP ECX
004019E3  |. 75 51          JNZ SHORT CrackMe.00401A36
004019E5  |. 8D45 FC        LEA EAX,DWORD PTR SS:[EBP-4]
004019E8  |. 50             PUSH EAX
004019E9  |. 6A 01          PUSH 1
004019EB  |. 57             PUSH EDI
004019EC  |. E8 C5410000    CALL CrackMe.00405BB6
004019F1  |. 8BF0           MOV ESI,EAX

fnGetRegSnToVerify每次返回固定数据如下:
0042D244  A4 47 98 0C 9E 40 D7 F6 EB 76 6E 6D 7E A3 3E EB  ?濦做雟nm~??
0042D254  D5 51 30 06 7D C0 FB 6C C2 7A 43 C5 A4 C9 B1 FD  誕0}利l聑C扭杀?

通过lua脚本可知,该数据与fnCalcUserInputRegSnAfterEnc函数的结果对比
分析fnCalcUserInputRegSnAfterEnc的流程为:
strRegSnIn = (sn^t2)^t1;
t1和t2为固定32字节数据,这里有个小技巧可以将fnGetRegSnToVerify的输出作为fnCalcUserInputRegSnAfterEnc的输入即可解密,如下:
020F6BF8  4B 7D 6F 22 BD EA 61 C3 0B E7 B2 D9 2C 6B 41 88  K}o"疥a?绮?kA?
020F6C08  5D 71 27 85 BA 71 F0 B9 23 77 28 6C FC 36 A6 D0  ]q'吅q鸸#w(l?π

将上述结果为逆向即可得序列号,为此可先定位正向加密算法。
通过断点GetWindowTextA可得到输入缓冲区,对缓冲区下范围断点,几次后来到加密函数,如下:
0040315B  /$ 55             PUSH EBP
0040315C  |. 8BEC           MOV EBP,ESP
0040315E  |. 83EC 1C        SUB ESP,1C
00403161  |. 8365 FC 00     AND DWORD PTR SS:[EBP-4],0
00403165  |. 57             PUSH EDI
00403166  |. 8BF9           MOV EDI,ECX
00403168  |. 837F 08 00     CMP DWORD PTR DS:[EDI+8],0
0040316C  |. 0F86 3C010000  JBE CrackMe.004032AE
00403172  |. 53             PUSH EBX
00403173  |. 56             PUSH ESI
00403174  |> 8B75 FC        /MOV ESI,DWORD PTR SS:[EBP-4]
00403177  |. 8B87 94000000  |MOV EAX,DWORD PTR DS:[EDI+94]
0040317D  |. 0377 04        |ADD ESI,DWORD PTR DS:[EDI+4]
00403180  |. 8D9F 90000000  |LEA EBX,DWORD PTR DS:[EDI+90]
00403186  |. C745 F4 100000>|MOV DWORD PTR SS:[EBP-C],10
0040318D  |. 2906           |SUB DWORD PTR DS:[ESI],EAX
0040318F  |. 8B87 98000000  |MOV EAX,DWORD PTR DS:[EDI+98]
00403195  |. 2946 08        |SUB DWORD PTR DS:[ESI+8],EAX
00403198  |> 8B4E 08        |/MOV ECX,DWORD PTR DS:[ESI+8]
0040319B  |. 8B46 0C        ||MOV EAX,DWORD PTR DS:[ESI+C]
0040319E  |. DD05 70E24200  ||FLD QWORD PTR DS:[42E270]
004031A4  |. 894E 0C        ||MOV DWORD PTR DS:[ESI+C],ECX
004031A7  |. 8B4E 04        ||MOV ECX,DWORD PTR DS:[ESI+4]
004031AA  |. 894E 08        ||MOV DWORD PTR DS:[ESI+8],ECX

结合IDA逆向得出代码

i = 0;
    do
    {
      cur_ptr = ptr + i;
      s3 = v1 + 144;
      count = 16;

      cur[0] -= s2[0];
      cur[2] -= s2[1];
      do
      {
        v4 = cur[3];
        cur[3] = cur[2];
        cur[2] = cur[1];
        cur[1] = cur[0];
        cur[0] = v4;

        v10 = ROL(cur[1] * (2 * cur[1] + 1), 5);
        v14 = ROL(cur[3] * (2 * cur[3] + 1), 5);

        cur[0] = v10 ^ ROR(cur[0] - *(_DWORD *)(s3 - 4), v14&0x1F);
        cur[2] = v14 ^ ROR(cur[2] - *(_DWORD *)s3, v10&0x1F);

        s3 -= 8;
        count--;
        
      }while ( count > 0 );

      cur[1] -= s4[0];
      cur[3] -= s4[1];

      i += 16;
    }while ( i < len );

写程序实现逆运算求出序列号,程序代码如下:

unsigned char gs2[] = { 0x6D, 0x07, 0xAF, 0x7F, 0x4C, 0xFA, 0xD7, 0x9B };
unsigned char gs3[] = {
        0x7A, 0xDA, 0x48, 0x47, 0xFC, 0x42, 0x43, 0xA4, 0x54, 0x76, 0x72, 0xB2, 0x9F, 0x11, 0xF9, 0xD3,
        0x52, 0x4F, 0xF0, 0x8C, 0xBE, 0x64, 0x65, 0x44, 0x2E, 0x0A, 0xD4, 0xB4, 0x67, 0x64, 0x96, 0x02,
        0xA5, 0xBA, 0xF2, 0xA3, 0x40, 0x30, 0xD9, 0x89, 0x8C, 0x36, 0x4B, 0xDC, 0xAB, 0x2F, 0x4D, 0x45,
        0xD7, 0x95, 0x07, 0xC4, 0x3B, 0xFD, 0x98, 0xE1, 0x02, 0x2F, 0x7D, 0x2F, 0xDB, 0xAA, 0x09, 0x37,
        0xD2, 0x2B, 0x88, 0xAC, 0xF5, 0x9B, 0x55, 0x20, 0xF6, 0x01, 0xB5, 0x69, 0x98, 0x4F, 0xD1, 0xA9,
        0x70, 0x40, 0x9E, 0xDC, 0x2B, 0x7D, 0xB9, 0x1F, 0x1F, 0xB2, 0xA0, 0x14, 0x5F, 0x49, 0xE1, 0xEA,
        0x50, 0x1E, 0x41, 0xD8, 0xEA, 0x22, 0xD6, 0x94, 0xE5, 0x8F, 0x56, 0xCD, 0x36, 0x63, 0x10, 0x32,
        0x1F, 0xF0, 0xF7, 0x09, 0xD9, 0xF6, 0x5C, 0x5E, 0xA0, 0x25, 0x2F, 0xBE, 0x92, 0x63, 0x9C, 0x2E,
        0xD1, 0x6D, 0xEA, 0xBB                                    

};
unsigned char gs4[] = { 0x37, 0x66, 0xF7, 0x5B, 0x7A, 0xDA, 0x48, 0x47 };

int ROL(unsigned int a1, char a2)
{
        return (a1 << a2) | (a1 >> (32 - a2));
}

int ROR(unsigned int a1, char a2)
{
        return (a1 >> a2) | (a1 << (32 - a2));
}

int encode(unsigned char *ptr, int len)
{
        unsigned long *s2 = (unsigned long *)gs2;
        unsigned long *s4 = (unsigned long *)gs4;
        unsigned long *cur = 0;
        int i = 0;
       
        do{
                cur = (unsigned long *)(ptr + i);
                unsigned char *s3 = gs3+(sizeof(gs3)-4);
                int count = 16;

                cur[0] -= s2[0];
                cur[2] -= s2[1];
                do
                {
                        unsigned long v4 = cur[3];
                        cur[3] = cur[2];
                        cur[2] = cur[1];
                        cur[1] = cur[0];
                        cur[0] = v4;

                        unsigned long v10 = ROL(cur[1] * (2 * cur[1] + 1), 5);
                        unsigned long v14 = ROL(cur[3] * (2 * cur[3] + 1), 5);

                        cur[0] -= *(unsigned long *)(s3 - 4);
                        cur[0] = ROR(cur[0], v14 & 0x1F);
                        cur[0] ^= v10;

                        cur[2] -= *(unsigned long *)s3;
                        cur[2] = ROR(cur[2], v10 & 0x1F);
                        cur[2] ^= v14;

                        s3 -= 8;
                        count--;

                } while (count > 0);

                cur[1] -= s4[0];
                cur[3] -= s4[1];

                i += 16;
        } while (i < len);
        return 0;
}

int decode(unsigned char *ptr, int len)
{
        unsigned long *s2 = (unsigned long *)gs2;
        unsigned long *s4 = (unsigned long *)gs4;
        unsigned long *cur = 0;
        int i = 0;

        do{
                cur = (unsigned long *)(ptr + i);
                unsigned char *s3 = gs3;
                int count = 16;

                cur[1] += s4[0];
                cur[3] += s4[1];
                do
                {
                        s3 += 8;

                        unsigned long v10 = ROL(cur[1] * (2 * cur[1] + 1), 5);
                        unsigned long v14 = ROL(cur[3] * (2 * cur[3] + 1), 5);

                        cur[0] ^= v10;
                        cur[0] = ROL(cur[0], v14 & 0x1F);
                        cur[0] += *(unsigned long *)(s3 - 4);

                        cur[2] ^= v14;
                        cur[2] = ROL(cur[2], v10 & 0x1F);
                        cur[2] += *(unsigned long *)s3;

                        unsigned long v4 = cur[0];
                        cur[0] = cur[1];
                        cur[1] = cur[2];
                        cur[2] = cur[3];
                        cur[3] = v4;
                       
                        count--;

                } while (count > 0);

                cur[0] += s2[0];
                cur[2] += s2[1];

                i += 16;
        } while (i < len);
        return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
        unsigned char sn[] = { "11111111111111111111111111111111111111111111111111111111111111111" };
        encode(sn, 32);

        decode(sn, 32);

        unsigned char sr[] = {
                0x4B, 0x7D, 0x6F, 0x22, 0xBD, 0xEA, 0x61, 0xC3, 0x0B, 0xE7, 0xB2, 0xD9, 0x2C, 0x6B, 0x41, 0x88,
                0x5D, 0x71, 0x27, 0x85, 0xBA, 0x71, 0xF0, 0xB9, 0x23, 0x77, 0x28, 0x6C, 0xFC, 0x36, 0xA6, 0xD0
        };
        decode(sr, 32);

        return 0;
}

最终序列号为:
+                sr        0x001efde0 "stK5CKpBsw7TPF45"        unsigned char[0x00000020]

[峰会]看雪.第八届安全开发者峰会10月23日上海龙之梦大酒店举办!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (9)
雪    币: 1355
活跃值: (334)
能力值: ( LV13,RANK:920 )
在线值:
发帖
回帖
粉丝
2
对称算法采用了RC6
2016-11-6 12:12
0
雪    币: 96
活跃值: (10)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
膜拜.........
2016-11-6 12:44
0
雪    币: 3053
活跃值: (891)
能力值: ( LV13,RANK:1300 )
在线值:
发帖
回帖
粉丝
4
感谢 爱琴海
2016-11-7 14:53
0
雪    币: 223
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
能不能请教下楼主是怎么制作的sig文件的,我尝试了好久都没能把lua的sig文件弄出来......
2016-12-4 11:17
0
雪    币: 3053
活跃值: (891)
能力值: ( LV13,RANK:1300 )
在线值:
发帖
回帖
粉丝
6
在lua_load函数下断啊,有一个参数是buffer,之后dump出来了。
2016-12-4 11:42
0
雪    币: 223
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
一开始我是自己编译了一个release版的lua5.3的lib文件,然后提取obj文件去使用pcf,但是失败了,后来用debug版本的lib去成功制作出来了一个sig文件,不过加载进ida里面还是不能识别出任何lua函数,如果楼主能指点一下的话不胜感激
2016-12-4 11:43
0
雪    币: 223
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这个dump lua脚本的过程我能理解,只是利用ida的sig文件去识别lua函数这个地方弄了好久都没有弄成功
2016-12-4 11:45
0
雪    币: 3053
活跃值: (891)
能力值: ( LV13,RANK:1300 )
在线值:
发帖
回帖
粉丝
9
你说的那种我也不会,我是手动根据lua函数特征识别的。好在不需要识别所有的,只需要特殊的几个就够了。
2016-12-4 11:52
0
雪    币: 223
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
好!感谢你哈,你的教程写的很详细,我再仔细学习下
2016-12-4 12:11
0
游客
登录 | 注册 方可回帖
返回
//