一款工具的逆向分析與再實現
一款工具的逆向分析與再實現
作者: 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稍微小點
相關文章
- FoxMail 本地密碼破解(提取) ,逆向分析與實現2019-02-02AI密碼
- 逆向一款收費版的開發工具2018-11-17
- CPython逆向實戰分析2024-10-25Python
- 富集分析的原理與實現2021-10-29
- 再見PowerDesigner!一款現代化的資料庫設計工具,介面夠炫酷!2024-10-28資料庫
- 逆向入門分析實戰(二)2020-04-24
- NETPLIER : 一款基於概率的網路協議逆向工具(一)理論2022-03-19協議
- Zepto 原始碼分析 3 - qsa 實現與工具函式設計2018-11-19原始碼函式
- Redux流程分析與實現2019-03-04Redux
- 如何實現一款好用的雲圖(word-cloud)生成工具?2021-07-06Cloud
- Python實現微博輿情分析的設計與實現2024-10-25Python
- 病毒逆向分析2018-03-18
- redux簡單實現與分析2018-04-12Redux
- 一款實用的前端截圖工具2018-07-03前端
- Linux 下的逆向分析-初探2022-06-26Linux
- 逆向實戰33——某東登入引數與流程分析(包含滑塊)2024-03-08
- 【JS逆向百例】cebupacificair 航空逆向分析2024-11-18JSAI
- 一款資料庫比較與同步軟體的設計與實現2019-03-12資料庫
- CreateProcess逆向分析-3環使用者層逆向分析(一)2022-03-11
- octavia的實現與分析(二)·原理,架構與基本流程2019-07-18架構
- 推薦一款超實用的皮膚工具,快速實現一鍵環境部署!2019-12-12
- Android逆向分析概述2018-10-10Android
- HashMap 實現原理與原始碼分析2019-04-26HashMap原始碼
- 一道關於逆向的實戰CTF題目分析2024-07-12
- 使用 Frida 逆向分析 Android 應用與 BLE 裝置的通訊2018-03-13Android
- 真實場景再現2019-05-11
- 逆向工具 Cycript安裝2018-05-17
- Laravel migration 逆向生成工具2020-02-28Laravel
- SeeRedis,一款實用的redis管理工具2019-01-24Redis
- WiFiAp探究實錄--功能實現與原始碼分析2024-08-04WiFi原始碼
- FFT演算法實現與分析MATLAB2020-11-28FFT演算法Matlab
- ThinkPHP 6.0 管道模式與中介軟體的實現分析2019-10-31PHP模式
- switch語句逆向分析2024-10-18
- ELF檔案逆向分析2020-12-26
- 一款基於 Java 開發的微信資料分析工具!2024-11-18Java
- React原始碼分析與實現(一):元件的初始化與渲染2018-08-27React原始碼元件
- 再見Jenkins!一款更適合國人的自動化部署工具,賊帶勁!2024-11-26Jenkins
- 再記一次經典Net程式的逆向過程2019-08-03
- Alist 與 ssfrp工具實現私人小影院2024-05-28FRP