There is another option you might consider. As can be seen from the CreateProcessInternal definition, Windows informs the CSRSS subsystem of a process being created through CsrClientCallServer (via BasepNotifyCsrOfCreation) and the LPC message system.
CsrClientCallServer messages are also used with MANY other API's, including CreateThread, CreateRemoteThread, ExitProcess, etc., as well as several of the Console API's.
The path is CsrClientCallServer -> NtRequestWaitReplyPort. The latter can be SSDT hooked as ZwRequestWaitReplyPort with all the advantages of kernel mode, though I suppose it could be done at the ntdll level as well. Individual LPC messages can be filtered based on an ApiNumber.
Here is the ReactOS definition of CsrClientCallServer:
Code:
NTSTATUS STDCALL CsrClientCallServer(PCSR_API_MESSAGE Request,
PVOID CapturedBuffer OPTIONAL,
CSR_API_NUMBER ApiNumber,
ULONG RequestLength)
Disassemble kernel32 and find the XREFS for CsrClientCallServer and you will find the various ApiNumbers, i.e.
Code:
// Selected CSR Api Numbers used with CsrClientCallServer
#define BasepCreateProcess 0x10000 // CreateProcessInternalW
#define BasepCreateThread 0x10001 // CreateThread / CreateRemoteThread
#define BasepGetTempFile 0x10002 // GetTempFileNameW
#define BasepExitProcess 0x10003 // ExitProcess
// Win2K only
#define BasepDebugProcess 0x10004 // DebugActiveProcess
#define BasepSetProcessShutdownParam 0x1000C // SetProcessShutdownParameters
#define BasepGetProcessShutdownParam 0x1000D // GetProcessShutdownParameters
// The following two ApiNumbers are used with the internal function _CsrBasepNlsGetUserInfo
// _CsrBasepNlsGetUserInfo is called by other functions, but for our interests the
// path is part of the general Process creation and loading routine:
// ??? -> _NlsDllInitialize@12 -> _NlsProcessInitialize@0 -> _CsrBasepNlsGetUserInfo
// The values are different for Win2K and XP
#define BasepNlsGetUserInfo_2K 0x1001A // _CsrBasepNlsGetUserInfo (Win2K)
#define BasepNlsGetUserInfo_XP 0x1001E // _CsrBasepNlsGetUserInfo (XP)
#define BasepSetClientTimeZoneInformation 0x1001A // SetClientTimeZoneInformation (XP)
#define ConsolepOpenConsole 0x20200 // OpenConsoleW
#define ConsolepReadConsoleInput 0x20201 // ReadConsoleInputA
#define ConsolepWriteConsoleInput 0x20202 // WriteConsoleInputA
#define ConsolepReadConsoleOutput 0x20203 // ReadConsoleOutputA
#define ConsolepWriteConsoleOutput 0x20204 // WriteConsoleOutputA
#define ConsolepReadConsoleOutputString 0x20205 // ReadConsoleOutputCharacterA
#define ConsolepWriteConsoleOutputString 0x20206 // WriteConsoleOutputCharacterA
#define ConsolepReadConsole 0x2021D // ReadConsoleA
#define ConsolepWriteConsole 0x2021E // WriteConsoleA
#define ConsolepVerifyIoHandle 0x20223 // VerifyConsoleIoHandle
#define ConsolepAlloc 0x20251 // AllocConsole
The CSR_API_MESSAGE structure is made up of a fixed length Header and a variable length Data section. This is the definition I used for my project:
Code:
// Main structure defining the type of LPC messages
// processed by CsrClientCallServer -> NtRequestWaitReplyPort
typedef struct _CSR_API_MESSAGE
{
LPC_MESSAGE_HEADER Header;
struct
{
ULONG Reserved;
ULONG ApiNumber; // CSR_API_NUMBER
union
{
CSRSS_CREATE_PROCESS_LPCMESSAGE CreateProcessRequest; // 0x10000
CSRSS_CREATE_THREAD_LPCMESSAGE CreateThreadRequest; // 0x10001
CSRSS_GETTEMPFILENAME_LPCMESSAGE GetTempFileNameRequest; // 0x10002
CSRSS_EXIT_PROCESS_LPCMESSAGE ExitProcessRequest; // 0x10003
// Win2K only!
CSRSS_DEBUGPROCESS_LPCMESSAGE DebugActiveProcessRequest; // 0x10004
// For Win2K and XP, called as part of process creation
CSRSS_NLSGETUSERINFO_LPCMESSAGE NlsGetUserInfoRequest; // 0x1001A (Win2K)
// 0x1001E (XP)
};
} Data;
} CSR_API_MESSAGE, *PCSR_API_MESSAGE;
The Header:
Code:
typedef struct _LPC_MESSAGE_HEADER {
USHORT DataSize;
USHORT MessageSize;
USHORT MessageType;
USHORT VirtualRangesOffset;
CLIENT_ID ClientId;
ULONG MessageId;
ULONG SectionSize;
// UCHAR Data[1];
} LPC_MESSAGE_HEADER, *PLPC_MESSAGE_HEADER;
And a few of the more useful undocumented LPC_MESSAGE Data structures. These are my proposed working definitions that may be incomplete.
Code:
// Structure defining the LPC_MESSAGE Data used with
// CreateProcessW -> CsrClientCallServer -> NtRequestWaitReplyPort
typedef struct _CSRSS_CREATE_PROCESS_LPCMESSAGE {
ULONG ReturnStatus; // for Win2K ONLY this is lpStartAddress in the Request message
ULONG Unknown1;
PROCESS_INFORMATION lpProcessInformation;
CLIENT_ID DebuggerClientId; // valid if debugger attached to process
ULONG dwCreationFlags;
ULONG Unknown2;
ULONG Unknown3;
ULONG Unknown4;
} CSRSS_CREATE_PROCESS_LPCMESSAGE, *PCSRSS_CREATE_PROCESS_LPCMESSAGE;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
ULONG dwProcessId;
ULONG dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION;
Code:
// Structure defining the LPC_MESSAGE Data used with
// CreateThread -> CreateRemoteThread -> CsrClientCallServer -> NtRequestWaitReplyPort
typedef struct _CSRSS_CREATE_THREAD_LPCMESSAGE {
ULONG ReturnStatus;
ULONG Unknown1;
HANDLE hThread;
CLIENT_ID ClientId;
} CSRSS_CREATE_THREAD_LPCMESSAGE, *PCSRSS_CREATE_THREAD_LPCMESSAGE;
Code:
// Structure defining the LPC_MESSAGE Data used with
// ExitProcess -> CsrClientCallServer -> NtRequestWaitReplyPort
typedef struct _CSRSS_EXIT_PROCESS_LPCMESSAGE {
ULONG ReturnStatus; // uExitCode;
ULONG Unknown1;
ULONG ErrorStatus;
} CSRSS_EXIT_PROCESS_LPCMESSAGE, *PCSRSS_EXIT_PROCESS_LPCMESSAGE;
Code:
// Structure defining the LPC_MESSAGE Data used with
// DebugActiveProcess -> CsrClientCallServer -> NtRequestWaitReplyPort
// Win2K Only!
typedef struct _CSRSS_DEBUGPROCESS_LPCMESSAGE {
ULONG ReturnStatus;
ULONG Unknown1; // FOR OLLY: offset of abnormal_termination in User32 (stack unwind)
ULONG dwProcessId;
CLIENT_ID DebuggerClientId;
ULONG AttachCompleteRoutine; // offset of BaseAttachComplete
} CSRSS_DEBUGPROCESS_LPCMESSAGE, *PCSRSS_DEBUGPROCESS_LPCMESSAGE;
Code:
typedef NTSTATUS (*ZWREQUESTWAITREPLYPORT)(
HANDLE PortHandle,
PLPC_MESSAGE Request,
PLPC_MESSAGE Reply
);
So what does this all mean? It means you can hook ZwRequestWaitReplyPort and filter the LPC messages based on ApiNumber. Therefore you've got a single hook to intercept both process and thread creation (as well as any of the other funky API's you might want to trap).
I used it for a number of reasons. Firstly, I simply dumped the raw LPC message output for everything, just to see what information was available. I also used the filtered hook as a location for injecting a dll or shellcode or tracing TLS Callbacks, or for preventing a process or thread from running (return STATUS_LPC_REPLY_LOST).
Here is a sample DbgPrint output capturing a process (Test.exe) starting and exiting, as well as a secondary thread being created with CreateThread.
Code:
LPCHOOK: A Process is being started
LPCHOOK: LPC_MESSAGE Header:
DataSize:0xa8
MessageSize:0xc4
MessageType:2 (LPC_REPLY)
VirtualRangesOffset:0
PID:46C (explorer.exe)
TID:404 (Win32StartAddress 75FA533D)
MessageId:7226
SectionSize:0
LPCHOOK: LPC_MESSAGE Data: (42 dwords)
Reserved:0
ApiNumber:10000
ReturnStatus:0
Unknown1:36
hProcess:2c0
hThread:1a8
dwProcessId:378 (Test.exe)
dwThreadId:17C (Win32StartAddress 4019D0)
DebuggerPID:0 ((null))
DebuggerTID:0
dwCreationFlags:4000410
Unknown2:0
Unknown3:21
Unknown4:193d9a4
LPCHOOK: A Thread is being created
LPCHOOK: LPC_MESSAGE Header:
DataSize:0x1c
MessageSize:0x38
MessageType:2 (LPC_REPLY)
VirtualRangesOffset:0
PID:378 (Test.exe)
TID:17C (Win32StartAddress 4019D0)
MessageId:72be
SectionSize:0
LPCHOOK: LPC_MESSAGE Data: (7 dwords)
Reserved:0
ApiNumber:10001
ReturnStatus:0
Unknown1:0
hThread:30
ProcessId:378 (Test.exe)
ThreadId:178 (Win32StartAddress 401351)
LPCHOOK: A process is being exited
LPCHOOK: LPC_MESSAGE Header:
DataSize:0x14
MessageSize:0x30
MessageType:2 (LPC_REPLY)
VirtualRangesOffset:0
PID:378 (Test.exe)
TID:17C (Win32StartAddress 4019D0)
MessageId:72cc
SectionSize:0
LPCHOOK: LPC_MESSAGE Data: (5 dwords)
Reserved:0
ApiNumber:10003
uExitCode:0
Unknown1:82
ErrorStatus:0
Here is another thread for reference, be sure to make use of (but confirm on your Windows version) Alex Ionescu's definitions if you decide to play with this stuff.
http://www.woodmann.com/forum/showthread.php?t=11256
Cheers,
Kayaker