• 标 题:Delphi逆向工程笔记[7](终结篇)
  • 作 者:firstrose
  • 时 间:004-11-21,00:57
  • 链 接:http://bbs.pediy.com

今天是列宁格勒保卫战中,伟大的“生命之路”开通的日子。纪念一下。

本以为About没有什么的,但分析下来才发现丝毫不亚于那个标题栏渐变。还好,
长长的路就要到尽头了……

================================================================================
  下面就是最后的AboutProc。

0000:004045F0 DialogFunc      proc near
0000:004045F0
0000:004045F0 Rect            = tagRECT ptr -58h
0000:004045F0 Paint           = PAINTSTRUCT ptr -48h
0000:004045F0 pt              = POINT ptr -8
0000:004045F0 hWnd            = dword ptr  8
0000:004045F0 arg_4           = dword ptr  0Ch
0000:004045F0 arg_8           = dword ptr  10h
0000:004045F0 arg_C           = word ptr  14h
0000:004045F0
0000:004045F0                 push    ebp
0000:004045F1                 mov     ebpesp
0000:004045F3                 add     esp, 0FFFFFFA8h
//100h-A8h=58h
0000:004045F6                 push    ebx
0000:004045F7                 push    esi
0000:004045F8                 mov     esi, [ebp+hWnd]
0000:004045FB                 xor     ebxebx
0000:004045FD                 mov     eax, [ebp+arg_4]
0000:00404600                 cmp     eax, 113h       ; WM_TIMER
0000:00404605                 jg      short loc_404632
0000:00404607                 jz      loc_4047B8
0000:0040460D                 sub     eax, 0Fh        ; WM_PAINT
0000:00404610                 jz      loc_404867
0000:00404616                 sub     eax, 1Ch        ; WM_DRAWITEM
0000:00404619                 jz      loc_4048D7
0000:0040461F                 sub     eax, 0E5h       ; WM_INITDIALOG
0000:00404624                 jz      short loc_404656
0000:00404626                 dec     eax             ; WM_COMMAND
0000:00404627                 jz      loc_40489C
0000:0040462D                 jmp     loc_404925
0000:00404632 loc_404632:
0000:00404632                 sub     eax, 136h       ; WM_CTLCOLORDLG
0000:00404637                 jz      loc_4048E3
0000:0040463D                 sub     eax, 2          ; WM_CTLCOLORSTATIC
0000:00404640                 jz      loc_404904
0000:00404646                 sub     eax, 0C9h       ; WM_LBUTTONDOWN
0000:0040464B                 jz      loc_404826
0000:00404651                 jmp     loc_404925
...
0000:00404925 loc_404925:
0000:00404925                 xor     ebxebx
0000:00404927 loc_404927:
0000:00404927                 mov     eaxebx
0000:00404929                 pop     esi
0000:0040492A                 pop     ebx
0000:0040492B                 mov     espebp
0000:0040492D                 pop     ebp
0000:0040492E                 retn    10h
0000:0040492E DialogFunc      endp

  框架及大致相同处:

Function AboutProc(hDlg:HWND;Msg,wParam,lParam:DWORD):LRESULT;stdcall;
Var
  sRect:TRect;
  sPaint:PAINTSTRUCT;
  sPoint:TPoint;
Begin
  Result:=0;

  Case Msg of
    WM_COMMAND:
      Begin
        Case wParam of
          ABOUT_OK:
            Begin
              KillTimer(hDlg,$A8);
              EndDialog(hDlg,0);
            End;
          ABOUT_CLOSE:
            Begin
              KillTimer(hDlg,$A8);
              EndDialog(hDlg,0);
            End;
        End;
      End;
    WM_PAINT:
      Begin
        Paint(BeginPaint(hDlg,sPaint),h_Icon,szMainCaption,$767676,0,sRectA);
        EndPaint(hDlg,sPaint);
      End;
    WM_DRAWITEM:
      Begin
        ItemDraw(PDrawItemStruct(lParam));
        Result:=0;
      End;
    WM_INITDIALOG:
      Begin
      End;
    WM_CTLCOLORDLG:
      Begin
        SetTextColor(wParam,$A0A0A0);
        SetBkMode(wParam,TRANSPARENT);
        Result:=h_Brush;
      End;
    WM_CTLCOLORSTATIC:
      Begin
        SetTextColor(wParam,$A0A0A0);
        SetBkMode(wParam,TRANSPARENT);
        Result:=h_Brush;
      End;
    WM_LBUTTONDOWN:
      Begin
        sPoint.x:=lParam AND $FFFF;
        sPoint.y:=lParam SHR 16;
        If PtInRect(sRectA,sPoint) Then
          Begin
            PostMessage(hDlg,WM_NCLBUTTONDOWN,2,0);
          End;
      End;
    WM_TIMER:
      Begin
      End;
  End;
End;

  下面是WM_INITDIALOG部分:

0000:00404656 loc_404656:
0000:00404656                 push    offset Rect     ; lpRect
//注意,一共有3个Rect,分别对应3个Dlg。这里很容易混淆。
//我的做法是在IDA注释里加上地址。要么重命名也可。
0000:0040465B                 push    esi             ; hWnd
0000:0040465C                 call    GetClientRect
0000:00404661                 mov     eaxds:Rect.top
0000:00404666                 add     eax, 14h
0000:00404669                 mov     ds:Rect.bottom, eax
0000:0040466E                 push    0BBDh           ; nIDDlgItem
0000:00404673                 push    esi             ; hDlg
0000:00404674                 call    GetDlgItem
0000:00404679                 mov     ds:hWnd, eax
//滚动字幕框的HWND,因为Timer的callback要的。
0000:0040467E                 push    0BBBh           ; nIDDlgItem
0000:00404683                 push    esi             ; hDlg
0000:00404684                 call    GetDlgItem
0000:00404689                 mov     ebxeax
0000:0040468B                 push    offset nullsub_3 ; LPCSTR
//其实不仅是IDA,Sourcer也有这毛病
0000:00404690                 push    0               ; DWORD
0000:00404692                 push    0               ; DWORD
0000:00404694                 push    0               ; DWORD
0000:00404696                 push    0               ; DWORD
0000:00404698                 push    1               ; DWORD
0000:0040469A                 push    0               ; DWORD
0000:0040469C                 push    0               ; DWORD
0000:0040469E                 push    0               ; DWORD
0000:004046A0                 push    2BCh            ; int
0000:004046A5                 push    0               ; int
0000:004046A7                 push    0               ; int
0000:004046A9                 push    0               ; int
0000:004046AB                 push    0FFFFFFF4h      ; int
0000:004046AD                 call    CreateFontA
0000:004046B2                 push    0               ; lParam
0000:004046B4                 push    eax             ; wParam
0000:004046B5                 push    30h             ; Msg
0000:004046B7                 push    ebx             ; hWnd
0000:004046B8                 call    SendMessageA
//设置字体了
0000:004046BD                 push    offset dword_40493C ; lpString
0000:004046C2                 push    0BBBh           ; nIDDlgItem
0000:004046C7                 push    esi             ; hDlg
0000:004046C8                 call    SetDlgItemTextA
0000:004046CD                 push    offset dword_404954 ; lpString
0000:004046D2                 push    0BBCh           ; nIDDlgItem
0000:004046D7                 push    esi             ; hDlg
0000:004046D8                 call    SetDlgItemTextA
0000:004046DD                 push    offset dword_40496C ; lpString
0000:004046E2                 push    esi             ; hWnd
0000:004046E3                 call    SetWindowTextA
0000:004046E8                 lea     eax, [ebp+Rect]
0000:004046EB                 push    eax             ; lpRect
0000:004046EC                 mov     eaxds:hWnd
0000:004046F1                 push    eax             ; hWnd
0000:004046F2                 call    GetClientRect
//得到滚动字幕的Rect
0000:004046F7                 mov     axword ptr [ebp+Rect.right]
0000:004046FB                 sub     axword ptr [ebp+Rect.left]
0000:004046FF                 mov     ds:word_4060C8, ax
//宽
0000:00404705                 mov     axword ptr [ebp+Rect.bottom]
0000:00404709                 sub     axword ptr [ebp+Rect.top]
0000:0040470D                 mov     ds:word_4060CC, ax
//高度
0000:00404713                 mov     axds:word_4060CC
//这里的话,如果查看xref就知道,4060CC只在这里有赋值
0000:00404719                 mov     ds:word_4060C4, ax
//4060C4在其他地方有变化(dec),所以应该是类似计数器的作用
//请注意,有时xref在确定变量用途的时候很有用。
0000:0040471F                 mov     eax, offset dword_40497C
//滚动字幕内容的地址
0000:00404724                 call    sub_403F94
0000:00404729                 mov     ds:word_4060D8, ax
//字符串里的换行数+1。注意这里的初始值为1。就是说,即使是空字符串,也
//应该在末尾加个换行。但是这里用了mov,联系到sub_403F94里有SEH,
//很容易想到在异常情况下,初始值起作用的。
0000:0040472F                 push    0               ; lpParam
0000:00404731                 movsx   eaxds:word_4060CC
0000:00404738                 push    eax             ; hInstance
0000:00404739                 movsx   eaxds:word_4060C8
0000:00404740                 push    eax             ; hMenu
0000:00404741                 movsx   eaxds:word_4060D8
0000:00404748                 shl     eax, 2
0000:0040474B                 lea     eax, [eax+eax*2]
0000:0040474E                 push    eax             ; hWndParent
0000:0040474F                 mov     eaxds:hWnd
0000:00404754                 push    eax             ; nHeight
0000:00404755                 push    0               ; nWidth
0000:00404757                 mov     eaxds:hInstance
0000:0040475C                 push    eax             ; Y
0000:0040475D                 push    0               ; X
//留意一下,IDA已经第三次发飚了。而且这次跟前两次不一样。
//如果真按照注释对参数是要死人的!反正……push的参数顺序全部倒了……
0000:0040475F                 mov     edx, offset dword_404A28 ; lpWindowName
0000:00404764                 mov     eax, offset dword_404A2C ; lpClassName
//象上面这样只有一个引用的lpstr就直接写字符串吧。
0000:00404769                 mov     ecx, 50000001h  ; dwStyle
//这里的值是3个值的组合。由于该窗口是Static,所以那个1是SS_CENTER
0000:0040476E                 call    sub_403C98
//调用CreateWindowEx
0000:00404773                 mov     ds:dword_4060D4, eax
0000:00404778                 push    offset sub_404484 ; dwNewLong
//专用的callback
0000:0040477D                 push    0FFFFFFFCh      ; nIndex
0000:0040477F                 mov     eaxds:dword_4060D4
0000:00404784                 push    eax             ; hWnd
0000:00404785                 call    SetWindowLongA
0000:0040478A                 push    eax             ; dwNewLong
0000:0040478B                 push    0FFFFFFEBh      ; nIndex
0000:0040478D                 mov     eaxds:dword_4060D4
0000:00404792                 push    eax             ; hWnd
0000:00404793                 call    SetWindowLongA
0000:00404798                 mov     eaxesi        ; hWnd
0000:0040479A                 call    sub_404358
//还好,是那个窗口特效……
0000:0040479F                 push    0               ; lpTimerFunc
0000:004047A1                 push    3Ch             ; uElapse
0000:004047A3                 push    0A8h            ; nIDEvent
0000:004047A8                 push    esi             ; hWnd
0000:004047A9                 call    SetTimer
0000:004047AE                 mov     ebx, 1
0000:004047B3                 jmp     loc_404927

  sub_403F94实际是统计$0A数目的。

0000:00403F94 sub_403F94      proc near
0000:00403F94
0000:00403F94 var_4           = dword ptr -4
0000:00403F94
0000:00403F94                 push    ebp
0000:00403F95                 mov     ebpesp
0000:00403F97                 push    ecx
0000:00403F98                 push    ebx
0000:00403F99                 mov     [ebp+var_4], eax
0000:00403F9C                 mov     eax, [ebp+var_4]
//var_4里是地址
0000:00403F9F                 call    @System@@LStrAddRef$qqrv ; System::__linkproc__ LStrAddRef(void)
//增加引用计数
0000:00403FA4                 xor     eaxeax
0000:00403FA6                 push    ebp
0000:00403FA7                 push    offset loc_403FEB
0000:00403FAC                 push    dword ptr fs:[eax]
0000:00403FAF                 mov     fs:[eax], esp
//设置SEH
0000:00403FB2                 mov     bx, 1
0000:00403FB6                 mov     eax, [ebp+var_4]
//仍然是地址
0000:00403FB9                 call    @System@_16823  ; System::_16823
//取字符串的净长度。通过查看dword_40497C附近的数据可以发现,
//dword_40497C前面的4个字节,内容是$A8。dword_40497C的最后部分,有一个
//dword的0,加上这个0,总长度是$AC。$AC-$A8=4,恰好是一个dword。
0000:00403FBE                 dec     eax
0000:00403FBF                 test    eaxeax
0000:00403FC1                 jl      short loc_403FD5
//判断空字符串
0000:00403FC3                 inc     eax
//加回来
0000:00403FC4                 xor     edxedx
//此时bx=1,edx=0
0000:00403FC6
0000:00403FC6 loc_403FC6:
0000:00403FC6                 mov     ecx, [ebp+var_4]
//字符串的开头地址
0000:00403FC9                 cmp     byte ptr [ecx+edx-1], 0Ah
//应该是用edx做索引,但是开始时edx=0,ecx+edx-1=(Var_4)-1,这样
//就到了长度字节。
0000:00403FCE                 jnz     short loc_403FD1
0000:00403FD0                 inc     ebx
0000:00403FD1
0000:00403FD1 loc_403FD1:
0000:00403FD1                 inc     edx
0000:00403FD2                 dec     eax
0000:00403FD3                 jnz     short loc_403FC6
0000:00403FD5
0000:00403FD5 loc_403FD5:
//ebx里应该是换行的数目+1
0000:00403FD5                 xor     eaxeax
0000:00403FD7                 pop     edx
0000:00403FD8                 pop     ecx
0000:00403FD9                 pop     ecx
0000:00403FDA                 mov     fs:[eax], edx
//恢复SEH
0000:00403FDD                 push    offset loc_403FF2
//实际和retn配套
//不得不说,虽然开始看到403FC6的循环就猜到这个是计算$0A的,但是因为是分析
//模板,开始时没想到里面居然会有push/ret这个变形的jmp。怀疑这个东西不是
//Delphi生成的。但是看403FE2又发现不像是手写的,真是!@#$
0000:00403FE2
0000:00403FE2 loc_403FE2:
0000:00403FE2                 lea     eax, [ebp+var_4]
0000:00403FE5                 call    @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &)
//这里没有改变eax,eax还是那个地址
//清除字符串?
0000:00403FEA                 retn

0000:00402EE8 @System@_16823  proc near
0000:00402EE8                 test    eaxeax
0000:00402EEA                 jz      short locret_402EEF
0000:00402EEC                 mov     eax, [eax-4]
0000:00402EEF
0000:00402EEF locret_402EEF:
0000:00402EEF                 retn
0000:00402EEF @System@_16823  endp

0000:00403FF2 loc_403FF2:
0000:00403FF2                 mov     eaxebx
//返回值就是ebx
0000:00403FF4                 pop     ebx
0000:00403FF5                 pop     ecx
0000:00403FF6                 pop     ebp
0000:00403FF7                 retn
0000:00403FF7 sub_403F94      endp

  CreateWindowEx的外套:

0000:00403C98 sub_403C98      proc near
0000:00403C98
0000:00403C98 lpParam         = dword ptr  8
0000:00403C98 hInstance       = dword ptr  0Ch
0000:00403C98 hMenu           = dword ptr  10h
0000:00403C98 hWndParent      = dword ptr  14h
0000:00403C98 nHeight         = dword ptr  18h
0000:00403C98 nWidth          = dword ptr  1Ch
0000:00403C98 Y               = dword ptr  20h
0000:00403C98 X               = dword ptr  24h
0000:00403C98
0000:00403C98                 push    ebp
0000:00403C99                 mov     ebpesp
0000:00403C9B                 push    ebx
0000:00403C9C                 mov     ebx, [ebp+lpParam]
0000:00403C9F                 push    ebx             ; lpParam
0000:00403CA0                 mov     ebx, [ebp+hInstance]
0000:00403CA3                 push    ebx             ; hInstance
0000:00403CA4                 mov     ebx, [ebp+hMenu]
0000:00403CA7                 push    ebx             ; hMenu
0000:00403CA8                 mov     ebx, [ebp+hWndParent]
0000:00403CAB                 push    ebx             ; hWndParent
0000:00403CAC                 mov     ebx, [ebp+nHeight]
0000:00403CAF                 push    ebx             ; nHeight
0000:00403CB0                 mov     ebx, [ebp+nWidth]
0000:00403CB3                 push    ebx             ; nWidth
0000:00403CB4                 mov     ebx, [ebp+Y]
0000:00403CB7                 push    ebx             ; Y
0000:00403CB8                 mov     ebx, [ebp+X]
0000:00403CBB                 push    ebx             ; X
0000:00403CBC                 push    ecx             ; dwStyle
0000:00403CBD                 push    edx             ; lpWindowName
0000:00403CBE                 push    eax             ; lpClassName
0000:00403CBF                 push    0               ; dwExStyle
0000:00403CC1                 call    CreateWindowExA
0000:00403CC6                 pop     ebx
0000:00403CC7                 pop     ebp
0000:00403CC8                 retn    20h
0000:00403CC8 sub_403C98      endp

  滚动框的callback,有点眼熟:

0000:00404484 sub_404484      proc near
0000:00404484
0000:00404484 Rect            = tagRECT ptr -50h
0000:00404484 Paint           = PAINTSTRUCT ptr -40h
0000:00404484 hWnd            = dword ptr  8
0000:00404484 arg_4           = dword ptr  0Ch
0000:00404484 wParam          = dword ptr  10h
0000:00404484 lParam          = dword ptr  14h
0000:00404484
0000:00404484                 push    ebp
0000:00404485                 mov     ebpesp
0000:00404487                 add     esp, 0FFFFFFB0h
0000:0040448A                 push    ebx
0000:0040448B                 push    esi
0000:0040448C                 push    edi
0000:0040448D                 mov     esi, [ebp+arg_4]
0000:00404490                 mov     ebx, [ebp+hWnd]
0000:00404493                 mov     eaxesi
0000:00404495                 sub     eax, 0Fh
//如果还不能直接看出是WM_PAINT,那也就太逊了。好歹也到这里了……总该有点长
//进吧。
0000:00404498                 jnz     short loc_404515
//结合下面可以看到,是典型的if then else结构
//往下就是纯粹的API调用,实在没什么好说的。只是要注意寄存器变化。
0000:0040449A                 lea     eax, [ebp+Paint]
0000:0040449D                 push    eax             ; lpPaint
0000:0040449E                 push    ebx             ; hWnd
0000:0040449F                 call    BeginPaint
0000:004044A4                 mov     esieax
0000:004044A6                 lea     eax, [ebp+Rect]
0000:004044A9                 push    eax             ; lpRect
0000:004044AA                 push    ebx             ; hWnd
0000:004044AB                 call    GetClientRect
0000:004044B0                 push    0A0A0A0h        ; COLORREF
0000:004044B5                 push    esi             ; HDC
0000:004044B6                 call    SetTextColor
0000:004044BB                 push    1               ; int
0000:004044BD                 push    esi             ; HDC
0000:004044BE                 call    SetBkMode
0000:004044C3                 push    offset nullsub_2 ; LPCSTR
0000:004044C8                 push    0               ; DWORD
0000:004044CA                 push    0               ; DWORD
0000:004044CC                 push    0               ; DWORD
0000:004044CE                 push    0               ; DWORD
0000:004044D0                 push    1               ; DWORD
0000:004044D2                 push    0               ; DWORD
0000:004044D4                 push    0               ; DWORD
0000:004044D6                 push    0               ; DWORD
0000:004044D8                 push    0               ; int
0000:004044DA                 push    0               ; int
0000:004044DC                 push    0               ; int
0000:004044DE                 push    0               ; int
0000:004044E0                 push    0FFFFFFF4h      ; int
0000:004044E2                 call    CreateFontA
0000:004044E7                 mov     edieax
0000:004044E9                 push    edi             ; HGDIOBJ
0000:004044EA                 push    esi             ; HDC
0000:004044EB                 call    SelectObject
0000:004044F0                 push    1               ; uFormat
0000:004044F2                 lea     eax, [ebp+Rect]
0000:004044F5                 push    eax             ; lpRect
0000:004044F6                 push    0FFFFFFFFh      ; nCount
0000:004044F8                 push    offset dword_404544 ; lpString
0000:004044FD                 push    esi             ; hDC
0000:004044FE                 call    DrawTextA
0000:00404503                 lea     eax, [ebp+Paint]
0000:00404506                 push    eax             ; lpPaint
0000:00404507                 push    ebx             ; hWnd
0000:00404508                 call    EndPaint
0000:0040450D                 push    edi             ; HGDIOBJ
0000:0040450E                 call    DeleteObject
0000:00404513                 jmp     short loc_40452D
0000:00404515 loc_404515:
0000:00404515                 push    0FFFFFFEBh      ; nIndex
0000:00404517                 push    ebx             ; hWnd
0000:00404518                 call    GetWindowLongA
0000:0040451D                 mov     edx, [ebp+lParam]
0000:00404520                 push    edx             ; lParam
0000:00404521                 mov     edx, [ebp+wParam]
0000:00404524                 push    edx             ; wParam
0000:00404525                 push    esi             ; Msg
0000:00404526                 push    ebx             ; hWnd
0000:00404527                 push    eax             ; lpPrevWndFunc
0000:00404528                 call    CallWindowProcA
0000:0040452D loc_40452D:
0000:0040452D                 mov     eax, 1
0000:00404532                 pop     edi
0000:00404533                 pop     esi
0000:00404534                 pop     ebx
0000:00404535                 mov     espebp
0000:00404537                 pop     ebp
0000:00404538                 retn    10h
0000:00404538 sub_404484      endp

0000:004047B8 loc_4047B8:
0000:004047B8                 push    14h             ; dwMilliseconds
0000:004047BA                 call    Sleep
0000:004047BF                 dec     ds:word_4060C4
0000:004047C6                 push    0               ; uFlags
0000:004047C8                 movsx   eaxds:word_4060D8
0000:004047CF                 shl     eax, 2
0000:004047D2                 lea     eax, [eax+eax*2]
0000:004047D5                 push    eax             ; cy
0000:004047D6                 movsx   eaxds:word_4060C8
0000:004047DD                 push    eax             ; cx
0000:004047DE                 movsx   eaxds:word_4060C4
0000:004047E5                 push    eax             ; Y
0000:004047E6                 push    0               ; X
0000:004047E8                 push    0               ; hWndInsertAfter
0000:004047EA                 mov     eaxds:dword_4060D4
0000:004047EF                 push    eax             ; hWnd
0000:004047F0                 call    SetWindowPos
0000:004047F5                 movsx   eaxds:word_4060C4
0000:004047FC                 movsx   edxds:word_4060D8
0000:00404803                 shl     edx, 2
0000:00404806                 lea     edx, [edx+edx*2]
0000:00404809                 add     eaxedx
0000:0040480B                 neg     eax
0000:0040480D                 test    eaxeax
0000:0040480F                 jle     loc_404927
0000:00404815                 mov     axds:word_4060CC
0000:0040481B                 mov     ds:word_4060C4, ax
0000:00404821                 jmp     loc_404927

  看来About是这个程序的华彩乐章。滚动字幕是使用了创建子窗口,然后改变其
位置的方法。

  下面就给出最后的几个部分:

    WM_INITDIALOG:
      Begin
        GetClientRect(hDlg,sRectA);
        sRectA.Bottom:=sRectA.Top+$14;
        h_ScrollParent:=GetDlgItem(hDlg,$BBD);
        SendMessage(GetDlgItem(hDlg,$BBB),WM_SETFONT,CreateFont(-$C,0,0,0,$2BC,0,0,0,1,0,0,0,0,'宋体'),0);
        SetDlgItemText(hDlg,$BBB,szKeyGenName);
        SetDlgItemText(hDlg,$BBC,szCracker);
        SetWindowText(hDlg,'关于');
        GetClientRect(h_ScrollParent,sRect);
        ScrollWidth:=sRect.right-sRect.left;
        ScrollHeight:=sRect.bottom-sRect.top;
        xCount:=ScrollHeight;
        LineCount:=CountCRLF(szScroll);
        h_Scroll:=CreateWindowEx(WS_EX_LEFT,'Static','',WS_CHILD OR WS_VISIBLE OR SS_CENTER,0,ScrollHeight,ScrollWidth,LineCount*12,h_ScrollParent,0,h_Inst,nil);
        SetWindowLong(h_Scroll,GWL_USERDATA,SetWindowLong(h_Scroll,GWL_WNDPROC,LongWord(@ScrollProc)));
        DialogInit(hDlg);
        SetTimer(hDlg,$A8,60,NIL);
        Result:=1;
      End;
    WM_TIMER:
      Begin
        Sleep(20);
        Dec(xCount);
        SetWindowPos(h_Scroll,HWND_TOP,0,xCount,ScrollWidth,LineCount*12,0);
        If xCount<(0-LineCount*12) Then
          Begin
            xCount:=ScrollHeight;
          End;
      End;

Function CountCRLF(Str:String):Word;
Var
  Count:Word;
  i:Word;
Begin
  Count:=1;
  For i:=1 to Length(Str) Do
    Begin
      If Str[i]=#$0A Then Inc(Count);
    End;
  CountCRLF:=Count;
End;

Function ScrollProc(hDlg:HWND;Msg,wParam,lParam:DWORD):LRESULT;stdcall;
Var
  sRect:TRect;
  sPaint:PAINTSTRUCT;
  DC:HDC;
  LFont:HFONT;
Begin
  If Msg=WM_PAINT Then
    Begin
      DC:=BeginPaint(hDlg,sPaint);
      GetClientRect(hDlg,sRect);
      SetTextColor(DC,$A0A0A0);
      SetBkMode(DC,TRANSPARENT);
      LFont:=CreateFont(-$C,0,0,0,0,0,0,0,1,0,0,0,0,'宋体');
      SelectObject(DC,LFont);
      DrawText(DC,szScroll,-1,sRect,DT_CENTER);
      EndPaint(hDlg,sPaint);
      DeleteObject(LFont);
    End
  Else
    Begin
      CallWindowProc(Pointer(GetWindowLong(hDlg,GWL_USERDATA)),hDlg,Msg,wParam,lParam);
    End;
  Result:=1;
End;

  这样,整个程序就分析完了。下面作一下总结。

  从编程方面看,该程序有几处地方值得借鉴:1、按钮的三维边框;2、渐变标题
栏;3、滚动字幕的方法。

  从逆向工程的角度看,难点主要在渐变标题栏和滚动字幕的处理上。主要是这两
个地方有较多的运算。

  另外,在分析过程中,有几点是值得注意的:

  1、注意区分全局变量和局部变量。局部变量一般用ebx引用。在分析滚动字幕时,
由于把一个全局变量当作局部变量,造成字幕无法重复滚动。不过,如果将该局部变
量用Const说明,还是可以达到目的的。
  2、注意变量的类型和地址。这样就不会弄错变量。
  3、API中的常量。这些常量一般可以在SDK中找到,但有些是组合值,比如
CreateWindowEx里使用的$50000001,这就需要仔细分析。
  4、有符号数的处理。在本例中直接体现在渐变标题栏上。
  5、不要迷信反汇编程序。

  另外,你必须熟悉该程序使用的语言(尤其是内部结构,而不是仅仅会写几行程
序就可以的),这样才能够得心应手。但伪编译语言(VB、Java、.NET)除外。当然,
你还必须熟悉汇编语言。什么?不熟悉?临时学习?呃,算了吧。逆向工程只是手段
而不是目的,而且这对阁下来说也不好玩。倒是交谊舞对于阁下更实际点儿……阁下
35以上?那……当我什么都没说好了……

  OK,这个笔记系列就基本结束了。但是有个缺憾:渐变标题栏没有作到原程序那
样的效果。也许以后我会分析出来的。不过现在只能说声抱歉了。

  谢谢各位观赏,也谢谢各位的耐心。

PS:不要轻易决定做逆向工程,更不要轻易说你要做逆向工程并要写下心得以供分
享。但是如果你决定了,那么无论如何要完成它。特别是在第一篇心得贴出来以
后……想想那些网络上的“太监书”,再想想曹老先生的半部《红楼梦》……

===========================================================================

原以为About不会很复杂,没想到里面居然套了那么多东西。直接后果就是这篇文章
比以前贴的任何一篇都长,打开的速度肯定要慢点。不过你既然都看到这里了,就
别抱怨了,OK?

附:
关于Delphi逆向工程笔记系列的说明

事情的起因是我看到laoqian的一篇文章,他提到了FCG的KG模板,但是碍于FCG的规矩不能放出来,只是给了编译过的程序。但是我又比较喜欢收集这个。本来嘛,加入FCG就可以了,但是本人不会脱壳,破解又只能到明码比较的水平,估计没有哪个组织会要。很幸运,那个模板是Delphi的,而且laoqian只用upx加了壳,这样我才可以很方便地把它脱掉。不过他还是给我找了点小麻烦,破坏了upx头部的校验字节,拿upx就脱不掉了。

更幸运的是KG是用SDK写的(这也是惯例)……大家肯定都知道SDK程序编译出来是什么样子。也正是如此,我才可以做这个RE。如果是VCL的话,我也只有去找非明码比较的麻烦了。

做了半天,发现有些很有趣的东西。于是想拿出来。原以为这些东西肯定不太入眼,特别是对于在pediy混的人。但发现反响比较好,这是我始料不及的。
=================================================================