請求執行時間段與Shell函式(轉)

heying1229發表於2007-07-28
請求執行時間段與Shell函式:

  請求執行時間段通常被簡稱為"appy time"。是指當系統VM穩定到充許VxDs和ring-3級別的應用軟體(特別是16-bit的應用軟體)互動時的時間段。例如,在一個特定時間段,VxDs能載入並呼叫在16-bit Dlls中的函式。這個appy time在Windows 3.x中是無效的。在Windows3.x,一個VxD能包含在16-bit DLLs中的任意函式的地址,並模擬一個遠呼叫到這個地址。然而,因為造成了VMM重入,這個操作將中斷所有正在ring-3中執行的任務。所以能被VxDs能呼叫的APIs被要求是中斷的,象PostMessage。在Windows 95,一個VxD在appy time的幫助下能呼叫任意一個在16-bit DLLs中的函式。

假如你的VxDs被通知正處在appy time,它就能載入16-bit DLLs並呼叫其中的函式。VxDs怎麼知道appy time到來了呢?這就要使用Shell VxD請求一個appy time。當系統VM在穩定狀態,Shell VxD將呼叫某VxD的一個回撥函式,此函式是在VxD請求appy time時指定的。Shell VxD發生一次appy time事件僅僅呼叫一次你的回撥函式。這就象找工作。你到職業介紹所,登記你的名字和電話號碼。當你回到家,如有一個工作適合你,職業介紹所將電話通知你這個好訊息。當你收到了這個訊息,他們就不再通知你了。

在一個appy time起作用前要花上一些時間進行相關處理。appy time事件以下環境中將不起作用:

1、系統啟動或關機時。
2、當系統VM在臨界段或等待一個訊號量時。

管理一個appy time事件

你可以透過呼叫_SHELL_CallAtAppyTime來註冊一個appy time事件,它的定義如下:

VxDCall _SHELL_CallAtAppyTime, <, dwRefData, dwFlags, dwTimeout>

pfnCallBack -- 當appy time事件發生時你要Shell VxD呼叫的回撥函式的平板地址。這個函式接收兩個引數,dwRefData和dwFlags,與你傳送給_SHELL_CallAtAppyTime的兩個一樣。記住,Shell VxD採用C呼叫順序呼叫你的回撥函式。總而言之,你要象這樣定義你的回撥函式:

BeginProc OnAppyTime, CCALL, PUBLIC
ArgVar dwRefData,DWORD ; declare argument name and type
ArgVar dwFlags, DWORD
EnterProc
你的程式碼...
LeaveProc
Return
EndProc OnAppyTime
dwRefData -- 你要Shell VxD傳送給你的回撥函式的參考資料。可以是你想要的任何東西。
dwFlags -- 事件標誌。

如下值之一:
CAAFL_RING0 ring-0事件
CAAFL_TIMEOUT 由dwTimeout指定的時間到期事件。

假如你只想在一定的時間內等待appy time事件,使用CAAFL_TIMEOUT標誌。如你想一直等待appy time事件,使用NULL。CAAFL_RING0作用不明。
dwTimeout -- 在appy time事件發生前,VxD能等待時間段長度。時間段的單位不明。
這個服務是非同步的,意味著你為appy time事件註冊回撥函式之後立即返回。

如果這個服務呼叫是成功的,在eax中返回appy time事件控制程式碼。假如呼叫失敗,在eax返回0。

你可以呼叫_SHELL_CancelAppyTimeEvent來撤消appy time事件註冊,它僅有一個引數,就是由_SHELL_CallAtAppyTime返回的appy time事件控制程式碼。

你應當在appy time事件到來時檢查系統。例如,當你在系統關閉時註冊appy time會發生什麼?你的VxD的回撥函式將不會得到呼叫!當appy time事件到來時,你應當呼叫_SHELL_QueryAppyTimeAvailable來查詢系統狀態。這個服務沒有引數。如果appy time無效,在eax中返回0,例如:當系統關閉時或訊息服務程式產生一個一般保護性錯誤時。

這個服務不會告訴你現在是不是appy time:它僅僅告訴你可能有一個appy time事件到來。簡而言之,如果你想執行,首先呼叫_SHELL_QueryAppyTimeAvailable並檢查eax中的值是否非零值,然後才可繼續呼叫_SHELL_CallAtAppyTime。

請求執行時間段Shell服務

當appy time到達時,你可以使用幾個Shell服務呼叫:

_SHELL_CallDll
_SHELL_FreeLibrary
_SHELL_GetProcAddress
_SHELL_LoadLibrary
_SHELL_LocalAllocEx
_SHELL_LocalFree
使用提供的這6個服務,VxDs可以呼叫在16-bit DLLs/EXE中的16-bit函式,象WinHelp。然而,我們馬上要進步到32-bit時代(未來是64-bit) ,所以我不會仔細研究它們。如你對此感興趣,你可以在Windows 95/98DDK文件中瞭解它們。

另外一些只請求執行時間段服務我想更有用處:_SHELL_ShellExecute和 _SHELL_BroadcastSystemMessage。使用_SHELL_BroadcastSystemMessage,你能在一次呼叫中傳送訊息到頂端的視窗和所有的VxDs。如果appy time有效,你可以傳送訊息到視窗和VxDs。如果appy time無效,你只能傳送訊息到VxDs。

_SHELL_ShellExecute是在ring-3中的函式ShellExecute在ring-0中的對應函式。實際上,它呼叫ring-3中的ShellExecute完成這個工作。使用外殼服務,你可以執行/開啟/列印任意檔案。

_SHELL_ShellExecute的定義如下所示:

VxDCall _SHELL_ShellExecute,

它僅有一個引數,SHEXPACKET結構的平板地址。它從ShellExecute函式中返回一個值到eax中。讓我們研究一下SHEXPACKET結構:

shex_dwTotalSize SHEXPACKET結構的位元組數加上可選的引數rgchBaggage的大小,它直接跟隨在這個結構之後。我等一下描述rgchBaggage。

shex_dwTotalSize SHEXPACKET結構的位元組數加上可選的引數rgchBaggage的大小,它直接跟隨在這個結構之後。我等一下描述rgchBaggage。
shex_dwSize SHEXPACKET結構的位元組數,不包括rgchBaggage。結合上面的shex_dwTotalSize值,外殼VxD能計算任意長度的rgchBaggage的大小。
shex_ibOp 你要完成的操作。假如你指定0,意味著你想要開啟一個檔案。如果是一個可執行檔案,就執行它。如果你想要完成其他的操作,你必須在rgchBaggage中指定操作的名字,在這個域中,必須包括從這個SHEXPACKET結構開始到一個ASCII字串的距離,此距離大小以位元組計數,字串指定你要完成的操作的名稱。SHEXPACKET結構的大小是32位元組。如果操作字串緊跟在SHEXPACKET結構後,shex_ibOp中的值必須是32。要知道你可以完成的操作,請檢視ShellExecute服務。有三個操作被定義,"open"、"print"和"explore"。
shex_ibFile 從此結構開始到一個ASCII字串的距離,這個字串是你想要傳遞給ShellExecute的檔名,就象Shex_ibOp成員。
shex_ibParams 你想傳遞到由shex_ibFile指定的檔案的可選引數。假如此檔案是一個文件檔案,你不想傳遞任何引數給它,就用0。假如你想傳遞引數給那個檔案,在把從此結構開始到此成員指定的字串的距離放在這個成員中。簡而言之,就象shex_ibOp和shex_ibFile成員。
shex_ibDir 工作目錄。假如你想使用Windows目錄就指定0,否則指定其為在此結構後的首選目錄名字串,並把從此結構開始到目錄名字串的距離值放到這個成員中。
shex_dwReserved 如其名字所指,它是保留的。不要理它。
shex_nCmdShow 應用程式視窗怎樣被顯示。這是一個你正常傳遞給ShowWindow的值,比如,SW_XXXX值。可在windows.inc中檢視這些值。
所有成員的大小都是雙字的。在這裡我介紹剛才我承諾的rgchBaggage成員。僅有一點不同,因為其大小是可變的,所以它作為SHEXPACKET結構的一員卻不能包括在此結構的定義中。檢視shell.inc,你看到rgchBaggage並不在SHEXPACKET結構的定義中,儘管在Windows 9x DDK文件中,宣告其是SHEXPACKET結構的一員。

rgchBaggage是什麼?簡單說來其是跟在SHEXPACKET結構後的一個字串陣列。在這個陣列中,你可以把你要對檔案完成的操作名稱、檔案的名字、你要傳遞給檔案的引數和工作目錄放在其中。首先得到從SHEXPACKET結構到這些字串的第一個字元之間的距離(即此結構的一些成員值),再把這些值加上SHEXPACKET結構的平板編移量,Shell VxD就能得到這些字串在rgchBaggage陣列中的偏移量。例如,假如SHEXPACKET結構從60000h開始,字串緊跟其後,結構與字串之間的距離是結構本身的大小,32位元組(20h)。所以Shell VxD知道字串定位在60020h。

例子

這是一個顯示如何註冊一個appy time事件並且使用_SHELL_ShellExecute的例子。VxD是動態的,被一個簡單的Win32應用程式使用。當一個使用者按下"run Calculator"按紐,win32應用程式呼叫DeviceIoControl去要求VxD註冊一個appy time事件並且執行在Windows目錄中的calc.exe。

;---------------------------------------------------------------------------------
; VxD Source Code
;---------------------------------------------------------------------------------
.386p
include masmincludevmm.inc
include masmincludevwin32.inc
include masmincludeshell.inc
VxDName TEXTEQU
ControlName TEXTEQU
VxDMajorVersion TEXTEQU <1>
VxDMinorVersion TEXTEQU <0>

VxD_STATIC_DATA_SEG
VxD_STATIC_DATA_ENDS

VXD_LOCKED_CODE_SEG
;----------------------------------------------------------------------------
; Remember: The name of the vxd MUST be uppercase else it won't work/unload
;----------------------------------------------------------------------------
DECLARE_VIRTUAL_DEVICE %VxDName,%VxDMajorVersion,%VxDMinorVersion, %ControlName,UNDEFINED_DEVICE_ID,UNDEFINED_INIT_ORDER

Begin_control_dispatch %VxDName
Control_Dispatch W32_DEVICEIOCONTROL, OnDeviceIoControl
End_control_dispatch %VxDName

BeginProc OnDeviceIoControl
assume esi:ptr DIOCParams
.if [esi].dwIoControlCode==1
VxDCall _SHELL_CallAtAppyTime,
.endif
xor eax,eax
ret
EndProc OnDeviceIoControl
VXD_LOCKED_CODE_ENDS

VXD_PAGEABLE_CODE_SEG
BeginProc OnAppyTime, CCALL
ArgVar RefData,DWORD
ArgVar TheFlag,DWORD
EnterProc
mov File.shex_dwTotalSize,sizeof SHEXPACKET
add File.shex_dwTotalSize,sizeof EXEName
mov File.shex_dwSize,sizeof SHEXPACKET
mov File.shex_ibOp,0
mov File.shex_ibFile,sizeof SHEXPACKET
mov File.shex_ibParams,0
mov File.shex_ibDir,0
mov File.shex_dwReserved,0
mov File.shex_nCmdShow,1
VxDCall _SHELL_ShellExecute,
LeaveProc
Return
EndProc OnAppyTime
VXD_PAGEABLE_CODE_ENDS

VXD_PAGEABLE_DATA_SEG
File SHEXPACKET <>
EXEName db "calc.exe",0
VXD_PAGEABLE_DATA_ENDS

end

講解

VxD等待一個DeviceIoControl訊息:第1號服務。當它收到上述訊息,它將註冊一個appy time事件。

VxDCall _SHELL_CallAtAppyTime,<,0,0,0>

它將傳送OnAppyTime函式的平板地址到_SHELL_CallAtAppyTime,這樣,當appy time事件發生時,Shell VxD將呼叫它。因我們不需要使用任何參考資料並且不需要處理過期情況,所以緊跟OnAppyTime函式的三個引數都是0。

當appy time事件發生時, Shell VxD呼叫OnAppyTime函式。

BeginProc OnAppyTime, CCALL

我們用BeginProc描述一個函式。因為Shell VxD用C呼叫順序呼叫OnAppyTime,我們需要指定CCALL屬性。

ArgVar RefData,DWORD
ArgVar TheFlag,DWORD
EnterProc
...
LeaveProc
Return

因為Shell VxD用兩個引數呼叫OnAppyTime,我們必須設定堆疊結構。ArgVar宏用來調整每個要傳遞到函式的引數的堆疊結構。它的語法如下:

ArgVar varname, size, used

varname是引數的名字。你可以使用你喜歡的任意名字。size是引數的大小。你可以使用BYTE、WORD、DWORD或1,2,4。used通常被忽略。

緊接著ArgVar宏,我們需要使用EnterProc和LeaveProc宏來標誌在程式中存放變數和引數的結構的開始與結束,使其能被正確訪問。使用Return宏返回到呼叫者。

mov File.shex_dwTotalSize,sizeof SHEXPACKET
add File.shex_dwTotalSize,sizeof EXEName
mov File.shex_dwSize,sizeof SHEXPACKET
mov File.shex_ibOp,0
mov File.shex_ibFile,sizeof SHEXPACKET
mov File.shex_ibParams,0
mov File.shex_ibDir,0
mov File.shex_dwReserved,0
mov File.shex_nCmdShow,1
VxDCall _SHELL_ShellExecute,

在這個程式中的指令是簡單的:初始化SHEXPACKET結構並且呼叫_SHELL_ShellExecute服務。記住 shex_dwTotalSize包含SHEXPACKET結構自身的和跟著它的字串的組合大小。這是一個簡單的事情。假如字串不緊跟在此結構之後的,你必須計算從結構第一個位元組到到字串最後一個位元組之間的距離。shex_ibFile包含此結構自身的大小,因為程式名緊跟在此結構之後。shex_ibDir是0,意味著把Windows目錄作為工作目錄。這並不意味著程式必須在Windows目錄中。程式可以在Windows能找到的任意地方。

shex_nCmdShow是1,即SW_SHOWNORMAL的值。

File SHEXPACKET <>
EXEName db "calc.exe",0

我們定義一個SHEXPACKET結構,其後緊跟著想要執行的程式名。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928816/,如需轉載,請註明出處,否則將追究法律責任。

相關文章