記錄外殼的活動
記錄外殼活動有很多好處 比如當需要監控用戶的行為 回溯系統崩潰前的過程 實現這壹功能的關鍵工具相當簡單 它就是接口IShellExecuteHook 編寫壹個實現了這壹接口的對象後 再在系統中註冊 就可以容易地控制並影響Windows外殼的運行 Windows 和Windows 都支持IShellExecuteHook外殼擴展 而在Windows 和Windows NT 上則必須安裝活動桌面擴展後才支持(也就是說必須安裝IE )
壹個實現了IShellExecuteHook接口的對象可以截獲所有對ShellExecute和ShellExecuteEx函數的調用 ShellExecute和ShellExecuteEx函數主要用於執行應用程序 它們可以接收壹個文件名並能自動獲得同文件名相關的可執行文件名 此外 它們還支持系統安全認證 如果在NT上設定了用戶的可執行權限 ShellExecute和ShellExecuteEx函數將會在創建新的進程前檢查權限(CreateProcess和WinExec函數則沒有這項功能) 函數調用的流程如下
( )獲得將要運行的可執行文件名
( )根據程序名檢查用戶執行權限
( )激活全部已註冊的IshellExecuteHook擴展
( )當所有擴展和權限都同意執行 創建新的進程並返回
Windows外殼大量調用ShellExecute和ShellExecuteEx函數來執行幾乎是所有的資源管理器的操作 比如雙擊目錄 瀏覽文件夾內容 打印編輯文檔 查看文件屬性 選擇文檔的上下文相關菜單等等 此外 開始菜單的運行對話框和DOS方式下的Start exe也使用ShellExecuteEx函數來執行程序 簡單地說幾乎用戶的所有外殼操作都可以被擴展截獲 包括其他應用程序對ShellExecute和ShellExecteEx的調用
編寫外殼活動記錄器
首先需要創建壹個進程內對象 選菜單命令New | ActiveX Library 然後點擊菜單New|Com Object 創建對象框架 按圖 填充對話框的內容 然後點擊OK按鈕 Delphi就會自動生成框架文件 並保存生成的文件
IShellExecuteHook的接口定義在shlobj pas單元中 添加shlobj到單元uses部分 然後添加IShellExecuteHooko方法原型到對象聲明部分 聲明部分代碼如下
unit ShellExecuteHookObj;
interface
uses
Windows ActiveX ComObj ShlObj ShellAPI;
type
TTShellExecuteHook = class (TComObject IShellExecuteHook)
protected
function Execute(var ShellExecuteInfo: TShellExecuteInfo): HResult; stdcall;
end;
const
Class_TShellExecuteHook: TGUID = { FA D D B E B AE BE } ;
下面就是用來截獲並記錄外殼操作的實現部分 壹旦外殼擴展被註冊後 每次ShellExecute 和ShellExecuteEx函數運行時都會調用對象的Execute函數 我們的核心代碼就是通過Execute方法實現的 方法定義如下
function TTShellExecuteHook Execute(var ShellExecuteInfo: TShellExecuteInfo): HResult;
Execute方法會從外殼獲得壹個類型為TshellExecuteInfo的參數 參數定義如下
_SHELLEXECUTEINFOA = record
cbSize: DWORD;
fMask: ULONG;
Wnd: HWND;
lpVerb: PAnsiChar;
lpFile: PAnsiChar;
lpParameters: PAnsiChar;
lpDirectory: PAnsiChar;
nShow: Integer;
hInstApp: HINST;
{ Optional fields }
lpIDList: Pointer;
lpClass: PAnsiChar;
eyClass: HKEY;
dwHotKey: DWORD;
hIcon: THandle;
hProcess: THandle;
end;
這個記錄結構中的lpFile包含了要運行的文件名 而lpVerb則表明執行的動作 動作由壹些標準的字符串代表 比如 open(打開) print(打印) edit(編輯) explore(瀏覽) properties(屬性) find(查找)和其他上下文菜單的命令名 有時 lpFile並不包含可執行文件名 這是因為ShellExecute接到的運行參數是壹個文檔名 比如當我們在資源管理器中雙擊文本文件時 Windows用文本文件名作為參數調用ShellExecute函數 而ShellExecute函數則獲得同文本文件相關聯的可執行文件名 然後執行
TShellExecuteInfo結構中還記錄了要運行程序的很多信息 然而這裏我們只能在Execute方法中修改nCmdShow參數 nCmdShow參數定義了窗口在運行後的顯示狀態 包括最大化 最小化 正常等選項 對於其他參數的修改都會被外殼忽略 除此之外 在Execute方法中可以根據情況允許外殼繼續缺省的任務或通知外殼取消執行 這可以通過Execute函數的返回值來實現
如果Execute的返回值為S_FALSE 外殼就繼續缺省的任務 如果返回S_OK 則外殼認為擴展已經成功 就不再繼續執行了 另外如果返回壹個錯誤代碼或系統無法識別的值 則外殼會彈出錯誤信息 這給了我們壹個控制程序運行的機會 比如可以限制任何對記事本的調用 代碼如下
function TTShellExecuteHook Execute(var ShellExecuteInfo: TShellExecuteInfo): HResult;
var
FileName: String;
begin
Result := S_FALSE;
with ShellExecuteInfo do
begin
FileName := UpperCase(ExtractFileName(lpFile));
if Pos( NOTEPAD FileName) = then
begin
Result := S_OK;
hInstApp := ;
MessageBox(Wnd 不允許記事本運行! 錯誤 MB_OK or MB_ICONERROR);
end;
end;
end;
進壹步 我們甚至可以利用這點實現壹個自定義的安全認證機制 根據用戶要求限制運行的程序 有興趣的朋友可以試驗壹下 壹定很有意思
有壹點要註意的是 在Execute方法下不能調用ShellExecute和ShellExecuteEx函數外部程序 如果是這樣的話 我們的Execute方法又會被新的ShellExecute調用 這樣系統就會進入死循環 如果我們確實想在Execute方法中調用外部程序的話 可以使用CreateProcess或WinExec函數來替代 這兩個函數不會被ShellExecuteHook截獲
對於外殼動作記錄器來說 只要在Execute方法中記錄程序信息到日誌文件中就可以了 代碼非常簡單 因為所有需要的信息都在TShellExecuteInfo記錄中包含了 這裏只記錄運行的動作 文件名和時間 需要記錄其他信息的話 大家可自行修改 代碼示意如下
function TTShellExecuteHook Execute(var ShellExecuteInfo: TShellExecuteInfo): HResult;
var
FileStream: TFileStream;
a:TStringList;
S:string;
begin
Result := S_FALSE;
with ShellExecuteInfo do
begin
FileStream:=TFileStream Create( c:\shellexecutehook txt fmopenwrite);
S:=string(lpVerb)+ : +string(lpFile)+DateTimeToStr(Now)+# # ;
FileStream Seek(FileStream Size soFromBeginning);
FileStream Write(PChar(S)^ Length(S));
FileStream Free;
end;
end;
註冊ShellExecuteHook
要想使對象被外殼加載 需要在註冊表中註冊壹些信息 在下面這個子鍵中添加類的GUID及描述字符串後就可以了(描述字符串可以不賦值 但不妨給壹個以便於識別)
HKEY_LOCAL_MACHINE
SOFARE
Microsoft
Windows
CurrentVersion
Explorer
ShellExecuteHooks
{CLSID}= 描述字符串
修改註冊表可以通過重載的類工廠的UpdateRegistry方法來實現 代碼示意如下
implementation
uses ComServ SysUtils;
resourcestring
sCreateRegKeyError = 創建註冊表項失敗 ;
type
TShellExComObjectFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
{ TShellExComObjectFactory }
procedure TShellExComObjectFactory UpdateRegistry(Register: Boolean);
const
hellExecuteHooksKey= SOFARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks ;
var
Handle: HKey;
Status Disposition: Integer;
ClassID: String;
begin
ClassID := GUIDToString(Class_TShellExecuteHook);
if Register then
begin
Status := RegCreateKeyEx(HKEY_LOCAL_MACHINE PChar(ShellExecuteHooksKey) REG_OPTION_NON_VOLATILE KEY_READ or KEY_WRITE nil Handle @Disposition);
if Status = then
begin
Status := RegSetValueEx(Handle PChar(ClassID) REG_SZ
PChar(Description) Length(Description) + );
RegCloseKey(Handle);
end;
end else
begin
Status := RegOpenKeyEx(HKEY_LOCAL_MACHINE PChar(ShellExecuteHooksKey)
KEY_READ or KEY_WRITE Handle);
if Status = then
begin
Status := RegDeleteValue(Handle PChar(ClassID));
RegCloseKey(Handle);
end;
end;
if Status <> then raise EOleError Create(sCreateRegKeyError);
inherited UpdateRegistry(Register);
end;
initialization
TShellExComObjectFactory Create(ComServer TTShellExecuteHook Class_TShellExecuteHook TShellExecuteHook ShellExecute hook sample ciMultiInstance tmApartment);
end 如果系統中有多個ShellExecuteHook的話 外殼會按照ShellExecuteHook的安裝順序進行調用 如果要想使某個外殼擴展優先運行 可先刪除其他擴展然後添加優先擴展 原來的擴展依次放在後面 不過這樣做也可能意義不大 因為別人也會這麽幹 最後 程序運行的結果
記住ShellExecuteHook並不是壹個完善的用於監視系統運行的擴展 它只能監視ShellExecute和ShellExecuteEx的運行 它不能保證記錄系統所有的行為 特別是很多情況下外殼並不使用ShellExecute來進行壹些常用的操作 比如我們在資源管理器中選擇壹個文件 然後調用右鍵菜單的屬性命令後 記錄器沒有記錄這個動作 但如果直接調用ShellExecute(如下示)的話 ShellExecuteHook卻會正確執行
ShellExecute(nil properties foo txt nil nil SW_SHOW);
lishixinzhi/Article/program/Delphi/201311/24787