{
17. Dezember 2007
by ErazerZ
Original code by soycola "a debuggable CreateProcessW implementation"
}
program xNtCreateThread;
{$APPTYPE CONSOLE}
uses
Windows, JwaNative, JwaWindows;
type
PINITIAL_TEB = ^INITIAL_TEB;
INITIAL_TEB =
record
OldStackBase: PVOID;
OldStackLimit: PVOID;
StackBase: PVOID;
StackLimit: PVOID;
StackAllocationBase: PVOID;
end;
BASE_CONTEXT_TYPE = (BaseContextTypeProcess, BaseContextTypeThread, BaseContextTypeFiber);
PBASE_CONTEXT_TYPE = ^BASE_CONTEXT_TYPE;
const
KGDT_NULL = 0;
KGDT_R0_CODE = 8;
KGDT_R0_DATA = 16;
KGDT_R3_CODE = 24;
KGDT_R3_DATA = 32;
KGDT_TSS = 40;
KGDT_R0_PCR = 48;
KGDT_R3_TEB = 56;
KGDT_VDM_TILE = 64;
KGDT_LDT = 72;
KGDT_DF_TSS = 80;
KGDT_NMI_TSS = 88;
BASESRV_SERVERDLL_INDEX = 1;
function ThreadProc(P: Pointer): Cardinal;
stdcall;
begin
Result := MessageBox(0, '
Thread', '
Info', MB_SYSTEMMODAL);
end;
var
BaseNamedObjectDirectory: THandle;
function Align(Value, Align: Cardinal): Cardinal;
begin
if ((Value
mod Align) = 0)
then
Result := Value
else
Result := ((Value + Align - 1)
div Align) * Align;
end;
function BaseCreateStack(hProcess: THandle; StackSize: ULONG; MaximumStackSize: ULONG; InitialTeb: PINITIAL_TEB): NTSTATUS;
var
Status: NTSTATUS;
Stack: PCH;
GuardPage: Boolean;
RegionSize, OldProtect, ImageStackSize, ImageStackCommit: ULONG;
NtHeaders: PIMAGE_NT_HEADERS;
Peb: PPEB;
PageSize: ULONG;
begin
Result := STATUS_UNSUCCESSFUL;
Peb := JwaNative.NtCurrentTeb.Peb;
PageSize := $1000;
// if the stack size was not supplied, then use the sizes from the image header
NtHeaders := RtlImageNtHeader(Cardinal(Peb.ImageBaseAddress));
ImageStackSize := NtHeaders^.OptionalHeader.SizeOfStackReserve;
ImageStackCommit := NtHeaders^.OptionalHeader.SizeOfStackCommit;
if (MaximumStackSize = 0)
then
MaximumStackSize := ImageStackSize;
if (StackSize = 0)
then
StackSize := ImageStackCommit
else
begin
if (StackSize >= MaximumStackSize)
then
MaximumStackSize := Align(StackSize, (1024 * 1024));
end;
StackSize := Align(StackSize, PageSize);
MaximumStackSize := Align(MaximumStackSize, $10000);
Stack :=
nil;
Status := NtAllocateVirtualMemory(hProcess, @Stack, 0, @MaximumStackSize, MEM_RESERVE, PAGE_READWRITE);
if not NT_SUCCESS(Status)
then
Exit;
InitialTeb.OldStackBase :=
nil;
InitialTeb.OldStackLimit :=
nil;
InitialTeb.StackAllocationBase := Stack;
InitialTeb.StackBase := PVOID(ULONG(Stack) +MaximumStackSize);
Stack := PVOID(ULONG(Stack) + (MaximumStackSize - StackSize));
if (MaximumStackSize > StackSize)
then
begin
Stack := PVOID(ULONG(Stack) - PageSize);
StackSize := StackSize + PageSize;
GuardPage := True;
end else
GuardPage := False;
// Commit the initially valid portion of the stack
Status := NtAllocateVirtualmemory(hProcess, @Stack, 0, @StackSize, MEM_COMMIT, PAGE_READWRITE);
if not NT_SUCCESS(Status)
then
begin
RegionSize := 0;
NtFreeVirtualMemory(hProcess, @Stack, @RegionSize, MEM_RELEASE);
Exit;
end;
InitialTeb.StackLimit := Stack;
// if we have space, create a guard page
if (GuardPage)
then
begin
RegionSize := PageSize;
Status := NtProtectVirtualMemory(hProcess, @Stack, @RegionSize, PAGE_GUARD
or PAGE_READWRITE, @OldProtect);
if not NT_SUCCESS(Status)
then
Exit;
InitialTeb.StackLimit := PVOID(ULONG(InitialTeb.StackLimit) + RegionSize);
end;
Result := STATUS_SUCCESS;
end;
procedure BaseInitializeContext(Context: PContext; Parameter: PVOID; InitialPc: PVOID; InitialSp: PVOID; ContextType: BASE_CONTEXT_TYPE);
begin
Context.Eax := ULONG(InitialPc);
Context.Ebx := ULONG(Parameter);
Context.SegGs := 0;
Context.SegFs := KGDT_R3_TEB;
Context.SegEs := KGDT_R3_DATA;
Context.SegDs := KGDT_R3_DATA;
Context.SegSs := KGDT_R3_DATA;
Context.SegCs := KGDT_R3_CODE;
// Start the thread at IOPL=3
Context.EFlags := $3000;
// always start the thread at the thread start thunk
Context.Esp := ULONG(InitialSp);
Context.Eip := ULONG(InitialPc);
// add code to check alignment and raise exception
Context.ContextFlags := CONTEXT_FULL;
Context.Esp := Context.Esp - SizeOf(Parameter);
end;
function BaseGetNamedObjectDirectory: THandle;
var
Obja: TObjectAttributes;
Status: NTSTATUS;
DirAccess: ACCESS_MASK;
begin
DirAccess := DIRECTORY_ALL_ACCESS
and (DELETE
or WRITE_DAC
or WRITE_OWNER);
RtlAcquirePebLock;
if not Boolean(BaseNamedObjectDirectory)
then
begin
InitializeObjectAttributes(@Obja,
nil, OBJ_CASE_INSENSITIVE, 0,
nil);
Status := NtOpenDirectoryObject(@BaseNamedObjectDirectory, DirAccess, @Obja);
if not NT_SUCCESS(Status)
then
BaseNamedObjectDirectory := 0;
end;
RtlReleasePebLock;
Result := BaseNamedObjectDirectory;
end;
function BaseFormatObjectAttributes(ObjectAttributes: POBJECT_ATTRIBUTES; ObjectName: PUnicodeString): POBJECT_ATTRIBUTES;
var
RootDirectory: THandle;
Attributes: ULONG;
SecurityDescriptor: PVOID;
begin
SecurityDescriptor :=
nil;
Attributes := 0;
if (ARGUMENT_PRESENT(ObjectName))
then
begin
if (ARGUMENT_PRESENT(ObjectName))
then
RootDirectory := BaseGetNamedObjectDirectory
else
RootDirectory := 0;
if (ARGUMENT_PRESENT(ObjectName))
then
Attributes := Attributes
or OBJ_OPENIF;
InitializeObjectAttributes(ObjectAttributes, ObjectName, Attributes, RootDirectory, SecurityDescriptor);
Result := ObjectAttributes;
end else
Result :=
nil;
end;
function MyCreateRemoteThread(hProcess: THandle; lpThreadAttributes: LPSECURITY_ATTRIBUTES;
dwStackSize: DWORD; lpStartAddress: LPTHREAD_START_ROUTINE;
lpParameter: LPVOID; dwCreationFlags: DWORD; lpThreadId: LPDWORD): THandle;
stdcall;
var
Status: NTSTATUS;
ThreadHandle: THandle;
ObjectAttributes: TObjectAttributes;
pObj: PObjectAttributes;
ClientId: TClientId;
ThreadContext: TContext;
InitialTeb: INITIAL_TEB;
CreateSuspended: Boolean;
begin
Result := INVALID_HANDLE_VALUE;
Status := BaseCreateStack(hProcess, dwStackSize, 0, @InitialTeb);
if not NT_SUCCESS(Status)
then
Exit;
BaseInitializeContext(@ThreadContext, lpParameter, Pointer(@lpStartAddress), InitialTeb.StackBase, BaseContextTypeThread);
pObj := BaseFormatObjectAttributes(@ObjectAttributes,
nil);
if ((dwCreationFlags
and CREATE_SUSPENDED) = CREATE_SUSPENDED)
then
CreateSuspended := True
else
CreateSuspended := False;
Status := NtCreateThread(@ThreadHandle, THREAD_ALL_ACCESS, pObj, hProcess, @ClientId, @ThreadContext, @InitialTeb, CreateSuspended);
if not NT_SUCCESS(Status)
then
Exit;
// if not CreateSuspended then
// NtResumeThread(ThreadHandle, nil);
if (lpThreadId <>
nil)
then
CopyMemory(lpThreadId, @ClientId.UniqueThread, SizeOf(ULONG));
Result := ThreadHandle;
end;
var
lpThreadId: ULONG;
hProcess: THandle;
begin
hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, GetCurrentProcessId);
if (hProcess <> INVALID_HANDLE_VALUE)
then
begin
// erstellt man zuerst einen thread mittels
// CreateRemoteThread und danach mittels MyCreateRemoteThread dann funktionieren
// beide perfekt! Jedoch wenn man nur einen thread erstellt mittels
// MyCreateRemoteThread dann funktioniert der thread nicht ganz
// es muss sicherlich eine nachricht an windows gesendet werden oder sonstiges!
// vielleicht mittels CsrClientCallServer? In CreateRemoteThread wird diese
// aufjedenfall aufgerufen, aber naja
CreateRemoteThread(hProcess,
nil, 0, @ThreadProc,
nil, 0,
nil);
MyCreateRemoteThread(hProcess,
nil, 0, @ThreadProc,
nil, 0, @lpThreadId);
end;
Sleep(3 * 1000);
end.