首页
社区
课程
招聘
7
[原创]Delphi驱动开发研究之内核同步对象—Mutex
发表于: 2010-1-17 16:29 9324

[原创]Delphi驱动开发研究之内核同步对象—Mutex

2010-1-17 16:29
9324

在上篇教程中,我们讲解了内核同步对象中的计时器对象的使用方法,有关同步的另一个常见的用法就是对数据的独占访问。
        在本教程中,我们将同时启动多个线程,所有这些线程都会数次对一个ULONG类型的共享变量进行累加操作,最终这个共享变量的值将会等于所有线程工作次数的总和。
        这个共享变量可以是任意类型的数据,比如我们拦截到的系统服务(这些将在后面的教程里讨论)的相关统计数据。如果没有同步机制,这些共享数据的结果就无法预知了,我们可能得不到正确的统计数据甚至严重的话可能会导致系统崩溃。
        解决上述问题最好的方案就是使用互斥(Mutex) 对象了,在内核中,Mutex又被称为突变体(mutants)。 互斥,顾名思义,就是排它性访问,也就是同一时间内只允许一个线程访问共享数据,同一驱动的所有线程共同拥有一个Mutex对象,如果一个线程要访问共享变量,它首先要获取这个Mutex对象,一旦某个线程取得了Mutex对象,就可以对共享变量进行存取操作,否则就只能等待直到其他线程释放Mutex对象。通过Mutex机制就能确保同一时间只能有一个线程访问共享变量。
11.1教程源码:

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
unit MutualExclusion;
{$POINTERMATH ON}
 
interface
 
uses
  nt_status, ntoskrnl, fcall;
 
function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                      pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
 
implementation
 
const
  { 不能超过MAXIMUM_WAIT_OBJECTS (64)   - 最大等待对象数 }
  NUM_THREADS   = 5;
  NUM_WORKS   = 10;
 
var
  g_usDeviceName, g_usSymbolicLinkName: UNICODE_STRING;
  g_pkWaitBlock: PKWAIT_BLOCK;
  { PKTHREAD数组 }
  g_apkThreads: array[0..NUM_THREADS - 1] of PVOID;
  g_dwCountThreads: DWORD;
  g_kMutex: KMUTEX;
  g_dwWorkElement: DWORD;
 
function ThreadProc: NTSTATUS; stdcall;
var
  liDelayTime:LARGE_INTEGER;
  pkThread: PVOID;  { PKTHREAD }
  dwWorkElement: ULONG;
  dwCount, dwTmp: ULONG;
begin
  pkThread := PsGetCurrentThread;
    DbgPrint('MutualExclusion: Thread %08X is entering ThreadProc'#13#10, pkThread);
  dwCount := 0;
  while dwCount < NUM_WORKS do
  begin
        DbgPrint('MutualExclusion: Thread %08X is working on #%d'#13#10, pkThread, dwCount);
    KeWaitForMutexObject(@g_kMutex, Executive, KernelMode,
                         False, nil);
        { 读取线程间共享资源的值 }
        dwWorkElement := g_dwWorkElement;
        { 这里做些其他的事情 }
    liDelayTime.HighPart := liDelayTime.HighPart or -1;
    liDelayTime.LowPart := -(rand shl 4);
        KeDelayExecutionThread(KernelMode, false, @liDelayTime);
        { 设置新的线程间共享资源的值 }
        Inc(dwWorkElement);
        g_dwWorkElement := dwWorkElement;
    KeReleaseMutex(@g_kMutex, False);
    dwTmp := (((ULONG(-liDelayTime.LowPart) * 3518437209) and $FFFF0000) shr 16) shr 13;
        DbgPrint('MutualExclusion: Thread %08X work #%d is done (%02dms)'#13#10,
                         pkThread, dwCount, dwTmp);
    { 计数器加一,做下一次循环 }
        Inc(dwCount);
    end;
    DbgPrint('MutualExclusion: Thread %08X is about to terminate'#13#10, pkThread);
    Result := PsTerminateSystemThread(STATUS_SUCCESS);
end;
 
procedure CleanUp(pDriverObject:PDRIVER_OBJECT);
begin
  IoDeleteSymbolicLink(@g_usSymbolicLinkName);
    IoDeleteDevice(pDriverObject^.DeviceObject);
    if g_pkWaitBlock <> nil then
  begin
        ExFreePool(g_pkWaitBlock);
        g_pkWaitBlock := nil;
    end;
end;
 
procedure DriverUnload(pDriverObject:PDRIVER_OBJECT); stdcall;
begin
  DbgPrint('MutualExclusion: Entering DriverUnload'#13#10);
    DbgPrint('MutualExclusion: Wait for threads exit...'#13#10);
 
    { 因为ThreadProc存在于我们的驱动主体中,因此只要还有一个
  { 线程在运行,就不能卸载驱动,必须要等到所有的线程都退出 }
    if g_dwCountThreads > 0 then
  begin
        { 没有设置超时时间,一直等待到所有线程退出 }
    { 因为有多个线程对象,所以使用KeWaitForMultipleObjects }
        KeWaitForMultipleObjects(g_dwCountThreads, @g_apkThreads,
                             WaitAll, Executive, KernelMode,
                             False, nil, g_pkWaitBlock);
    { 执行到这里时,所有线程均已退出,就可以释放所有线程对象了 }
        while g_dwCountThreads > 0 do
    begin
            Dec(g_dwCountThreads);
            ObfDereferenceObject(g_apkThreads[g_dwCountThreads]);
        end;
    end;
    CleanUp(pDriverObject);
    { 打印结果. 这里g_dwWorkElement的值应该等于NUM_THREADS * NUM_WORKS }
    DbgPrint('MutualExclusion: WorkElement = %d'#13#10, g_dwWorkElement);
    DbgPrint('MutualExclusion: Leaving DriverUnload'#13#10);
end;
 
function StartThreads: NTSTATUS;
var
  hThread:HANDLE;
  i, dwCount:ULONG;
  rtnCode: NTSTATUS;
begin
  i := 0;
  { dwCount保存实际运行的线程数 }
    dwCount := 0;
    while i < NUM_THREADS do
  begin
    { 启动NUM_THREADS个线程 }
    rtnCode := PsCreateSystemThread(@hThread, THREAD_ALL_ACCESS,
                                    nil, 0, nil,
                                    @ThreadProc, nil);
        if rtnCode = STATUS_SUCCESS then
    begin
            { 我们不需要PsCreateSystemThread返回的线程句柄. }
            { 但是我们需要指向它的指针. 以便我们引用线程对象并且关闭它. }
            ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS,
                                nil, KernelMode,
                                @g_apkThreads[dwCount], nil);
            ZwClose(hThread);
            DbgPrint('MutualExclusion: System thread created. Thread Object: %08X'#13#10,
                             g_apkThreads[dwCount]);
            Inc(dwCount);
        end else
    begin
            DbgPrint('MutualExclusion: Can''t create system thread. Status: %08X'#13#10, rtnCode);
        end;
        Inc(i);
    end;
 
    g_dwCountThreads := dwCount;
    if dwCount <> 0 then
  begin
        Result := STATUS_SUCCESS; { 返回成功说明至少有一个线程在运行 }
    end else
  begin
        Result := STATUS_UNSUCCESSFUL; { 无法启动任何线程 }
    end;
end;
 
function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                      pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
var
  status:NTSTATUS;
  pDeviceObject:PDEVICE_OBJECT;
  liTickCount:LARGE_INTEGER;
begin
  status := STATUS_DEVICE_CONFIGURATION_ERROR;
  RtlInitUnicodeString(@g_usDeviceName, '\Device\MutualExclusion');
  RtlInitUnicodeString(@g_usSymbolicLinkName, '\DosDevices\MutualExclusion');
  if IoCreateDevice(pDriverObject, 0, @g_usDeviceName,
                                    FILE_DEVICE_UNKNOWN, 0, False,
                    @pDeviceObject) = STATUS_SUCCESS then
  begin
    if IoCreateSymbolicLink(@g_usSymbolicLinkName,
                            @g_usDeviceName) = STATUS_SUCCESS then
    begin
      { 因为ThreadProc存在于我们的驱动主体中,因此只要还有一个
      { 线程在运行,就不能卸载驱动,必须要等到所有的线程都退出
      { 方可卸载驱动。为了达到这个目的,我们需要一些内存。必须
      { 在这里分配这些内存,因为如果我们在DriverUnload中去分配,
      { 一旦分配失败,就没有办法停止驱动了。}
 
            { 每个线程对象都有一个内建的等待块(wait block)数组(缺省一
      { 个数组中有3个等待块)用于有几个对象并存时的等待操作。由于
      { 没有额外的等待块可供使用,因此通常情况下,这些内建的等待
      { 块数组被用于多个等待操作。当然,如果需要并存的对象的数目
      { 超过了内建等待块的数目, 可以通过设置WaitBlockArray参数指
      { 定一个替代的等待块用于等待操作。}
 
            { 在本例中,我们的NUM_THREADS大于THREAD_WAIT_OBJECTS(3). }
      { 因此就必须要使用自己的Wait Block了. }
      g_pkWaitBlock := ExAllocatePool(NonPagedPool, NUM_THREADS * SizeOf(KWAIT_BLOCK));
      if g_pkWaitBlock <> nil then
      begin
        { 初始化mutex }
        KeInitializeMutex(@g_kMutex, 0);
        { 出于性能方面的考虑, 可以使用Ex..FastMutex函数替代
        { Ke..Mutex. 当然, 快速mutex无法递归取得而内核mutex
        { 却可以;另一个缺点是ExAcquireFastMutex设置IRQL=APC_LEVEL,
        { 并且调用ExAcquireFastMutex返回后,调用者函数也将运行在
        { APC_LEVEL. }
        KeQueryTickCount(@liTickCount);
        { 初始化随机数发生器种子 }
        srand(liTickCount.LowPart);
                g_dwWorkElement := 0;
        if StartThreads = STATUS_SUCCESS then
                begin
                    pDriverObject^.DriverUnload := @DriverUnload;
                    status := STATUS_SUCCESS;
                end else
        begin
                    CleanUp(pDriverObject);
                end;
      end else
      begin
                CleanUp(pDriverObject);
                DbgPrint('MutualExclusion: Couldn''t allocate memory for Wait Block'#13#10);
      end;
    end else
    begin
      IoDeleteDevice(pDeviceObject);
    end;
  end;
  result :=  status;
end;
 
end.
1
g_pkWaitBlock := ExAllocatePool(NonPagedPool, NUM_THREADS * SizeOf(KWAIT_BLOCK));
1
KeInitializeMutex(@g_kMutex, 0);
1
2
3
4
5
6
7
8
KMUTANT = packed record
    Header:TDispatcherHeader;
    MutantListEntry:TListEntry;
    OwnerThread:PKThread;
    Abandoned:Boolean;
    ApcDisable:Byte;
    Alignment0:Word;
end;
1
KeQueryTickCount(@liTickCount);
1
g_dwWorkElement := 0;
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
i := 0;
  { dwCount保存实际运行的线程数 }
    dwCount := 0;
    while i < NUM_THREADS do
  begin
    { 启动NUM_THREADS个线程 }
    rtnCode := PsCreateSystemThread(@hThread, THREAD_ALL_ACCESS,
                                    nil, 0, nil,
                                    @ThreadProc, nil);
        if rtnCode = STATUS_SUCCESS then
    begin
            { 我们不需要PsCreateSystemThread返回的线程句柄. }
            { 但是我们需要指向它的指针. 以便我们引用线程对象并且关闭它. }
            ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS,
                                nil, KernelMode,
                                @g_apkThreads[dwCount], nil);
            ZwClose(hThread);
            DbgPrint('MutualExclusion: System thread created. Thread Object: %08X'#13#10,
                             g_apkThreads[dwCount]);
            Inc(dwCount);
        end else
    begin
            DbgPrint('MutualExclusion: Can''t create system thread. Status: %08X'#13#10, rtnCode);
        end;
        Inc(i);
    end;
    g_dwCountThreads := dwCount;
1
2
pkThread := PsGetCurrentThread;
DbgPrint('MutualExclusion: Thread %08X is entering ThreadProc'#13#10, pkThread);

[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 7
支持
分享
赞赏记录
参与人
雪币
留言
时间
Youlor
为你点赞~
2024-4-1 00:07
QinBeast
为你点赞~
2024-1-9 00:11
伟叔叔
为你点赞~
2024-1-8 00:08
shinratensei
为你点赞~
2023-12-27 00:44
PLEBFE
为你点赞~
2023-12-5 00:07
心游尘世外
为你点赞~
2023-11-21 00:38
飘零丶
为你点赞~
2023-11-11 01:41
最新回复 (4)
雪    币: 89
活跃值: (205)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
2
传说中的沙发
2010-1-17 19:01
0
雪    币: 109
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
学习~~~~~~~
2010-1-18 14:59
0
雪    币: 270
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
一直很欣赏楼主对D的坚持,支持楼主呀!
2010-1-19 13:05
0
雪    币: 163
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主万岁,希望一直走下去
2010-12-15 15:50
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

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