mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2136 lines
63 KiB
2136 lines
63 KiB
/*++
|
|
|
|
Copyright (c) 1998-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
init.c
|
|
|
|
Abstract:
|
|
|
|
Process and thread init code for wow64.dll
|
|
|
|
Author:
|
|
|
|
12-May-1998 BarryBo
|
|
|
|
Revision History:
|
|
|
|
08-Mar-2001 SamerA Initialize the SystemEmulation environment using
|
|
the system serivces.
|
|
|
|
--*/
|
|
|
|
#define _WOW64DLLAPI_
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <winbasep.h>
|
|
#include "wow64p.h"
|
|
#include "wow64cpu.h"
|
|
#include "nt32.h"
|
|
#include "thnkhlpr.h"
|
|
#include "regremap.h"
|
|
|
|
ASSERTNAME;
|
|
|
|
|
|
extern WOW64SERVICE_TABLE_DESCRIPTOR sdwhnt32;
|
|
extern __declspec(dllimport) WOW64SERVICE_TABLE_DESCRIPTOR sdwhwin32;
|
|
extern __declspec(dllimport) WOW64SERVICE_TABLE_DESCRIPTOR sdwhcon;
|
|
extern WOW64SERVICE_TABLE_DESCRIPTOR sdwhbase;
|
|
extern __declspec(dllimport) const PVOID Win32kCallbackTable[]; // in wow64win's ntcbc.c
|
|
|
|
|
|
ULONG Ntdll32LoaderInitRoutine;
|
|
ULONG Ntdll32KiUserExceptionDispatcher;
|
|
ULONG Ntdll32KiUserApcDispatcher;
|
|
ULONG Ntdll32KiUserCallbackDispatcher;
|
|
ULONG Ntdll32KiRaiseUserExceptionDispatcher;
|
|
|
|
PPEB32 Peb32; // Pointer to 32-bit PEB for this process
|
|
ULONG NtDll32Base; // Base address for 32-bit ntdll.dll
|
|
|
|
WOW64_SYSTEM_INFORMATION RealSysInfo;
|
|
WOW64_SYSTEM_INFORMATION EmulatedSysInfo;
|
|
|
|
WCHAR NtSystem32PathBuffer[264];
|
|
WCHAR NtWindowsImePathBuffer[264];
|
|
UNICODE_STRING NtSystem32Path;
|
|
UNICODE_STRING NtWindowsImePath;
|
|
WCHAR RegeditPathBuffer[264];
|
|
UNICODE_STRING RegeditPath;
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
Map64BitDlls(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
LookupEntryPoint(
|
|
IN ULONG DllBase,
|
|
IN PSZ NameOfEntryPoint,
|
|
OUT ULONG *AddressOfEntryPoint,
|
|
IN BOOLEAN DllIs64bit
|
|
);
|
|
|
|
USHORT
|
|
NameToOrdinal (
|
|
IN PSZ NameOfEntryPoint,
|
|
IN ULONG DllBase,
|
|
IN ULONG NumberOfNames,
|
|
IN PULONG NameTableBase,
|
|
IN PUSHORT NameOrdinalTableBase
|
|
);
|
|
|
|
NTSTATUS
|
|
MapNtdll32(
|
|
OUT ULONG *pNtDllBase
|
|
);
|
|
|
|
PWSTR
|
|
GetImageName (
|
|
IN PWSTR DefaultImageName
|
|
);
|
|
|
|
typedef DWORD (WINAPI *PPROCESS_START_ROUTINE)(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
Wow64InitializeEmulatedSystemInformation(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the global variable EmulatedSysInfo with the
|
|
system information for the emulated system.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS NtStatus;
|
|
|
|
NtStatus = NtQuerySystemInformation (SystemEmulationBasicInformation,
|
|
&EmulatedSysInfo.BasicInfo,
|
|
sizeof (EmulatedSysInfo.BasicInfo),
|
|
NULL);
|
|
|
|
if (NT_SUCCESS (NtStatus)) {
|
|
|
|
NtStatus = NtQuerySystemInformation (SystemEmulationProcessorInformation,
|
|
&EmulatedSysInfo.ProcessorInfo,
|
|
sizeof (EmulatedSysInfo.ProcessorInfo),
|
|
NULL);
|
|
|
|
if (NT_SUCCESS (NtStatus)) {
|
|
EmulatedSysInfo.RangeInfo = 0x80000000;
|
|
}
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
Wow64InitializeSystemInformation(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the status system information from
|
|
the system and initializes the global variable RealSysInfo.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtQuerySystemInformation(SystemBasicInformation,
|
|
&RealSysInfo.BasicInfo,
|
|
sizeof(SYSTEM_BASIC_INFORMATION),
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = NtQuerySystemInformation(SystemProcessorInformation,
|
|
&RealSysInfo.ProcessorInfo,
|
|
sizeof(SYSTEM_PROCESSOR_INFORMATION),
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = NtQuerySystemInformation(SystemRangeStartInformation,
|
|
&RealSysInfo.RangeInfo,
|
|
sizeof(ULONG_PTR),
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
Wow64pCopyString(
|
|
PCHAR *p,
|
|
PUNICODE_STRING32 str32,
|
|
PUNICODE_STRING str64
|
|
)
|
|
{
|
|
*p = (PCHAR)ROUND_UP((SIZE_T)*p, sizeof(ULONG));
|
|
|
|
str32->Length = str64->Length;
|
|
str32->MaximumLength = str64->MaximumLength;
|
|
RtlCopyMemory(*p, str64->Buffer, str64->MaximumLength);
|
|
str32->Buffer = PtrToUlong(*p);
|
|
|
|
*p += str64->MaximumLength;
|
|
}
|
|
|
|
|
|
ENVIRONMENT_THUNK_TABLE EnvironmentVariableTable[] =
|
|
{
|
|
{
|
|
L"ProgramFiles", // Native environment variable to thunk
|
|
L"ProgramFiles(x86)", // Value for the thunked environment variable
|
|
L"ProgramW6432", // New environment variable to hold the original value being thunked
|
|
TRUE // Treat the first value as an environment variable
|
|
},
|
|
{
|
|
L"CommonProgramFiles",
|
|
L"CommonProgramFiles(x86)",
|
|
L"CommonProgramW6432",
|
|
TRUE
|
|
},
|
|
|
|
{
|
|
L"PROCESSOR_ARCHITECTURE",
|
|
L"x86",
|
|
L"PROCESSOR_ARCHITEW6432",
|
|
FALSE
|
|
},
|
|
};
|
|
|
|
|
|
NTSTATUS
|
|
Wow64pThunkEnvironmentVariables(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will thunk the environment variables pointed out by the EnvironmentVariableTable
|
|
to the ones used on an x86 system, and will save the original values. The original
|
|
values are restored when the
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING Name;
|
|
UNICODE_STRING Value, Value2;
|
|
WCHAR Buffer [ 128 ];
|
|
WCHAR Buffer2 [ 128 ];
|
|
NTSTATUS NtStatus;
|
|
ULONG i;
|
|
|
|
|
|
i = 0;
|
|
|
|
while (i < (sizeof(EnvironmentVariableTable) / sizeof(EnvironmentVariableTable[0])))
|
|
{
|
|
|
|
//
|
|
// If a fake name already exists, then skip this one.
|
|
//
|
|
|
|
RtlInitUnicodeString (&Name, EnvironmentVariableTable[i].FakeName);
|
|
|
|
Value.Length = 0;
|
|
Value.MaximumLength = sizeof (Buffer);
|
|
Value.Buffer = Buffer;
|
|
|
|
NtStatus = RtlQueryEnvironmentVariable_U (NULL,
|
|
&Name,
|
|
&Value
|
|
);
|
|
|
|
if (!NT_SUCCESS (NtStatus)) {
|
|
|
|
//
|
|
// Retreive the name of the ProgramFiles(x86) environment variable
|
|
//
|
|
|
|
if (EnvironmentVariableTable[i].IsX86EnvironmentVar == TRUE) {
|
|
|
|
RtlInitUnicodeString (&Name, EnvironmentVariableTable[i].X86);
|
|
|
|
Value.Length = 0;
|
|
Value.MaximumLength = sizeof (Buffer);
|
|
Value.Buffer = Buffer;
|
|
|
|
|
|
NtStatus = RtlQueryEnvironmentVariable_U (NULL,
|
|
&Name,
|
|
&Value
|
|
);
|
|
} else {
|
|
|
|
RtlInitUnicodeString(&Value, EnvironmentVariableTable[i].X86);
|
|
|
|
NtStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (NT_SUCCESS (NtStatus))
|
|
{
|
|
|
|
//
|
|
// Save the original ProgramFiles environment variable
|
|
//
|
|
|
|
RtlInitUnicodeString (&Name, EnvironmentVariableTable[i].Native);
|
|
|
|
Value2.Length = 0;
|
|
Value2.MaximumLength = sizeof (Buffer2);
|
|
Value2.Buffer = Buffer2;
|
|
|
|
NtStatus = RtlQueryEnvironmentVariable_U (NULL,
|
|
&Name,
|
|
&Value2
|
|
);
|
|
|
|
if (NT_SUCCESS (NtStatus))
|
|
{
|
|
//
|
|
// Set the ProgramFiles environment variable to the x86 one
|
|
//
|
|
|
|
NtStatus = RtlSetEnvironmentVariable (NULL,
|
|
&Name,
|
|
&Value
|
|
);
|
|
|
|
if (NT_SUCCESS (NtStatus))
|
|
{
|
|
|
|
RtlInitUnicodeString (&Name, EnvironmentVariableTable[i].FakeName);
|
|
|
|
NtStatus = RtlSetEnvironmentVariable (NULL,
|
|
&Name,
|
|
&Value2
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
LOGPRINT((TRACELOG, "Result of thunking programfiles environment variables - %lx\n", NtStatus));
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
Wow64pThunkProcessParameters(
|
|
PPEB32 Peb32,
|
|
PPEB Peb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function copies the process parameters from the 64bit peb to the 32bit peb.
|
|
|
|
Arguments:
|
|
|
|
Peb32 - Supplies the pointer to the 32bit peb that will recieve the process parameters.
|
|
Peb - Supplies the pointer to the 64bit peb that will supply the process parameters.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
{
|
|
|
|
SIZE_T AllocSize;
|
|
PRTL_USER_PROCESS_PARAMETERS Params64;
|
|
PRTL_USER_PROCESS_PARAMETERS32 Params32;
|
|
SIZE_T Index;
|
|
PCHAR p;
|
|
PVOID Base;
|
|
SIZE_T RegionSize;
|
|
NTSTATUS st;
|
|
|
|
|
|
//
|
|
// Thunk the environment variables now.
|
|
//
|
|
|
|
Wow64pThunkEnvironmentVariables();
|
|
|
|
// Process Parameters should have been normalized by ntdll.
|
|
|
|
Params64 = Peb->ProcessParameters;
|
|
if(NULL == Params64) {
|
|
Peb32->ProcessParameters = (TYPE32(PRTL_USER_PROCESS_PARAMETERS))0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Compute the required space for the continous memory region.
|
|
|
|
AllocSize = sizeof(RTL_USER_PROCESS_PARAMETERS32);
|
|
AllocSize += ROUND_UP(Params64->CurrentDirectory.DosPath.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->DllPath.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->ImagePathName.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->CommandLine.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->WindowTitle.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->DesktopInfo.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->ShellInfo.MaximumLength, sizeof(ULONG));
|
|
AllocSize += ROUND_UP(Params64->RuntimeData.MaximumLength, sizeof(ULONG));
|
|
|
|
for(Index=0; Index < RTL_MAX_DRIVE_LETTERS; Index++) {
|
|
AllocSize += ROUND_UP(Params64->CurrentDirectores[Index].DosPath.MaximumLength, sizeof(ULONG));
|
|
}
|
|
|
|
Base = NULL;
|
|
RegionSize = AllocSize;
|
|
st = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&Base,
|
|
0,
|
|
&RegionSize,
|
|
MEM_COMMIT|MEM_RESERVE,
|
|
PAGE_READWRITE);
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "ThunkProcessParameters: NtAllocateVirtualMemory failed allocating process paramaters, error %x.\n", st));
|
|
return st;
|
|
}
|
|
|
|
Params32 = (PRTL_USER_PROCESS_PARAMETERS32)Base;
|
|
Peb32->ProcessParameters = (TYPE32(PRTL_USER_PROCESS_PARAMETERS))PtrToUlong(Params32);
|
|
p = (PCHAR)Params32 + sizeof(RTL_USER_PROCESS_PARAMETERS32);
|
|
|
|
Params32->MaximumLength = Params32->Length = (ULONG)AllocSize;
|
|
Params32->Flags = Params64->Flags;
|
|
Params32->DebugFlags = Params64->DebugFlags;
|
|
Params32->ConsoleHandle = (TYPE32(HANDLE))PtrToUlong(Params64->ConsoleHandle);
|
|
Params32->ConsoleFlags = (ULONG)Params64->ConsoleFlags;
|
|
Params32->StandardInput = (TYPE32(HANDLE)) PtrToUlong(Params64->StandardInput);
|
|
Params32->StandardOutput = (TYPE32(HANDLE)) PtrToUlong(Params64->StandardOutput);
|
|
Params32->StandardError = (TYPE32(HANDLE)) PtrToUlong(Params64->StandardError);
|
|
|
|
Params32->CurrentDirectory.Handle = (TYPE32(HANDLE)) PtrToUlong(Params64->CurrentDirectory.Handle);
|
|
Wow64pCopyString(&p, &Params32->CurrentDirectory.DosPath, &Params64->CurrentDirectory.DosPath);
|
|
|
|
Wow64pCopyString(&p, &Params32->DllPath, &Params64->DllPath);
|
|
Wow64pCopyString(&p, &Params32->ImagePathName, &Params64->ImagePathName);
|
|
Wow64pCopyString(&p, &Params32->CommandLine, &Params64->CommandLine);
|
|
Params32->Environment = PtrToUlong(Params64->Environment);
|
|
|
|
Params32->StartingX = Params64->StartingX;
|
|
Params32->StartingY = Params64->StartingY;
|
|
Params32->CountX = Params64->CountX;
|
|
Params32->CountY = Params64->CountY;
|
|
Params32->CountCharsX = Params64->CountCharsX;
|
|
Params32->CountCharsY = Params64->CountCharsY;
|
|
Params32->FillAttribute = Params64->FillAttribute;
|
|
|
|
Params32->WindowFlags = Params64->WindowFlags;
|
|
Params32->ShowWindowFlags = Params64->ShowWindowFlags;
|
|
Wow64pCopyString(&p, &Params32->WindowTitle, &Params64->WindowTitle);
|
|
Wow64pCopyString(&p, &Params32->DesktopInfo, &Params64->DesktopInfo);
|
|
Wow64pCopyString(&p, &Params32->ShellInfo, &Params64->ShellInfo);
|
|
|
|
// RuntimeData is mis-prototyped as a UNICODE_STRING. However,
|
|
// it is really used by the C runtime as a mechanism to pass file
|
|
// handles around. Thunk it as such. See sdktools\vctools\crtw32
|
|
// \exec\dospawn.c and lowio\ioinit.c for the gory details.
|
|
if (Params64->RuntimeData.Length && Params64->RuntimeData.Buffer) {
|
|
int cfi_len;
|
|
char *posfile64;
|
|
UINT_PTR UNALIGNED *posfhnd64;
|
|
char *posfile32;
|
|
UINT UNALIGNED *posfhnd32;
|
|
int i;
|
|
|
|
cfi_len = *(int UNALIGNED *)Params64->RuntimeData.Buffer;
|
|
|
|
Params32->RuntimeData.Length = Params64->RuntimeData.Length - cfi_len * sizeof(ULONG);
|
|
Params32->RuntimeData.MaximumLength = Params32->RuntimeData.Length;
|
|
Params32->RuntimeData.Buffer = PtrToUlong(p);
|
|
p += Params32->RuntimeData.Length;
|
|
|
|
posfile64 = (char *)((UINT_PTR)Params64->RuntimeData.Buffer+sizeof(int));
|
|
posfhnd64 = (UINT_PTR UNALIGNED *)(posfile64 + cfi_len);
|
|
posfile32 = (char *)((ULONG_PTR)Params32->RuntimeData.Buffer+sizeof(int));
|
|
posfhnd32 = (UINT UNALIGNED *)(posfile32 + cfi_len);
|
|
|
|
*(int *)Params32->RuntimeData.Buffer = cfi_len;
|
|
for (i=0; i<cfi_len; ++i) {
|
|
*posfile32 = *posfile64;
|
|
*posfhnd32 = (ULONG)*posfhnd64;
|
|
posfile32++;
|
|
posfile64++;
|
|
posfhnd32++;
|
|
posfhnd64++;
|
|
}
|
|
|
|
// Any bytes past the end of 4+(cfi_len*(sizeof(UINT_PTR)+sizeof(UINT))
|
|
// must be copied verbatim. They are probably from a non-MS C runtime.
|
|
memcpy(posfhnd32, posfhnd64, (Params64->RuntimeData.Length - ((ULONG_PTR)posfhnd64 - (ULONG_PTR)Params64->RuntimeData.Buffer)));
|
|
}
|
|
|
|
for(Index = 0; Index < RTL_MAX_DRIVE_LETTERS; Index++) {
|
|
Params32->CurrentDirectores[Index].Flags = Params64->CurrentDirectores[Index].Flags;
|
|
Params32->CurrentDirectores[Index].Length = sizeof(RTL_DRIVE_LETTER_CURDIR);
|
|
Params32->CurrentDirectores[Index].TimeStamp = Params64->CurrentDirectores[Index].TimeStamp;
|
|
Wow64pCopyString(&p, (PUNICODE_STRING32)&Params32->CurrentDirectores[Index].DosPath, (PUNICODE_STRING)&Params64->CurrentDirectores[Index].DosPath);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// This compile-time assert ensures that the PEB64 and PEB32 structures are in
|
|
// alignment on an IA64 build. If this fails, someone added, deleted, or changed
|
|
// the type of a field in PEB32/PEB64 depending on the compile destination. Check
|
|
// %ntdir%\base\published\wow64t.w for alignment errors.
|
|
//
|
|
// If you hit hit this, you'll see messages something like:
|
|
//
|
|
// error C2118: negative subscript or subscript is too large
|
|
//
|
|
#ifdef _WIN64
|
|
#define PEB_ALIGN_TARGET PEB64
|
|
#define PEB_ALIGN_SOURCE PEB
|
|
#else
|
|
#define PEB_ALIGN_TARGET PEB32
|
|
#define PEB_ALIGN_SOURCE PEB
|
|
#endif
|
|
#define CHECK_PEB_ALIGNMENT( f ) C_ASSERT( FIELD_OFFSET( PEB_ALIGN_SOURCE, f ) == FIELD_OFFSET( PEB_ALIGN_TARGET, f ) )
|
|
|
|
CHECK_PEB_ALIGNMENT( ActivationContextData );
|
|
CHECK_PEB_ALIGNMENT( ProcessAssemblyStorageMap );
|
|
CHECK_PEB_ALIGNMENT( SystemDefaultActivationContextData );
|
|
CHECK_PEB_ALIGNMENT( SystemAssemblyStorageMap );
|
|
CHECK_PEB_ALIGNMENT( pShimData );
|
|
|
|
#undef CHECK_PEB_ALIGNMENT
|
|
#undef PEB_ALIGN_TARGET
|
|
#undef PEB_ALIGN_SOURCE
|
|
|
|
|
|
NTSTATUS
|
|
ProcessInit(
|
|
PSIZE_T pCpuThreadSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform per-process initialization for wow64.dll. That includes
|
|
creating the 32-bit PEB and mapping in 32-bit ntdll.dll.
|
|
|
|
Arguments:
|
|
|
|
pCpuThreadSize - OUT PTR to store the CPU's per-thread data requirements
|
|
in.
|
|
|
|
Return Value:
|
|
|
|
Status. If this fails, it doesn't clean up, assuming that the
|
|
process is going to fail to run and exit right away, so nothing really
|
|
gets leaked.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS st;
|
|
PVOID Base;
|
|
SIZE_T RegionSize;
|
|
PPEB Peb64 = NtCurrentPeb(); // get 64-bit PEB pointer
|
|
ULONG ul;
|
|
BOOLEAN b;
|
|
HANDLE hKey;
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
PWSTR defaultName;
|
|
PWSTR imageName;
|
|
|
|
InitializeDebug();
|
|
|
|
//
|
|
// Initialize Wow64Info
|
|
//
|
|
st = Wow64pInitializeWow64Info();
|
|
if (!NT_SUCCESS (st)) {
|
|
}
|
|
|
|
st = InitializeContextMapper();
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
st = Wow64InitializeSystemInformation();
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
st = Wow64InitializeEmulatedSystemInformation();
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Initialize all critical sections
|
|
//
|
|
st = RtlInitializeCriticalSection(&HandleDataCriticalSection);
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Map in 32-bit ntdll32.dll and fill in the Ntdll32* global vars
|
|
// with the system exports from ntdll.
|
|
//
|
|
st = MapNtdll32(&NtDll32Base);
|
|
if (!NT_SUCCESS (st)) {
|
|
LOGPRINT((ERRORLOG, "ProcessInit: MapNtdll32 failed, error %x \n", st));
|
|
return st;
|
|
}
|
|
|
|
//Map in the 64bit DLLs.
|
|
st = Map64BitDlls();
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
// Create the SuspendThread mutant to serialize access to the API
|
|
st = Wow64pInitializeSuspendMutant();
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
// Get the full Nt Pathname to the %windir%\system32 directory,
|
|
// %windir% and %windir%\regedit.exe
|
|
NtSystem32PathBuffer[0] = L'\\';
|
|
NtSystem32PathBuffer[1] = L'?';
|
|
NtSystem32PathBuffer[2] = L'?';
|
|
NtSystem32PathBuffer[3] = L'\\';
|
|
wcscpy(&NtSystem32PathBuffer[4], USER_SHARED_DATA->NtSystemRoot);
|
|
wcscpy(RegeditPathBuffer, NtSystem32PathBuffer);
|
|
wcscpy(NtWindowsImePathBuffer, NtSystem32PathBuffer);
|
|
wcscat(&NtSystem32PathBuffer[4], L"\\system32");
|
|
NtSystem32Path.Buffer = NtSystem32PathBuffer;
|
|
NtSystem32Path.MaximumLength = sizeof(NtSystem32PathBuffer);
|
|
NtSystem32Path.Length = wcslen(NtSystem32PathBuffer) * sizeof(WCHAR);
|
|
|
|
wcscat(NtWindowsImePathBuffer, L"\\ime");
|
|
NtWindowsImePath.Buffer = NtWindowsImePathBuffer;
|
|
NtWindowsImePath.MaximumLength = sizeof(NtWindowsImePathBuffer);
|
|
NtWindowsImePath.Length = wcslen(NtWindowsImePathBuffer) * sizeof(WCHAR);
|
|
|
|
wcscat(RegeditPathBuffer, L"\\regedit.exe");
|
|
RegeditPath.Buffer = RegeditPathBuffer;
|
|
RegeditPath.MaximumLength = sizeof(RegeditPathBuffer);
|
|
RegeditPath.Length = wcslen(RegeditPathBuffer) * sizeof(WCHAR);
|
|
|
|
//
|
|
// Initialize the system service tables
|
|
//
|
|
ServiceTables[WHNT32_INDEX] = sdwhnt32;
|
|
ServiceTables[WHCON_INDEX] = sdwhcon;
|
|
ServiceTables[WHWIN32_INDEX] = sdwhwin32;
|
|
ServiceTables[WHBASE_INDEX] = sdwhbase;
|
|
|
|
NtCurrentPeb()->KernelCallbackTable = Win32kCallbackTable;
|
|
|
|
//
|
|
// Get the address of the PEB32 from the process information
|
|
//
|
|
st = NtQueryInformationProcess(NtCurrentProcess(),
|
|
ProcessWow64Information,
|
|
&Peb32,
|
|
sizeof(Peb32),
|
|
NULL);
|
|
if (!NT_SUCCESS (st)) {
|
|
return st;
|
|
}
|
|
|
|
st = Wow64pThunkProcessParameters(Peb32, Peb64);
|
|
if (!NT_SUCCESS (st)) {
|
|
LOGPRINT((ERRORLOG, "ProcessInit: ThunkProcessParameters failed, error %x\n", st));
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Copy this one field down to the PEB32 from the native PEB. It
|
|
// is written into the 64-bit PEB by Fusion in csrss.exe after
|
|
// NtCreateProcess has been called by the parent process.
|
|
//
|
|
|
|
Peb32->ActivationContextData = PtrToUlong(Peb64->ActivationContextData);
|
|
Peb32->SystemDefaultActivationContextData = PtrToUlong(Peb64->SystemDefaultActivationContextData);
|
|
|
|
//
|
|
// Copy the pShimData if it exists on the 64-bit side and doesn't
|
|
// exist on the 32-bit side of the peb
|
|
//
|
|
|
|
if (Peb32->pShimData == 0L) {
|
|
Peb32->pShimData = PtrToUlong (Peb64->pShimData);
|
|
}
|
|
|
|
//
|
|
// If wow64 is running in guimode setup, then make sure the 32-bit Peb
|
|
// BeingDebugged flag is FALSE. Otherwise, if we are being debugged
|
|
// by a 64-bit debugger with no WOW64 debugger extensions then it will
|
|
// hit the STATUS_WX86_BREAKPOINT exception and halt.
|
|
//
|
|
RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\Setup");
|
|
InitializeObjectAttributes(&ObjA, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtOpenKey(&hKey, KEY_READ, &ObjA);
|
|
if (NT_SUCCESS(st)) {
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
WCHAR Buffer[400];
|
|
ULONG ResultLength;
|
|
|
|
RtlInitUnicodeString(&KeyName, L"SystemSetupInProgress");
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
|
|
st = NtQueryValueKey(hKey,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof(Buffer),
|
|
&ResultLength);
|
|
if (NT_SUCCESS(st) &&
|
|
KeyValueInformation->Type == REG_DWORD &&
|
|
*(DWORD *)(KeyValueInformation->Data)) {
|
|
|
|
Peb32->BeingDebugged = FALSE;
|
|
|
|
}
|
|
NtClose(hKey);
|
|
}
|
|
|
|
//
|
|
// Initialize the CPU
|
|
//
|
|
defaultName = L"Unknown Image";
|
|
imageName = GetImageName(defaultName);
|
|
|
|
st = CpuProcessInit(imageName, pCpuThreadSize);
|
|
|
|
//
|
|
// Success or failure, we are done with the image name
|
|
//
|
|
if (imageName != defaultName) {
|
|
Wow64FreeHeap(imageName);
|
|
}
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "ProcessInit: CpuProcessInit failed, error %x.\n", st));
|
|
return st;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
VOID
|
|
Wow64Shutdown(
|
|
HANDLE ProcessHandle
|
|
)
|
|
{
|
|
CpuProcessTerm(ProcessHandle);
|
|
CleanupReflector ( 0);
|
|
ShutdownDebug();
|
|
}
|
|
|
|
|
|
VOID
|
|
ThunkPeb64ToPeb32(
|
|
IN PPEB Peb64,
|
|
OUT PPEB32 Peb32
|
|
)
|
|
{
|
|
RtlZeroMemory(Peb32, sizeof(PEB32));
|
|
Peb32->Mutant = 0xffffffff;
|
|
|
|
//
|
|
// Initialize the Peb32 (copied from ntos\mm\procsup.c's MmCreatePeb)
|
|
//
|
|
Peb32->ImageBaseAddress = PtrToUlong(Peb64->ImageBaseAddress);
|
|
Peb32->AnsiCodePageData = PtrToUlong(Peb64->AnsiCodePageData);
|
|
Peb32->OemCodePageData = PtrToUlong(Peb64->OemCodePageData);
|
|
Peb32->UnicodeCaseTableData = PtrToUlong(Peb64->UnicodeCaseTableData);
|
|
Peb32->NumberOfProcessors = Peb64->NumberOfProcessors;
|
|
Peb32->BeingDebugged = Peb64->BeingDebugged;
|
|
Peb32->NtGlobalFlag = Peb64->NtGlobalFlag;
|
|
Peb32->CriticalSectionTimeout = Peb64->CriticalSectionTimeout;
|
|
if (Peb64->HeapSegmentReserve > 1024*1024*1024) { // 1gig
|
|
Peb32->HeapSegmentReserve = 1024*1024; // 1meg
|
|
} else {
|
|
Peb32->HeapSegmentReserve = (ULONG)Peb64->HeapSegmentReserve;
|
|
}
|
|
if (Peb64->HeapSegmentCommit > Peb32->HeapSegmentReserve) {
|
|
Peb32->HeapSegmentCommit = 2*PAGE_SIZE;
|
|
} else {
|
|
Peb32->HeapSegmentCommit = (ULONG)Peb64->HeapSegmentCommit;
|
|
}
|
|
Peb32->HeapDeCommitTotalFreeThreshold = (ULONG)Peb64->HeapDeCommitTotalFreeThreshold;
|
|
Peb32->HeapDeCommitFreeBlockThreshold = (ULONG)Peb64->HeapDeCommitFreeBlockThreshold;
|
|
Peb32->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB32))/sizeof(ULONG);
|
|
Peb32->ProcessHeaps = PtrToUlong(Peb32+1);
|
|
Peb32->OSMajorVersion = Peb64->OSMajorVersion;
|
|
Peb32->OSMinorVersion = Peb64->OSMinorVersion;
|
|
Peb32->OSBuildNumber = Peb64->OSBuildNumber;
|
|
Peb32->OSPlatformId = Peb64->OSPlatformId;
|
|
Peb32->OSCSDVersion = Peb64->OSCSDVersion;
|
|
Peb32->ImageSubsystem = Peb64->ImageSubsystem;
|
|
Peb32->ImageSubsystemMajorVersion = Peb64->ImageSubsystemMajorVersion;
|
|
Peb32->ImageSubsystemMinorVersion = Peb64->ImageSubsystemMinorVersion;
|
|
Peb32->ImageProcessAffinityMask = PtrToUlong((PVOID)Peb64->ImageProcessAffinityMask);
|
|
Peb32->SessionId = Peb64->SessionId;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ThreadInit(
|
|
PVOID pCpuThreadData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform per-thread initialization for wow64.dll.
|
|
|
|
Arguments:
|
|
|
|
pCpuThreadData - pointer to private per-thread data for the CPU to use.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS st;
|
|
PVOID Base;
|
|
SIZE_T RegionSize;
|
|
PTEB32 Teb32;
|
|
PCH Stack;
|
|
BOOLEAN GuardPage;
|
|
ULONG OldProtect;
|
|
SIZE_T ImageStackSize, ImageStackCommit, MaximumStackSize, StackSize;
|
|
PIMAGE_NT_HEADERS32 NtHeaders;
|
|
PPEB Peb64;
|
|
PTEB Teb64;
|
|
|
|
|
|
|
|
Peb64 = NtCurrentPeb();
|
|
Teb64 = NtCurrentTeb();
|
|
Teb32 = NtCurrentTeb32();
|
|
|
|
if (Teb32->DeallocationStack == PtrToUlong( NULL ))
|
|
{
|
|
//
|
|
// Allocate the 32-bit stack. Cloned from windows\base\client\support.c
|
|
// If the stack size was not supplied, then use the sizes from the
|
|
// image header.
|
|
//
|
|
NtHeaders = (PIMAGE_NT_HEADERS32)RtlImageNtHeader(Peb64->ImageBaseAddress);
|
|
ImageStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve;
|
|
ImageStackCommit = NtHeaders->OptionalHeader.SizeOfStackCommit;
|
|
|
|
MaximumStackSize = ImageStackSize;
|
|
StackSize = ImageStackCommit;
|
|
|
|
//
|
|
// Align the stack size to a page boundry and the reserved size
|
|
// to an allocation granularity boundry.
|
|
//
|
|
StackSize = ROUND_UP ( StackSize, PAGE_SIZE );
|
|
MaximumStackSize = ROUND_UP ( MaximumStackSize, 65536 );
|
|
|
|
//
|
|
// Reserve address space for the stack
|
|
//
|
|
|
|
Stack = NULL;
|
|
st = NtAllocateVirtualMemory(
|
|
NtCurrentProcess(),
|
|
(PVOID *)&Stack,
|
|
0,
|
|
&MaximumStackSize,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!NT_SUCCESS( st ))
|
|
{
|
|
LOGPRINT((ERRORLOG, "ThreadInit: NtAllocateVirtualMemory failed, error %x\n", st));
|
|
goto ReturnError;
|
|
}
|
|
|
|
LOGPRINT((TRACELOG, "ThreadInit: 32 bit stack allocated at %I64x \n", (ULONGLONG)Stack));
|
|
|
|
Teb32->DeallocationStack = PtrToUlong(Stack);
|
|
Teb32->NtTib.StackBase = PtrToUlong(Stack + MaximumStackSize);
|
|
|
|
Stack += MaximumStackSize - StackSize;
|
|
if (MaximumStackSize > StackSize)
|
|
{
|
|
Stack -= PAGE_SIZE;
|
|
StackSize += PAGE_SIZE;
|
|
GuardPage = TRUE;
|
|
}
|
|
else
|
|
{
|
|
GuardPage = FALSE;
|
|
}
|
|
|
|
//
|
|
// Commit the initially valid portion of the stack
|
|
//
|
|
st = NtAllocateVirtualMemory(
|
|
NtCurrentProcess(),
|
|
(PVOID *)&Stack,
|
|
0,
|
|
&StackSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!NT_SUCCESS( st ))
|
|
{
|
|
//
|
|
// commit failed
|
|
//
|
|
|
|
LOGPRINT((ERRORLOG, "ThreadInit: NtAllocateVirtualMemory commit failed, error %x\n", st));
|
|
goto ErrorFreeStack;
|
|
}
|
|
|
|
Teb32->NtTib.StackLimit = PtrToUlong(Stack);
|
|
|
|
//
|
|
// if we have space, create a guard page.
|
|
//
|
|
if (GuardPage)
|
|
{
|
|
RegionSize = PAGE_SIZE;
|
|
st = NtProtectVirtualMemory(
|
|
NtCurrentProcess(),
|
|
(PVOID *)&Stack,
|
|
&RegionSize,
|
|
PAGE_GUARD | PAGE_READWRITE,
|
|
&OldProtect
|
|
);
|
|
|
|
if (!NT_SUCCESS( st ))
|
|
{
|
|
LOGPRINT((ERRORLOG, "ThreadInit: NtAllocateVirtualMemory for guard-page failed, error %x\n", st));
|
|
goto ErrorFreeStack;
|
|
}
|
|
Teb32->NtTib.StackLimit = PtrToUlong ((PUCHAR)Teb32->NtTib.StackLimit + RegionSize);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Migrate the Teb->IdealProcessor & Teb->CurrentLocale from the 64-bit TEB.
|
|
// The kernel does exactly the same thing before starting the usermode thread by
|
|
// migrating these values from the TCB.
|
|
//
|
|
Teb32->CurrentLocale = Teb64->CurrentLocale;
|
|
Teb32->IdealProcessor = Teb64->IdealProcessor;
|
|
|
|
//
|
|
// Now that everything else is initialized, run the CPU's per-thread
|
|
// initialization code.
|
|
//
|
|
|
|
st = CpuThreadInit (pCpuThreadData);
|
|
|
|
if (NT_SUCCESS( st ))
|
|
{
|
|
return st;
|
|
}
|
|
|
|
LOGPRINT((ERRORLOG, "ThreadInit: CpuThreadInit failed, error %x\n", st));
|
|
|
|
ErrorFreeStack:
|
|
|
|
Base = (PVOID) Teb32->DeallocationStack;
|
|
RegionSize = 0;
|
|
NtFreeVirtualMemory(NtCurrentProcess(), &Base, &RegionSize, MEM_RELEASE);
|
|
|
|
ReturnError:
|
|
|
|
return st;
|
|
}
|
|
|
|
NTSTATUS
|
|
MapNtdll32(
|
|
OUT ULONG *pNtDll32Base
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Map 32-bit ntdll32.dll into memory and look up all of the important
|
|
entrypoints.
|
|
|
|
Arguments:
|
|
|
|
pNtDll32Base - OUT base address of the DLL.
|
|
|
|
Return Value:
|
|
|
|
Status. On success, Ntdll32* globals are set.
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING SystemDllPath;
|
|
WCHAR SystemDllPathBuffer[DOS_MAX_PATH_LENGTH];
|
|
NTSTATUS st;
|
|
UNICODE_STRING FullDllName;
|
|
WCHAR FullDllNameBuffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING NtFileName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
HANDLE File;
|
|
HANDLE Section;
|
|
PVOID ViewBase;
|
|
SIZE_T ViewSize;
|
|
PVOID pv;
|
|
PTEB Teb;
|
|
PVOID ArbitraryUserPointer;
|
|
|
|
|
|
//
|
|
// Build up the name of the 32-bit system directory
|
|
//
|
|
SystemDllPath.Buffer = SystemDllPathBuffer;
|
|
SystemDllPath.Length = 0;
|
|
SystemDllPath.MaximumLength = sizeof(SystemDllPathBuffer);
|
|
RtlAppendUnicodeToString(&SystemDllPath, USER_SHARED_DATA->NtSystemRoot);
|
|
RtlAppendUnicodeToString(&SystemDllPath, L"\\" WOW64_SYSTEM_DIRECTORY_U);
|
|
|
|
//
|
|
// Build up the full pathname to %SystemRoot%\syswow64\ntdll32.dll
|
|
//
|
|
FullDllName.Buffer = FullDllNameBuffer;
|
|
FullDllName.Length = 0;
|
|
FullDllName.MaximumLength = sizeof(FullDllNameBuffer);
|
|
RtlCopyUnicodeString(&FullDllName, &SystemDllPath);
|
|
RtlAppendUnicodeToString(&FullDllName, L"\\ntdll.dll");
|
|
|
|
//
|
|
// Convert the Win32 pathname to an NT pathname
|
|
//
|
|
if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer,
|
|
&NtFileName,
|
|
NULL,
|
|
NULL)) {
|
|
// probably out-of-memory
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&NtFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
st = NtOpenFile(&File,
|
|
SYNCHRONIZE | FILE_EXECUTE,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NtFileName.Buffer);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Create the section
|
|
//
|
|
st = NtCreateSection(&Section,
|
|
SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
|
|
NULL,
|
|
NULL,
|
|
PAGE_EXECUTE,
|
|
SEC_IMAGE,
|
|
File);
|
|
NtClose(File);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32: NtCreateSection failed, error %x\n", st));
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Map the section in and let the debugger know the name of the image.
|
|
// We lie to NTSD about the image name, calling it ntdll32.dll so
|
|
// it can disambiguate between the two when doing name resolution.
|
|
// Put 64-bit symbols first on sympath, then 32-bit, and NTSD will find
|
|
// the 32-bit ntdll.pdb and use it for ntdll32.dll.
|
|
//
|
|
*pNtDll32Base = 0;
|
|
pv = NULL;
|
|
ViewSize = 0;
|
|
Teb = NtCurrentTeb();
|
|
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
|
|
FullDllName.Buffer = FullDllNameBuffer;
|
|
FullDllName.Length = 0;
|
|
FullDllName.MaximumLength = sizeof(FullDllNameBuffer);
|
|
RtlCopyUnicodeString(&FullDllName, &SystemDllPath);
|
|
RtlAppendUnicodeToString(&FullDllName, L"\\ntdll32.dll");
|
|
Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;
|
|
st = NtMapViewOfSection(Section,
|
|
NtCurrentProcess(),
|
|
&pv,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&ViewSize,
|
|
ViewShare,
|
|
0,
|
|
PAGE_EXECUTE);
|
|
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
|
|
NtClose(Section);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32, NtMapViewOfSection failed, error %x\n", st));
|
|
return st;
|
|
} else if (st == STATUS_IMAGE_NOT_AT_BASE) {
|
|
LOGPRINT((ERRORLOG, "ntdll32.dll not at base.\n"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
*pNtDll32Base = PtrToUlong(pv);
|
|
|
|
//
|
|
// Look up the required exports from the Dll.
|
|
//
|
|
// main entrypoint
|
|
st = LookupEntryPoint(*pNtDll32Base,
|
|
"LdrInitializeThunk",
|
|
&Ntdll32LoaderInitRoutine,
|
|
FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32: LookupEntryPoint LdrInitializeThunk failed, error %x\n", st));
|
|
return st;
|
|
}
|
|
|
|
// exception dispatching
|
|
st = LookupEntryPoint(*pNtDll32Base,
|
|
"KiUserExceptionDispatcher",
|
|
&Ntdll32KiUserExceptionDispatcher,
|
|
FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32: LookupEntryPoint KiUserExceptionDispatcher failed, error %x\n", st));
|
|
WOWASSERT(FALSE);
|
|
return st;
|
|
}
|
|
|
|
// usermode APC dispatching
|
|
st = LookupEntryPoint(*pNtDll32Base,
|
|
"KiUserApcDispatcher",
|
|
&Ntdll32KiUserApcDispatcher,
|
|
FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32: LookupEntryPoint KiUserApcDispatcher failed, error %x\n", st));
|
|
WOWASSERT(FALSE);
|
|
return st;
|
|
}
|
|
|
|
// callback dispatching
|
|
st = LookupEntryPoint(*pNtDll32Base,
|
|
"KiUserCallbackDispatcher",
|
|
&Ntdll32KiUserCallbackDispatcher,
|
|
FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32: LookupEntryPoint KiUserCallbackDispatcher failed, error %x\n", st));
|
|
WOWASSERT(FALSE);
|
|
return st;
|
|
}
|
|
|
|
// raise a usermode exception
|
|
st = LookupEntryPoint(*pNtDll32Base,
|
|
"KiRaiseUserExceptionDispatcher",
|
|
&Ntdll32KiRaiseUserExceptionDispatcher,
|
|
FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((ERRORLOG, "MapNtDll32: LookupEntryPoint KiRaiseUserExceptionDispatcher failed, error %x\n", st));
|
|
WOWASSERT(FALSE);
|
|
return st;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LookupEntryPoint(
|
|
IN ULONG DllBase,
|
|
IN PSZ NameOfEntryPoint,
|
|
OUT ULONG *AddressOfEntryPoint,
|
|
BOOLEAN DllIs64bit
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cloned from ntos\init\init.c LookupEntryPoint(). Tiny version of
|
|
GetProcAddress.
|
|
|
|
Arguments:
|
|
|
|
DllBase - Dll to look export up in
|
|
NameOfEntryPoint - Name of export to look up
|
|
AddressOfEntryPoint - OUT ptr to location to write the proc address
|
|
DllIs64bit - TRUE if DLL is 64bit
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
{
|
|
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
|
|
ULONG ExportSize;
|
|
USHORT Ordinal;
|
|
PULONG Addr;
|
|
CHAR NameBuffer[64];
|
|
|
|
if (DllIs64bit) {
|
|
|
|
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
|
|
RtlImageDirectoryEntryToData(
|
|
(PVOID)DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
&ExportSize);
|
|
|
|
}
|
|
else {
|
|
|
|
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
|
|
RtlImageDirectoryEntryToData(
|
|
(PVOID)DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
&ExportSize);
|
|
}
|
|
|
|
if ( strlen(NameOfEntryPoint) > sizeof(NameBuffer)-2 ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
strcpy(NameBuffer,NameOfEntryPoint);
|
|
|
|
Ordinal = NameToOrdinal(
|
|
NameBuffer,
|
|
DllBase,
|
|
ExportDirectory->NumberOfNames,
|
|
(PULONG)((UINT_PTR)DllBase + ExportDirectory->AddressOfNames),
|
|
(PUSHORT)((UINT_PTR)DllBase + ExportDirectory->AddressOfNameOrdinals)
|
|
);
|
|
|
|
//
|
|
// If Ordinal is not within the Export Address Table,
|
|
// then DLL does not implement function.
|
|
//
|
|
|
|
if ( (ULONG)Ordinal >= ExportDirectory->NumberOfFunctions ) {
|
|
return STATUS_PROCEDURE_NOT_FOUND;
|
|
}
|
|
|
|
Addr = (PULONG)(DllBase + ExportDirectory->AddressOfFunctions);
|
|
*AddressOfEntryPoint = (DllBase + Addr[Ordinal]);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USHORT
|
|
NameToOrdinal (
|
|
IN PSZ NameOfEntryPoint,
|
|
IN ULONG DllBase,
|
|
IN ULONG NumberOfNames,
|
|
IN PULONG NameTableBase,
|
|
IN PUSHORT NameOrdinalTableBase
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cloned from ntos\init\init.c NameToOrdinal().
|
|
|
|
Arguments:
|
|
|
|
NameOfEntryPoint - entrypoint name
|
|
DllBase - base address of dll
|
|
NumberOfNames - # names in the dll export table
|
|
NameTableBase - address of the dll name table
|
|
NameOrdinalTableBase - address of the dll ordinal table
|
|
|
|
Return Value:
|
|
|
|
Ordinal of the export. -1 for failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
ULONG SplitIndex;
|
|
LONG CompareResult;
|
|
|
|
SplitIndex = NumberOfNames >> 1;
|
|
|
|
CompareResult = strcmp(NameOfEntryPoint, (PSZ)(DllBase + NameTableBase[SplitIndex]));
|
|
|
|
if ( CompareResult == 0 ) {
|
|
return NameOrdinalTableBase[SplitIndex];
|
|
}
|
|
|
|
if ( NumberOfNames <= 1 ) {
|
|
return (USHORT)-1;
|
|
}
|
|
|
|
if ( CompareResult < 0 ) {
|
|
NumberOfNames = SplitIndex;
|
|
} else {
|
|
NameTableBase = &NameTableBase[SplitIndex+1];
|
|
NameOrdinalTableBase = &NameOrdinalTableBase[SplitIndex+1];
|
|
NumberOfNames = NumberOfNames - SplitIndex - 1;
|
|
}
|
|
|
|
return NameToOrdinal(NameOfEntryPoint,DllBase,NumberOfNames,NameTableBase,NameOrdinalTableBase);
|
|
|
|
}
|
|
|
|
CONST PCHAR Kernel32ExportNames32[] = {
|
|
"BaseProcessStartThunk",
|
|
"BaseThreadStartThunk",
|
|
"CtrlRoutine",
|
|
"ConsoleIMERoutine"
|
|
};
|
|
|
|
CONST PCHAR Kernel32ExportNames64[] = {
|
|
#if defined(_IA64_)
|
|
"BaseProcessStartThunk",
|
|
"BaseThreadStartThunk",
|
|
#else
|
|
"BaseProcessStart",
|
|
"BaseThreadStart",
|
|
#endif
|
|
"CtrlRoutine",
|
|
"ConsoleIMERoutine"
|
|
};
|
|
|
|
UINT NumberKernel32Exports = sizeof(Kernel32ExportNames32) / sizeof(PCHAR);
|
|
ULONG Kernel32Exports64[sizeof(Kernel32ExportNames64) / sizeof(PCHAR)];
|
|
ULONG Kernel32Exports32[sizeof(Kernel32ExportNames32) / sizeof(PCHAR)];
|
|
|
|
CONST WCHAR * Kernel32DllNames[] = {
|
|
L"\\System32\\Kernel32.dll",
|
|
L"\\" WOW64_SYSTEM_DIRECTORY_U L"\\kernel32.dll",
|
|
};
|
|
PULONG Kernel32DllPtrTables[] = {Kernel32Exports64, Kernel32Exports32};
|
|
CONST PCHAR *Kernel32ExportNames[] = {Kernel32ExportNames64, Kernel32ExportNames32};
|
|
|
|
CONST BOOLEAN Kernel32Is64bit[] = {TRUE, FALSE};
|
|
#define NumberKernel32Dlls 2
|
|
|
|
#define BASE_PROCESS_START32 (Kernel32Exports32[0])
|
|
#define BASE_PROCESS_START64 (Kernel32Exports64[0])
|
|
#define BASE_THREAD_START32 (Kernel32Exports32[1])
|
|
#define BASE_THREAD_START64 (Kernel32Exports64[1])
|
|
#define BASE_ATTACH_COMPLETE_THUNK64 (Kernel32Exports64[2])
|
|
|
|
NTSTATUS
|
|
InitializeContextMapper(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds a mapping table that is used by ThunkInitialContext to map from addresses
|
|
in 64bit kernel32 to address in 32bit kernel32.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NT Error code.
|
|
|
|
--*/
|
|
{
|
|
UINT DllNumber;
|
|
UINT ExportNumber;
|
|
PTEB Teb;
|
|
|
|
LOGPRINT((TRACELOG, "Initializing context mapper\n"));
|
|
|
|
Teb = NtCurrentTeb();
|
|
|
|
for(DllNumber = 0; DllNumber < NumberKernel32Dlls; DllNumber++) {
|
|
|
|
WCHAR FullDllNameBuffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING DllName;
|
|
BOOLEAN DllNameAllocated;
|
|
HANDLE File, Section;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PVOID ViewBase;
|
|
SIZE_T ViewSize;
|
|
NTSTATUS st;
|
|
PVOID ArbitraryUserPointer;
|
|
|
|
File = Section = INVALID_HANDLE_VALUE;
|
|
ViewBase = NULL;
|
|
ViewSize = 0;
|
|
DllNameAllocated = FALSE;
|
|
|
|
try {
|
|
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Mapping in %S\n", Kernel32DllNames[DllNumber]));
|
|
|
|
//Build up the file name
|
|
wcscpy(FullDllNameBuffer, USER_SHARED_DATA->NtSystemRoot);
|
|
wcscat(FullDllNameBuffer, Kernel32DllNames[DllNumber]);
|
|
|
|
//
|
|
// Convert the Win32 pathname to an NT pathname
|
|
//
|
|
if (!RtlDosPathNameToNtPathName_U(FullDllNameBuffer,
|
|
&DllName,
|
|
NULL,
|
|
NULL)) {
|
|
// probably out-of-memory
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
DllNameAllocated = TRUE;
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Opening %wZ\n", &DllName));
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DllName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
st = NtOpenFile(&File,
|
|
SYNCHRONIZE | FILE_EXECUTE,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Unable to open file, status %x\n", st));
|
|
return st;
|
|
}
|
|
|
|
//
|
|
// Create the section
|
|
//
|
|
st = NtCreateSection(&Section,
|
|
SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
|
|
NULL,
|
|
NULL,
|
|
PAGE_EXECUTE,
|
|
SEC_IMAGE,
|
|
File);
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Unable to create section, status %x\n", st));
|
|
return st;
|
|
}
|
|
|
|
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
|
|
Teb->NtTib.ArbitraryUserPointer = L"NOT_AN_IMAGE";
|
|
st = NtMapViewOfSection(Section,
|
|
NtCurrentProcess(),
|
|
&ViewBase,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_EXECUTE);
|
|
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
|
|
|
|
if (!NT_SUCCESS(st) || STATUS_IMAGE_NOT_AT_BASE == st) {
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Unable to map view of section, status %x\n", st));
|
|
if (st == STATUS_IMAGE_NOT_AT_BASE) {
|
|
st = STATUS_UNSUCCESSFUL;
|
|
}
|
|
return st;
|
|
}
|
|
|
|
for(ExportNumber = 0; ExportNumber < NumberKernel32Exports; ExportNumber++) {
|
|
|
|
st = LookupEntryPoint((ULONG)(ULONG_PTR)ViewBase,
|
|
Kernel32ExportNames[DllNumber][ExportNumber],
|
|
&(Kernel32DllPtrTables[DllNumber][ExportNumber]),
|
|
Kernel32Is64bit[DllNumber]);
|
|
if (!NT_SUCCESS(st)) {
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Unable to lookup entrypoint %s, status %x\n", Kernel32ExportNames[DllNumber][ExportNumber], st));
|
|
return st;
|
|
}
|
|
|
|
LOGPRINT((TRACELOG, "InitializeContextMapper: Found entrypoint %s at %x\n", Kernel32ExportNames[DllNumber][ExportNumber], Kernel32DllPtrTables[DllNumber][ExportNumber]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
//Deallocate all the allocated resources
|
|
if (ViewBase) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(),
|
|
ViewBase);
|
|
}
|
|
if (INVALID_HANDLE_VALUE != Section) {
|
|
NtClose(Section);
|
|
}
|
|
if (INVALID_HANDLE_VALUE != File) {
|
|
NtClose(File);
|
|
}
|
|
if (DllNameAllocated) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, DllName.Buffer);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
ULONG
|
|
MapContextAddress64TO32(
|
|
IN ULONG Address
|
|
)
|
|
{
|
|
|
|
UINT i;
|
|
|
|
for(i=0; i<NumberKernel32Exports; i++) {
|
|
|
|
if (Address == Kernel32Exports64[i]) {
|
|
return Kernel32Exports32[i];
|
|
}
|
|
|
|
}
|
|
|
|
return Address;
|
|
|
|
}
|
|
|
|
VOID
|
|
ThunkStartupContext64TO32(
|
|
IN OUT PCONTEXT32 Context32,
|
|
IN PCONTEXT Context64
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Munges the InitialPC and arguments registers to compensate for the fact that
|
|
32bit kernel32 has different entry points then 64bit kernel32. The 32bit context must
|
|
have had the context flags set to full and the stack pointer initialized.
|
|
|
|
Arguments:
|
|
|
|
Context32 - Receives a munged 32bit context.
|
|
Context64 - Supplies the initial 64bit context.
|
|
|
|
Return Value:
|
|
|
|
NT Error code.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Thunk the 64-bit CONTEXT down to 32-bit.
|
|
//
|
|
ULONG InitialPC, StartupAddress, Arg1;
|
|
|
|
#if defined(_AMD64_)
|
|
InitialPC = (ULONG)Context64->Rip;
|
|
StartupAddress = (ULONG)Context64->Rcx;
|
|
Arg1 = (ULONG)Context64->Rdx;
|
|
#elif defined(_IA64_)
|
|
InitialPC = (ULONG)Context64->StIIP;
|
|
StartupAddress = (ULONG)Context64->IntS1;
|
|
Arg1 = (ULONG)Context64->IntS2;
|
|
if (Context64->IntS3 != 0) {
|
|
Context32->Esp = (ULONG)Context64->IntS3;
|
|
}
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
|
|
LOGPRINT((TRACELOG, "ThunkStartupContent64TO32: Original InitialPC %x, StartupAddress %x, Arg1 %x\n", InitialPC, StartupAddress, Arg1));
|
|
|
|
if (InitialPC == BASE_PROCESS_START64) {
|
|
LOGPRINT((TRACELOG, "ThunkStartupContext64TO32: Thunking kernel32 process start\n"));
|
|
InitialPC = BASE_PROCESS_START32;
|
|
StartupAddress = MapContextAddress64TO32(StartupAddress);
|
|
}
|
|
else if (InitialPC == BASE_THREAD_START64) {
|
|
LOGPRINT((TRACELOG, "ThunkStartupContext64TO32: Thunking kernel32 thread start\n"));
|
|
InitialPC = BASE_THREAD_START32;
|
|
StartupAddress = MapContextAddress64TO32(StartupAddress);
|
|
}
|
|
else {
|
|
LOGPRINT((TRACELOG, "ThunkStartupContext64TO32: thunking generic context\n"));
|
|
InitialPC = MapContextAddress64TO32(InitialPC);
|
|
}
|
|
|
|
LOGPRINT((TRACELOG, "ThunkStartupContent64TO32: New InitialPC %x, StartupAddress %x, Arg1 %x\n", InitialPC, StartupAddress, Arg1));
|
|
|
|
Context32->Eip = InitialPC;
|
|
Context32->Eax = StartupAddress;
|
|
Context32->Ebx = Arg1;
|
|
}
|
|
|
|
VOID
|
|
SetProcessStartupContext64(
|
|
OUT PCONTEXT Context64,
|
|
IN HANDLE ProcessHandle,
|
|
IN PCONTEXT32 Context32,
|
|
IN ULONGLONG InitialSP64,
|
|
IN ULONGLONG TransferAddress64
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a 64bit context for startup of a 64bit process.
|
|
|
|
Arguments:
|
|
|
|
Context32 - Receives a initial 64bit context.
|
|
ProcessHandle - Handle to the process the context is being created for.
|
|
Context32 - Supplies the initial 32bit context as passed to NtCreateThread.
|
|
InitialSP64 - Supplies the Initial 64bit stack pointer.
|
|
TransferAddress64 - Supplies the address of the app startup code.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Do what BaseInitializeContext(&Context64) would have done
|
|
//
|
|
|
|
#if defined(_AMD64_)
|
|
|
|
RtlZeroMemory(Context64, sizeof(CONTEXT));
|
|
Context64->Rsp = InitialSP64;
|
|
Context64->ContextFlags = CONTEXT_FULL;
|
|
if (Context32->Eip == BASE_PROCESS_START32) {
|
|
|
|
//
|
|
// This is a call from CreateProcess.
|
|
//
|
|
// RIP should be kernel32.dll's process startup routine and rcx should
|
|
// contain the exe's startup address.
|
|
//
|
|
|
|
Context64->Rip = BASE_PROCESS_START64;
|
|
Context64->Rcx = TransferAddress64;
|
|
|
|
} else if (Context32->Eip == BASE_THREAD_START32) {
|
|
|
|
//
|
|
// This is a call from CreateThread.
|
|
//
|
|
// RIP should be kernel32.dll's process startup routine and rcx should
|
|
// contain the exe's startup address.
|
|
//
|
|
|
|
Context64->Rip = BASE_THREAD_START64;
|
|
Context64->Rcx = TransferAddress64;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a call from ntdll.
|
|
//
|
|
// RIP should point to the exe startup address and rcx is the parameter.
|
|
//
|
|
|
|
ULONGLONG Argument;
|
|
NTSTATUS Status;
|
|
|
|
Context64->Rip = TransferAddress64;
|
|
Argument = 0;
|
|
Status = NtReadVirtualMemory(ProcessHandle,
|
|
(PVOID)(Context32->Esp + sizeof(ULONG)),
|
|
&Argument,
|
|
sizeof(ULONG),
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Context64->Rcx = Argument;
|
|
}
|
|
}
|
|
|
|
#elif defined(_IA64_)
|
|
|
|
RtlZeroMemory(Context64, sizeof(CONTEXT));
|
|
|
|
//
|
|
// Everyone is assumed to have this...
|
|
//
|
|
Context64->SegCSD = USER_CODE_DESCRIPTOR;
|
|
Context64->SegSSD = USER_DATA_DESCRIPTOR;
|
|
Context64->Cflag = (ULONGLONG)((CR4_VME << 32) | CR0_PE | CFLG_II);
|
|
Context64->Eflag = 0x00003000ULL;
|
|
|
|
//
|
|
// from ...\win\base\client\ia64\context.c
|
|
//
|
|
// Context64->RsPFS = 0; // Done by the RtlZeroMemory() above
|
|
//
|
|
Context64->StIPSR = USER_PSR_INITIAL;
|
|
Context64->StFPSR = USER_FPSR_INITIAL;
|
|
Context64->RsBSP = Context64->RsBSPSTORE = Context64->IntSp = InitialSP64;
|
|
Context64->IntSp -= STACK_SCRATCH_AREA; // scratch area as per convention
|
|
Context64->IntS1 = TransferAddress64;
|
|
Context64->IntS0 = Context64->StIIP = BASE_PROCESS_START64;
|
|
|
|
//
|
|
// Enable RSE engine
|
|
//
|
|
Context64->RsRSC = (RSC_MODE_EA<<RSC_MODE)
|
|
| (RSC_BE_LITTLE<<RSC_BE)
|
|
| (0x3<<RSC_PL);
|
|
|
|
//
|
|
// Note that we purposely set IntGp = 0ULL, to indicate special protocol
|
|
// (see ps\ia64\psctxia64.c) - specifically, the StIIP address is really
|
|
// a pointer to a plabel instead of the usual (a valid executable
|
|
// address)
|
|
//
|
|
// Context64->IntGp = 0ULL; // Done by the RtlZeroMemory() above
|
|
//
|
|
//
|
|
// set nat bits for every thing except ap, gp, sp, also T0 and T1
|
|
//
|
|
Context64->ApUNAT = 0xFFFFFFFFFFFFEDF1ULL;
|
|
|
|
Context64->ContextFlags = CONTEXT_CONTROL| CONTEXT_INTEGER;
|
|
|
|
if (Context32->Eip == BASE_PROCESS_START32) {
|
|
//
|
|
// This is a call from CreateProcess. The IIP should be
|
|
// kernel32.dll's process startup routine, and IntS0 should contain
|
|
// the exe's startup address
|
|
//
|
|
Context64->IntS0 = Context64->StIIP = BASE_PROCESS_START64;
|
|
Context64->IntS1 = TransferAddress64;
|
|
|
|
} else if (Context32->Eip == BASE_THREAD_START32) {
|
|
//
|
|
// This is a call from CreateThread. The IIP should be
|
|
// kernel32.dll's process startup routine, and IntS0 should contain
|
|
// the exe's startup address
|
|
//
|
|
Context64->IntS0 = Context64->StIIP = BASE_THREAD_START64;
|
|
Context64->IntS1 = TransferAddress64;
|
|
|
|
} else {
|
|
//
|
|
// This is a call from ntdll. The IIP should point to the
|
|
// exe startup address, and IntA0 is the parameter.
|
|
//
|
|
ULONGLONG Argument;
|
|
NTSTATUS Status;
|
|
|
|
Context64->IntS0 = Context64->StIIP = TransferAddress64;
|
|
Argument = 0;
|
|
Status = NtReadVirtualMemory(ProcessHandle,
|
|
(PVOID)(Context32->Esp + sizeof(ULONG)),
|
|
&Argument,
|
|
sizeof(ULONG),
|
|
NULL);
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Note: IA64 RtlInitializeContext does this write and ignores
|
|
// the return value, so we'll do the same.
|
|
//
|
|
NtWriteVirtualMemory(ProcessHandle,
|
|
(PVOID)((ULONG_PTR)Context64->RsBSPSTORE),
|
|
(PVOID)&Argument,
|
|
sizeof(Argument),
|
|
NULL);
|
|
}
|
|
}
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// names are in the NT name space.
|
|
//
|
|
CONST WCHAR *DllsToMapList[] = {L"\\KnownDlls\\kernel32.dll",
|
|
L"\\KnownDlls\\user32.dll"};
|
|
struct {
|
|
PVOID DllBase;
|
|
SIZE_T Length;
|
|
} DllsToMap[sizeof(DllsToMapList) / sizeof(sizeof(DllsToMapList[0]))];
|
|
|
|
NTSTATUS
|
|
Map64BitDlls(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reserve ONLY the dlls address space without committing. This is to prevent 32bit versions
|
|
of these DLLs appearing at the same address and to catch unthunked callbacks.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NT Error code.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
UINT c;
|
|
PTEB Teb;
|
|
PVOID BaseAddress;
|
|
SIZE_T RegionSize;
|
|
HANDLE SectionHandle;
|
|
|
|
Teb=NtCurrentTeb();
|
|
|
|
for(c=0;c<sizeof(DllsToMapList)/sizeof(DllsToMapList[0]);c++) {
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING SectionName;
|
|
SIZE_T ViewSize;
|
|
PVOID ArbitraryUserPointer;
|
|
|
|
LOGPRINT((TRACELOG, "Map64BitDlls: Mapping 64bit section for %S\n", DllsToMapList[c]));
|
|
|
|
RegionSize = 0;
|
|
BaseAddress = NULL;
|
|
SectionHandle = INVALID_HANDLE_VALUE;
|
|
|
|
RtlInitUnicodeString(&SectionName, DllsToMapList[c]);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&SectionName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenSection(&SectionHandle,
|
|
SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE | SECTION_QUERY,
|
|
&ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
LOGPRINT((ERRORLOG, "Map64BitDlls: Unable to open section for %S, error %x\n", DllsToMapList[c], Status));
|
|
SectionHandle = INVALID_HANDLE_VALUE;
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the image base and size
|
|
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
|
|
Teb->NtTib.ArbitraryUserPointer = L"NOT_AN_IMAGE";
|
|
Status = NtMapViewOfSection(SectionHandle,
|
|
NtCurrentProcess(),
|
|
&BaseAddress,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&RegionSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_NOACCESS);
|
|
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
|
|
|
|
if (!NT_SUCCESS(Status) || STATUS_IMAGE_NOT_AT_BASE == Status) {
|
|
LOGPRINT((ERRORLOG, "Map64BitDlls: Unable to map view for %S, error %x\n", DllsToMapList[c], Status));
|
|
BaseAddress = NULL;
|
|
|
|
if (Status == STATUS_IMAGE_NOT_AT_BASE) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
|
|
NtClose(SectionHandle);
|
|
|
|
// just reserve address space
|
|
DllsToMap[c].DllBase = BaseAddress;
|
|
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&DllsToMap[c].DllBase,
|
|
0,
|
|
&RegionSize,
|
|
MEM_RESERVE,
|
|
PAGE_EXECUTE_READWRITE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
LOGPRINT((ERRORLOG, "Map64BitDlls: Couldn't reserve memory Base=%lx, Size=%lx - Status = %lx\n",
|
|
DllsToMap[c].DllBase, RegionSize, Status));
|
|
DllsToMap[c].DllBase = NULL;
|
|
goto CleanupLoop;
|
|
}
|
|
DllsToMap[c].Length = RegionSize;
|
|
|
|
LOGPRINT((TRACELOG, "Map64BitDlls: %S mapped in at %p, size %p\n", DllsToMapList[c], DllsToMap[c].DllBase, DllsToMap[c].Length));
|
|
}
|
|
return STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (NULL != BaseAddress) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(),
|
|
BaseAddress);
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != SectionHandle) {
|
|
NtClose(SectionHandle);
|
|
}
|
|
|
|
CleanupLoop:
|
|
for(c=0;c<sizeof(DllsToMapList)/sizeof(DllsToMapList[0]);c++) {
|
|
if (NULL != DllsToMap[c].DllBase) {
|
|
RegionSize = 0;
|
|
NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&DllsToMap[c].DllBase,
|
|
&RegionSize,
|
|
MEM_RELEASE);
|
|
DllsToMap[c].DllBase = NULL;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
Wow64pBreakPoint(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is remote called to after a successful debug attach. Its
|
|
purpose is to issue a breakpoint and then simulate 64-bit
|
|
kernel32!ExitThread.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE DebugPort;
|
|
NTSTATUS Status;
|
|
|
|
DebugPort = (HANDLE)NULL;
|
|
|
|
Status = NtQueryInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessDebugPort,
|
|
(PVOID)&DebugPort,
|
|
sizeof(DebugPort),
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(Status) && DebugPort)
|
|
{
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
Wow64ExitThread(NtCurrentThread(), 0);
|
|
}
|
|
|
|
VOID
|
|
Run64IfContextIs64(
|
|
IN PCONTEXT Context,
|
|
IN BOOLEAN IsFirstThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called early in Wow64LdrpInitialize. This routine checks the initial
|
|
64-bit CONTEXT record, and if it looks like the new thread should be run
|
|
as 64-bit (ie. without emulation), then this routine runs the 64-bit
|
|
CONTEXT and terminates the thread/process. If the initial CONTEXT
|
|
appears to be one that should be run as 32-bit, then it returns back to
|
|
its caller, and the caller must convert the CONTEXT to 32-bit and
|
|
simulate it.
|
|
|
|
Arguments:
|
|
|
|
Context - 64bit initial context for this thread.
|
|
IsFirstThread - TRUE for the initial thread in the process, FALSE
|
|
for all other threads.
|
|
|
|
Return Value:
|
|
|
|
None - Runs context if context is 64bit. Otherwise, return.
|
|
|
|
--*/
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLDR_DATA_TABLE_ENTRY32 Entry32;
|
|
PPEB_LDR_DATA32 Data32;
|
|
ULONG64 InitialPC;
|
|
NTSTATUS Status;
|
|
LIST_ENTRY *NtDllEntry;
|
|
int i;
|
|
|
|
#if defined(_AMD64_)
|
|
InitialPC = Context->Rbx;
|
|
#elif defined(_IA64_)
|
|
InitialPC = Context->IntS1;
|
|
#else
|
|
#error "No Target Architeture"
|
|
#endif
|
|
|
|
// Try to match the InitialPC with 64-bit ntdll.dll. 64-bit ntdll.dll
|
|
// is the second entry in the InLoadOrderModuleList.
|
|
NtDllEntry = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink->Flink;
|
|
Entry = CONTAINING_RECORD(NtDllEntry,
|
|
LDR_DATA_TABLE_ENTRY,
|
|
InLoadOrderLinks);
|
|
// Just put this statement in the code so this structure is loaded
|
|
// in the .pdb file for debugging.
|
|
Entry32 = CONTAINING_RECORD(NtDllEntry,
|
|
LDR_DATA_TABLE_ENTRY32,
|
|
InLoadOrderLinks);
|
|
Data32 = (PPEB_LDR_DATA32) NtCurrentPeb()->Ldr;
|
|
|
|
if (InitialPC >= (ULONG64)Entry->DllBase &&
|
|
InitialPC < (ULONG64)((PCHAR)Entry->DllBase + Entry->SizeOfImage)) {
|
|
|
|
// The address is within 64-bit ntdll.dll. Run the 64-bit function directly
|
|
|
|
#if defined(_AMD64_)
|
|
// ****** fixfix ******
|
|
#elif defined(_IA64_)
|
|
Context->IntGp = ((PPLABEL_DESCRIPTOR)Context->IntS0)->GlobalPointer;
|
|
Context->StIIP = ((PPLABEL_DESCRIPTOR)Context->IntS0)->EntryPoint;
|
|
if (Context->StIPSR & IPSR_RI_MASK) {
|
|
LOGPRINT((ERRORLOG, "Warning! IPSR has nonzero slot #. Slot# is %d\n",(Context->StIPSR >> PSR_RI) & 3));
|
|
Context->StIPSR &= ~IPSR_RI_MASK;
|
|
}
|
|
#else
|
|
#error "No Target Architeture"
|
|
#endif
|
|
LOGPRINT((TRACELOG, "InitialPC %p is within 64-bit ntdll.dll. Running 64-bit context unchanged.\n", InitialPC));
|
|
goto runcontext64;
|
|
|
|
}
|
|
|
|
// Check if the address is within one of the address-space holes reserved
|
|
// for 64-bit kernel32 and user32.
|
|
for (i=0; i<sizeof(DllsToMapList)/sizeof(DllsToMapList[0]); ++i) {
|
|
if (InitialPC >= (ULONG64)DllsToMap[i].DllBase &&
|
|
InitialPC < (ULONG64)DllsToMap[i].DllBase+DllsToMap[i].Length) {
|
|
// The InitialPC is inside one of the reserved holes
|
|
if (MapContextAddress64TO32((ULONG)InitialPC) == InitialPC) {
|
|
// The InitialPC is not one that we special-case by converting
|
|
// into a call to the 32-bit DLL. It may be kernel32!DebugBreak
|
|
// or some other routine.
|
|
LOGPRINT((TRACELOG, "InitialPC %p found in the space reserved for 64-bit %wZ.", InitialPC, DllsToMapList[i]));
|
|
#if defined(_AMD64_)
|
|
// ****** fixfix ******
|
|
#elif defined(_IA64_)
|
|
Context->IntGp = ((PPLABEL_DESCRIPTOR)Wow64pBreakPoint)->GlobalPointer;
|
|
Context->StIIP = ((PPLABEL_DESCRIPTOR)Wow64pBreakPoint)->EntryPoint;
|
|
if (Context->StIPSR & IPSR_RI_MASK) {
|
|
LOGPRINT((ERRORLOG, "Warning! IPSR has nonzero slot #. Slot# is %d\n",(Context->StIPSR >> PSR_RI) & 3));
|
|
Context->StIPSR &= ~IPSR_RI_MASK;
|
|
}
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
goto runcontext64;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The initial context should be run as 32-bit
|
|
return;
|
|
|
|
runcontext64:
|
|
Status = NtContinue(Context, TRUE);
|
|
WOWASSERT(!NT_SUCCESS(Status));
|
|
if (IsFirstThread) {
|
|
NtTerminateProcess(NtCurrentProcess(), Status);
|
|
} else {
|
|
NtTerminateThread(NtCurrentThread(), Status);
|
|
}
|
|
}
|