Skip to content

Instantly share code, notes, and snippets.

@gmh5225
Forked from senko37/createprocess.cpp
Created February 17, 2024 10:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmh5225/ab00f831ffdf4ef608ab3b6eb0d37250 to your computer and use it in GitHub Desktop.
Save gmh5225/ab00f831ffdf4ef608ab3b6eb0d37250 to your computer and use it in GitHub Desktop.
Create process from KernelMode via APC
typedef struct _STARTUPINFOW {
UINT32 cb;
LPWSTR lpReserved;
LPWSTR lpDesktop;
LPWSTR lpTitle;
UINT32 dwX;
UINT32 dwY;
UINT32 dwXSize;
UINT32 dwYSize;
UINT32 dwXCountChars;
UINT32 dwYCountChars;
UINT32 dwFillAttribute;
UINT32 dwFlags;
UINT16 wShowWindow;
UINT16 cbReserved2;
PVOID lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOW;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
UINT32 dwProcessId;
UINT32 dwThreadId;
} PROCESS_INFORMATION;
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashLinks;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
typedef VOID(*PKNORMAL_ROUTINE)(
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);
typedef VOID(*PKKERNEL_ROUTINE)(
IN PKAPC Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2);
typedef VOID(*PKRUNDOWN_ROUTINE)(
IN PKAPC Apc);
extern "C" void KeInitializeApc(
PRKAPC Apc,
PRKTHREAD Thread,
KAPC_ENVIRONMENT Environment,
PKKERNEL_ROUTINE KernelRoutine,
PKRUNDOWN_ROUTINE RundownRoutine,
PKNORMAL_ROUTINE NormalRoutine,
KPROCESSOR_MODE ProcessorMode,
PVOID NormalContext);
extern "C" BOOLEAN KeInsertQueueApc(
PRKAPC Apc,
PVOID SystemArgument1,
PVOID SystemArgument2,
KPRIORITY Increment);
extern "C" PVOID RtlFindExportedRoutineByName(
PVOID ImageBase,
PCCH RoutineName);
CHAR CreateProcessWShell[]{
0x48, 0x83, 0xEC, 0x58, // sub rsp,0x58
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rax,_PROCESS_INFORMATION
0x48, 0x89, 0x44, 0x24, 0x48, // mov QWORD PTR [rsp+0x48],rax
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rax,_STARTUPINFOW
0x48, 0x89, 0x44, 0x24, 0x40, // mov QWORD PTR [rsp+0x40],rax
0x48, 0xC7, 0x44, 0x24, 0x38, 0x00, 0x00, 0x00, 0x00, // mov QWORD PTR [rsp+0x38],0x0
0x48, 0xC7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00, // mov QWORD PTR [rsp+0x30],0x0
0xC7, 0x44, 0x24, 0x28, 0x00, 0x00, 0x00, 0x00, // mov DWORD PTR [rsp+0x28],0x0
0xC7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, // mov DWORD PTR [rsp+0x20],0x0
0x45, 0x31, 0xC9, // xor r9d,r9d
0x45, 0x31, 0xC0, // xor r8d,r8d
0x31, 0xD2, // xor edx,edx
0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rcx,lpApplicationName
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rax,CreateProcessW
0xFF, 0xD0, // call rax
0x31, 0xC0, // xor eax,eax
0x48, 0x83, 0xC4, 0x58, // add rsp,0x58
0xC3 // ret
};
VOID ApcKernelRoutine(PKAPC Apc, PKNORMAL_ROUTINE* NormalRoutine, PVOID* NormalContext, PVOID* SystemArgument1, PVOID* SystemArgument2) {
if (Apc)
ExFreePoolWithTag(Apc, 0);
}
NTSTATUS RunUsermodeProcess(const wchar_t* Path) {
NTSTATUS Status = STATUS_SUCCESS;
UINT64 Process, ProcessHead;
Status = PsLookupProcessByProcessId((HANDLE)0x4, (PEPROCESS*)&ProcessHead);
if (!NT_SUCCESS(Status))
return Status;
Process = ProcessHead;
bool Found = false;
do {
if (strncmp("explorer.exe", (const char*)(Process + 0x5a8), 12) == 0 && // Process->ImageFileName
*(UINT64*)(Process + 0x840) == 0) { // Process->ExitTime
Found = true;
break;
}
Process = *(UINT64*)(Process + 0x448) - 0x448; // Process->ActiveProcessLinks.Flink
} while (Process != ProcessHead);
ObDereferenceObject((PEPROCESS)ProcessHead);
if (!Found)
return STATUS_NOT_FOUND;
UINT64 Thread, ThreadHead = *(UINT64*)(Process + 0x5e0) - 0x538; // Thread->ThreadListHead.Flink
Thread = ThreadHead;
Found = false;
do {
if (*(UINT32*)(Thread + 0x74) & (1 << 4)) { // Thread.Tcb.MiscFlags & Alertable
Found = true;
break;
}
Thread = *(UINT64*)(Thread + 0x538) - 0x538; // Thread->ThreadListEntry.Flink
} while (Thread != ThreadHead);
if (!Found)
return STATUS_NOT_FOUND;
KAPC_STATE ApcState;
KeStackAttachProcess((PKPROCESS)Process, &ApcState);
PLDR_DATA_TABLE_ENTRY Module, ModuleHead = (PLDR_DATA_TABLE_ENTRY)(*(UINT64*)(*(UINT64*)(Process + 0x550) + 0x18) + 0x10); // Process->Peb->Ldr.InLoadOrderModuleList.Flink;
Module = ModuleHead;
UNICODE_STRING Kernel32Str = RTL_CONSTANT_STRING(L"kernel32.dll");
Found = false;
do {
if (RtlCompareUnicodeString(&Module->BaseDllName, &Kernel32Str, TRUE) == 0) {
Found = true;
break;
}
Module = (PLDR_DATA_TABLE_ENTRY)Module->InLoadOrderLinks.Flink;
} while (Module != ModuleHead);
if (!Found) {
KeUnstackDetachProcess(&ApcState);
return STATUS_NOT_FOUND;
}
PVOID CreateProcessW = RtlFindExportedRoutineByName(Module->DllBase, "CreateProcessW");
if (!CreateProcessW) {
KeUnstackDetachProcess(&ApcState);
return STATUS_NOT_FOUND;
}
PVOID BaseAddress = 0;
SIZE_T RegionSize = 0x1000;
Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(Status)) {
KeUnstackDetachProcess(&ApcState);
return Status;
}
RtlZeroMemory(BaseAddress, RegionSize);
wcscpy((wchar_t*)BaseAddress, Path);
STARTUPINFOW* si = (STARTUPINFOW*)((UINT64)BaseAddress + (wcslen(Path) + 1) * 2);
si->cb = sizeof(STARTUPINFOW);
PROCESS_INFORMATION* pi = (PROCESS_INFORMATION*)((UINT64)si + sizeof(STARTUPINFOW));
PVOID ShellEntry = (PVOID)((UINT64)pi + sizeof(PROCESS_INFORMATION));
*(UINT64*)&CreateProcessWShell[6] = (UINT64)pi;
*(UINT64*)&CreateProcessWShell[21] = (UINT64)si;
*(UINT64*)&CreateProcessWShell[78] = (UINT64)BaseAddress;
*(UINT64*)&CreateProcessWShell[88] = (UINT64)CreateProcessW;
memcpy(ShellEntry, CreateProcessWShell, 105);
PKAPC Apc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC));
if (!Apc) {
ZwFreeVirtualMemory(ZwCurrentProcess(), &BaseAddress, 0, MEM_RELEASE);
KeUnstackDetachProcess(&ApcState);
return STATUS_NO_MEMORY;
}
KeInitializeApc(Apc, (PKTHREAD)Thread, OriginalApcEnvironment, ApcKernelRoutine, 0, (PKNORMAL_ROUTINE)ShellEntry, UserMode, BaseAddress);
if (!KeInsertQueueApc(Apc, 0, 0, IO_NO_INCREMENT)) {
ExFreePoolWithTag(Apc, 0);
ZwFreeVirtualMemory(ZwCurrentProcess(), &BaseAddress, 0, MEM_RELEASE);
Status = STATUS_INVALID_PARAMETER;
}
KeUnstackDetachProcess(&ApcState);
return Status;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment