工具:1. 按钮事件地址转换器E2A
2. PEID
3. Ollydbg
同样我们先来运行一下这个程序,
ok按钮是被禁用的,有一个help按钮点击后弹出一个消息框:消息框显示提示信息为。本程序需要输入正确的姓名的序列号,把OK按钮和Cancella按钮隐藏显示出其后面的RingZero的LOGO
我们随便输入姓名和序列号
发现OK还是禁用的,那我们就点击Canella试试,发现没用并且序列号被清零
我们熟悉完程序后,正式开始分析。
我们还是先用PEID看看其文件信息,
发现其是用Delphi编写的32位程序,我们打开OD载入程序。
运行程序随便输入用户名和序列号
随后我们需要分析其对应的各个按钮事件,用E2A打开程序文件查看各个按钮事件对应的地址,因为OK按键被禁用所以我们先分析Cancela按钮消息,用E2A程序打开查看Cancella按钮事件的地址
然后在OD中搜索此地址并下段,然后单击CancellaClick按钮程序会停在此地址处
然后单步向下并且注意跳转指令(因为我们输入的是错误的注册码,所以程序走的肯定是不正确的分支,所以我们就和程序反着来,显式改变eip,逆其道而为之),先看看这样后能不能满足条件(如果能满足在回头来分析如何让其实现跳转)。
F8向下分析遇见一个跳转指令,按此程序其是要跳转(那么我们就逆其道而为之),显式改变eip为00442EF0,然后执行程序
发现CancellaClick按钮没了,而且OK被启用了(那么正好我们再来分析一下OK按钮),用E2A查找OK按钮的地址
在OD中搜索地址并下断点后点击OK按钮,点击后程序将停在此地址处
F8单步运行,直到遇见判断跳转指令其程序要跳转(我们就逆其道而为之),显式改变eip为00442dca后,运行程序运行程序后,程序的OK按钮也去除了(哈哈!完成任务)
下面我们就看如何能让程序实现这两个跳转,
下面我们我们先来分析一下第一处跳转(即在按CancellaClick按钮处的跳转)
重新加载程序然后运行并输入用户名和序列号,
然后点击Cancella按钮程序停在其按钮事件处理处,我们向下分析看见测试指令前面的关键函数,我们需要分析这个关键函数,F8向下运行并F7进入关键函数
分析关键函数
00442AF4 /$ 55 push ebp
00442AF5 |. 8BEC mov ebp,esp
00442AF7 |. 83C4 F8 add esp,-0x8
00442AFA |. 53 push ebx
00442AFB |. 56 push esi
00442AFC |. 8955 F8 mov [local.2],edx
00442AFF |. 8945 FC mov [local.1],eax
00442B02 |. 8B45 FC mov eax,[local.1]
00442B05 |. E8 DE10FCFF call aLoNg3x_.00403BE8
00442B0A |. 33C0 xor eax,eax
00442B0C |. 55 push ebp
00442B0D |. 68 902B4400 push aLoNg3x_.00442B90
00442B12 |. 64:FF30 push dword ptr fs:[eax]
00442B15 |. 64:8920 mov dword ptr fs:[eax],esp
00442B18 |. 8B45 FC mov eax,[local.1]
00442B1B |. E8 140FFCFF call aLoNg3x_.00403A34 ; 求用户名长度
00442B20 |. 83F8 05 cmp eax,0x5 ; 用户名长度不能小于5
00442B23 |. 7E 53 jle XaLoNg3x_.00442B78
00442B25 |. 8B45 FC mov eax,[local.1]
00442B28 |. 0FB640 04 movzx eax,byte ptr ds:[eax+0x4] ; eax = 用户名的第五个字符
00442B2C |. B9 07000000 mov ecx,0x7
00442B31 |. 33D2 xor edx,edx
00442B33 |. F7F1 div ecx
00442B35 |. 8BC2 mov eax,edx
00442B37 |. 83C0 02 add eax,0x2 ; eax = eax % 7 +2
00442B3A |. E8 E1FEFFFF call aLoNg3x_.00442A20 ; 计算eax的阶乘
00442B3F |. 8BF0 mov esi,eax
00442B41 |. 33DB xor ebx,ebx
00442B43 |. 8B45 FC mov eax,[local.1]
00442B46 |. E8 E90EFCFF call aLoNg3x_.00403A34 ; 计算用户名的长度
00442B4B |. 85C0 test eax,eax
00442B4D |. 7E 16 jle XaLoNg3x_.00442B65
00442B4F |. BA 01000000 mov edx,0x1
00442B54 |> 8B4D FC /mov ecx,[local.1] ; 此循环用用户名的每一个字符乘以esi(esi为eax的阶乘),结果存在ebx中
00442B57 |. 0FB64C11 FF |movzx ecx,byte ptr ds:[ecx+edx-0x1]
00442B5C |. 0FAFCE |imul ecx,esi
00442B5F |. 03D9 |add ebx,ecx
00442B61 |. 42 |inc edx
00442B62 |. 48 |dec eax
00442B63 |.^ 75 EF jnz XaLoNg3x_.00442B54
00442B65 |> 2B5D F8 sub ebx,[local.2] ; 用 ebx - 局部变量2
00442B68 |. 81FB 697A0000 cmp ebx,0x7A69
00442B6E |. 75 04 jnz XaLoNg3x_.00442B74 ; if(ebx == 0x7a69) ebx=1; else ebx=0
00442B70 |. B3 01 mov bl,0x1
00442B72 |. EB 06 jmp XaLoNg3x_.00442B7A
00442B74 |> 33DB xor ebx,ebx
00442B76 |. EB 02 jmp XaLoNg3x_.00442B7A
00442B78 |> 33DB xor ebx,ebx
00442B7A |> 33C0 xor eax,eax
00442B7C |. 5A pop edx
00442B7D |. 59 pop ecx
00442B7E |. 59 pop ecx
00442B7F |. 64:8910 mov dword ptr fs:[eax],edx
00442B82 |. 68 972B4400 push aLoNg3x_.00442B97
00442B87 |> 8D45 FC lea eax,[local.1]
00442B8A |. E8 290CFCFF call aLoNg3x_.004037B8
00442B8F . C3 retn
00442B90 .^ E9 E306FCFF jmp aLoNg3x_.00403278
00442B95 .^ EB F0 jmp XaLoNg3x_.00442B87
00442B97 . 8BC3 mov eax,ebx ; ebx =eax,因为要想不跳转 al不为0,即ebx不能为0
00442B99 . 5E pop esi
00442B9A . 5B pop ebx
00442B9B . 59 pop ecx
00442B9C . 59 pop ecx
00442B9D . 5D pop ebp
00442B9E . C3 retn
if((ebx-局部变量2 )== 0x7a69) 则会使al=1从而达到目的不跳转。
而ebx是由 用户名的每一位分别乘以 ((用户名第五个字符%7+2)的阶乘)的累加
而我们需要分析局部变量2怎么来的,
由mov [local.2],edx
可得其是由edx得到的,再往关键函数上分析看edx是怎么得到的
00442ECC |. 8B45 FC mov eax,[local.1] ; eax = 填写的注册码
00442ECF |. E8 9C47FCFF call aLoNg3x_.00407670 ; 将注册码变为10进制形式
00442ED4 |. 50 push eax ; 返回值入栈
00442ED5 |. 8D55 FC lea edx,[local.1]
00442ED8 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC]
00442EDE |. E8 DD03FEFF call aLoNg3x_.004232C0
00442EE3 |. 8B45 FC mov eax,[local.1]
00442EE6 |. 5A pop edx ; 栈中值出栈到edx
00442EE7 |. E8 08FCFFFF call aLoNg3x_.00442AF4
00442EEC |. 84C0 test al,al
所以要想去掉cancella按钮,并启用OK按钮就用户名和注册码应该满足条件为
用户名的每一位分别乘以 ((用户名第五个字符%7+2)的阶乘)的累加 减去 注册码 等于0x7a69
如果用户名为987654,则注册码为204103我们重新填写并点击Cancella按钮后程序如我们所愿,
然后我们分析OK按钮事件处理的第二处判断跳转
用E2A查看OK按钮事件地址
在OD中搜索并下断点后,单击OK按钮程序会停在此地址处
F8向下运行并进入测试转移指令前的关键函数,并分析此关键函数的代码
00442BA0 /$ 55 push ebp
00442BA1 |. 8BEC mov ebp,esp
00442BA3 |. 6A 00 push 0x0
00442BA5 |. 6A 00 push 0x0
00442BA7 |. 6A 00 push 0x0
00442BA9 |. 53 push ebx
00442BAA |. 56 push esi
00442BAB |. 8BF2 mov esi,edx
00442BAD |. 8945 FC mov [local.1],eax
00442BB0 |. 8B45 FC mov eax,[local.1]
00442BB3 |. E8 3010FCFF call aLoNg3x_.00403BE8
00442BB8 |. 33C0 xor eax,eax
00442BBA |. 55 push ebp
00442BBB |. 68 672C4400 push aLoNg3x_.00442C67
00442BC0 |. 64:FF30 push dword ptr fs:[eax]
00442BC3 |. 64:8920 mov dword ptr fs:[eax],esp
00442BC6 |. 33DB xor ebx,ebx
00442BC8 |. 8D55 F8 lea edx,[local.2]
00442BCB |. 8BC6 mov eax,esi
00442BCD |. E8 6E4AFCFF call aLoNg3x_.00407640
00442BD2 |. 8D45 F4 lea eax,[local.3]
00442BD5 |. 8B55 F8 mov edx,[local.2]
00442BD8 |. E8 730CFCFF call aLoNg3x_.00403850
00442BDD |. 8B45 F8 mov eax,[local.2]
00442BE0 |. E8 4F0EFCFF call aLoNg3x_.00403A34 ; 求长度
00442BE5 |. 83F8 05 cmp eax,0x5
00442BE8 |. 7E 60 jle XaLoNg3x_.00442C4A
00442BEA |. 8B45 F8 mov eax,[local.2]
00442BED |. E8 420EFCFF call aLoNg3x_.00403A34 ; 求注册码长度
00442BF2 |. 8BF0 mov esi,eax
00442BF4 |. 83FE 01 cmp esi,0x1
00442BF7 |. 7C 2F jl XaLoNg3x_.00442C28
00442BF9 |> 8D45 F4 /lea eax,[local.3] ; 此循环将 (注册码的每一个字符乘以本身后)* 注册码长度 % 19 + 0x41
00442BFC |. E8 0310FCFF |call aLoNg3x_.00403C04 ; 得到的字符与原字符替代保存到局部变量3中
00442C01 |. 8D4430 FF |lea eax,dword ptr ds:[eax+esi-0x1]
00442C05 |. 50 |push eax
00442C06 |. 8B45 F8 |mov eax,[local.2]
00442C09 |. 0FB64430 FF |movzx eax,byte ptr ds:[eax+esi-0x1]
00442C0E |. F7E8 |imul eax
00442C10 |. 0FBFC0 |movsx eax,ax
00442C13 |. F7EE |imul esi
00442C15 |. B9 19000000 |mov ecx,0x19
00442C1A |. 99 |cdq
00442C1B |. F7F9 |idiv ecx
00442C1D |. 83C2 41 |add edx,0x41
00442C20 |. 58 |pop eax
00442C21 |. 8810 |mov byte ptr ds:[eax],dl
00442C23 |. 4E |dec esi
00442C24 |. 85F6 |test esi,esi
00442C26 |.^ 75 D1 jnz XaLoNg3x_.00442BF9
00442C28 |> 8B45 F4 mov eax,[local.3]
00442C2B |. 8B55 FC mov edx,[local.1]
00442C2E |. E8 110FFCFF call aLoNg3x_.00403B44 ; 比较eax 和 edx所指的字符串是否相等
00442C33 |. 75 17 jnz XaLoNg3x_.00442C4C ; if(eax == ecx) ebx = 1; else ebx = 0
00442C35 |. 8B45 FC mov eax,[local.1]
00442C38 |. 8B55 F4 mov edx,[local.3]
00442C3B |. E8 040FFCFF call aLoNg3x_.00403B44
00442C40 |. 75 04 jnz XaLoNg3x_.00442C46
00442C42 |. B3 01 mov bl,0x1
00442C44 |. EB 06 jmp XaLoNg3x_.00442C4C
00442C46 |> 33DB xor ebx,ebx
00442C48 |. EB 02 jmp XaLoNg3x_.00442C4C
00442C4A |> 33DB xor ebx,ebx
00442C4C |> 33C0 xor eax,eax
00442C4E |. 5A pop edx
00442C4F |. 59 pop ecx
00442C50 |. 59 pop ecx
00442C51 |. 64:8910 mov dword ptr fs:[eax],edx
00442C54 |. 68 6E2C4400 push aLoNg3x_.00442C6E
00442C59 |> 8D45 F4 lea eax,[local.3]
00442C5C |. BA 03000000 mov edx,0x3
00442C61 |. E8 760BFCFF call aLoNg3x_.004037DC
00442C66 . C3 retn
00442C67 .^ E9 0C06FCFF jmp aLoNg3x_.00403278
00442C6C .^ EB EB jmp XaLoNg3x_.00442C59
00442C6E . 8BC3 mov eax,ebx ; eax = ebx, 要想让al不等于0,则ebx不能为0
00442C70 . 5E pop esi
00442C71 . 5B pop ebx
00442C72 . 8BE5 mov esp,ebp
00442C74 . 5D pop ebp
00442C75 . C3 retn
eax所指的字符串是 (注册码的每一个字符乘以本身后)* 注册码长度 % 19 + 0x41得到的新字符串
ebx所指的字符串为用户名。
if(eax所指的字符串 == ebx所指的字符串)
ebx=1;
else
ebx=0;
当ebx==1时,则eax也为1,不发生跳转从而使OK按钮隐藏
所以用户名等于(注册码的每一个字符乘以本身后)* 注册码长度 % 19 + 0x41得到的字符串
此时注册码为204103,其对应的用户名为 : AIMEUG
重新输入用户名点击OK后,程序如我们所愿显示出RingZero的logo
总结:程序逆向可以先采用 “逆其道而为之”的思想,显示改变eip试探程序是否能如我们所愿,如果如我们所愿就返回分析如何满足条件让其自主执行正确的分支。另外有关delphi程序注意使用E2A按钮事件地址转换器从而在OD中追踪相应的按钮事件