標籤:提示 工作 傳回值 cal param register .net sha ack
【文章標題】: 用組合語言給XP記事本添加“自動儲存”功能
【文章作者】: newjueqi
【作者郵箱】:[email protected]
【作者QQ】:190678908
【使用工具】: OD, LordPE,eXeScope
【操作平台】: XP-SP2
【作者聲明】: 本人平時一般的文文書處理都是用記事本(用Word好像大材小用了),電腦自從拿去大修後有時候會莫名其妙的重啟,弄得經常重寫(本人常常忘記儲存^-^),於是想給記事本增加類似於Word的自動儲存功能,以圖個方便。失誤之處敬請諸位大俠賜教!
具體的思路:其實自動儲存功能就是程式隔一段時間儲存一次,而記事本程式是內建了儲存功能的,所以只要編程安裝一個記時器,每隔一段時間類比點擊“儲存菜單”,就能實現自動儲存,最後退出時把記時器撤銷。
首先,用eXeScope給記事本增加兩個功能表項目“自動儲存”“關閉自動儲存”,1所示:
圖1
其中,“自動儲存”的菜單ID是8 ,“關閉自動儲存” 的菜單ID是13。
完成後的菜單選項2所示:
圖2
本次修改要用到3個window API函數: SetTimer, KillTimer , SendMessageA, 這3個API沒包括在記事本的輸入表裡,所以要用LordPE在輸入表裡增加這3個API。
用LordPE開啟記事本程式,依次選擇Direction->輸入表的“…”按鈕->點擊右鍵->add import,出現3的介面,就可以在輸入表裡增加新的API
圖3
完成後就新增了一個區段Slivana4,放的就是新的IID資料,4所示
圖4
添加完新的API後又有一個新的問題了,那就是要確定新的API地址,這個在PEDIY的過程中是要自己解決的,而平時是編譯器幫忙的。獲得API地址有兩個方法:
(1) 直接查看新增的IID資料,載入程式後在命令列打入d 1013000,5所示:
圖5
從IAT與INT的對應關係可得
SetTimer的地址=[0101302C ]
MessageBoxA的地址=[ 01013030]
KillTimer的地址=[01013034 ]
(2) 在OD中,按Ctrl+N按鍵組合,就能看到API地址的位置,6的紅框部分所示
圖6
由於PEDIY一般都是在預設的視窗過程前先處理我們定義的訊息,所以接下來分析一下記事本程式的流程,看看記事本的視窗過程地址。眾所周知,在一個標準的視窗程序中,必須要註冊視窗類別,函數原型如下:
ATON RegisterClassEx(CONST WNDCLASSEX *Ipwcx);
其中WNDCLASSEX *Ipwcx是個結構體指標,定義如下:
WNDCLASSEX STRUCT DWORD
cbSize DWORD ?
style DWORD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
hIconSm DWORD ?
WNDCLASSEX ENDS
lpfnWndProc就是視窗過程的地址,所以我們只需要在RegisterClassEx下中斷,就能知道視窗過程的地址,在OD中7所示:
圖7
從圖7可看到1003429就是系統預設的視窗過程的地址。
在開始寫代碼前我們先簡單回顧一下的windows的視窗過程函數和一般函數調用的過程,因為我們在後面的實際編碼過程中必須要對這兩個方面的知識有一定的瞭解。
視窗過程的原型為
wondowPrco proc hwnd,uMsg, wParam, lParam
hwnd: 視窗的控制代碼,可通告CreateWindowEx函數的傳回值擷取
uMsg: 訊息的標識,一般以WM開頭
wParam, lParam: 訊息的兩個參數
當我們點擊某個菜單後就產生了WM_COMMAND訊息,對於菜單引發的的WM_COMMAND訊息, lParam 的值為0,wParam的低16位是命令ID,高16位是通知碼,菜單訊息的通知碼是0。
當調用一個_stdcall函數的時候,首先是函數的參數按從右至左的順序入棧,然後是函數的返回地址入棧,一般函數調用的開始有“push ebp mov ebp,esp”這句,所以我們進入視窗過程後就能得到類似於圖8的堆棧結構
圖8
0006FDE0 /0006FE0C ; ebp的值
0006FDE4 |77D18734 ; 返回到 user32.77D18734,返回地址值
0006FDE8 |00100618 ;視窗的控制代碼
0006FDEC |00000111 ;WM_COMMAND的16進位值,可查閱WinUser.h檔案獲得
0006FDF0 |00000008 ;wParam,“自動儲存”的菜單ID是8
0006FDF4 |00000000 ;lParam,
由此可知,[ebp+c]是訊息的類型,[ebp+10]是wParam參數,擷取菜單的
ID就需要知道wParam參數。
另外還有一個問題要解決,SetTimer和KillTimer這兩個API函數的參數中必須要傳入視窗控制代碼作為參數,但視窗控制代碼怎麼找呢?我們知道,在用CreateWindowExW函數建立視窗後會返回一個視窗控制代碼,在OD裡對CreateWindowExW下斷點,中斷後代碼9:
圖9
從紅框部分可知,視窗控制代碼儲存在地址[1009830]。
我們要類比點擊“儲存”菜單,可以通過SendMessageW函數向視窗過程發送菜單訊息,SendMessageW的原型如下:
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
參數:
hWnd就是視窗控制代碼,從上面分析可知,儲存在[1009830]地址上。
Msg是要發送的訊息類型,查閱WinUser.h檔案可知,WM_COMMAND為0x111
wParam的低16位是命令ID,高16位是通知碼,菜單訊息的通知碼是0,通過eXeScope可查知“儲存”的菜單ID 為3,所以wParam參數為3。
lParam,對於菜單訊息來說,為0。
接下來就是實際的代碼,本人選擇在0100874F處添加代碼,所以要把圖7紅框部分的1003429改為0100874F。
下面是寫入的代碼
0100874F . 55 push ebp
01008750 . 8BEC mov ebp, esp
01008752 . 60 pushad
01008753 . 817D 0C 11010>cmp dword ptr [ebp+C], 111 ;先比較訊息的類型,看是否WM_COMMAND, 如果是WM_COMMAND的話再檢查wParam參數進一步確定是哪個菜單的訊息
0100875A . 75 0C jnz short 01008768 ;如果不是WM_COMMAND訊息的話就檢查是否WM_TIMER或WM_CLOSE訊息
0100875C . 837D 10 08 cmp dword ptr [ebp+10], 8 ;比較菜單的ID號,看是否“自動儲存”的菜單ID
01008760 . 74 1C je short 0100877E ;不是的話就跳去檢查其它的訊息類型
01008762 . 837D 10 0D cmp dword ptr [ebp+10], 0D ;比較菜單的ID號,看是否“關閉自動儲存”的菜單ID
01008766 . 74 48 je short 010087B0 ;不是的話就跳去檢查其它的訊息類型
01008768 > 817D 0C 13010>cmp dword ptr [ebp+C], 113 ;比較訊息的類型是否是WM_TIMER訊息,響應定時器訊息
0100876F . 74 5F je short 010087D0 ;不是的話就跳去函數的結束清場工作
01008771 . 837D 0C 10 cmp dword ptr [ebp+C], 10 ;比較訊息的類型是否是WM_CLOSE訊息,閉關時要先撤銷定時器
01008775 . 74 39 je short 010087B0 ;不是的話就跳去函數的結束清場工作
01008777 > 61 popad
01008778 . 5D pop ebp
01008779 .^ E9 ABACFFFF jmp 01003429 ;跳回原來記事本的訊息迴圈
0100877E > C605 20890001>mov byte ptr [1008920], 1 ;標記已使用自動儲存功能
01008785 . 6A 00 push 0 ; /Timerproc = NULL
01008787 . 68 60EA0000 push 0EA60 ; |Timeout = 60000. ms
0100878C . 6A 01 push 1 ; |TimerID = 1
0100878E . FF35 30980001 push dword ptr [1009830] ; |hWnd = [1009830],在CreateWindowExW後儲存視窗控制代碼的地址
01008794 . FF15 2C300101 call dword ptr [<&user32.SetTimer>] ; /SetTimer
0100879A . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
0100879C . 68 00DB0001 push 0100DB00 ; |Title = "Note"
010087A1 . 68 10DB0001 push 0100DB10 ; |Text = "AutoSave Start!"
010087A6 . 6A 00 push 0 ; |hOwner = NULL
010087A8 . FF15 30300101 call dword ptr [<&user32.MessageBoxA>>; /MessageBoxA ;提示已使用自動儲存功能
010087AE .^ EB C7 jmp short 01008777 ;跳去函數結束清場
010087B0 > 803D 20890001>cmp byte ptr [1008920], 0 ;先比較是否已啟動定時器
010087B7 .^ 74 BE je short 01008777 ;如果沒啟動定時器就跳去函數結束清場
010087B9 . C605 20890001>mov byte ptr [1008920], 0 ;把啟動定時器的標記恢複
010087C0 . 6A 01 push 1 ; /TimerID = 1
010087C2 . FF35 30980001 push dword ptr [1009830] ; |hWnd,在CreateWindowExW後儲存視窗控制代碼的地址
010087C8 . FF15 34300101 call dword ptr [<&user32.KillTimer>] ; /KillTimer,撤銷定時器
010087CE .^ EB A7 jmp short 01008777 ;跳去函數結束清場
010087D0 > 6A 00 push 0 ; /lParam = 0
010087D2 . 6A 03 push 3 ; |wParam = 3 ,“儲存”菜單的ID號
010087D4 . 68 11010000 push 111 ; |Message = WM_COMMAND
010087D9 . FF35 30980001 push dword ptr [1009830] ; |hWnd ,在CreateWindowExW後儲存視窗控制代碼的地址
010087DF . FF15 40120001 call dword ptr [<&USER32.SendMessageW>; /SendMessageW ;發送點擊菜單“儲存”的訊號給記事本視窗,實現儲存功能。
010087E5 .^ EB 90 jmp short 01008777 ;跳去函數結束清場
下載修改好的記事本地址
http://download.csdn.NET/source/1109397
http://blog.csdn.net/newjueqi/article/details/3992084
用組合語言給XP記事本添加“自動儲存”功能 good