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

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

2010-2-11 20:47
16238

假如我们需要实现一个驱动程序去收集一些操作系统的统计信息,并将这些信息发送给用户层的管理程序去处理,这就需要驱动程序与管理程序交互,如何实现这种功能呢?最简单的办法就是在管理程序里创建一个计时器,这个计时器每隔一定的间隔时间(例如,每秒一次)就触发一次,通过使用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中定义的一些全局数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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.
1
2
3
if not DeviceIoControl(g_hDevice, IOCTL_SET_NOTIFY,
                  @g_hEvent, SizeOf(g_hEvent), nil, 0,
                  dwBytesReturned, nil) then
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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.
1
2
3
4
5
constructor TGetData.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  Priority := tpHighest;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
1
2
3
4
5
function GetLongPathName(lpszShortPath: PWideChar; lpszLongPath: PWideChar;
                       cchBuffer: DWORD): DWORD; stdcall;
◎   lpszShortPath是欲转换的短路径名字符串缓冲区
◎   lpszLongPath是转换成长路径名的接收缓冲区
◎   cchBuffer则是接收缓冲区的长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
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.
1
2
3
4
5
6
7
8
9
10
11
12
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;
1
2
3
4
5
6
7
8
9
if iRtnVal = 0 then
  begin
    { Found. }
    Result := iCnt;
end else
  begin
    { Not found. }
    Result := 0;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
赞赏记录
参与人
雪币
留言
时间
Youlor
为你点赞~
2024-5-20 01:00
伟叔叔
为你点赞~
2024-1-14 02:11
QinBeast
为你点赞~
2024-1-9 05:24
shinratensei
为你点赞~
2024-1-3 02:00
PLEBFE
为你点赞~
2023-12-10 00:29
心游尘世外
为你点赞~
2023-11-25 01:23
飘零丶
为你点赞~
2023-11-16 00:39
最新回复 (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
活跃值: (121)
能力值: ( LV2,RANK:16 )
在线值:
发帖
回帖
粉丝
4
前排围观 学习
2010-2-11 22:10
0
雪    币: 433
活跃值: (1890)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
5
支持,过来膜拜一下大牛
2010-2-12 00:07
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
不错, 支持这样详细的!!!!!
2010-2-12 03:46
0
雪    币: 55923
活跃值: (21575)
能力值: (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
雪    币: 4911
活跃值: (150)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
占位膜拜!
可惜一般不用D
2010-2-17 11:54
0
雪    币: 13488
活跃值: (5042)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
15
收藏起来,非常感谢分享经验~,这个很有用
2010-2-19 01:24
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
观~~~~~~~~
2010-2-20 13:19
0
雪    币: 144
活跃值: (204)
能力值: ( 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
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册