首页
社区
课程
招聘
[看雪读书月]2008看雪论坛读书月第二题 KeyGen
发表于: 2008-7-26 00:38 8505

[看雪读书月]2008看雪论坛读书月第二题 KeyGen

2008-7-26 00:38
8505
收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 136
活跃值: (20)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
2
【文章标题】: 【原创】【看雪读书月】《2008看雪论坛读书月第二题crackMe02》KeyGen
【文章作者】: 沙金
【软件名称】: 2008看雪论坛读书月第二题crackMe02
【软件大小】: 49.2 KB
【下载地址】: http://bbs.pediy.com/showthread.php?t=68795
【软件作者】: MegaX
【编写语言】: Microsoft Visual C++ 2008 (.net)
【使用工具】: OD
【操作平台】: WINDOWS2003
【软件介绍】: 2008看雪论坛读书月第二题
--------------------------------------------------------------------------------
【详细过程】

一、测试篇
                拿到题以后,首先用OD载入,直接报错(我的系统是winxp)。
                启动VM,把题拷入2003系统下,运行,OK了。
                随便输入用户名和key,提示重启应用程序,查看没有生成文件,于是在注册表里查找刚才输入的用户名,果然在 HKEY_CURRENT_USER\Software\MegaX 找到。
二、反编译篇
                由于从来没有调试过.net程序,所以,在google一下,才知道.net可以反编译,于是,开始下载工具。
                首先是Reflector 下载了最新版,结果反编译失败。
                然后使用ildasm 可以读出部分结构,但很多类名乱码,没有得到可用信息。
                本想试试PEBrowse ,在google里发现,在使用PEBrowse前,要用ildasm反编译后,生成带debug信息的exe才有效果。只能放弃。
                仔细想想,如果可以这么简单,那就不是看雪的风格了。应该是加了壳,所以一般的反编译工具没有办法。只能动态调试了。
三、OD篇
                用OD载入,程序直接自动启动,OD的ICE区一片空白。
                想到有注册表的操作,于是 bp RegOpenKeyExA 和 bp RegOpenKeyExW ,重新载入,在RegOpenKeyExW处断下。可以判断是UNICODE码。
                断了70次左右,总算看到了"MegaX" 字符串,记录下当前寄存器变量发现esi值可以利用,于是,
                在 77F57AAB >  81FE 04000080   cmp     esi, 80000004 下esi == 1DC 的条件断点,再次重启程序,果然断下。(ESI和不同的系统,值不一样,而且,也不是每次都可以成功断下)
                接下来的工作非常郁闷,一直没法到“程序领域”,经过N次尝试后发现,程序代码在堆空间里,而且,边执行边解压,难怪反编译不成功。
                而且,更郁闷的是,由于在堆空间里,所以,没办法保存断点,只要跑飞了,就得从头再来。代码里,很多暗桩,稍不注意就飞了,大大增加了分析难度。
                废话了这么多,现在开始
00F19A43    8B8F 60010000   mov     ecx, dword ptr [edi+160]         
00F19A49    8B97 58010000   mov     edx, dword ptr [edi+158]         ; name
00F19A4F    FFB7 5C010000   push    dword ptr [edi+15C]              ; key
00F19A55    FF15 90659A00   call    dword ptr [9A6590]               ; 比较函数
               
                F7跟进,到
00F1B7C0    8B8D 0CFFFFFF   mov     ecx, dword ptr [ebp-F4]
00F1B7C6    FF15 9C659A00   call    dword ptr [9A659C]
00F1B7CC    83F8 05         cmp     eax, 5 

                可以确定key的格式为 key1-key2-key3-key4
                key1,key2,key3,key4 的长度都为5,如果不满足条件,就跳出函数,注册失败。
                于是,改注册表里的KEY键值为 AAAAA-BBBBB-CCCCC-DDDDD 后重新分析。
               
00F1B90A    50              push    eax
00F1B90B    FFB5 24FFFFFF   push    dword ptr [ebp-DC]               
00F1B911    8B95 1CFFFFFF   mov     edx, dword ptr [ebp-E4]
00F1B917    8B8D 20FFFFFF   mov     ecx, dword ptr [ebp-E0]
00F1B91D    FF15 AC659A00   call    dword ptr [9A65AC] 

                此函数产生一个新字符串: key1+key3+"MegaX"+name
               
00F1B923    8BC8            mov     ecx, eax
00F1B925    8B95 18FFFFFF   mov     edx, dword ptr [ebp-E8]
00F1B92B    FF15 B0659A00   call    dword ptr [9A65B0]               
00F1B931    8985 08FFFFFF   mov     dword ptr [ebp-F8], eax
00F1B937    BF 58000000     mov     edi, 58

                此函数通过key3生成一个5位长度的字符串,跟进
               
00F1CDA1    8BF0            mov     esi, eax
00F1CDA3    8BD3            mov     edx, ebx
00F1CDA5    8BCE            mov     ecx, esi
00F1CDA7    FF15 B87A9A00   call    dword ptr [9A7AB8]

                生成一个0x3E大小的字符串数组,内容如下:
                第一个为:8x3p5BeabcdfghijklmnoqrstuvwyzACDEFGHIJKLMNOPQRSTUVWXYZ1246790
                第二个为:x3p5BeabcdfghijklmnoqrstuvwyzACDEFGHIJKLMNOPQRSTUVWXYZ12467908
                最后一个为:08x3p5BeabcdfghijklmnoqrstuvwyzACDEFGHIJKLMNOPQRSTUVWXYZ124679
                可以看出,第一个是a-z,A-Z,0-9的字符串组合,以8x3p5Be打头,后面的字符串相应位置去掉8x3p5Be字符。
                后面的字符串做循环左移。
                同时,产生了新的字符串 "CrackMe" + key1 + key2 + "MegaX" + name + "MegaX"
		
00F1D160    8BD0            mov     edx, eax
00F1D162    8BCE            mov     ecx, esi
00F1D164    FF15 BC7A9A00   call    dword ptr [9A7ABC]

                此函数对"CrackMe" + key1 + key2 + "MegaX" + name + "MegaX"字符串进行md5的hash运算
                并按每一位的asc码产生一个十进制的数字字符串
                算法如下:
    CString StrToMd5Hash(char *pCharDate,int nOutSize)
    {
        HCRYPTPROV hProv = 0;
        HCRYPTHASH hHash;  
        byte hash[MAXBYTE];
        DWORD nLen = strlen(pCharDate), nSize = nOutSize; 
        CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);
    
        CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
        CryptHashData(hHash,(const byte *) pCharDate, nLen, 0);
        CryptGetHashParam(hHash, HP_HASHVAL, (byte *)hash, &nSize, 0); 
    
        CString OutBuff = pByteToAscStr(hash);

        CryptDestroyHash(hHash);   
        CryptReleaseContext(hProv, 0);
        return OutBuff;
    }
    CString pByteToAscStr(byte *pByteDate)
    {
        CString str1;
        CString OutBuff = "";
        for (int i=0;i<16;i++)
        {
            str1.Format("%d",pByteDate[i]);
            OutBuff += str1;
        }
        return OutBuff;
    }
   
    接下来是本题的核心算法
    我没以四个变量来表示参加运算的字符串
    strBuff         <--"CrackMe" + key1 + key2 + "MegaX" + name + "MegaX"
    strMd5          <-- StrToMd5Hash(strBuff)
    arrayKeyStr     <-- x3E大小的字符串数组
    key3            <-- key的第三部分
   
    1.取出key3的第 i 位,在arrayKeyStr[0]里查找其相应的位置 放如变量 nIndex
    2.取出strMd5的第 i 位
    3.在arrayKeyStr[j]里取出 nIndex 位(j从1到0x3E)与 strMd5的第 i 位 比较
    4.找到相同的字符时,取出arrayKeyStr[j]的第一个字符记录下
    如此循环5次
   
    算法如下:
    CString str = "CrackMe" + keyArray[0] + keyArray[1] + "MegaX" + strUser + "MegaX";   //构成 strBuff
    CString strmd5 = StrToMd5Hash((LPSTR)(LPCTSTR)str,16);                               //构成 strMd5
    int j=0;
    CString strout = "";
    for (int i=0;i<5;i++)
    {
        int nIndex = keyStrArray[0].Find((LPSTR)(LPCTSTR)keyArray[2].Mid(i,1),0);       //keyArray[2] 表示key的第三部分
        CString strIndex = strmd5.Mid(i,1);
        j=0;
        while (strcmp((LPSTR)(LPCTSTR)keyStrArray[j].Mid(nIndex,1),(LPSTR)(LPCTSTR)strIndex) != 0)
        {
            j++;
            if (j>=keyStrArray.GetSize())
            {
                j=0;
            }
        }
        if (j>0)
        {
            strout += keyStrArray[j-1].Mid(1,1);
        }
        else
        {
            strout += keyStrArray[j].Mid(1,1);
        }
    }
    strout.MakeUpper();    //转成大写


                strout是通过算法产生的新字符串。这时,仔细想算认证过程,发现,此题没有唯一的解,key1,key2,key3是可以随机生成的。关键是key4
                我本以为strout就是key4,结果发现不对。。
                再跟,终于在
794F955A    8B80 18010000   mov     eax, dword ptr [eax+118]
794F9560    FF70 04         push    dword ptr [eax+4]
794F9563    8D4C24 10       lea     ecx, dword ptr [esp+10]
794F9567    33D2            xor     edx, edx
794F9569    E8 06AFEAFF     call    793A4474   

                发现了原因
                这里的参数是一个字符串数组
                arry1 = strout //刚才生成的
                arry2 = key4   //自己输入的
                arry3 = "MegaX"
                arry4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
               
                运行函数后,会返回 0和1 这个返回值决定了注册是否成功,修改返回值为1,爆破成功。
                分析函数,发现在比较时产生了一个新的字符串。试着把输入的key4改为此值,注册成功!!哈哈,运气太好了。。。
                分析字符串的生成法,发现很简单。算法如下:
	CString GetKey4(CString &strKey)
	{
		CString str1 = "";
		CString str2 = "MegaX";
		CString str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		for (int i=0;i<5;i++)
		{
			char cTemp1 = strKey.GetAt(i);
			char cTemp2 = str2.GetAt(i);
			int nIndex =  (cTemp1 + cTemp2 - 1) % str3.GetLength();
			str1 += str3.Mid(nIndex,1);
    		}
		return str1;
	}

                asc码相加,取模得到位置,取出即可。
               
                分析算法完毕,下面看看他的保护机制。
                1、程序使用了混淆工具混淆了代码
                2、对Reflector、PEBrowse、Anakrino、Assembly View、ProcessDasm、VirtualCode、
                Dasm、FrogsICE、DriverWorkbench、OllyDbg、flyDBG、OllyICE、CrackMenetexe
                等工具做了判断,当发现其进程,就退出
                3、代码采用边执行边解压
                4、代码有很多花指令
                5、代码有利用esp使其返回到其他地址,造成很多地方不能用F8跳过。
                分析保护机制我太菜了,有很多不对的,请指正,谢谢!
               
               
                科锐学子:沙金
2008-7-26 12:00
0
雪    币: 47147
活跃值: (20465)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
答题结束,从答案提交区移出。
更多事项参考:http://bbs.pediy.com/showthread.php?t=68795

由于本题只有mstwugui和沙金提交了答案,因此不需要投票即可决定奖项。
2008-7-27 20:05
0
雪    币: 97697
活跃值: (200839)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
4
support .
2008-7-27 20:34
0
雪    币: 274
活跃值: (13)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
支持!
2008-7-27 22:12
0
雪    币: 5275
活跃值: (461)
能力值: (RANK:1170 )
在线值:
发帖
回帖
粉丝
6
也支持下!
2008-7-27 22:21
0
雪    币: 423
活跃值: (11)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
7
也许是距离产生美吧,我开始佩服整天想着沙里淘金的人了。
2008-7-27 22:42
0
雪    币: 503
活跃值: (80)
能力值: (RANK:280 )
在线值:
发帖
回帖
粉丝
8
支持一下, :)
2008-7-27 23:42
0
雪    币: 440
活跃值: (61)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9


真的很强哦。。。。。。

看了破文收获很多。。。

最后支持一下。。。。。
2008-7-28 00:51
0
雪    币: 37
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
看在你的面子上支持一下!
2008-7-29 21:39
0
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
11
厉害,学习。
期待大虾的另一角度解法。
2008-7-30 10:31
0
雪    币: 372
活跃值: (31)
能力值: ( LV12,RANK:410 )
在线值:
发帖
回帖
粉丝
12
学习,LZ把花指令给dump出来看看~~
2008-7-30 17:08
0
雪    币: 117
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
沙哥太牛B了。
膜拜一下。
2008-11-24 19:02
0
游客
登录 | 注册 方可回帖
返回
//