this might be a really simple question, but somehow I just cannot wrap my head around the answer, and I cannot find any good and related documentations on such topic either.
So I am attempting to do a PoC using Python's ctypes module and the CreateThread method within ctypes.windll.kernel32 class (to do some shellcode injection within a program's memory space)
According to msdn documentationCreateThreadThe 7 parameters are:
- Pointer to Security Attributes
- Initial Stack Size
- Pointer to Start Address
- Pointer to any parameters
- Creation Flag
- Pointer to a value that receives the thread identifier
And all the examples of using python to call c style functions and libs are as such:
thread_handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
Can someone please explain why the last parameter was used as ctypes.pointer(c_int0), while the other null pointer's constant value of integer 0 is used for the other parameters. (eg. ctypes.c_int(0))
Update: Here is a sample code, and this implementation can be seen all over the net:
Line 786 of createThread function call in python
Note at the line of the script linked above, the comments mentioned:
# _Out_opt_ LPDWORD lpThreadId // NULL, so the thread identifier is not returned.
It looks like the author might be wrong when commenting the reference for the CreateThread function call.
Assumption: As per the comment in Mark's answer mentioned, the ThreadID and the ThreadHandle are different, and by passing in a ctypes.pointer(ctypes.c_int(0)) instead of just plain ctypes.c_int(0) (NULL) means that at the int 0 location, will store the thread ID. Can someone confirm this assumption?
The last parameter instantiates a C integer (
c_int(0)
) and passes it as a pointer. This matches the last parameter definition loosely. It should be a DWORD which is typically defined asunsigned long
(c_ulong
in ctypes). Usingctypes.byref
is more efficient than creating a pointer. The parameter is used to return the thread ID as an output parameter, so need the address of an instance of the correct C type to store the ID.Here's a working example that explicitly defines then inputs/outputs of each function with ctypes. Note that
ctypes
has pre-defined Windows types inwintypes
:Here's the code for a simple C function to run as a thread:
Here's the output: