170620 逆向-CrackMe之034

1625-5 王子昂 总结《2017年6月20日》 【连续第261天总结】

A.CrackMe(34)

B.从没见过的KeyFile形式验证,拖到PEiD中查看,幸好是MASM汇编写的

拖到OD里运行,比昨天还恐怖,没有任何输入、选择和确认信息

猜测应该是开始运行的时候检测根目录下是否存在KeyFile,但是失败了也没有弹窗,连查MessageBox的机会都不给

那么有两个办法:

一,因为这是汇编写的程序。而汇编写的程序有一个特点,非常短。上下拖动就可以看出来整个程序的指令极其少,因此可以从一开始就单步跟踪

二,查找参考字符串

第二步是可以的,不过我干脆单步跟好了,反正也不长,从头开始更便于逻辑的理解

跟了几步,发现有一个OD加上了CreateFile的注释的call:

00401028  |.  68 D7204000   push Cruehead.004020D7                   ; |FileName = "CRACKME3.KEY"

0040102D  |.  E8 76040000   call <jmp.&KERNEL32.CreateFileA>         ; \CreateFileA
00401032  |.  83F8 FF       cmp eax,-0x1                             ;  文件判断
00401035  |.  75 0C         jnz short Cruehead.00401043              ;  失败跳

再下面是判断和跳转,当直接运行的时候将会不跳转,然后看到一行字符串被添加了“Uncracked”的字样,再然后就直接跳转显示窗口了

很明显, eax=-1是出错的返回值,也就是说利用了CreateFile时判断文件是否已存在的特性

再往上一行就是FileName,因此很明显,所需的KeyFile的文件名就是这个

在同目录下创建一个CRACKME3.KEY,用记事本向其中随便写入,再次运行

发现能够跳转了,再往下跟:

00401061  |.  E8 30040000   call <jmp.&KERNEL32.ReadFile>            ; \ReadFile
00401066  |.  833D A0214000>cmp dword ptr ds:[0x4021A0],0x12         ;  判断长度,非18失败
0040106D  |.^ 75 C8         jnz short Cruehead.00401037              ;  失败跳
0040106F  |.  68 08204000   push Cruehead.00402008
00401074  |.  E8 98020000   call Cruehead.00401311                   ;  每个字符与0x40+N异或,并累加

是一个ReadFile的API,下面是一个判断和跳转

这种判断不是长度就是内容的整型值,第一次随便写入的是123456,此时0x4021A0的值是6

那八成就是长度了,可以再改变一下KEY的内容,再看看该内存的值是否跟随变化

既然长度要求是0x12即18个字符的话,那么我们就乖乖在KEY里面写18个字符

通过跳转,下面一个call的参数00102008是上一个ReadFile的buffer参数,因此这个call就是对数据处理的了

跟进去,是一个循环:

00401319  |.  B3 41         mov bl,0x41
0040131B  |>  8A06          /mov al,byte ptr ds:[esi]
0040131D  |.  32C3          |xor al,bl                               ;  第n个字符ASCII与(0x40+n)异或
0040131F  |.  8806          |mov byte ptr ds:[esi],al
00401321  |.  46            |inc esi                                 ;  Cruehead.<ModuleEntryPoint>
00401322  |.  FEC3          |inc bl
00401324  |.  0105 F9204000 |add dword ptr ds:[0x4020F9],eax         ;  异或结果累加
0040132A  |.  3C 00         |cmp al,0x0                              ;  异或结果为0时结束处理
0040132C  |.  74 07         |je short Cruehead.00401335
0040132E  |.  FEC1          |inc cl
00401330  |.  80FB 4F       |cmp bl,0x4F                             ;  最多处理16个字符
00401333  |.^ 75 E6         \jnz short Cruehead.0040131B
00401335  |>  890D 49214000 mov dword ptr ds:[0x402149],ecx          ;  处理次数-1
0040133B  \.  C3            retn

很好理解,其中有一个0判断值得注意一下,当异或结果为0时结束处理,作用后面再说

注意累加和和处理次数保存在内存中

出了这个call以后:

00401079  |.  8135 F9204000>xor dword ptr ds:[0x4020F9],0x12345678   ;  累加和与0x12345678异或
00401083  |.  83C4 04       add esp,0x4
00401086  |.  68 08204000   push Cruehead.00402008                   ;  ASCII "night"
0040108B  |.  E8 AC020000   call Cruehead.0040133C                   ;  最后四位ASCII送入EAX
00401090  |.  83C4 04       add esp,0x4
00401093  |.  3B05 F9204000 cmp eax,dword ptr ds:[0x4020F9]          ;  最后四位的ASCII与之前的异或结果比较,不等则失败
00401099  |.  0f94c0        sete al
0040109C  |.  50            push eax
0040109D  |.  84C0          test al,al
0040109F  |.^ 74 96         je short Cruehead.00401037               ;  失败跳
004010A1  |.  68 0E214000   push Cruehead.0040210E                   ;  ASCII "CrackMe v3.0             "
004010A6  |.  E8 9B020000   call Cruehead.00401346                   ;  成功提示

累加和与0x12345678异或,然后与KEY的最后4位字符的ASCII进行比较,相等则通过

通过的call是对字符串添加“Cracked"的字样

这里是通过sete al和test来判断的,当相等时令al=1,否则令al=0

00401188  |.  3C 01         cmp al,0x1                               ;  重复验证
0040118A  |.  75 17         jnz short Cruehead.004011A3
0040118C  |.  68 86214000   push Cruehead.00402186                   ;  ASCII "Now try the next crackme!"
00401191  |.  68 6A214000   push Cruehead.0040216A                   ;  ASCII "Cracked by:                 Now try the next crackme!"
00401196  |.  68 08204000   push Cruehead.00402008                   ;  ASCII "night"
0040119B  |.  E8 C2010000   call Cruehead.00401362                   ;  成功弹窗

在后面又对al进行了一次验证,成功的话会弹窗

值得一提的是,有一个Cracked by:  !的内容,中间的地方填的是之前字符串异或后的结果

还记得之前那个异或后结果为0则判断结束的地方吗,那里就是用来设置字符串结束的

总结一下:

对输入的字符串逐个字符异或,遇到0则停止。将异或后的字符串累加求和,与字符串最后四个字符校验,相等则成功。

那么脚本就很简单了,输入一个名字,先求和,然后如果不满14个字符就以0填充,再对名字与0x40+n逐个异或,最后4个字符为之前求和的结果与0x12345678异或,每2位转换一个字符而成。

这里就有一个问题了:如果累加和与0x12345678异或后出的结果,超出了ASCII表怎么办。ASCII表只有128个,到0x7F就结束了。在转换字符的时候就会因为没有对应的字符而出问题。

事实上我自己的ID转换出来就无法输出,因此没办法看到Cracked by:我 的弹窗TAT

异或的运算对大小没什么规律,跟名字长短也没啥关系,因此也没办法提前判断

脚本:

n="0"*17
name=""
while(len(n)>14):
    n=input("请输入你的名字(小于15个字符的英文)")
    if(len(n)>14):
        print("你输入的名字超出长度啦,请重试")

bx=0x41
s=0
for i in range(len(n)):
    s=s+ord(n[i])
    name=name+chr(ord(n[i]) ^ bx)
    bx=bx+1
if(bx<0x4e):
    name=name+chr(0^bx)*(0x4e-bx+1)
s=s^0x12345678
for i in range(4):
    #print(hex(s))
    if(s%0x100>126):
        print("很抱歉由于CM的BUG,无法输出该Name对应的Serial")
        break
    name=name+chr(s%0x100)
    s=s//0x100
else:
    f=open("CRACKME3.KEY","w")
    f.write(name)
    f.close
    print("生成KeyFile成功:\n"+name)

总体来说,虽然KeyFile的形式是第一次见,但是由于汇编比较友好,再加上OD能即使识别API的名称,再加上整个算法不怎么复杂,所以还是挺简单的(吧

看了一眼52破解的前辈留下的集合,好像没做出来,有点成就感www

C.明日计划

CrackMe(27)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值