前言:
刚入逆向,想着做一些Crackme练练手,也记录一下做的时候自己是怎么想的。
1. 观察程序
首先运行程序,观察程序的布局。三个按钮,其中Ok是不可点击的。Nome和Codice应该就是用户名和注册码。
点About Help看看,大概意思就是这个Crackme破解的方法是让OK和Cancella按钮消失以看到一个logo。后续的弹窗意思就是有问题邮件他。
OK不管这个弹窗,弹窗应该只是起到提示的功能。对于破解没什么用。
点击Cancella按钮,发现这个按钮是让注册码的值改为0的。
2. 查壳
使用die分析程序。32位系统,Delphi写的,无壳。
3. 分析
将程序拖入IDR中,IDR是一款用于分析Delphi程序中,控件以及对应事件的软件。
可以看到两个按钮都有一个点击事件。因为OK处于不可点击状态。所以先分析Cancella看看。
找到Cancella的点击事件的地址并记下来。push ebp:把栈底指针入栈,一般代表了函数开始。
在把程序拖入x86dbg进行分析。在push ebp处下断点。单步运行看值。不知道为啥我使用SwissArmyKnife不能导入Map文件。只好对着call的地址进入IDA中查找。然后在dbg中添加标签,便于后续分析。
00442AFC | 8955 F8 | mov dword ptr ss:[ebp-8],edx | edx = 前面经过strtoint得到的数
00442AFF | 8945 FC | mov dword ptr ss:[ebp-4],eax | [ebp-4] = ID
00442B02 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] | [ebp-4]:"abcde"
00442B05 | E8 DE10FCFF | call <along3x.1.字符串引用计数(不管)> |
00442B0A | 33C0 | xor eax,eax |
00442B0C | 55 | push ebp |
00442B0D | 68 902B4400 | push along3x.1.442B90 |
00442B12 | 64:FF30 | push dword ptr fs:[eax] |
00442B15 | 64:8920 | mov dword ptr fs:[eax],esp |
00442B18 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] | [ebp-4]:&"斢@"
00442B1B | E8 140FFCFF | call <along3x.1.strLength> | 返回eax = 5
00442B20 | 83F8 05 | cmp eax,5 | 小于5就jmp走了
00442B23 | 7E 53 | jle along3x.1.442B78 |
00442B25 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] | [ebp-4] = ID
00442B28 | 0FB640 04 | movzx eax,byte ptr ds:[eax+4] | eax+4 = "ef" | eax = ‘e’倒数第二位
00442B2C | B9 07000000 | mov ecx,7 |
00442B31 | 33D2 | xor edx,edx |
00442B33 | F7F1 | div ecx | 除以7
00442B35 | 8BC2 | mov eax,edx | 取余数
00442B37 | 83C0 02 | add eax,2 | 余数+2
00442B3A | E8 E1FEFFFF | call <along3x.1.sub_442A20> | 还没进去看
00442B3F | 8BF0 | mov esi,eax | eax = 'x' = 78
00442B41 | 33DB | xor ebx,ebx |
00442B43 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] | [ebp-4] = "abcdef"
00442B46 | E8 E90EFCFF | call <along3x.1.strLength> |
00442B4B | 85C0 | test eax,eax |
00442B4D | 7E 16 | jle along3x.1.442B65 |
00442B4F | BA 01000000 | mov edx,1 |
00442B54 | 8B4D FC | mov ecx,dword ptr ss:[ebp-4] | [ebp-4]:&"斢@"
00442B57 | 0FB64C11 FF | movzx ecx,byte ptr ds:[ecx+edx-1] | 取ID第一位 | 第二位
00442B5C | 0FAFCE | imul ecx,esi | 第一位乘以'x'(0x78) = 0x2D78
00442B5F | 03D9 | add ebx,ecx | ebx = 0 |乘的值累加起来
00442B61 | 42 | inc edx |
00442B62 | 48 | dec eax |
00442B63 | 75 EF | jne along3x.1.442B54 |
00442B65 | 2B5D F8 | sub ebx,dword ptr ss:[ebp-8] | ebp-8是前面strtoint出来的数
00442B68 | 81FB 697A0000 | cmp ebx,7A69 | 这里关键,不能跳转到eax清零的地方 这里让ebx = 7A69才能跳出去
ID大于5,取倒数第二位的asicc,除以7的余数加2,取出来为m(不是一个常数,是操作出来的数),计算前面m的阶乘为n,把每个数乘以n再累加。最后减去函数前算出来的strtoint(密码直接变成数字)得到的是7A69。才解开右边的按钮。
注册机如下:例如:abcdef:40303
str = str(x)
ascii = ord(str[-2])
tmp = ascii % 7 + 2
res = 0
sum = 1
for i in range(2, tmp + 1):
sum = sum * i
for i in range(len(str)):
res += ord(str[i]) * sum
num = res - int("7A69", base=16)
print(num)
之后会发现,OK按钮可以点了。然后同样的步骤,找到OK的地址,下断点,接着分析。
00442C01 | 8D4430 FF | lea eax,dword ptr ds:[eax+esi-1] |
00442C05 | 50 | push eax |
00442C06 | 8B45 F8 | mov eax,dword ptr ss:[ebp-8] | eax = 密码
00442C09 | 0FB64430 FF | movzx eax,byte ptr ds:[eax+esi-1] | 取密码从最后一位开始往前取
00442C0E | F7E8 | imul eax | 自乘
00442C10 | 0FBFC0 | movsx eax,ax |
00442C13 | F7EE | imul esi | 乘以第几位
00442C15 | B9 19000000 | mov ecx,19 |
00442C1A | 99 | cdq |
00442C1B | F7F9 | idiv ecx | 除以0x19
00442C1D | 83C2 41 | add edx,41 | edx = 15+41|edx放的是余数
00442C20 | 58 | pop eax |
00442C21 | 8810 | mov byte ptr ds:[eax],dl | 把edx后两位移动到了eax上去
00442C23 | 4E | dec esi |
00442C24 | 85F6 | test esi,esi |
00442C26 | 75 D1 | jne along3x.1.442BF9 |
00442C28 | 8B45 F4 | mov eax,dword ptr ss:[ebp-C] | [ebp-C] = 密码
00442C2B | 8B55 FC | mov edx,dword ptr ss:[ebp-4] | [ebp-4] = 用户名
逻辑是,判断密码大于5,然后从最后一位往前取。ASCII码值自乘,再乘以位数,除以0x19,余数加41获得其中一位
注册机如下:例如:123456:BADQUV
str = str(x)
sum = 0
res = []
for i in range(len(str) - 1, -1, -1):
tmp = ord(str[i])
sum = (tmp * tmp * (i + 1)) % int("19", base=16) + int("41", base=16)
letter = chr(sum)
res.append(letter)
result = "".join(res[::-1])
print(result)
4.验证