当前位置:首页 > 技术学习 > 我的160个CrackMe学习系列(二):CrackMe.23-Chafe.1

我的160个CrackMe学习系列(二):CrackMe.23-Chafe.1

0x00  序言

本学习系列(二)将分析并总结160个CM之中难度中等的题目.

本次分析的程序难度中偏下,好理解好掌握!


0x01  程序分析正文

拿到程序后先查壳:

image.png

可以看到,没有壳子,所以直接开始分析,本次我选择的是全程使用od动态调试.

然后我们先运行下程序看看是大概怎样的程序.

image.png

可以看到就是一个name/serial型的程序,不过成功的提示应该会在Status编辑框里显示,并不会有MessageBox提示,这一点应该也是为了防止下断

程序载入OD后先搜索一下字符串:

image.png

可以看到成功提示的字,跳过去看一下

image.png

可以发现非常明显的成功失败判断的跳转,在该关键跳转处下断后,可以发现程序是一直被断下的,说明他的验证函数是被循环执行的,可能是创建了一个时钟进行循环验证.

此外还能发现,serial输入纯数字才能触发验证判断,否则是不会在关键跳处断下,这说明很可能是验证函数中serial被作为纯数字来进行运算.

image.png

图中可看出,最终被判断的是eax是否为0x10,而eax的值来源于byte ptr [0x403166],这个地址很可能是一个global var.因此接下来的思路就是寻找是什么地方在修改这个变量

那么就在0x403166处下个内存断点看看:

image.png

以下是断下的三个位置:

image.png

image.png

image.png

由此我们可以看出程序中每次都是给这个变量+4,那么需要加到0x10还差一次,那来试试在程序中搜索(Ctrl+F)这条汇编命令,按Ctrl+L可以跳到下一个搜索结果

image.png

啊哈!果不其然,的确有第四条指令,而且这条上面有一个jnz的跳转,如果跳成功就是执行下面把0x403166置0的操作,否则就是执行+4操作

而jnz跳转上面的test eax,eax 其实就是判断eax是否为0,若为0则不跳转jnz,再往上看,是把另一个疑似global var的[0x403188]赋值给eax,然后eax+0x9112478

后面判断eax是否为零.

因此要想这里的第四条+4指令执行,则[0x403188]里的值必须是-0x9112478,在内存中,则是(0xFFFFFFFF-0x9112478+1)=0xF6EEDB88(好像是补码?大概)

因此现在我们要再次使用刚才的套路,内存下断看看[0x403188]在哪里被written,这里特别说一点就是这里的下断是下4个字节,因为上图中运算的长度是4字节.

最终断下了三个地址:

image.png

image.png

并且根据对该指针的监视我们可以知道,在程序走到0x401488时,[0x403188]的内容是一开始在serial输入的"1234"的hex格式0x000004D2,对上图代码下断走了一遍发现

00401361   .  8D3D 8C314000 lea     edi, dword ptr [0x40318C]

[0x40318C]存的就是输入的name的四个字节,并且发现这部分的代码会被循环4次,每次[0x40318C]的内容会后移4字节.

也就是说这里可能就是核心算法,计算了name和serial得出结果存放到[0x403188],若结果是0xF6EEDB88,验证就会成功!

接下来分析下这里算法的具体步骤:

如上文所述:这部分的代码会被循环4次,每次[0x40318C]的内容会后移1字节

经过多次的单步运行,查看指针指向的内容后可知,总共会把name分成16部分,因此需循环16次,每部分4字节,name最大长度是16,超过的字符应该是不影响

如果name不足16字节,则不足的部分由0x00来代替.

接下来直接从jmp跳下面开始分析:

00401361   .  8D3D 8C314000 lea     edi, dword ptr [0x40318C]          ; 此处是把name的指针赋值给edi

00401367   .  0FBE05 683140>movsx   eax, byte ptr [0x403168]         ; 将[0x403168]内容赋值给eax,这里的内容是计次的变量

0040136E   .  03F8          add     edi, eax                                                ; 根据计次变量移动edi指针,实现每次循环四个字节向右移一位

00401370   .  FE05 68314000 inc     byte ptr [0x403168]                      ; 计次变量+=1

00401376   .  A1 88314000   mov     eax, dword ptr [0x403188]          ; 将[0x403188]的内容赋值给eax,这里初始值是输入的serial的int格式,例如输入"1234"则是0x000004D2 (HEX:D2 04 00 00)

0040137B   .  8B25 A0314000 mov     esp, dword ptr [0x4031A0]        ; esp指向变化,这里应该是和他循环有关但是和算法内容无关,所以可以忽略

00401381   .  40            inc     eax                                                          ; eax 中的内容自增1

00401382   .  FF05 88314000 inc     dword ptr [0x403188]                   ; [0x403188]的内容自增1,此时eax和[0x403188]内容相等

00401388   .  3307          xor     eax, dword ptr [edi]                             ; 四个字节转成int后与eax异或,结果保存在eax

0040138A   .  A3 88314000   mov     dword ptr [0x403188], eax          ; 把eax的结果赋值给[0x403188]

0040138F   .  803D 68314000>cmp     byte ptr [0x403168], 0x10        ; 判断计次变量是否循环到16,若是则跳出循环

00401396   .  75 07         jnz     short 0040139F                                    ; 跳转,跳出循环

00401398   .  8005 66314000>add     byte ptr [0x403166], 0x4           ;  3


经过以上循环后,如果[0x403188]的内容是0xF6EEDB88则验证成功,因此我们只要写出逆向的循环解密即可!

易语言代码如下:


serial_int = 4142848904' 此处是0xF6EEDB88

.计次循环首 (16, i)

    name_i = 到字节集 (取文本中间 (name, 17 - i, 4)) '这里是取出4字节

    name_i = name_i + 取空白字节集 (4 - 取字节集长度 (name_i))'这里是name不足则补齐0x00

    name_site = 取字节集数据 (到字节集 (name_i), #长整数型, )'将四字节转成长整数,以便下面异或运算

    serial_int = xor64 (serial_int, name_site)'这里用的异或非易语言原生异或,原生异或不支持4字节长整数

    serial_int = serial_int - 1

.计次循环尾 ()

调试输出 (serial_int)

image.png

成功计算出正确Serial

除特别注明外,本站所有文章均为Moeray 的博客 | MoerayBlog原创,转载请注明出处来自https://www.r966.com/post/14.html

赞 (0
Ray's Qq:1422401186.

发表评论

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。