吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10249|回复: 26
收起左侧

[CrackMe] 【吾爱2013CM大赛解答】驱动CM-网际座山雕 完全分析过程&注册机源码&驱动源码

  [复制链接]
baby 发表于 2013-12-15 05:45
CM是什么?Crackme是什么?这是什么东西?楼主发的什么?
他们都是一些公开给别人尝试破解的小程序,制作 Crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 Cracker,想挑战一下其它 Cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破解,KeyGenMe是要求别人做出它的 keygen (序号产生器), ReverseMe 要求别人把它的算法做出逆向分析, UnpackMe 是要求别人把它成功脱壳,本版块禁止回复非技术无关水贴。

本帖最后由 baby 于 2013-12-15 06:10 编辑

下面文章完整DOC文档(本帖可能格式有点乱)&易语言版本注册机源码和驱动源码&C++版本注册机源码和驱动源码附件:

吾爱破解2013CrackMe大赛
作者:Baby
题目:驱动CrackMe —— 网际座山雕
前言:感冒发烧,晕乎乎在吾爱溜达溜达,发现有大赛进行,我等菜鸟正是锻炼自己的时候,那么动手来考验考验自己吧!
题目选择:浏览了目前的题目,不知道该搞什么,看到有一个驱动的,那么自己驱动刚刚入门,拿驱动来破解也正是加强自己学的时候,那么根据目前已经列出的题目,先选择当前题目进行分析把。
准备工作与题目分析:看到驱动,首先想到的就是Ring0层是个关键,他可能带强力的Anti,驱动中可能做一些羞羞的事情,这些都是影响我们逆向分析的绊脚石,搞不定它就没有办法继续下面的逆向分析。CrackMe的破解与追码实际上就是追算法,题目所定是驱动级别,那么驱动在这个情况下能想到的它的用处只有以下情况:1.保护CrackMe防止调试器调试或干扰调试器正常动作;2.与驱动进行通信将部分或全部关键算法驻留在驱动中进行计算并返回相关结果。
逆向环境:Windows 7 Professional Service Pack 1
逆向工具:OllyDbg吾爱破解专版,IDA 6.4 Pro Plus,DbgView,Miscrosoft OneNote,PCHunter,SPX Instant Screen Capture
编程语言:易语言5.11(附带WonderWall扩展编译环境),VC++(Visual Studio 2012 Pro)

完整逆向笔记
在所选题目的帖子中下载Bin.rar解压至桌面到文件夹Bin可得到主程序mycrack.exe和驱动文件crackme.sys。
运行题目主程序,界面很简单,给出机器码需要填入序列号,我随手填写了一个baby,弹出“胜败乃兵家常事,大侠请重新来过。”的信息(如图1所示),那么此次的目的已经完全明确,找出真正的序列号并还原算法再重写整个CrackMe代码和驱动代码,使自己写的驱动替换原始驱动能正常工作。
既然提示让我们重新来过了,那开始切入正题。看了下驱动文件只有4KB大小,我很好奇,4KB他能做很多羞羞的事情么。运行主程序后,打开PCHunter,枚举驱动发现驱动已经加载(如图2所示)。前面已经说道分析时对此驱动的想法,那么首先先看Anti,用PCHunter从Notify、SSDT、Shadow SSDT、内核钩子枚举发现没有任何驱动上的Anti,难道是他用了一些猥琐的手法?后面证明我想多了。。。。。。
既然没有发现Anti,题目要求不能使用除了自己写的工具之外的所有加密手段除UPX压缩,那么对于驱动我们直接上IDA。IDA后只有6个Sub,完全是自己多虑了。。。。。。
6个Sub都很简单,经过简单浏览只有DriverControl有让我们关心的核心关键代码,将Sub进行重命名后继续分析(如图2和图3所示)。
      à   
虽然是简单代码,但是还是不禁IDA F5一下走起,得到伪代码,所有流程清晰可见。根据驱动菜鸟编程经验,对各个变量名称进行了自己的命名以方便阅读,整理下代码格式并修正代码得到以下伪代码(测试编译通过):
NTSTATUS __stdcallDriverControl(int pDeviceObject, PIRP Irp)
{
     struct _IRP_IO_STACK_LOCATION*pStackLocation; // ST30_4@1
     intIoControlCode2; // [sp+8h] [bp-24h]@1
     unsigned intIoControlCode; // [sp+Ch] [bp-20h]@1
     intInputBufferLength; // [sp+10h] [bp-1Ch]@1
     NTSTATUSNTResult; // [sp+14h] [bp-18h]@1
     struct _IRP*pIoBuffer; // [sp+18h] [bp-14h]@1
     unsigned intLoopCount; // [sp+1Ch] [bp-10h]@1
     ULONGOutputBufferLength; // [sp+28h] [bp-4h]@1
     NTResult = STATUS_INVALID_DEVICE_REQUEST;
     pStackLocation= (struct _IRP_IO_STACK_LOCATION *)Irp->Tail.Overlay.CurrentStackLocation;
     LoopCount = 0;
     IoControlCode =*((DWORD*)pStackLocation + 3);
     pIoBuffer = Irp->AssociatedIrp.MasterIrp;
     InputBufferLength= *((DWORD*)pStackLocation + 2);
     OutputBufferLength= *((DWORD*)pStackLocation + 1);
     IoControlCode2= *((DWORD*)pStackLocation + 3); // 重复取IoControlCode无实际意义
     if (IoControlCode == 0x222000 ){ // IO_DBG_HELLO 简单打印字符串
         DbgPrint("[crackme]Hello World!\r\n");
         NTResult = STATUS_SUCCESS;
     }else{
         if (IoControlCode2 == 0x222004 ){ // IO_Calculate
              DbgPrint("[crackme]IOCTRL_REC_FROM_APP\r\n");
              if (InputBufferLength ){
                   DbgPrint("[crackme]Get Data from App: %s\r\n", pIoBuffer);
                   DbgPrint("[crackme] GetData from App: %x\r\n", pIoBuffer);
              }
              while (LoopCount < 9 ) {
                   // 所有值的操作以字节对齐 原MasterIrp的Flags成员和Type成员各为指针如下
                   // char*HWID = (char*)(pIoBuffer)
                   // char*SN = (char*)(pIoBuffer+0x8)
                   // char*Result = HWID
                   // 循环9次Result[LoopCount] = SN[LoopCount] - HWID[LoopCount] + 1
                   // 根据这个计算看出 我们输入的序列号长度应该限定为9跟机器码一样
                   if ( *((BYTE*)&pIoBuffer->Flags + LoopCount + 1) )
                       *((BYTE*)&pIoBuffer->Type + LoopCount) =
                       *((BYTE*)&pIoBuffer->Flags + LoopCount + 1)
                       -*((BYTE*)&pIoBuffer->Type + LoopCount) + 1;
                   ++LoopCount;}
              *((BYTE*)&pIoBuffer->Flags + 1)= 0; // Flags成员置NULL即 Result[9]
              DbgPrint("[crackme]Get Data from App: %s\r\n", pIoBuffer);
              DbgPrint("[crackme]Get Data from App: %x\r\n", pIoBuffer);
              Irp->IoStatus.Information= 9; // 返回长度9
              DbgPrint("[crackme]Send Data to App: %s\r\n", pIoBuffer);
              NTResult= STATUS_SUCCESS;
         }else{
              if (IoControlCode2 == 0x222008 ){ // IO_Send_Result
                   DbgPrint("[crackme]IOCTRL_SEND_TO_APP\r\n");
                   if (OutputBufferLength >= 24 ){ // 如果输出缓冲区长度大于等于24则返回24
                       memcpy(pIoBuffer,"HelloWorld from Driver", 24);
                       Irp->IoStatus.Information= 24;
                       DbgPrint("[crackme]Send Data to App: %s\r\n", pIoBuffer);
                       NTResult= STATUS_SUCCESS;
                   }
              }else{
                   DbgPrint("[crackme]Unknown IOCTL: 0x%X (%04X,%04X)\r\n",
                       IoControlCode,(IoControlCode& 0xFFFF0000) >> 16,(IoControlCode >> 2) & 0xFFF);
                   NTResult= STATUS_INVALID_PARAMETER;
              }
         }
     }
     if (NTResult ) // 如果NTResult!=NT_STATUS_SUCCESS则返回长度0否则返回OutputBufferLength
         Irp->IoStatus.Information= 0;
     else
         Irp->IoStatus.Information= OutputBufferLength;
     Irp->IoStatus.Status= NTResult;
     IofCompleteRequest(Irp, IO_NO_INCREMENT);
     returnNTResult;
}

由上面代码可直观看到整个驱动IO流程,IO_Calculate通讯码中有详细的计算流程,并在IO_Send_Result通讯码流程中有向主程序返回关键字符串。在详细的计算流程中有一个简单的算法,在上面的代码中已经有了详细的解释,为了安全起见,我们从汇编再验证一遍合个小计算(如图4所示)。

至此,我们已经完全轻触了Ring0下的主要核心流程并完成了可编译的关键代码,为最后完成驱动替换做了完全的保障,下面进行Ring3下的主程序分析。
直接运行主程序,驱动都没做Anti,那么主程序应该没明显Anti(PCHunter扫了下应用层钩子证实确实木有),OD直接调试。我们根据前面的驱动调试已经知道是IRP通信方式,所以Ring3下直接下断bp DeviceIoControl,在序列好号中输入baby ,代码断下,得到以下信息:
调用代码:
00401B06      6A 00              push 0x0
00401B08      68 60644400        push mycrack.00446460
00401B0D      68 03010000        push 0x103
00401B12      68 70654400        push mycrack.00446570
00401B17      68 03010000        push 0x103
00401B1C      68 68644400        push mycrack.00446468  ; ASCII "134797f97baby"
00401B21      68 04202200        push 0x222004
00401B26      52                 push edx
00401B27      FF15 80424300      call dword ptrds:[<&KERNEL32.DeviceIoControl>]
堆栈信息:
0012F7C0   758DB9B5 /CALL DeviceIoControl 来自 kernel32.758DB9B0
0012F7C4   00000134 |hDevice = 00000134 (window)
0012F7C8   00222004 |IoControlCode = 222004 //IO_Calculate
0012F7CC   00446468 |InBuffer = mycrack.00446468
0012F7D0   00000103 |InBufferSize = 103 (259.)
0012F7D4   00446570 |OutBuffer = mycrack.00446570
0012F7D8   00000103 |OutBufferSize = 103 (259.)
0012F7DC   00446460 |pBytesReturned = mycrack.00446460
0012F7E0   00000000 \pOverlapped = NULL
0012F7E4   00371950 ASCII "134797f97baby"
0012F7E8  /0012F83C
0012F7EC  |00401B2D 返回到 mycrack.00401B2D 来自 kernel32.DeviceIoControl
通过 push mycrack.00446468  ; ASCII "134797f97baby" 后调用DeviceIoControl发送IRP给驱动,可以看到IoControlCode的通讯码正是我们之前分析的IO_Calculate,算法在上面我们已经分析过,我们现在先手动计算下结果,内存中数据如下(余下缓冲区都为0字节):
00446468  31 33 34 37 39 37 66 39 37 62 61 62 79 00 0000  134797f97baby...

根据前面的算法可知道共循环9次, Result[LoopCount] = SN[LoopCount] - HWID[LoopCount] + 1,可到结果,得到结果后OD断点跑起,观察OutBuffer与计算结果一致,如下所示:
00446570  32 2F 2F 43 39 37 66 39 37 00 61 62 79 00 0000  2//C97f97.aby...
不过根据之前驱动的分析,SN即我们输入的序列号应该是9位,所以这里测试baby当序列号明显不符。上面的断点返回后紧跟着下面出现了一个算法,算法完成后有比较明显的判断然后跳到失败与成功的地方。在跑了代码流程后发现,失败代码直接执行,真正的成功代码是根据已经存在的一张加密表和处理过的HWID&SN解密出来后再执行的ShellCode。
[C] 纯文本查看 复制代码
00401B65   .  833D 60644400>cmp dword ptr ds:[0x446460],0x0  
//判断DeviceIoControl的BytesReturn是否为空
00401B6C   .  76 36         jbe Xmycrack.00401BA4
00401B6E   .  33C9          xor ecx,ecx
00401B70   >  8A99 F8384400 mov bl,byte ptr ds:[ecx+0x4438F8]
//0x4438F8即为已经加密的代码码表
00401B76   .  80FB E0       cmp bl,0xE0
//循环取出字节判断是否大于E0,如果大于则不进行解密处理,查看码表后发现第一个字节EB,则可以认定解密后的代码第一处是jmp short
00401B79   .  73 16         jnb Xmycrack.00401B91
00401B7B      B8 398EE338   mov eax,0x38E38E39
00401B80   .  F7E1          mul ecx
00401B82   .  D1EA          shr edx,1
00401B84   .  8D04D2        lea eax,dword ptr ds:[edx+edx*8]
00401B87   .  8BD1          mov edx,ecx
00401B89   .  2BD0          sub edx,eax
00401B8B   .  2A9A 70654400 sub bl,byte ptr ds:[edx+0x446570]
00401B91   >  8899 80674400 mov byte ptr ds:[ecx+0x446780],bl
00401B97   .  83C1 01       add ecx,0x1
00401B9A   .  81F9 A3000000 cmp ecx,0xA3
//0x446570储存着DeviceIoControl处理过的HWID&SN,0x446780极为真正的ShellCode代码地址,循环A3次进行解密,因为本人菜鸟,所以IDA直接F5了这里(不懂C++的乘除优化),得到以下伪代码:
	int LoopCount = 0;
do{
byteTemp = CodeEnBuffer_byte_4438F8[LoopCount];
if ( (unsigned __int8)byteTemp < 224 ) byteTemp -= HWID_SN_byte_446570[LoopCount % 9];
*((_BYTE *)CodeRealBuffer_446780 + LoopCount++) = byteTemp;
}
while ( (signed int)LoopCount < 163 ); 
00401BA0   .^ 7C CE         jl Xmycrack.00401B70
00401BA2   .  EB 12         jmp Xmycrack.00401BB6
00401BA4   >  68 70654400   push mycrack.00446570
00401BA9   .  68 DC974300   push mycrack.004397DC    ;  \tNo data received: %s\n
00401BAE   .  E8 01C50100   call mycrack.0041E0B4
00401BB3   .  83C4 08       add esp,0x8
00401BB6   >  68 F4974300   push mycrack.004397F4   ;   \tDeviceIoControl IOCTRL_SEND_TO_APP sucessfully sent. \n\n
00401BBB   .  E8 F4C40100   call mycrack.0041E0B4
00401BC0   .  83C4 04       add esp,0x4
00401BC3   .  803D 22684400>cmp byte ptr ds:[0x446822],0x21
//已经知道了ShellCode长度为A3,判断最后一个字节是否为0x21,则LoopCount=0xA2根据上面的HWID_SN_byte_446570[LoopCount%9]算法,可知道驱动中计算后SN所在未知的的第一个字节,CodeEnBuffer_byte_4438F8[LoopCount]为0x22, byteTemp -= HWID_SN_byte_446570[LoopCount%9]可得0x22+0x21=0x1, 我的机器码是134797f97,第一位1的ASCII为0x31,再做驱动中的逆运算SN[0] = Result[8] + HWID[0] - 1  -> SN[0] = 0x1 + 0x31 – 1 = 0x31,由此可知我们的序列号第一位一定是数字1,根据此算法可以完全确定,序列好长度必须为9,验证了驱动中计算时猜解的序列号长度
00401BCA   .  75 21         jnz Xmycrack.00401BED
00401BCC   .  B8 80674400   mov eax,mycrack.00446780
00401BD1   .  C605 D8674400>mov byte ptr ds:[0x4467D8],0xDB
00401BD8   .  FFD0          call eax
//解密后的ShellCode一处机器码置0xDB,调用解密后的ShellCode,如果解密错误执行当然无法成功
00401BDA   .  8B4C24 14     mov ecx,dword ptr ss:[esp+0x14]
00401BDE   .  64:890D 00000>mov dword ptr fs:[0],ecx
00401BE5   .  59            pop ecx
00401BE6   .  5F            pop edi
00401BE7   .  5E            pop esi
00401BE8   .  5B            pop ebx
00401BE9   .  83C4 10       add esp,0x10
00401BEC   .  C3            retn
00401BED   >  6A 00         push 0x0
00401BEF   .  6A 00         push 0x0
00401BF1   .  68 30984300   push mycrack.00439830     ;  胜败乃兵家常事,大侠请重新来过。
00401BF6   .  8BCF          mov ecx,edi
00401BF8   .  E8 47690000   call mycrack.00408544
00401BFD   .  8B4C24 14     mov ecx,dword ptr ss:[esp+0x14]
00401C01   .  64:890D 00000>mov dword ptr fs:[0],ecx
00401C08   .  59            pop ecx
00401C09   .  5F            pop edi
00401C0A   .  5E            pop esi
00401C0B   .  5B            pop ebx
00401C0C   .  83C4 10       add esp,0x10
00401C0F   .  C3            retn
加密的代码码表(长度为162):
EB 44 8E 5D 41 91 63 13 81 04 DB8E 77 25 09 F1 3B FF 50 49 B0 37 ED 15 BD 1C 0A 39 F2 77 0C C6
D3 0A 0B EA 41 EB F0 3F 71 2A 0B7D E6 8C 75 27 07 F1 6C 92 44 87 8C 75 1F 07 F1 91 0B C6 0C C2
5D 62 57 C8 EB 56 3B C9 65 35 4334 90 46 13 93 79 1D AF 8E 4C 0D 5E 3A E3 3C FF 68 C2 37 37 5D
6F 7D 7C 66 74 8E FC 58 57 5A 5859 54 59 6B 58 17 87 27 E8 93 FF FF FF FF D5 91 CF 70 2E 5A 3C
E4 E8 81 FF FF FF FF D1 5B 6B 9B1E 72 34 E8 78 FF FF FF FF D5 E8 B3 FF FF FF 49 72 73 69 26 51
至此,我们已经完全清楚了整个流程,首先获取到HWID,HWID长度恒定9(后面讲到),SN的长度也必须为9且SN第一位一定是1,根据整个流程来看,加密的代码码表决定了必须对应密钥才能和驱动中计算出来的HWID&SN才能解密出真正的成功ShellCode,如果不知道已经解密好的ShellCode或者正确的一组HWID和SN,理论上Key是无法还原的。但是,我们从驱动中简单的加减计算和Ring3下解密ShellCode的简单加减计算,可以产生一个断言:Key与HWID在计算过程中也是简单的加减法,那么,前面已经知道SN第一位必须是1,我们需要知道HWID的计算方法变可以清楚的知道Key的第一个字节。
程序一眼可以看出是MFC框架,则直接下断bp SetDlgItemTextA,向上返回后得到HWID的详细获取和计算过程。
[C] 纯文本查看 复制代码
0040186B   > \68 00010000   push 0x100                    ; /pFileSystemNameSize = 00000100
00401870   .  8D4424 20     lea eax,dword ptr ss:[esp+0x20]          ; |
00401874   .  50            push eax                                 ; |pFileSystemNameBuffer
00401875   .  8D4C24 1C     lea ecx,dword ptr ss:[esp+0x1C]          ; |
00401879   .  51            push ecx                                 ; |pFileSystemFlags
0040187A   .  8D5424 24     lea edx,dword ptr ss:[esp+0x24]          ; |
0040187E   .  52            push edx                                 ; |pMaxFilenameLength
0040187F   .  8D4424 20     lea eax,dword ptr ss:[esp+0x20]          ; |
00401883   .  50            push eax                                 ; |pVolumeSerialNumber
00401884   .  68 00010000   push 0x100                      ; |MaxVolumeNameSize = 100 (256.)
00401889   .  8D8C24 340100>lea ecx,dword ptr ss:[esp+0x134]         ; |
00401890   .  51            push ecx                                 ; |VolumeNameBuffer
00401891   .  68 B8974300   push mycrack.004397B8                    ; |c:\
00401896   .  FF15 84424300 call dword ptr ds:[<&KERNEL32.GetVolumeI>; \GetVolumeInformationA
// GetVolumeInformationA获取到VolumeSerialNumber即C盘的卷序列号
0040189C   .  E8 8AC60000   call mycrack.0040DF2B
004018A1   .  33C9          xor ecx,ecx
004018A3   .  85C0          test eax,eax
004018A5   .  0F95C1        setne cl
004018A8   .  85C9          test ecx,ecx
004018AA   .  75 0A         jnz Xmycrack.004018B6
004018AC   .  68 05400080   push 0x80004005
004018B1   .  E8 4A060000   call mycrack.00401F00
004018B6   >  8B10          mov edx,dword ptr ds:[eax]
004018B8   .  8BC8          mov ecx,eax
004018BA   .  8B42 0C       mov eax,dword ptr ds:[edx+0xC]
004018BD   .  FFD0          call eax
004018BF   .  83C0 10       add eax,0x10
004018C2   .  894424 0C     mov dword ptr ss:[esp+0xC],eax
004018C6   .  C78424 280200>mov dword ptr ss:[esp+0x228],0x0
004018D1   .  8B4C24 10     mov ecx,dword ptr ss:[esp+0x10]
004018D5   .  81F1 14587934 xor ecx,0x34795814
//直接与0x34795814异或
004018DB   .  51            push ecx
004018DC   .  8D5424 10     lea edx,dword ptr ss:[esp+0x10]
004018E0   .  68 BC974300   push mycrack.004397BC                    ;  1%04x
004018E5   .  52            push edx
004018E6   .  E8 15090000   call mycrack.00402200
//printf打印异或后的HWID,注意打印的字符串前面有个恒定的1
004018EB   .  8B7C24 18     mov edi,dword ptr ss:[esp+0x18]
004018EF   .  83C4 0C       add esp,0xC
004018F2   .  57            push edi
004018F3   .  68 EA030000   push 0x3EA
004018F8   .  8BCE          mov ecx,esi
004018FA   .  E8 78A90000   call mycrack.0040C277 // SetDlgItemTextA
根据上面的算法继续验证,GetVolumeInformationA执行完后VolumeSerialNumber为0x00002783,与0x34795814异或后得0x34797F97,小写打印后得字符串HWID为134797f97。
       既然知道了HWID的第一位和SN的第一位恒定为数字1,重复之前ShellCode解密算法可知,Key的第一位一定是字节0,根据HWID和SN已知长度以及ShellCode解密算法可得知Key的长度是9。之前已经提到,再没有一组正确密钥或知道ShellCode部分真正代码,我们无法得知逆算出Key。未知Key的8位若使用穷解的方法为 255^8组,这显然不现实,那么CrackMe是人写的,他的Key排除乱打的情况下,要么为他经常用的一组8位数字,要么就是0 1 2 3 4 5 6 7 8这种最简单的密钥,抱着尝试的心态,我首先尝试了0-8的Key。
       侥幸的情况下我用此Key与HWID进行逐位相加计算得到我的正确SN应该为146:=<l@?,于是我输入了SN确定后在0x00401BD8未知断下跟进ShellCode,没错,Key被蒙对了。。。。。。可以看见规整的ShellCode,如下:
[C] 纯文本查看 复制代码
00446780   /EB 42           jmp Xmycrack.004467C4
//此Sub枚举导出表根据Hash获取函数地址
00446782   |8B59 3C         mov ebx,dword ptr ds:[ecx+0x3C]
00446785   |8B5C0B 78       mov ebx,dword ptr ds:[ebx+ecx+0x78]
00446789   |03D9            add ebx,ecx
0044678B   |8B73 20         mov esi,dword ptr ds:[ebx+0x20]
0044678E   |03F1            add esi,ecx
00446790   |33FF            xor edi,edi
00446792   |4F              dec edi
00446793   |47              inc edi
00446794   |AD              lods dword ptr ds:[esi]
00446795   |33ED            xor ebp,ebp
00446797   |0FB61401        movzx edx,byte ptr ds:[ecx+eax]
0044679B   |38F2            cmp dl,dh
0044679D   |74 08           je Xmycrack.004467A7
0044679F   |C1CD 03         ror ebp,0x3
004467A2   |03EA            add ebp,edx
004467A4   |40              inc eax
004467A5  ^|EB F0           jmp Xmycrack.00446797
004467A7   |3B6C24 04       cmp ebp,dword ptr ss:[esp+0x4]
004467AB  ^|75 E6           jnz Xmycrack.00446793
004467AD   |8B73 24         mov esi,dword ptr ds:[ebx+0x24]
004467B0   |03F1            add esi,ecx
004467B2   |66:8B3C7E       mov di,word ptr ds:[esi+edi*2]
004467B6   |8B73 1C         mov esi,dword ptr ds:[ebx+0x1C]
004467B9   |03F1            add esi,ecx
004467BB   |8B04BE          mov eax,dword ptr ds:[esi+edi*4]
004467BE   |03C1            add eax,ecx
004467C0   |5B              pop ebx
004467C1   |5F              pop edi
004467C2   |53              push ebx
004467C3   |C3              retn
004467C4   \EB 4F           jmp Xmycrack.00446815
004467C6    33C0            xor eax,eax
004467C8    64:3340 30      xor eax,dword ptr fs:[eax+0x30]
004467CC    8B40 0C         mov eax,dword ptr ds:[eax+0xC]
004467CF    8B70 1C         mov esi,dword ptr ds:[eax+0x1C]
004467D2    AD              lods dword ptr ds:[esi]
004467D3    8B48 08         mov ecx,dword ptr ds:[eax+0x8]
//从PEB->_PEB_LDR_DATA->InInitializationOrderModuleList中遍历双向链表
004467D6    58              pop eax
//[esp]为“Good Job!”字符串指针
004467D7    33DB            xor ebx,ebx
004467D9    33FF            xor edi,edi
004467DB    66:BF 3332      mov di,0x3233
004467DF    57              push edi
004467E0    68 75736572     push 0x72657375
004467E5    8BFC            mov edi,esp
004467E7    53              push ebx
004467E8    51              push ecx
004467E9    53              push ebx
004467EA    50              push eax
004467EB    50              push eax
004467EC    53              push ebx
004467ED    57              push edi
004467EE    68 54128120     push 0x20811254
004467F3    E8 8AFFFFFF     call mycrack.00446782
004467F8    FFD0            call eax
004467FA    8BC8            mov ecx,eax
004467FC    68 25593AE4     push 0xE43A5925
00446801    E8 7CFFFFFF     call mycrack.00446782
00446806    FFD0            call eax
00446808    59              pop ecx
00446809    68 97196C2D     push 0x2D6C1997
0044680E    E8 6FFFFFFF     call mycrack.00446782
00446813    FFD0            call eax
00446815    E8 ACFFFFFF     call mycrack.004467C6
0044681A  //此处储存着“Good Job!”字符串,call后从堆栈取出字符串指针
对应机器码表为:
EB 42 8B 59 3C 8B 5C 0B 78 03 D98B 73 20 03 F1 33 FF 4F 47 AD 33 ED 0F B6 14 01 38 F2 74 08 C1
CD 03 03 EA 40 EB F0 3B 6C 24 0475 E6 8B 73 24 03 F1 66 8B 3C 7E 8B 73 1C 03 F1 8B 04 BE 03 C1
5B 5F 53 C3 EB 4F 33 C0 64 33 4030 8B 40 0C 8B 70 1C AD 8B 48 08 58 33 DB 33 FF 66 BF 33 32 57
68 75 73 65 72 8B FC 53 51 53 5050 53 57 68 54 12 81 20 E8 8A FF FF FF FF D0 8B C8 68 25 59 3A
E4 E8 7C FF FF FF FF D0 59 68 9719 6C 2D E8 6F FF FF FF FF D0 E8 AC FF FF FF
于是F9快乐跑起,结果程序异常结束。。。难道有Anti?不可能!接着跟进ShellCode进行分析错误原因,很快变跟到了错误。
00446797 >  0FB61401        movzx edx,byte ptr ds:[ecx+eax]
在重新跑起逐步跟进发现,错误原因是在PEB中遍历DLL的双向链表后,DLL中获取函数失败导致,那么第一反应是系统问题,于是抛弃Windows 7,跑起Win XP SP3虚拟机进行测试,果然不出所料,根据算法得到的SN成功提示了“Good Job!”(如图5所示)!
那么根据XP调试进行对比可得到Win7崩溃原因可知,在从PEB遍历DLL后要获取LoadLibraryA和MessageBoxA,XP下获取HASH对比的是kernel32.dll,而Win7遍历直接取出是kernelbase.dll的跳板Dll,如此ShellCode中写的HASH无法匹配,导致ExportTable遍历越界,修正办法改HASH算法或指定kernel32.dll遍历即可,WIN7修正CrackMe ShellCode后成功提示(如图6所示)。
至此,完全完成此CrackMe的逆向工作,贴出KeyGen关键代码易语言版(如图7所示)和C版:
易语言版:
C版:
[C] 纯文本查看 复制代码
void MakeKey(unsigned char *HWID,unsigned char *SN){
        unsigned char Key[10]={0,1,2,3,4,5,6,7,8};

        for(int i = 0 ; i < 9 ; i++){
                SN[i] = HWID[i] + Key[i];
        }
}
备注:完整的CrackMe驱动源代码我分别用C++和易语言写了,两个版本驱动替换原有驱动正常工作,KeyGen源代码也包含这两个语言,可以在目录下找到相关源代码。因为发烧生病进度慢,文章排版较麻烦,此文未发之前已经有人在吾爱发此贴,如有雷同纯属巧合~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 9热心值 +9 收起 理由
Chief + 1 吾爱破解2013CM大赛,有你更精彩!
vipcrack + 1 膜拜,更关心IDA 6.4 Pro Plus是否可分享。
Peace + 1 精品文章啊,目前赛事奖励,最后评分活动结.
onlyonen + 1 太厉害了
brack + 1 你为何如此牛逼。
温柔 + 1 除了膜拜我想不起其他词了
马斯维尔 + 1 大牛,你为什么这么叼。完全膜拜!
指尖留香 + 1 谢谢@Thanks!
Cari丶 + 1 已答复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

指尖留香 发表于 2013-12-15 07:33
前排支持,下来慢慢学习.
lxczeus 发表于 2013-12-15 08:11
xxt1680 发表于 2013-12-15 08:20
马斯维尔 发表于 2013-12-15 08:50
太精彩了!太精彩了!无发言表!震撼!优雅的解答!
Cari丶 发表于 2013-12-15 08:54
一股膜拜之情油然而生。分析的很到位
ZZ1196326205 发表于 2013-12-15 09:12
Worship da niu!
Peace 发表于 2013-12-15 11:06
真心很帅的一篇文章,写得很用心,膜拜一下
JoyChou 发表于 2013-12-15 16:46
   mov ebx,fs:[0x30]   //得到peb结构体的地址
   mov ebx,[ebx + 0xc] //得到Ldr结构体的地址
   mov ebx,[ebx + 0xc] //得到ldr.InLoadOrderModuleList.Flink 第一个模块,当前进程
   mov ebx,[ebx]   //得到第二个模块地址 ntdll.dll
   mov ebx,[ebx]   //得到第三个模块地址 kernel32.dll
   mov ebx,[ebx+0x18]  //得到第三个模块地址(kernel32模块的dllbase)

这样就可以通用,至少xp和win7可以
a070458 发表于 2013-12-15 19:53
{:1_931:}好文章! 膜拜之!~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止回复与主题无关非技术内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-29 05:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表