首页
社区
课程
招聘
[原创]Delphi驱动开发研究(12)-- Shared Event
发表于: 2010-2-11 20:47 16138

[原创]Delphi驱动开发研究(12)-- Shared Event

2010-2-11 20:47
16138

假如我们需要实现一个驱动程序去收集一些操作系统的统计信息,并将这些信息发送给用户层的管理程序去处理,这就需要驱动程序与管理程序交互,如何实现这种功能呢?最简单的办法就是在管理程序里创建一个计时器,这个计时器每隔一定的间隔时间(例如,每秒一次)就触发一次,通过使用DeviceIoControl函数向驱动程序发送I/O控制码来收集统计信息(RegMon和FileMon就是这样做的)。但是如果我们的驱动程序要收集的是一些不常发生的事件信息,比如我们要收集进程创建、销毁的信息,由于系统并非每时每刻都有进程创建或者销毁,所以定时执行DeviceIoControl往往都是白忙一场,获取不到任何信息。此时我们可以考虑增加触发定时器的间隔时间(例如,把间隔时间增加到10秒),但是如果间隔时间过长又可能会出现驱动程序已经收集到信息但是由于管理程序的定时器尚未被触发而导致未能及时获取统计信息的情况发生。显然,比较合理的方案应该是由驱动程序来决定管理程序什么时候获取统计信息,因为只有驱动程序才知道具体的事件是什么时候发生的,当事件发生后,驱动程序以某种方式通知管理程序,有事件发生,你可以获取数据了,此时管理程序再用DeviceIoControl向驱动程序发送控制码以获取最新的统计信息数据。
事实上,Windows给我们提供了两种比较好的方法可以让我们达到目的。一种方法是使用异步的DeviceIoControl,这种方法要求用户在使用CreateFile创建对象时必须要设置FILE_FLAG_OVERLAPPED标志,同时填充一个OVERLAPPED结构并将其地址做为CreateFile的最后一个参数传递。异步DeviceIoControl调用可能返回的结果有三种:返回TRUE,这说明驱动程序的分派子程序可以立即完成请求;返回FALSE并且调用GetLastError取得的错误码是ERROR_IO_PENDING,这表明驱动程序的分派子程序返回STATUS_PENDING并会在稍后完成控制操作。这里要注意的是,ERROR_IO_PENDING并不代表真的出错了,它仅仅是一种系统表明任何事件正在被正常处理的途径而已;如果返回FALSE并且用GetLastError取得的错误码也不是STATUS_PENDING,那就是真的出错了。这里我们不打算详细讨论异步DeviceIoControl方法,有兴趣的话,您可以参考DDK或者MSDN。另一种方法就是同步调用了,这种方法相对要容易些,这种方法要求在驱动程序和用户程序间共享一个事件,驱动程序通过这个事件对象向客户程序发送信号,客户程序收到信号后,就可以从驱动程序接收一些自己感兴趣的信息。
如何才能共享事件呢?你可以使用一个命名对象,然后通过名字访问这个对象。我们之前在SharedSection的例子中就使用过这种方法。这种方法的基本技巧就是让该命名对象一直存在于内存中,所以缺点也就显而易见了,这个命名对象对所有进程都是可见的,但是即便如些,它也比使用无名对象要些。当然,我们还有更好的方法,就是先在客户程序中用CreateEvent创建一个对象,然后调用DeviceIoControl把事件句柄传递给驱动程序。在本章中我们将使用这种方法创建一个简单的进程监视器,这个监视器可以监视进程的创建与销毁。下面是ProcessMon管理程序运行时的截图:

12.1 全局数据
我们先来看一下common.pas中定义的一些全局数据。

const
  IOCTL_SET_NOTIFY		   = (FILE_DEVICE_UNKNOWN shl 16) or (FILE_WRITE_ACCESS shl 14) or ($800 shl 2) or METHOD_BUFFERED;
  IOCTL_REMOVE_NOTIFY		 = (FILE_DEVICE_UNKNOWN shl 16) or (0 shl 14) or ($801 shl 2) or 0;
  IOCTL_GET_PROCESS_DATA = (FILE_DEVICE_UNKNOWN shl 16) or (FILE_READ_ACCESS shl 14) or ($802 shl 2) or METHOD_BUFFERED;

  IMAGE_FILE_PATH_LEN    = 512;

type
  {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
  PROCESS_DATA = packed record
	  bCreate: DWORD;
	  dwProcessId: DWORD;
    { full process's image file path }
	  szProcessName: array[0..IMAGE_FILE_PATH_LEN - 1] of AnsiChar;
  end;
unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, WinSvc, common;

type
  TForm1 = class(TForm)
    lvProcessInfo: TListView;
    procedure FormActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  g_hEvent: THandle;
  g_fbExitNow: Boolean;
  g_hDevice: THandle;

implementation

uses
  GetData;

var
  g_hSCManager: THandle;
  g_hService: THandle;
  tgd: TGetData;
{$R *.dfm}

procedure TForm1.FormActivate(Sender: TObject);
var
  acModulePath: string;
  lpTemp: PChar;
  dwBytesReturned: DWORD;
begin
  g_hSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if g_hSCManager <> 0 then
  begin
    acModulePath := GetCurrentDir + '\' + ExtractFileName('ProcessMon.sys');
    g_hService := CreateService(g_hSCManager, 'ProcessMon',
                                'Process creation/destruction monitor',
			                          SERVICE_START or SERVICE_STOP or _DELETE,
                                SERVICE_KERNEL_DRIVER,
                                SERVICE_DEMAND_START,
			                          SERVICE_ERROR_IGNORE,
                                PChar(acModulePath),
                                nil, nil, nil, nil, nil);
    if g_hService <> 0 then
    begin
      if StartService(g_hService, 0, lpTemp) then
      begin
        g_hDevice := CreateFile('\\.\ProcessMon', GENERIC_READ or GENERIC_WRITE,
								                0, nil, OPEN_EXISTING, 0, 0);
        if g_hDevice <> INVALID_HANDLE_VALUE then
        begin
          { No need it to be registered anymore }
					DeleteService(g_hService);
					{ Create unnamed auto-reset event to be signalled when there is data to read. }
					g_hEvent := CreateEvent(nil, False, false, nil);
          { Create thread to wait event signalled. }
          tgd := TGetData.Create(False);
          if not DeviceIoControl(g_hDevice, IOCTL_SET_NOTIFY,
						                     @g_hEvent, SizeOf(g_hEvent), nil, 0,
                                 dwBytesReturned, nil) then
          begin
						ShowMessage('无法设置通知!');
					end;
        end else
        begin
          ShowMessage('无法打开设备!');
        end;
      end else
      begin
        ShowMessage('无法启动驱动!');
      end;
    end else
    begin
      ShowMessage('无法注册驱动!');
    end;
  end else
  begin
    ShowMessage('无法连接到SCM!');
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  dwBytesReturned: DWORD;
  _ss: SERVICE_STATUS;
begin
  DeviceIoControl(g_hDevice, IOCTL_REMOVE_NOTIFY,
                  nil, 0, nil, 0, dwBytesReturned, nil);

	g_fbExitNow := true;  { If exception has occured not in loop thread it should exit now. }
	SetEvent(g_hEvent);
	Sleep(100);

	CloseHandle(g_hEvent);
	CloseHandle(g_hDevice);

	ControlService(g_hService, SERVICE_CONTROL_STOP, _ss);

	DeleteService(g_hService);

	CloseServiceHandle(g_hService);
	CloseServiceHandle(g_hSCManager);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  if(MessageDlg('由于条件所限,本驱动仅在Windows XP sp3上做过测试,是否继续?',
    mtWarning, [mbYes, mbNo], 0, mbYes) = mrNo) then
  begin
    Application.Terminate;
  end;
end;

end.
if not DeviceIoControl(g_hDevice, IOCTL_SET_NOTIFY,
				  @g_hEvent, SizeOf(g_hEvent), nil, 0,
                  dwBytesReturned, nil) then
unit GetData;

interface

uses
  Windows, WinSvc, Classes, common, ComCtrls, SysUtils, Dialogs;

type
  TGetData = class(TThread)
  private
    { Private declarations }
    ProcessData: PROCESS_DATA;
    procedure FillProcessInfo;
  protected
    constructor Create(CreateSuspended: Boolean);
    procedure Execute; override;
  end;

implementation

{ 
  Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);  

  and UpdateCaption could look like,

    procedure TGetData.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; 
    
    or 
    
    Synchronize( 
      procedure 
      begin
        Form1.Caption := 'Updated in thread via an anonymous method' 
      end
      )
    );
    
  where an anonymous method is passed.
  
  Similarly, the developer can call the Queue method with similar parameters as 
  above, instead passing another TThread class as the first parameter, putting
  the calling thread in a queue with the other thread.
    
}

{ TGetData }
uses
  main;

constructor TGetData.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  Priority := tpHighest;
end;

procedure TGetData.FillProcessInfo;
var
  buffer: array[0..1023] of AnsiChar;
  rtnVal: DWORD;
  pTmp: PAnsiChar;
  tlItems: TListItem;
  iItemCnt: Integer;
begin
  { The path can be it the short form. Convert it to long. }
	{ If no long path is found or path is in long form, GetLongPathName }
	{ simply returns the specified path. }
  FillChar(buffer, SizeOf(buffer), 0);
  rtnVal := GetLongPathName(@processData.szProcessName, @buffer, SizeOf(buffer));
  if (rtnVal = 0) or (rtnVal >= SizeOf(buffer)) then
  begin
    { 1024 bytes was not enough. Just display whatever we've got from the driver. }
		{ I want to keep the things simple. But you'd better to allocate more memory }
		{ and call GetLongPathName again and again until the buffer size will }
		{ satisfy the need. }
    pTmp := @processData.szProcessName;
  end else
    pTmp := @buffer;
  tlItems := Form1.lvProcessInfo.Items.Add;
  tlItems.Caption := string(pTmp);
  tlItems.SubItems.Add(Format('%8.8X', [processData.dwProcessId]));
  if ProcessData.bCreate <> 0 then
    tlItems.SubItems.Add('Created')
  else
    tlItems.SubItems.Add('Destroyed');
  iItemCnt := Form1.lvProcessInfo.Items.Count;
  Form1.lvProcessInfo.Items[iItemCnt - 1].MakeVisible(True);
end;

procedure TGetData.Execute;
var
  hThread: THandle;
  dwBytesReturned: DWORD;
begin
  { Place thread code here }
while True do
  begin
		if WaitForSingleObject(g_hEvent, INFINITE) <> WAIT_FAILED then
    begin
			if g_fbExitNow then
        Break;

			if DeviceIoControl(g_hDevice, IOCTL_GET_PROCESS_DATA, nil, 0,
						             @ProcessData, SizeOf(ProcessData),
                         dwBytesReturned, nil) then
      begin
				Synchronize(FillProcessInfo);
			end;
		end else
    begin
			ShowMessage('Wait for event failed. Thread now exits. Restart application.');
			Break;
		end;
    Sleep(1);
	end;
end;

end.
constructor TGetData.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  Priority := tpHighest;
end;
while True do
  begin
	if WaitForSingleObject(g_hEvent, INFINITE) <> WAIT_FAILED then
    begin
	  if g_fbExitNow then
        Break;

	  if DeviceIoControl(g_hDevice, IOCTL_GET_PROCESS_DATA, nil, 0,
					  @ProcessData, SizeOf(ProcessData),
                      dwBytesReturned, nil) then
      begin
		Synchronize(FillProcessInfo);
	  end;
function GetLongPathName(lpszShortPath: PWideChar; lpszLongPath: PWideChar;
                       cchBuffer: DWORD): DWORD; stdcall;
◎	lpszShortPath是欲转换的短路径名字符串缓冲区
◎	lpszLongPath是转换成长路径名的接收缓冲区
◎	cchBuffer则是接收缓冲区的长度
Var
  buffer: array[0..1023] of AnsiChar;
  … …
FillChar(buffer, SizeOf(buffer), 0);
  rtnVal := GetLongPathName(@processData.szProcessName, @buffer, SizeOf(buffer));
  if (rtnVal = 0) or (rtnVal >= SizeOf(buffer)) then
  begin
    { 1024 bytes was not enough. Just display whatever we've got from the driver. }
		{ I want to keep the things simple. But you'd better to allocate more memory }
		{ and call GetLongPathName again and again until the buffer size will }
		{ satisfy the need. }
    pTmp := @processData.szProcessName;
  end else
pTmp := @buffer;
unit ProcessMon;
{$POINTERMATH ON}

interface

uses
  nt_status, common;

function _DriverEntry(p_DriverObject: PDRIVER_OBJECT;
                  pusRegistryPath: PUNICODE_STRING): NTSTATUS; stdcall;

implementation

uses
  ntoskrnl, fcall, macros, ProcPath;

var
  g_usDeviceName, g_usSymbolicLinkName: UNICODE_STRING;
  g_pkEventObject: PKEVENT;
  g_fbNotifyRoutineSet: Boolean;
  g_ProcessData: PROCESS_DATA;
  g_dwImageFileNameOffset: DWORD;

function DispatchCreateClose(p_DeviceObject:PDEVICE_OBJECT; p_Irp:PIRP): NTSTATUS; stdcall;
begin
	p_Irp^.IoStatus.Status := STATUS_SUCCESS;
	p_Irp^.IoStatus.Information := 0;

	IofCompleteRequest(p_Irp, IO_NO_INCREMENT);
	result := STATUS_SUCCESS;
end;

procedure DriverUnload(pDriverObject:PDRIVER_OBJECT); stdcall;
begin
	IoDeleteSymbolicLink(@g_usSymbolicLinkName);
	IoDeleteDevice(pDriverObject^.DeviceObject);
end;

procedure ProcessNotifyRoutine(dwParentId:HANDLE; dwProcessId:HANDLE; bCreate: DWORD); stdcall;
var
  peProcess: PVOID;   { PEPROCESS }
  fbDereference: Boolean;
  us: UNICODE_STRING;
  _as: ANSI_STRING;
begin
  { reserve DWORD on stack }
	if PsLookupProcessByProcessId(dwProcessId, peProcess) = STATUS_SUCCESS then
  begin
	  //pop peProcess					; -> EPROCESS
		fbDereference := True;  { PsLookupProcessByProcessId references process object }
	end else
  begin
		{ 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. }
		peProcess := IoGetCurrentProcess;
		fbDereference := False; {IoGetCurrentProcess doesn't references process object }
	end;
  g_ProcessData.dwProcessId := dwProcessId;
  g_ProcessData.bCreate := bCreate;

	memset(@g_ProcessData.szProcessName, 0, SizeOf(IMAGE_FILE_PATH_LEN));
	if GetImageFilePath(peProcess, @us) = STATUS_SUCCESS then
  begin
		//lea eax, g_ProcessData.szProcessName
		_as.Buffer := @g_ProcessData.szProcessName;
		_as.MaximumLength := IMAGE_FILE_PATH_LEN;
		_as._Length := 0;

		RtlUnicodeStringToAnsiString(@_as, @us, False);
    { Free memory allocated by GetImageFilePath }
		ExFreePool(us.Buffer);
	end else
  begin
		{ If we fail to get process's image file path }
		{ just use only process name from EPROCESS. }
		if g_dwImageFileNameOffset <> 0 then
    begin
			memcpy(@g_ProcessData.szProcessName, PAnsiChar(DWORD(peProcess) + g_dwImageFileNameOffset), 16);
		end;
	end;
	if fbDereference then
  begin
		ObfDereferenceObject(peProcess);
	end;
	{ Notify user-mode client. }
	KeSetEvent(g_pkEventObject, 0, False);
end;

function DispatchControl(p_DeviceObject:PDEVICE_OBJECT; p_Irp:PIRP): NTSTATUS; stdcall;
var
  liDelayTime: LARGE_INTEGER;
  pIoStkLoc: PIO_STACK_LOCATION;
  UserHandle: Handle;
  lpExEventObjectType: PPointer;
  pObjectType: Pointer;
  rtnCode: NTSTATUS;
begin
  { Initialize to failure. }
	p_Irp^.IoStatus.Status := STATUS_UNSUCCESSFUL;
	p_Irp^.IoStatus.Information := 0;
  pIoStkLoc := IoGetCurrentIrpStackLocation(p_Irp);
  if pIoStkLoc^.Parameters.DeviceIoControl.IoControlCode = IOCTL_SET_NOTIFY then
  begin
		if pIoStkLoc^.Parameters.DeviceIoControl.InputBufferLength >= SizeOf(HANDLE) then
    begin
			if not g_fbNotifyRoutineSet then		{ For sure }
      begin
        UserHandle := Handle(p_Irp^.AssociatedIrp.SystemBuffer^);
        lpExEventObjectType := GetImportFunAddr(@ExEventObjectType);
        pObjectType := PVOID(lpExEventObjectType^);
        rtnCode := ObReferenceObjectByHandle(UserHandle, EVENT_MODIFY_STATE,
                    pObjectType, UserMode, @g_pkEventObject,
                    nil);
				if rtnCode = STATUS_SUCCESS then
        begin
					{ If passed event handle is valid add a driver-supplied callback routine }
					{ to a list of routines to be called whenever a process is created or deleted. }
					rtnCode := PsSetCreateProcessNotifyRoutine(@ProcessNotifyRoutine, False);
					p_Irp^.IoStatus.Status := rtnCode;
					if rtnCode = STATUS_SUCCESS then
          begin
						g_fbNotifyRoutineSet := True;
						DbgPrint('ProcessMon: Notification was set'#13#10);

						{ Make driver nonunloadable }
            p_DeviceObject^.DriverObject^.DriverUnload := nil;
					end else
          begin
						DbgPrint('ProcessMon: Couldn''t set notification'#13#10);
					end;
				end else
        begin
					p_Irp^.IoStatus.Status := rtnCode;
					DbgPrint('ProcessMon: Couldn''t reference user event object. Status: %08X'#13#10, rtnCode);
				end;
			end;
		end else
    begin
			p_Irp^.IoStatus.Status := STATUS_BUFFER_TOO_SMALL;
		end;
	end else if pIoStkLoc^.Parameters.DeviceIoControl.IoControlCode = IOCTL_REMOVE_NOTIFY then
  begin
		{ Remove a driver-supplied callback routine from a list of routines }
		{ to be called whenever a process is created or deleted. }
		if g_fbNotifyRoutineSet then
    begin
			rtnCode := PsSetCreateProcessNotifyRoutine(@ProcessNotifyRoutine, True);
			p_Irp^.IoStatus.Status := rtnCode;
			if rtnCode = STATUS_SUCCESS then
      begin
				g_fbNotifyRoutineSet := False;
				DbgPrint('ProcessMon: Notification was removed'#13#10);
				{ Just for sure. It's theoreticaly possible our ProcessNotifyRoutine is now being executed. }
				{ So we wait for some small amount of time (~50 ms). }
				liDelayTime.HighPart := liDelayTime.HighPart or -1;
				liDelayTime.LowPart := ULONG(-1000000);

				KeDelayExecutionThread(KernelMode, False, @liDelayTime);

				{ Make driver unloadable }
        p_DeviceObject^.DriverObject^.DriverUnload := @DriverUnload;
				if g_pkEventObject <> nil then
        begin
					ObfDereferenceObject(g_pkEventObject);
					g_pkEventObject := nil;
				end;
			end else
      begin
				DbgPrint('ProcessMon: Couldn''t remove notification'#13#10);
			end;
		end;
	end else if pIoStkLoc^.Parameters.DeviceIoControl.IoControlCode = IOCTL_GET_PROCESS_DATA then
  begin
		if pIoStkLoc^.Parameters.DeviceIoControl.OutputBufferLength >= SizeOf(PROCESS_DATA) then
    begin
			//mov eax, [esi].AssociatedIrp.SystemBuffer
			memcpy(p_Irp^.AssociatedIrp.SystemBuffer, @g_ProcessData, SizeOf(g_ProcessData));
			p_Irp^.IoStatus.Status := STATUS_SUCCESS;
			p_Irp^.IoStatus.Information := SizeOf(g_ProcessData);
		end else
    begin
			p_Irp^.IoStatus.Status := STATUS_BUFFER_TOO_SMALL;
		end;
	end else
  begin
		p_Irp^.IoStatus.Status := STATUS_INVALID_DEVICE_REQUEST;
	end;
	{ After IoCompleteRequest returns, the IRP pointer }
	{ is no longer valid and cannot safely be dereferenced. }
	IofCompleteRequest(p_Irp, IO_NO_INCREMENT);
  Result := p_Irp^.IoStatus.Status;
end;

function GetImageFileNameOffset: DWORD;
var
  iCnt: Integer;
  iRtnVal: Integer;
  pTmp: PAnsiChar;
begin
  { 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. }

	pTmp := PAnsiChar(IoGetCurrentProcess);
	iCnt := 0;
  iRtnVal := 0;
  { one page more than enough. }
	while iCnt < $1000 do
  begin
		{ Case insensitive compare. }
    iRtnVal := _strnicmp(PAnsiChar(pTmp + iCnt), PAnsiChar('system'), 6);
		if iRtnVal = 0 then
      Break;
		Inc(iCnt)
  end;

	if iRtnVal = 0 then
  begin
		{ Found. }
		Result := iCnt;
	end else
  begin
		{ Not found. }
		Result := 0;
	end;
end;

function _DriverEntry(p_DriverObject: PDRIVER_OBJECT;
                      pusRegistryPath: PUNICODE_STRING): NTSTATUS; stdcall;
var
  status: NTSTATUS;
  pDeviceObject: PDEVICE_OBJECT;
begin
  status := STATUS_DEVICE_CONFIGURATION_ERROR;
  RtlInitUnicodeString(@g_usDeviceName, '\Device\ProcessMon');
  RtlInitUnicodeString(@g_usSymbolicLinkName, '\DosDevices\ProcessMon');
	if IoCreateDevice(p_DriverObject, 0, @g_usDeviceName,
				            FILE_DEVICE_UNKNOWN, 0, True,
                    @pDeviceObject) = STATUS_SUCCESS then
  begin
		if IoCreateSymbolicLink(@g_usSymbolicLinkName, @g_usDeviceName) = STATUS_SUCCESS then
    begin
			p_DriverObject^.MajorFunction[IRP_MJ_CREATE] := @DispatchCreateClose;
			p_DriverObject^.MajorFunction[IRP_MJ_CLOSE] := @DispatchCreateClose;
			p_DriverObject^.MajorFunction[IRP_MJ_DEVICE_CONTROL] := @DispatchControl;
			p_DriverObject^.DriverUnload := @DriverUnload;

			g_fbNotifyRoutineSet := False;
			memset(@g_ProcessData, 0, SizeOf(g_ProcessData));
      { it can be not found and equal to 0, btw }
      g_dwImageFileNameOffset := GetImageFileNameOffset;

			status := STATUS_SUCCESS;
		end else
        begin
			IoDeleteDevice(pDeviceObject);
		end;
	end;
  result := status;
end;

end.
pTmp := PAnsiChar(IoGetCurrentProcess);
iCnt := 0;
  iRtnVal := 0;
  { one page more than enough. }
while iCnt < $1000 do
  begin
	{ Case insensitive compare. }
    iRtnVal := _strnicmp(PAnsiChar(pTmp + iCnt), PAnsiChar('system'), 6);
	if iRtnVal = 0 then
      Break;
	Inc(iCnt)
  end;
if iRtnVal = 0 then
  begin
	{ Found. }
	Result := iCnt;
end else
  begin
	{ Not found. }
	Result := 0;
end;
function ObReferenceObjectByHandle(
    _Handle: HANDLE;
    DesiredAccess: ACCESS_MASK;
    ObjectType: PVOID;
    AccessMode: KPROCESSOR_MODE;
    _Object: PVOID;
HandleInformation: POBJECT_HANDLE_INFORMATION): NTSTATUS; stdcall;

◎	_Handle指定打开的对象句柄;
◎	DesiredAccess指定对象的访问请求类型,需要指出,这里的访问类型依赖于具体的对象类型,请不要使用常用的访问权限;
◎	ObjectType是一个指向对象类型的指针,在这里,对象类型可以是PExEventObjectType、 PExSemaphoreObjectType、PIoFileObjectType、PPsProcessType、PPsThreadType、PSeTokenObjectType、PTmEnlistmentObjectType、PTmResourceManagerObjectType、PTmTransactionManagerObjectType、PTmTransactionObjectType,如果此参数为nil,则由操作系统负责根据所传递的对象句柄去检查并匹配出对应的对象类型;
◎	AccessMode指定访问检查的方式,有UserMode和KernelMode两种,一般较低级的驱动都使用KernelMode;
◎	_Object用于接收返回的指向与对象句柄相关联的对象类型的指针;
◎	HandleInformation,这个参数在驱动程序中都设为nil。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (19)
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
2
本篇教程比较长,占位贴!!!
2010-2-11 20:50
0
雪    币: 347
活跃值: (30)
能力值: ( LV9,RANK:420 )
在线值:
发帖
回帖
粉丝
3
坐前排膜拜大牛
2010-2-11 21:25
0
雪    币: 558
活跃值: (46)
能力值: ( LV2,RANK:16 )
在线值:
发帖
回帖
粉丝
4
前排围观 学习
2010-2-11 22:10
0
雪    币: 433
活跃值: (1870)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
5
支持,过来膜拜一下大牛
2010-2-12 00:07
0
雪    币: 1312
活跃值: (5164)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
不错, 支持这样详细的!!!!!
2010-2-12 03:46
0
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
7
mickeylan新年快乐!
2010-2-12 09:24
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
8
本人水平有限,教程中的一些内容难免会有谬误,请大家发现后一定要告诉我,以便我修正。
另外祝看雪老大和论坛里的兄弟姐妹们新年快乐,心想事成,笑口常开!
2010-2-12 09:27
0
雪    币: 67
活跃值: (66)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
9
顶, 一直在收藏你的文章.
2010-2-13 03:25
0
雪    币: 67
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
首页Support!!
2010-2-14 00:54
0
雪    币: 205
活跃值: (12)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
delphi开发驱动的好处是什么?
2010-2-16 16:15
0
雪    币: 270
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
占位,以后学习。
膜拜mickeylan。
2010-2-16 21:42
0
雪    币: 156
活跃值: (107)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
许久不见,10年冒出来了!
2010-2-17 10:59
0
雪    币: 4902
活跃值: (130)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
占位膜拜!
可惜一般不用D
2010-2-17 11:54
0
雪    币: 12688
活跃值: (4294)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
15
收藏起来,非常感谢分享经验~,这个很有用
2010-2-19 01:24
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
观~~~~~~~~
2010-2-20 13:19
0
雪    币: 149
活跃值: (128)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
大大就是大大啊
2010-2-22 10:41
0
雪    币: 759
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
占个坐位先...
2010-3-9 22:54
0
雪    币: 73
活跃值: (70)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
有用啊!  不错不错!!
2010-3-9 23:43
0
雪    币: 163
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
好的教程,总是让人充满期待
2010-12-15 15:51
0
游客
登录 | 注册 方可回帖
返回
//