static PVOID AllocateGuarded( _In_ SIZE_T SizeRequested) { NTSTATUS Status; SIZE_T Size = PAGE_ROUND_UP(SizeRequested + PAGE_SIZE); PVOID VirtualMemory = NULL; PCHAR StartOfBuffer; Status = NtAllocateVirtualMemory(NtCurrentProcess(), &VirtualMemory, 0, &Size, MEM_RESERVE, PAGE_NOACCESS); if (!NT_SUCCESS(Status)) return NULL; Size -= PAGE_SIZE; if (Size) { Status = NtAllocateVirtualMemory(NtCurrentProcess(), &VirtualMemory, 0, &Size, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { Size = 0; Status = NtFreeVirtualMemory(NtCurrentProcess(), &VirtualMemory, &Size, MEM_RELEASE); ok(Status == STATUS_SUCCESS, "Status = %lx\n", Status); return NULL; } } StartOfBuffer = VirtualMemory; StartOfBuffer += Size - SizeRequested; return StartOfBuffer; }
// What happens if we commit an area in the middle of a reserved block? // Does that affect how we can free it? void test_commit_in_the_middle(void) { VOID *address, *a; ULONG size; NTSTATUS r; HANDLE handle = (HANDLE) ~0; // allocate 32 pages address = NULL; size = 0x30000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x30000, "size wrong\n"); a = address; // split it into three bits address = a + 0x10000; size = 0x10000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_COMMIT, PAGE_READONLY ); ok(r == STATUS_SUCCESS, "wrong return code\n"); // now try free everything address = a; size = 0x30000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); }
// What happens if we try to free two blocks // that were allocated separately at the same time? void test_separate_alloc_single_free(void) { VOID *address, *a; ULONG size; NTSTATUS r; HANDLE handle = (HANDLE) ~0; // allocate 32 pages address = NULL; size = 0x20000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x20000, "size wrong\n"); a = address; // free all the memory again address = a; size = 0x20000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); // allocate again at the same address, but in two chunks address = a; size = 0x10000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x10000, "size wrong\n"); // allocate again at the same address, but in two chunks address = a + 0x10000; size = 0x10000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x10000, "size wrong\n"); // try free two separately allocated chunks in one go address = a; size = 0x20000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_UNABLE_TO_FREE_VM, "wrong return code\n"); // that doesn't work... free it properly in two goes address = a; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); address = a + 0x10000; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); }
// What happens if we split an area by freeing a piece of virtual memory in the middle of it? // Does it rejoin itself when we reallocate the area in the middle? void test_split_and_join(void) { VOID *address, *a; ULONG size; NTSTATUS r; HANDLE handle = (HANDLE) ~0; // allocate 32 pages address = NULL; size = 0x30000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x30000, "size wrong\n"); a = address; // split it into three bits address = a + 0x10000; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); // reallocate the bits we just removed address = a + 0x10000; size = 0x10000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x10000, "size wrong\n"); // now try free everything address = a; size = 0x30000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_UNABLE_TO_FREE_VM, "wrong return code\n"); // now really free it address = a; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); address = a + 0x10000; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); address = a + 0x20000; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); }
// The granulatiry of memory allocations on NT seems to be 0x10000 or 16 x 4k pages. // The size is rounded to multiples of pages size. // So what happens when we try to allocate memory at an address that is not // a multiple of 0x10000? What happens if a chunk of memory in that set of 16 pages // is already allocated? What happens if it's not already allocated? void test_chunksize( void ) { VOID *address[2]; ULONG size; NTSTATUS r; HANDLE handle = (HANDLE) ~0; address[0] = NULL; size = 0x1000; r = NtAllocateVirtualMemory( handle, &address[0], 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address[0] != NULL, "address wrong\n"); ok(size == 0x1000, "size wrong\n"); // try allocate memory one page after the previous allocation. address[1] = address[0] + 0x1000; size = 0x1000; r = NtAllocateVirtualMemory( handle, &address[1], 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_CONFLICTING_ADDRESSES, "wrong return code (%08lx)\n", r); ok(address[1] == address[0] + 0x1000, "address wrong\n"); ok(size == 0x1000, "size wrong\n"); // free the memory... assume nobody else grabs it (ie. there's no other threads) r = NtFreeVirtualMemory( handle, &address[0], &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(size == 0x1000, "size wrong\n"); // try allocate memory one page after the previous (free'd) allocation r = NtAllocateVirtualMemory( handle, &address[1], 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code (%08lx)\n", r); ok(address[1] == address[0], "address wrong\n"); ok(size == 0x2000, "size wrong\n"); // free what we allocated address[1] = address[0] + 0x1000; size = 0x1000; r = NtFreeVirtualMemory( handle, &address[1], &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address[1] == address[0] + 0x1000, "address wrong\n"); ok(size == 0x1000, "size wrong\n"); // free what we didn't allocate... address[1] = address[0]; size = 0x1000; r = NtFreeVirtualMemory( handle, &address[1], &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address[1] == address[0], "address wrong\n"); ok(size == 0x1000, "size wrong\n"); }
UINT_PTR SectionRelayBuffer::RegisterSection(UINT_PTR section) { UINT_PTR i; UINT_PTR base=(UINT_PTR)(section)>>31; for (i=0;i<section_count;i++) if (record[i].section_register==base) break; if (i<section_count) { record[i].section_referenced++; return record[i].section_relay_buffer; } UINT_PTR addr=(base<<31)+0x40000000; UINT_PTR size=0x1000,len; LPVOID allocate; MEMORY_BASIC_INFORMATION info; allocate=(LPVOID)addr; for (;;) { allocate=(LPVOID)addr; NtQueryVirtualMemory(NtCurrentProcess(),allocate, MemoryBasicInformation,&info,sizeof(info),&len); if ((info.State&MEM_FREE)) if ((UINT_PTR)info.BaseAddress+info.RegionSize-addr>=0x1000) break; addr=(UINT_PTR)info.BaseAddress-0x1000; } NtAllocateVirtualMemory(NtCurrentProcess(),&allocate, 0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); addr=(UINT_PTR)allocate; record[section_count].section_register=section>>31; record[section_count].section_relay_buffer=addr; record[section_count].section_referenced=1; section_count++; return addr; }
void test_open_process_param_size( void ) { NTSTATUS r; PVOID p = 0; PCLIENT_ID id; ULONG sz = 0x1000; OBJECT_ATTRIBUTES oa; HANDLE handle = 0, dummy = (void*) 1; r = NtAllocateVirtualMemory(NtCurrentProcess(), &p, 0, &sz, MEM_COMMIT, PAGE_READWRITE); ok( r == STATUS_SUCCESS, "wrong return %08lx\n", r ); memset( &oa, 0, sizeof oa ); id = (p + sz - 4); id->UniqueProcess = dummy; r = NtOpenProcess( &handle, 0, &oa, id ); ok( r == STATUS_ACCESS_VIOLATION, "wrong return %08lx\n", r ); id = (p + sz - 8); id->UniqueProcess = dummy; id->UniqueThread = dummy; r = NtOpenProcess( &handle, 0, &oa, id ); ok( r == STATUS_INVALID_CID, "wrong return %08lx\n", r ); r = NtFreeVirtualMemory(NtCurrentProcess(), &p, &sz, MEM_RELEASE ); ok( r == STATUS_SUCCESS, "wrong return %08lx\n", r ); }
NTSTATUS AcquireLMCredentials ( VOID ) { UNICODE_STRING Ntlm; PUCHAR AllocateMemory = NULL; ULONG AllocateLength = 8; NTSTATUS status; TimeStamp Expiry; status = NtAllocateVirtualMemory( NtCurrentProcess(), &AllocateMemory, 0, &AllocateLength, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS(status) ) { return status; } Ntlm.Length = Ntlm.MaximumLength = 8; Ntlm.Buffer = (LPWSTR)AllocateMemory, RtlCopyMemory( Ntlm.Buffer, L"NTLM", 8 ); status = AcquireCredentialsHandle( NULL, // Default principal (PSECURITY_STRING) &Ntlm, SECPKG_CRED_INBOUND, // Need to define this NULL, // No LUID NULL, // No AuthData NULL, // No GetKeyFn NULL, // No GetKeyArg &SrvLmLsaHandle, &Expiry ); (VOID)NtFreeVirtualMemory( NtCurrentProcess(), &AllocateMemory, &AllocateLength, MEM_DECOMMIT ); if ( !NT_SUCCESS(status) ) { status = MapSecurityError(status); return status; } SrvHaveCreds |= HAVENTLM; return status; } // AcquireLMCredentials
// alloc buffer ... LPVOID misc_AllocBuffer(DWORD dwSize) { LPVOID lpBuffer=NULL; DWORD dwLen=dwSize; if (NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),&lpBuffer,0,&dwLen,MEM_COMMIT|MEM_TOP_DOWN,PAGE_READWRITE))) return lpBuffer; return NULL; }
void test_free_mem( void ) { VOID *address, *a; ULONG size; NTSTATUS r; HANDLE handle = (HANDLE) ~0; // allocate 16 pages address = NULL; size = 0x10000; r = NtAllocateVirtualMemory( handle, &address, 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address != NULL, "address wrong\n"); ok(size == 0x10000, "size wrong\n"); a = address; // free half of the memory we committed address = a; size = 0x8000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(size == 0x8000, "size wrong\n"); // try free memory across a block boundary address = a + 0xc000; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_UNABLE_TO_FREE_VM, "wrong return code (%08lx)\n", r); ok(size == 0x10000, "size wrong\n"); // try free all the memory again address = a; size = 0x10000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_MEMORY_NOT_ALLOCATED, "wrong return code\n"); // free the last 4 pages address = a + 0xc000; size = 0x4000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); // free the last 4 pages again... should fail address = a + 0xc000; size = 0x4000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_MEMORY_NOT_ALLOCATED, "wrong return code\n"); ok(size == 0x4000, "size wrong\n"); // free the rest address = a + 0x8000; size = 0x4000; r = NtFreeVirtualMemory( handle, &address, &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); }
LPVOID GetProcAddr(HANDLE hProc) { AcquireLock(); DWORD pid,addr,len; if (hProc==NtCurrentProcess()) pid=current_process_id; else { PROCESS_BASIC_INFORMATION info; NtQueryInformationProcess(hProc,ProcessBasicInformation,&info,sizeof(info),&len); pid=info.uUniqueProcessId; } pid>>=2; for (UINT_PTR i=0;i<count;i++) { if ((proc_record[i]&0xFFF)==pid) { addr=proc_record[i]&~0xFFF; ReleaseLock(); return (LPVOID)addr; } } len=0x1000; NtAllocateVirtualMemory(hProc,(PVOID*)(proc_record+count),0,&len, MEM_COMMIT,PAGE_EXECUTE_READWRITE); DWORD base = proc_record[count]; proc_record[count] |= pid; union { LPVOID buffer; DWORD b; }; b = base; LPVOID fun_table[3]; *(DWORD*)(normal_routine + ADDR0) += base; NtWriteVirtualMemory(hProc, buffer, normal_routine, 0x14, 0); *(DWORD*)(normal_routine + ADDR0) -= base; b += 0x14; fun_table[0] = NtTerminateThread; fun_table[1] = NtQueryVirtualMemory; fun_table[2] = MessageBoxW; NtWriteVirtualMemory(hProc, buffer, fun_table, 0xC, 0); b += 0xC; *(DWORD*)(except_routine + ADDR1) += base; *(DWORD*)(except_routine + ADDR2) += base; *(DWORD*)(except_routine + ADDR3) += base; NtWriteVirtualMemory(hProc, buffer, except_routine, 0xE0, 0); *(DWORD*)(except_routine + ADDR1) -= base; *(DWORD*)(except_routine + ADDR2) -= base; *(DWORD*)(except_routine + ADDR3) -= base; count++; ReleaseLock(); return (LPVOID)base; }
DWORD Inject(HANDLE hProc, LPWSTR engine) { LPVOID lpvAllocAddr = 0; DWORD dwWrite = 0x1000, len = 0; HANDLE hTH; WCHAR path[MAX_PATH]; LPWSTR p; if (!IthCheckFile(DllName)) return -1; p = GetMainModulePath(); len = wcslen(p); memcpy(path, p, len << 1); memset(path + len, 0, (MAX_PATH - len) << 1); for (p = path + len; *p != L'\\'; p--); //Always a \ after drive letter. p++; wcscpy(p, DllName); NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_COMMIT, PAGE_READWRITE); if (lpvAllocAddr == 0) return -1; CheckThreadStart(); //Copy module path into address space of target process. NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); hTH = IthCreateThread(LoadLibrary, (DWORD)lpvAllocAddr, hProc); if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) { ConsoleOutput(ErrorRemoteThread); return -1; } NtWaitForSingleObject(hTH, 0, 0); THREAD_BASIC_INFORMATION info; NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite); NtClose(hTH); if (info.ExitStatus != 0) { wcscpy(p, engine); NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); hTH = IthCreateThread(LoadLibrary, (DWORD)lpvAllocAddr, hProc); if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) { ConsoleOutput(ErrorRemoteThread); return -1; } NtWaitForSingleObject(hTH, 0, 0); NtClose(hTH); } dwWrite = 0; NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE); return info.ExitStatus; }
/*********************************************************************** * VirtualAllocEx (KERNEL32.@) * * Seems to be just as VirtualAlloc, but with process handle. * * PARAMS * hProcess [I] Handle to process to do mem operation. * addr [I] Address of region to reserve or commit. * size [I] Size of region. * type [I] Type of allocation. * protect [I] Type of access protection. * * * RETURNS * Success: Base address of allocated region of pages. * Failure: NULL. */ LPVOID WINAPI VirtualAllocEx( HANDLE hProcess, LPVOID addr, SIZE_T size, DWORD type, DWORD protect ) { LPVOID ret = addr; NTSTATUS status; if ((status = NtAllocateVirtualMemory( hProcess, &ret, 0, &size, type, protect ))) { SetLastError( RtlNtStatusToDosError(status) ); ret = NULL; } return ret; }
/* return 1 on success, 0 on nonexistent file, -1 on other error */ static int read_file( const char *name, void **data, SIZE_T *size ) { struct stat st; int fd, ret = -1; size_t header_size; IMAGE_DOS_HEADER *dos; IMAGE_NT_HEADERS *nt; const size_t min_size = sizeof(*dos) + sizeof(fakedll_signature) + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader.MajorLinkerVersion ); if ((fd = open( name, O_RDONLY | O_BINARY )) == -1) return 0; if (fstat( fd, &st ) == -1) goto done; *size = st.st_size; if (!file_buffer || st.st_size > file_buffer_size) { VirtualFree( file_buffer, 0, MEM_RELEASE ); file_buffer = NULL; file_buffer_size = st.st_size; if (NtAllocateVirtualMemory( GetCurrentProcess(), &file_buffer, 0, &file_buffer_size, MEM_COMMIT, PAGE_READWRITE )) goto done; } /* check for valid fake dll file */ if (st.st_size < min_size) goto done; header_size = min( st.st_size, 4096 ); if (pread( fd, file_buffer, header_size, 0 ) != header_size) goto done; dos = file_buffer; if (dos->e_magic != IMAGE_DOS_SIGNATURE) goto done; if (dos->e_lfanew < sizeof(fakedll_signature)) goto done; if (memcmp( dos + 1, fakedll_signature, sizeof(fakedll_signature) )) goto done; if (dos->e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader.MajorLinkerVersion) > header_size) goto done; nt = (IMAGE_NT_HEADERS *)((char *)file_buffer + dos->e_lfanew); if (nt->Signature == IMAGE_NT_SIGNATURE && nt->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) { /* wrong 32/64 type, pretend it doesn't exist */ ret = 0; goto done; } if (st.st_size == header_size || pread( fd, (char *)file_buffer + header_size, st.st_size - header_size, header_size ) == st.st_size - header_size) { *data = file_buffer; ret = 1; } done: close( fd ); return ret; }
/****************************************************************************** * RtlCreateEnvironment [NTDLL.@] */ NTSTATUS WINAPI RtlCreateEnvironment(BOOLEAN inherit, PWSTR* env) { NTSTATUS nts; TRACE("(%u,%p)!\n", inherit, env); if (inherit) { MEMORY_BASIC_INFORMATION mbi; RtlAcquirePebLock(); nts = NtQueryVirtualMemory(NtCurrentProcess(), NtCurrentTeb()->Peb->ProcessParameters->Environment, 0, &mbi, sizeof(mbi), NULL); if (nts == STATUS_SUCCESS) { *env = NULL; nts = NtAllocateVirtualMemory(NtCurrentProcess(), (void**)env, 0, &mbi.RegionSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (nts == STATUS_SUCCESS) memcpy(*env, NtCurrentTeb()->Peb->ProcessParameters->Environment, mbi.RegionSize); else *env = NULL; } RtlReleasePebLock(); } else { SIZE_T size = 1; PVOID addr = NULL; nts = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (nts == STATUS_SUCCESS) *env = addr; } return nts; }
static PVOID AllocateReadOnly( _In_ SIZE_T SizeRequested) { NTSTATUS Status; SIZE_T Size = PAGE_ROUND_UP(SizeRequested); PVOID VirtualMemory = NULL; Status = NtAllocateVirtualMemory(NtCurrentProcess(), &VirtualMemory, 0, &Size, MEM_COMMIT, PAGE_READONLY); if (!NT_SUCCESS(Status)) return NULL; return VirtualMemory; }
IHFSERVICE DWORD IHFAPI IHF_GetPIDByName(LPWSTR pwcTarget) { DWORD dwSize = 0x20000, dwExpectSize = 0; LPVOID pBuffer = 0; SYSTEM_PROCESS_INFORMATION *spiProcessInfo; DWORD dwPid = 0; DWORD dwStatus; NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_COMMIT, PAGE_READWRITE); dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize); if (!NT_SUCCESS(dwStatus)) { NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE); if (dwStatus != STATUS_INFO_LENGTH_MISMATCH || dwExpectSize < dwSize) return 0; dwSize = (dwExpectSize | 0xFFF) + 0x4001; // pBuffer = 0; NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_COMMIT, PAGE_READWRITE); dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize); if (!NT_SUCCESS(dwStatus)) goto _end; } for (spiProcessInfo = (SYSTEM_PROCESS_INFORMATION*)pBuffer; spiProcessInfo->dNext;) { spiProcessInfo = (SYSTEM_PROCESS_INFORMATION*) ((DWORD)spiProcessInfo + spiProcessInfo -> dNext); if (_wcsicmp(pwcTarget, spiProcessInfo -> usName.Buffer) == 0) { dwPid = spiProcessInfo->dUniqueProcessId; break; } } if (dwPid == 0) ConsoleOutput(ErrorNoProcess); _end: NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE); return dwPid; }
NTSTATUS xmsCommitBlock( ULONG BaseAddress, ULONG Size ) /*++ Routine Description: This routine commits a block of memory using NtAllocateVirtualMemory. Arguments: BaseAddress -- Supplies the base address to commit memory at Size -- Supplies the size of the block to commit Return Value: Same as NtAllocateVirtualMemory. --*/ { PVOID Address; ULONG s; NTSTATUS Status; // // Copy the parameters locally, so that MM doesn't // change them for us // Address = (PVOID)BaseAddress; s = Size; // // Perform the allocation // Status = NtAllocateVirtualMemory( NtCurrentProcess(), &Address, 0L, &s, MEM_COMMIT, PAGE_READWRITE ); return Status; }
// copy pe image on lpbase from current process to dwpid on the same address BOOL engine_CopyImageToProcess(HANDLE hProcess,LPVOID lpBase) { DWORD dwSize,dwFree=0; LPVOID lpNew=lpBase; // get PE size dwSize=engine_GetPEImageSize(lpBase); if (!NT_SUCCESS(NtAllocateVirtualMemory(hProcess,&lpNew,0,&dwSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) return FALSE; if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpNew,lpBase,dwSize,NULL))) { NtFreeVirtualMemory(hProcess,&lpNew,&dwFree,MEM_RELEASE); return FALSE; } return TRUE; }
static VOID MakeReadOnly( PVOID Pointer, SIZE_T SizeRequested) { NTSTATUS Status; SIZE_T Size = PAGE_ROUND_UP(SizeRequested); PVOID VirtualMemory = (PVOID)PAGE_ROUND_DOWN((SIZE_T)Pointer); if (Size) { Status = NtAllocateVirtualMemory(NtCurrentProcess(), &VirtualMemory, 0, &Size, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { Size = 0; Status = NtFreeVirtualMemory(NtCurrentProcess(), &VirtualMemory, &Size, MEM_RELEASE); ok(Status == STATUS_SUCCESS, "Status = %lx\n", Status); } } }
PVOID ITstAllocatePool( IN ULONG NumberOfBytes ) { NTSTATUS Status; PVOID PoolAddress = NULL; ULONG RegionSize; RegionSize = NumberOfBytes; Status = NtAllocateVirtualMemory( NtCurrentProcess(), &PoolAddress, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); return PoolAddress; }
BOOL MapNullPage() { HMODULE hNtdll; SIZE_T RegionSize = 0x1000; // will be rounded up to the next host // page size address boundary -> 0x2000 PVOID BaseAddress = (PVOID)0x00000001; // will be rounded down to the next host // page size address boundary -> 0x00000000 NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; hNtdll = GetModuleHandle("ntdll.dll"); // Grab the address of NtAllocateVirtualMemory NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t)GetProcAddress(hNtdll, "NtAllocateVirtualMemory"); if (!NtAllocateVirtualMemory) { DEBUG_ERROR("\t\t[-] Failed Resolving NtAllocateVirtualMemory: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } // Allocate the Virtual memory NtStatus = NtAllocateVirtualMemory((HANDLE)0xFFFFFFFF, &BaseAddress, 0, &RegionSize, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); if (NtStatus != STATUS_SUCCESS) { DEBUG_ERROR("\t\t\t\t[-] Virtual Memory Allocation Failed: 0x%x\n", NtStatus); exit(EXIT_FAILURE); } else { DEBUG_INFO("\t\t\t[+] Memory Allocated: 0x%p\n", BaseAddress); DEBUG_INFO("\t\t\t[+] Allocation Size: 0x%X\n", RegionSize); } FreeLibrary(hNtdll); return TRUE; }
BOOL AllocateNullPage(DWORD size) { NTSTATUS status; DWORD addr = 1, len = size; status = NtAllocateVirtualMemory( GetCurrentProcess(), (PVOID*)&addr, 0, &len, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if(!NT_SUCCESS(status)) { printf("[-] Error with ZwAllocateVirtualMemory : 0x%.8x\n", status); return FALSE; } return TRUE; }
void test_granularity( void ) { VOID *address[0x100]; ULONG size, i; NTSTATUS r; HANDLE handle = (HANDLE) ~0; UINT too_small, too_big; for (i=0; i<0x100; i++) { address[i] = NULL; size = 0x1000; r = NtAllocateVirtualMemory( handle, &address[i], 0, &size, MEM_RESERVE, PAGE_NOACCESS ); ok(r == STATUS_SUCCESS, "wrong return code\n"); ok(address[i] != NULL, "NULL allocated\n"); ok(size == 0x1000, "size wrong\n"); //dprintf("%p\n", address[i]); } too_small = 0; too_big = 0; for (i=1; i<0x100; i++) { if ((address[i] - address[i-1]) < 0x10000) too_small++; if ((address[i] - address[i-1]) > 0x10000) too_big++; } ok( too_small == 0, "virtual memory granularity too small\n"); ok( too_big < 0x10, "virtual memory granularity too big\n"); for (i=0; i<0x100; i++) { size = 0x1000; r = NtFreeVirtualMemory( handle, &address[i], &size, MEM_RELEASE ); ok(r == STATUS_SUCCESS, "wrong return code\n"); } }
/* * @unimplemented */ PRTL_DEBUG_INFORMATION NTAPI RtlCreateQueryDebugBuffer(IN ULONG Size, IN BOOLEAN EventPair) { NTSTATUS Status; PRTL_DEBUG_INFORMATION Buf = NULL; SIZE_T ViewSize = 100 * PAGE_SIZE; Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &ViewSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return NULL; Buf->ViewBaseClient = Buf; Buf->ViewSize = (ULONG)ViewSize; DPRINT("RtlCQDB: BA: %p BS: 0x%lx\n", Buf->ViewBaseClient, Buf->ViewSize); return Buf; }
BOOL NTAPI BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment, IN PANSI_STRING AnsiEnv, IN PUNICODE_STRING UnicodeEnv) { BOOL Result; ULONG RegionSize, EnvironmentSize = 0; PWCHAR p, Environment, NewEnvironment = NULL; NTSTATUS Status; /* Make sure we have both strings */ if (!(AnsiEnv) || !(UnicodeEnv)) { /* Fail */ SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /* Check if an environment was passed in */ if (!lpEnvironment) { /* Nope, create one */ Status = RtlCreateEnvironment(TRUE, (PWCHAR*)&Environment); if (!NT_SUCCESS(Status)) goto Quickie; } else { /* Use the one we got */ Environment = lpEnvironment; } /* Do we have something now ? */ if (!Environment) { /* Still not, fail out */ SetLastError(ERROR_BAD_ENVIRONMENT); goto Quickie; } /* Count how much space the whole environment takes */ p = Environment; while ((*p++ != UNICODE_NULL) && (*p != UNICODE_NULL)) EnvironmentSize++; EnvironmentSize += sizeof(UNICODE_NULL); /* Allocate a new copy */ RegionSize = (EnvironmentSize + MAX_PATH) * sizeof(WCHAR); if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&NewEnvironment, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE))) { /* We failed, bail out */ SetLastError(ERROR_NOT_ENOUGH_MEMORY); NewEnvironment = NULL; goto Quickie; } /* Begin parsing the new environment */ p = NewEnvironment; /* FIXME: Code here */ /* Terminate it */ *p++ = UNICODE_NULL; /* Initialize the unicode string to hold it */ EnvironmentSize = (p - NewEnvironment) * sizeof(WCHAR); RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment, (USHORT)EnvironmentSize); UnicodeEnv->Length = (USHORT)EnvironmentSize; /* Create the ASCII version of it */ Status = RtlUnicodeStringToAnsiString(AnsiEnv, UnicodeEnv, TRUE); if (!NT_SUCCESS(Status)) { /* Set last error if conversion failure */ BaseSetLastNTError(Status); } else { /* Everything went okay, so return success */ Result = TRUE; NewEnvironment = NULL; } Quickie: /* Cleanup path starts here, start by destroying the envrionment copy */ if (!(lpEnvironment) && (Environment)) RtlDestroyEnvironment(Environment); /* See if we are here due to failure */ if (NewEnvironment) { /* Initialize the paths to be empty */ RtlInitEmptyUnicodeString(UnicodeEnv, NULL, 0); RtlInitEmptyAnsiString(AnsiEnv, NULL, 0); /* Free the environment copy */ RegionSize = 0; Status = NtFreeVirtualMemory(NtCurrentProcess(), (PVOID*)&NewEnvironment, &RegionSize, MEM_RELEASE); ASSERT(NT_SUCCESS(Status)); } /* Return the result */ return Result; }
void mymain(void){ ///A valid module name cannot exceed the Windows path part constraints. UCHAR szInternalDllName[MAX_PATH]; PVOID pDllBase = NULL; PVOID pListBuf = NULL; ULONGLONG requiredBufSize = 0; ULONGLONG bufSize = 0; ULONGLONG entryStringLength = 0; NTSTATUS status, fatalStatus = STATUS_HANDLE_NO_LONGER_VALID; for (;;){ pDllBase = NULL; pListBuf = NULL; requiredBufSize = 0; bufSize = 0; entryStringLength = 0; system("CLS"); printf_s("Welcome to .def File Creator V0.1!\n\n"); printf_s("Enter the full DLL or exe name as in the following example:\n"); printf_s("\"ntdll.dll\" (without quotes).\n\n"); printf_s("DLL must reside in \\System32 or in current directory.\n\n"); printf_s("DLL name: "); status = getAndLoadDllByInput(&pDllBase); if (status){ mydie(status, STATUS_SUCCESS); continue; } printf_s("DLL loaded successfully. Address: %p\n", pDllBase); status = obtainImageFileEatEntries(pDllBase, NULL, &requiredBufSize, szInternalDllName, sizeof(szInternalDllName)); if (status != STATUS_BUFFER_TOO_SMALL){ mydie(status, STATUS_SUCCESS); continue; } status = NtAllocateVirtualMemory(INVALID_HANDLE_VALUE, &pListBuf, 0, &requiredBufSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (status){ mydie(status, STATUS_SUCCESS); continue; } bufSize = requiredBufSize; status = obtainImageFileEatEntries(pDllBase, pListBuf, &requiredBufSize, szInternalDllName, sizeof(szInternalDllName)); if (status){ mydie(status, STATUS_SUCCESS); continue; } printf_s("\n%s\n\nlist size: 0x%llX", pListBuf, requiredBufSize); ///We don't want to write zeros into the file. The standard EOF is sufficient. entryStringLength = requiredBufSize - sizeof(WCHAR); status = dumpEatEntriesToDefFile(pListBuf, entryStringLength, szInternalDllName); fatalStatus = NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pListBuf, &bufSize, MEM_RELEASE); if (status || fatalStatus){ mydie(status, fatalStatus); continue; } printf_s("\nThe image has been successfully scanned and its exports were"); printf_s("\ndumped into \"exports.def\" successfully."); printf_s("\n\nYou can press any key to dump another module or close the window to exit."); fflush(stdin); _getch(); } }
NTSTATUS dumpEatEntriesToDefFile(PVOID pEatEntryList, ULONGLONG listBufferSize, PUCHAR pModuleName){ OBJECT_ATTRIBUTES defFileAttr; UNICODE_STRING uDefFileName; IO_STATUS_BLOCK ioSb; NTSTATUS status, fatalStatus = STATUS_CLUSTER_NETINTERFACE_EXISTS; PCHAR pDefPreamble = NULL; ///Keep it simple. We can't allocate less than 4 kB using NtXxx funcs anyway, ///so just allocate 4 kB although we never will need that much. ULONGLONG defPreambleSize = PAGE_SIZE; HANDLE hDefFile = NULL; HANDLE hParentDir = NULL; ///Quite some ugly hacking... char szDefPreamblePart[] = { 0x0D, 0x0A, 0x0D, 0x0A, 'E', 'X', 'P', 'O', 'R', 'T', 'S', 0x0D, 0x0A, 0x0 }; status = NtAllocateVirtualMemory(INVALID_HANDLE_VALUE, &pDefPreamble, 0, &defPreambleSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (status) return status; status = RtlStringCbPrintfA(pDefPreamble, defPreambleSize, "LIBRARY %s%s", pModuleName, szDefPreamblePart); if (status){ fatalStatus = NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pDefPreamble, &defPreambleSize, MEM_RELEASE); if (fatalStatus) mydie(status, fatalStatus); return status; } status = RtlStringCbLengthA(pDefPreamble, defPreambleSize, &defPreambleSize); if (status){ fatalStatus = NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pDefPreamble, &defPreambleSize, MEM_RELEASE); if (fatalStatus) mydie(status, fatalStatus); return status; } hParentDir = NtCurrentPeb()->ProcessParameters->CurrentDirectory.Handle; RtlInitUnicodeString(&uDefFileName, L"exports.def"); InitializeObjectAttributes(&defFileAttr, &uDefFileName, OBJ_CASE_INSENSITIVE, hParentDir, NULL); status = NtCreateFile(&hDefFile, FILE_ALL_ACCESS | SYNCHRONIZE, &defFileAttr, &ioSb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_SUPERSEDE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, NULL, 0); if (status){ fatalStatus = NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pDefPreamble, &defPreambleSize, MEM_RELEASE); if (fatalStatus) mydie(status, fatalStatus); return status; } status = NtWriteFile(hDefFile, NULL, NULL, NULL, &ioSb, pDefPreamble, (ULONG)defPreambleSize, NULL, NULL); if (!status) status = NtWriteFile(hDefFile, NULL, NULL, NULL, &ioSb, pEatEntryList, (ULONG)listBufferSize, NULL, NULL); fatalStatus = NtClose(hDefFile); if (fatalStatus) mydie(status, fatalStatus); fatalStatus = NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pDefPreamble, &defPreambleSize, MEM_RELEASE); if (fatalStatus) mydie(status, fatalStatus); return status; }
/*********************************************************************** * thread_init * * Setup the initial thread. * * NOTES: The first allocated TEB on NT is at 0x7ffde000. */ HANDLE thread_init(void) { TEB *teb; void *addr; SIZE_T size, info_size; HANDLE exe_file = 0; LARGE_INTEGER now; NTSTATUS status; struct ntdll_thread_data *thread_data; static struct debug_info debug_info; /* debug info for initial thread */ virtual_init(); /* reserve space for shared user data */ addr = (void *)0x7ffe0000; size = 0x10000; status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE ); if (status) { MESSAGE( "wine: failed to map the shared user data: %08x\n", status ); exit(1); } user_shared_data = addr; /* allocate and initialize the PEB */ addr = NULL; size = sizeof(*peb); NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 1, &size, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE ); peb = addr; peb->ProcessParameters = ¶ms; peb->TlsBitmap = &tls_bitmap; peb->TlsExpansionBitmap = &tls_expansion_bitmap; peb->FlsBitmap = &fls_bitmap; peb->LdrData = &ldr; params.CurrentDirectory.DosPath.Buffer = current_dir; params.CurrentDirectory.DosPath.MaximumLength = sizeof(current_dir); params.wShowWindow = 1; /* SW_SHOWNORMAL */ ldr.Length = sizeof(ldr); RtlInitializeBitMap( &tls_bitmap, peb->TlsBitmapBits, sizeof(peb->TlsBitmapBits) * 8 ); RtlInitializeBitMap( &tls_expansion_bitmap, peb->TlsExpansionBitmapBits, sizeof(peb->TlsExpansionBitmapBits) * 8 ); RtlInitializeBitMap( &fls_bitmap, peb->FlsBitmapBits, sizeof(peb->FlsBitmapBits) * 8 ); InitializeListHead( &peb->FlsListHead ); InitializeListHead( &ldr.InLoadOrderModuleList ); InitializeListHead( &ldr.InMemoryOrderModuleList ); InitializeListHead( &ldr.InInitializationOrderModuleList ); #ifdef __APPLE__ peb->Reserved[0] = get_dyld_image_info_addr(); #endif /* allocate and initialize the initial TEB */ signal_alloc_thread( &teb ); teb->Peb = peb; teb->Tib.StackBase = (void *)~0UL; teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); thread_data = (struct ntdll_thread_data *)teb->SpareBytes1; thread_data->request_fd = -1; thread_data->reply_fd = -1; thread_data->wait_fd[0] = -1; thread_data->wait_fd[1] = -1; thread_data->debug_info = &debug_info; InsertHeadList( &tls_links, &teb->TlsLinks ); signal_init_thread( teb ); virtual_init_threading(); debug_info.str_pos = debug_info.strings; debug_info.out_pos = debug_info.output; debug_init(); /* setup the server connection */ server_init_process(); info_size = server_init_thread( peb ); /* create the process heap */ if (!(peb->ProcessHeap = RtlCreateHeap( HEAP_GROWABLE, NULL, 0, 0, NULL, NULL ))) { MESSAGE( "wine: failed to create the process heap\n" ); exit(1); } /* allocate user parameters */ if (info_size) { init_user_process_params( info_size, &exe_file ); } else { if (isatty(0) || isatty(1) || isatty(2)) params.ConsoleHandle = (HANDLE)2; /* see kernel32/kernel_private.h */ if (!isatty(0)) wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE, OBJ_INHERIT, ¶ms.hStdInput ); if (!isatty(1)) wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, OBJ_INHERIT, ¶ms.hStdOutput ); if (!isatty(2)) wine_server_fd_to_handle( 2, GENERIC_WRITE|SYNCHRONIZE, OBJ_INHERIT, ¶ms.hStdError ); } /* initialize time values in user_shared_data */ NtQuerySystemTime( &now ); user_shared_data->SystemTime.LowPart = now.u.LowPart; user_shared_data->SystemTime.High1Time = user_shared_data->SystemTime.High2Time = now.u.HighPart; user_shared_data->u.TickCountQuad = (now.QuadPart - server_start_time) / 10000; user_shared_data->u.TickCount.High2Time = user_shared_data->u.TickCount.High1Time; user_shared_data->TickCountLowDeprecated = user_shared_data->u.TickCount.LowPart; user_shared_data->TickCountMultiplier = 1 << 24; fill_cpu_info(); NtCreateKeyedEvent( &keyed_event, GENERIC_READ | GENERIC_WRITE, NULL, 0 ); return exe_file; }
/*********************************************************************** * init_user_process_params * * Fill the RTL_USER_PROCESS_PARAMETERS structure from the server. */ static NTSTATUS init_user_process_params( SIZE_T data_size, HANDLE *exe_file ) { void *ptr; WCHAR *src, *dst; SIZE_T info_size, env_size, size, alloc_size; NTSTATUS status; startup_info_t *info; RTL_USER_PROCESS_PARAMETERS *params = NULL; if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, data_size ))) return STATUS_NO_MEMORY; SERVER_START_REQ( get_startup_info ) { wine_server_set_reply( req, info, data_size ); if (!(status = wine_server_call( req ))) { data_size = wine_server_reply_size( reply ); info_size = reply->info_size; env_size = data_size - info_size; *exe_file = wine_server_ptr_handle( reply->exe_file ); } } SERVER_END_REQ; if (status != STATUS_SUCCESS) goto done; size = sizeof(*params); size += MAX_NT_PATH_LENGTH * sizeof(WCHAR); size += info->dllpath_len + sizeof(WCHAR); size += info->imagepath_len + sizeof(WCHAR); size += info->cmdline_len + sizeof(WCHAR); size += info->title_len + sizeof(WCHAR); size += info->desktop_len + sizeof(WCHAR); size += info->shellinfo_len + sizeof(WCHAR); size += info->runtime_len + sizeof(WCHAR); alloc_size = size; status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)¶ms, 0, &alloc_size, MEM_COMMIT, PAGE_READWRITE ); if (status != STATUS_SUCCESS) goto done; NtCurrentTeb()->Peb->ProcessParameters = params; params->AllocationSize = alloc_size; params->Size = size; params->Flags = PROCESS_PARAMS_FLAG_NORMALIZED; params->DebugFlags = info->debug_flags; params->ConsoleHandle = wine_server_ptr_handle( info->console ); params->ConsoleFlags = info->console_flags; params->hStdInput = wine_server_ptr_handle( info->hstdin ); params->hStdOutput = wine_server_ptr_handle( info->hstdout ); params->hStdError = wine_server_ptr_handle( info->hstderr ); params->dwX = info->x; params->dwY = info->y; params->dwXSize = info->xsize; params->dwYSize = info->ysize; params->dwXCountChars = info->xchars; params->dwYCountChars = info->ychars; params->dwFillAttribute = info->attribute; params->dwFlags = info->flags; params->wShowWindow = info->show; src = (WCHAR *)(info + 1); dst = (WCHAR *)(params + 1); /* current directory needs more space */ get_unicode_string( ¶ms->CurrentDirectory.DosPath, &src, &dst, info->curdir_len ); params->CurrentDirectory.DosPath.MaximumLength = MAX_NT_PATH_LENGTH * sizeof(WCHAR); dst = (WCHAR *)(params + 1) + MAX_NT_PATH_LENGTH; get_unicode_string( ¶ms->DllPath, &src, &dst, info->dllpath_len ); get_unicode_string( ¶ms->ImagePathName, &src, &dst, info->imagepath_len ); get_unicode_string( ¶ms->CommandLine, &src, &dst, info->cmdline_len ); get_unicode_string( ¶ms->WindowTitle, &src, &dst, info->title_len ); get_unicode_string( ¶ms->Desktop, &src, &dst, info->desktop_len ); get_unicode_string( ¶ms->ShellInfo, &src, &dst, info->shellinfo_len ); /* runtime info isn't a real string */ params->RuntimeInfo.Buffer = dst; params->RuntimeInfo.Length = params->RuntimeInfo.MaximumLength = info->runtime_len; memcpy( dst, src, info->runtime_len ); /* environment needs to be a separate memory block */ ptr = NULL; alloc_size = max( 1, env_size ); status = NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0, &alloc_size, MEM_COMMIT, PAGE_READWRITE ); if (status != STATUS_SUCCESS) goto done; memcpy( ptr, (char *)info + info_size, env_size ); params->Environment = ptr; done: RtlFreeHeap( GetProcessHeap(), 0, info ); return status; }