I'm trying to invoke ZwCreateThread()
. But since it is a undocumented function I don't know how to do it. The 3rd, 5th, 6th and 7th argument of the function?
How to initialize those structures to use them in ZwCreateThread()
?
I'm trying to invoke ZwCreateThread()
. But since it is a undocumented function I don't know how to do it. The 3rd, 5th, 6th and 7th argument of the function?
How to initialize those structures to use them in ZwCreateThread()
?
I don't fully agree with the assessment by Basile in the comments that - if something is undocumented - it should not be used. But it requires extra caution for sure.
There is also a relevant bit of information missing from your question: kernel or user mode?!
I cannot answer regarding fasm, but if you know how to read C function prototypes and make those work for you, you can use one of the following resources to find the prototypes:
winternl.h
is one of the headersZwCreateThread
and NtCreateThread
are identical in user mode (you can even verify this by using dumpbin
on ntdll.dll
and checking the RVA of the two exports. This is true for most (if not all) Zw*
and Nt*
functions
Up front: if what you are trying to achieve is to create a user mode thread, in all likelihood RtlCreateUserThread()
is more likely what you are looking for (for reference phnt)!
The prototype for ZwCreateThread
is as follows:
// source: ntzwapi.h from https://github.com/processhacker/phnt
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwCreateThread(
_Out_ PHANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ HANDLE ProcessHandle,
_Out_ PCLIENT_ID ClientId,
_In_ PCONTEXT ThreadContext,
_In_ PINITIAL_TEB InitialTeb,
_In_ BOOLEAN CreateSuspended
);
// for reference, Nebbett gives the prototype as:
NTSYSAPI
NTSTATUS
NTAPI
ZwCreateThread(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN HANDLE ProcessHandle,
OUT PCLIENT_ID ClientId,
IN PCONTEXT ThreadContext,
IN PUSER_STACK UserStack,
IN BOOLEAN CreateSuspended
);
You probably noticed the difference in the parameter 7 (starting at 1) immediately: PUSER_STACK
vs. PINITIAL_TEB
. TEB stands for Thread Environment Block and is the extension of the TIB (IIRC: Thread Information Block). Compare Nebbett's take on the USER_STACK
struct:
typedef struct _USER_STACK {
PVOID FixedStackBase;
PVOID FixedStackLimit;
PVOID ExpandableStackBase;
PVOID ExpandableStackLimit;
PVOID ExpandableStackBottom;
} USER_STACK, *PUSER_STACK;
... with the INITIAL_TEB from undocumented.ntinternals.net you will notice they're the same (also for reference phnt). Nebbett took a guess at the name at the time of his writing. But this right there is the reason why Basile cautions not to use undocumented functions. You have to put in your own research and can't blindly rely on others' research. Also you should apply a very defensive coding style when using those functions.
Either way, Nebbett lists CreateThread()
and CreateRemoteThread()
as related Windows APIs. The latter is obviously a superset of the former and ZwCreateThread()
uses ProcessHandle
for the target process. It's always worthwhile looking for and looking at related Windows APIs.
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes
The _In_opt
(see SAL) tells you, that you may pass a NULL
pointer here. But if you insist on passing the object attributes and know what to put in them: InitializeObjectAttributes()
- a macro - is there to help.
_Out_ PCLIENT_ID ClientId
The _Out_
tells you that you supply the space for this info to be filled. The CLIENT_ID
combines process and thread ID into a struct. The struct can be found in the headers of the WDKs/DDKs (e.g. wdm.h
or ntddk.h
) and looks like this in C:
typedef struct _CLIENT_ID {
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID;
_In_ PCONTEXT ThreadContext
The CONTEXT
is indeed a documented struct and used in official APIs such as SetThreadContext()
. Its definition depends on the target architecture, but can be found in official MS headers.
This struct also plays a big role in exception handling, e.g. when resuming execution after an exception.
For ZwCreateThread()
this defines the initial state of the thread, especially the initial address of the instruction pointer and the initial CPU register states (which explains why it depends on the target architecture).
_In_ PINITIAL_TEB InitialTeb
By now you should have an idea that you need to supply the space and initial values for this struct as well.
And this complicated "having to know and fill in all details" explains why in all likelihood you want to use:
typedef NTSTATUS (NTAPI *PUSER_THREAD_START_ROUTINE)(
_In_ PVOID ThreadParameter
);
NTSYSAPI
NTSTATUS
NTAPI
RtlCreateUserThread(
_In_ HANDLE Process,
_In_opt_ PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
_In_ BOOLEAN CreateSuspended,
_In_opt_ ULONG ZeroBits,
_In_opt_ SIZE_T MaximumStackSize,
_In_opt_ SIZE_T CommittedStackSize,
_In_ PUSER_THREAD_START_ROUTINE StartAddress,
_In_opt_ PVOID Parameter,
_Out_opt_ PHANDLE Thread,
_Out_opt_ PCLIENT_ID ClientId
);
// again taken from phnt
This is much closer to CreateRemoteThreadEx
and similar APIs. You don't have to care about the initial CONTEXT
and don't have to deal with OBJECT_ATTRIBUTES
or INITIAL_TEB
.