一款工具的逆向分析與再實現

看雪資料發表於2004-12-22

一款工具的逆向分析與再實現

作者: nbw   www.vxer.com

目的:    逆向分析一款視窗控制程式碼察看工具(dRag0nMa 大俠寫的hwnd_pwd)
        並重新寫出來

分析工具:  IDA,fly_OD
編寫語言:  Masm32(原軟體估計是VC)
備註:    剛看了firstrose兄弟寫的逆向文章,自己也想來練練手。未曾徵得原作者的同意就發出筆記,請多原諒!
        感謝悠長假期的幫助。

原軟體用upx加殼,脫掉。

用HackRes檢視一下資源,可以獲得資原始檔:

101 DIALOGEX 0, 0, 181, 53
STYLE DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "WinClass&HWND+viEw\"*\"pwd"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 8, "Times New Roman"
{
   CONTROL "", 1000, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP | WS_TABSTOP, 41, 13, 122, 12 
   CONTROL "", 1005, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 40, 31, 91, 12 
   CONTROL "Win.Class:", 1037, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 13, 35, 8 , 0x00000020
   CONTROL "HWND:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 8, 31, 29, 9 
   CONTROL "", -1, BUTTON, BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 2, 0, 174, 47 
   CONTROL "cOde:dRag0nMa", -1, STATIC, SS_RIGHT | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP | WS_TABSTOP, 113, 41, 58, 8 
}

需要注意的是:

CAPTION "WinClass&HWND+viEw\"*\"pwd"
這裡在asm中編譯不透過,直接去掉\"就可以,改為:
CAPTION "WinClass&HWND+viEw*pwd"
但是它這裡的LANGUAGE和FONT我這邊不能用(估計我的機器太爛,裝配不全),所以最後看到的介面比原來的軟體要大。

程式碼初始:

mBC:00401048                 push    1
mBC:0040104A                 call    ds:GetCommandLineA
mBC:00401050                 push    eax
mBC:00401051                 push    0
mBC:00401053                 push    0               ; lpModuleName
mBC:00401055                 call    ds:GetModuleHandleA
mBC:0040105B                 push    eax
mBC:0040105C                 call    sub_401213
mBC:00401061                 push    eax             ; uExitCode
mBC:00401062                 call    ds:ExitProcess
mBC:00401068                 retn

其中,call    sub_401213為:

mov     eax, [esp+hInstance]
push    0               ; dwInitParam
push    offset DialogFunc ; lpDialogFunc
push    0               ; hWndParent
push    65h             ; lpTemplateName
push    eax             ; hInstance
mov     ds:dword_401344, eax
call    ds:DialogBoxParamA ; Create a modal dialog box from a
xor     eax, eax

這裡需要注意的是:
push    65h 
這個地方為主視窗資源ID

mov     ds:dword_401344, eax
由於eax為程式控制程式碼,因此,我們用  mov     hInstance, eax代替

xor     eax, eax
故,下面的  call    ExitProcess引數為NULL

理解了call    sub_401213的含義,故此,把該函式去掉,只保留該函式的主體。

進入主訊息處理瞅瞅:

DialogFunc      proc near               ; DATA XREF: sub_401213+6o
mBC:004010DE hDlg            = dword ptr  8
mBC:004010DE arg_4           = dword ptr  0Ch

mBC:004010DE                 push    ebp
mBC:004010DF                 mov     ebp, esp
mBC:004010E1                 mov     eax, [ebp+arg_4] ; wMsg

其中ebp + hDlg相當於我們的hWnd,ebp+arg_4相當於我們的wMsg。程式碼開頭就是編譯器的經典call處理,我們作彙編的,不管這個。

mBC:004010E4                 sub     eax, 0Fh
mBC:004010E7                 jz      loc_40120D

這2句是錯誤訊息處理,如果訊息是0Fh,那麼直接返回。程式碼中應該做如下處理:
  sub  eax, 0Fh
  cmp  eax, 0
  jne  @F
  xor  eax, eax
  ret
@@:          ;normal Message
那麼0Fh究竟是什麼訊息呢?一查查了一大堆,也懶得去猜。因此我也不曉得。

繼續向下看:

mBC:004010ED                 dec     eax
mBC:004010EE                 jz      loc_4011F7

如果傳來的訊息為0F + 1(注意上面已經sub eax, 0Fh) ,那麼:

mBC:004011F7                 push    1               ; uIDEvent
mBC:004011F9                 push    [ebp+hDlg]      ; hWnd
mBC:004011FC                 call    ds:KillTimer
mBC:00401202                 push    0               ; nResult
mBC:00401204                 push    [ebp+hDlg]      ; hDlg
mBC:00401207                 call    ds:EndDialog
mBC:0040120D                 xor     eax, eax
mBC:0040120F                 pop     ebp
mBC:00401210                 retn    10h

就是:
push 1
push hWnd
call KillTimer
push 0
push hWnd
call EndDialog
xor  eax, eax
很明顯是close事件。那麼處理的訊息是不是WM_CLOSE呢?可以認為是的,因為 (0F + 1) equ 10h equ WM_CLOSE。
因此,這裡可以翻譯成:
  .if  wMsg == WM_CLOSE
    invoke  KillTimer,hWnd,1
    invoke  EndDialog,hWnd,NULL
    xor eax, eax
  .endif


再往下看下一條訊息吧:
  push    0               ; lpTimerFunc
  push    12Ch            ; uElapse
  push    1               ; nIDEvent
  push    hWnd
  call    SetTimer
  push    offset Rect     ; lpRect
  push    hWnd
  call    GetWindowRect
  push    41h             ; uFlags
  push    Rect.bottom  ; cy
  push    Rect.right   ; cx
  push    Rect.top     ; Y
  push    Rect.left    ; X
  push    0FFFFFFFFh      ; hWndInsertAfter
  push    hWnd
  call    SetWindowPos
  push    1
  pop     eax
  jmp     short loc_40120F
short loc_40120F:
  pop    ebp
  ret

SetTimer設定一個時間,300毫秒監測一次,lpTimerFunc 為0,也就是這個timer專門供主程式用。
GetWindowRect獲取主視窗位置。Rect結構定義要注意以下。具體參考程式碼。
SetWindowPos設定窗體顯示屬性,比如說總置頂顯示。
最後是mov eax, 1,然後ret
這個訊息數值為110h,參考以下功能,可以判定為WM_INITDIALOG。

再看下一條訊息處理:

mBC:004011F2                 push    1
mBC:004011F4                 pop     eax
mBC:004011F5                 jmp     short loc_40120F

完成mov eax, 1然後退出。
該訊息值為111h,判定為WM_COMMAND。因為資源定義中使用了menu,因此這裡懷疑是否為用來處理menu訊息的部分。但是由於沒用到,所以程式中我刪除了這個。


繼續看下一條:

訊息檢測為:

mBC:00401106                 dec     eax
mBC:00401107                 dec     eax
mBC:00401108                 jz      short loc_401132

訊息數值為: 111h + 2 = 113h,查一下文件,就2個訊息為該值,毋庸置疑,該訊息是WM_TIMER ,總算到了核心部分。
訊息處理如下:

mBC:00401132 loc_401132:                             ; CODE XREF: DialogFunc+2Aj
mBC:00401132                 push    esi
mBC:00401133                 push    edi
mBC:00401134                 push    offset Point    ; lpPoint
mBC:00401139                 call    ds:GetCursorPos
mBC:0040113F                 push    ds:Point.y
mBC:00401145                 push    ds:Point.x      ; Point
mBC:0040114B                 call    ds:WindowFromPoint ; Get handle of the window that
mBC:0040114B                                         ; contains the specified poin
mBC:00401151                 push    eax
mBC:00401152                 push    50h          ;注意這裡是顯示資料的label的ID
mBC:00401154                 mov     ds:hWnd, eax    ;儲存滑鼠所在的視窗控制程式碼,用於下一步呼叫
mBC:00401159                 call    sub_401069
mBC:0040115E                 pop     ecx
mBC:0040115F                 mov     esi, ds:SetDlgItemTextA
mBC:00401165                 pop     ecx
mBC:00401166                 push    offset String   ; lpString
mBC:0040116B                 push    3EDh            ; nIDDlgItem
mBC:00401170                 push    [ebp+hDlg]      ; hDlg
mBC:00401173                 call    esi ; SetDlgItemTextA
mBC:00401175                 mov     edi, offset ClassName
mBC:0040117A                 push    10h             ; nMaxCount
mBC:0040117C                 push    edi             ; lpClassName
mBC:0040117D                 push    ds:hWnd         ; hWnd
mBC:00401183                 call    ds:GetClassNameA
mBC:00401189                 push    edi             ; lpString
mBC:0040118A                 push    3E8h            ;注意這裡是顯示資料的label的ID
mBC:0040118F                 push    [ebp+hDlg]      ; hDlg
mBC:00401192                 call    esi ; SetDlgItemTextA
mBC:00401194                 push    0               ; lParam
mBC:00401196                 push    0               ; wParam
mBC:00401198                 push    0CCh            ; Msg
mBC:0040119D                 push    ds:hWnd         ; hWnd
mBC:004011A3                 call    ds:PostMessageA


首先,透過GetCursorPos獲取滑鼠所在位置,然後透過WindowFromPoint函式獲取該位置處視窗句炳,利用SetDlgItemTextA顯示出來。
再用GetClassNameA獲取該視窗類,顯示出來。

其中有函式:
push    eax
push    50h
mov     hWnd, eax
call    sub_401069
去瞅瞅:

mBC:00401070                 mov     esi, [ebp+arg_4]        ;取獲得的視窗控制程式碼
mBC:00401073                 xor     ecx, ecx
mBC:00401075                 test    esi, esi
mBC:00401077                 jz      short loc_4010A7        ;如果沒取到,便退出
mBC:00401079                 push    edi
mBC:0040107A

;****************************
;這個小迴圈,透過除以10h,依次取出來視窗控制程式碼的每一位(肯定小於0x10了)。
;如果小於9,就加上30h,轉換成真正的9,否則,加上37h,轉換成字母。
;不過轉換過來的順序顛倒過來了
;比如B0638 轉換成:
; 38 33 36 30 42 00

mBC:0040107A                 mov     eax, esi
mBC:0040107C                 push    10h
mBC:0040107E                 cdq
mBC:0040107F                 pop     edi
mBC:00401080                 idiv    edi
mBC:00401082                 lea     eax, [ebp+ecx+var_C]
mBC:00401086                 cmp     dl, 9
mBC:00401089                 mov     [eax], dl
mBC:0040108B                 jle     short loc_401092
mBC:0040108D                 add     dl, 37h
mBC:00401090                 jmp     short loc_401095
mBC:00401092
mBC:00401092 loc_401092:                             ; CODE XREF: sub_401069+22j
mBC:00401092                 add     dl, 30h
mBC:00401095
mBC:00401095 loc_401095:                             ; CODE XREF: sub_401069+27j
mBC:00401095                 mov     [eax], dl
mBC:00401097                 mov     eax, esi
mBC:00401099                 push    10h
mBC:0040109B                 cdq
mBC:0040109C                 pop     esi
mBC:0040109D                 idiv    esi
mBC:0040109F                 inc     ecx
mBC:004010A0                 mov     esi, eax
mBC:004010A2                 test    esi, esi
mBC:004010A4                 jnz     short loc_40107A
;****************************

mBC:004010AF                 mov     dl, byte ptr [ebp+ecx+var_C]      ;把轉換後的資料移動到輸出緩衝區
mBC:004010B3                 mov     ds:String[eax], dl
mBC:004010B9                 inc     eax
mBC:004010BA                 dec     ecx
mBC:004010BB                 jns     short loc_4010AF

;在視窗控制程式碼輸出緩衝區最後新增“(H)”
mBC:004010BD                 mov     ds:String[eax], 28h        ;28h為“(”的ASCII碼
mBC:004010C4                 mov     ds:byte_401239[eax], 48h    ;48h為“H”的ASCII碼
mBC:004010CB                 mov     ds:byte_40123A[eax], 29h    ;29h為“)”的ASCII碼
mBC:004010D2                 and     ds:byte_40123B[eax], 0

現在看來這個函式 sub_401069 就是把獲取得視窗轉換成可以讓使用者看的資料,然後在後面新增“(H)”,表示是16進位制。
我翻譯一下:

      xor  ecx, ecx
      mov  esi, offset szString1    
    @@:               ;這個小迴圈用來把控制程式碼數值轉換成字串,存放在szString1,不過存放的數值是倒的
      test  eax, eax
      jz    @lExit
      xor  edx, edx
      mov  edi, 10h
      idiv  edi
      .if    dl >  9
        add  dl, 37h
      .else
        add  dl, 30h
      .endif
      mov  byte ptr [esi], dl
      inc  esi
      inc  ecx
      test  eax, eax
      jnz  @B

    @lExit:
      mov  edi, offset szString2
      dec  esi
    @@:              ;把倒過來的字串再修正回來,放在szString2
      mov  al, BYTE ptr [esi]
      mov  BYTE ptr [edi], al
      dec  ecx
      dec  esi
      inc  edi
      test  ecx, ecx
      jnz  @B
      
      mov     BYTE ptr [edi ], 28h      ;填寫控制程式碼字串最後的"(H)"
      mov     BYTE ptr [edi+1], 48h
      mov     BYTE ptr [edi +2 ], 29h
      mov     BYTE ptr [edi +3 ], 00

寫的比較垃圾,湊和用吧。

看下一個訊息:

mBC:0040110A                 sub     eax, 0EEh
mBC:0040110F                 jnz     loc_40120D

WM_TIMER = 113h + 0EEh,估計是WM_LBUTTONDOWN。

而loc_40120D:
xor     eax, eax
ret

其實就是訊息case的返回。

由於這裡是jnz,就是說如果不是WM_LBUTTONDOWN,就返回。

結合上面的程式碼,WM_LBUTTONDOWN是最後一條需要專門處理的訊息。處理如下:

mBC:00401115                 call    ds:ReleaseCapture
mBC:0040111B                 push    0               ; lParam
mBC:0040111D                 push    2               ; wParam
mBC:0040111F                 push    0A1h            ; Msg
mBC:00401124                 push    [ebp+hDlg]      ; hWnd
mBC:00401127                 call    ds:SendMessageA

解釋一下,就是當本視窗發生滑鼠事件時候,不接受滑鼠的位置(減輕程式的處理負擔)。

具體翻譯參考以下程式碼就行。

總結一下:

總體來說,程式流程規整,便於分析。但是也存在一些問題,比如:

1、他這裡每隔300毫秒,利用滑鼠api和控制程式碼api來檢測視窗控制程式碼和類,雖然程式實現比較容易,但是效率不高,一般來說應該用hook攔截滑鼠訊息。
2、另外,多處理了WM_COMMAND訊息(或許有用)。
3、對字串的處理(:0040107A),沒有處理好視窗控制程式碼,因為存在本程式控制程式碼和獲取的視窗控制程式碼,最好多新增一個hWnd變數,用起來也方便。

我這裡程式碼寫的比較爛,時間倉促,自己也太垃圾,不足之處多包涵。

附程式程式碼:


資原始檔:

“Main.rc”檔案:

#define DLG_MAIN 101
#include "resource.h"

DLG_MAIN DIALOGEX 0, 0, 181, 53
CAPTION "WinClass&HWND+viEw*pwd"
STYLE DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
BEGIN
   CONTROL "", 1000, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP | WS_TABSTOP, 41, 13, 122, 12 
   CONTROL "", 1005, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 40, 31, 91, 12 
   CONTROL "Win.Class:", 1037, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 13, 35, 8 , 0x00000020
   CONTROL "HWND:", -1, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 8, 31, 29, 9 
   CONTROL "", -1, BUTTON, BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 2, 0, 174, 47 
   CONTROL "cOde:dRag0nMa", -1, STATIC, SS_RIGHT | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP | WS_TABSTOP, 113, 41, 58, 8 
END
IDM_MAIN MENUEX
BEGIN
    MENUITEM "",,0x00000800
END


“Main.asm”檔案:

.386
.model flat, stdcall
option casemap :none

;---------------------------------------------------------------------------
include    nbwFun.inc

_ProcDlgMain  PROTO :DWORD,:DWORD,:DWORD,:DWORD

;---------------------------------------------------------------------------
.data
  
  lRect    RECT <> 
  lPoint  POINT <>
  szClassName  db  16 dup(?)

.data?
  lhWnd      dd  ?
  szString1  db  16 dup(?)
  szString2  db  16 dup(?)

.code

start:

  push    1
  call    GetCommandLineA
  push    eax
  push    0
  push    0               ; lpModuleName
  call    GetModuleHandleA
  push    0               ; dwInitParam
  push    offset _ProcDlgMain ; lpDialogFunc
  push    0               ; hWndParent
  push    65h        ; lpTemplateName
  push    eax             ; hInstance
  call    DialogBoxParam  ; Create a modal dialog box

  push    NULL
  call    ExitProcess

;---------------------------------------------------------------------------

_ProcDlgMain  proc  uses ebx edi esi hWnd,wMsg,wParam,lParam
  mov  eax,wMsg
  .if  eax == WM_CLOSE
    invoke  EndDialog,hWnd,NULL
  .elseif  eax == WM_INITDIALOG
      push    0               ; lpTimerFunc
      push    12Ch            ; uElapse
      push    1               ; nIDEvent
      push    hWnd
      call    SetTimer
      push    offset lRect     ; lpRect
      push    hWnd
      call    GetWindowRect
      push    41h             ; uFlags
      push    lRect.bottom  ; cy
      push    lRect.right   ; cx
      push    lRect.top     ; Y
      push    lRect.left    ; X
      push    0FFFFFFFFh    ; hWndInsertAfter = HWND_TOPMOST
      push    hWnd
      call    SetWindowPos
      push    1
      pop     eax
      ret
;  .elseif  eax == WM_COMMAND    ;原來軟體中對此進行處理。我這裡註釋掉了
;      mov  eax, TRUE
;      ret
  .elseif  eax == WM_TIMER
      push    offset lPoint    ; lpPoint
      call    GetCursorPos
      push    lPoint.y
      push    lPoint.x      ; Point
      call    WindowFromPoint ; Get handle of the window that

      push    eax       ;獲取的視窗控制程式碼
      pop    lhWnd       ;儲存,供下一步應用

      xor  ecx, ecx
      mov  esi, offset szString1    
    @@:               ;這個小迴圈用來把控制程式碼數值轉換成字串,存放在szString1,不過存放的數值是倒的
      test  eax, eax
      jz    @lExit
      xor  edx, edx
      mov  edi, 10h
      idiv  edi
      .if    dl >  9
        add  dl, 37h
      .else
        add  dl, 30h
      .endif
      mov  byte ptr [esi], dl
      inc  esi
      inc  ecx
      test  eax, eax
      jnz  @B

    @lExit:
      mov  edi, offset szString2
      dec  esi
    @@:              ;把倒過來的字串再修正回來,放在szString2
      mov  al, BYTE ptr [esi]
      mov  BYTE ptr [edi], al
      dec  ecx
      dec  esi
      inc  edi
      test  ecx, ecx
      jnz  @B
      
      mov     BYTE ptr [edi ], 28h      ;填寫控制程式碼字串最後的"(H)"
      mov     BYTE ptr [edi+1], 48h
      mov     BYTE ptr [edi +2 ], 29h
      mov     BYTE ptr [edi +3 ], 00

      push    offset szString2
      push    3EDh
      push    hWnd
      call    SetDlgItemText

      mov     edi, offset szClassName
      push    10h             ; nMaxCount
      push    edi             ; lpClassName
      push    lhWnd         ; hWnd
      call    GetClassName
      mov     edi, offset szClassName
      push    edi             ; lpString
      push    3E8h            ; nIDDlgItem
      push    hWnd
      call    SetDlgItemText          ; SetDlgItemTextA
      push    0               ; lParam
      push    0               ; wParam
      push    0CCh            ; Msg
      push    hWnd
      call    PostMessageA

  .elseif  eax == WM_LBUTTONDOWN

      call    ReleaseCapture
      push    0               ; lParam
      push    2               ; wParam
      push    0A1h            ; Msg
      push    hWnd
      call    SendMessageA

  .else
    mov  eax,FALSE
    ret
  .endif
  xor  eax, eax
  ret
_ProcDlgMain  endp
;---------------------------------------------------------------------------

end  start

   編譯成功後4.5k,比原來軟體12k稍微小點

相關文章