首页
社区
课程
招聘
对[FCG]KeyGen2.0按钮特效的分析2
发表于: 2006-10-26 16:35 5807

对[FCG]KeyGen2.0按钮特效的分析2

2006-10-26 16:35
5807

下面开始分析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、对程序里使用的变量和数据结构做出猜测并重新命名。最后再用你要使用的语言写一遍就是了。不会花太久的。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (4)
雪    币: 3686
活跃值: (1036)
能力值: (RANK:760 )
在线值:
发帖
回帖
粉丝
2
学习,认真的学习,呵呵
2006-10-26 19:37
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
3
期待链表释放代码的分析
2006-10-27 00:19
0
雪    币: 451
活跃值: (78)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
4
学习到了几个API的巧妙使用
2006-10-28 11:38
0
雪    币: 332
活跃值: (479)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
5
呵呵,支持,此分析,结束之后,就有源码了――我也没有源码的!
2006-10-31 10:19
0
游客
登录 | 注册 方可回帖
返回
//