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

那么我们就有两种解决方案:

  1. 补丁程序逻辑,不让它进行加密操作(直接加载你修改后的 DLL)
  2. 或将“破解后”的 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进行了简单的加密,需要经过解密才可以正常调用。

posted @ 2025-05-22 16:08  一只本本  阅读(15)  评论(0)    收藏  举报