熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Delphi編程 >> 正文

Delphi深度探索之外殼執行操作記錄器

2013-11-23 17:37:55  來源: Delphi編程 

記錄外殼的活動

  記錄外殼活動有很多好處比如當需要監控用戶的行為回溯系統崩潰前的過程實現這一功能的關鍵工具相當簡單它就是COM接口IShellExecuteHook編寫一個實現了這一接口的COM對象後再在系統中注冊就可以容易地控制並影響Windows外殼的運行Windows 和Windows 都支持IShellExecuteHook外殼擴展而在Windows 和Windows NT 上則必須安裝活動桌面擴展後才支持(也就是說必須安裝IE

  一個實現了IShellExecuteHook接口的COM對象可以截獲所有對ShellExecute和ShellExecuteEx函數的調用ShellExecute和ShellExecuteEx函數主要用於執行應用程序它們可以接收一個文件名並能自動獲得同文件名相關的可執行文件名此外它們還支持系統安全認證如果在NT上設定了用戶的可執行權限ShellExecute和ShellExecuteEx函數將會在創建新的進程前檢查權限(CreateProcess和WinExec函數則沒有這項功能)函數調用的流程如下

  ()獲得將要運行的可執行文件名

  ()根據程序名檢查用戶執行權限

  ()激活全部已注冊的IshellExecuteHook擴展

  ()當所有擴展和權限都同意執行創建新的進程並返回

  Windows外殼大量調用ShellExecute和ShellExecuteEx函數來執行幾乎是所有的資源管理器的操作比如雙擊目錄浏覽文件夾內容打印編輯文檔查看文件屬性選擇文檔的上下文相關菜單等等此外開始菜單的運行對話框和DOS方式下的Startexe也使用ShellExecuteEx函數來執行程序簡單地說幾乎用戶的所有外殼操作都可以被擴展截獲包括其他應用程序對ShellExecute和ShellExecteEx的調用



編寫外殼活動記錄器

  首先需要創建一個進程內COM對象選菜單命令New | ActiveX Library然後點擊菜單New|Com Object創建COM對象框架按圖填充對話框的內容然後點擊OK按鈕Delphi就會自動生成框架文件並保存生成的文件

  IShellExecuteHook的接口定義在shlobjpas單元中添加shlobj到單元uses部分然後添加IShellExecuteHooko方法原型到COM對象聲明部分聲明部分代碼如下

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 = {FADDBEBAEBE};

  下面就是用來截獲並記錄外殼操作的實現部分一旦外殼擴展被注冊後每次ShellExecute 和ShellExecuteEx函數運行時都會調用COM對象的Execute函數我們的核心代碼就是通過Execute方法實現的方法定義如下

function TTShellExecuteHookExecute(
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;

hkeyClass: 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 TTShellExecuteHookExecute(
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 TTShellExecuteHookExecute(
var ShellExecuteInfo: TShellExecuteInfo): HResult;

var

FileStream: TFileStream;

a:TStringList;

S:string;

begin

Result := S_FALSE;

with ShellExecuteInfo do

begin

FileStream:=TFileStreamCreate(c:\shellexecutehooktxtfmopenwrite);

S:=string(lpVerb)+:+string(lpFile)+DateTimeToStr(Now)+##;

FileStreamSeek(FileStreamSizesoFromBeginning);

FileStreamWrite(PChar(S)^Length(S));

FileStreamFree;

end;

end;

注冊ShellExecuteHook

  要想使COM對象被外殼加載需要在注冊表中注冊一些信息在下面這個子鍵中添加COM類的GUID及描述字符串後就可以了(描述字符串可以不賦值但不妨給一個以便於識別)

HKEY_LOCAL_MACHINE

SOFTWARE

Microsoft

Windows

CurrentVersion

Explorer

ShellExecuteHooks

{CLSID}= 描述字符串

  修改注冊表可以通過重載COM的類工廠的UpdateRegistry方法來實現代碼示意如下

implementation

uses ComServ SysUtils;

resourcestring

sCreateRegKeyError = 創建注冊表項失敗;

type

TShellExComObjectFactory = class(TComObjectFactory)

public

procedure UpdateRegistry(Register: Boolean); override;

end;

{ TShellExComObjectFactory }

procedure TShellExComObjectFactoryUpdateRegistry(Register: Boolean);

const

hellExecuteHooksKey=
SOFTWARE\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 EOleErrorCreate(sCreateRegKeyError);

inherited UpdateRegistry(Register);

end;

initialization

TShellExComObjectFactoryCreate(
ComServer TTShellExecuteHook Class_TShellExecuteHookTShellExecuteHook
ShellExecute hook sample ciMultiInstance tmApartment);

end

  如果系統中有多個ShellExecuteHook的話外殼會按照ShellExecuteHook的安裝順序進行調用如果要想使某個外殼擴展優先運行可先刪除其他擴展然後添加優先擴展原來的擴展依次放在後面不過這樣做也可能意義不大因為別人也會這麼干最後程序運行的結果

  記住ShellExecuteHook並不是一個完善的用於監視系統運行的擴展它只能監視ShellExecute和ShellExecuteEx的運行它不能保證記錄系統所有的行為特別是很多情況下外殼並不使用ShellExecute來進行一些常用的操作比如我們在資源管理器中選擇一個文件然後調用右鍵菜單的屬性命令後記錄器沒有記錄這個動作但如果直接調用ShellExecute(如下示)的話ShellExecuteHook卻會正確執行

ShellExecute(nil properties footxtnilnilSW_SHOW);

  這說明外殼並不使用ShellExecute函數顯示屬性對話框總之一定要謹慎使用這項技術確保它確實符合工作的需求


From:http://tw.wingwit.com/Article/program/Delphi/201311/24787.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.