Process Hacker
dyndata.c
Go to the documentation of this file.
1 /*
2  * KProcessHacker
3  *
4  * Copyright (C) 2010-2015 wj32
5  *
6  * This file is part of Process Hacker.
7  *
8  * Process Hacker is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * Process Hacker is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <kph.h>
23 #define _DYNDATA_PRIVATE
24 #include <dyndata.h>
25 
26 #define INIT_SCAN(scan, bytes, length, address, scanLength, displacement) \
27  ( \
28  ((scan)->Initialized = TRUE), \
29  ((scan)->Scanned = FALSE), \
30  ((scan)->Bytes = (bytes)), \
31  ((scan)->Length = (length)), \
32  ((scan)->StartAddress = (address)), \
33  ((scan)->ScanLength = (scanLength)), \
34  ((scan)->Displacement = (displacement)), \
35  ((scan)->ProcedureAddress = NULL), \
36  bytes \
37  )
38 #define C_2sTo4(x) ((unsigned int)(signed short)(x))
39 
41  __in PVOID Buffer,
42  __in ULONG Length
43  );
44 
45 #ifdef _X86_
46 
47 NTSTATUS KphpX86DataInitialization(
48  VOID
49  );
50 
51 #else
52 
54  VOID
55  );
56 
57 #endif
58 
59 #ifdef ALLOC_PRAGMA
60 #pragma alloc_text(PAGE, KphDynamicDataInitialization)
61 #pragma alloc_text(PAGE, KphReadDynamicDataParameters)
62 #pragma alloc_text(PAGE, KphpLoadDynamicConfiguration)
63 #ifdef _X86_
64 #pragma alloc_text(PAGE, KphpX86DataInitialization)
65 #else
66 #pragma alloc_text(PAGE, KphpAmd64DataInitialization)
67 #endif
68 #endif
69 
70 #ifdef _X86_
71 
72 // x86
73 
74 // PsTerminateProcess/PspTerminateProcess
75 static UCHAR PspTerminateProcess51Bytes[] =
76 {
77  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x56, 0x64, 0xa1,
78  0x24, 0x01, 0x00, 0x00, 0x8b, 0x75, 0x08, 0x3b
79 };
80 static UCHAR PspTerminateProcess52Bytes[] =
81 {
82  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x56, 0x8b, 0x75,
83  0x08, 0x57, 0x8d, 0xbe, 0x40, 0x02, 0x00, 0x00
84 };
85 static UCHAR PsTerminateProcess60Bytes[] =
86 {
87  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x57,
88  0x33, 0xd2, 0x6a, 0x08, 0x42, 0x5e, 0x8d, 0xb9
89 };
90 static UCHAR PsTerminateProcess61Bytes[] =
91 {
92  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x51, 0x51, 0x53,
93  0x56, 0x64, 0x8b, 0x35, 0x24, 0x01, 0x00, 0x00,
94  0x66, 0xff, 0x8e, 0x84, 0x00, 0x00, 0x00, 0x57,
95  0xc7, 0x45, 0xfc
96 }; // a lot of functions seem to share the first
97  // 16 bytes of the Windows 7 PsTerminateProcess,
98  // and a few even share the first 24 bytes.
99 static UCHAR PsTerminateProcess62Bytes[] =
100 {
101  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x51, 0x53, 0x64,
102  0x8b, 0x1d, 0x24, 0x01, 0x00, 0x00, 0x56, 0x8d,
103  0xb3, 0x3c, 0x01, 0x00, 0x00, 0x66, 0xff, 0x0e
104 };
105 static UCHAR PsTerminateProcess63Bytes[] =
106 {
107  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
108  0x56, 0x64, 0x8b, 0x35, 0x24, 0x01, 0x00, 0x00,
109  0x57, 0x66, 0xff, 0x8e, 0x3c, 0x01, 0x00, 0x00
110 };
111 
112 // PspTerminateThreadByPointer
113 static UCHAR PspTerminateThreadByPointer51Bytes[] =
114 {
115  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xec, 0x0c,
116  0x83, 0x4d, 0xf8, 0xff, 0x56, 0x57, 0x8b, 0x7d
117 };
118 static UCHAR PspTerminateThreadByPointer52Bytes[] =
119 {
120  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x57,
121  0x8b, 0x7d, 0x08, 0x8d, 0xb7, 0x40, 0x02, 0x00
122 };
123 static UCHAR PspTerminateThreadByPointer60Bytes[] =
124 {
125  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
126  0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d,
127  0xbe, 0x60, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40
128 };
129 static UCHAR PspTerminateThreadByPointer61Bytes[] =
130 {
131  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
132  0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d,
133  0xbe, 0x80, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40
134 };
135 static UCHAR PspTerminateThreadByPointer62Bytes[] =
136 {
137  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x8d, 0x87, 0x68,
138  0x02, 0x00, 0x00, 0xf6, 0x00, 0x20, 0x53, 0x8a
139 };
140 static UCHAR PspTerminateThreadByPointer63Bytes[] =
141 {
142  0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x8b,
143  0xf1, 0x8b, 0xda, 0x57, 0x8d, 0xbe, 0xb8, 0x03
144 };
145 
146 #endif
147 
149  VOID
150  )
151 {
152  NTSTATUS status = STATUS_SUCCESS;
153 
154  PAGED_CODE();
155 
156  // Get Windows version information.
157 
158  KphDynOsVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
159  status = RtlGetVersion((PRTL_OSVERSIONINFOW)&KphDynOsVersionInfo);
160 
161  if (!NT_SUCCESS(status))
162  return status;
163 
164  // Dynamic data should be read from the registry, but we use the fallback
165  // function here if needed.
166  if (KphDynNtVersion == 0)
167  {
168 #ifdef _X86_
169  KphpX86DataInitialization();
170 #else
172 #endif
173  }
174 
175  return status;
176 }
177 
179  __in_opt HANDLE KeyHandle
180  )
181 {
182  NTSTATUS status;
183  UNICODE_STRING valueName;
185  ULONG resultLength;
186 
187  PAGED_CODE();
188 
189  if (!KeyHandle)
190  return STATUS_UNSUCCESSFUL;
191 
192  RtlInitUnicodeString(&valueName, L"DynamicConfiguration");
193 
194  status = ZwQueryValueKey(
195  KeyHandle,
196  &valueName,
198  NULL,
199  0,
200  &resultLength
201  );
202 
203  if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL)
204  {
205  // Unexpected status; fail now.
206  return STATUS_UNSUCCESSFUL;
207  }
208 
209  info = ExAllocatePoolWithTag(PagedPool, resultLength, 'ThpK');
210 
211  if (!info)
212  return STATUS_INSUFFICIENT_RESOURCES;
213 
214  status = ZwQueryValueKey(
215  KeyHandle,
216  &valueName,
218  info,
219  resultLength,
220  &resultLength
221  );
222 
223  if (NT_SUCCESS(status))
224  {
225  if (info->Type == REG_BINARY)
226  status = KphpLoadDynamicConfiguration(info->Data, info->DataLength);
227  else
228  status = STATUS_OBJECT_TYPE_MISMATCH;
229 
230  if (!NT_SUCCESS(status))
231  dprintf("Unable to load dynamic configuration: 0x%x\n", status);
232  }
233 
234  ExFreePoolWithTag(info, 'ThpK');
235 
236  return status;
237 }
238 
240  __in PVOID Buffer,
241  __in ULONG Length
242  )
243 {
244  PKPH_DYN_CONFIGURATION config;
245  ULONG i;
246  PKPH_DYN_PACKAGE package;
247 
248  PAGED_CODE();
249 
250  config = Buffer;
251 
252  if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages))
253  return STATUS_INVALID_PARAMETER;
254  if (config->Version != KPH_DYN_CONFIGURATION_VERSION)
255  return STATUS_INVALID_PARAMETER;
257  return STATUS_INVALID_PARAMETER;
258  if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages) + config->NumberOfPackages * sizeof(KPH_DYN_PACKAGE))
259  return STATUS_INVALID_PARAMETER;
260 
261  dprintf("Loading dynamic configuration with %u package(s)\n", config->NumberOfPackages);
262 
263  for (i = 0; i < config->NumberOfPackages; i++)
264  {
265  package = &config->Packages[i];
266 
267  if (package->MajorVersion == KphDynOsVersionInfo.dwMajorVersion &&
268  package->MinorVersion == KphDynOsVersionInfo.dwMinorVersion &&
269  (package->ServicePackMajor == (USHORT)-1 || package->ServicePackMajor == KphDynOsVersionInfo.wServicePackMajor) &&
270  (package->BuildNumber == (USHORT)-1 || package->BuildNumber == KphDynOsVersionInfo.dwBuildNumber))
271  {
272  dprintf("Found matching package at index %u for Windows %u.%u\n", i, package->MajorVersion, package->MinorVersion);
273 
275 
276  KphDynEgeGuid = C_2sTo4(package->StructData.EgeGuid);
277  KphDynEpObjectTable = C_2sTo4(package->StructData.EpObjectTable);
278  KphDynEpRundownProtect = C_2sTo4(package->StructData.EpRundownProtect);
279  KphDynEreGuidEntry = C_2sTo4(package->StructData.EreGuidEntry);
280  KphDynHtHandleContentionEvent = C_2sTo4(package->StructData.HtHandleContentionEvent);
281  KphDynOtName = C_2sTo4(package->StructData.OtName);
282  KphDynOtIndex = C_2sTo4(package->StructData.OtIndex);
285 
286  return STATUS_SUCCESS;
287  }
288  }
289 
290  return STATUS_NOT_FOUND;
291 }
292 
293 #ifdef _X86_
294 
295 static NTSTATUS KphpX86DataInitialization(
296  VOID
297  )
298 {
299  ULONG majorVersion, minorVersion, servicePack, buildNumber;
300 
301  PAGED_CODE();
302 
303  majorVersion = KphDynOsVersionInfo.dwMajorVersion;
304  minorVersion = KphDynOsVersionInfo.dwMinorVersion;
305  servicePack = KphDynOsVersionInfo.wServicePackMajor;
306  buildNumber = KphDynOsVersionInfo.dwBuildNumber;
307  dprintf("Windows %d.%d, SP%d.%d, build %d\n",
308  majorVersion, minorVersion, servicePack,
309  KphDynOsVersionInfo.wServicePackMinor, buildNumber
310  );
311 
312  // Windows XP
313  if (majorVersion == 5 && minorVersion == 1)
314  {
315  ULONG_PTR searchOffset = (ULONG_PTR)KphGetSystemRoutineAddress(L"NtClose");
316  ULONG scanLength = 0x100000;
317 
319 
320  // Windows XP SP0 and 1 are not supported
321  if (servicePack == 0)
322  {
323  return STATUS_NOT_SUPPORTED;
324  }
325  else if (servicePack == 1)
326  {
327  return STATUS_NOT_SUPPORTED;
328  }
329  else if (servicePack == 2)
330  {
331  }
332  else if (servicePack == 3)
333  {
334  }
335  else
336  {
337  return STATUS_NOT_SUPPORTED;
338  }
339 
340  KphDynEpObjectTable = 0xc4;
341  KphDynEpRundownProtect = 0x80;
342  KphDynOtName = 0x40;
343  KphDynOtIndex = 0x4c;
344 
345  if (searchOffset)
346  {
347  // We are scanning for PspTerminateProcess which has
348  // the same signature as PsTerminateProcess because
349  // PsTerminateProcess is simply a wrapper on XP.
350  INIT_SCAN(
352  PspTerminateProcess51Bytes,
353  sizeof(PspTerminateProcess51Bytes),
354  searchOffset, scanLength, 0
355  );
356  INIT_SCAN(
358  PspTerminateThreadByPointer51Bytes,
359  sizeof(PspTerminateThreadByPointer51Bytes),
360  searchOffset, scanLength, 0
361  );
362  }
363 
364  dprintf("Initialized version-specific data for Windows XP SP%d\n", servicePack);
365  }
366  // Windows Server 2003
367  else if (majorVersion == 5 && minorVersion == 2)
368  {
369  ULONG_PTR searchOffset = (ULONG_PTR)KphGetSystemRoutineAddress(L"RtlCreateHeap");
370  ULONG scanLength = 0x100000;
371 
373 
374  if (servicePack == 0)
375  {
376  }
377  else if (servicePack == 1)
378  {
379  }
380  else if (servicePack == 2)
381  {
382  }
383  else
384  {
385  return STATUS_NOT_SUPPORTED;
386  }
387 
388  KphDynEpObjectTable = 0xd4;
389  KphDynEpRundownProtect = 0x90;
390  KphDynOtName = 0x40;
391  KphDynOtIndex = 0x4c;
392 
393  if (searchOffset)
394  {
395  // We are scanning for PspTerminateProcess which has
396  // the same signature as PsTerminateProcess because
397  // PsTerminateProcess is simply a wrapper on Server 2003.
398  INIT_SCAN(
400  PspTerminateProcess52Bytes,
401  sizeof(PspTerminateProcess52Bytes),
402  searchOffset - 0x50000, scanLength, 0
403  );
404  INIT_SCAN(
406  PspTerminateThreadByPointer52Bytes,
407  sizeof(PspTerminateThreadByPointer52Bytes),
408  searchOffset - 0x20000, scanLength, 0
409  );
410  }
411 
412  dprintf("Initialized version-specific data for Windows Server 2003 SP%d\n", servicePack);
413  }
414  // Windows Vista, Windows Server 2008
415  else if (majorVersion == 6 && minorVersion == 0)
416  {
417  ULONG_PTR searchOffset = (ULONG_PTR)KphGetSystemRoutineAddress(L"NtClose");
418  ULONG scanLength = 0x100000;
419 
421 
422  if (servicePack == 0)
423  {
424  KphDynOtName = 0x40;
425  KphDynOtIndex = 0x4c;
426  }
427  else if (servicePack == 1)
428  {
429  KphDynOtName = 0x8; // they moved Mutex (ERESOURCE) further down
430  KphDynOtIndex = 0x14;
431  }
432  else if (servicePack == 2)
433  {
434  KphDynOtName = 0x8;
435  KphDynOtIndex = 0x14;
436  }
437  else
438  {
439  return STATUS_NOT_SUPPORTED;
440  }
441 
442  KphDynEgeGuid = 0xc;
443  KphDynEpObjectTable = 0xdc;
444  KphDynEpRundownProtect = 0x98;
445  KphDynEreGuidEntry = 0x8;
446 
447  if (searchOffset)
448  {
449  INIT_SCAN(
451  PsTerminateProcess60Bytes,
452  sizeof(PsTerminateProcess60Bytes),
453  searchOffset, scanLength, 0
454  );
455  INIT_SCAN(
457  PspTerminateThreadByPointer60Bytes,
458  sizeof(PspTerminateThreadByPointer60Bytes),
459  searchOffset - 0x50000, scanLength, 0
460  );
461  }
462 
463  dprintf("Initialized version-specific data for Windows Vista SP%d/Windows Server 2008\n", servicePack);
464  }
465  // Windows 7, Windows Server 2008 R2
466  else if (majorVersion == 6 && minorVersion == 1)
467  {
468  ULONG_PTR searchOffset1 = (ULONG_PTR)KphGetSystemRoutineAddress(L"LsaFreeReturnBuffer");
469  ULONG_PTR searchOffset2 = (ULONG_PTR)KphGetSystemRoutineAddress(L"RtlMapGenericMask");
470 
472 
473  if (servicePack == 0)
474  {
475  }
476  else if (servicePack == 1)
477  {
478  }
479  else
480  {
481  return STATUS_NOT_SUPPORTED;
482  }
483 
484  KphDynEgeGuid = 0xc;
485  KphDynEpObjectTable = 0xf4;
486  KphDynEpRundownProtect = 0xb0;
487  KphDynEreGuidEntry = 0x8;
488  KphDynOtName = 0x8;
489  KphDynOtIndex = 0x14; // now only a UCHAR, not a ULONG
490 
491  if (searchOffset1)
492  {
493  INIT_SCAN(
495  PsTerminateProcess61Bytes,
496  sizeof(PsTerminateProcess61Bytes),
497  searchOffset1, 0xa000, 0
498  );
499  }
500 
501  if (searchOffset2)
502  {
503  INIT_SCAN(
505  PspTerminateThreadByPointer61Bytes,
506  sizeof(PspTerminateThreadByPointer61Bytes),
507  searchOffset2, 0x1a000, 0
508  );
509  }
510 
511  dprintf("Initialized version-specific data for Windows 7 SP%d\n", servicePack);
512  }
513  // Windows 8, Windows Server 2012
514  else if (majorVersion == 6 && minorVersion == 2)
515  {
516  ULONG_PTR searchOffset1 = (ULONG_PTR)KphGetSystemRoutineAddress(L"IoSetIoCompletion");
517  ULONG_PTR searchOffset2 = searchOffset1;
518 
520 
521  if (servicePack == 0)
522  {
523  }
524  else
525  {
526  return STATUS_NOT_SUPPORTED;
527  }
528 
529  KphDynEgeGuid = 0xc;
530  KphDynEpObjectTable = 0x150;
531  KphDynEpRundownProtect = 0xb0;
532  KphDynEreGuidEntry = 0x8;
533  KphDynOtName = 0x8;
534  KphDynOtIndex = 0x14;
535 
536  if (searchOffset1)
537  {
538  INIT_SCAN(
540  PsTerminateProcess62Bytes,
541  sizeof(PsTerminateProcess62Bytes),
542  searchOffset1, 0x8000, 0
543  );
544  }
545 
546  if (searchOffset2)
547  {
548  INIT_SCAN(
550  PspTerminateThreadByPointer62Bytes,
551  sizeof(PspTerminateThreadByPointer62Bytes),
552  searchOffset2, 0x8000, 0
553  );
554  }
555 
556  dprintf("Initialized version-specific data for Windows 8 SP%d\n", servicePack);
557  }
558  // Windows 8.1, Windows Server 2012 R2
559  else if (majorVersion == 6 && minorVersion == 3)
560  {
561  ULONG_PTR searchOffset1 = (ULONG_PTR)KphGetSystemRoutineAddress(L"IoSetIoCompletion");
562  ULONG_PTR searchOffset2 = searchOffset1;
563 
565 
566  if (servicePack == 0)
567  {
568  }
569  else
570  {
571  return STATUS_NOT_SUPPORTED;
572  }
573 
574  KphDynEgeGuid = 0xc;
575  KphDynEpObjectTable = 0x150;
576  KphDynEpRundownProtect = 0xb0;
577  KphDynOtName = 0x8;
578  KphDynOtIndex = 0x14;
579 
580  if (searchOffset1)
581  {
582  INIT_SCAN(
584  PsTerminateProcess63Bytes,
585  sizeof(PsTerminateProcess63Bytes),
586  searchOffset1, 0x8000, 0
587  );
588  }
589 
590  if (searchOffset2)
591  {
592  INIT_SCAN(
594  PspTerminateThreadByPointer63Bytes,
595  sizeof(PspTerminateThreadByPointer63Bytes),
596  searchOffset2, 0x8000, 0
597  );
598  }
599 
600  dprintf("Initialized version-specific data for Windows 8.1 SP%d\n", servicePack);
601  }
602  else if (majorVersion == 6 && minorVersion > 3 || majorVersion > 6)
603  {
604  KphDynNtVersion = 0xffffffff;
605  return STATUS_NOT_SUPPORTED;
606  }
607  else
608  {
609  return STATUS_NOT_SUPPORTED;
610  }
611 
612  return STATUS_SUCCESS;
613 }
614 
615 #else
616 
618  VOID
619  )
620 {
621  ULONG majorVersion, minorVersion, servicePack, buildNumber;
622 
623  PAGED_CODE();
624 
625  majorVersion = KphDynOsVersionInfo.dwMajorVersion;
626  minorVersion = KphDynOsVersionInfo.dwMinorVersion;
627  servicePack = KphDynOsVersionInfo.wServicePackMajor;
628  buildNumber = KphDynOsVersionInfo.dwBuildNumber;
629  dprintf("Windows %d.%d, SP%d.%d, build %d\n",
630  majorVersion, minorVersion, servicePack,
631  KphDynOsVersionInfo.wServicePackMinor, buildNumber
632  );
633 
634  // Windows XP
635  if (majorVersion == 5 && minorVersion == 1)
636  {
638 
639  if (servicePack == 0)
640  {
641  return STATUS_NOT_SUPPORTED;
642  }
643  else if (servicePack == 1)
644  {
645  return STATUS_NOT_SUPPORTED;
646  }
647  else if (servicePack == 2)
648  {
649  }
650  else if (servicePack == 3)
651  {
652  }
653  else
654  {
655  return STATUS_NOT_SUPPORTED;
656  }
657  }
658  // Windows Server 2003
659  else if (majorVersion == 5 && minorVersion == 2)
660  {
662 
663  if (servicePack == 0)
664  {
665  }
666  else if (servicePack == 1)
667  {
668  }
669  else if (servicePack == 2)
670  {
671  }
672  else
673  {
674  return STATUS_NOT_SUPPORTED;
675  }
676  }
677  // Windows Vista, Windows Server 2008
678  else if (majorVersion == 6 && minorVersion == 0)
679  {
681 
682  if (servicePack == 0)
683  {
684  }
685  else if (servicePack == 1)
686  {
687  }
688  else if (servicePack == 2)
689  {
690  }
691  else
692  {
693  return STATUS_NOT_SUPPORTED;
694  }
695  }
696  // Windows 7, Windows Server 2008 R2
697  else if (majorVersion == 6 && minorVersion == 1)
698  {
700 
701  if (servicePack == 0)
702  {
703  }
704  else if (servicePack == 1)
705  {
706  }
707  else
708  {
709  return STATUS_NOT_SUPPORTED;
710  }
711  }
712  // Windows 8, Windows Server 2012
713  else if (majorVersion == 6 && minorVersion == 2)
714  {
716 
717  if (servicePack == 0)
718  {
719  }
720  else
721  {
722  return STATUS_NOT_SUPPORTED;
723  }
724  }
725  // Windows 8.1, Windows Server 2012 R2
726  else if (majorVersion == 6 && minorVersion == 3)
727  {
729 
730  if (servicePack == 0)
731  {
732  }
733  else
734  {
735  return STATUS_NOT_SUPPORTED;
736  }
737  }
738  else if (majorVersion == 6 && minorVersion > 3 || majorVersion > 6)
739  {
740  KphDynNtVersion = 0xffffffff;
741  return STATUS_NOT_SUPPORTED;
742  }
743  else
744  {
745  return STATUS_NOT_SUPPORTED;
746  }
747 
748  return STATUS_SUCCESS;
749 }
750 
751 #endif
752 
754  __inout PKPH_PROCEDURE_SCAN ProcedureScan
755  )
756 {
757  PUCHAR bytes;
758  ULONG length;
759  ULONG_PTR endAddress;
760  ULONG_PTR address;
761 
762  if (!ProcedureScan->Initialized)
763  return NULL;
764 
765  // This function is thread-safe.
766 
767  if (!ProcedureScan->Scanned)
768  {
769  bytes = ProcedureScan->Bytes;
770  length = ProcedureScan->Length;
771  endAddress = ProcedureScan->StartAddress + ProcedureScan->ScanLength;
772 
773  // Make sure we're scanning inside valid memory.
774  if (NT_SUCCESS(KphValidateAddressForSystemModules((PVOID)ProcedureScan->StartAddress, ProcedureScan->ScanLength)))
775  {
776  for (address = ProcedureScan->StartAddress; address < endAddress; address++)
777  {
778  if (RtlCompareMemory((PVOID)address, bytes, length) == length)
779  {
780  ProcedureScan->ProcedureAddress = (PVOID)(address + ProcedureScan->Displacement);
781  break;
782  }
783  }
784  }
785  else
786  {
787  ProcedureScan->ProcedureAddress = NULL;
788  }
789 
790  ProcedureScan->Scanned = TRUE;
791  }
792 
793  return ProcedureScan->ProcedureAddress;
794 }