Python ctypes调用CreateThread参数差异及线程ID存储逻辑求证
Great question—let’s unpack this clearly, since it’s a common point of confusion when working with Windows API functions via ctypes.
Your Assumption is 100% Correct
First off: yes, passing ctypes.pointer(ctypes.c_int(0)) as the 6th parameter is explicitly for letting the CreateThread function store the new thread’s identifier in the memory location that pointer points to. That’s exactly what this parameter is designed to do.
Why the Parameter Difference?
The root of the difference lies in the direction and type of each parameter, as defined in the Windows API documentation for CreateThread:
HANDLE CreateThread( [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] SIZE_T dwStackSize, [in] LPTHREAD_START_ROUTINE lpStartAddress, [in, optional] LPVOID lpParameter, [in] DWORD dwCreationFlags, [out, optional] LPDWORD lpThreadId );
Let’s break down the relevant parameters:
- Input optional parameters (like
lpThreadAttributesorlpParameter): These are pointers where you provide data to the function. If you don’t need to specify a value (e.g., default security attributes), you passNULL(which translates toctypes.c_int(0)in Python, sinceNULLis just a zero-valued pointer on Windows). The function doesn’t write to these pointers when you passNULL—it just ignores them. - Output optional parameter (
lpThreadId): This is a pointer to aDWORD(which maps toctypes.c_inton most systems) where the function writes data back to your code. If you passNULL(ctypes.c_int(0)here), you’re telling the function “I don’t need the thread ID, don’t bother writing it.” But if you pass a valid pointer (likectypes.pointer(ctypes.c_int(0))), the function will overwrite the value at that memory address with the actual ID of the newly created thread.
Example of Using the Thread ID
To make this concrete, here’s how you’d actually retrieve the thread ID after calling CreateThread:
import ctypes # Initialize a variable to hold the thread ID thread_id = ctypes.c_int(0) # Call CreateThread, passing a pointer to our variable handle = ctypes.windll.kernel32.CreateThread( ctypes.c_int(0), # lpThreadAttributes (NULL = default) 0, # dwStackSize (0 = default) shellcode_ptr, # lpStartAddress (your shellcode) ctypes.c_int(0), # lpParameter (no argument to thread) 0, # dwCreationFlags (run immediately) ctypes.pointer(thread_id) # lpThreadId (store ID here) ) # Now we can access the actual thread ID print(f"Created thread with ID: {thread_id.value}")
Why That GitHub Comment Was Wrong
It’s common to see outdated or incorrect comments in sample code. The mistake here was likely a misunderstanding of input vs. output parameters—whoever wrote the comment probably didn’t distinguish between passing a NULL for an unused input versus passing a pointer to capture output data.
内容的提问来源于stack exchange,提问作者0x5929

