NTSTATUS NTAPI RtlpCreateUserStack(IN HANDLE hProcess, IN SIZE_T StackReserve OPTIONAL, IN SIZE_T StackCommit OPTIONAL, IN ULONG StackZeroBits OPTIONAL, OUT PINITIAL_TEB InitialTeb) { NTSTATUS Status; SYSTEM_BASIC_INFORMATION SystemBasicInfo; PIMAGE_NT_HEADERS Headers; ULONG_PTR Stack = 0; BOOLEAN UseGuard = FALSE; ULONG Dummy; SIZE_T GuardPageSize; /* Get some memory information */ Status = ZwQuerySystemInformation(SystemBasicInformation, &SystemBasicInfo, sizeof(SYSTEM_BASIC_INFORMATION), NULL); if (!NT_SUCCESS(Status)) return Status; /* Use the Image Settings if we are dealing with the current Process */ if (hProcess == NtCurrentProcess()) { /* Get the Image Headers */ Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); /* If we didn't get the parameters, find them ourselves */ if (!StackReserve) StackReserve = Headers->OptionalHeader. SizeOfStackReserve; if (!StackCommit) StackCommit = Headers->OptionalHeader. SizeOfStackCommit; } else { /* Use the System Settings if needed */ if (!StackReserve) StackReserve = SystemBasicInfo.AllocationGranularity; if (!StackCommit) StackCommit = SystemBasicInfo.PageSize; } /* Align everything to Page Size */ StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity); StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize); // FIXME: Remove once Guard Page support is here #if 1 StackCommit = StackReserve; #endif /* Reserve memory for the stack */ Status = ZwAllocateVirtualMemory(hProcess, (PVOID*)&Stack, StackZeroBits, &StackReserve, MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return Status; /* Now set up some basic Initial TEB Parameters */ InitialTeb->PreviousStackBase = NULL; InitialTeb->PreviousStackLimit = NULL; InitialTeb->AllocatedStackBase = (PVOID)Stack; InitialTeb->StackBase = (PVOID)(Stack + StackReserve); /* Update the Stack Position */ Stack += StackReserve - StackCommit; /* Check if we will need a guard page */ if (StackReserve > StackCommit) { /* Remove a page to set as guard page */ Stack -= SystemBasicInfo.PageSize; StackCommit += SystemBasicInfo.PageSize; UseGuard = TRUE; } /* Allocate memory for the stack */ Status = ZwAllocateVirtualMemory(hProcess, (PVOID*)&Stack, 0, &StackCommit, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return Status; /* Now set the current Stack Limit */ InitialTeb->StackLimit = (PVOID)Stack; /* Create a guard page */ if (UseGuard) { /* Attempt maximum space possible */ GuardPageSize = SystemBasicInfo.PageSize; Status = ZwProtectVirtualMemory(hProcess, (PVOID*)&Stack, &GuardPageSize, PAGE_GUARD | PAGE_READWRITE, &Dummy); if (!NT_SUCCESS(Status)) return Status; /* Update the Stack Limit keeping in mind the Guard Page */ InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit - GuardPageSize); } /* We are done! */ return STATUS_SUCCESS; }
/// <summary> /// Process section object pages /// Function will attempt to trigger copy-on-write for underlying pages to convert them into private /// If copy-on-write fails, region will be then mapped as read-only /// </summary> /// <param name="pEntry">Region data</param> /// <returns>Status code</returns> NTSTATUS BBHandleSharedRegion( IN PMAP_ENTRY pEntry ) { NTSTATUS status = STATUS_SUCCESS; MEMORY_BASIC_INFORMATION mbi = { 0 }; SIZE_T length = 0; ULONG_PTR memptr = 0; // Iterate underlying memory regions for (memptr = pEntry->originalPtr; memptr < pEntry->originalPtr + pEntry->size; memptr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize) { PVOID pBase = NULL; SIZE_T size = 0; MEMORY_WORKING_SET_EX_INFORMATION wsInfo = { 0 }; ULONG oldProt = 0; BOOLEAN writable = FALSE; status = ZwQueryVirtualMemory( ZwCurrentProcess(), (PVOID)memptr, MemoryBasicInformation, &mbi, sizeof( mbi ), &length ); if (!NT_SUCCESS( status )) { DPRINT( "BlackBone: %s: ZwQueryVirtualMemory for address 0x%p failed\n", __FUNCTION__, memptr ); return status; } writable = (mbi.Protect & (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE)); pBase = mbi.BaseAddress; size = (SIZE_T)mbi.RegionSize; // Make readonly pages writable if (writable || NT_SUCCESS( ZwProtectVirtualMemory( ZwCurrentProcess(), &pBase, &size, PAGE_EXECUTE_READWRITE, &oldProt ) )) { BOOLEAN failed = FALSE; // Touch pages to trigger copy-on-write and create private copies // This is crucial to prevent changes on section pages __try { ProbeForWrite( mbi.BaseAddress, mbi.RegionSize, PAGE_SIZE ); } __except (EXCEPTION_EXECUTE_HANDLER) { DPRINT( "BlackBone: %s: Exception while touching address 0x%p\n", __FUNCTION__, mbi.BaseAddress ); failed = TRUE; } // Restore protection if (writable == FALSE) ZwProtectVirtualMemory( ZwCurrentProcess(), &pBase, &size, oldProt, &oldProt ); if (failed) return STATUS_SHARING_VIOLATION; // If region was writable, it's safe to map shared pages if (writable) continue; // // Ensure pages were made private // for (ULONG_PTR pPage = (ULONG_PTR)mbi.BaseAddress; pPage < (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; pPage += PAGE_SIZE) { RtlZeroMemory( &wsInfo, sizeof( wsInfo ) ); wsInfo.VirtualAddress = (PVOID)pPage; // Check page 'shared' flag if (NT_SUCCESS( ZwQueryVirtualMemory( ZwCurrentProcess(), NULL, MemoryWorkingSetExInformation, &wsInfo, sizeof( wsInfo ), &length ) ) && wsInfo.VirtualAttributes.Shared) { DPRINT( "BlackBone: %s: Page at address 0x%p is still shared!", __FUNCTION__, pPage ); return STATUS_SHARING_VIOLATION; } } } else {
NTSTATUS RtlpCreateStack( IN HANDLE Process, IN SIZE_T MaximumStackSize OPTIONAL, IN SIZE_T CommittedStackSize OPTIONAL, IN ULONG ZeroBits OPTIONAL, OUT PINITIAL_TEB InitialTeb ) { NTSTATUS Status; PCH Stack; SYSTEM_BASIC_INFORMATION SysInfo; BOOLEAN GuardPage; SIZE_T RegionSize; ULONG OldProtect; Status = ZwQuerySystemInformation( SystemBasicInformation, (PVOID)&SysInfo, sizeof( SysInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // if stack is in the current process, then default to // the parameters from the image // if ( Process == NtCurrentProcess() ) { PPEB Peb; PIMAGE_NT_HEADERS NtHeaders; Peb = NtCurrentPeb(); NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress); if (!NtHeaders) { return STATUS_INVALID_IMAGE_FORMAT; } if (!MaximumStackSize) { MaximumStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve; } if (!CommittedStackSize) { CommittedStackSize = NtHeaders->OptionalHeader.SizeOfStackCommit; } } else { if (!CommittedStackSize) { CommittedStackSize = SysInfo.PageSize; } if (!MaximumStackSize) { MaximumStackSize = SysInfo.AllocationGranularity; } } // // Enforce a minimal stack commit if there is a PEB setting // for this. // if ( CommittedStackSize >= MaximumStackSize ) { MaximumStackSize = ROUND_UP(CommittedStackSize, (1024*1024)); } CommittedStackSize = ROUND_UP( CommittedStackSize, SysInfo.PageSize ); MaximumStackSize = ROUND_UP( MaximumStackSize, SysInfo.AllocationGranularity ); Stack = NULL; Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, ZeroBits, &MaximumStackSize, MEM_RESERVE, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Reservation Status == %X\n", Process, Status ); #endif // DBG return( Status ); } InitialTeb->OldInitialTeb.OldStackBase = NULL; InitialTeb->OldInitialTeb.OldStackLimit = NULL; InitialTeb->StackAllocationBase = Stack; InitialTeb->StackBase = Stack + MaximumStackSize; Stack += MaximumStackSize - CommittedStackSize; if (MaximumStackSize > CommittedStackSize) { Stack -= SysInfo.PageSize; CommittedStackSize += SysInfo.PageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &CommittedStackSize, MEM_COMMIT, PAGE_READWRITE ); InitialTeb->StackLimit = Stack; if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Commit Status == %X\n", Process, Status ); #endif // DBG return( Status ); } // // if we have space, create a guard page. // if (GuardPage) { RegionSize = SysInfo.PageSize; Status = ZwProtectVirtualMemory( Process, (PVOID *)&Stack, &RegionSize, PAGE_GUARD | PAGE_READWRITE, &OldProtect); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Guard Page Creation Status == %X\n", Process, Status ); #endif // DBG return( Status ); } InitialTeb->StackLimit = (PVOID)((PUCHAR)InitialTeb->StackLimit + RegionSize); } return( STATUS_SUCCESS ); }