New160CrackMe之riijj_cm
exeinfo查壳,未发现壳,是VC++5.0编写的32位程序
运行程序:没有错误回显,但是提示name长度需要4-15
用x32dbg打开,打开字符串视图:
猜测这是正确回显,双击定位到对应代码段
下断点,开始调试,发现怎么都运行不了,
会暂停于00000000
,这个地址访问不了导致了异常之后停止,猜测这是反调试手段。
这一步可以通过调试器自带的异常忽略功能解决:
选项->选项->异常,设置忽略范围点击保存
这个异常就处理好了
F9让程序运行起来,点击符号,双击进入pf1.dll
,
再次搜索字符串
定位过去,
爆破思路是:直接将这个判断跳转jne
给nop掉
但是会发现前面还有长度检验,彻底的爆破方式是,将最上面的跳转直接改为无条件跳转到打印成功字符。
之后,将原来的pf1.dll
给重名名为pf2.dll
,补丁后的dll修改为:pf1.dll
提示dll包含错误,怎么回事呢?我们明明在dll源程序上修改再dump出来的,为什么是错误的dll
我们用PETool查一下,
发现程序原理的pf1.dll非PE文件,我们dump出来的dll反而是正确的PE文件??这是怎么回事呢?
结合前面的调试分析,猜测主程序中隐含一个解密程序,当程序正确解密之后,才会调用pf1.dll
那么我们就有两种解决方案:
- 补丁程序逻辑,不让它进行加密操作(直接加载你修改后的 DLL)
- 或将“破解后”的 DLL 重新加密,让它能被正确解密。
我们需要先找到这个解密程序块,跟踪字符串pf1.dll
发现这两个在同一个函数中,那么我们继续往下走
猜测这一段就是解密程序块,尝试将这个jbe
跳转改为无条件跳转,跳过这个解密块。
为了方便测试,我们将修改后的主程序和dll都放在另一个文件夹中,
运行结果如下:
可以发现,程序正常调用我们的爆破后的dll,且爆破成功!
说明dll解密点爆破,和逻辑判断爆破都成功了。
算法分析
| ;ebx:Name字符串 ebp:Name长度 edx:Serial长度
1000105D | 83FD 0F | cmp ebp,F | ; Name长度检验 4-15
10001060 | AA | stosb |
10001061 | 0F8D A1000000 | jge pf1.10001108 |
10001067 | 83FD 04 | cmp ebp,4 |
1000106A | 0F8C 98000000 | jl pf1.10001108 |
10001070 | 83FA 1E | cmp edx,1E | ; Name长度检验 4-30
10001073 | 0F8F 8F000000 | jg pf1.10001108 |
10001079 | 83FA 04 | cmp edx,4 |
1000107C | 0F8C 86000000 | jl pf1.10001108 |
10001082 | 33C9 | xor ecx,ecx | ; ecx清空,做计数器
10001084 | 85ED | test ebp,ebp | ; ebp验空
10001086 | 76 26 | jbe pf1.100010AE |
10001088 | 8D7424 11 | lea esi,dword ptr ss:[esp+11] | ; esi做预留地址
1000108C | 0FBE0419 | movsx eax,byte ptr ds:[ecx+ebx]| ; 逐个取"admin"字符给eax ;循环点
10001090 | 99 | cdq |
10001091 | BF 62000000 | mov edi,62 | edi = 62
10001096 | 83C6 02 | add esi,2 | ; esi += 2
10001099 | F7FF | idiv edi | ; eax = edi // eax, edx = edi % eax
1000109B | 41 | inc ecx | ; ecx += 1
1000109C | 3BCD | cmp ecx,ebp | ; 循环条件检验
1000109E | 8A4414 30 | mov al,byte ptr ss:[esp+edx+30]| ; 根据运算结果取固定字符串中的字符
100010A2 | 8A5414 31 | mov dl,byte ptr ss:[esp+edx+31]| ; 一次取两位
100010A6 | 8846 FD | mov byte ptr ds:[esi-3],al | ; 将取到的值放到预定好的位置
100010A9 | 8856 FE | mov byte ptr ds:[esi-2],dl |
100010AC | 72 DE | jb pf1.1000108C | ; 循环跳转
100010AE | 8BB424 9C000000| mov esi,dword ptr ss:[esp+9C] | ; esi:"12345678" Serial
100010B5 | C64424 2E 00 | mov byte ptr ss:[esp+2E],0 | ;
100010BA | 8D4424 10 | lea eax,dword ptr ss:[esp+10] | ; eax = 上一步结果地址
100010BE | 8A10 | mov dl,byte ptr ds:[eax] | ; dl = 逐字符 eax
100010C0 | 8A1E | mov bl,byte ptr ds:[esi] | ; bl = 逐字符Serial:"12345678"
100010C2 | 8ACA | mov cl,dl | ; cl = dl
100010C4 | 3AD3 | cmp dl,bl | ; 逐字符比较
100010C6 | 75 1E | jne pf1.100010E6 | ; 不相等则跳过比较过程
100010C8 | 84C9 | test cl,cl | ; 验空cl
100010CA | 74 16 | je pf1.100010E2 | ;
100010CC | 8A50 01 | mov dl,byte ptr ds:[eax+1] | ; 下面是一样的,逐字符比较
100010CF | 8A5E 01 | mov bl,byte ptr ds:[esi+1] | ;
100010D2 | 8ACA | mov cl,dl |
100010D4 | 3AD3 | cmp dl,bl |
100010D6 | 75 0E | jne pf1.100010E6 |
100010D8 | 83C0 02 | add eax,2 |
100010DB | 83C6 02 | add esi,2 | esi:happytime+40A8
100010DE | 84C9 | test cl,cl |
100010E0 | 75 DC | jne pf1.100010BE |
100010E2 | 33C0 | xor eax,eax |
100010E4 | EB 05 | jmp pf1.100010EB |
100010E6 | 1BC0 | sbb eax,eax |
100010E8 | 83D8 FF | sbb eax,FFFFFFFF |
100010EB | 85C0 | test eax,eax |
100010ED | 75 19 | jne pf1.10001108 |
100010EF | 50 | push eax |
100010F0 | 8B8424 A4000000| mov eax,dword ptr ss:[esp+A4] | [esp+A4]:sub_4011E0+17
100010F7 | 68 4C500010 | push pf1.1000504C | 1000504C:"Crackme"
100010FC | 68 30500010 | push pf1.10005030 | 10005030:"Registration successful !"
10001101 | 50 | push eax |
10001102 | FF15 B0400010 | call dword ptr ds:[<MessageBoxA|
10001108 | 5F | pop edi |
10001109 | 5E | pop esi | esi:happytime+40A8
1000110A | 5D | pop ebp |
1000110B | 5B | pop ebx | ebx:"admin"
1000110C | 81C4 84000000 | add esp,84 |
10001112 | C3 | ret |
欧克,依据此,我们可以得出注册机代码:
username = input("请输入你的用户名: ")
const_str = "fytugjhkuijonlbpvqmcnxbvzdaeqrwtryetdgfkgphonuivmdbxfanqydexzwztqnkcfkvcpvlbmhotyiufdkdnjxuzyqhfstae"
serial = ""
for c in username:
idx = ord(c) % 98
serial += const_str[idx]
serial += const_str[idx+1]
print(f'name: {username}\nserial: {serial}')
运行结果:
总结
算是一种不一样的方式,验证程序写在dll里面。有一定的反调试手段,且对dll进行了简单的加密,需要经过解密才可以正常调用。
本文作者: 一只本本,文章链接: https://www.cnblogs.com/abenben/p/18891482
欢迎志同道合的朋友一起学习,进步