9

[原创][原创]看雪CTF2017 第五题 CrakeMe逆向分析

lacoucou 2017-6-10 13:24 1060

                                      解题过程

1.查看程序

1.题目提示要在xp下运行,看了看资源,发现有驱动,将文件提取出来,用PEID的算法插件KANAL扫描驱动,发现有MD5算法:

2.用OD加载程序CrakeME,下断点CreateFileA,一次断在释放驱动的时候,另一次断在加载驱动的时候:

00401DE8  |.  53            PUSH    EBX                              ; /hTemplateFile => NULL
00401DE9  |.  68 80000000   PUSH    0x80                             ; |Attributes = NORMAL
00401DEE  |.  6A 03         PUSH    0x3                              ; |Mode = OPEN_EXISTING
00401DF0  |.  53            PUSH    EBX                              ; |pSecurity => NULL
00401DF1  |.  53            PUSH    EBX                              ; |ShareMode => 0
00401DF2  |.  68 000000C0   PUSH    0xC0000000                       ; |Access = GENERIC_READ|GENERIC_WRITE
00401DF7  |.  68 58D34200   PUSH    CrackMe1.0042D358                ; |FileName = "\\.\vmxdrv"
00401DFC  |.  FF15 88324200 CALL    NEAR DWORD PTR DS:[<&KERNEL32.Cr>; \CreateFileA

在CreateFileA 的下一条指令下断点,运行程序,程序直接出错退出。

有反调试,用IDA打开程序,发现了IsDebuggerPresent,这个应该不会导致程序崩溃。

接着找,发现了一个线程:

线程中死循环在发送控制码,打开驱动,找到对应的控制码:

嘿嘿,EPROCESS,最后有个清零的动作,这里就是反调试了吧。没有查这个结构体,据猜测应该是把调试端口清零了。

解决办法:

1.

通过资源工具导出驱动,然后修改驱动中的sub_10486函数(即上图的fantishi_10486):

and     dword ptr [eax+0BCh], 0   //*(result+47)=0
83 A0 BC 00 00 00 00
//改成
or     dword ptr [eax+0BCh], 0
83 88 BC 00 00 00 00

这样就不会清空端口了。

然后用loadpe修改驱动的校验和。修改方法详见:http://bbs.pediy.com/thread-185490.htm

用winhex修复crakeme,修复后的程序见附件。

2.跟踪流程

跟踪了几次之后,理出来的流程大致如下:

1.sub_4013E0 主要负责释放驱动,启动驱动,还有反调试

2.sub_4013E0 是按下enter之后的处理函数

主要流程:

1)sub_4182FA((CString *)&v7);   // 获取注册码

2)sub_4182FA((CString *)&v7);   //注册码转小写  CString::MakeLower

3)sub_41830C((CString *)&v7);   //注册码倒序    CString::MakeReverse

4)  if ( *(_DWORD *)(v7 - 8) != 6 || IsDebuggerPresent() )  判断注册码长度是否等于6 加反调试

5)Sub_401D50(v1, v3, (size_t)v5);  这个函数主要功能:把获取到的注册码发送到驱动层,计算 hash后载读取回来。


DeviceIoControl 这个地方是个坑,原来以为只是用来反调试的,后续发现它会设置一个全局变量dword_114D8 = 1; 

这个变量在驱动层计算md5中要使用,没有这个,每次不管输入的是什么,驱动层返回的md5都一样。

WriteFile 用来发送我们输入的注册码。

ReadFile 用来读取计算出来的hash.

接着说说计算hash(md5)的算法--驱动中:

圆圈中圈住的就是上边说的全局变量。

方框中圈的也与正规的md5算法不同:

多了一些莫名其妙的操作。是一个变形的MD5算法。

6)获取到驱动返回的MD5之后:

(1).sub_417D43((CString *)&v4, (LPCSTR *)v1 + 23);  

返回类似md5值的字符串形式  md5_like=md5_(userInput)

(2).sub_401920(v4, (CString *)v5);  将上述md5字符串再计算一次md5

   Md5=sub_401920(md5_like)

(3).mid_415A78((LPCSTR *)&v8, (int)&v9, 2, 0xAu);

   subMd5=mid(md5,2,10)

   将md5字符串从位置2处截取10个字符。

(4)if ( _mbsicmp(v8, a888aeda4ab) ) 

  截取的字符串与888aeda4ab 比较。

(5)show_success_402030  拼接字符串Success^^! 并显示在窗体上。

整个算法用伪代码描述就是:

subString=string_substr(md5(md5_like(CString::MakeReverse(CString::MakeLower(userInPut)))),2,10)

已知条件,输入字符串长度为6最终的subString 为888aeda4ab 。求输入字符串。

所以只能乖乖的爆破。

由于驱动中的md5是非标准版算法,这里也不再详细跟了,直接自己写程序调用他的驱动,Crakeme内的驱动为标准算法,直接找一个md5源码就可以了。

找一个加载驱动的工具加载导出的驱动,然后运行下面的算法:

代码如下:

string Mem2String(unsigned char* psz,int nLen)
{
    string strText;
    unsigned char szBuf[4]={0};
    for (int i=0;i<nLen;i++)
    {
        sprintf_s((char*)szBuf,4,"%02x",psz[i]);
        strText+=(char*)szBuf;
    }
    return strText;
}
void calc_hash()
{
    unsigned char szMd5[0x10]={0};
    char szBuf[10]={0};
    char* pszFileName="\\\\.\\vmxdrv";
    HANDLE  hFile=CreateFileA(pszFileName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0x80,NULL);
    if (hFile==0)
    {
        printf("CreateFileA error!");
        return;
    }
    char OutBuffer[0x100]={0};
    DWORD BytesReturned=0;
    int result = DeviceIoControl(hFile, 0x222004u, 0, 0, OutBuffer, 0x100u, &BytesReturned, 0);
    if (!result)
    {
        printf("DeviceIoControl error! %d ",result);
        return;
    }              
    int nnumcount=0;
    char szKEY[7]={0};
    char szKeys[]="987654321zyxwvutsrqponmlkjihgfedcba";
    int nCount=strlen(szKeys);
    for (int i=0;i<nCount;i++)
    {
        for (int i1=0;i1<nCount;i1++)
        {
            for (int i2=0;i2<nCount;i2++)
            {
                for (int i3=0;i3<nCount;i3++)
                {
                    for (int i4=0;i4<nCount;i4++)
                    {
                        for (int i5=0;i5<nCount;i5++)
                        {
                            szKEY[0]=szKeys[i];
                            szKEY[01]=szKeys[i1];
                            szKEY[02]=szKeys[i2];
                            szKEY[03]=szKeys[i3];
                            szKEY[04]=szKeys[i4];
                            szKEY[05]=szKeys[i5];
                            DWORD NumberOfBytesWritten=0;
                            DWORD NumberOfBytesRead=0;
                            if ( WriteFile(hFile, szKEY,7, &NumberOfBytesWritten, 0) )
                            {
                                ReadFile(hFile, szMd5, 0x10, &NumberOfBytesRead, 0);
                                string xxx=Mem2String(szMd5,16);
                                MD5((unsigned char*)xxx.c_str(),xxx.length(),(unsigned char*)szMd5);
                                string xxx2=Mem2String(szMd5,16);
                                string subStr=xxx2.substr(2,10);
                                if (subStr=="888aeda4ab")
                                {
                                    printf("find it:%s",szKEY);
                                    printf("key:%s  md51:%s  md52:%s submd5:%s\n",szKEY,xxx.c_str(),xxx2.c_str(),subStr.c_str());
                                    getchar();
                                    getchar();
                                    getchar();
                                    getchar();
                                    getchar();
                                    break;
                                }
                                nnumcount++;
                                if (nnumcount%100000==0)
                                {
                                    printf("%d  key:%s  md51:%s  md52:%s submd5:%s\n",nnumcount,szKEY,xxx.c_str(),xxx2.c_str(),subStr.c_str());
                                }
                            }
                            else
                            {
                                printf("WriteFile error!");
                                getchar();                            
                            }
                        }
                    }
                }
            }
        }
    }
    CloseHandle(hFile);
    printf("end");
}

最终:

程序中没有出来倒序的问题,因此这里反过来输入就可以了。

上传的附件:
最新回复 (2)
netwind 2017-6-10 15:47
2
应该是标准MD5吧
lacoucou 2017-6-11 17:28
3
驱动的算法没详细看,只验证了结果,所以以为不是标准的。
返回