Create Suspended Process

Hi,
When using the usermode function CreateProcess, I have the option to give CREATE_SUSPENDED in creationFlags parameter.
What I need is to suspend every process that is being created - in kernel mode. Means, the first thread is suspended immediately before it starts to run.
Afterwards I want to get the PID of the newly created process and pass it to some usermode process that will later on resume it.

Using callbacks I always got “STATUS_ACCESS_DENIED” - which means “The callback routines do not reside in a signed kernel binary image”.
I disabled signature enforcement because my driver is not signed.
Is there any safe way of doing so? without getting any deadlocks of course.
Thanks!

xxxxx@gmail.com wrote:

When using the usermode function CreateProcess, I have the option to give CREATE_SUSPENDED in creationFlags parameter.
What I need is to suspend every process that is being created - in kernel mode. Means, the first thread is suspended immediately before it starts to run.
Afterwards I want to get the PID of the newly created process and pass it to some usermode process that will later on resume it.

I assume this is planned for some harebrained security scheme.  The
problem is your user-mode process is not secure.  Any decision-making
done in user-mode can be hacked from user-mode.

Using callbacks I always got “STATUS_ACCESS_DENIED” - which means “The callback routines do not reside in a signed kernel binary image”.

Which callbacks, and exactly where did you get this error?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

> When using the usermode function CreateProcess, I have the option to give

CREATE_SUSPENDED in creationFlags parameter.
What I need is to suspend every process that is being created - in kernel mode.
Means, the first thread is suspended immediately before it starts to run.

FYI, CreateProcess() is just a Win32 API function that makes multiple calls to native API in order to create a Win32 process. Process creation includes at least 5 steps, and, hence, 5 system calls:

1.Opening the target image file

  1. Creating an executable section that is backed up by the above mentioned file

  2. Creating a process that is based upon above mentioned section. It does not yet have any threads in it - for the time being this is just an address space with with the executable file image, as well as NTDLL.DLL, mapped to it .

4.Creating a primary thread of the process, which is created in initially suspended state.

  1. Informing Win32 subsystem about the newly created process

Only at this point the primary thread of a newly-created may be allowed to run, and this is when.
CREATE_SUSPENDED may come into the play

As you can see, your question simply does not make sense in itself, because, from the kernel’s perspective, this is just a sequence of system calls

Anton Bassov

Hi, thanks for the notes.
I’ll post here part of the code that is relevant, and you’ll understand why it didn’t work.
The code:

OB_PREOP_CALLBACK_STATUS
ObjectPreCallback(
IN PVOID RegistrationContext,
IN POB_PRE_OPERATION_INFORMATION OperationInformation
)
{

UNREFERENCED_PARAMETER(RegistrationContext);

if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_CREATE_PROCESS) == PROCESS_CREATE_PROCESS)
{
//Create the process suspended
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess |= PROCESS_SUSPEND_RESUME;

//The line abode didn’t work.

}
}

return OB_PREOP_SUCCESS;
}

VOID
ObjectPostCallback(
IN PVOID RegistrationContext,
IN POB_POST_OPERATION_INFORMATION OperationInformation
)
{
UNREFERENCED_PARAMETER(OperationInformation);
UNREFERENCED_PARAMETER(RegistrationContext);
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, “PostCallback. \n”);
}

NTSTATUS
RegisterCallbackZwCreateProcess(VOID)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING Altitude;
USHORT filterVersion = ObGetFilterVersion();
USHORT registrationCount = 1;
OB_OPERATION_REGISTRATION RegisterOperation;
OB_CALLBACK_REGISTRATION RegisterCallBack;
REG_CONTEXT RegistrationContext;
memset(&RegisterOperation, 0, sizeof(OB_OPERATION_REGISTRATION));
memset(&RegisterCallBack, 0, sizeof(OB_CALLBACK_REGISTRATION));
memset(&RegistrationContext, 0, sizeof(REG_CONTEXT));
RegistrationContext.ulIndex = 1;
RegistrationContext.Version = 120;
if (filterVersion == OB_FLT_REGISTRATION_VERSION) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, “Filter Version is correct.\n”);

RegisterOperation.ObjectType = PsProcessType;
RegisterOperation.Operations = OB_OPERATION_HANDLE_CREATE;
RegisterOperation.PreOperation = ObjectPreCallback;
RegisterOperation.PostOperation = ObjectPostCallback;
RegisterCallBack.Version = OB_FLT_REGISTRATION_VERSION;
RegisterCallBack.OperationRegistrationCount = registrationCount;
RtlInitUnicodeString(&Altitude, L"XXXXXXX");
RegisterCallBack.Altitude = Altitude;
RegisterCallBack.RegistrationContext = &RegistrationContext;
RegisterCallBack.OperationRegistration = &RegisterOperation;
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, “Register Callback Function Entry.\n”);

ntStatus = ObRegisterCallbacks(&RegisterCallBack, &hCallbacks);
if (ntStatus == STATUS_SUCCESS) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, “Register Callback Function Successful.\n”);
}
else {
if (ntStatus == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, “Status Filter Instance Altitude Collision.\n”);
}
if (ntStatus == STATUS_INVALID_PARAMETER) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, “Status Invalid Parameter.\n”);
}
if (ntStatus == STATUS_ACCESS_DENIED) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, “The callback routines do not reside in a signed kernel binary image.\n”);
}
if (ntStatus == STATUS_INSUFFICIENT_RESOURCES) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, “Status Allocate Memory Failed.\n”);
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, “Register Callback Function Failed with 0x%08x\n”, ntStatus);
}
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, “Filter Version is not supported.\n”);
}
return ntStatus;
}


Two problems actually:
Somehow, the code flow started to get to STATUS_ACCESS_DENIED and the registration can’t complete.
Furthermore, before I started debugging the driver (I have no idea, but after I connected the first time with WinDbg - it began with the ACCESS_DENIED problems), the registration completed successfully. But, PROCESS_SUSPEND_RESUME doesn’t seem to do what I want (I know that the callback previously registered fine because of other code run in the ObjectPreCallback).
How can I create every process with its thread suspended?
Thanks

> As you can see, your question simply does not make sense in itself, because, from the kernel’s perspective, this is just a sequence of system calls

Ok, got it.
Which means there is no other “hacky way” in order to do that?
Maybe there is a way to get the thread and inject to it an APC so that when it gets the time quota it will suspend itself?

You might consider PsSetCreateProcessNotifyRoutine.

Mark Roddy

On Wed, Dec 13, 2017 at 6:58 PM, xxxxx@gmail.com <
xxxxx@lists.osr.com> wrote:

Hi, thanks for the notes.
I’ll post here part of the code that is relevant, and you’ll understand
why it didn’t work.
The code:

OB_PREOP_CALLBACK_STATUS
ObjectPreCallback(
IN PVOID RegistrationContext,
IN POB_PRE_OPERATION_INFORMATION OperationInformation
)
{

UNREFERENCED_PARAMETER(RegistrationContext);

if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
if ((OperationInformation->Parameters->
CreateHandleInformation.OriginalDesiredAccess & PROCESS_CREATE_PROCESS)
== PROCESS_CREATE_PROCESS)
{
//Create the process suspended
OperationInformation->Parameters->
CreateHandleInformation.DesiredAccess |= PROCESS_SUSPEND_RESUME;

//The line abode didn’t work.

}
}

return OB_PREOP_SUCCESS;
}

VOID
ObjectPostCallback(
IN PVOID RegistrationContext,
IN POB_POST_OPERATION_INFORMATION OperationInformation
)
{
UNREFERENCED_PARAMETER(OperationInformation);
UNREFERENCED_PARAMETER(RegistrationContext);
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, “PostCallback.
\n”);
}

NTSTATUS
RegisterCallbackZwCreateProcess(VOID)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING Altitude;
USHORT filterVersion = ObGetFilterVersion();
USHORT registrationCount = 1;
OB_OPERATION_REGISTRATION RegisterOperation;
OB_CALLBACK_REGISTRATION RegisterCallBack;
REG_CONTEXT RegistrationContext;
memset(&RegisterOperation, 0, sizeof(OB_OPERATION_REGISTRATION));
memset(&RegisterCallBack, 0, sizeof(OB_CALLBACK_REGISTRATION));
memset(&RegistrationContext, 0, sizeof(REG_CONTEXT));
RegistrationContext.ulIndex = 1;
RegistrationContext.Version = 120;
if (filterVersion == OB_FLT_REGISTRATION_VERSION) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
“Filter Version is correct.\n”);

RegisterOperation.ObjectType = PsProcessType;
RegisterOperation.Operations = OB_OPERATION_HANDLE_CREATE;
RegisterOperation.PreOperation = ObjectPreCallback;
RegisterOperation.PostOperation = ObjectPostCallback;
RegisterCallBack.Version = OB_FLT_REGISTRATION_VERSION;
RegisterCallBack.OperationRegistrationCount =
registrationCount;
RtlInitUnicodeString(&Altitude, L"XXXXXXX");
RegisterCallBack.Altitude = Altitude;
RegisterCallBack.RegistrationContext =
&RegistrationContext;
RegisterCallBack.OperationRegistration =
&RegisterOperation;
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
“Register Callback Function Entry.\n”);

ntStatus = ObRegisterCallbacks(&RegisterCallBack,
&hCallbacks);
if (ntStatus == STATUS_SUCCESS) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
DPFLTR_INFO_LEVEL, “Register Callback Function Successful.\n”);
}
else {
if (ntStatus == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
DPFLTR_ERROR_LEVEL, “Status Filter Instance Altitude Collision.\n”);
}
if (ntStatus == STATUS_INVALID_PARAMETER) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
DPFLTR_ERROR_LEVEL, “Status Invalid Parameter.\n”);
}
if (ntStatus == STATUS_ACCESS_DENIED) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
DPFLTR_ERROR_LEVEL, “The callback routines do not reside in a signed kernel
binary image.\n”);
}
if (ntStatus == STATUS_INSUFFICIENT_RESOURCES) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
DPFLTR_ERROR_LEVEL, “Status Allocate Memory Failed.\n”);
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID,
DPFLTR_ERROR_LEVEL, “Register Callback Function Failed with 0x%08x\n”,
ntStatus);
}
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
“Filter Version is not supported.\n”);
}
return ntStatus;
}


Two problems actually:
Somehow, the code flow started to get to STATUS_ACCESS_DENIED and the
registration can’t complete.
Furthermore, before I started debugging the driver (I have no idea, but
after I connected the first time with WinDbg - it began with the
ACCESS_DENIED problems), the registration completed successfully. But,
PROCESS_SUSPEND_RESUME doesn’t seem to do what I want (I know that the
callback previously registered fine because of other code run in the
ObjectPreCallback).
How can I create every process with its thread suspended?
Thanks


NTDEV is sponsored by OSR

Visit the list online at: http:> showlists.cfm?list=ntdev>
>
> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
> software drivers!
> Details at http:
>
> To unsubscribe, visit the List Server section of OSR Online at <
> http://www.osronline.com/page.cfm?name=ListServer&gt;
></http:></http:>

> Ok, got it. Which means there is no other “hacky way” in order to do that?

Maybe there is a way to get the thread and inject to it an APC so that when it gets the time
quota it will suspend itself?

Well, you can experiment with thread and process creation callbacks if you wish.
PsSetCreateThreadNotifyRoutineEx() seems to be particularly interesting in this context

https://msdn.microsoft.com/en-us/library/windows/hardware/dn957857(v=vs.85).aspx

Pay a special attention to the last sentence. Once the callback is executed in context of the newly created thread, the subsequent fate of the target thread my be decided before the callback returns,
i.e. before the target thread even gets a chance to run…

Anton Bassov

You have to link with /INTEGRITYCHECK to use these APIs.

https://msdn.microsoft.com/en-us/library/dn195769.aspx

-scott
OSR
@OSRDrivers

That looks promising.
What I did is I keep track of every newly created process and every newly created thread in a list, and that way I can recognize a new thread when created in a new process.
One last obstacle - how do I suspend the thread from kernel mode?

Here is my status:
I can catch the first thread in a newly created process, under its context (using the API you gave me above).
Now what I want to do is to suspend the thread, let the usermode program know which process has been created (that I can do using simple synchronized communication with shared events and IOCTLs).
After that, a user mode debugger is opened and attaches to the newly created process (that is suspended in the first place).

I tried using KeDelayExecutionThread, but it puts the thread in a Wait state, and I can’t attach to the process with the debugger.
So it seems I still need to suspend the thread from kernel somehow :confused: