* fixed APC delegate

* more progress on native.html

git-svn-id: svn://svn.code.sf.net/p/processhacker/code@1608 21ef857c-d57f-4fe0-8362-d861dc6d29cd
This commit is contained in:
wj32
2009-07-16 06:30:32 +00:00
parent 055f20dbc3
commit 72d38eea0f
5 changed files with 407 additions and 71 deletions
+1 -1
View File
@@ -1885,7 +1885,7 @@ namespace ProcessHacker.Native.Api
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool QueueUserAPC(
[MarshalAs(UnmanagedType.FunctionPtr)]
[In] Action<IntPtr> APC,
[In] ApcRoutine APC,
[In] IntPtr ThreadHandle,
[In] IntPtr Data
);
@@ -26,6 +26,7 @@ using System.Runtime.InteropServices;
namespace ProcessHacker.Native.Api
{
public delegate void ApcCallbackDelegate(NtStatus ioStatus, IntPtr apcContext, IntPtr context);
public delegate void ApcRoutine(IntPtr parameter);
public delegate void TimerApcRoutine(IntPtr context, int lowValue, int highValue);
public delegate void WaitOrTimerCallbackDelegate(IntPtr context, bool timeout);
public delegate void WorkerCallbackDelegate(IntPtr context);
@@ -1,61 +0,0 @@
/*
* Process Hacker -
* heap memory allocation wrapper
*
* Copyright (C) 2008 wj32
*
* 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/>.
*/
using System;
using ProcessHacker.Native.Api;
namespace ProcessHacker.Native
{
/// <summary>
/// Represents a heap memory allocation.
/// </summary>
public sealed class HeapMemoryAlloc : MemoryAlloc
{
public HeapMemoryAlloc(int size)
{
this.Memory = Win32.HeapAlloc(Win32.GetProcessHeap(), 0, size);
if (this.Memory == IntPtr.Zero)
throw new OutOfMemoryException();
base.Size = size;
}
protected override void Free()
{
Win32.HeapFree(Win32.GetProcessHeap(), 0, this);
}
public override void Resize(int newSize)
{
IntPtr newMemory;
newMemory = Win32.HeapReAlloc(Win32.GetProcessHeap(), 0, this, newSize);
if (newMemory == IntPtr.Zero)
throw new OutOfMemoryException();
this.Memory = newMemory;
base.Size = newSize;
}
}
}
@@ -713,7 +713,7 @@ namespace ProcessHacker.Native.Objects
Win32.ThrowLastError();
}
public void QueueApc(Action<IntPtr> action, IntPtr parameter)
public void QueueApc(ApcRoutine action, IntPtr parameter)
{
if (!Win32.QueueUserAPC(action, this, parameter))
Win32.ThrowLastError();
+404 -8
View File
@@ -26,11 +26,15 @@ p {
font-style: italic;
}
h1 {
border-top: solid 3px #000;
}
h2 {
font-size: 20pt;
margin-top: 1.3em;
margin-bottom: 0.3em;
border-top: dotted 3px #333;
border-top: dashed 1px #333;
}
h3 {
@@ -45,10 +49,280 @@ h4 {
</style>
</head>
<body>
<h1 style="font-size: 30pt;">The Definitive Native API Guide</h1>
<h1 style="font-size: 30pt; border: none;">The Definitive Native API Guide</h1>
<p>Written by wj32.</p>
<h1>Native API</h1>
<h1>NT Concepts</h1>
<h3>ALPC Port</h3>
<p>Local Inter-process Communication (LPC) ports are an interprocess communication (IPC) method.
A server process creates a port object and waits for a client to connect to the port. Once
the connection is established, both the client and server receive a handle to a communication
port, a special instance of a port object which can be used to send and receive messages. From
Windows Vista onward, LPC ports have been replaced with ALPC ports (<code>NtAlpc*</code>
system calls). Existing port-related system calls now redirect to the new ALPC port functions.</p>
<p>Related functions:
<code>NtCreatePort</code> (server),
<code>NtCreateWaitablePort</code> (server),
<code>NtConnectPort</code> (client),
<code>NtListenPort</code> (server),
<code>NtAcceptConnectPort</code> (server),
<code>NtRequestWaitReplyPort</code> (client),
<code>NtReplyWaitReceivePort</code> (server),
<code>NtReplyWaitReplyPort</code>,
<code>NtReplyPort</code>.
</p>
<p>Related types:
<code>PORT_MESSAGE</code>,
<code>PORT_VIEW</code>,
<code>REMOTE_PORT_VIEW</code>,
<code>LPCP_PORT_OBJECT</code>.
</p>
<h3>Asynchronous Procedure Calls (APCs)</h3>
<p>Asynchronous procedure calls are functions which execute in the context of a specific thread.
There are two types of APCs, user-mode and kernel-mode. Each thread has two APC queues, one for
each type.</p>
<p>User-mode APCs are queued using <code>NtQueueApcThread</code>. They will not be called
unless an <em>alertable</em> wait is being performed or <code>NtTestAlert</code> is called in the
target thread. In those cases, a flag will be set in the target thread's APC state indicating that
one or more user-mode APCs are pending and the wait operation will be interrupted. When the system
call returns to user-mode, any pending user-mode APCs will be called.</p>
<p>A special use of user-mode APCs is thread termination, where a thread termination APC
(<code>PspExitNormalApc</code>) is inserted into the target thread. <code>KiInsertQueueApc</code>
contains a special case for thread termination and inserts the APC at the beginning of the
user-mode APC queue so that any wait operations in the target thread are interrupted and
the thread is terminated upon exiting the currently executing system service.</p>
<p>Kernel-mode APCs always preempt user-mode code, including user-mode APCs. There are two types of
kernel-mode APCs, normal and special. Normal APCs can be temporarily disabled by using
<code>KeEnterCriticalRegion</code> and both types of APCs can be temporarily disabled by using
<code>KeEnterGuardedRegion</code> or raising the IRQL to <code>APC_LEVEL</code> or higher.</p>
<ul>
<li>Normal kernel-mode APCs run at IRQL = <code>PASSIVE_LEVEL</code> and are inserted at the end
of the kernel-mode APC queue.</li>
<li>Special kernel-mode APCs run at IRQL = <code>APC_LEVEL</code> and are inserted after all existing
special APCs in the kernel-mode APC queue.</li>
</ul>
<p>When a kernel-mode APC is inserted:</p>
<ul>
<li>If the target thread is running, a software interrupt is issued to call any queued kernel-mode
APCs in the thread.</li>
<li>If the target thread is waiting at IRQL = <code>PASSIVE_LEVEL</code> and special kernel-mode APCs
are not disabled, the wait will be interrupted with <code>STATUS_KERNEL_APC</code>. Note that normal
kernel-mode APCs cannot interrupt currently executing kernel-mode APCs which are waiting. Kernel-mode
APCs do not cause wait operations to return; rather, the wait function will be interrupted,
execute any queued kernel-mode APCs, and continue waiting.</li>
</ul>
<p>Normal kernel-mode APCs are used to implement thread suspension.</p>
<h3>Dispatcher Object</h3>
<p>A dispatcher object is one which has two states: <em>signaled</em> and <em>non-signaled</em>.
These objects can be used with standard wait functions such as <code>NtWaitForSingleObject</code> or
<code>NtWaitForMultipleObjects</code>. These functions wait until one or more objects are set to
a signaled state. The dispatcher objects are:</p>
<ul>
<li>Events</li>
<li>Gates (kernel-mode only)</li>
<li>Mutants</li>
<li>Processes</li>
<li>Queues (kernel-mode only)</li>
<li>Semaphores</li>
<li>Threads</li>
<li>Timers</li>
</ul>
<p>Events and gates are the most basic dispatcher objects, consisting of only a dispatcher header.</p>
<p>Note that the dispatcher header of a dispatcher object uses a signed integer field to represent
its signal state. Signal state values greater than 0 are considered to be signaled, while 0 is
considered to be non-signaled. This is useful for objects such as mutants and semaphores which can be
acquired and released multiple times.</p>
<p>Related types:
<code>DISPATCHER_HEADER</code>.
</p>
<h3>Event</h3>
<p>An event is a synchronization object that can be explicitly set to the signaled state. There are two
types of events:</p>
<ul>
<li><strong>Notification event.</strong> When a notification event is set, all waiting threads are
released. The event remains signaled until it is explicitly reset.</li>
<li><strong>Synchronization event.</strong> When a synchronization event is set, a single waiting
thread is released and the event is reset to a non-signaled state. When multiple threads wait
on a synchronization event, there is no guarantee of first-in first-out (FIFO) ordering.</li>
</ul>
<p>Related functions:
<code>NtCreateEvent</code>,
<code>NtOpenEvent</code>,
<code>NtClearEvent</code>,
<code>NtPulseEvent</code>,
<code>NtQueryEvent</code>,
<code>NtResetEvent</code>,
<code>NtSetEvent</code>,
<code>NtSetEventBoostPriority</code>.
</p>
<p>Related types:
<code>EVENT_INFORMATION_CLASS</code>,
<code>EVENT_BASIC_INFORMATION</code>,
<code>KEVENT</code>.
</p>
<h3>Event Pair</h3>
<p>An event pair is a synchronization object containing two events, <em>high</em> and <em>low</em>. The
system provides set, wait, and atomic signal-and-wait functions for event pairs. Note that an event pair
object is not a dispatcher object and cannot be used with the standard wait functions.</p>
<p>Related functions:
<code>NtCreateEventPair</code>,
<code>NtOpenEventPair</code>,
<code>NtSetHighEventPair</code>,
<code>NtSetHighWaitLowEventPair</code>,
<code>NtSetLowEventPair</code>,
<code>NtSetLowWaitHighEventPair</code>,
<code>NtWaitHighEvenPair</code>,
<code>NtWaitLowEventPair</code>.
</p>
<p>Related types:
<code>EEVENT_PAIR</code>.
</p>
<h3>Keyed Event</h3>
<p>A keyed event is a dictionary of events. Each key must be even (the lowest bit must be clear). Internally,
the keyed event object is implemented using a linked list of pointers to threads. Every thread object has
two fields, <code>KeyedWaitValue</code> and <code>KeyedWaitSemaphore</code>. The <code>KeyedWaitValue</code>
contains the key being waited for by the thread. When a thread attempts to release a key which is not being
waited for, its <code>KeyedWaitValue</code> will be set to the key OR'ed with 1, to indicate that the thread
is attempting to release the key, and the thread will wait until another thread waits for the key.</p>
<p>Related functions:
<code>NtCreateKeyedEvent</code>,
<code>NtOpenKeyedEvent</code>,
<code>NtReleaseKeyedEvent</code>,
<code>NtWaitForKeyedEvent</code>.
</p>
<p>Related types:
<code>KEYED_EVENT_OBJECT</code>.
</p>
<h3>Mutant</h3>
<p>A "mutant" is a standard mutex. When a thread successfully waits for a mutant, it will acquire the mutant
and become the owner of the mutant; the mutant will be set to a non-signaled state. When the owning thread
releases the mutant the same number of times it has acquired it, the mutant will be set to a signaled state
and the mutant will no longer be owned, allowing other threads to acquire the mutant. Note that the mutant
can be acquired recursively, i.e. the owning thread can acquire the mutant more than once without causing a
deadlock.</p>
<p>Related functions:
<code>NtCreateMutant</code>,
<code>NtOpenMutant</code>,
<code>NtQueryMutant</code>,
<code>NtReleaseMutant</code>
</p>
<p>Related types:
<code>MUTANT_INFORMATION_CLASS</code>,
<code>MUTANT_BASIC_INFORMATION</code>,
<code>KMUTANT</code>.
</p>
<h3>Port</h3>
<p>See <strong>ALPC Port</strong>.</p>
<h3>Profile</h3>
<p>A profile object can be used for performance monitoring. When certain profiling events are triggered,
a corresponding counter in a user-allocated buffer is incremented.</p>
<p>Related functions:
<code>NtCreateProfile</code>,
<code>NtQueryIntervalProfile</code>,
<code>NtSetIntervalProfile</code>,
<code>NtStartProfile</code>,
<code>NtStopProfile</code>.
</p>
<h3>Section</h3>
<p>Sections are objects describing a region of memory "backed" by a file. There are two types of section
objects:</p>
<ul>
<li><strong>File-backed section.</strong> File-backed sections are memory-mapped files, where mapped
view contents are the same as in the file. Writing to mapped views will also change the contents of the
the file, unless the section is mapped copy-on-write, where any changes are discarded after the last
view is unmapped and the last reference to the section is closed.</li>
<li><strong>Pagefile-backed section.</strong> Page-file-backed sections are a form of shared memory;
any changes will be discarded after the section is freed. The section is not backed by any
user-specified file.</li>
</ul>
<p>Multiple views of the section can be mapped, and changes will be reflected across processes.</p>
<p>Related functions:
<code>NtCreateSection</code>,
<code>NtOpenSection</code>,
<code>NtAreMappedFilesTheSame</code>,
<code>NtExtendSection</code>,
<code>NtMapViewOfSection</code>,
<code>NtQuerySection</code>,
<code>NtUnmapViewOfSection</code>.
</p>
<h3>Semaphore</h3>
<p>A semaphore is a synchronization object with a signal state that represents how many times it has been
acquired. Each time a semaphore is acquired, its signal state is decremented. Each time a semaphore is
released, its signal state is incremented (but cannot be greater than the limit). If a semaphore's
signal state is 0 (non-signaled), threads must wait until another thread releases the semaphore before they
can acquire the semaphore.</p>
<p>Related functions:
<code>NtCreateSemaphore</code>,
<code>NtOpenSemaphore</code>,
<code>NtQuerySemaphore</code>,
<code>NtReleaseSemaphore</code>.
</p>
<p>Related types:
<code>SEMAPHORE_INFORMATION_CLASS</code>,
<code>SEMAPHORE_BASIC_INFORMATION</code>,
<code>KSEMAPHORE</code>.
</p>
<h3>Timer</h3>
<p>A timer is executive object and a wrapper around the kernel timer object. There are two types of timers:</p>
<ul>
<li><strong>Notification timer.</strong> When a notification timer is signaled, all waiting threads are
released. The timer remains signaled until explicitly reset.</li>
<li><strong>Synchronization timer.</strong> When a synchronization timer is signaled, one waiting thread is
released and the timer is set to a non-signaled state.</li>
</ul>
<p>A timer can be configured to be signaled periodically or to insert an APC into the thread that set the
timer when the timer is signaled.</p>
<p>Related functions:
<code>NtCreateTimer</code>,
<code>NtOpenTimer</code>,
<code>NtCancelTimer</code>,
<code>NtQueryTimer</code>,
<code>NtSetTimer</code>.
</p>
<p>Related types:
<code>TIMER_INFORMATION_CLASS</code>,
<code>TIMER_BASIC_INFORMATION</code>,
<code>ETIMER</code>,
<code>KTIMER</code>,
<code>PTIMER_APC_ROUTINE</code>.
</p>
<h3>Wait</h3>
<p>A thread can wait for one or more objects; the standard system calls are <code>NtWaitForSingleObject</code>,
<code>NtWaitForMultipleObjects</code>, <code>NtSignalAndWaitForSingleObject</code>, and a few type-specific
wait functions. The pointer-based kernel-mode functions are <code>KeWaitForSingleObject</code> and
<code>KeWaitForMultipleObjects</code>. These functions will block until a certain condition is met. For
example, <code>*WaitForSingleObject</code> will return when the specified object is signaled.
<code>*WaitForMultipleObjects</code> will return when all/any specified objects are signaled.</p>
<p>When a wait function is called, it initializes a wait block for each object to be waited for. The storage
for the wait blocks is supplied in the thread object by default, but the caller can allocate storage if
they wish. The wait function then checks if the wait can be satisfied immediately. If it could not, the
wait function inserts the wait block(s) into the dispatch header(s) of the object(s), sets the thread's state
to Waiting and will no longer be considered for execution. It then switches to another ready thread.</p>
<p>When an object is set to a signaled state (such as when an event is set or a mutant is released),
the function performs a <em>wait test</em> (<code>KiWaitTest</code>) which enumerates the wait blocks in the
object's dispatcher header and unwaits each waiting thread. Each waiting thread will now be ready to run.</p>
<p>A waiting thread regains control due to either a wait test or a kernel-mode APC. It proceeds to
call any queued kernel-mode APCs and check if the wait operation has been satisfied (for multiple-object
waits, this is when any/all objects have been signaled). If it has not, the wait function continues to repeat
the wait process until the wait operation has been satisfied.</p>
<p>For some object types, object state must be modified when a thread is finished waiting for the object.
For example, a semaphore's signal state must be decremented. These operations are called <em>side-effects</em>,
and are performed when a wait is satisfied.</p>
<h1>NT Enumerations</h1>
<h2>Debug Object Access</h2>
<pre>
@@ -56,8 +330,8 @@ h4 {
#define DEBUG_PROCESS_ASSIGN 0x0002
#define DEBUG_SET_INFORMATION 0x0004
#define DEBUG_QUERY_INFORMATION 0x0008
#define DEBUG_ALL_ACCESS STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | DEBUG_READ_EVENT |
DEBUG_PROCESS_ASSIGN | DEBUG_SET_INFORMATION | DEBUG_QUERY_INFORMATION</pre>
#define DEBUG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | DEBUG_READ_EVENT | \
DEBUG_PROCESS_ASSIGN | DEBUG_SET_INFORMATION | DEBUG_QUERY_INFORMATION)</pre>
<h2>Directory Object Access</h2>
<pre>
@@ -68,6 +342,99 @@ h4 {
#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xf)</pre>
<h2>Event Access</h2>
<pre>
#define EVENT_QUERY_STATE 0x0001
#define EVENT_MODIFY_STATE 0x0002
#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3)</pre>
<h2>Event Pair Access</h2>
<pre>
#define EVENT_PAIR_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE)</pre>
<h2>Keyed Event Access</h2>
<pre>
#define KEYEDEVENT_WAIT 0x0001
#define KEYEDEVENT_WAKE 0x0002
#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE)</pre>
<h2>Mutant Access</h2>
<pre>
#define MUTANT_QUERY_STATE 0x0001
#define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE| MUTANT_QUERY_STATE)</pre>
<h2>Object Flags</h2>
<pre>
#define OBJ_INHERIT 0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_KERNEL_HANDLE 0x00000200L
#define OBJ_FORCE_ACCESS_CHECK 0x00000400L
#define OBJ_VALID_ATTRIBUTES 0x000007f2L</pre>
<h3>Members</h3>
<h4>OBJ_INHERIT</h4>
<p>Specifies that the handle (in the appropriate context) should be inherited by child processes.</p>
<h4>OBJ_PERMANENT</h4>
<p>Specifies that the object is permanent and should not be freed when all references to it have been
closed. If this flag is not specified, the object is temporary and will be freed when all references
have been closed. User-mode callers must have <code>SeCreatePermanentPrivilege</code> in order to
create permanent objects.</p>
<h4>OBJ_EXCLUSIVE</h4>
<p>Specifies that the object should be opened for exclusive access; the object cannot be opened
again until the handle is closed.</p>
<h4>OBJ_CASE_INSENSITIVE</h4>
<p>Specifies that name comparisons should be made case insensitively.</p>
<h4>OBJ_OPENIF</h4>
<p>Specifies that if an object with the specified name already exists, the creation routine should
open the existing object. If this flag is not specified and the name already exists, the creation
routine will return <code>STATUS_OBJECT_NAME_COLLISION</code>.</p>
<h4>OBJ_OPENLINK</h4>
<p>Not used.</p>
<h4>OBJ_KERNEL_HANDLE</h4>
<p>Specifies that the handle should be opened in the context of the System process, i.e. a kernel
handle.</p>
<h4>OBJ_FORCE_ACCESS_CHECK</h4>
<p>Specifies that an access check should be performed, even if the caller is from kernel-mode.</p>
<h2>Profile Access</h2>
<pre>
#define PROFILE_CONTROL 0x0001
#define PROFILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | PROFILE_CONTROL)</pre>
<h2>Section Access</h2>
<pre>
#define SECTION_QUERY 0x0001
#define SECTION_MAP_WRITE 0x0002
#define SECTION_MAP_READ 0x0004
#define SECTION_MAP_EXECUTE 0x0008
#define SECTION_EXTEND_SIZE 0x0010
#define SECTION_MAP_EXECUTE_EXPLICIT 0x0020
#define SECTION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | \
SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | \
SECTION_EXTEND_SIZE)</pre>
<h2>Semaphore Access</h2>
<pre>
#define SEMAPHORE_QUERY_STATE 0x0001
#define SEMAPHORE_MODIFY_STATE 0x0002
#define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3)</pre>
<h2>Timer Access</h2>
<pre>
#define TIMER_QUERY_STATE 0x0001
#define TIMER_MODIFY_STATE 0x0002
#define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \
TIMER_QUERY_STATE | TIMER_MODIFY_STATE)</pre>
<h1>NT Structures</h1>
<h2>CLIENT_ID</h2>
<p>A structure identifying a process or thread.</p>
<pre>
@@ -128,6 +495,33 @@ typedef struct _INITIAL_TEB
<a href="http://www.reactos.org/serendipity/index.php?/archives/15-They-lied-to-you-about-INITIAL_TEB!.html">this blog post</a>
for more details.</p>
<h2>OBJECT_ATTRIBUTES</h2>
<p>A structure describing object properties such as its name, location and security attributes.</p>
<pre>
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;</pre>
<h3>Fields</h3>
<h4>Length</h4>
<p>The length of the <code>OBJECT_ATTRIBUTES</code> structure; 24 on 32-bit systems and 40 on 64-bit systems.</p>
<h4>RootDirectory</h4>
<p>A handle to a directory object from which to begin searching for the object. If this value is <code>NULL</code>,
the object manager will use the default root directory.</p>
<h4>ObjectName</h4>
<p>The name of the object, optional when creating most types of objects.</p>
<h4>Attributes</h4>
<p>See <strong>Object Flags</strong>.</p>
<h4>SecurityDescriptor</h4>
<p>A pointer to a <code>SECURITY_DESCRIPTOR</code> structure for the object.</p>
<h4>SecurityQualityOfService</h4>
<p>A pointer to a <code>SECURITY_QUALITY_OF_SERVICE</code> structure for the object.</p>
<h2>RTL_DRIVE_LETTER_CURDIR</h2>
<p>Unknown.</p>
<pre>
@@ -228,6 +622,8 @@ typedef struct _UNICODE_STRING
<h4>Buffer</h4>
<p>A buffer containing the string.</p>
<h1>NT System Calls</h1>
<h2>NtAlertThread</h2>
<p>Alerts the specified thread, causing it to resume execution if it is in an alertable Wait state.
Otherwise, the thread is set to an alerted state.</p>
@@ -503,7 +899,7 @@ NtCreateThreadEx(
__in_opt ULONG Reserved,
__in_opt ULONG StackCommit,
__in_opt ULONG StackReserve,
__in_opt PVOID Unknown
__in_opt PVOID ProcessContext
);</pre>
<h3>Arguments</h3>
<h4>ThreadHandle</h4>
@@ -529,7 +925,7 @@ NtCreateThreadEx(
<p>The number of bytes to commit in the thread stack.</p>
<h4>StackReserve</h4>
<p>The number of bytes to reserve for the thread stack.</p>
<h4>Unknown</h4>
<h4>ProcessContext</h4>
<p>An optional structure which is passed to <code>PspBuildCreateProcessContext</code>.</p>
<h3>Code paths</h4>
<p><code>NtCreateThreadEx</code> ... <code>PspCreateThread</code> ... <code>PspAllocateThread</code> ...
@@ -572,7 +968,7 @@ NtOpenProcess(
<h2>NtQueueApcThread</h2>
<p>Queues a user-mode APC to the specified thread. The APC will execute when the thread performs an alertable wait or
calls <code>NtTestAlert</code>.</p>
calls <code>NtTestAlert</code>. Any wait operations will return with <code>STATUS_USER_APC</code>.</p>
<pre>
NTSYSCALLAPI
NTSTATUS