003-Cruehead-CrackMeV3Keyfile类型:key:软件应用程序注册后创建的通用许可证文件,存储了一个以纯文本格式保存的加密密钥或注册信息,以证明购买了该程序的合法副本。首先打开软件查看程序的做法,发现是一个程序,但是需要使用许可证才能进行使用将程序放入OllyDbg可以看到在程序入口点就有一个创建文件的操作,所以我们要根据程序的需求将文件创建好,在这个key文件中随便输入内容使用F8进行单步步过判断文件是否存在(判断eax的值是否为-1,若是则代表这个文件不存在,若不是则代表这个文件存在)判断存在后就去读取文件中的内容读取了文件中的前18个字符将获取到的数据放入00401311地址的函数00401311 /$ 33C9 xor ecx,ecx
00401313 |. 33C0 xor eax,eax
00401315 |. 8B7424 04 mov esi,dword ptr ss:[esp+0x4] ; Cruehead.00402008
00401319 |. B3 41 mov bl,0x41
0040131B |> 8A06 /mov al,byte ptr ds:[esi]
0040131D |. 32C3 |xor al,bl
0040131F |. 8806 |mov byte ptr ds:[esi],al
00401321 |. 46 |inc esi ; Cruehead.
00401322 |. FEC3 |inc bl
00401324 |. 0105 F9204000 |add dword ptr ds:[0x4020F9],eax
0040132A |. 3C 00 |cmp al,0x0
0040132C |. 74 07 |je short Cruehead.00401335
0040132E |. FEC1 |inc cl
00401330 |. 80FB 4F |cmp bl,0x4F
00401333 |.^ 75 E6 \jnz short Cruehead.0040131B
00401335 |> 890D 49214000 mov dword ptr ds:[0x402149],ecx
0040133B \. C3 retn
​读取了传入进来的值将bl进行赋值为0x41将值的第一位进行获取,存入到al中此时bl(ebx)和al(eax)的值将两个值进行异或运算(A与a进行异或)异或后的结果再存入al中,并保存在00402008这个地址中在数据窗口中可以查看到esi和bl都加一将0x4020F位址的值加上eax的值,这时的值为20,也就是上面异或后的值异或al与0x00,为真则会跳转到00401335,为假则继续向下执行cl(ecx)加上一(用来计数的)bl(ebx)与0x4F进行比较,若是为假则跳回最开始继续进行异或的运算,若是为真则retn,函数调用结束(所以需要)异或14为的字符到了第15位则会跳出这个函数总结:回到之前的位址继续进行分析将异或完的值再与0x12345678进行异或异或完了的值与读取的后四位进行比较,相等就成功,不相等就失败输入的前十四个为注册机的用户名,后四位为密钥根据分析的算法写脚本username = input("请输入用户名(不超过14位):").center(14)
​
if(len(username) > 14):
print("请输入小于14位的用户名!")
exit(0)
​
tmp_sum = 0
crypt_result = []
#根据逆向分析的加密算法计算用户名对应的密钥,并且对用户名做一个简单的异或,使程序可以解析出正确的用户名字符串
for i in range(14):
xor_key = ord('A') + i
user_chr = ord(username)
xor_user_char = xor_key ^ user_chr
crypt_result.append(xor_user_char)
​
#计算用户名ascii码的累加
tmp_sum = tmp_sum + user_chr
#对用户名ascii码累加结果进行加密,0x12345678密钥来自对程序算法分析
key = tmp_sum ^ 0x12345678
key_bytes = bytes(key.to_bytes(4,byteorder='little'))
with open('CRACKME3.KEY',"wb+") as f:
#分别以2进制方式写入加密后的用户名字符串和用户名字串密钥
f.write(bytes(crypt_result))
f.write(bytes(key_bytes))
print("密钥文件已生成!请拷贝到软件所在目录下使用!")
004-AcidBytes2首先查看程序的位数发现是32位并且是有upx壳的,可以直接使用upx.exe来进行脱壳,也可以使用od里做好的脱壳步骤来进行脱壳(堆栈平衡)运行后发现是需要输入序列号的程序将脱壳后的程序放入od中查看字符串后就可以看到一些敏感的字符串直接跟进找到这个函数的开头,先对开头下一个断点这个函数也不长,先看汇编语言进行静态分析可以看到jnz short CrackMe2.00442B79这条指令的上面的函数执行完成后,若是判断为真则不进行跳转就会输出success,若是判读为假则进行跳转就会输出bad爆破所以若是将这个指令nop,或者是改为je,则判断后的操作是相反的,那输入错误的序列号也会输出success修改为je:直接nop:
算法将我们刚刚输入的序列号读取此时eax的值为1112223,也就是刚才输入进去的值进入到现在call的函数进行分析此时寄存器内的值eax为输入的值,edx为程序定义的值然后将eax和edx的值复制到esi和edi中接着将两个值进行比较若是真则跳转,为假则不跳转,跳转则认为成功所以这个函数就是用来比较输入的值和程序定义的值是否相同那我们已经知道到了程序定义的值,那就直接注入正确的值就可以成功可以直接成功005-Andrnalin1需要下载mvsbvm50.dll,放在C:\Windows\System32文件夹下然后可以打开程序,放入od在od中分析后可以直接看到key的值然后可以直接进行爆破006-ArturDents_CM#2使用中文的智能搜索可以看到程序规定name的长度不能小于5然后还可以看到Yeah, you did it这样的字样跟进爆破可以看到在进入这个函数时会先有一个跳转若是这个比较为相同,则不会跳到指定位址,若是不相同,则会进行跳转所以直接将上面的跳转nop将上面的计算完成后就可以直接弹出成功的窗口算法这一段的指令是用来检查输入的name长度是否大于或等于5,并且将输入的数据进行读取会将name和serial的值分别放入eax和ebx中此时寄存器中的状态这一段是将输入的数据进行循环计算首先是将输入的name的字符串的第一个字符放入了dl中并且获取了name字符串的长度,并且存放在了cl(ecx)此时寄存器的状态接着dl减去cl的值此时cl和dl的值此时dl的值为6D(m)将此时的dl与ebx的第一个字符进行比较所以是将m与1进行比较若是这个比较为相同,则不会跳到指定位址,若是不相同,则会进行跳转,跳转完后就会直接结束进程接着将eax和ebx分别加1接着进行下一位的计算一直循环到结束根据分析的思路写脚本username = input("请输入用户名(不能少于5):")
num = []
for i in range(len(username)):
num.append(chr(ord(username) - (len(username)-i)))
result = ''.join(num)
print(num)007-Reg先查看是几位的32位;无壳先将程序使用od打开然后观察或猜测程序是个共享文件,会出现key使用od中的中文搜索查看程序的关键字在这里可以看到打开软件是出现的,在这里可以分析到应该是会有一个读取reg.dll文件中数据的操作,然后若是这个程序注册成功了会有有效期的范围进行跟进这里可以看到是赋值了一个地址在edx中这里又将一个地址放在了eax中,去看看数据窗口中这两个地址的数据是什么第一个显示的是"未注册",第二个显示的是"您的有限期至"爆破那就可以证明上面的call Reg.004088D4指令是来判断是否有这个reg.dll文件所以下面的跳转指令对最终成功的操作没有影响接着就是开始读取reg.dll文件里的内容,也就是username和sn(序列号)在下一个跳转操作前会先将上面获取的数据放入这个call Reg.0045D0F4里面进行计算下面这个跳转要是跳了的话就跳到了快要结束的地方了,所以这个是个关键跳,所以需要将这个跳转指令进行修改直接运行,这个程序就已经被破解了已经显示出来了有效期的时间还有另外的一种爆破的方式,是将上面的CALL中的内容进行修改进入到这个CALL的函数中将上面两个指令修改为mov eax,1retn因为下面的对比指令只要返回为真就可以执行跳转指令,所以直接在这个CALL中将返回值改为1,则下面再进行对比是返回的一定是真,那这个跳转就会实现修改完成后需要将修改后的程序进行保存这样就可以将修改完成后的程序进行保存处理直接运行保存后的新程序,这个程序就是直接被破解的注意在修改call中指令时,需要先查看call中原本“retn”指令的操作,需要在修改时修改为一样的指令,不然返回的地址会出现错误算法接着去分析上面找到的算法CALL中的操作先将一些地址的值进行清空处理接着将name的值放人00404810这个CALL中进行运算获取sn的值将sn值放入这个call中进行操作返回的值这个值是sn值的长度然后将获取到的长度与16进行异或运算若是返回为0则会进行下面的跳转而下面的跳转指令会直接跳到这个call的末尾,并且会将返回为0的值带出去,则这个call下面的比较指令则会判断为0,返回为0,那到正确的跳转命令就实现不了所以这个操作应该是要判断输入的sn值长度是否为16位条件一:sn值的长度为16下面的指令是将输入进去的sn值的每一个字符进行计算,应该是核验sn值是否有特殊的字符接着将会获取到一个时间段将上面的三个数据放入了call Reg.0045C5E0这个CALL中从call中出来后会出现一个新的数据,并且这个数据很巧,也是16位的数据那这个程序可以注册的第一个条件就是sn的长度为16,那就先猜测这个数据会不会就是真正的key值接着就是一大段的算法操作计算后会获取到一些32位的数据可以先去尝试上面的猜想是正确的需要注意的是name的值要和之前分析时用相同的