-
Notifications
You must be signed in to change notification settings - Fork 570
/
Copy pathntdll.c
5764 lines (5226 loc) · 214 KB
/
ntdll.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
/* **********************************************************
* Copyright (c) 2010-2021 Google, Inc. All rights reserved.
* Copyright (c) 2003-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/*
* ntdll.c
* Routines for calling Windows system calls via the ntdll.dll wrappers.
*
* This file is used by the main library, the preinject library, and the
* standalone injector.
*/
#include "configure.h"
#ifdef NOT_DYNAMORIO_CORE
# define ASSERT(x)
# define ASSERT_CURIOSITY(x)
# define ASSERT_NOT_REACHED()
# define ASSERT_NOT_IMPLEMENTED(x)
# define DODEBUG(x)
# define DOCHECK(n, x)
# define DEBUG_DECLARE(x)
# pragma warning(disable : 4210) // nonstd extension: function given file scope
# pragma warning(disable : 4204) // nonstd extension: non-constant aggregate
// initializer
# define INVALID_FILE INVALID_HANDLE_VALUE
# define snprintf _snprintf
# include <stdio.h> /* _snprintf */
#else
/* we include globals.h mainly for ASSERT, even though we're
* used by preinject.
* preinject just defines its own d_r_internal_error!
*/
# include "../globals.h"
# include "../module_shared.h"
#endif
/* We have to hack away things we use here that won't work for non-core */
#if defined(NOT_DYNAMORIO_CORE_PROPER) || defined(NOT_DYNAMORIO_CORE)
# undef ASSERT_OWN_NO_LOCKS
# define ASSERT_OWN_NO_LOCKS() /* who cares if not the core */
#endif
#include "ntdll.h"
#ifndef NOT_DYNAMORIO_CORE
# include "os_private.h"
#endif
#include <wchar.h> /* _snwprintf */
/* WARNING: these routines use the Native API, an undocumented API
* exported by ntdll.dll.
* It could change without warning with a new version of Windows.
*/
/* FIXME : combine NTPRINT with NTLOG */
/* must turn on VERBOSE in inject_shared.c as well since we're now
* using display_verbose_message() -- FIXME: link them automatically */
#if defined(NOT_DYNAMORIO_CORE_PROPER) || defined(NOT_DYNAMORIO_CORE)
# define VERBOSE 0
#else
# define VERBOSE 0
#endif
#if VERBOSE
/* in inject_shared.c: must turn on VERBOSE=1 there as well */
void
display_verbose_message(char *format, ...);
# define NTPRINT(...) display_verbose_message(__VA_ARGS__)
#else
# define NTPRINT(...)
#endif
/* i#437 support ymm */
uint context_xstate = 0;
/* needed for injector and preinject, to avoid them requiring asm and syscalls */
#if defined(NOT_DYNAMORIO_CORE_PROPER) || defined(NOT_DYNAMORIO_CORE)
/* use ntdll wrappers for simplicity, to keep ntdll.c standalone */
# define GET_SYSCALL(name, ...) GET_NTDLL(Nt##name, (__VA_ARGS__))
# define GET_RAW_SYSCALL GET_SYSCALL
# define NT_SYSCALL(name, ...) Nt##name(__VA_ARGS__)
# define NTLOG(...)
#else
# define NTLOG LOG
/* Our own syscall wrapper to avoid relying on ntdll, for 4 reasons:
* 1) Maximum interoperability w/ ntdll hookers
* 2) Security by avoiding being disabled via ntdll being messed up
* 3) Early injection: although ntdll is already in the address space,
* this way we don't need the loader
* 4) Easier trampolines on ntdll syscall wrappers for handling native code
* (don't have to worry about DR syscalls going through the trampolines)
*
* For now we only use our own wrapper for syscalls in the app-relevant array.
* When we add the rest we can:
* 1) leave out of array, and dynamically determine
* (ideally using our own version of GetProcAddress)
* 2) add to array, then everything's consistent
* 3) eliminate array completely and always dynamically determine
* But, relying on dynamic determination means we won't work if there's a hook
* already placed there (losing a big advantage of our own wrappers), and
* dynamically determining doesn't give us that much more independence --
* we still need to manually verify each new ntdll for other types of
* syscall changes (new syscalls we care about, semantic changes, etc.)
*/
/* decides which of dynamorio_syscall_{int2e,sysenter,wow64} to use */
static enum {
DR_SYSCALL_INT2E,
DR_SYSCALL_SYSENTER,
DR_SYSCALL_SYSCALL,
DR_SYSCALL_WOW64,
} dr_which_syscall_t;
/* For x64 "raw syscalls", i.e., those we call directly w/o invoking the
* ntdll wrapper routine, we play some games with types to work more
* easily w/ the x64 calling convention:
*/
# define GET_RAW_SYSCALL(name, arg1, ...) \
GET_NTDLL(Nt##name, (arg1, __VA_ARGS__)); \
typedef NTSTATUS name##_type(int sysnum, arg1, __VA_ARGS__); \
typedef NTSTATUS name##_dr_type(int sys_enum, __VA_ARGS__, arg1)
# define GET_SYSCALL(name, ...) \
GET_NTDLL(Nt##name, (__VA_ARGS__)); \
typedef NTSTATUS name##_type(int sysnum, __VA_ARGS__)
/* FIXME - since it doesn't vary we could have a variable to store the dr
* syscall routine to use, but would be yet another function pointer in
* our data segment... */
/* We use the wrappers till the native_exec Nt hooks go in (at which point
* the options have been read) so that we can have sygate compatibility as a
* runtime option. */
/* For X64 sycall we need the 1st arg last to preserve the rest in their
* proper registers. If we ever support 0-arg syscalls here we'll
* need a separate macro for those.
* Any syscall called using this macro must be declared with GET_RAW_SYSCALL
* rather than GET_SYSCALL to get the types to match up.
*/
/* i#1011: We usually use NT_SYSCALL to invoke a system call. However,
* for system calls that do not exist in older Windows, e.g. NtOpenKeyEx,
* we use NT_RAW_SYSCALL to avoid static link and build failure.
*/
# define NT_RAW_SYSCALL(name, arg1, ...) \
((dr_which_syscall_t == DR_SYSCALL_WOW64) \
? (!syscall_uses_edx_param_base() \
? ((name##_type *)dynamorio_syscall_wow64_noedx)(SYS_##name, arg1, \
__VA_ARGS__) \
: (((name##_type *)dynamorio_syscall_wow64)(SYS_##name, arg1, \
__VA_ARGS__))) \
: ((IF_X64_ELSE(dr_which_syscall_t == DR_SYSCALL_SYSCALL, false)) \
? ((name##_dr_type *)IF_X64_ELSE(dynamorio_syscall_syscall, NULL))( \
SYS_##name, __VA_ARGS__, arg1) \
: (((name##_type *)((dr_which_syscall_t == DR_SYSCALL_SYSENTER) \
? (DYNAMO_OPTION(dr_sygate_sysenter) \
? dynamorio_syscall_sygate_sysenter \
: dynamorio_syscall_sysenter) \
: (DYNAMO_OPTION(dr_sygate_int) \
? dynamorio_syscall_sygate_int2e \
: dynamorio_syscall_int2e)))( \
syscalls[SYS_##name], arg1, __VA_ARGS__))))
# define NT_SYSCALL(name, arg1, ...) \
(nt_wrappers_intercepted ? Nt##name(arg1, __VA_ARGS__) \
: NT_RAW_SYSCALL(name, arg1, __VA_ARGS__))
/* check syscall numbers without using any heap */
# ifdef X64
# define SYSNUM_OFFS 4
# else
# define SYSNUM_OFFS 1
# endif
# define CHECK_SYSNUM_AT(pc, idx) \
ASSERT(pc != NULL && \
(*((int *)((pc) + SYSNUM_OFFS)) == syscalls[idx] || ALLOW_HOOKER(pc) || \
(idx == SYS_TestAlert && *(uint *)(pc) == 0xe9505050))); /* xref 9288 */
/* assuming relative CTI's are the only one's used by hookers */
# define ALLOW_HOOKER(pc) \
(*(unsigned char *)(pc) == JMP_REL32_OPCODE || \
*(unsigned char *)(pc) == CALL_REL32_OPCODE)
/* FIXME: we'll evaluate pc multiple times in the above macro */
static void
tls_exit(void);
#endif /* !NOT_DYNAMORIO_CORE_PROPER */
/* cached value */
static PEB *own_peb = NULL;
/****************************************************************************
* Defines only needed internally to this file
*/
/* TlsSlots offset is hardcoded into kernel32!TlsGetValue as 0xe10 on all
* 32-bit platforms we've seen, 0x1480 for 64-bit:
*/
#ifdef X64
# define TEB_TLS64_OFFSET 0x1480
#else
# define TEB_TLS64_OFFSET 0xe10
#endif
/***************************************************************************
* declarations for ntdll exports shared by several routines in this file
*/
GET_NTDLL(NtQueryInformationProcess,
(DR_PARAM_IN HANDLE ProcessHandle,
DR_PARAM_IN PROCESSINFOCLASS ProcessInformationClass,
DR_PARAM_OUT PVOID ProcessInformation,
DR_PARAM_IN ULONG ProcessInformationLength,
DR_PARAM_OUT PULONG ReturnLength OPTIONAL));
GET_NTDLL(NtQueryInformationFile,
(DR_PARAM_IN HANDLE FileHandle, DR_PARAM_OUT PIO_STATUS_BLOCK IoStatusBlock,
DR_PARAM_OUT PVOID FileInformation, DR_PARAM_IN ULONG FileInformationLength,
DR_PARAM_IN FILE_INFORMATION_CLASS FileInformationClass));
GET_NTDLL(NtQuerySection,
(DR_PARAM_IN HANDLE SectionHandle,
DR_PARAM_IN SECTION_INFORMATION_CLASS SectionInformationClass,
DR_PARAM_OUT PVOID SectionInformation,
DR_PARAM_IN ULONG SectionInformationLength,
DR_PARAM_OUT PULONG ResultLength OPTIONAL));
GET_NTDLL(NtQueryInformationToken,
(DR_PARAM_IN HANDLE TokenHandle,
DR_PARAM_IN TOKEN_INFORMATION_CLASS TokenInformationClass,
DR_PARAM_OUT PVOID TokenInformation, DR_PARAM_IN ULONG TokenInformationLength,
DR_PARAM_OUT PULONG ReturnLength));
/* routines that we may hook if specified in
* syscall_requires_action[], all new routines can use GET_SYSCALL
* instead of GET_NTDLL if we provide the syscall numbers - see
* comments in GET_SYSCALL definition.
*/
GET_RAW_SYSCALL(QueryVirtualMemory, DR_PARAM_IN HANDLE ProcessHandle,
DR_PARAM_IN const void *BaseAddress,
DR_PARAM_IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
DR_PARAM_OUT PVOID MemoryInformation,
DR_PARAM_IN SIZE_T MemoryInformationLength,
DR_PARAM_OUT PSIZE_T ReturnLength OPTIONAL);
GET_RAW_SYSCALL(UnmapViewOfSection, DR_PARAM_IN HANDLE ProcessHandle,
DR_PARAM_IN PVOID BaseAddress);
GET_RAW_SYSCALL(CreateSection, DR_PARAM_OUT PHANDLE SectionHandle,
DR_PARAM_IN ACCESS_MASK DesiredAccess,
DR_PARAM_IN POBJECT_ATTRIBUTES ObjectAttributes,
DR_PARAM_IN PLARGE_INTEGER SectionSize OPTIONAL,
DR_PARAM_IN ULONG Protect, DR_PARAM_IN ULONG Attributes,
DR_PARAM_IN HANDLE FileHandle);
GET_RAW_SYSCALL(OpenSection, DR_PARAM_OUT PHANDLE SectionHandle,
DR_PARAM_IN ACCESS_MASK DesiredAccess,
DR_PARAM_IN POBJECT_ATTRIBUTES ObjectAttributes);
GET_RAW_SYSCALL(AllocateVirtualMemory, DR_PARAM_IN HANDLE ProcessHandle,
DR_PARAM_INOUT PVOID *BaseAddress, DR_PARAM_IN ULONG ZeroBits,
DR_PARAM_INOUT PSIZE_T AllocationSize, DR_PARAM_IN ULONG AllocationType,
DR_PARAM_IN ULONG Protect);
GET_RAW_SYSCALL(FreeVirtualMemory, DR_PARAM_IN HANDLE ProcessHandle,
DR_PARAM_INOUT PVOID *BaseAddress, DR_PARAM_INOUT PSIZE_T FreeSize,
DR_PARAM_IN ULONG FreeType);
GET_RAW_SYSCALL(ProtectVirtualMemory, DR_PARAM_IN HANDLE ProcessHandle,
DR_PARAM_INOUT PVOID *BaseAddress, DR_PARAM_INOUT PSIZE_T ProtectSize,
DR_PARAM_IN ULONG NewProtect, DR_PARAM_OUT PULONG OldProtect);
GET_RAW_SYSCALL(QueryInformationThread, DR_PARAM_IN HANDLE ThreadHandle,
DR_PARAM_IN THREADINFOCLASS ThreadInformationClass,
DR_PARAM_OUT PVOID ThreadInformation,
DR_PARAM_IN ULONG ThreadInformationLength,
DR_PARAM_OUT PULONG ReturnLength OPTIONAL);
/* CreateFile is defined CreateFileW (Unicode) or CreateFileA (ANSI),
* undefine here for system call.
*/
#undef CreateFile
GET_RAW_SYSCALL(CreateFile, DR_PARAM_OUT PHANDLE FileHandle,
DR_PARAM_IN ACCESS_MASK DesiredAccess,
DR_PARAM_IN POBJECT_ATTRIBUTES ObjectAttributes,
DR_PARAM_OUT PIO_STATUS_BLOCK IoStatusBlock,
DR_PARAM_IN PLARGE_INTEGER AllocationSize OPTIONAL,
DR_PARAM_IN ULONG FileAttributes, DR_PARAM_IN ULONG ShareAccess,
DR_PARAM_IN ULONG CreateDisposition, DR_PARAM_IN ULONG CreateOptions,
DR_PARAM_IN PVOID EaBuffer OPTIONAL, DR_PARAM_IN ULONG EaLength);
GET_RAW_SYSCALL(CreateKey, DR_PARAM_OUT PHANDLE KeyHandle,
DR_PARAM_IN ACCESS_MASK DesiredAccess,
DR_PARAM_IN POBJECT_ATTRIBUTES ObjectAttributes,
DR_PARAM_IN ULONG TitleIndex, DR_PARAM_IN PUNICODE_STRING Class OPTIONAL,
DR_PARAM_IN ULONG CreateOptions,
DR_PARAM_OUT PULONG Disposition OPTIONAL);
GET_RAW_SYSCALL(OpenKey, DR_PARAM_OUT PHANDLE KeyHandle,
DR_PARAM_IN ACCESS_MASK DesiredAccess,
DR_PARAM_IN POBJECT_ATTRIBUTES ObjectAttributes);
GET_RAW_SYSCALL(SetInformationFile, DR_PARAM_IN HANDLE FileHandle,
DR_PARAM_OUT PIO_STATUS_BLOCK IoStatusBlock,
DR_PARAM_IN PVOID FileInformation,
DR_PARAM_IN ULONG FileInformationLength,
DR_PARAM_IN FILE_INFORMATION_CLASS FileInformationClass);
/* the same structure as _CONTEXT_EX in winnt.h */
typedef struct _context_chunk_t {
LONG offset;
DWORD length;
} context_chunk_t;
/* the same structure as _CONTEXT_CHUNK in winnt.h */
typedef struct _context_ex_t {
context_chunk_t all;
context_chunk_t legacy;
context_chunk_t xstate;
} context_ex_t;
/* XXX, the function below can be statically-linked if all versions of
* ntdll have the corresponding routine, which need to be checked, so we use
* get_proc_address to get instead here.
*/
typedef int(WINAPI *ntdll_RtlGetExtendedContextLength_t)(DWORD, int *);
typedef int(WINAPI *ntdll_RtlInitializeExtendedContext_t)(PVOID, DWORD, context_ex_t **);
typedef CONTEXT *(WINAPI *ntdll_RtlLocateLegacyContext_t)(context_ex_t *, DWORD);
ntdll_RtlGetExtendedContextLength_t ntdll_RtlGetExtendedContextLength = NULL;
ntdll_RtlInitializeExtendedContext_t ntdll_RtlInitializeExtendedContext = NULL;
ntdll_RtlLocateLegacyContext_t ntdll_RtlLocateLegacyContext = NULL;
#if !defined(NOT_DYNAMORIO_CORE_PROPER) && !defined(NOT_DYNAMORIO_CORE)
/* Nt* routines that are not available on all versions of Windows */
typedef NTSTATUS(WINAPI *NtGetNextThread_t)(__in HANDLE ProcessHandle,
__in HANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in ULONG HandleAttributes, __in ULONG Flags,
__out PHANDLE NewThreadHandle);
NtGetNextThread_t NtGetNextThread;
#endif
/***************************************************************************
* Implementation
*/
#if !defined(NOT_DYNAMORIO_CORE_PROPER) && !defined(NOT_DYNAMORIO_CORE)
/* for Sygate 5441 compatibility hack, we need a tls slot for NT_SYSCALL when
* using sysenter system calls */
uint sysenter_tls_offset = 0xffffffff; /* something that will fault */
/* will be set to false once the options are read but before the native_exec
* Nt* hooks are put in. Till then lets NT_SYSCALL know it's safe to call via
* the wrappers for Sygate compatibility before the option string is read in. */
static bool nt_wrappers_intercepted = true;
void
syscalls_init_options_read()
{
if (DYNAMO_OPTION(dr_sygate_sysenter)) {
tls_alloc(false /* don't grab lock */, &sysenter_tls_offset);
}
nt_wrappers_intercepted = false;
}
static int
syscalls_init_get_num(HANDLE ntdllh, int sys_enum)
{
app_pc wrapper;
ASSERT(ntdllh != NULL);
/* We can't check syscalls[] for SYSCALL_NOT_PRESENT b/c it's not set up yet */
/* d_r_get_proc_address() does invoke NtQueryVirtualMemory, but we go through the
* ntdll wrapper for that syscall and thus it works this early.
*/
wrapper = (app_pc)d_r_get_proc_address(ntdllh, syscall_names[sys_enum]);
if (wrapper != NULL && !ALLOW_HOOKER(wrapper))
return *((int *)((wrapper) + SYSNUM_OFFS));
else
return -1;
}
/* Called very early, prior to any system call use by us, making error
* reporting problematic once we have all syscalls requiring this!
* See windows_version_init() comments.
* The other problem w/ error reporting is that other code assumes
* that things are initialized -- that's all fixed now, with stats, dcontext,
* etc. checked for NULL in all the right places.
*/
bool
syscalls_init()
{
/* Determine which syscall routine to use
* We don't have heap available yet (no syscalls yet!) so
* we can't decode easily.
* FIXME: for app syscalls, we wait until we see one so we know
* the method being used -- should we move that decision up, since
* we're checking here for DR?
*/
/* pick a syscall that is unlikely to be hooked, ref case 5217 Sygate
* requires all int system call to occur in ntdll.dll or sysfer.dll
* so we borrow the int 2e from NtYieldExecution for system calls!
* (both our own and the apps via shared_syscall). The Nt* wrappers
* are stdcall so NtYieldExecution is convenient since it has zero
* args and is unlikely to be hooked. Ref case 5441, Sygate also sometimes
* verifies the top of the stack for sysenter system calls in a similar
* fashion (must be in ntdll/sysfer). For that we again borrow out of
* NtYieldExecution (this time just the ret) to fix up our stack. */
GET_NTDLL(NtYieldExecution, (VOID));
/* N.B.: if we change which syscall, for WOW64 the wrapper can change */
app_pc pc = (app_pc)NtYieldExecution;
app_pc int_target = pc + 9;
ushort check = *((ushort *)(int_target));
HMODULE ntdllh = get_ntdll_base();
if (!windows_version_init(syscalls_init_get_num(ntdllh, SYS_GetContextThread),
syscalls_init_get_num(ntdllh, SYS_AllocateVirtualMemory)))
return false;
ASSERT(syscalls != NULL);
/* We check the 10th and 11th bytes to identify the gateway.
* XXX i#1854: we should try and reduce how fragile we are wrt small
* changes in syscall wrapper sequences.
*
* int 2e: {2k}
* 77F97BFA: B8 BA 00 00 00 mov eax,0BAh
* 77F97BFF: 8D 54 24 04 lea edx,[esp+4]
* 77F97C03: CD 2E int 2Eh
* ret (stdcall)
* sysenter: {xpsp[0,1] 2k3sp0}
* 0x77f7eb23 b8 77 00 00 00 mov $0x00000077 -> %eax
* 0x77f7eb28 ba 00 03 fe 7f mov $0x7ffe0300 -> %edx
* 0x77f7eb2d ff d2 call %edx
* ret (stdcall)
* sysenter: {xpsp2 2k3sp1}
* 0x77f7eb23 b8 77 00 00 00 mov $0x00000077 -> %eax
* 0x77f7eb28 ba 00 03 fe 7f mov $0x7ffe0300 -> %edx
* 0x77f7eb2d ff 12 call [%edx]
* ret (stdcall)
* wow64 xp64 (case 3922):
* 7d61ce3f b843000000 mov eax,0x43
* 7d61ce44 b901000000 mov ecx,0x1
* 7d61ce49 8d542404 lea edx,[esp+0x4]
* 7d61ce4d 64ff15c0000000 call dword ptr fs:[000000c0]
* 7d61ce54 c3 ret
* x64 syscall (PR 215398):
* 00000000`78ef16c0 4c8bd1 mov r10,rcx
* 00000000`78ef16c3 b843000000 mov eax,43h
* 00000000`78ef16c8 0f05 syscall
* 00000000`78ef16ca c3 ret
* win8+ sysenter w/ co-located "inlined" callee:
* 77d7422c b801000000 mov eax,1
* 77d74231 e801000000 call ntdll!NtYieldExecution+0xb (77d74237)
* 77d74236 c3 ret
* 77d74237 8bd4 mov edx,esp
* 77d74239 0f34 sysenter
* 77d7423b c3 ret
* win8 and win8.1 wow64 syscall (has no ecx):
* 777311bc b844000100 mov eax,10044h
* 777311c1 64ff15c0000000 call dword ptr fs:[0C0h]
* 777311c8 c3 ret
* win10 wow64 syscall:
* 77cd9040 b846000100 mov eax,10046h
* 77cd9045 bab0d5ce77 mov edx,offset ntdll!Wow64SystemServiceCall
* 77cd904a ffd2 call edx
* 77cd904c c3 ret
* ntdll!Wow64SystemServiceCall:
* 77ced5b0 648b1530000000 mov edx,dword ptr fs:[30h]
* 77ced5b7 8b9254020000 mov edx,dword ptr [edx+254h]
* 77ced5bd f7c202000000 test edx,2
* 77ced5c3 7403 je ntdll!Wow64SystemServiceCall+0x18 (77ced5c8)
* 77ced5c5 cd2e int 2Eh
* 77ced5c7 c3 ret
* 77ced5c8 eacfd5ce773300 jmp 0033:77CED5CF
* 77ced5cf 41 inc ecx
* win10-1607 wow64:
* ntdll!Wow64SystemServiceCall:
* 77c32330 ff251812cc77 jmp dword ptr [ntdll!Wow64Transition (77cc1218)]
* 0:000> U poi(77cc1218)
* 58787000 ea097078583300 jmp 0033:58787009
* win10-TH2(1511) x64:
* 7ff9`13185630 4c8bd1 mov r10,rcx
* 7ff9`13185633 b843000000 mov eax,43h
* 7ff9`13185638 f604250803fe7f01 test byte ptr [SharedUserData+0x308(`7ffe0308)],1
* 7ff9`13185640 7503 jne ntdll!NtContinue+0x15 (00007ff9`13185645)
* 7ff9`13185642 0f05 syscall
* 7ff9`13185644 c3 ret
* 7ff9`13185645 cd2e int 2Eh
* 7ff9`13185647 c3 ret
*/
if (check == 0x2ecd) {
dr_which_syscall_t = DR_SYSCALL_INT2E;
set_syscall_method(SYSCALL_METHOD_INT);
int_syscall_address = int_target;
/* ASSERT is simple ret (i.e. 0 args) */
ASSERT(*(byte *)(int_target + 2) == 0xc3 /* ret 0 */);
} else if (check == 0x8d00 || check == 0x0000 /* win8 */) {
ASSERT(is_wow64_process(NT_CURRENT_PROCESS));
dr_which_syscall_t = DR_SYSCALL_WOW64;
set_syscall_method(SYSCALL_METHOD_WOW64);
if (check == 0x8d00) /* xp through win7 */
wow64_index = (int *)windows_XP_wow64_index;
DOCHECK(1, {
int call_start_offs = (check == 0x8d00) ? 5 : -4;
ASSERT(*((uint *)(int_target + call_start_offs)) == 0xc015ff64);
ASSERT(*((uint *)(int_target + call_start_offs + 3)) == WOW64_TIB_OFFSET);
});
DOCHECK(1, {
/* We assume syscalls go through teb->WOW32Reserved */
TEB *teb = get_own_teb();
ASSERT(teb != NULL && teb->WOW32Reserved != NULL);
});
# ifdef X64 /* PR 205898 covers 32-bit syscall support */
} else if (check == 0xc305 || check == 0x2504) {
dr_which_syscall_t = DR_SYSCALL_SYSCALL;
set_syscall_method(SYSCALL_METHOD_SYSCALL);
/* ASSERT is syscall */
ASSERT(*(int_target - 1) == 0x0f || *(ushort *)(int_target + 9) == 0x050f);
# endif
} else if (check == 0xff7f &&
/* rule out win10 wow64 */
*(app_pc *)(pc + 6) == VSYSCALL_BOOTSTRAP_ADDR) {
app_pc vsys;
/* verify is call %edx or call [%edx] followed by ret 0 [0xc3] */
ASSERT(*((ushort *)(int_target + 2)) == 0xc3d2 ||
*((ushort *)(int_target + 2)) == 0xc312);
/* Double check use_ki_syscall_routines() matches type of ind call used */
ASSERT((!use_ki_syscall_routines() && *((ushort *)(int_target + 1)) == 0xd2ff) ||
(use_ki_syscall_routines() && *((ushort *)(int_target + 1)) == 0x12ff));
/* verify VSYSCALL_BOOTSTRAP_ADDR */
IF_X64(ASSERT_NOT_IMPLEMENTED(false));
ASSERT(*((uint *)(int_target - 3)) == (uint)(ptr_uint_t)VSYSCALL_BOOTSTRAP_ADDR);
/* DrM i#1724 (and old case 5463): old hardware, or virtualized hardware,
* may not suport sysenter.
* Thus we need to drill down into the vsyscall code itself:
* 0x7ffe0300 8b d4 mov %esp -> %edx
* 0x7ffe0302 0f 34 sysenter
* Versus:
* 0x7c90e520 8d 54 24 08 lea edx,[esp+8]
* 0x7c90e524 cd 2e int 2Eh
* XXX: I'd like to use d_r_safe_read() but that's not set up yet.
*/
if (*((ushort *)(int_target + 1)) == 0xd2ff) {
vsys = VSYSCALL_BOOTSTRAP_ADDR;
} else {
vsys = *(app_pc *)VSYSCALL_BOOTSTRAP_ADDR;
}
if (*((ushort *)(vsys + 2)) == 0x340f) {
sysenter_ret_address = (app_pc)int_target + 3; /* save addr of ret */
/* i#537: we do not support XPSP{0,1} wrt showing the skipped ret,
* which requires looking at the vsyscall code.
*/
KiFastSystemCallRet_address =
(app_pc)d_r_get_proc_address(ntdllh, "KiFastSystemCallRet");
set_syscall_method(SYSCALL_METHOD_SYSENTER);
dr_which_syscall_t = DR_SYSCALL_SYSENTER;
} else {
dr_which_syscall_t = DR_SYSCALL_INT2E;
set_syscall_method(SYSCALL_METHOD_INT);
int_syscall_address = int_target;
ASSERT(*(byte *)(vsys + 6) == 0xc3 /* ret 0 */);
}
} else if (check == 0xc300 || check == 0xc200) {
/* win8: call followed by ret */
IF_X64(ASSERT_NOT_IMPLEMENTED(false));
/* kernel returns control to KiFastSystemCallRet, not local sysenter, of course */
sysenter_ret_address =
(app_pc)d_r_get_proc_address(ntdllh, "KiFastSystemCallRet");
ASSERT(sysenter_ret_address != NULL);
KiFastSystemCallRet_address =
(app_pc)d_r_get_proc_address(ntdllh, "KiFastSystemCallRet");
set_syscall_method(SYSCALL_METHOD_SYSENTER);
dr_which_syscall_t = DR_SYSCALL_SYSENTER;
} else {
/* win10 wow64 */
app_pc tgt;
ASSERT(*(ushort *)(pc + 10) == 0xd2ff);
ASSERT(is_wow64_process(NT_CURRENT_PROCESS));
tgt = *(app_pc *)(pc + 6);
dr_which_syscall_t = DR_SYSCALL_WOW64;
set_syscall_method(SYSCALL_METHOD_WOW64);
wow64_syscall_call_tgt = tgt;
}
/* Prime use_ki_syscall_routines() */
use_ki_syscall_routines();
if (syscalls == windows_unknown_syscalls ||
/* There are variations within the versions we have (e.g., i#4587), so our
* static arrays are not foolproof. It is simpler to just get the ground truth
* for any moderately recent version.
*/
get_os_version() >= WINDOWS_VERSION_10_1511) {
/* i#1598: try to work on new, unsupported Windows versions */
int i;
app_pc wrapper;
ASSERT(ntdllh != NULL);
for (i = 0; i < SYS_MAX; i++) {
if (syscalls[i] == SYSCALL_NOT_PRESENT) /* presumably matches known ver */
continue;
wrapper = (app_pc)d_r_get_proc_address(ntdllh, syscall_names[i]);
if (wrapper != NULL && !ALLOW_HOOKER(wrapper)) {
syscalls[i] = *((int *)((wrapper) + SYSNUM_OFFS));
}
/* We ignore TestAlert complications: we don't call it anyway */
}
} else {
/* Quick sanity check that the syscall numbers we care about are what's
* in our static array. We still do our later full-decode sanity checks.
* This will always be true if we went through the wrapper loop above.
*/
DOCHECK(1, {
int i;
ASSERT(ntdllh != NULL);
for (i = 0; i < SYS_MAX; i++) {
if (syscalls[i] == SYSCALL_NOT_PRESENT)
continue;
/* note that this check allows a hooker so we'll need a
* better way of determining syscall numbers
*/
app_pc wrapper = (app_pc)d_r_get_proc_address(ntdllh, syscall_names[i]);
CHECK_SYSNUM_AT((byte *)d_r_get_proc_address(ntdllh, syscall_names[i]),
i);
}
});
}
return true;
}
/* Returns true if machine is using the Ki*SysCall routines (indirection via vsyscall
* page), false otherwise.
*
* XXX: on win8, KiFastSystemCallRet is used, but KiFastSystemCall is never
* executed even though it exists. This routine returns true there (we have not
* yet set up the versions so can't just call get_os_version()).
*/
bool
use_ki_syscall_routines()
{
/* FIXME - two ways to do this. We could use the byte matching above in
* syscalls_init to match call edx vs call [edx] or we could check for the
* existence of the Ki*SystemCall* routines. We do the latter and have
* syscalls_init assert that the two methods agree. */
/* We use KiFastSystemCall, but KiIntSystemCall and KiFastSystemCallRet would
* work just as well. */
static generic_func_t ki_fastsyscall_addr = (generic_func_t)PTR_UINT_MINUS_1;
if (ki_fastsyscall_addr == (generic_func_t)PTR_UINT_MINUS_1) {
ki_fastsyscall_addr = d_r_get_proc_address(get_ntdll_base(), "KiFastSystemCall");
ASSERT(ki_fastsyscall_addr != (generic_func_t)PTR_UINT_MINUS_1);
}
return (ki_fastsyscall_addr != NULL);
}
static void
nt_get_context_extended_functions(app_pc base)
{
if (YMM_ENABLED()) { /* indicates OS support, not just processor support */
ntdll_RtlGetExtendedContextLength =
(ntdll_RtlGetExtendedContextLength_t)d_r_get_proc_address(
base, "RtlGetExtendedContextLength");
ntdll_RtlInitializeExtendedContext =
(ntdll_RtlInitializeExtendedContext_t)d_r_get_proc_address(
base, "RtlInitializeExtendedContext");
ntdll_RtlLocateLegacyContext =
(ntdll_RtlLocateLegacyContext_t)d_r_get_proc_address(
base, "RtlLocateLegacyContext");
ASSERT(ntdll_RtlGetExtendedContextLength != NULL &&
ntdll_RtlInitializeExtendedContext != NULL &&
ntdll_RtlLocateLegacyContext != NULL);
}
}
static void
nt_init_dynamic_syscall_wrappers(app_pc base)
{
NtGetNextThread = (NtGetNextThread_t)d_r_get_proc_address(base, "NtGetNextThread");
}
#endif /* !NOT_DYNAMORIO_CORE_PROPER */
void
ntdll_init()
{
/* FIXME: decode kernel32!TlsGetValue and get the real offset
* from there?
*/
ASSERT(offsetof(TEB, TlsSlots) == TEB_TLS64_OFFSET);
#if !defined(NOT_DYNAMORIO_CORE_PROPER) && !defined(NOT_DYNAMORIO_CORE)
nt_init_dynamic_syscall_wrappers((app_pc)get_ntdll_base());
nt_get_context_extended_functions((app_pc)get_ntdll_base());
#endif
}
/* note that this function is called even on the release fast exit path
* (via os_exit) and thus should only do necessary cleanup without ifdef
* DEBUG, but also be carefull about ifdef DEBUG since Detach wants to remove
* as much of us as possible
*/
void
ntdll_exit(void)
{
#if !defined(NOT_DYNAMORIO_CORE_PROPER) && !defined(NOT_DYNAMORIO_CORE)
tls_exit();
set_ntdll_base(NULL);
if (doing_detach) {
own_peb = NULL;
sysenter_tls_offset = 0xffffffff;
nt_wrappers_intercepted = true;
}
#endif
}
/* export this if needed elsewhere */
static NTSTATUS
query_thread_info(HANDLE h, THREAD_BASIC_INFORMATION *info)
{
NTSTATUS res;
ULONG got;
memset(info, 0, sizeof(THREAD_BASIC_INFORMATION));
res = NT_SYSCALL(QueryInformationThread, h, ThreadBasicInformation, info,
sizeof(THREAD_BASIC_INFORMATION), &got);
ASSERT(!NT_SUCCESS(res) || got == sizeof(THREAD_BASIC_INFORMATION));
return res;
}
/* Get a segment descriptor. This code assumes the selector is set
* appropriately in entry->Selector */
NTSTATUS
query_seg_descriptor(HANDLE hthread, DESCRIPTOR_TABLE_ENTRY *entry)
{
NTSTATUS res;
ULONG got;
res = NT_SYSCALL(QueryInformationThread, hthread, ThreadDescriptorTableEntry, entry,
sizeof(DESCRIPTOR_TABLE_ENTRY), &got);
/* This call only writes the LDT_ENTRY portion of the table entry */
ASSERT(!NT_SUCCESS(res) || got == sizeof(LDT_ENTRY));
return res;
}
/* Get a win32 start address. NOTE: According to Nebbet, the value
* retrieved with ThreadQuerySetWin32StartAddress is invalid if the
* thread has call ZwReplyWaitReplyPort or ZwReplyWaitReceivePort.
*/
NTSTATUS
query_win32_start_addr(HANDLE hthread, PVOID start_addr)
{
NTSTATUS res;
ULONG got;
res = NT_SYSCALL(QueryInformationThread, hthread, ThreadQuerySetWin32StartAddress,
start_addr, sizeof(app_pc), &got);
ASSERT(!NT_SUCCESS(res) || got == sizeof(PVOID));
return res;
}
/* Collects system information available through the NtQuerySystemInformation
* system call.
*/
NTSTATUS
query_system_info(DR_PARAM_IN SYSTEM_INFORMATION_CLASS info_class,
DR_PARAM_IN int info_size, DR_PARAM_OUT PVOID info)
{
NTSTATUS result;
ULONG bytes_received = 0;
GET_NTDLL(NtQuerySystemInformation,
(DR_PARAM_IN SYSTEM_INFORMATION_CLASS info_class, DR_PARAM_OUT PVOID info,
DR_PARAM_IN ULONG info_size, DR_PARAM_OUT PULONG bytes_received));
result = NtQuerySystemInformation(info_class, info, info_size, &bytes_received);
return result;
}
/* since not exporting get_own_teb() */
#ifndef NOT_DYNAMORIO_CORE
thread_id_t
d_r_get_thread_id()
{
return (thread_id_t)get_own_teb()->ClientId.UniqueThread;
}
process_id_t
get_process_id()
{
return (process_id_t)get_own_teb()->ClientId.UniqueProcess;
}
int
get_last_error()
{
return get_own_teb()->LastErrorValue;
}
void
set_last_error(int error)
{
get_own_teb()->LastErrorValue = error;
}
#endif /* !NOT_DYNAMORIO_CORE */
HANDLE
get_stderr_handle()
{
HANDLE herr = get_own_peb()->ProcessParameters->StdErrorHandle;
if (herr == NULL)
return INVALID_HANDLE_VALUE;
return herr;
}
HANDLE
get_stdout_handle()
{
HANDLE hout = get_own_peb()->ProcessParameters->StdOutputHandle;
if (hout == NULL)
return INVALID_HANDLE_VALUE;
return hout;
}
HANDLE
get_stdin_handle()
{
HANDLE hin = get_own_peb()->ProcessParameters->StdInputHandle;
if (hin == NULL)
return INVALID_HANDLE_VALUE;
return hin;
}
thread_exited_status_t
is_thread_exited(HANDLE hthread)
{
LARGE_INTEGER timeout;
wait_status_t result;
/* Keep the timeout small, just want to check if signaled. Don't want to wait at all
* really, but no way to specify that. Note negative => relative time offset (so is
* a 1 millisecond timeout). */
timeout.QuadPart = -((int)1 * TIMER_UNITS_PER_MILLISECOND);
if (thread_id_from_handle(hthread) == (thread_id_t)PTR_UINT_MINUS_1) {
/* not a thread handle */
ASSERT(false && "Not a valid thread handle.");
return THREAD_EXIT_ERROR;
}
if (!TEST(SYNCHRONIZE, nt_get_handle_access_rights(hthread))) {
/* Note that our own thread handles will have SYNCHRONIZE since, like
* THREAD_TERMINATE, that seems to be a right the thread can always get for
* itself (prob. due to how stacks are freed). So only a potential issue with
* app handles for which we try to dup with the required rights. xref 9529 */
HANDLE ht = INVALID_HANDLE_VALUE;
NTSTATUS res = duplicate_handle(NT_CURRENT_PROCESS, hthread, NT_CURRENT_PROCESS,
&ht, SYNCHRONIZE, 0, 0);
if (!NT_SUCCESS(res)) {
ASSERT_CURIOSITY(false && "Unable to check if thread has exited.");
return THREAD_EXIT_ERROR;
}
result = nt_wait_event_with_timeout(ht, &timeout);
close_handle(ht);
} else {
result = nt_wait_event_with_timeout(hthread, &timeout);
}
if (result == WAIT_SIGNALED)
return THREAD_EXITED;
if (result == WAIT_TIMEDOUT)
return THREAD_NOT_EXITED;
ASSERT(result == WAIT_ERROR);
ASSERT_CURIOSITY(false && "is_thread_exited() unknown error");
return THREAD_EXIT_ERROR;
}
/* The other ways to get thread info, like OpenThread and Toolhelp, don't
* let you go from handle to id (remember handles can be duplicated and
* there's no way to tell equivalence), plus are only on win2k.
* Returns POINTER_MAX on failure
*/
thread_id_t
thread_id_from_handle(HANDLE h)
{
THREAD_BASIC_INFORMATION info;
NTSTATUS res = query_thread_info(h, &info);
if (!NT_SUCCESS(res))
return POINTER_MAX;
else
return (thread_id_t)info.ClientId.UniqueThread;
}
/* export this if needed elsewhere */
static NTSTATUS
query_process_info(HANDLE h, PROCESS_BASIC_INFORMATION *info)
{
NTSTATUS res;
ULONG got;
memset(info, 0, sizeof(PROCESS_BASIC_INFORMATION));
res = NtQueryInformationProcess(h, ProcessBasicInformation, info,
sizeof(PROCESS_BASIC_INFORMATION), &got);
ASSERT(!NT_SUCCESS(res) || got == sizeof(PROCESS_BASIC_INFORMATION));
return res;
}
/* Returns POINTER_MAX on failure */
process_id_t
process_id_from_handle(HANDLE h)
{
PROCESS_BASIC_INFORMATION info;
NTSTATUS res = query_process_info(h, &info);
if (!NT_SUCCESS(res))
return POINTER_MAX;
else
return (process_id_t)info.UniqueProcessId;
}
/* Returns POINTER_MAX on failure */
process_id_t
process_id_from_thread_handle(HANDLE h)
{
THREAD_BASIC_INFORMATION info;
NTSTATUS res = query_thread_info(h, &info);
if (!NT_SUCCESS(res))
return POINTER_MAX;
else
return (process_id_t)info.ClientId.UniqueProcess;
}
HANDLE
process_handle_from_id(process_id_t pid)
{
NTSTATUS res;
HANDLE h;
OBJECT_ATTRIBUTES oa;
CLIENT_ID cid;
InitializeObjectAttributes(&oa, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
memset(&cid, 0, sizeof(cid));
cid.UniqueProcess = (HANDLE)pid;
res = nt_raw_OpenProcess(&h, PROCESS_ALL_ACCESS, &oa, &cid);
if (!NT_SUCCESS(res)) {
NTPRINT("nt_open_process failed: %x\n", res);
}
if (!NT_SUCCESS(res))
return INVALID_HANDLE_VALUE;
else
return h;
}
#if !defined(NOT_DYNAMORIO_CORE_PROPER) && !defined(NOT_DYNAMORIO_CORE)
HANDLE
thread_handle_from_id(thread_id_t tid)
{
NTSTATUS res;
HANDLE h;
OBJECT_ATTRIBUTES oa;
CLIENT_ID cid;
InitializeObjectAttributes(&oa, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
memset(&cid, 0, sizeof(cid));
cid.UniqueThread = (HANDLE)tid;
res = nt_raw_OpenThread(&h, THREAD_ALL_ACCESS, &oa, &cid);
if (!NT_SUCCESS(res)) {
NTPRINT("nt_open_thread failed: %x\n", res);
}
if (!NT_SUCCESS(res))
return INVALID_HANDLE_VALUE;
else
return h;
}
#endif
/* PEB:
* for a running thread this is stored at fs:[30h]
* it's always at 0x7FFDF000 according to InsideWin2k p.290
* but that's out of date, is randomized within 0x7ffd... on XPsp2
* so use query_process_info to get it
*/
PEB *
get_peb(HANDLE h)
{