1

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()?

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
  • 2
    **If it is undocumented you should not use it** (unless you have legitimate access to Windows source code). Remember that your time (or the time of the developer succeeding to you) is much more valuable and costly than computer time. Use *documented* and *stable* APIs, in particular the [WinAPI](https://learn.microsoft.com/en-us/windows/desktop/apiindex/windows-api-list) or POSIX [Pthreads](https://en.wikipedia.org/wiki/POSIX_Threads) or C++11 [threads](https://en.cppreference.com/w/cpp/thread) – Basile Starynkevitch Jun 24 '19 at 10:56
  • And you want to make a home project above low level thread functions, switch to some [Linux distribution](https://en.wikipedia.org/wiki/Linux_distribution), because their standard C or C++ libraries and OS kernel code are [open source](https://en.wikipedia.org/wiki/Open_source) and you are invited to study the source code – Basile Starynkevitch Jun 24 '19 at 11:03
  • Notice that anyone capable to answer to your question (unless he works at MicroSoft and get permission from his/her boss to answer here) is putting himself at legal risk. Those who have access to Microsoft source code have signed some [NDA](https://en.wikipedia.org/wiki/Non-disclosure_agreement) – Basile Starynkevitch Jun 24 '19 at 11:04
  • 1
    Also, [reverse engineering](https://en.wikipedia.org/wiki/Reverse_engineering) the proprietary library or DLL defining `ZwCreateThread` could be illegal in several countries (including the USA) – Basile Starynkevitch Jun 24 '19 at 11:10
  • 4
    I'd recommend you get the book [Windows NT/2000 Native API Reference](https://www.amazon.ca/Windows-2000-Native-API-Reference/dp/1578701996) by Gary Nebbett. He covers this topic with example code on calling `ZwCreateThread` – Michael Petch Jun 24 '19 at 12:19
  • If it is in User Mode, it is **NtCreateThread**.. You can find samples on Google, but use [CreateThread](https://learn.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createthread) instead – Castorix Jun 24 '19 at 15:21
  • 1
    @Castorix if it's in user mode both exports from ntdll.dll are _exactly the same_. In kernel mode the difference is the diligence with which arguments are checked (user mode isn't to be trusted). Otherwise you are welcome to ask actual reverse engineering questions over on [RE.SE](https://reverseengineering.stackexchange.com/). – 0xC0000022L May 18 '22 at 20:44
  • First 4 comments are brain dead – EatingTechnobladesRemainsAt3am May 10 '23 at 16:22

1 Answers1

4

The question is a loaded one

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?!

Resources

I cannot answer regarding , 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:

  • undocumented.ntinternals.net
  • the phnt offspring of the Process Hacker (mind the license!)
  • ReactOS tries to be a fairly faithful functional clone of Windows (the target version has been adjusted over the years)
    • The desired information is part of the NDK inside psfuncs.h
  • The Windows Research Kernel and its documentation have leaked and were available in several shapes and forms all over the web (IIRC this was based on Windows 2003 Server)
  • Microsoft documents some functions and structs as part of their Windows Driver Kits (WDKs, formerly Device Driver Kits [DDKs])
    • winternl.h is one of the headers
  • "Windows NT/2000 Native API Reference" by Gary Nebbett
  • "Undocumented Windows 2000 Secrets" by Sven B. Schreiber; IIRC the author made this available on his website as several PDFs some years ago
  • "Undocumented Windows NT" by Prasad Dabak, Sandeep Phadke, and Milind Borate
  • The syscall tables (j00ru/windows-syscalls) as a reference of sorts for availability of functions from user mode
  • For structs as found from the PDBs (debugging symbols) made available by Microsoft: Vergilius Project
  • Yet again for structs, but more useful for user mode: Terminus Project by ReWolf
  • Windows NT, Secret APIs and the Consequences by Bruce Ediger
  • To a lesser extent the "Windows Internals" books from Microsoft Press
  • A code search or GitHub/GitLab/etc search for some of your desired function names or related functions
    • E.g. in the source code to several debuggers or other very low-level tools, to binary instrumentation frameworks etc ...
  • Books and resources for related topics such as sandboxes, rootkits, bootkits, malware ...

Useful knowledge

  • ZwCreateThread 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
    • This means their prototype is identical!
  • In kernel mode they differ, one of them does more checking and is supposed to receive calls via the SSDT from user mode, the other is supposed to be called from kernel mode.
    • The identical prototype notion also holds true here

... let's try to answer it

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.

3rd parameter: _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.

5th parameter: _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;

6th parameter: _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).

7th parameter: _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.

Conclusion

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.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152