Delphi逆向工程筆記[7](終結篇)

看雪資料發表於2004-11-21

今天是列寧格勒保衛戰中,偉大的“生命之路”開通的日子。紀念一下。

本以為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+edw-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混的人。但發現反響比較好,這是我始料不及的。
=================================================================

相關文章