用消息攔截技術制作系統日志
發表于:2007-07-14來源:作者:點擊數:
標簽:
康帕斯(中國)國際信息服務有限公司 馬文騫 能夠完整記錄電腦使用情況的日志文件在 Windows系統 安全 管理方面的作用是不可低估的。本文介紹了利用消息攔截技術制作日志文件的方法,其中的關鍵函數是一個未公開的 API系統調用。 一、利用鉤子(Hook)攔截系
康帕斯(中國)國際信息服務有限公司 馬文騫
能夠完整記錄電腦使用情況的日志文件在
Windows系統安全管理方面的作用是不可低估的。本文介紹了利用消息攔截技術制作日志文件的方法,其中的關鍵函數是一個未公開的 API系統調用。
一、利用鉤子(Hook)攔截系統消息
日志文件對于一個大企業內部
網絡的維護與管理是至關重要的。另外還有許多其它場合也離不開日志的使用,例如:多人共享一臺電腦,或在家庭中要記錄兒童使用電腦的細節,等等。
日志程序若想完整記錄電腦運行期間有哪些軟件啟動過、各使用了多長時間、以及使用瀏覽器訪問互聯網的情況等,必須對系統級消息進行攔截。RegisterShellHook是一個未公開的 API系統函數,它可以幫助日志程序在整個 Windows系統范圍內感知到其它窗體的創建、激活或關閉等消息,而且不要求這些窗體與日志程序有父子關系,哪怕是 Windows最高級別的窗體也可以。RegisterShellHook 調用方法為:
Public Declare Function RegisterShellHook Lib "Shell32" Alias "#181" _
(ByVal hwnd As Long, ByVal nAction As Long) As Long
其中參數hwnd為日志程序的句柄,參數 nAction為所要進行操作的代碼。具體的調用細節參見下面的例子及其注釋。
二、將日志程序隱藏起來
把日志程序的Visible屬性設為False當然是必要的一步。然后是 ShowInTaskbar屬性也設為 False,以便其在 Windows的任務欄中不出現。最后,為了在 CTRL+ALT+DEL 所彈出的列表中隱藏日志程序,需要調用RegisterServiceProcess函數:
Public Declare Function RegisterServiceProcess Lib "kernel32" _
(ByVal dwProcessID As Long, ByVal dwType As Long) As Long
其中參數dwType是操作代碼,值“1”表示從CTRL+ALT+DEL列表中去除,值“0”表示在列表中恢復;參數 dwProcessID是要在列表中去除或恢復的進程標識,可以用GetCurrentProcessId() API 函數得到日志程序的進程標識,也可以用更簡便的方法,即把 dwProcessID參數置為空值,其含義是用當前程序的進程標識作為參數(見下例)。
另外,為了讓日志程序在 Windows每次啟動時都能自動運行,需要修改注冊表,即在注冊表的下述位置新建一個以日志程序的路徑及名稱為值的“串值”:
\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
此外,產生的日志文件也應妥為隱藏,最好用 Winsock控件隨時向
服務器傳送。
為了簡潔,下面的例子僅將日志文件放在了根目錄,并且略去了用TCP/IP傳送文件的代碼。
三、一個完整的例子
下面的代碼雖然短小,卻是一個完整的能自我隱藏的日志程序(用
VB6.0實現,在 Win98下
測試通過)。
' 窗體部分的代碼(Form1.frm)
Option Explicit
Private Sub Form_Load()
Dim tmp As Long
' 將日志程序的名稱從 CTRL+ALT+DEL 列表中清除
tmp = RegisterServiceProcess(ByVal 0&, 1)
Timer1.Interval = 60000 ' 定時器的作用是每隔一分鐘將日志存盤
' 定義一個新的系統級的消息類型
Msg_ID = RegisterWindowMessage("SHELLHOOK")
Call RegisterShellHook(hwnd, 1) ' 調用未公開的函數(進行注冊)
' 實施攔截:在存儲了原入口地址的同時,將新地址指向自定義的函數WindowProc
Original = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc)
End Sub
Private Sub Form_Unload(Cancel As Integer)
Dim tmp As Long
Call RegisterShellHook(hwnd, 0) ' 調用未公開的函數(取消注冊)
tmp = SetWindowLong(hwnd, GWL_WNDPROC, Original) ' 將入口地址還原
End Sub
Private Sub Timer1_Timer()
If Len(Text1.Text) > 0 Then
Open "C:\SystemLog.Sys" For Append As #1 ' 以“添加”方式打開日志
Print #1, Text1.Text ' 日志自動存盤
Text1.Text = ""
Close #1
End If
End Sub
' 模塊部分的代碼(模塊1.bas)
Public Declare Function RegisterShellHook Lib "Shell32" Alias "#181" _
(ByVal hwnd As Long, ByVal nAction As Long) As Long
Public Declare Function RegisterWindowMessage Lib "user32" Alias _
"RegisterWindowMessageA" (ByVal lpString As String) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal
clearcase/" target="_blank" >cch As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _
(ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal _
wParam As Long, ByVal lParam As Long) As Long
Public Declare Function RegisterServiceProcess Lib "kernel32" _
(ByVal dwProcessID As Long, ByVal dwType As Long) As Long
Const HSHELL_WINDOWCREATED = 1 ' 系統級的窗體被創建
Const HSHELL_WINDOWDESTROYED = 2 ' 系統級的窗體即將被關閉
'Const HSHELL_ACTIVATESHELLWINDOW = 3 ' SHELL 的主窗體將被激活(本例未用)
Const HSHELL_WINDOWACTIVATED = 4 ' 系統級的窗體被激活
'Const HSHELL_GET
MINRECT = 5 ' 窗體被最大化或最小化(本例未用)
'Const HSHELL_REDRAW = 6 ' Windows 任務欄被刷新(本例未用)
'Const HSHELL_TASKMAN = 7 ' 任務列表的內容被選中(本例未用)
'Const HSHELL_LANGUAGE = 8 ' 中英文切換或輸入法切換(本例未用)
Public Const GWL_WNDPROC = -4 ' 該索引用來創建窗口類的子類
Public Msg_ID As Long, Original As Long
Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal _
wParam As Long, ByVal lParam As Long) As Long ' 回調函數
Dim tmp1 As String, tmp2 As String, i As Long
If uMsg = Msg_ID Then
tmp1 = String(200, "*")
i = GetWindowText(lParam, tmp1, 200) ' 取窗體的標題
If i > 0 Then tmp1 = Left(tmp1, i) Else tmp1 = "未命名"
tmp1 = tmp1 + " " + Str(Date) + " " + Str(Time) +
vbCrLf ' 加入日期
' 下面對窗體句柄值進行格式化的目的是為了日志文件在視覺上更美觀
tmp2 = Format(lParam, "000000")
If Right(Form1.Text1, 2) <> vbCrLf Then tmp2 = vbCrLf + tmp2
Select Case wParam
Case HSHELL_WINDOWCREATED
Form1.Text1 = Form1.Text1 + tmp2 + " 創建:" + tmp1
Case HSHELL_WINDOWDESTROYED
Form1.Text1 = Form1.Text1 + tmp2 + " 關閉:" + tmp1
Case HSHELL_WINDOWACTIVATED
Form1.Text1 = Form1.Text1 + tmp2 + " 激活:" + tmp1
' 為了程序簡潔,本例僅處理“創建”、“激活”和“關閉”這三個消息,
' 其實就生成日志文件的目的,上述三個消息已基本夠用。
' Case ...
' ...
End Select
Else
' 使用已被存儲下來的原入口地址
WindowProc = CallWindowProc(Original, hwnd, uMsg, wParam, lParam)
End If
End Function
下面列出的即為上述日志程序所產生的日志文件(長約十分鐘的片段)。從中可以看出在該時間段內的電腦使用情況:曾撥號上網、瀏覽過“計算機世界”、收過郵件、訪問過注冊表等。左列的數字是相應窗體的句柄。
002624 激活:Project1 - Microsoft Visual Basic [設計]
002624 關閉:Microsoft Visual Basic [設計]
001692 創建:正在連接到 95963
001692 激活:正在連接到 95963
003512 關閉:Hotmail - 通行全球的免費 Web 電子郵件 - Microsoft Internet Explorer
001880 創建:未命名 01-6-6 16:01:25
001880 激活:未命名 01-6-6 16:01:25
001880 激活:計算機世界網-應用與方案-首頁 - Microsoft Internet Explorer
001880 激活:計算機世界網-應用與方案-應用編程 - Microsoft Internet Explorer
003488 創建:Microsoft Internet Explorer 01-6-6 16:07:40
003488 激活:Microsoft Internet Explorer 01-6-6 16:07:41
003488 關閉:計算機世界網-用屏幕取詞技術實現動態標注 - Microsoft Internet Explorer
001880 激活:計算機世界網-e海航標-首頁 - Microsoft Internet Explorer
001880 關閉:計算機世界網-e海航標-首頁 - Microsoft Internet Explorer
001132 激活:瀏覽 - C:\
001132 關閉:瀏覽 - C:\
002772 創建:Outlook Express 01-6-6 16:10:41
002772 激活:Outlook Express 01-6-6 16:10:41
002772 激活:收件箱 - Outlook Express
002772 關閉:收件箱 - Outlook Express
003920 關閉:瀏覽 - 我的電腦
000640 創建:注冊表編輯器
000640 激活:注冊表編輯器
000640 關閉:注冊表編輯器
003756 創建:未命名 01-6-6 16:11:30
003756 關閉:未命名 01-6-6 16:11:30
001328 創建:網絡監視器
001328 激活:網絡監視器
001328 激活:網絡監視器 - 0 連接到 \\CD_PROGRAM
001328 關閉:網絡監視器 - 0 連接到 \\CD_PROGRAM
002700 關閉:連接到 95963
001804 關閉:未命名
原文轉自:http://www.anti-gravitydesign.com