首页
社区
课程
招聘
[原创]第一阶段第二题:找规律玩填字游戏
2008-10-7 12:24 7129

[原创]第一阶段第二题:找规律玩填字游戏

2008-10-7 12:24
7129
bp GetWindowTextA下断,运行后随便输入name和key点击“确定”,中断下来后ALT+F9返回到用户领空:
00403C22   call      <jmp.&MFC42.#3874_CWnd::GetWindowTextA>
00403C27   mov    ecx, dword ptr [ebp-4]
向下不远处:
00403C84   mov     edx, dword ptr [ebp-8]
00403C87   push      edx
00403C88    mov       eax, dword ptr [ebp-C]
00403C8B   push      eax
00403C8C   call      00401113
跟进就是处理name和key的。
由代码:
00403335    cmp     eax, 0C      ;  用户名长度必须是12
00403338    je      short 0040334E
知道用户名长度必须是12;从后面的分析还知道name串中不能有重复的字符。
向下跟到:
00403598    cmp     eax, 35      ;  key长度要大于35H
0040359B   jnb       short 004035B1
知道key长度要大于等于53,从后面的分析还知道key串中字符只可能取‘0’,‘1’,‘2’,‘3’中的一个。向下跟到:
00403615     push      ecx
00403616     lea       edx, dword ptr [ebp-100]
0040361C    push      edx
0040361D     lea       eax, dword ptr [ebp-2B0]
00403623    push      eax          ;  key每个字符减30H后按DWORD存放
00403624     call     00401087
……
00401087     jmp       004022A0
……
便到了主要部分了,sub_004022A0函数是用来验证key的,其复杂程度相当大!!!好多人在此放弃了,sub_004022A0函数共有三个参数,第一个dwKey是输入的key串逐个字符转换成整型后存储的内存地址;第二个参数buffer是存储生成的字符串的缓冲区,大小为24字节;第三个参数a3为:{0,1,2,3,4,5,6,7,8,9,0x0A,0x0B};现将整理后的C代码贴出来分析:

/*[第一阶段◇第二题]看雪论坛.腾讯公司2008软件安全技术竞赛
            拨开乌云见月明 
        key验证函数反汇编代码
            singsing整理
*/


typedef long DWORD;
typedef char byte;


int __cdecl sub_4022A0(DWORD dwKey[], byte buffer[], DWORD a3[])
{
    int result;  
    signed int v4;  
    int v5; 
    DWORD v6[12];  
    int v7;  
    signed int v8;  
    signed int v9;  
    int v10;  
    int v11;  
    byte v12[192];  
    int v13;  
    int v14;  
    int v15;  
    int v16;  
    int v17;  
    int v18;  

    v4 = 0;
    v5 = 0;
    while ( v4 < 12 )
    {
        result = v4;
        v6[v4++] = 30;
    }
    v7 = 0;
    v8 = 0;
    if ( dwKey[0] > 0 )
    {
        result = 12 / dwKey[0];
        v7 = 12 / dwKey[0];
    }
    v9 = 0;
    while ( v8 < 2 )
    {
        if ( v9 > 0 )
            v6[a3[v9-1]] = 30;
        v9 = 0;
        while ( v9 < 12 )
        {
            if ( v9 > 0 )
                v6[a3[v9-1]] = 30;
            if ( !v8 )
                v6[a3[v9++]] = 40;
            if ( v8 == 1 )
                v6[a3[v9++]] = 20;
            v10 = 0;
            v4 = 0;
            while ( v4 < dwKey[0] )
            {
                v11 = 0;
                while ( v11 < v7 )
                    v12[v4*4+v11++]=v6[v10++];
                ++v4;
            }
            v13 = 0;
            v14 = 0;
            v4 = 0;
            while ( v4 < v7 )
            {
                v13 += v12[4 * dwKey[1] + v4];
                v14 += v12[4 * dwKey[2] + v4];
                v4++;
            }
            if ( v13 == v14 )
            {
                if ( v12[4 * dwKey[3] + dwKey[4]] == v12[4 * dwKey[5] + dwKey[6]] )
                {
                    if ( v12[4 * dwKey[7] + dwKey[8]] == v12[4 * dwKey[9]+ dwKey[10]] )
                        buffer[v5++] = 'l';
                    else
                        buffer[v5++] = 'k';
                }
                else
                {
                    if ( v12[4 * dwKey[7] + dwKey[8]] == v12[4 * dwKey[9]+ dwKey[10]] )
                        buffer[v5++] = 'j';
                    else
                        buffer[v5++] = 'i';
                }
            }
            if ( v13 > v14 )
            {
                v4 = 0;
                v15 = 0;
                v16 = 0;
                v12[4 * dwKey[1] + dwKey[11]] = v12[4 * dwKey[12]+ dwKey[13]];
                v12[4 * dwKey[1] + dwKey[14]] = v12[4 * dwKey[15]+ dwKey[16]];
                v12[4 * dwKey[1] + dwKey[17]] = v12[4 * dwKey[18]+ dwKey[19]];
                v12[4 * dwKey[2] + dwKey[20]] = v12[4 * dwKey[21]+ dwKey[22]];
                v12[4 * dwKey[2] + dwKey[23]] = v12[4 * dwKey[24]+ dwKey[25]];
                v12[4 * dwKey[2] + dwKey[26]] = v12[4 * dwKey[27]+ dwKey[28]];
                while ( v4 < v7 )
                {
                    v15 += v12[4 * dwKey[1] + v4];
                    v16 += v12[4 * dwKey[2] + v4++];
                }
                if ( v15 > v16 )
                {
                    if ( v12[4 * dwKey[29] + dwKey[30]] == v12[4 * dwKey[31]+ dwKey[32]] )
                        buffer[v5++] = 'e';
                    else
                        buffer[v5++] = 'a';
                }
                if ( v15 < v16 )
                {
                    if ( v12[4 * dwKey[33] + dwKey[34]] > v12[4 * dwKey[35]+ dwKey[36]] )
                        buffer[v5++] = 'g';
                    if ( v12[4 * dwKey[33] + dwKey[34]] < v12[4 * dwKey[35]+ dwKey[36]] )
                        buffer[v5++] = 'f';
                    if ( v12[4 * dwKey[33] + dwKey[34]] ==v12[4 * dwKey[35]+ dwKey[36]] )
                        buffer[v5++] = 'h';
                }
                if ( v15 == v16 )
                {
                    v10 = 0;
                    v4 = 0;
                    while ( v4 < dwKey[0] )
                    {
                        v11 = 0;
                        while ( v11 < v7 )
                        v12[4 * v4 + v11++] = v6[v10++];
                        ++v4;
                    }
                    if ( v12[4 * dwKey[37] + dwKey[38]] > v12[4 * dwKey[39]+ dwKey[40]] )
                        buffer[v5++] = 'b';
                    if ( v12[4 * dwKey[37] + dwKey[38]] < v12[4 * dwKey[39]+ dwKey[40]] )
                        buffer[v5++] = 'c';
                    if ( v12[4 * dwKey[37] + dwKey[38]] ==v12[4 * dwKey[39]+ dwKey[40]] )
                        buffer[v5++] = 'd';
                }
            }
            if ( v13 < v14 )
            {
                v4 = 0;
                v17 = 0;
                v18 = 0;
                v12[4 * dwKey[1] + dwKey[11]] = v12[4 * dwKey[12]+ dwKey[13]];
                v12[4 * dwKey[1] + dwKey[14]] = v12[4 * dwKey[15]+ dwKey[16]];
                v12[4 * dwKey[1] + dwKey[17]] = v12[4 * dwKey[18]+ dwKey[19]];
                v12[4 * dwKey[2] + dwKey[20]] = v12[4 * dwKey[21]+ dwKey[22]];
                v12[4 * dwKey[2] + dwKey[23]] = v12[4 * dwKey[24]+ dwKey[25]];
                v12[4 * dwKey[2] + dwKey[26]] = v12[4 * dwKey[27]+ dwKey[28]];
                while ( v4 < v7 )
                {
                    v17 += v12[4 * dwKey[1] + v4];
                    v18 += v12[4 * dwKey[2] + v4++];
                }
                if ( v17 > v18 )
                {
                    if ( v12[4 * dwKey[41] + dwKey[42]] > v12[4 * dwKey[43]+ dwKey[44]] )
                        buffer[v5++] = 'f';
                    if ( v12[4 * dwKey[41] + dwKey[42]] < v12[4 * dwKey[43]+ dwKey[44]] )
                        buffer[v5++] = 'g';
                    if ( v12[4 * dwKey[41] + dwKey[42]] < v12[4 * dwKey[43]+ dwKey[44]] )
                        buffer[v5++] = 'h';
                }
                if ( v17 < v18 )
                {
                    if ( v12[4 * dwKey[45] + dwKey[46]] == v12[4 * dwKey[47]+ dwKey[48]] )
                        buffer[v5++] = 'e';
                    else
                        buffer[v5++] = 'a';
                }
                if ( v17 == v18 )
                {
                    v10 = 0;
                    v4 = 0;
                    while ( v4 < dwKey[0] )
                    {
                        v11 = 0;
                        while ( v11 < v7 )
                        v12[4 * v4 + v11++] = v6[v10++];
                        ++v4;
                    }
                    if ( v12[4 * dwKey[49] + dwKey[50]] > v12[4 * dwKey[51]+ dwKey[52]] )
                        buffer[v5++] = 'c';
                    if ( v12[4 * dwKey[49] + dwKey[50]] < v12[4 * dwKey[51]+ dwKey[52]] )
                        buffer[v5++] = 'b';
                    if ( v12[4 * dwKey[49] + dwKey[50]] ==v12[4 * dwKey[51]+ dwKey[52]] )
                        buffer[v5++] = 'd';
                }
            }
        }// end v9 < 12
        result = v8++ + 1;
    }//end while ( v8 < 2 )
    return result;
}

void main()
{
    DWORD dwKey[53]={3,0,1,2,1,2,0,2,2,2,0,1,1,1,2,1,2,3,1,3,1,2,1,2,2,2,3,2,3,0,0,0,1,0,1,0,2,0,1,0,2,0,1,0,2,0,1,0,0,0,1,0,2};
    DWORD a3[12]={0,1,2,3,4,5,6,7,8,9,0x0A,0x0B};
    char  buffer[50];
    int ret;

    ret=sub_4022A0(dwKey,buffer,a3);
}
 
  可以看到最终生成的buffer串字符是由字符‘a’到‘l’组成的,而且buffer最终长度是24,因此最先想到构造的buffer串是:“abcdefghijklabcdefghijkl”。当然要先确定这个串是否合理,具体方法是在sub_004022A0函数的出口“00402F27   retn”处F4运行至,然后观察buffer的内容,二进制编辑成“abcdefghijklabcdefghijkl”,F9运行发现弹出对话框“ok!”,说明构造的这个buffer是合理的。
  我“悟出”的算法思想是这样的:
外层循环2次,内层循环12次,也就是说尽量在第一次大循环时就构造出“abcdefghijkl”;dwKey[0]不为空,我这里设置为3;将12分割为3块,每块元素有四个(12 div 3)。
  



  每次循环元素40的位置下移一位,上图中把V12的12个关键元素分割为3块,分别标示为0块,1块,2块。如果设置dwKey[1]=0,dwKey[2]=1,则v13和v14是分别求0块和1块4个元素之和的。
  第一次循环中,40在0块的第0个元素,因此v13 > v14,在C代码里找到条件“if ( v13 > v14 )”进去分析。

v12[4 * dwKey[1] + dwKey[11]] = v12[4 * dwKey[12]+ dwKey[13]];
v12[4 * dwKey[1] + dwKey[14]] = v12[4 * dwKey[15]+ dwKey[16]];
v12[4 * dwKey[1] + dwKey[17]] = v12[4 * dwKey[18]+ dwKey[19]];
v12[4 * dwKey[2] + dwKey[20]] = v12[4 * dwKey[21]+ dwKey[22]];
v12[4 * dwKey[2] + dwKey[23]] = v12[4 * dwKey[24]+ dwKey[25]];
v12[4 * dwKey[2] + dwKey[26]] = v12[4 * dwKey[27]+ dwKey[28]];

这段代码是对v12表元素换位用的,暂不分析,向下看v15和v16也是分别求0块和1块4个元素之和的。如果不对v12表的元素进行换位操作,则第一次v15 > v16,满足条件“if ( v15 > v16 )”进去分析。

  if ( v12[4 * dwKey[29] + dwKey[30]] == v12[4 * dwKey[31]+ dwKey[32]] )
            buffer[v5++] = 'e';
  else
            buffer[v5++] = 'a';

欲得到‘a’,则使4 * dwKey[29] + dwKey[30]不等于4 * dwKey[31]+ dwKey[32],则令
4 * dwKey[29] + dwKey[30]=0(此时v12[0]=40),令4 * dwKey[31]+ dwKey[32]=1(此时v12[1]=30),故dwKey[29]= dwKey[30]=0,dwKey[31]=0,dwKey[32]=1.
  如果按上述分析结果重新设置dwKey,则可以得到第一个字符‘a’。由于后面几轮循环仍是v13 > v14,而又想得到字符‘b’,‘c’,‘d’,则要满足v15 == v16,因此就要考虑上面提及的v12表元素换位代码。
经过分析换位形式为:
            0块换用1块元素,换用元素索引为1,2,3
            1块换用2块元素,换用元素索引为1,2,3
只有这样后面连续的3次循环v15 才能等于v16,故有:
4 * dwKey[1] + dwKey[11]=0/1
4 * dwKey[1] + dwKey[14]=0/2
4 * dwKey[1] + dwKey[17]=0/3
4 * dwKey[12]+ dwKey[13]=0/1
4 * dwKey[15]+ dwKey[16]=0/2
4 * dwKey[18]+ dwKey[19]=0/3
4 * dwKey[2] + dwKey[20]=0/1
4 * dwKey[2] + dwKey[23]=0/2
4 * dwKey[2] + dwKey[26]=0/3
4 * dwKey[21]+ dwKey[22]=0/1
4 * dwKey[24]+ dwKey[25]=0/2
4 * dwKey[27]+ dwKey[28]=0/3
已知dwKey[1]=0,dwKey[2]=1,进行简单求解得:
dwKey[12]=dwKey[15]=dwKey[18]=1;
dwKey[21]=dwKey[24]=dwKey[27]=2;
简单求解得:
dwKey[11]=1 dwKey[13]=1 dwKey[20] =1 dwKey[22] =1
dwKey[14]=2 dwKey[16]=2 dwKey[23] =2 dwKey[25] =2
dwKey[17]=3 dwKey[19]=3 dwKey[26] =3 dwKey[28] =3

按照这个规则重写dwKey,这样第2,3,4次循环便满足条件“if ( v15 == v16 )”,
           if ( v12[4 * dwKey[37] + dwKey[38]] > v12[4 * dwKey[39]+ dwKey[40]] )
            buffer[v5++] = 'b';
          if ( v12[4 * dwKey[37] + dwKey[38]] < v12[4 * dwKey[39]+ dwKey[40]] )
            buffer[v5++] = 'c';
          if ( v12[4 * dwKey[37] + dwKey[38]] ==v12[4 * dwKey[39]+ dwKey[40]] )
            buffer[v5++] = 'd';
第2次循环v12[1]=40,第3次循环v12[2]=40,第4次循环v12[3]=40,想要依次得到‘b’,‘c’,‘d’,则令4 * dwKey[37] + dwKey[38]=1;4 * dwKey[39]+ dwKey[40]=2即可。
简单求解得:
dwKey[37]=0;dwKey[38]=1;dwKey[39]=0;dwKey[40]=2;

  第5次循环开始40元素便在v12表的第1块了,因此“v13 < v14”,下面要得到字符‘e’,就要使“v12[4 * dwKey[45] + dwKey[46]] == v12[4 * dwKey[47]+ dwKey[48]]”成立(v17 < v18已经成立,因为代码的条件是对称,分析当如v13与v14)。因为v12表中的12个元素11个是30,只有一个是40,因此随便填写dwKey[45],dwKey[46],dwKey[47],dwKey[48]满足上式成立即可。
  再下面是字符‘f’,‘g’,‘h’,这里要注意一点是虽然第6次循环本来v12[5]=40,但是经过换位处理后v12[1]=40.所以有:
4 * dwKey[41] + dwKey[42]=1;4 * dwKey[43]+ dwKey[44]=2;
简单求解得:
dwKey[41]=0,dwKey[42]=1,dwKey[43]=0,dwKey[44]=2.
 
接下来是字符‘i’,‘j’,‘k’,‘l’,这个有点技巧。如果要按照字符大小顺序来计算的话,条件形式为:
不等/不等;不等/相等;相等/不等;相等/相等。
第9次循环: v12[8]=40,v12[9]=30,v12[10]=30,v12[11]=30。
第10次循环:v12[8]=30,v12[9]=40,v12[10]=30,v12[11]=30。
第11次循环:v12[8]=30,v12[9]=30,v12[10]=40,v12[11]=30。
第12次循环:v12[8]=30,v12[9]=30,v12[10]=30,v12[11]=40。
我选择的比较方式是v12[9]和v12[8]比较,v12[10]和v12[8]比较,这样就能满足上述条件形式,便可以顺序得到字符‘i’,‘j’,‘k’,‘l’。所以有:
4 * dwKey[3] + dwKey[4]=9; 4 * dwKey[5] + dwKey[6]=8;
4 * dwKey[7] + dwKey[8]=10;4 * dwKey[9]+ dwKey[10]=8;
得:dwKey[3]=2; dwKey[4]=1; dwKey[5]=2; dwKey[6]=0;
 dwKey[7]=2; dwKey[8]=2; dwKey[9]=2; dwKey[10]=0;

按照上述规则重写dwKey,在第一次12轮循环之后便可以得到字符串“abcdefghijkl”。但是在第二轮的循环中还需要注意几点(关键元素40变为了20),不过也很简单,主要是因为算法是对称的。
在确定第二轮的‘b’,‘c’,‘d’字符时,令4 * dwKey[49] + dwKey[50]=1;4 * dwKey[51]+ dwKey[52]=2;
得:dwKey[49]=0,dwKey[50]=1,dwKey[51]=0,dwKey[52]=2。
在确定第二轮的‘f’,‘g’,‘h’字符时,令4 * dwKey[33] + dwKey[34]=1;4 * dwKey[35]+ dwKey[36]=2;
得:dwKey[33]=0,dwKey[34]=1,dwKey[35]=0,dwKey[36]=2。
  重写dwKey,如:
{3,0,1,2,1,2,0,2,2,2,0,1,1,1,2,1,2,3,1,3,1,2,1,2,2,2,3,2,3,0,0,0,1,0,1,0,2,0,1,0,2,0,1,0,2,0,1,0,0,0,1,0,2};
输出的buffer串便是“abcdefghijklabcdefghijkl”.
现在用cm.exe程序测试,输入序列:
abcdefghijkl
30121202220111212313121222323000101020102010201000102
点击“确定”后弹出“ok!”:


正确的序列很多,只要name合理key就能用,感觉netwind所说的keygen似乎不合理,应该是namegen吧?而且cm.exe程序有个bug:只要key合理,则后加N个合法字符(0,1,2,3)也合理,因为sub_004022A0不验证53字节之后的字符。

  本人对上述算法不懂,仅仅是靠找规律玩填字游戏而已,因此未能提出通用的解法。

by  sing
2008-10-7

[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞7
打赏
分享
最新回复 (5)
雪    币: 186
活跃值: (15)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
singsing 7 2008-10-7 12:31
2
0
发帖的排版不和word一致,真乱!!!!发一次帖子真难!
雪    币: 220
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
manbug 2008-10-7 13:15
3
0
楼主真利害,注册码那部分,我只跟了一半,放弃了,变量太多,感觉自己跟不来了,呵呵
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
lunglungyu 1 2008-10-7 14:26
4
0
同3楼。。
感觉自己缺少经验和毅力。
还有是对搜索引擊的运用
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
debugcn 2008-10-7 14:43
5
0
写的挺详细!!我只发了两个注册码,没有给说明呀。。。。。
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
luckcai 2008-10-11 18:43
6
0
bp GetWindowTextA下断,这个OD载入,CTRL+N。中好像没有 GetWindowTextA这个函数呀|???请问下为什么???
游客
登录 | 注册 方可回帖
返回