-
Notifications
You must be signed in to change notification settings - Fork 112
/
basesup.c
6458 lines (5591 loc) · 167 KB
/
basesup.c
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Process Hacker -
* base support functions
*
* Copyright (C) 2009-2016 wj32
* Copyright (C) 2019-2021 dmex
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This file contains basic low-level code as well as general algorithms and data structures.
*
* Memory allocation. PhAllocate is a wrapper around RtlAllocateHeap, and always allocates from the
* phlib heap. PhAllocatePage is a wrapper around NtAllocateVirtualMemory and allocates pages.
*
* Null-terminated strings. The Ph*StringZ functions manipulate null-terminated strings. The copying
* functions provide a simple way to copy strings which may not be null-terminated, but have a
* specified limit.
*
* String. The design of the string object was chosen for maximum compatibility. As such each string
* buffer must be null-terminated, and each object contains an embedded PH_STRINGREF structure. Note
* that efficient sub-string creation (no copying, only references the parent string object) could
* not be implemented due to the mandatory null-termination. String objects must be regarded as
* immutable (for thread-safety reasons) unless the object has just been created and no references
* have been shared.
*
* String builder. This is a set of functions which allow for efficient modification of strings. For
* performance reasons, these functions modify string objects directly, even though they are
* normally immutable.
*
* List. A simple PVOID list that resizes itself when needed.
*
* Pointer list. Similar to the normal list object, but uses a free list in order to support
* constant time insertion and deletion. In order for the free list to work, normal entries have
* their lowest bit clear while free entries have their lowest bit set, with the index of the next
* free entry in the upper bits.
*
* Hashtable. A hashtable with power-of-two bucket sizes and with all entries stored in a single
* array. This improves locality but may be inefficient when resizing the hashtable. It is a good
* idea to store pointers to objects as entries, as opposed to the objects themselves.
*
* Simple hashtable. A wrapper around the normal hashtable, with PVOID keys and PVOID values.
*
* Free list. A thread-safe memory allocation method where freed blocks are stored in a S-list, and
* allocations are made from this list whenever possible.
*
* Callback. A thread-safe notification mechanism where clients can register callback functions
* which are then invoked by other code.
*/
#include <phbase.h>
#include <math.h>
#include <objbase.h>
#include <phintrnl.h>
#define PH_VECTOR_LEVEL_NONE 0
#define PH_VECTOR_LEVEL_SSE2 1
#define PH_VECTOR_LEVEL_AVX 2
#define PH_NATIVE_STRING_CONVERSION 1
typedef struct _PHP_BASE_THREAD_CONTEXT
{
PUSER_THREAD_START_ROUTINE StartAddress;
PVOID Parameter;
} PHP_BASE_THREAD_CONTEXT, *PPHP_BASE_THREAD_CONTEXT;
VOID NTAPI PhpListDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
VOID NTAPI PhpPointerListDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
VOID NTAPI PhpHashtableDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
// Types
PPH_OBJECT_TYPE PhStringType = NULL;
PPH_OBJECT_TYPE PhBytesType = NULL;
PPH_OBJECT_TYPE PhListType = NULL;
PPH_OBJECT_TYPE PhPointerListType = NULL;
PPH_OBJECT_TYPE PhHashtableType = NULL;
// Misc.
static BOOLEAN PhpVectorLevel = PH_VECTOR_LEVEL_NONE;
static PPH_STRING PhSharedEmptyString = NULL;
// Threads
static PH_FREE_LIST PhpBaseThreadContextFreeList;
#ifdef DEBUG
ULONG PhDbgThreadDbgTlsIndex;
LIST_ENTRY PhDbgThreadListHead = { &PhDbgThreadListHead, &PhDbgThreadListHead };
PH_QUEUED_LOCK PhDbgThreadListLock = PH_QUEUED_LOCK_INIT;
#endif
// Data
static ULONG PhpPrimeNumbers[] =
{
0x3, 0x7, 0xb, 0x11, 0x17, 0x1d, 0x25, 0x2f, 0x3b, 0x47, 0x59, 0x6b, 0x83,
0xa3, 0xc5, 0xef, 0x125, 0x161, 0x1af, 0x209, 0x277, 0x2f9, 0x397, 0x44f,
0x52f, 0x63d, 0x78b, 0x91d, 0xaf1, 0xd2b, 0xfd1, 0x12fd, 0x16cf, 0x1b65,
0x20e3, 0x2777, 0x2f6f, 0x38ff, 0x446f, 0x521f, 0x628d, 0x7655, 0x8e01,
0xaa6b, 0xcc89, 0xf583, 0x126a7, 0x1619b, 0x1a857, 0x1fd3b, 0x26315, 0x2dd67,
0x3701b, 0x42023, 0x4f361, 0x5f0ed, 0x72125, 0x88e31, 0xa443b, 0xc51eb,
0xec8c1, 0x11bdbf, 0x154a3f, 0x198c4f, 0x1ea867, 0x24ca19, 0x2c25c1, 0x34fa1b,
0x3f928f, 0x4c4987, 0x5b8b6f, 0x6dda89
};
/**
* Initializes the base support module.
*/
BOOLEAN PhBaseInitialization(
VOID
)
{
PH_OBJECT_TYPE_PARAMETERS parameters;
// The following relies on the (technically undefined) value of XState being zero before Windows 7 SP1.
// NOTE: This is unused for now.
/*if (USER_SHARED_DATA->XState.EnabledFeatures & XSTATE_MASK_AVX)
PhpVectorLevel = PH_VECTOR_LEVEL_AVX;
else if (USER_SHARED_DATA->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE])
PhpVectorLevel = PH_VECTOR_LEVEL_SSE2;*/
if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE))
PhpVectorLevel = PH_VECTOR_LEVEL_SSE2;
PhStringType = PhCreateObjectType(L"String", 0, NULL);
PhBytesType = PhCreateObjectType(L"Bytes", 0, NULL);
parameters.FreeListSize = sizeof(PH_LIST);
parameters.FreeListCount = 128;
PhListType = PhCreateObjectTypeEx(L"List", PH_OBJECT_TYPE_USE_FREE_LIST, PhpListDeleteProcedure, ¶meters);
PhPointerListType = PhCreateObjectType(L"PointerList", 0, PhpPointerListDeleteProcedure);
parameters.FreeListSize = sizeof(PH_HASHTABLE);
parameters.FreeListCount = 64;
PhHashtableType = PhCreateObjectTypeEx(L"Hashtable", PH_OBJECT_TYPE_USE_FREE_LIST, PhpHashtableDeleteProcedure, ¶meters);
PhInitializeFreeList(&PhpBaseThreadContextFreeList, sizeof(PHP_BASE_THREAD_CONTEXT), 16);
#ifdef DEBUG
PhDbgThreadDbgTlsIndex = TlsAlloc();
#endif
return TRUE;
}
NTSTATUS PhpBaseThreadStart(
_In_ PVOID Parameter
)
{
NTSTATUS status;
HRESULT result;
PHP_BASE_THREAD_CONTEXT context;
#ifdef DEBUG
PHP_BASE_THREAD_DBG dbg;
#endif
context = *(PPHP_BASE_THREAD_CONTEXT)Parameter;
PhFreeToFreeList(&PhpBaseThreadContextFreeList, Parameter);
#ifdef DEBUG
dbg.ClientId = NtCurrentTeb()->ClientId;
dbg.StartAddress = context.StartAddress;
dbg.Parameter = context.Parameter;
dbg.CurrentAutoPool = NULL;
PhAcquireQueuedLockExclusive(&PhDbgThreadListLock);
InsertTailList(&PhDbgThreadListHead, &dbg.ListEntry);
PhReleaseQueuedLockExclusive(&PhDbgThreadListLock);
TlsSetValue(PhDbgThreadDbgTlsIndex, &dbg);
#endif
// Initialization code
result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// Call the user-supplied function.
status = context.StartAddress(context.Parameter);
// De-initialization code
if (result == S_OK || result == S_FALSE)
CoUninitialize();
#ifdef DEBUG
PhAcquireQueuedLockExclusive(&PhDbgThreadListLock);
RemoveEntryList(&dbg.ListEntry);
PhReleaseQueuedLockExclusive(&PhDbgThreadListLock);
#endif
return status;
}
// rev from RtlCreateUserThread (dmex)
NTSTATUS PhCreateUserThread(
_Out_opt_ PHANDLE ThreadHandle,
_In_ HANDLE ProcessHandle,
_In_ ULONG CreateFlags,
_In_opt_ SIZE_T StackSize,
_In_ PUSER_THREAD_START_ROUTINE StartAddress,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
HANDLE threadHandle;
OBJECT_ATTRIBUTES objectAttributes;
UCHAR buffer[FIELD_OFFSET(PS_ATTRIBUTE_LIST, Attributes) + sizeof(PS_ATTRIBUTE[2])] = { 0 };
PPS_ATTRIBUTE_LIST attributeList = (PPS_ATTRIBUTE_LIST)buffer;
CLIENT_ID clientId = { 0 };
PTEB teb = NULL;
InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL);
attributeList->TotalLength = sizeof(buffer);
attributeList->Attributes[0].Attribute = PS_ATTRIBUTE_CLIENT_ID;
attributeList->Attributes[0].Size = sizeof(CLIENT_ID);
attributeList->Attributes[0].ValuePtr = &clientId;
attributeList->Attributes[0].ReturnLength = NULL;
attributeList->Attributes[1].Attribute = PS_ATTRIBUTE_TEB_ADDRESS;
attributeList->Attributes[1].Size = sizeof(PTEB);
attributeList->Attributes[1].ValuePtr = &teb;
attributeList->Attributes[1].ReturnLength = NULL;
status = NtCreateThreadEx(
&threadHandle,
THREAD_ALL_ACCESS,
&objectAttributes,
ProcessHandle,
StartAddress,
Parameter,
CreateFlags,
0,
StackSize,
StackSize,
0 // attributeList
);
if (NT_SUCCESS(status))
{
if (ThreadHandle)
{
*ThreadHandle = threadHandle;
}
else if (threadHandle)
{
NtClose(threadHandle);
}
}
return status;
}
/**
* Creates a thread.
*
* \param StackSize The initial stack size of the thread.
* \param StartAddress The function to execute in the thread.
* \param Parameter A user-defined value to pass to the function.
*/
HANDLE PhCreateThread(
_In_opt_ SIZE_T StackSize,
_In_ PUSER_THREAD_START_ROUTINE StartAddress,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
HANDLE threadHandle;
PPHP_BASE_THREAD_CONTEXT context;
context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList);
context->StartAddress = StartAddress;
context->Parameter = Parameter;
status = RtlCreateUserThread(
NtCurrentProcess(),
NULL,
FALSE,
0,
0,
StackSize,
PhpBaseThreadStart,
context,
&threadHandle,
NULL
);
// NOTE: PhCreateThread previously used CreateThread with callers using GetLastError()
// for checking errors. We need to preserve this behavior for compatibility -dmex
// TODO: Migrate code over to PhCreateThreadEx and remove this function.
//RtlSetLastWin32ErrorAndNtStatusFromNtStatus(status);
SetLastError(RtlNtStatusToDosError(status));
if (NT_SUCCESS(status))
{
PHLIB_INC_STATISTIC(BaseThreadsCreated);
return threadHandle;
}
else
{
PHLIB_INC_STATISTIC(BaseThreadsCreateFailed);
PhFreeToFreeList(&PhpBaseThreadContextFreeList, context);
return NULL;
}
}
NTSTATUS PhCreateThreadEx(
_Out_ PHANDLE ThreadHandle,
_In_ PUSER_THREAD_START_ROUTINE StartAddress,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
HANDLE threadHandle;
PPHP_BASE_THREAD_CONTEXT context;
context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList);
context->StartAddress = StartAddress;
context->Parameter = Parameter;
status = RtlCreateUserThread(
NtCurrentProcess(),
NULL,
FALSE,
0,
0,
0,
PhpBaseThreadStart,
context,
&threadHandle,
NULL
);
if (NT_SUCCESS(status))
{
PHLIB_INC_STATISTIC(BaseThreadsCreated);
*ThreadHandle = threadHandle;
}
else
{
PHLIB_INC_STATISTIC(BaseThreadsCreateFailed);
PhFreeToFreeList(&PhpBaseThreadContextFreeList, context);
}
return status;
}
NTSTATUS PhCreateThread2(
_In_ PUSER_THREAD_START_ROUTINE StartAddress,
_In_opt_ PVOID Parameter
)
{
NTSTATUS status;
PPHP_BASE_THREAD_CONTEXT context;
context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList);
context->StartAddress = StartAddress;
context->Parameter = Parameter;
status = RtlCreateUserThread(
NtCurrentProcess(),
NULL,
FALSE,
0,
0,
0,
PhpBaseThreadStart,
context,
NULL,
NULL
);
if (NT_SUCCESS(status))
{
PHLIB_INC_STATISTIC(BaseThreadsCreated);
}
else
{
PHLIB_INC_STATISTIC(BaseThreadsCreateFailed);
PhFreeToFreeList(&PhpBaseThreadContextFreeList, context);
}
return status;
}
/**
* Gets the current system time (UTC).
*
* \remarks Use this function instead of NtQuerySystemTime() because no system calls are involved.
*/
VOID PhQuerySystemTime(
_Out_ PLARGE_INTEGER SystemTime
)
{
do
{
SystemTime->HighPart = USER_SHARED_DATA->SystemTime.High1Time;
SystemTime->LowPart = USER_SHARED_DATA->SystemTime.LowPart;
} while (SystemTime->HighPart != USER_SHARED_DATA->SystemTime.High2Time);
}
/**
* Gets the offset of the current time zone from UTC.
*/
VOID PhQueryTimeZoneBias(
_Out_ PLARGE_INTEGER TimeZoneBias
)
{
do
{
TimeZoneBias->HighPart = USER_SHARED_DATA->TimeZoneBias.High1Time;
TimeZoneBias->LowPart = USER_SHARED_DATA->TimeZoneBias.LowPart;
} while (TimeZoneBias->HighPart != USER_SHARED_DATA->TimeZoneBias.High2Time);
}
/**
* Converts system time to local time.
*
* \param SystemTime A UTC time value.
* \param LocalTime A variable which receives the local time value. This may be the same variable as
* \a SystemTime.
*
* \remarks Use this function instead of RtlSystemTimeToLocalTime() because no system calls are
* involved.
*/
VOID PhSystemTimeToLocalTime(
_In_ PLARGE_INTEGER SystemTime,
_Out_ PLARGE_INTEGER LocalTime
)
{
LARGE_INTEGER timeZoneBias;
PhQueryTimeZoneBias(&timeZoneBias);
LocalTime->QuadPart = SystemTime->QuadPart - timeZoneBias.QuadPart;
}
/**
* Converts local time to system time.
*
* \param LocalTime A local time value.
* \param SystemTime A variable which receives the UTC time value. This may be the same variable as
* \a LocalTime.
*
* \remarks Use this function instead of RtlLocalTimeToSystemTime() because no system calls are
* involved.
*/
VOID PhLocalTimeToSystemTime(
_In_ PLARGE_INTEGER LocalTime,
_Out_ PLARGE_INTEGER SystemTime
)
{
LARGE_INTEGER timeZoneBias;
PhQueryTimeZoneBias(&timeZoneBias);
SystemTime->QuadPart = LocalTime->QuadPart + timeZoneBias.QuadPart;
}
/**
* Allocates a block of memory.
*
* \param Size The number of bytes to allocate.
*
* \return A pointer to the allocated block of memory.
*
* \remarks If the function fails to allocate the block of memory, it raises an exception. The block
* is guaranteed to be aligned at MEMORY_ALLOCATION_ALIGNMENT bytes.
*/
_May_raise_
_Post_writable_byte_size_(Size)
PVOID PhAllocate(
_In_ SIZE_T Size
)
{
assert(Size);
return RtlAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Size);
}
/**
* Allocates a block of memory.
*
* \param Size The number of bytes to allocate.
*
* \return A pointer to the allocated block of memory, or NULL if the block could not be allocated.
*/
_Must_inspect_result_
_Ret_maybenull_
_Post_writable_byte_size_(Size)
PVOID PhAllocateSafe(
_In_ SIZE_T Size
)
{
assert(Size);
return RtlAllocateHeap(PhHeapHandle, 0, Size);
}
/**
* Allocates a block of memory.
*
* \param Size The number of bytes to allocate.
* \param Flags Flags controlling the allocation.
*
* \return A pointer to the allocated block of memory, or NULL if the block could not be allocated.
*/
_Must_inspect_result_
_Ret_maybenull_
_Post_writable_byte_size_(Size)
PVOID PhAllocateExSafe(
_In_ SIZE_T Size,
_In_ ULONG Flags
)
{
assert(Size);
return RtlAllocateHeap(PhHeapHandle, Flags, Size);
}
/**
* Frees a block of memory allocated with PhAllocate().
*
* \param Memory A pointer to a block of memory.
*/
VOID PhFree(
_Frees_ptr_opt_ PVOID Memory
)
{
RtlFreeHeap(PhHeapHandle, 0, Memory);
}
/**
* Re-allocates a block of memory originally allocated with PhAllocate().
*
* \param Memory A pointer to a block of memory.
* \param Size The new size of the memory block, in bytes.
*
* \return A pointer to the new block of memory. The existing contents of the memory block are
* copied to the new block.
*
* \remarks If the function fails to allocate the block of memory, it raises an exception.
*/
_May_raise_
_Post_writable_byte_size_(Size)
PVOID PhReAllocate(
_Frees_ptr_opt_ PVOID Memory,
_In_ SIZE_T Size
)
{
assert(Size);
return RtlReAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Memory, Size);
}
/**
* Re-allocates a block of memory originally allocated with PhAllocate().
*
* \param Memory A pointer to a block of memory.
* \param Size The new size of the memory block, in bytes.
*
* \return A pointer to the new block of memory, or NULL if the block could not be allocated. The
* existing contents of the memory block are copied to the new block.
*/
PVOID PhReAllocateSafe(
_In_ PVOID Memory,
_In_ SIZE_T Size
)
{
assert(Size);
return RtlReAllocateHeap(PhHeapHandle, 0, Memory, Size);
}
/**
* Allocates pages of memory.
*
* \param Size The number of bytes to allocate. The number of pages allocated will be large enough
* to contain \a Size bytes.
* \param NewSize The number of bytes actually allocated. This is \a Size rounded up to the next
* multiple of PAGE_SIZE.
*
* \return A pointer to the allocated block of memory, or NULL if the block could not be allocated.
*/
_Check_return_
_Ret_maybenull_
_Success_(return != NULL)
PVOID PhAllocatePage(
_In_ SIZE_T Size,
_Out_opt_ PSIZE_T NewSize
)
{
PVOID baseAddress;
baseAddress = NULL;
if (NT_SUCCESS(NtAllocateVirtualMemory(
NtCurrentProcess(),
&baseAddress,
0,
&Size,
MEM_COMMIT,
PAGE_READWRITE
)))
{
if (NewSize)
*NewSize = Size;
return baseAddress;
}
else
{
return NULL;
}
}
/**
* Frees pages of memory allocated with PhAllocatePage().
*
* \param Memory A pointer to a block of memory.
*/
VOID PhFreePage(
_In_ _Post_invalid_ PVOID Memory
)
{
SIZE_T size;
size = 0;
NtFreeVirtualMemory(
NtCurrentProcess(),
&Memory,
&size,
MEM_RELEASE
);
}
/**
* Determines the length of the specified string, in characters.
*
* \param String The string.
*/
SIZE_T PhCountStringZ(
_In_ PWSTR String
)
{
#ifndef _ARM64_
if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2)
{
PWSTR p;
ULONG unaligned;
__m128i b;
__m128i z;
ULONG mask;
ULONG index;
p = (PWSTR)((ULONG_PTR)String & ~0xe); // String should be 2 byte aligned
unaligned = PtrToUlong(String) & 0xf;
z = _mm_setzero_si128();
if (unaligned != 0)
{
b = _mm_load_si128((__m128i *)p);
b = _mm_cmpeq_epi16(b, z);
mask = _mm_movemask_epi8(b) >> unaligned;
if (_BitScanForward(&index, mask))
return index / sizeof(WCHAR);
p += 16 / sizeof(WCHAR);
}
while (TRUE)
{
b = _mm_load_si128((__m128i *)p);
b = _mm_cmpeq_epi16(b, z);
mask = _mm_movemask_epi8(b);
if (_BitScanForward(&index, mask))
return (SIZE_T)(p - String) + index / sizeof(WCHAR);
p += 16 / sizeof(WCHAR);
}
}
else
#endif
{
return wcslen(String);
}
}
/**
* Allocates space for and copies a byte string.
*
* \param String The string to duplicate.
*
* \return The new string, which can be freed using PhFree().
*/
PSTR PhDuplicateBytesZ(
_In_ PSTR String
)
{
PSTR newString;
SIZE_T length;
length = strlen(String) + sizeof(ANSI_NULL); // include the null terminator
newString = PhAllocate(length);
memcpy(newString, String, length);
return newString;
}
/**
* Allocates space for and copies a byte string.
*
* \param String The string to duplicate.
*
* \return The new string, which can be freed using PhFree(), or NULL if storage could not be
* allocated.
*/
PSTR PhDuplicateBytesZSafe(
_In_ PSTR String
)
{
PSTR newString;
SIZE_T length;
length = strlen(String) + sizeof(ANSI_NULL); // include the null terminator
newString = PhAllocateSafe(length);
if (!newString)
return NULL;
memcpy(newString, String, length);
return newString;
}
/**
* Allocates space for and copies a 16-bit string.
*
* \param String The string to duplicate.
*
* \return The new string, which can be freed using PhFree().
*/
PWSTR PhDuplicateStringZ(
_In_ PWSTR String
)
{
PWSTR newString;
SIZE_T length;
length = PhCountStringZ(String) * sizeof(WCHAR) + sizeof(UNICODE_NULL); // include the null terminator
newString = PhAllocate(length);
memcpy(newString, String, length);
return newString;
}
/**
* Copies a string with optional null termination and a maximum number of characters.
*
* \param InputBuffer A pointer to the input string.
* \param InputCount The maximum number of characters to copy, not including the null terminator.
* Specify -1 for no limit.
* \param OutputBuffer A pointer to the output buffer.
* \param OutputCount The number of characters in the output buffer, including the null terminator.
* \param ReturnCount A variable which receives the number of characters required to contain the
* input string, including the null terminator. If the function returns TRUE, this variable contains
* the number of characters written to the output buffer.
*
* \return TRUE if the input string was copied to the output string, otherwise FALSE.
*
* \remarks The function stops copying when it encounters the first null character in the input
* string. It will also stop copying when it reaches the character count specified in \a InputCount,
* if \a InputCount is not -1.
*/
_Success_(return)
BOOLEAN PhCopyBytesZ(
_In_ PSTR InputBuffer,
_In_ SIZE_T InputCount,
_Out_writes_opt_z_(OutputCount) PSTR OutputBuffer,
_In_ SIZE_T OutputCount,
_Out_opt_ PSIZE_T ReturnCount
)
{
SIZE_T i;
BOOLEAN copied;
// Determine the length of the input string.
if (InputCount != SIZE_MAX)
{
i = 0;
while (i < InputCount && InputBuffer[i])
i++;
}
else
{
i = strlen(InputBuffer);
}
// Copy the string if there is enough room.
if (OutputBuffer && OutputCount >= i + sizeof(ANSI_NULL)) // need one character for null terminator
{
memcpy(OutputBuffer, InputBuffer, i);
OutputBuffer[i] = ANSI_NULL;
copied = TRUE;
}
else
{
copied = FALSE;
}
if (ReturnCount)
*ReturnCount = i + sizeof(ANSI_NULL);
return copied;
}
/**
* Copies a string with optional null termination and a maximum number of characters.
*
* \param InputBuffer A pointer to the input string.
* \param InputCount The maximum number of characters to copy, not including the null terminator.
* Specify -1 for no limit.
* \param OutputBuffer A pointer to the output buffer.
* \param OutputCount The number of characters in the output buffer, including the null terminator.
* \param ReturnCount A variable which receives the number of characters required to contain the
* input string, including the null terminator. If the function returns TRUE, this variable contains
* the number of characters written to the output buffer.
*
* \return TRUE if the input string was copied to the output string, otherwise FALSE.
*
* \remarks The function stops copying when it encounters the first null character in the input
* string. It will also stop copying when it reaches the character count specified in \a InputCount,
* if \a InputCount is not -1.
*/
_Success_(return)
BOOLEAN PhCopyStringZ(
_In_ PWSTR InputBuffer,
_In_ SIZE_T InputCount,
_Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer,
_In_ SIZE_T OutputCount,
_Out_opt_ PSIZE_T ReturnCount
)
{
SIZE_T i;
BOOLEAN copied;
// Determine the length of the input string.
if (InputCount != SIZE_MAX)
{
i = 0;
while (i < InputCount && InputBuffer[i])
i++;
}
else
{
i = PhCountStringZ(InputBuffer);
}
// Copy the string if there is enough room.
if (OutputBuffer && OutputCount >= i + sizeof(UNICODE_NULL)) // need one character for null terminator
{
memcpy(OutputBuffer, InputBuffer, i * sizeof(WCHAR));
OutputBuffer[i] = UNICODE_NULL;
copied = TRUE;
}
else
{
copied = FALSE;
}
if (ReturnCount)
*ReturnCount = i + sizeof(UNICODE_NULL);
return copied;
}
/**
* Copies a string with optional null termination and a maximum number of characters.
*
* \param InputBuffer A pointer to the input string.
* \param InputCount The maximum number of characters to copy, not including the null terminator.
* Specify -1 for no limit.
* \param OutputBuffer A pointer to the output buffer.
* \param OutputCount The number of characters in the output buffer, including the null terminator.
* \param ReturnCount A variable which receives the number of characters required to contain the
* input string, including the null terminator. If the function returns TRUE, this variable contains
* the number of characters written to the output buffer.
*
* \return TRUE if the input string was copied to the output string, otherwise FALSE.
*
* \remarks The function stops copying when it encounters the first null character in the input
* string. It will also stop copying when it reaches the character count specified in \a InputCount,
* if \a InputCount is not -1.
*/
_Success_(return)
BOOLEAN PhCopyStringZFromBytes(
_In_ PSTR InputBuffer,
_In_ SIZE_T InputCount,
_Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer,
_In_ SIZE_T OutputCount,
_Out_opt_ PSIZE_T ReturnCount
)
{
SIZE_T i;
BOOLEAN copied;
// Determine the length of the input string.
if (InputCount != SIZE_MAX)
{
i = 0;
while (i < InputCount && InputBuffer[i])
i++;
}
else
{
i = strlen(InputBuffer);
}
// Copy the string if there is enough room.
if (OutputBuffer && OutputCount >= i + sizeof(ANSI_NULL)) // need one character for null terminator
{
PhZeroExtendToUtf16Buffer(InputBuffer, i, OutputBuffer);
OutputBuffer[i] = UNICODE_NULL;
copied = TRUE;
}
else
{
copied = FALSE;
}
if (ReturnCount)
*ReturnCount = i + sizeof(ANSI_NULL);
return copied;
}
/**
* Copies a string with optional null termination and a maximum number of characters.
*
* \param InputBuffer A pointer to the input string.
* \param InputCount The maximum number of characters to copy, not including the null terminator.
* Specify -1 for no limit.
* \param OutputBuffer A pointer to the output buffer.
* \param OutputCount The number of characters in the output buffer, including the null terminator.
* \param ReturnCount A variable which receives the number of characters required to contain the
* input string, including the null terminator. If the function returns TRUE, this variable contains
* the number of characters written to the output buffer.
*
* \return TRUE if the input string was copied to the output string, otherwise FALSE.
*
* \remarks The function stops copying when it encounters the first null character in the input
* string. It will also stop copying when it reaches the character count specified in \a InputCount,
* if \a InputCount is not -1.
*/
_Success_(return)
BOOLEAN PhCopyStringZFromMultiByte(
_In_ PSTR InputBuffer,
_In_ SIZE_T InputCount,
_Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer,
_In_ SIZE_T OutputCount,
_Out_opt_ PSIZE_T ReturnCount