返回
顶部

references:

如果要密码,就是1

要破解的二进制文件

1651910118691

这回情况和上次那个程序稍有不同,没有任何提示字符串,我们只能在系统API函数调用处下端点

windows GUI程序的常用于下断点的API函数

使用OllyDBG加载程序之后,Ctrl+N获取当前程序引入的所有库函数

1651910570406

其中有一个GetWinTextA,这个函数用于获取指定窗口中的字符串,可以用来下断点,选中这个函数右键下断点即可

1651910612591

可以看到,与该函数相关的指令都会被下断点,这两个断点对我们来说都没啥用,我们直接删除掉即可,然后在00401323的下一个位置下断点,也就是00401328,这个才是CrackHead程序的代码

我们跟入这个call即可

1651910838366

该函数的代码:

004013D2  /$ 56             PUSH ESI
004013D3  |. 33C0           XOR EAX,EAX
004013D5  |. 8D35 C4334000  LEA ESI,DWORD PTR DS:[4033C4]
004013DB  |. 33C9           XOR ECX,ECX
004013DD  |. 33D2           XOR EDX,EDX
004013DF  |. 8A06           MOV AL,BYTE PTR DS:[ESI]
004013E1  |. 46             INC ESI
004013E2  |. 3C 2D          CMP AL,2D
004013E4  |. 75 08          JNZ SHORT CrackHea.004013EE
004013E6  |. BA FFFFFFFF    MOV EDX,-1
004013EB  |. 8A06           MOV AL,BYTE PTR DS:[ESI]
004013ED  |. 46             INC ESI
004013EE  |> EB 0B          JMP SHORT CrackHea.004013FB
004013F0  |> 2C 30          /SUB AL,30
004013F2  |. 8D0C89         |LEA ECX,DWORD PTR DS:[ECX+ECX*4]
004013F5  |. 8D0C48         |LEA ECX,DWORD PTR DS:[EAX+ECX*2]
004013F8  |. 8A06           |MOV AL,BYTE PTR DS:[ESI]
004013FA  |. 46             |INC ESI
004013FB  |> 0AC0            OR AL,AL
004013FD  |.^75 F1          \JNZ SHORT CrackHea.004013F0
004013FF  |. 8D040A         LEA EAX,DWORD PTR DS:[EDX+ECX]
00401402  |. 33C2           XOR EAX,EDX
00401404  |. 5E             POP ESI
00401405  |. 81F6 53757A79  XOR ESI,797A7553
0040140B  \. C3             RETN

第三行中4033C4是序列号字符串的地址,是由之前的函数调用GetWindowTextA获取的

004013D5  |. 8D35 C4334000  LEA ESI,DWORD PTR DS:[4033C4]

现在ESI指向序列号

004013DF  |. 8A06           MOV AL,BYTE PTR DS:[ESI]
004013E1  |. 46             INC ESI
004013E2  |. 3C 2D          CMP AL,2D
004013E4  |. 75 08          JNZ SHORT CrackHea.004013EE

获取第一个序列号,然后ESI往后偏移1,如果第一个字符不是-(ascii为2D),则跳转

004013EE  |> EB 0B          JMP SHORT CrackHea.004013FB

接着跳

004013FB  |> 0AC0            OR AL,AL
004013FD  |.^75 F1          \JNZ SHORT CrackHea.004013F0

AL不为0就跳

004013F0  |> 2C 30          /SUB AL,30
004013F2  |. 8D0C89         |LEA ECX,DWORD PTR DS:[ECX+ECX*4]
004013F5  |. 8D0C48         |LEA ECX,DWORD PTR DS:[EAX+ECX*2]
004013F8  |. 8A06           |MOV AL,BYTE PTR DS:[ESI]
004013FA  |. 46             |INC ESI
004013FB  |> 0AC0            OR AL,AL
004013FD  |.^75 F1          \JNZ SHORT CrackHea.004013F0

先减去0x30,下面两个LEA指令相当于ECX=ECX*10+EAX,不过在这个循环里,ECX并没有什么作用,整个循环就是对序列号进行遍历,直到读完(最后的结束标志符0x00)

004013FF  |. 8D040A         LEA EAX,DWORD PTR DS:[EDX+ECX]
00401402  |. 33C2           XOR EAX,EDX
00401404  |. 5E             POP ESI
00401405  |. 81F6 53757A79  XOR ESI,797A7553
0040140B  \. C3             RETN

循环完成后,执行了四条指令,就返回了,现在我并不清楚这四条指令的作用

1651912694057

函数返回后,对EAX和ESI寄存器进行了比较,如果两者不相等,就会跳走,继续执行程序会发现没有任何反应,这显然不是我们想要的,我们不希望程序跳走,我们想让他执行下面的那条JMP指令,显然这个JMP跳到的地方有更多的东西

因此,我们需要确保在函数执行完成后,EAX和ESI的值是相等的

通过调试器可以看到,ESI的初始值是0,且函数的第一行代码就对ESI的值进行了保存

1651912968818

在函数返回的时候,取出ESI的值和0x797A7553进行异或,那么无论如何,函数返回的时候ESI的值一定会变成0x797A7553,现在我们的问题就是如何控制AX也是这个值

在函数一开始的地方

004013E4  |. 75 08          JNZ SHORT CrackHea.004013EE

这条指令是否执行只会影响EDX的值,如果这条指令没执行,那么EDX的值就会变成0xffffffff

但是,我并没有办法输入-,所以这条指令肯定会执行,也就是说EDX的值肯定是0,那么在最后和EAX进行异或运算的时候,EDX是完全没有影响的,异或完之后,EAX的值是不会变的

现在就是要想办法,怎么把EAX的值弄成0x797A7553

现在的问题就是要循环多少次,每次EAX的值应该是多少,才能在循环结束之后,ECX的值为0x797A7553,因为在EDX为0的情况下,下面这两条指令完成后,其实就是MOV EAX, ECX

004013FF  |. 8D040A         LEA EAX,DWORD PTR DS:[EDX+ECX]
00401402  |. 33C2           XOR EAX,EDX

刚测试了一下,输入框允许的最大长度为21,EAX的值一共存在10种可能(0~9)

也就是说最多存在10^21个组合,这太大了,显然是爆破不出来的

仔细观察一下代码,可以发现下面这个表达式:

ECX=ECX*10+EAX

而由于EAX每次在运算之前会减去0x30,因此EAX的值就是我们在程序中输入的序列号种的字符

0x797A7553的10进制形式为2038068563,而ECX的初始值为0,第一次循环,ECX的值就是EAX的值,第二次循环,ECX的值是之前的EAX的值乘以10加上当前EAX的值,很明显,我们只需要将序列号设置为2038068563即可

1651912968818

弹出的对话框提示我们编写一个序列号生成器,而且程序提示说每台电脑上的序列号都不一样

刚才把这个程序放到了其他机器上,输入这个序列号,结果还是这个对话框

又换了一台机器,还是这个样子,不管了,既然他这么说了,我就看一下

破案了

很明显0x797A7553就是硬编码,唯一可能会影响到ESI值的因素就是在调用检验函数之前的代码

00401310  |. 8B35 9C334000          MOV ESI,DWORD PTR DS:[40339C]
00401316  |. 6A 28                  PUSH 28                                  ; /Count = 28 (40.)
00401318  |. 68 C4334000            PUSH CrackHea.004033C4                   ; |Buffer = CrackHea.004033C4
0040131D  |. FF35 90314000          PUSH DWORD PTR DS:[403190]               ; |hWnd = 00070812 (class='Edit',parent=00090814)
00401323  |. E8 4C010000            CALL <JMP.&USER32.GetWindowTextA>        ; \GetWindowTextA
00401328  |. E8 A5000000            CALL CrackHea.004013D2

就是上面代码中的第一行,这个0x40339C也是硬编码,但是这块内存中的值可能会在不同的机器上有不同的值,这里触及到我的知识盲区了,就到这儿吧

告辞!!