最近在看加密解密,看到了第五章序列号保护方式的crackme,分析了一下。
在输入用户名和序列号的地方随便输入,弹出错误对话框。
用lordPE查看输入表发现user32模块中有GetDlgItemTextA,直接OD加载对其下断,运行回到程序领空:
004012B5 |. 6A 0B |push 0B ; /Count = B (11.)
004012B7 |. 68 8E214000 |push 0040218E ; |Buffer = serial.0040218E
004012BC |. 68 E8030000 |push 3E8 ; |ControlID = 3E8 (1000.)
004012C1 |. FF75 08 |push dword ptr [ebp+8] ; |hWnd
004012C4 |. E8 07020000 |call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA 得到用户名
004012C9 |. 83F8 01 |cmp eax, 1 ; 判断得到用户名是否为空
004012CC |. C745 10 EB030>|mov dword ptr [ebp+10], 3EB
004012D3 |.^ 72 CC \jb short 004012A1
004012D5 |. 6A 0B push 0B ; /Count = B (11.)
004012D7 |. 68 7E214000 push 0040217E ; |Buffer = serial.0040217E
004012DC |. 68 E9030000 push 3E9 ; |ControlID = 3E9 (1001.)
004012E1 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
004012E4 |. E8 E7010000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA 得到序列号
004012E9 |. B8 01000000 mov eax, 1
004012EE |. EB 07 jmp short 004012F7
004012F0 |> B8 00000000 mov eax, 0
004012F5 |.^ EB 8D jmp short 00401284
004012F7 |> 50 push eax ; /Result
004012F8 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
004012FB |. E8 B2010000 call <jmp.&USER32.EndDialog> ; \EndDialog 关闭输入对话框
00401300 |. B8 01000000 mov eax, 1
00401305 \.^ E9 7AFFFFFF jmp 00401284
继续跟来到这里
00401228 . 68 8E214000 push 0040218E ; ASCII “lingdux”
0040122D . E8 4C010000 call 0040137E ; 用户名算法call
00401232 . 50 push eax ; 保存用户名加密后的值
00401233 . 68 7E214000 push 0040217E ; ASCII “123456″
00401238 . E8 9B010000 call 004013D8 ; 序列号的算法call
0040123D . 83C4 04 add esp, 4
00401240 . 58 pop eax ; 从堆栈中取出之前保存的用户名加密后的结果
00401241 . 3BC3 cmp eax, ebx ; 序列号算完保存在ebx中,与用户名比较
00401243 . 74 07 je short 0040124C
nop掉这处je就可以爆破了,然后分析两个算法call
第一个call 0040137E是加密用户名的,跟进去分析。。
0040137E /$ 8B7424 04 mov esi, dword ptr [esp+4]
00401382 |. 56 push esi
00401383 |> 8A06 /mov al, byte ptr [esi]
00401385 |. 84C0 |test al, al ; 检测是不是到了字符串结尾
00401387 |. 74 13 |je short 0040139C
00401389 |. 3C 41 |cmp al, 41 ; 小于字符A跳
0040138B |. 72 1F |jb short 004013AC
0040138D |. 3C 5A |cmp al, 5A ; 大于字符Z跳
0040138F |. 73 03 |jnb short 00401394
00401391 |. 46 |inc esi
00401392 |.^ EB EF |jmp short 00401383
00401394 |> E8 39000000 |call 004013D2 ; 小写字母转换成大写字母call
00401399 |. 46 |inc esi ; serial.0040218E
0040139A |.^ EB E7 \jmp short 00401383 ; 如果字符串中有小写字母转换为大写,如果有数字则直接跳出
0040139C |> 5E pop esi
0040139D |. E8 20000000 call 004013C2 ; 求字母asc码值的和call
004013A2 |. 81F7 78560000 xor edi, 5678 ; 结果和5678异或
004013A8 |. 8BC7 mov eax, edi ; 结果保存在eax中作为返回值
004013AA |. EB 15 jmp short 004013C1
004013AC |> 5E pop esi
004013AD |. 6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013AF |. 68 60214000 push 00402160 ; |Title = “Error! ”
004013B4 |. 68 69214000 push 00402169 ; |Text = “Incorrect!,Try Again”
004013B9 |. FF75 08 push dword ptr [ebp+8] ; |hOwner
004013BC |. E8 79000000 call <jmp.&USER32.MessageBox>; \MessageBoxA
004013C1 \> C3 retn
004013C2 /$ 33FF xor edi, edi
004013C4 |. 33DB xor ebx, ebx
004013C6 |> 8A1E /mov bl, byte ptr [esi]
004013C8 |. 84DB |test bl, bl ; 检测是否到字符串尾
004013CA |. 74 05 |je short 004013D1
004013CC |. 03FB |add edi, ebx ; 循环加和到edi中
004013CE |. 46 |inc esi
004013CF |.^ EB F5 \jmp short 004013C6
004013D1 \> C3 retn
004013D2 /$ 2C 20 sub al, 20 ; 小写字母asc值加0×20就是对应的大写
004013D4 |. 8806 mov byte ptr [esi], al
004013D6 \. C3 retn
用户名中不能有数字,小写字母均转换成大写并所有字母的数值加和与0×5678异或得出的值用来做比较
下面分析序列号加密。。
004013D8 /$ 33C0 xor eax, eax
004013DA |. 33FF xor edi, edi
004013DC |. 33DB xor ebx, ebx
004013DE |. 8B7424 04 mov esi, dword ptr [esp+4]
004013E2 |> B0 0A /mov al, 0A
004013E4 |. 8A1E |mov bl, byte ptr [esi]
004013E6 |. 84DB |test bl, bl ; 判断是否到字符串结尾
004013E8 |. 74 0B |je short 004013F5
004013EA |. 80EB 30 |sub bl, 30 ; 数字的asc减0×30相当于把字符转换成值
004013ED |. 0FAFF8 |imul edi, eax ; edi用于保存结果,每次相加前edi乘以0×0A,相当于进位
004013F0 |. 03FB |add edi, ebx ; 和数值相加
004013F2 |. 46 |inc esi
004013F3 |.^ EB ED \jmp short 004013E2
004013F5 |> 81F7 34120000 xor edi, 1234 ; 执行循环后结果和0×1234异或
004013FB |. 8BDF mov ebx, edi ; 最终结果保存在ebx中
004013FD \. C3 retn
序列号的加密方法就是:把输入的字符串转换成一个数然后和0×1324异或
由于异或是可逆的,非常容易写出算法注册机。
char name[256];
cin>>name;
CharUpper(name);
int s=0;
int i=0;
while(name[i])
{
s+=name[i];
i++;
}
s^=0×5678;
s^=0×1234;
cout<<s<<endl;
上面代码编译执行,输入用户名就输出序列号了~!~
前段时间我也在看诶~
[回复]
零度x 回复:
一月 7th, 2010 at 12:19
呵呵,等有不会的向你请教!
[回复]
一击屠夫 回复:
一月 7th, 2010 at 22:35
- -||我不行的
[回复]
零度x 回复:
一月 7th, 2010 at 23:12
屠夫兄谦虚!呵呵。对了,屠夫兄知道什么时候google更新PR吗?
PR更新这个事。不好说,完全无规律可循。
最近一次就是元旦。
零度兄这个blog pr值不达到预期的原因我想跟外链建设力度还不够有关~
[回复]
零度x 回复:
一月 11th, 2010 at 11:55
受教了!
上次是从2降到1的
[回复]
一击屠夫 回复:
一月 12th, 2010 at 18:56
恩,链条是这样的
内容-流量-链接-更多流量-再绕回内容
我们这些blog都很难做的成功的另一个原因是受众不关注互联网产品,衡量一个博客成功与否的最好方法其实是看订阅数- -可是很多人都不使用订阅- =||
[回复]