对[FCG]KeyGen2.0按钮特效的分析2
发表于:
2006-10-26 16:35
5808
下面开始分析sub_406A1F,看代码:
seg000:00406A1F sub_406A1F proc near ; CODE XREF: sub_406984+49
seg000:00406A1F ; fn+18...
seg000:00406A1F
seg000:00406A1F var_4 = dword ptr -4
seg000:00406A1F arg_0 = dword ptr 8
seg000:00406A1F
seg000:00406A1F push ebp
seg000:00406A20 mov ebp, esp
seg000:00406A22 push ecx
seg000:00406A23 push ebx
seg000:00406A24 push esi
seg000:00406A25 push edi
//首先根据前面的分析,得到函数的类型。由此判断以上代码为保存寄存器
seg000:00406A26 mov esi, [ebp+arg_0]
seg000:00406A29 push esi
seg000:00406A2A call sub_406B69
//sub_406B69需要传进来的窗口句柄,稍后分析
seg000:00406A2F test eax, eax
//注意返回值的类型
seg000:00406A31 jnz loc_406AE3
//非0就跳
seg000:00406A37 push esi
seg000:00406A38 call sub_406B89
//再次调用
seg000:00406A3D mov edi, eax
//保存返回值
seg000:00406A3F test edi, edi
seg000:00406A41 jb loc_406AE3
//非常奇怪,因为按照Test的说明,CF是一定为0的。
//这样jb就一定不会跳转
//谁看明白了,请告诉我。反正我觉得这个是废代码,可能是作者干的事情。
seg000:00406A47 cmp edi, 1
seg000:00406A4A ja loc_406AE3
//若大于1就跳。这样,返回值应该是整数类型
seg000:00406A50 push 0FFFFFFF0h
seg000:00406A52 push esi
seg000:00406A53 call GetWindowLongA_0
seg000:00406A58 mov [ebp+var_4], eax
//局部变量
seg000:00406A5B push 14h ; dwBytes
//20字节!
seg000:00406A5D push 0 ; dwFlags
seg000:00406A5F call GetProcessHeap
//注意,这里GetProcessHeap是无参API
seg000:00406A64 push eax ; hHeap
seg000:00406A65 call HeapAlloc
//返回指针
seg000:00406A6A mov ebx, eax
seg000:00406A6C mov eax, ds:dword_40A150
seg000:00406A71 mov [ebx+10h], eax
seg000:00406A74 mov ds:dword_40A150, ebx
//将全局变量dword_40A150的内容放入刚刚申请的内存块的最后4字节,再将刚刚申请的内存块的的地址存入dword_40A150。这里的操作让人想起单向链表!
seg000:00406A7A mov [ebx], esi
//窗口句柄
seg000:00406A7C mov [ebx+4], edi
//某个神秘的返回值!
seg000:00406A7F test byte ptr [ebp+var_4+3], 8
//这里是测试var_4的最高位
seg000:00406A83 jz short loc_406A8C
//为0则跳
seg000:00406A85 mov edx, 1
seg000:00406A8A jmp short loc_406A8E
seg000:00406A8C
seg000:00406A8C loc_406A8C: ; CODE XREF: sub_406A1F+64
seg000:00406A8C xor edx, edx
seg000:00406A8E
seg000:00406A8E loc_406A8E: ; CODE XREF: sub_406A1F+6B
//edx为var_4的最高位
seg000:00406A8E mov [ebx+8], edx
//内存块
seg000:00406A91 call GetFocus
seg000:00406A96 cmp esi, eax
seg000:00406A98 jnz short loc_406A9E
seg000:00406A9A or dword ptr [ebx+8], 8
//置位,注意dword ptr!
seg000:00406A9E
seg000:00406A9E loc_406A9E: ; CODE XREF: sub_406A1F+79
seg000:00406A9E push offset sub_406D7D
seg000:00406AA3 push 0FFFFFFFCh
seg000:00406AA5 push esi
seg000:00406AA6 call SetWindowLongA_0
//为参数指定的窗口设置Callback
//这里的nIndex是GWL_WNDPROC
seg000:00406AAB mov [ebx+0Ch], eax
//该窗口属性的原始值
seg000:00406AAE sub edi, 1
//参见上文
seg000:00406AB1 jb short loc_406AB7
seg000:00406AB3 jz short loc_406AC6
seg000:00406AB5 jmp short loc_406AD0
seg000:00406AB7
seg000:00406AB7 loc_406AB7: ; CODE XREF: sub_406A1F+92
seg000:00406AB7 mov eax, [ebp+var_4]
seg000:00406ABA and eax, 1Fh
seg000:00406ABD dec eax
seg000:00406ABE jnz short loc_406AD0
seg000:00406AC0 or dword ptr [ebx+8], 10h
seg000:00406AC4 jmp short loc_406AD0
seg000:00406AC6
seg000:00406AC6 loc_406AC6: ; CODE XREF: sub_406A1F+94
seg000:00406AC6 test byte ptr [ebp+var_4+1], 8
//var_4的低字的第12位
seg000:00406ACA jz short loc_406AD0
seg000:00406ACC or dword ptr [ebx+8], 20h
seg000:00406AD0
seg000:00406AD0 loc_406AD0: ; CODE XREF: sub_406A1F+96
seg000:00406AD0 ; sub_406A1F+9F ...
seg000:00406AD0 push 705h ; flags
seg000:00406AD5 push 0 ; hrgnUpdate
seg000:00406AD7 push 0 ; lprcUpdate
seg000:00406AD9 push esi ; hWnd
seg000:00406ADA call RedrawWindow
seg000:00406ADF mov eax, ebx
seg000:00406AE1 jmp short loc_406AE5
seg000:00406AE3
seg000:00406AE3 loc_406AE3: ; CODE XREF: sub_406A1F+12
seg000:00406AE3 ; sub_406A1F+22 ...
seg000:00406AE3 xor eax, eax
seg000:00406AE5
seg000:00406AE5 loc_406AE5: ; CODE XREF: sub_406A1F+C2
//以下代码为函数出口,恢复寄存器
seg000:00406AE5 pop edi
seg000:00406AE6 pop esi
seg000:00406AE7 pop ebx
seg000:00406AE8 pop ecx
seg000:00406AE9 pop ebp
seg000:00406AEA retn 4
seg000:00406AEA sub_406A1F endp
显然,这又是一个stdcall/pascal的函数。
看看那个神秘的内存块里是什么东西:
10H ds:dword_40A150,疑为指针
0CH 该窗口属性的原始值
08H var_4的最高位
04H sub_406B89的返回值
00H 窗口句柄
看起来应该是作为单向链表元素的结构体。但是目前尚无法判断是否为作者自定义结构。这个函数有个很值得注意的地方:从函数体看,它返回一个指向上述结构体的指针(链表的头)。但是调用它的函数并没有使用这个返回值。就函数代码看,这个返回值是被放到一个全局变量ds:dword_40A150里的。看起来,象是作者干的事情。
既然有链表,就一定有分配和释放。分配的代码已经有了。释放的代码有待分析。
整理一下代码,得到初步结果:
Type
PWindowProperty=^WindowProperty;
WindowProperty=Record
CurrentWindow:HWND;
Preserve1:Cardinal;//sub_406B89的返回值
Preserve2:Cardinal;//var_4的最高位
OrignalProperty:Cardinal;
NextWindowProperty:PWindowProperty;
End;
Var
WinPropLinkHead:PWindowProperty;//就是ds:dword_40A150
Function sub_406A1F(Window:HWND):PWindowProperty;
Var
CurrentWinProp:PWindowProperty;
CurrentWinStyle:Cardinal;
EDI:Cardinal;
Begin
Result:=0;
If sub_406B69(Window)<>0 Then Exit;
EDI:=sub_406B89(Window);
If EDI>1 Then Exit;
CurrentWinStyle:=GetWindowLongA(Window,GWL_STYLE);
CurrentWinProp:=HeapAlloc(GetProcessHeap,0,20);
CurrentWinProp^.NextWindowProperty:=WinPropLinkHead;
WinPropLinkHead:=CurrentWinProp;
CurrentWinProp^.CurrentWindow:=Window;
CurrentWinProp^.Preserve1:=edi;
CurrentWinProp^.Preserve2:=CurrentWinStyle SHR 31;//WS_POPUP?
If GetFocus=Window Then
Begin
CurrentWinProp^.Preserve2:=CurrentWinProp^.Preserve2 OR 8;
End;
CurrentWinProp^.OrignalProperty:=SetWindowLongA(Window,GWL_WNDPROC,sub_406D7D);
If EDI<1 Then
Begin
If CurrentWinStyle AND $1F=1 Then
//$1F=WS_EX_ACCEPTFILES OR WS_EX_TOPMOST OR WS_EX_NOPARENTNOTIFY OR WS_EX_DLGMODALFRAME
Begin
CurrentWinProp^.Preserve2:=CurrentWinProp^.Preserve2 OR $10;
End;
End;
If EDI=1 Then
Begin
If CurrentWinStyle AND $80000<>0 Then
//WS_SYSMENU
Begin
CurrentWinProp^.Preserve2:=CurrentWinProp^.Preserve2 OR $20;
End;
End;
RedrawWindow(Window,Nil,0,RDW_FRAME OR RDW_ERASENOW OR RDW_UPDATENOW OR RDW_ERASE OR RDW_INVALIDATE);
Result:=CurrentWinProp;
End;
这里说一下我整理代码的一般步骤:
1、首先从API入手,根据API的说明弄清楚它用到了哪些寄存器。然后就可以知道哪些寄存器是干吗的:是否是什么句柄、是否指向什么结构……
2、将API调用中的常数整理出来。这个需要查相关资料。
3、将其余的指令直接用你觉得方便的伪代码写出来。只要你能看懂而且不会弄错就可以。注意,IDA自动生成的地址标签一定要保留,这是下面步骤的基础。比如loc_406AC6我就写成:
loc_406AC6:
if var_4 and $800=0 then goto loc_406AD0
else ebx_008h=ebx_008h or $20
另外一个值得注意的地方是堆栈寻址。必要的时候,可以用debug写段代码验证一下。不过我用的是GRDB,比debug好不少。
4、重写控制结构,比如If Else等……到这一步,程序基本就出来了。
5、对程序里使用的变量和数据结构做出猜测并重新命名。最后再用你要使用的语言写一遍就是了。不会花太久的。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!