solly 发表于 2019-4-8 21:43

160个crackme 之152---The AntiXryst 的MD5注册分析和注册机实现

本帖最后由 solly 于 2019-4-13 16:22 编辑

看看160个crackme集合大多数都在这里有教程,只有少数几个没有,选择了一个没有的试一下。
我选择了第152个,The AntiXryst,好象这个还没有教程,于是动手了。

这个程序在 CHM集合的列表中説是delphi编写的。先检查一下看看。

没检测到壳,省事不少。

有个RCDATA资源表,并含有一个TFORM1的资源,再通过UltraEdit看一下:

标准的Delphi程序第1节内容,是Delphi编写没有错了,通过 DeDeDark打开程序文件:

只有一个表单,而且只有一个事件 OnTimer,一看就是定时器事件,进入事件:

记住事件的起始地址:0x00457AF8。并记住一些delphi的系统调用,这些系统调用在OD中就不用去跟踪了。
启动OD,载入Crackme程序,直接右键”转到->表达式“,输入前面的地址:

来到 OnTimer 事件处理程序,先不下断,F9进入程序,输入一个用户名。

这个crackme的注册码通过一个checkbox矩陈来输入注册码。
回到OD下断,稍等一下OD就断下了程序,原来是通过定时器不停的来验证注册码。

下面是其汇编代码:
; 以下是 Timer 的事件处理函数
00457AF8/.55            push    ebp
00457AF9|.8BEC          mov   ebp, esp
00457AFB|.83C4 8C       add   esp, -74
00457AFE|.53            push    ebx
00457AFF|.56            push    esi
00457B00|.57            push    edi
00457B01|.33C9          xor   ecx, ecx
00457B03|.894D 8C       mov   dword ptr , ecx
00457B06|.894D F8       mov   dword ptr , ecx
00457B09|.8BD8          mov   ebx, eax
00457B0B|.33C0          xor   eax, eax
00457B0D|.55            push    ebp
00457B0E|.68 337C4500   push    00457C33                                    ;SEH 地址
00457B13|.64:FF30       push    dword ptr fs:                        ;保存上一个SEH地址
00457B16|.64:8920       mov   dword ptr fs:, esp                     ;设置当前SEH地址为 0x00457C33
00457B19|.8D55 F8       lea   edx, dword ptr
00457B1C|.8B83 68040000 mov   eax, dword ptr
00457B22|.E8 A9F4FDFF   call    00436FD0                                    ;GetText,读取注册名
00457B27|.8D55 8C       lea   edx, dword ptr
00457B2A|.8B45 F8       mov   eax, dword ptr                       ;eax==>注册名
00457B2D|.E8 16FBFAFF   call    00407648                                    ;Delphi 函数:Trim()
00457B32|.837D 8C 00    cmp   dword ptr , 0                     ;是否空串
00457B36|.0F84 D9000000 je      00457C15
00457B3C|.8D45 A0       lea   eax, dword ptr
00457B3F|.E8 0CE9FFFF   call    00456450                                    ;MD5初始化,4个DWORD引子
00457B44|.8B45 F8       mov   eax, dword ptr                       ;eax==>注册名
00457B47|.E8 8CBFFAFF   call    00403AD8                                    ;取注册码名的长度
00457B4C|.83F8 40       cmp   eax, 40                                     ;eax=注册码的长度
00457B4F|.7D 18         jge   short 00457B69                              ;长度大于或等于0x40则跳过注册名复制加长
00457B51|>8D45 F8       /lea   eax, dword ptr
00457B54|.8B55 F8       |mov   edx, dword ptr                      ;edx==>注册名
00457B57|.E8 84BFFAFF   |call    00403AE0                                 ;复制用户名并连接成一个新串,regName = regName + regName
00457B5C|.8B45 F8       |mov   eax, dword ptr                      ;eax==>克隆后的注册名
00457B5F|.E8 74BFFAFF   |call    00403AD8                                 ;重新计算长度
00457B64|.83F8 40       |cmp   eax, 40                                    ;eax 由 5,10,20,40,80...递增
00457B67|.^ 7E E8         \jle   short 00457B51
00457B69|>8B45 F8       mov   eax, dword ptr
00457B6C|.E8 67BFFAFF   call    00403AD8                                    ;最后取得克隆后的字符串长度
00457B71|.83F8 40       cmp   eax, 40                                     ;eax==0x50==80
00457B74|.7E 24         jle   short 00457B9A                              ;长度小于或等于则跳转
00457B76|>8B45 F8       /mov   eax, dword ptr                      ;eax==>克隆加长后的注册名
00457B79|.E8 5ABFFAFF   |call    00403AD8                                 ;取长度
00457B7E|.8BD0          |mov   edx, eax
00457B80|.8D45 F8       |lea   eax, dword ptr
00457B83|.B9 01000000   |mov   ecx, 1                                     ;删除的字符数
00457B88|.E8 8FC1FAFF   |call    00403D1C                                 ;删除注册名最后1个字符
00457B8D|.8B45 F8       |mov   eax, dword ptr
00457B90|.E8 43BFFAFF   |call    00403AD8                                 ;计算长度
00457B95|.83F8 40       |cmp   eax, 40                                    ;是否长度为0x40
00457B98|.^ 75 DC         \jnz   short 00457B76                           ;不是0x40则继续去删除最后1个字符
00457B9A|>33D2          xor   edx, edx                                    ;int i = 0
00457B9C|.8D45 B0       lea   eax, dword ptr                      ;指向字符串第0字节,即delphi短String类型的保存长度的位置
00457B9F|>8B4D F8       /mov   ecx, dword ptr                      ;ecx==>缩短后注册名,长度为0x40
00457BA2|.8A4C11 FF   |mov   cl, byte ptr                    ;取string(i-1)位置字符, 第1次读取的字符为delphi字符串长度的高8位,为0
00457BA6|.8808          |mov   byte ptr , cl                         ;复制注册名,第1个字节为'\0',前面缩短的注册名最后1字节没有复制
00457BA8|.42            |inc   edx
00457BA9|.40            |inc   eax
00457BAA|.83FA 40       |cmp   edx, 40
00457BAD|.^ 75 F0         \jnz   short 00457B9F
00457BAF|.8D55 A0       lea   edx, dword ptr                      ;edx==>MD5算法引子,0019FCD8:01 23 45 67 89 AB CD EF FE DC BA 98 76 54 32 10
00457BB2|.8D45 B0       lea   eax, dword ptr                      ;eax==>'\0' + 注册名前0x3F个字符
00457BB5|.E8 FEE8FFFF   call    004564B8                                    ;MD5运算
00457BBA|.8D75 A0       lea   esi, dword ptr                      ;esi==>MD5计算后的值
00457BBD|.8D7D 90       lea   edi, dword ptr
00457BC0|.B9 04000000   mov   ecx, 4
00457BC5|.F3:A5         rep   movs dword ptr es:, dword ptr
00457BC7|.8D55 90       lea   edx, dword ptr                      ;edx==>前面MD5计算值,当作下一次的MD5引子
00457BCA|.8D45 B0       lea   eax, dword ptr                      ;eax==>'\0' + 注册名前0x3F个字符
00457BCD|.E8 E6E8FFFF   call    004564B8                                    ;MD5运算
00457BD2|.8B45 A0       mov   eax, dword ptr                      ;EAX=MD5_1,即第1次计算后的MD5第1个整数
00457BD5|.2B45 A4       sub   eax, dword ptr                      ;EAX=MD5_1 - MD5_1
00457BD8|.2B45 A8       sub   eax, dword ptr                      ;EAX=MD5_1 - MD5_1 - MD5_1
00457BDB|.0345 AC       add   eax, dword ptr                      ;EAX=MD5_1 - MD5_1 - MD5_1 + MD5_1
00457BDE|.8945 A0       mov   dword ptr , eax                     ;=MD5_1 - MD5_1 - MD5_1 + MD5_1, 保存第1个SN整数
00457BE1|.8B45 90       mov   eax, dword ptr                      ;EAX=MD5_2,即第2次计算后的MD5第1个整数
00457BE4|.2B45 94       sub   eax, dword ptr                      ;EAX=MD5_2 - MD5_2
00457BE7|.2B45 98       sub   eax, dword ptr                      ;EAX=MD5_2 - MD5_2 - MD5_2
00457BEA|.0345 9C       add   eax, dword ptr                      ;EAX=MD5_2 - MD5_2 - MD5_2 + MD5_2
00457BED|.8945 90       mov   dword ptr , eax                     ;=MD5_2 - MD5_2 - MD5_2 + MD5_2,保存第2个SN整数
00457BF0|.8D55 F0       lea   edx, dword ptr                      ;eax,edx两个整数缓冲区,用来保存checkbox的状态
00457BF3|.8D45 F4       lea   eax, dword ptr
00457BF6|.E8 B1F8FFFF   call    004574AC                                    ;取 CheckBox 的状态,生成两个32位无符号数,过程简单统一,不深入解释了
00457BFB|.8B45 F0       mov   eax, dword ptr                      ;参数4,第2个checkbox状态值
00457BFE|.50            push    eax
00457BFF|.8B4D F4       mov   ecx, dword ptr                       ;参数3,第1个checkbox状态值
00457C02|.8B55 90       mov   edx, dword ptr                      ;参数2,第2个MD5计算值变形运算后的数值
00457C05|.8B45 A0       mov   eax, dword ptr                      ;参数1,第1个MD5运算值变形运算后的数值
00457C08|.E8 AFFEFFFF   call    00457ABC                                    ;注册码比较调用,对比两组整数,不相等则用随机数取得一个索引地址
00457C0D|.8945 FC       mov   dword ptr , eax                ; eax 为上面调用通过查表返回的一个地址
00457C10|.8B45 FC       mov   eax, dword ptr                 ; eax为调用入口地址,注册不成功是地址范围 0x0045746C~004574A8 内的一个随机地址,成功则是 0x004573C8
00457C13|.FFD0          call    eax                                       ;如果注册成功,则显示成功注册,不然就继续显示失败。
00457C15|>33C0          xor   eax, eax                                    ;指示 SEH 头的索引
00457C17|.5A            pop   edx                                       ;取保存的SEH地址
00457C18|.59            pop   ecx
00457C19|.59            pop   ecx
00457C1A|.64:8910       mov   dword ptr fs:, edx                     ;恢复SEH
00457C1D|.68 3A7C4500   push    00457C3A                                    ;下面的 retn 返回地址
00457C22|>8D45 8C       lea   eax, dword ptr
00457C25|.E8 32BCFAFF   call    0040385C                                    ;Delphi 资源释放过程
00457C2A|.8D45 F8       lea   eax, dword ptr
00457C2D|.E8 2ABCFAFF   call    0040385C                                    ;Delphi 资源释放过程
00457C32\.C3            retn                                                ;跳转到 0x00457C3A
00457C33   .^ E9 E4B6FAFF   jmp   0040331C                                    ;SEH 异常处理
00457C38   .^ EB E8         jmp   short 00457C22                              ;SEH 处理完成后,跳回到这里
00457C3A   .5F            pop   edi
00457C3B   .5E            pop   esi
00457C3C   .5B            pop   ebx
00457C3D   .8BE5          mov   esp, ebp
00457C3F   .5D            pop   ebp
00457C40   .C3            retn
以上是主验证过程,其取得用户名,如果长度没有64个字符,就一直重复拼接直到超过64个字符,然后再截短为64个字符。
再进行MD5处理。不过,其传入的用户名在进行MD5处理时,只取了前63个字符填充在MD5输入缓冲区的后63字节中,而缓冲区第1个字符位置填充的是'\0'(这个在C/C++中就不好计算长度了,需要从第2个字符开始处理)。
这个生成的'\0' + 重复注册名字符串,进行两次 MD5处理,头一次是标准的,并且不需要padding填充。第二次直接用第一次的MD5码作为初始化引子,进行MD5计算。
下面是MD 5 运算调用的过程,汇编代码太长,反正是一个标准过程,因此中间删除了一部分,免得显示得太长:
0045646C/$53            push    ebx
0045646D|.56            push    esi
0045646E|.57            push    edi
0045646F|.83C4 C0       add   esp, -40
00456472|.8BF0          mov   esi, eax
00456474|.8D3C24      lea   edi, dword ptr
00456477|.B9 10000000   mov   ecx, 10                                 ;长度,16 * 4 字节
0045647C|.F3:A5         rep   movs dword ptr es:, dword ptr ;一次移动4字节,一共移动16次
0045647E|.BB 10000000   mov   ebx, 10                                 ;循环16次 int n = 16
00456483|.8BC4          mov   eax, esp
00456485|.8BCA          mov   ecx, edx
00456487|>0FB630      /movzx   esi, byte ptr                       ;读取注册名第1个字符,第1个是'\0'
0045648A|.0FB678 01   |movzx   edi, byte ptr                   ;读取注册名第2个字符,其实就是注册名的第1个字符
0045648E|.C1E7 08       |shl   edi, 8
00456491|.0BF7          |or      esi, edi
00456493|.0FB678 02   |movzx   edi, byte ptr                   ;第3个
00456497|.C1E7 10       |shl   edi, 10
0045649A|.0BF7          |or      esi, edi
0045649C|.0FB678 03   |movzx   edi, byte ptr                   ;第4个
004564A0|.C1E7 18       |shl   edi, 18
004564A3|.0BF7          |or      esi, edi
004564A5|.8931          |mov   dword ptr , esi                     ;将每4字节一组转换成16进制数字保存
004564A7|.83C1 04       |add   ecx, 4
004564AA|.83C0 04       |add   eax, 4
004564AD|.4B            |dec   ebx                                    ;n--
004564AE|.^ 75 D7         \jnz   short 00456487                           ;循环转换
004564B0|.83C4 40       add   esp, 40
004564B3|.5F            pop   edi
004564B4|.5E            pop   esi
004564B5|.5B            pop   ebx
004564B6\.C3            retn
004564B7      90            nop
004564B8/$55            push    ebp
004564B9|.8BEC          mov   ebp, esp
004564BB|.81C4 64FFFFFF add   esp, -9C
004564C1|.53            push    ebx
004564C2|.56            push    esi
004564C3|.57            push    edi
004564C4|.33C9          xor   ecx, ecx
004564C6|.894D E4       mov   dword ptr , ecx                   ; = 0
004564C9|.8BF0          mov   esi, eax                                  ;esi==>'\0' + 注册名前0x3F字符
004564CB|.8D7D A4       lea   edi, dword ptr                    ;edi=0x0019FC48
004564CE|.B9 10000000   mov   ecx, 10
004564D3|.F3:A5         rep   movs dword ptr es:, dword ptr ;复制16*4字节注册名
004564D5|.8955 FC       mov   dword ptr , edx                  ;==>MD5引子
004564D8|.33C0          xor   eax, eax
004564DA|.55            push    ebp
004564DB|.68 B9734500   push    004573B9
004564E0|.64:FF30       push    dword ptr fs:
004564E3|.64:8920       mov   dword ptr fs:, esp
004564E6|.8D95 64FFFFFF lea   edx, dword ptr
004564EC|.8D45 A4       lea   eax, dword ptr                    ;eax==>注册名
004564EF|.E8 78FFFFFF   call    0045646C                                  ;字符串复制,复制64字节
004564F4|.E8 13C3FAFF   call    0040280C                                  ;随机数引子初始化,即当前时间的毫秒值
004564F9|.8B45 FC       mov   eax, dword ptr                   ;EAX==>MD5算法引子
004564FC|.8B00          mov   eax, dword ptr                       ;eax=第1个整数,0x67452301
004564FE|.8945 F8       mov   dword ptr , eax
00456501|.8B45 FC       mov   eax, dword ptr
00456504|.8B40 04       mov   eax, dword ptr
00456507|.8945 F0       mov   dword ptr , eax
0045650A|.8B75 FC       mov   esi, dword ptr
0045650D|.8B76 08       mov   esi, dword ptr
00456510|.8B5D FC       mov   ebx, dword ptr
00456513|.8B5B 0C       mov   ebx, dword ptr
00456516|.8B45 F0       mov   eax, dword ptr
00456519|.8945 F4       mov   dword ptr , eax
0045651C|.8B7D F8       mov   edi, dword ptr
0045651F|.8975 F8       mov   dword ptr , esi
00456522|.F755 F4       not   dword ptr
00456525|.8B45 F0       mov   eax, dword ptr
00456528|.2145 F8       and   dword ptr , eax
0045652B|.215D F4       and   dword ptr , ebx
0045652E|.8B45 F4       mov   eax, dword ptr
00456531|.0945 F8       or      dword ptr , eax
00456534|.B8 65000000   mov   eax, 65
00456539|.E8 92C4FAFF   call    004029D0                                  ;生成0~100的随机数
0045653E|.8B1485 F09245>mov   edx, dword ptr              ;查一个表(0~0x64)取地址,不过该表保存是同一个地址0x0045642C,该地址(edx)指向“MatrixxxMadness1 - UNREGISTERED!”
00456545|.8D45 E4       lea   eax, dword ptr                    ;保存上面读取到的字符串地址
00456548|.E8 A7D3FAFF   call    004038F4                                  ;保存地址到
0045654D|.8B85 64FFFFFF mov   eax, dword ptr
00456553|.8945 F4       mov   dword ptr , eax
00456556|.8B45 F4       mov   eax, dword ptr
00456559|.0145 F8       add   dword ptr , eax
0045655C|.037D F8       add   edi, dword ptr
0045655F|.81EF 885B9528 sub   edi, 28955B88                           ; MD5 标准写法是加法,因此这里实际上是 edi += 0xd76aa478
00456565|.897D F4       mov   dword ptr , edi
00456568|.8B45 F4       mov   eax, dword ptr
0045656B|.8945 F8       mov   dword ptr , eax
0045656E|.C165 F8 07    shl   dword ptr , 7                      ;左7右25,模拟循环左移7位
00456572|.C16D F4 19    shr   dword ptr , 19
00456576|.8B45 F4       mov   eax, dword ptr
00456579|.0945 F8       or      dword ptr , eax
0045657C|.8B45 F0       mov   eax, dword ptr
0045657F|.8945 F4       mov   dword ptr , eax
00456582|.8B45 F0       mov   eax, dword ptr
00456585|.0145 F8       add   dword ptr , eax
00456588|.8B7D F8       mov   edi, dword ptr
0045658B|.8B45 F8       mov   eax, dword ptr
0045658E|.2145 F4       and   dword ptr , eax
00456591|.F7D7          not   edi
00456593|.23FE          and   edi, esi
00456595|.097D F4       or      dword ptr , edi
00456598|.8BBD 68FFFFFF mov   edi, dword ptr
0045659E|.017D F4       add   dword ptr , edi

;此处省略n行汇编代码,都是MD5算法的代码

004572EC|.0375 F8       add   esi, dword ptr
004572EF|.81C6 BBD2D72A add   esi, 2AD7D2BB
004572F5|.8B5D 88       mov   ebx, dword ptr
004572F8|.8975 F8       mov   dword ptr , esi
004572FB|.C165 F8 0F    shl   dword ptr , 0F
004572FF|.C1EE 11       shr   esi, 11
00457302|.0975 F8       or      dword ptr , esi
00457305|.8BF7          mov   esi, edi
00457307|.8B45 F0       mov   eax, dword ptr
0045730A|.0145 F8       add   dword ptr , eax
0045730D|.F7D6          not   esi
0045730F|.0B75 F8       or      esi, dword ptr
00457312|.3375 F0       xor   esi, dword ptr
00457315|.03F3          add   esi, ebx
00457317|.B8 65000000   mov   eax, 65
0045731C|.E8 AFB6FAFF   call    004029D0
00457321|.8B1485 F09245>mov   edx, dword ptr
00457328|.8D45 E4       lea   eax, dword ptr
0045732B|.E8 C4C5FAFF   call    004038F4
00457330|.8B5D FC       mov   ebx, dword ptr
00457333|.8B1B          mov   ebx, dword ptr
00457335|.03DF          add   ebx, edi
00457337|.0375 F4       add   esi, dword ptr
0045733A|.81EE 6F2C7914 sub   esi, 14792C6F
00457340|.8975 F4       mov   dword ptr , esi
00457343|.8B75 FC       mov   esi, dword ptr
00457346|.8B76 04       mov   esi, dword ptr
00457349|.8B7D F4       mov   edi, dword ptr
0045734C|.8B45 FC       mov   eax, dword ptr
0045734F|.8918          mov   dword ptr , ebx
00457351|.C1EF 0B       shr   edi, 0B
00457354|.C165 F4 15    shl   dword ptr , 15
00457358|.0B7D F4       or      edi, dword ptr
0045735B|.8B45 FC       mov   eax, dword ptr
0045735E|.8B40 08       mov   eax, dword ptr
00457361|.8945 F4       mov   dword ptr , eax
00457364|.037D F8       add   edi, dword ptr
00457367|.8B45 F8       mov   eax, dword ptr
0045736A|.0145 F4       add   dword ptr , eax
0045736D|.8B45 FC       mov   eax, dword ptr
00457370|.8B40 0C       mov   eax, dword ptr
00457373|.8945 F8       mov   dword ptr , eax
00457376|.03F7          add   esi, edi
00457378|.8B45 F0       mov   eax, dword ptr
0045737B|.0145 F8       add   dword ptr , eax
0045737E|.8B45 FC       mov   eax, dword ptr
00457381|.8B55 F4       mov   edx, dword ptr
00457384|.8950 08       mov   dword ptr , edx
00457387|.8B45 FC       mov   eax, dword ptr
0045738A|.8B55 F8       mov   edx, dword ptr
0045738D|.8950 0C       mov   dword ptr , edx
00457390|.8B45 FC       mov   eax, dword ptr
00457393|.8970 04       mov   dword ptr , esi
00457396|.8B55 E4       mov   edx, dword ptr                    ;edx==>“MatrixxxMadness1 - UNREGISTERED!”
00457399|.A1 08A84500   mov   eax, dword ptr
0045739E|.E8 5DFCFDFF   call    00437000                                  ;SetText,显示上面的字符串
004573A3|.33C0          xor   eax, eax
004573A5|.5A            pop   edx
004573A6|.59            pop   ecx
004573A7|.59            pop   ecx
004573A8|.64:8910       mov   dword ptr fs:, edx
004573AB|.68 C0734500   push    004573C0
004573B0|>8D45 E4       lea   eax, dword ptr
004573B3|.E8 A4C4FAFF   call    0040385C
004573B8\.C3            retn
004573B9   .^ E9 5EBFFAFF   jmp   0040331C
004573BE   .^ EB F0         jmp   short 004573B0
004573C0   .5F            pop   edi
004573C1   .5E            pop   esi
004573C2   .5B            pop   ebx
004573C3   .8BE5          mov   esp, ebp
004573C5   .5D            pop   ebp
004573C6   .C3            retn

下面是注册码比较过程:

00457ABC/$55            push    ebp
00457ABD|.8BEC          mov   ebp, esp
00457ABF|.53            push    ebx
00457AC0|.56            push    esi
00457AC1|.57            push    edi
00457AC2|.8BF9          mov   edi, ecx                                     ;参数3,第1个checkbox状态值
00457AC4|.8BF2          mov   esi, edx                                     ;参数2,第2个MD5计算值变形运算后的数值
00457AC6|.8BD8          mov   ebx, eax                                     ;参数1,第1个MD5运算值变形运算后的数值
00457AC8|.E8 3FADFAFF   call    0040280C
00457ACD|.2BDF          sub   ebx, edi                                     ;参数1与参数3比较
00457ACF|.75 09         jnz   short 00457ADA
00457AD1|.2B75 08       sub   esi, dword ptr                      ;参数2与参数4比较,为参数4,第2个checkbox状态值
00457AD4|.75 04         jnz   short 00457ADA
00457AD6|.33C0          xor   eax, eax                                     ;都相等则生成索引 0
00457AD8|.EB 0A         jmp   short 00457AE4
00457ADA|>B8 00010000   mov   eax, 100                                     ;生成0~0xFF范围内的随机数
00457ADF|.E8 ECAEFAFF   call    004029D0                                     ;生成随机数
00457AE4|>25 FF000000   and   eax, 0FF                                     ; 限制索引在0~0xFF范围内
00457AE9|.8B0485 849445>mov   eax, dword ptr                 ; 通过索引查表返回调用地址
00457AF0|.5F            pop   edi
00457AF1|.5E            pop   esi
00457AF2|.5B            pop   ebx
00457AF3|.5D            pop   ebp
00457AF4\.C2 0400       retn    4

如果注册码不相等,则会调用下面这些函数,都是空函数,只有一个retn:
///////////////// 注册失败时的 call eax 调用的函数列表 ////////////////////////
0045746C   .C3            retn
0045746D      8D40 00       lea   eax, dword ptr
00457470   .C3            retn
00457471      8D40 00       lea   eax, dword ptr
00457474   .C3            retn
00457475      8D40 00       lea   eax, dword ptr
00457478   .C3            retn
00457479      8D40 00       lea   eax, dword ptr
0045747C   .C3            retn
0045747D      8D40 00       lea   eax, dword ptr
00457480   .C3            retn
00457481      8D40 00       lea   eax, dword ptr
00457484   .C3            retn
00457485      8D40 00       lea   eax, dword ptr
00457488   .C3            retn
00457489      8D40 00       lea   eax, dword ptr
0045748C   .C3            retn
0045748D      8D40 00       lea   eax, dword ptr
00457490   .C3            retn
00457491      8D40 00       lea   eax, dword ptr
00457494   .C3            retn
00457495      8D40 00       lea   eax, dword ptr
00457498   .C3            retn
00457499      8D40 00       lea   eax, dword ptr
0045749C   .C3            retn
0045749D      8D40 00       lea   eax, dword ptr
004574A0   .C3            retn
004574A1      8D40 00       lea   eax, dword ptr
004574A4   .C3            retn
004574A5      8D40 00       lea   eax, dword ptr
004574A8   .C3            retn
004574A9      8D40 00       lea   eax, dword ptr
这个Crackme 输入注册码的方式比较特别,是通过一个checkbox矩陈来输入的。
序列号共两个整数,每个整数32bits,分成4行,每行输入8bits,一起8行x8位,完成SN的输入。
通过上面分析,输入用户名:solly,得到SN:919D5B79-3826ACAC,再转成二进制矩陈显示如下,”1“表示选中,”0“表示不选中:
SN Matrix:
10010001
10011101
01011011
01111001
00111000
00100110
10101100
10101100
输入注册码,成功画面如下:

注册码验证过程分析完毕,以下是注册机,其中MD5代码参考网上代码,并按crackme的要求作一定修改,源码如下,共3个文件:
/*
main.cpp
*/
#include <stdio.h>
#include <stdlib.h>
#include "MD5.hpp"

int main(int argc, char** argv) {
      
      unsigned char decrypt;
      //unsigned char regname[] = "\0sollysollysollysollysollysollysollysollysollysollysollysollysol";
      char temp;
      char regname;

      printf("Keygen for 152 - 'The AntiXryst' of 160 crackme.\n");
      //// 输入用户名
      printf("Enter your name: ");
      gets(temp); //// 输入用户名

      //// 用户名处理
      int n = strlen(temp);
      char * p = regname; /// 从第2个字节开始保存连接的用户名
      strncpy(p, temp, 64);
      int m = n;
      while(m<63) {
                strcat(p+n, temp);
                m += n;
      }
      regname= '\0';/// 第1个字节设为'\0'
      regname = '\0';/// 字符串null结束符
      //printf("Name: %s\n", p);
      
      ////MD5算法
      int len = 0x40; /// 固定长度为64
      MD5_CTX md5;

      MD5Init(&md5);
      MD5Update(&md5, (unsigned char *)regname, len);
      MD5Final(&md5, decrypt);

      //////
      unsigned int sn;
      sn = md5.state2 - md5.state2 - md5.state2 + md5.state2;
      sn = md5.state - md5.state - md5.state + md5.state;
      ////
      printf("\n\nSN: %08X-%08X\n", sn, sn);
      
      printf("SN Matrix:\n");
      for(int i=0; i<2; i++) {
                unsigned int a = sn;
                unsigned int b = 0x80000000;
                for(int j=1; j<=32; j++) {
                        if(a & b) {
                              printf("1");
                        } else {
                              printf("0");
                        }
                        b >>=1;
                        if((j % 8) == 0) {
                              printf("\n");
                        }
                }
      }
      
      system("pause");

      return 0;
}
第二个是 MD5.cpp:
#include "Md5.hpp"
unsigned char PADDING[] = {
    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void MD5Init(MD5_CTX *context){
    context->count=0;
    context->count=0;
    context->state=0x67452301;
    context->state=0xEFCDAB89;
    context->state=0x98BADCFE;
    context->state=0x10325476;
    //// context->buffer;
}

void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen){
    unsigned int i=0,index=0,partlen=0;
    index=(context->count>>3)&0x3F;
    partlen=64-index;
    context->count+=inputlen<<3;
    if(context->count<(inputlen<<3)) context->count++;
    context->count+=inputlen>>29;
   
    if(inputlen>=partlen){
      memcpy(&context->buffer, input, partlen);
      MD5Transform(context->state, context->buffer);
      for(i=partlen;i+64<=inputlen;i+=64) MD5Transform(context->state,&input);
      index=0;
      //// 相对标准MD5第1处改动, 在第1次基础上直接进行第2次MD5计算/////////////
      context->state2 = context->state; //// 保存第1次的MD5值
      context->state2 = context->state; //// 保存第1次的MD5值
      context->state2 = context->state; //// 保存第1次的MD5值
      context->state2 = context->state; //// 保存第1次的MD5值
      MD5Transform(context->state, context->buffer);
      for(i=partlen;i+64<=inputlen;i+=64) MD5Transform(context->state,&input);
      index=0;
      //////////////////////////////////////////////////////////////////////////
    }
    else i=0;
    memcpy(&context->buffer, &input, inputlen-i);
}

void MD5Final(MD5_CTX *context, unsigned char digest){
    unsigned int index=0,padlen=0;
    unsigned char bits;
    index=(context->count>>3)&0x3F;
    padlen=(index<56)?(56-index):(120-index);
    ////// 相对标准MD5第2处改动, 因为主注册名已经有64字节,并且由于其第1字节为'\0',会padding掉全部内容,所以不进行Padding处理
    // MD5Update(context,PADDING,padlen); //// no padding, comments by solly
    //////////////////////////////////////////////////////////////////////////////////   
    MD5Update(context,bits,8);//index=0
    /////
    MD5Encode(digest,context->state,16);
}

void MD5Encode(unsigned char *output, unsigned int *input, unsigned int len){
    unsigned int i = 0, j = 0;
    while (j<len){
      output=input & 0xFF;
      output=(input>>8)&0xFF;
      output=(input>>16)&0xFF;
      output=(input>>24)&0xFF;
      i++;
      j += 4;
    }
}

void MD5Decode(unsigned int *output, unsigned char *input, unsigned int len){
    unsigned int i = 0, j = 0;
    while (j < len){
      output = (input) |
      (input << 8) |
      (input << 16) |
      (input << 24);
      i++;
      j += 4;
    }
}

void MD5Transform(unsigned int state, unsigned char block){
    unsigned int a = state;
    unsigned int b = state;
    unsigned int c = state;
    unsigned int d = state;
    unsigned int x;
   
    MD5Decode(x, block, 64);
    FF(a, b, c, d, x, 7, 0xd76aa478);
    FF(d, a, b, c, x, 12, 0xe8c7b756);
    FF(c, d, a, b, x, 17, 0x242070db);
    FF(b, c, d, a, x, 22, 0xc1bdceee);
    FF(a, b, c, d, x, 7, 0xf57c0faf);
    FF(d, a, b, c, x, 12, 0x4787c62a);
    FF(c, d, a, b, x, 17, 0xa8304613);
    FF(b, c, d, a, x, 22, 0xfd469501);
    FF(a, b, c, d, x, 7, 0x698098d8);
    FF(d, a, b, c, x, 12, 0x8b44f7af);
    FF(c, d, a, b, x, 17, 0xffff5bb1);
    FF(b, c, d, a, x, 22, 0x895cd7be);
    FF(a, b, c, d, x, 7, 0x6b901122);
    FF(d, a, b, c, x, 12, 0xfd987193);
    FF(c, d, a, b, x, 17, 0xa679438e);
    FF(b, c, d, a, x, 22, 0x49b40821);
   
   
    GG(a, b, c, d, x, 5, 0xf61e2562);
    GG(d, a, b, c, x, 9, 0xc040b340);
    GG(c, d, a, b, x, 14, 0x265e5a51);
    GG(b, c, d, a, x, 20, 0xe9b6c7aa);
    GG(a, b, c, d, x, 5, 0xd62f105d);
    GG(d, a, b, c, x, 9, 0x2441453);
    GG(c, d, a, b, x, 14, 0xd8a1e681);
    GG(b, c, d, a, x, 20, 0xe7d3fbc8);
    GG(a, b, c, d, x, 5, 0x21e1cde6);
    GG(d, a, b, c, x, 9, 0xc33707d6);
    GG(c, d, a, b, x, 14, 0xf4d50d87);
    GG(b, c, d, a, x, 20, 0x455a14ed);
    GG(a, b, c, d, x, 5, 0xa9e3e905);
    GG(d, a, b, c, x, 9, 0xfcefa3f8);
    GG(c, d, a, b, x, 14, 0x676f02d9);
    GG(b, c, d, a, x, 20, 0x8d2a4c8a);
   
   
    HH(a, b, c, d, x, 4, 0xfffa3942);
    HH(d, a, b, c, x, 11, 0x8771f681);
    HH(c, d, a, b, x, 16, 0x6d9d6122);
    HH(b, c, d, a, x, 23, 0xfde5380c);
    HH(a, b, c, d, x, 4, 0xa4beea44);
    HH(d, a, b, c, x, 11, 0x4bdecfa9);
    HH(c, d, a, b, x, 16, 0xf6bb4b60);
    HH(b, c, d, a, x, 23, 0xbebfbc70);
    HH(a, b, c, d, x, 4, 0x289b7ec6);
    HH(d, a, b, c, x, 11, 0xeaa127fa);
    HH(c, d, a, b, x, 16, 0xd4ef3085);
    HH(b, c, d, a, x, 23, 0x4881d05);
    HH(a, b, c, d, x, 4, 0xd9d4d039);
    HH(d, a, b, c, x, 11, 0xe6db99e5);
    HH(c, d, a, b, x, 16, 0x1fa27cf8);
    HH(b, c, d, a, x, 23, 0xc4ac5665);
   
   
    II(a, b, c, d, x, 6, 0xf4292244);
    II(d, a, b, c, x, 10, 0x432aff97);
    II(c, d, a, b, x, 15, 0xab9423a7);
    II(b, c, d, a, x, 21, 0xfc93a039);
    II(a, b, c, d, x, 6, 0x655b59c3);
    II(d, a, b, c, x, 10, 0x8f0ccc92);
    II(c, d, a, b, x, 15, 0xffeff47d);
    II(b, c, d, a, x, 21, 0x85845dd1);
    II(a, b, c, d, x, 6, 0x6fa87e4f);
    II(d, a, b, c, x, 10, 0xfe2ce6e0);
    II(c, d, a, b, x, 15, 0xa3014314);
    II(b, c, d, a, x, 21, 0x4e0811a1);
    II(a, b, c, d, x, 6, 0xf7537e82);
    II(d, a, b, c, x, 10, 0xbd3af235);
    II(c, d, a, b, x, 15, 0x2ad7d2bb);
    II(b, c, d, a, x, 21, 0xeb86d391);
    state += a;
    state += b;
    state += c;
    state += d;
}

第三个是 MD5.hpp:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>

typedef struct{
    unsigned int count;
    unsigned int state;
    unsigned char buffer;
    unsigned int state2;//// 这里是改动之三,不对算法产生影响,只是用来备份第1次MD5结果
} MD5_CTX;

#define F(x,y,z) ((x&y)|(~x&z))
#define G(x,y,z) ((x&z)|(y&~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y^(x|~z))
#define ROTATE_LEFT(x,n) ((x<<n)|(x>>(32-n)))
#define FF(a,b,c,d,x,s,ac) { a+=F(b,c,d)+x+ac; a=ROTATE_LEFT(a,s); a+=b;}
#define GG(a,b,c,d,x,s,ac) { a+=G(b,c,d)+x+ac; a=ROTATE_LEFT(a,s); a+=b;}
#define HH(a,b,c,d,x,s,ac) { a+=H(b,c,d)+x+ac; a=ROTATE_LEFT(a,s); a+=b;}
#define II(a,b,c,d,x,s,ac) { a+=I(b,c,d)+x+ac; a=ROTATE_LEFT(a,s); a+=b;}

void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen);
void MD5Final(MD5_CTX *context, unsigned char digest);
void MD5Transform(unsigned int state, unsigned char block);
void MD5Encode(unsigned char *output, unsigned int *input, unsigned int len);
void MD5Decode(unsigned int *output, unsigned char *input, unsigned int len);

以上代码是在 Dev-C++上调试通过的。
///////
完毕,码字不易!!!!评分免费!!!!

于洪生 发表于 2019-4-8 22:05

楼主辛苦了,虽然我不懂这个

yangtianqi 发表于 2019-4-9 12:27

这个厉害,膜拜大神~~~
页: [1]
查看完整版本: 160个crackme 之152---The AntiXryst 的MD5注册分析和注册机实现