既然可以通过进程活动链表来枚举进程,当然可以用来监视进程是否被创建或者销毁。下面的程序是KmdKit例子里的代码,我在学习的过程中已经加了详细注释。
KmdKit/examples/basic/Synchronization/SharedEvent - ProcessMon
驱动代码:
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;通过进程活动链表取得活动进程,当一个进程被创建或者被销毁,都会提供通知。
;其中g_ProcessData这个结构如下:
;PROCESS_DATA STRUCT
; bCreate BOOL ? 负责通知进程是被创建还是销毁
; dwProcessId DWORD ? 进程PID
; szProcessName CHAR IMAGE_FILE_PATH_LEN dup(?) ; 进程全路径
;PROCESS_DATA ENDS
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.386
.model flat, stdcall
option casemap:none
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; I N C L U D E F I L E S
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
include w2k/ntstatus.inc
include w2k/ntddk.inc
include w2k/ntoskrnl.inc
includelib ntoskrnl.lib
include Strings.mac
include ../common.inc
include ProcPath.asm
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; R E A D O N L Y D A T A
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.const
CCOUNTED_UNICODE_STRING "//Device//ProcessMon", g_usDeviceName, 4
CCOUNTED_UNICODE_STRING "//DosDevices//ProcessMon", g_usSymbolicLinkName, 4
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; U N I N I T I A L I Z E D D A T A
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.data?
g_pkEventObject PKEVENT ?
g_dwImageFileNameOffset DWORD ?
g_fbNotifyRoutineSet BOOL ?
; We do not syncronize access to this global variable
; because system itself syncronize its procrss database
; while creation/termination the process. So only one
; thread can touch it at a time.
g_ProcessData PROCESS_DATA <> ;就是此结构有进程信息
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; C O D E
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.code
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; DispatchCreateClose
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
mov ecx, pIrp
mov (_IRP PTR [ecx]).IoStatus.Status, STATUS_SUCCESS
and (_IRP PTR [ecx]).IoStatus.Information, 0
fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
mov eax, STATUS_SUCCESS
ret
DispatchCreateClose endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;通过链表获取进程信息.链表存在,则进程活动,链表被删,则进程销毁
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ProcessNotifyRoutine proc dwParentId:DWORD, dwProcessId:DWORD, bCreate:BOOL ; BOOLEAN
local peProcess:PVOID ; PEPROCESS
local fbDereference:BOOL
local us:UNICODE_STRING
local as:ANSI_STRING
push eax ; reserve DWORD on stack
invoke PsLookupProcessByProcessId, dwProcessId, esp ;得进程链表EPROCESS
pop peProcess ; -> EPROCESS
.if eax == STATUS_SUCCESS
mov fbDereference, TRUE ; PsLookupProcessByProcessId references process object
.else
; PsLookupProcessByProcessId fails (on w2k only) with STATUS_INVALID_PARAMETER
; if called in the very same process context.
; So if we are here it maight mean (on w2k) we are in process context being terminated.
invoke IoGetCurrentProcess
mov peProcess, eax
and fbDereference, FALSE ; IoGetCurrentProcess doesn't references process object
.endif
mov eax, dwProcessId
mov g_ProcessData.dwProcessId, eax ;保存其PID
mov eax, bCreate
mov g_ProcessData.bCreate, eax ;进程是否有活动?
invoke memset, addr g_ProcessData.szProcessName, 0, IMAGE_FILE_PATH_LEN
invoke GetImageFilePath, peProcess, addr us
.if eax == STATUS_SUCCESS
lea eax, g_ProcessData.szProcessName
mov as.Buffer,eax ;保存有进程名的eax填充as.Buffer
mov as.MaximumLength, IMAGE_FILE_PATH_LEN
and as._Length, 0
invoke RtlUnicodeStringToAnsiString, addr as, addr us, FALSE
invoke ExFreePool, us.Buffer ; Free memory allocated by GetImageFilePath
.else
; 如果我们获取进程全路径失败的话
; 只能在进程链表用进程名。
;也就是说GetImageFilePath函数调用失败,那么我们只能从活动链表获取
mov eax, g_dwImageFileNameOffset
.if eax != 0
add eax, peProcess
invoke memcpy, addr g_ProcessData.szProcessName, eax, 16
.endif
.endif
.if fbDereference
fastcall ObfDereferenceObject, peProcess
.endif
; Notify user-mode client.
invoke KeSetEvent, g_pkEventObject, 0, FALSE ;异步的唤醒睡眠的线程
ret
ProcessNotifyRoutine endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;这个函数应该是在进程创建或者被销毁的时候,提供通知。
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DispatchControl proc uses esi edi pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
local liDelayTime:LARGE_INTEGER
mov esi, pIrp
assume esi:ptr _IRP
; Initialize to failure.
mov [esi].IoStatus.Status, STATUS_UNSUCCESSFUL
and [esi].IoStatus.Information, 0
IoGetCurrentIrpStackLocation esi ;获取当前堆栈单元
mov edi, eax
assume edi:ptr IO_STACK_LOCATION
.if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_SET_NOTIFY
.if [edi].Parameters.DeviceIoControl.InputBufferLength >= sizeof HANDLE
.if g_fbNotifyRoutineSet == FALSE ; For sure
mov edx, [esi].AssociatedIrp.SystemBuffer
mov edx, [edx] ; user-mode hEvent
mov ecx, ExEventObjectType
mov ecx, [ecx]
mov ecx, [ecx] ; PTR OBJECT_TYPE
invoke ObReferenceObjectByHandle, edx, EVENT_MODIFY_STATE, ecx, /
UserMode, addr g_pkEventObject, NULL
.if eax == STATUS_SUCCESS
;得到一个句柄对应的文件对象
invoke PsSetCreateProcessNotifyRoutine, ProcessNotifyRoutine, FALSE
mov [esi].IoStatus.Status, eax
;每当一个进程被创建(被销毁)时就往例行程序链表中添加(从例行程序链表中删除)一个驱动提供的召回例行程序
;也就是说,当一个进程被创建或者被销毁,此函数都要被调用
.if eax == STATUS_SUCCESS
mov g_fbNotifyRoutineSet, TRUE
invoke DbgPrint, /
$CTA0("ProcessMon: 设置通知/n")
; Make driver nonunloadable
mov eax, pDeviceObject
mov eax, (DEVICE_OBJECT PTR [eax]).DriverObject
and (DRIVER_OBJECT PTR [eax]).DriverUnload, NULL
.else
invoke DbgPrint, /
$CTA0("ProcessMon: 不能设置通知/n")
.endif
.else
mov [esi].IoStatus.Status, eax
invoke DbgPrint, /
$CTA0("ProcessMon: 不能设置用户对象 状态: %08X/n"), /
eax
.endif
.endif
.else
mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL
.endif
.elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_REMOVE_NOTIFY
;当一个进程被删除,或创建,因回收。
.if g_fbNotifyRoutineSet == TRUE
invoke PsSetCreateProcessNotifyRoutine, ProcessNotifyRoutine, TRUE
mov [esi].IoStatus.Status, eax
;每当一个进程被创建(被销毁)时就往例行程序链表中添加(从例行程序链表中删除)一个驱动提供的召回例行程序
;也就是说,当一个进程被创建或者被销毁,此函数都要被调用
.if eax == STATUS_SUCCESS
and g_fbNotifyRoutineSet, FALSE
invoke DbgPrint, $CTA0("ProcessMon: 去除了通知/n")
; Just for sure. It's theoreticaly possible our ProcessNotifyRoutine is now being executed.
; So we wait for some small amount of time (~50 ms).
or liDelayTime.HighPart, -1
mov liDelayTime.LowPart, -1000000
invoke KeDelayExecutionThread, KernelMode, FALSE, addr liDelayTime
;等待知道该线程再次被CPU执行
;Make driver unloadable
mov eax, pDeviceObject
mov eax, (DEVICE_OBJECT PTR [eax]).DriverObject
mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
.if g_pkEventObject != NULL
invoke ObDereferenceObject, g_pkEventObject
and g_pkEventObject, NULL
;调用完PsLookupProcessByProcessId之后记住要调用ObDereferenceObject,否则Object还会在内存中不被释放
.endif
.else
invoke DbgPrint, /
$CTA0("ProcessMon: Couldn't remove notification/n")
.endif
.endif
.elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_PROCESS_DATA
.if [edi].Parameters.DeviceIoControl.OutputBufferLength >= sizeof PROCESS_DATA
mov eax,[esi].AssociatedIrp.SystemBuffer
invoke memcpy, eax, offset g_ProcessData, sizeof g_ProcessData
mov [esi].IoStatus.Status, STATUS_SUCCESS
mov [esi].IoStatus.Information, sizeof g_ProcessData
.else
mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL
.endif
.else
mov [esi].IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST
.endif
; After IoCompleteRequest returns, the IRP pointer
; is no longer valid and cannot safely be dereferenced.
push [esi].IoStatus.Status
assume edi:nothing
assume esi:nothing
fastcall IofCompleteRequest, esi, IO_NO_INCREMENT
pop eax ; [esi].IoStatus.Status
ret
DispatchControl endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;函数负责卸载驱动。如果没有,并且在驱动入口中返回的是STATUS_SUCCESS,
;那么驱动将存在内存中而不被卸载
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DriverUnload proc pDriverObject:PDRIVER_OBJECT
invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName
;删除连接设备
mov eax, pDriverObject
invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject
;删除驱动设备
ret
DriverUnload endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; D I S C A R D A B L E C O D E
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.code INIT
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; GetImageFileNameOffset
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
GetImageFileNameOffset proc uses esi ebx
; Finds EPROCESS.ImageFileName field offset
; W2K EPROCESS.ImageFileName = 01FCh
; WXP EPROCESS.ImageFileName = 0174h
; WNET EPROCESS.ImageFileName = 0154h
; Instead of hardcoding above offsets we just scan
; the EPROCESS structure of System process one page down.
; It's well-known trick.
invoke IoGetCurrentProcess
mov esi, eax
xor ebx, ebx
.while ebx < 1000h ; one page more than enough.
; Case insensitive compare.
lea eax, [esi+ebx]
invoke _strnicmp, eax, $CTA0("system"), 6
.break .if eax == 0
inc ebx
.endw
.if eax == 0
; Found.
mov eax, ebx
.else
; Not found.
xor eax, eax
.endif
ret
GetImageFileNameOffset endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;驱动函数入口
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
local pDeviceObject:PDEVICE_OBJECT
mov status, STATUS_DEVICE_CONFIGURATION_ERROR ;返回时需要的。
invoke IoCreateDevice, pDriverObject, 0, addr g_usDeviceName, /
FILE_DEVICE_UNKNOWN, 0, TRUE, addr pDeviceObject
;创建一个驱动设备
.if eax == STATUS_SUCCESS
invoke IoCreateSymbolicLink, addr g_usSymbolicLinkName, addr g_usDeviceName
;设置驱动设备连接
.if eax == STATUS_SUCCESS
mov eax, pDriverObject
assume eax:ptr DRIVER_OBJECT
mov [eax].MajorFunction[IRP_MJ_Create*(sizeof PVOID)],offset DispatchCreateClose
mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)],offset DispatchCreateClose
;上面两行代码在驱动关闭是,解除一个pIrp。比如客人退房后要收拾房间。
mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)], offset DispatchControl
;上面一行代码是驱动的执行代码。
mov [eax].DriverUnload,offset DriverUnload
;上面一行代码负责驱动的卸载。
assume eax:nothing
and g_fbNotifyRoutineSet, FALSE
invoke memset, addr g_ProcessData, 0, sizeof g_ProcessData
;初始化为0
invoke GetImageFileNameOffset
mov g_dwImageFileNameOffset, eax ; it can be not found and equal to 0, btw
mov status, STATUS_SUCCESS
.else
invoke IoDeleteDevice, pDeviceObject
.endif
.endif
mov eax, status ;mov eax,STATUS_DEVICE_CONFIGURATION_ERROR
ret
DriverEntry endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
end DriverEntry
下载地址 下载