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.
4885 lines
155 KiB
4885 lines
155 KiB
/*++
|
|
|
|
INTEL CORPORATION PROPRIETARY INFORMATION
|
|
|
|
This software is supplied under the terms of a license
|
|
agreement or nondisclosure agreement with Intel Corporation
|
|
and may not be copied or disclosed except in accordance with
|
|
the terms of that agreement.
|
|
|
|
Copyright (c) 1991-2002 INTEL CORPORATION
|
|
|
|
Module Name:
|
|
|
|
btlib.c
|
|
|
|
Abstract:
|
|
|
|
The OS dependent part of IA-32 Execution Layer for Windows.
|
|
It is a Windows operating-system-specific component of the dynamic binary translator.
|
|
Its responsibility is to locate and load OS-independent component
|
|
(IA32Exec.bin), to forward Wow64 calls to the OS-independent part
|
|
and to supply OS-dependent services to it when necessary.
|
|
Part of the services in this module are not used for windows,
|
|
and part of them are needed for debugging and/or performance tuning only.
|
|
--*/
|
|
|
|
#define _WOW64BTAPI_
|
|
|
|
#ifndef NODEBUG
|
|
#define OVERRIDE_TIA 1
|
|
#endif
|
|
|
|
#include "btlib.h"
|
|
|
|
#ifndef IA32EX_G_NAME
|
|
#define IA32EX_G_NAME L"IA32Exec"
|
|
#endif
|
|
|
|
#ifndef IA32EX_G_SUFFIX
|
|
#define IA32EX_G_SUFFIX L"bin"
|
|
#endif
|
|
|
|
#define WOW64BT_IMPL __declspec(dllexport)
|
|
|
|
extern VOID Wow64LogPrint(UCHAR LogLevel, char *format, ...);
|
|
#define LF_TRACE 2
|
|
#define LF_ERROR 1
|
|
|
|
/*
|
|
* File location enumerator
|
|
*/
|
|
#define F_NOT_FOUND 0
|
|
#define F_CURRENT_DIR 1
|
|
#define F_BTLIB 2
|
|
#define F_HKLM 3
|
|
#define F_HKCU 4
|
|
|
|
|
|
/*
|
|
* Initial memory allocation addresses for code and data
|
|
*/
|
|
|
|
#define INITIAL_CODE_ADDRESS ((void *)0x44000000)
|
|
#define INITIAL_DATA_ADDRESS ((void *)0x40000000)
|
|
|
|
|
|
ASSERTNAME;
|
|
|
|
// Persistent variables
|
|
|
|
U32 BtlpInfoOffset; // Offset of the wowIA32X.dll-specific info in Wow64Cpu TLS
|
|
U32 BtlpGenericIA32ContextOffset; // Offset of the IA32 context in IA32Exec.bin's TLS
|
|
|
|
PLABEL_PTR_TYPE BtlpPlaceHolderTable[NO_OF_APIS];
|
|
WCHAR ImageName[128], LogDirName[128];
|
|
|
|
// Interface for debug printing
|
|
|
|
HANDLE BtlpWow64LogFile = INVALID_HANDLE_VALUE;
|
|
DWORD BtlpLogOffset = 0;
|
|
#ifndef NODEBUG
|
|
WCHAR BtlpLogFileFullPath[1024];
|
|
BOOL BtlpLogFilePerThread = FALSE;
|
|
#endif
|
|
|
|
// Temporary workaround for IA32 debugging support.
|
|
//To be removed after fixing FlushIC(ProcessHandle).
|
|
BOOL BeingDebugged; //copy of the PEB->BeingDebugged; can be overriden by the
|
|
//debug_btrans switch
|
|
|
|
//Critical section interface
|
|
#define BtlpInitializeCriticalSection(pCS) RtlInitializeCriticalSection (pCS)
|
|
#define BtlpDeleteCriticalSection(pCS) RtlDeleteCriticalSection (pCS)
|
|
__inline void BtlpEnterCriticalSection(PRTL_CRITICAL_SECTION pCS)
|
|
{
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
RtlEnterCriticalSection(pCS);
|
|
}
|
|
__inline void BtlpLeaveCriticalSection(PRTL_CRITICAL_SECTION pCS)
|
|
{
|
|
RtlLeaveCriticalSection(pCS);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
}
|
|
|
|
//Report failure in NT service
|
|
// msg - error message text
|
|
// status - error status
|
|
#define BTLP_REPORT_NT_FAILURE(msg, status) \
|
|
DBCODE((status != STATUS_SUCCESS), BtlpPrintf("\n%s : NT FAILURE STATUS = 0x%X\n" , msg, status))
|
|
|
|
VOID BtlDebugPrint (
|
|
U8 * buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print of the buffer.
|
|
The text is printed into the debugging log file,
|
|
or through Wow64 debugging facility, if the file is not available
|
|
|
|
Arguments:
|
|
|
|
buffer - IN Text to be printed
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
extern U64 BtAtomicInc(U64 * pCounter);
|
|
extern U64 BtAtomicDec(U64 * pCounter);
|
|
|
|
HANDLE hTarget = INVALID_HANDLE_VALUE;
|
|
static U64 InLogging = 0; //counter of concurrent entrances to the function
|
|
|
|
if (BtAtomicInc(&InLogging)) {
|
|
//Some thread is printing at this moment. Exit if BTLIB_BLOCKED_LOG_DISABLED
|
|
if (BTL_THREAD_INITIALIZED() && BTLIB_BLOCKED_LOG_DISABLED()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifndef NODEBUG
|
|
if ( BtlpLogFilePerThread ) {
|
|
if (BTL_THREAD_INITIALIZED()) {
|
|
hTarget = BTLIB_LOG_FILE();
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
hTarget = BtlpWow64LogFile;
|
|
}
|
|
|
|
if ( hTarget != INVALID_HANDLE_VALUE ) {
|
|
NTSTATUS ret;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
size_t size;
|
|
LARGE_INTEGER offset;
|
|
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
Wow64LogPrint(LF_TRACE, "%s", buffer);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
size = strlen(buffer);
|
|
offset.HighPart = 0;
|
|
|
|
#ifndef NODEBUG
|
|
if ( BtlpLogFilePerThread ) {
|
|
offset.LowPart = BTLIB_INFO_PTR()->LogOffset;
|
|
BTLIB_INFO_PTR()->LogOffset += size;
|
|
} else
|
|
#endif
|
|
{
|
|
//Following two lines should be replaced with atomic operation:
|
|
//offset.LowPart = InterlockedExchangeAdd(&BtlpLogOffset, size);
|
|
offset.LowPart = BtlpLogOffset;
|
|
BtlpLogOffset += size;
|
|
}
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
ret = NtWriteFile(hTarget, NULL, NULL, NULL, &IoStatusBlock,
|
|
(void *)buffer, (ULONG)size, &offset, NULL);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
} else {
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
Wow64LogPrint(LF_ERROR, "%s", buffer);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
}
|
|
BtAtomicDec(&InLogging);
|
|
}
|
|
|
|
int BtlpPrintf (
|
|
IN char * Format,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper function for format printing.
|
|
|
|
Arguments:
|
|
|
|
Format - IN Format string to be printed
|
|
... - IN parameter(s) according to the format string
|
|
|
|
Return Value:
|
|
|
|
Just like in vsprintf.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define MAX_DEBUG_PRINT_BUF_SZ 4096
|
|
extern U64 DisableFPInterrupt();
|
|
extern void RestoreFPInterrupt(U64 prev_fpsr);
|
|
U64 prev_fpsr;
|
|
va_list ParmList;
|
|
int res;
|
|
char PrintBuffer[MAX_DEBUG_PRINT_BUF_SZ];
|
|
|
|
prev_fpsr = DisableFPInterrupt();
|
|
va_start(ParmList, Format);
|
|
res = vsprintf(PrintBuffer, Format, ParmList);
|
|
BtlDebugPrint (PrintBuffer);
|
|
RestoreFPInterrupt(prev_fpsr);
|
|
return res;
|
|
}
|
|
|
|
VOID __cdecl _assert (
|
|
VOID *expr,
|
|
VOID *file_name,
|
|
unsigned line_no
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper assert function (in order to print assert message our way)
|
|
|
|
Arguments:
|
|
|
|
expr - IN failing expression string
|
|
file_name - IN name of the source file
|
|
line_no - IN number of the source line
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BtlpPrintf ("wowIA32X.dll: Assertion failed %s/%d: %s\n", (const char *)file_name, line_no, (char *)expr);
|
|
BTLIB_ABORT ();
|
|
}
|
|
|
|
VOID BtlAbort(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Abort function (in order to avoid using run-time library in WINNT)
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
BtlpPrintf ("Execution aborted, TEB=%p\n", BT_CURRENT_TEB());
|
|
// Cause failure
|
|
((VOID (*)()) 0) ();
|
|
}
|
|
|
|
VOID BtlInitializeTables(
|
|
IN API_TABLE_TYPE * BTGenericTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize placeholder table with plabels of IA32Exec.bin functions
|
|
|
|
Arguments:
|
|
|
|
BTGenericTable - IN pointer to IA32Exec.bin API table.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
unsigned int i;
|
|
|
|
// initialize wowIA32X.dll placeholder table
|
|
for(i=0; i < BTGenericTable->NoOfAPIs; i++) {
|
|
BtlPlaceHolderTable[i] = BTGenericTable->APITable[i].PLabelPtr;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// VTUNE support
|
|
|
|
HANDLE BtlpVtuneTIADmpFileHandle = INVALID_HANDLE_VALUE;
|
|
LARGE_INTEGER BtlpVtuneOffset = { 0, 0 };
|
|
|
|
static VOID BtlpVtuneOpenTIADmpFile (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open file fot VTUNE analysis
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
UNICODE_STRING tiaFileName;
|
|
LARGE_INTEGER AllocSz = { 0, 0 };
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS ret;
|
|
WCHAR CurDirBuf[512];
|
|
WCHAR CurrentDir[1024];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
|
|
//swprintf(CurDirBuf, L"\\DosDevices\\%s\\tia.dmp", CurrentDir);
|
|
|
|
|
|
if (0==LogDirName[0] && 0==LogDirName[1]) {
|
|
RtlGetCurrentDirectory_U(512, CurrentDir);
|
|
swprintf(CurDirBuf, L"\\DosDevices\\%s\\%s.tia.dmp", CurrentDir, ImageName);
|
|
}
|
|
else {
|
|
swprintf(CurDirBuf, L"\\DosDevices\\%s\\%s.tia.dmp", LogDirName, ImageName);
|
|
}
|
|
RtlInitUnicodeString(&tiaFileName, CurDirBuf);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, &tiaFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile (&BtlpVtuneTIADmpFileHandle,
|
|
FILE_GENERIC_WRITE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
&AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_SUPERSEDE,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpPrintf("Save: NtCreateFile() failed: status = 0x%X\n", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static VOID BtlpVtuneWriteU64 (
|
|
IN U64 value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write 64 bit unsigned value into VTUNE file
|
|
|
|
Arguments:
|
|
|
|
value - IN 64bit unsigned value to be send to VTUNE file.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
char space = ' ';
|
|
NTSTATUS ret;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
if (BtlpVtuneTIADmpFileHandle == INVALID_HANDLE_VALUE) {
|
|
BtlpVtuneOpenTIADmpFile ();
|
|
}
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
ret = NtWriteFile ( BtlpVtuneTIADmpFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(VOID *) &value,
|
|
sizeof(U64),
|
|
&BtlpVtuneOffset,
|
|
NULL);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpPrintf("-1. NtWriteFile() failed: status = 0x%X\n", ret);
|
|
}
|
|
BtlpVtuneOffset.LowPart += sizeof(U64);
|
|
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
ret = NtWriteFile ( BtlpVtuneTIADmpFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(VOID *) &space,
|
|
sizeof(space),
|
|
&BtlpVtuneOffset,
|
|
NULL);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpPrintf("-1. NtWriteFile() failed: status = 0x%X\n", ret);
|
|
}
|
|
BtlpVtuneOffset.LowPart += sizeof(char);
|
|
}
|
|
|
|
static VOID BtlpVtuneWriteU32 (
|
|
IN U32 value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write 64 bit unsigned value into VTUNE file
|
|
|
|
Arguments:
|
|
|
|
value - IN 32bit unsigned value to be send to VTUNE file.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
U64 valueToWrite;
|
|
char space = ' ';
|
|
NTSTATUS ret;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
if (BtlpVtuneTIADmpFileHandle == INVALID_HANDLE_VALUE) {
|
|
BtlpVtuneOpenTIADmpFile ();
|
|
}
|
|
valueToWrite = value;
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
ret = NtWriteFile ( BtlpVtuneTIADmpFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(VOID *) &valueToWrite,
|
|
sizeof(U64),
|
|
&BtlpVtuneOffset,
|
|
NULL);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpPrintf("-1. NtWriteFile() failed: status = 0x%X\n", ret);
|
|
}
|
|
BtlpVtuneOffset.LowPart += sizeof(U64);
|
|
|
|
//Disable suspension during blocked (synchronized) file access
|
|
BTL_THREAD_INITIALIZED() && BTLIB_DISABLE_SUSPENSION();
|
|
ret = NtWriteFile ( BtlpVtuneTIADmpFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(VOID *) &space,
|
|
sizeof(space),
|
|
&BtlpVtuneOffset,
|
|
NULL);
|
|
BTL_THREAD_INITIALIZED() && BTLIB_ENABLE_SUSPENSION();
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpPrintf("-1. NtWriteFile() failed: status = 0x%X\n", ret);
|
|
}
|
|
BtlpVtuneOffset.LowPart += sizeof(char);
|
|
}
|
|
|
|
VOID BtlVtuneCodeToTIADmpFile (
|
|
IN U64 * emCode,
|
|
IN U64 emSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report translated code block to VTUNE file
|
|
|
|
Arguments:
|
|
|
|
emCode - IN code start pointer
|
|
emSize - IN code size in bytes
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
#if 0
|
|
U64 bundle;
|
|
|
|
assert ((emSize % (2*sizeof (U64))) == 0);
|
|
emSize /= (2*sizeof (U64));
|
|
|
|
BtlpVtuneWriteU64 (emSize);
|
|
|
|
for (; emSize; --emSize) {
|
|
bundle = *emCode++;
|
|
BtlpVtuneWriteU64 (bundle);
|
|
bundle = *emCode++;
|
|
BtlpVtuneWriteU64 (bundle);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID BtlVtuneEnteringDynamicCode(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify VTUNE about entering dynamically generated code (no action for NT)
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
VOID BtlVtuneExitingDynamicCode(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify VTUNE about leaving dynamically generated code (no action for NT)
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
VOID BtlVtuneCodeDeleted(
|
|
IN U64 blockStart
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify VTUNE about removal of dynamically generated code (no action for NT)
|
|
|
|
Arguments:
|
|
|
|
blockStart - IN start of the block.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
VOID BtlVtuneCodeCreated(
|
|
IN VTUNE_BLOCK_TYPE *block
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify VTUNE about generation of a code block
|
|
|
|
Arguments:
|
|
|
|
block - IN VTUNE block descriptor.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
// keep this order of fields, it's expected on the reader side to be in that order
|
|
BtlpVtuneWriteU32(VTUNE_CALL_ID_CREATED);
|
|
BtlpVtuneWriteU64(block->name);
|
|
BtlpVtuneWriteU32(block->type);
|
|
BtlpVtuneWriteU64(block->start);
|
|
BtlpVtuneWriteU64(block->size);
|
|
BtlpVtuneWriteU32(block->IA32start);
|
|
BtlpVtuneWriteU64(block->traversal);
|
|
BtlpVtuneWriteU64(block->reserved);
|
|
}
|
|
|
|
// SSC Client support - absent in NT
|
|
|
|
U64 BtlSscPerfGetCounter64(
|
|
IN U32 Handle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get SSE client performance counter (Unavailable in NT)
|
|
|
|
Arguments:
|
|
|
|
Handle - IN SSE client handle
|
|
|
|
Return Value:
|
|
|
|
Counter value.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
U32 BtlSscPerfSetCounter64(
|
|
IN U32 Handle,
|
|
IN U64 Value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set SSE client performance counter (Unavailable in NT)
|
|
|
|
Arguments:
|
|
|
|
Handle - IN SSE client handle
|
|
Value - IN new counter value
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
U32 BtlSscPerfSendEvent(
|
|
IN U32 Handle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send event to SSE client (Unavailable in NT)
|
|
|
|
Arguments:
|
|
|
|
Handle - IN SSE client handle
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
U64 BtlSscPerfEventHandle(
|
|
IN U64 EventName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive event handle from SSE client (Unavailable in NT)
|
|
|
|
Arguments:
|
|
|
|
EventName - IN handle identification
|
|
|
|
Return Value:
|
|
|
|
SSE client handle.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
U64 BtlSscPerfCounterHandle(
|
|
IN U64 DataItemName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive counter handle from SSE client (Unavailable in NT)
|
|
|
|
Arguments:
|
|
|
|
DataItemName - IN handle identification
|
|
|
|
Return Value:
|
|
|
|
SSE client handle.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// wowIA32X.dll/IA32Exec.bin support
|
|
|
|
static NTSTATUS BtlpBt2NtExceptCode (
|
|
IN BT_EXCEPTION_CODE BtExceptCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert given BT exception code to NT-specific exception code.
|
|
|
|
Arguments:
|
|
|
|
BtExceptCode - BT exception code
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS representing converted BT exception code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ret;
|
|
switch (BtExceptCode) {
|
|
case BT_EXCEPT_ILLEGAL_INSTRUCTION:
|
|
ret = EXCEPTION_ILLEGAL_INSTRUCTION;
|
|
break;
|
|
case BT_EXCEPT_ACCESS_VIOLATION:
|
|
ret = EXCEPTION_ACCESS_VIOLATION;
|
|
break;
|
|
case BT_EXCEPT_DATATYPE_MISALIGNMENT:
|
|
ret = EXCEPTION_DATATYPE_MISALIGNMENT;
|
|
break;
|
|
case BT_EXCEPT_ARRAY_BOUNDS_EXCEEDED:
|
|
ret = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
|
break;
|
|
case BT_EXCEPT_FLT_DENORMAL_OPERAND:
|
|
ret = EXCEPTION_FLT_DENORMAL_OPERAND;
|
|
break;
|
|
case BT_EXCEPT_FLT_DIVIDE_BY_ZERO:
|
|
ret = EXCEPTION_FLT_DIVIDE_BY_ZERO;
|
|
break;
|
|
case BT_EXCEPT_FLT_INEXACT_RESULT:
|
|
ret = EXCEPTION_FLT_INEXACT_RESULT;
|
|
break;
|
|
case BT_EXCEPT_FLT_INVALID_OPERATION:
|
|
ret = EXCEPTION_FLT_INVALID_OPERATION;
|
|
break;
|
|
case BT_EXCEPT_FLT_OVERFLOW:
|
|
ret = EXCEPTION_FLT_OVERFLOW;
|
|
break;
|
|
case BT_EXCEPT_FLT_UNDERFLOW:
|
|
ret = EXCEPTION_FLT_UNDERFLOW;
|
|
break;
|
|
case BT_EXCEPT_FLT_STACK_CHECK:
|
|
ret = EXCEPTION_FLT_STACK_CHECK;
|
|
break;
|
|
case BT_EXCEPT_INT_DIVIDE_BY_ZERO:
|
|
ret = EXCEPTION_INT_DIVIDE_BY_ZERO;
|
|
break;
|
|
case BT_EXCEPT_INT_OVERFLOW:
|
|
ret = EXCEPTION_INT_OVERFLOW;
|
|
break;
|
|
case BT_EXCEPT_PRIV_INSTRUCTION:
|
|
ret = EXCEPTION_PRIV_INSTRUCTION;
|
|
break;
|
|
case BT_EXCEPT_FLOAT_MULTIPLE_FAULTS:
|
|
ret = STATUS_FLOAT_MULTIPLE_FAULTS;
|
|
break;
|
|
case BT_EXCEPT_FLOAT_MULTIPLE_TRAPS:
|
|
ret = STATUS_FLOAT_MULTIPLE_TRAPS;
|
|
break;
|
|
case BT_EXCEPT_STACK_OVERFLOW:
|
|
ret = STATUS_STACK_OVERFLOW;
|
|
break;
|
|
case BT_EXCEPT_GUARD_PAGE:
|
|
ret = STATUS_GUARD_PAGE_VIOLATION;
|
|
break;
|
|
case BT_EXCEPT_BREAKPOINT:
|
|
ret = STATUS_WX86_BREAKPOINT;
|
|
break;
|
|
case BT_EXCEPT_SINGLE_STEP:
|
|
ret = STATUS_WX86_SINGLE_STEP;
|
|
break;
|
|
default:
|
|
DBCODE(TRUE, BtlpPrintf ("\nConverting unknown BT exception 0x%X to EXCEPTION_ACCESS_VIOLATION", BtExceptCode));
|
|
ret = EXCEPTION_ACCESS_VIOLATION;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static BT_EXCEPTION_CODE BtlpNt2BtExceptCode (
|
|
IN NTSTATUS NtExceptCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert given NT-specific exception code to BT-generic exception code.
|
|
|
|
Arguments:
|
|
|
|
NtExceptCode - NT exception code
|
|
|
|
Return Value:
|
|
|
|
BT_EXCEPTION_CODE representing converted NT exception code.
|
|
|
|
--*/
|
|
{
|
|
BT_EXCEPTION_CODE ret;
|
|
switch (NtExceptCode) {
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
ret = BT_EXCEPT_ILLEGAL_INSTRUCTION;
|
|
break;
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
ret = BT_EXCEPT_ACCESS_VIOLATION;
|
|
break;
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
ret = BT_EXCEPT_DATATYPE_MISALIGNMENT;
|
|
break;
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
ret = BT_EXCEPT_ARRAY_BOUNDS_EXCEEDED;
|
|
break;
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
ret = BT_EXCEPT_FLT_DENORMAL_OPERAND;
|
|
break;
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
ret = BT_EXCEPT_FLT_DIVIDE_BY_ZERO;
|
|
break;
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
ret = BT_EXCEPT_FLT_INEXACT_RESULT;
|
|
break;
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
ret = BT_EXCEPT_FLT_INVALID_OPERATION;
|
|
break;
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
ret = BT_EXCEPT_FLT_OVERFLOW;
|
|
break;
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
ret = BT_EXCEPT_FLT_UNDERFLOW;
|
|
break;
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
ret = BT_EXCEPT_FLT_STACK_CHECK;
|
|
break;
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
ret = BT_EXCEPT_INT_DIVIDE_BY_ZERO;
|
|
break;
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
ret = BT_EXCEPT_INT_OVERFLOW;
|
|
break;
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
ret = BT_EXCEPT_PRIV_INSTRUCTION;
|
|
break;
|
|
case STATUS_FLOAT_MULTIPLE_FAULTS:
|
|
ret = BT_EXCEPT_FLOAT_MULTIPLE_FAULTS;
|
|
break;
|
|
case STATUS_FLOAT_MULTIPLE_TRAPS:
|
|
ret = BT_EXCEPT_FLOAT_MULTIPLE_TRAPS;
|
|
break;
|
|
case STATUS_STACK_OVERFLOW:
|
|
ret = BT_EXCEPT_STACK_OVERFLOW;
|
|
break;
|
|
case STATUS_GUARD_PAGE_VIOLATION:
|
|
ret = BT_EXCEPT_GUARD_PAGE;
|
|
break;
|
|
case EXCEPTION_BREAKPOINT:
|
|
case STATUS_WX86_BREAKPOINT:
|
|
ret = BT_EXCEPT_BREAKPOINT;
|
|
break;
|
|
case EXCEPTION_SINGLE_STEP:
|
|
case STATUS_WX86_SINGLE_STEP:
|
|
ret = BT_EXCEPT_SINGLE_STEP;
|
|
break;
|
|
default:
|
|
DBCODE(TRUE, BtlpPrintf ("\nConverting unknown NT exception 0x%X to BT_EXCEPT_UNKNOWN", NtExceptCode));
|
|
ret = BT_EXCEPT_UNKNOWN;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void BtlpNt2BtExceptRecord (
|
|
IN const EXCEPTION_RECORD * NtExceptRecordP,
|
|
OUT BT_EXCEPTION_RECORD * BtExceptRecordP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert given NT-specific exception record to BT-generic exception record.
|
|
The NT exception record should represent a real 64-bit exception (fault or trap)
|
|
Arguments:
|
|
|
|
NtExceptRecordP - Pointer to NT exception record to be converted
|
|
BtExceptRecordP - Pointer to BT exception record to be constructed
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BtExceptRecordP->ExceptionCode = BtlpNt2BtExceptCode(NtExceptRecordP->ExceptionCode);
|
|
if (NtExceptRecordP->NumberParameters >= 5) {
|
|
BtExceptRecordP->Ia64IIPA = NtExceptRecordP->ExceptionInformation[3];
|
|
BtExceptRecordP->Ia64ISR = NtExceptRecordP->ExceptionInformation[4];
|
|
}
|
|
else {
|
|
BtExceptRecordP->Ia64IIPA = 0;
|
|
BtExceptRecordP->Ia64ISR = UNKNOWN_ISR_VALUE;
|
|
}
|
|
}
|
|
|
|
static NTSTATUS BtlpBt2NtStatusCode (
|
|
IN BT_STATUS_CODE BtStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert given BT staus code to NT-specific status code.
|
|
|
|
Arguments:
|
|
|
|
BtStatus - BT status code
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS representing converted BT status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ret;
|
|
switch (BtStatus) {
|
|
case BT_STATUS_SUCCESS:
|
|
ret = STATUS_SUCCESS;
|
|
break;
|
|
case BT_STATUS_UNSUCCESSFUL:
|
|
ret = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
case BT_STATUS_NO_MEMORY:
|
|
ret = STATUS_NO_MEMORY;
|
|
break;
|
|
case BT_STATUS_ACCESS_VIOLATION:
|
|
ret = STATUS_ACCESS_VIOLATION;
|
|
break;
|
|
default:
|
|
DBCODE(TRUE, BtlpPrintf ("\nConverting unknown status 0x%X to STATUS_UNSUCCESSFUL", BtStatus));
|
|
ret = STATUS_UNSUCCESSFUL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static BT_FLUSH_REASON BtlpWow2BtFlushReason (
|
|
IN WOW64_FLUSH_REASON Wow64FlushReason
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert given WOW64_FLUSH_REASON code to BT-generic code.
|
|
|
|
Arguments:
|
|
|
|
Wow64FlushReason - WOW64_FLUSH_REASON code
|
|
|
|
Return Value:
|
|
|
|
BT_FLUSH_REASON code representing converted WOW64_FLUSH_REASON code.
|
|
|
|
--*/
|
|
{
|
|
BT_FLUSH_REASON ret;
|
|
switch (Wow64FlushReason) {
|
|
case WOW64_FLUSH_FORCE:
|
|
ret = BT_FLUSH_FORCE;
|
|
break;
|
|
case WOW64_FLUSH_FREE:
|
|
ret = BT_FLUSH_FREE;
|
|
break;
|
|
case WOW64_FLUSH_ALLOC:
|
|
ret = BT_FLUSH_ALLOC;
|
|
break;
|
|
case WOW64_FLUSH_PROTECT:
|
|
ret = BT_FLUSH_PROTECT;
|
|
break;
|
|
default:
|
|
//BtlpPrintf ("\nConverting unknown WOW64_FLUSH_REASON %d to BT_FLUSH_PROTECT", Wow64FlushReason);
|
|
ret = BT_FLUSH_PROTECT;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static SIZE_T BtlpGetMemAllocSize(
|
|
IN PVOID AllocationBase,
|
|
OUT BOOL * pIsCommited
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate size of a region allocated by the NtAllocateVirtualMemory function.
|
|
|
|
Arguments:
|
|
|
|
AllocationBase - Allocation base address
|
|
pIsCommited - Pointer to returned boolean flag that indicates is there exist
|
|
a commited page in the allocated region
|
|
Return Value:
|
|
|
|
Size of the allocated region starting from the given base address.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
SIZE_T dwRetSize;
|
|
PVOID BaseAddress = AllocationBase;
|
|
*pIsCommited = FALSE;
|
|
//Iterate through all regions with the same allocation base address
|
|
for (;;) {
|
|
status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
BaseAddress,
|
|
MemoryBasicInformation,
|
|
&memInfo,
|
|
sizeof (memInfo),
|
|
&dwRetSize);
|
|
|
|
if ((status != STATUS_SUCCESS) ||
|
|
(memInfo.State == MEM_FREE) ||
|
|
(AllocationBase != memInfo.AllocationBase)) {
|
|
break;
|
|
}
|
|
assert(memInfo.RegionSize != 0);
|
|
BaseAddress = (PVOID)((UINT_PTR)(memInfo.BaseAddress) + memInfo.RegionSize);
|
|
*pIsCommited |= (memInfo.State == MEM_COMMIT);
|
|
}
|
|
return ((UINT_PTR)BaseAddress - (UINT_PTR)AllocationBase);
|
|
}
|
|
|
|
// Registry access section
|
|
|
|
static BOOL BtlpRetrieveHKCUValue (
|
|
IN PWCHAR RegistryEntryName,
|
|
OUT PWCHAR RegistryValueBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve a registry value from HKCU
|
|
|
|
Arguments:
|
|
|
|
RegistryEntryName - IN Registry Entry Name
|
|
RegistryValueBuf - OUT Buffer for the result (pointer to WCHAR string)
|
|
|
|
Return Value:
|
|
|
|
Success/failure (TRUE/FALSE)
|
|
|
|
--*/
|
|
{
|
|
|
|
WCHAR wBuf[256], cmpbuf[128];
|
|
UNICODE_STRING us_Buffer, us_EnvVarUserName, us_UserName, us_HiveList;
|
|
OBJECT_ATTRIBUTES oa;
|
|
HANDLE hHiveList, hCurUser;
|
|
PKEY_FULL_INFORMATION pkfi1, pkfi2;
|
|
PKEY_VALUE_FULL_INFORMATION pkvfi1, pkvfi2;
|
|
ULONG ret_len, i, j, values1, values2;
|
|
NTSTATUS ret;
|
|
WCHAR UserNameBuf[80];
|
|
|
|
// Check the HKEY_CURRENT_USER\Software\Intel\Btrans registry key for the
|
|
// 'RegistryEntryName' entry.
|
|
//
|
|
// The problem is that the HKEY_CURRENT_USER hive is not directly available if
|
|
// we are limited only to using the NTDLL interface. In fact, only two high-level
|
|
// keys are available: HKEY_LOCAL_MACHINE and HKEY_USERS. To sidestep this,
|
|
// the following mechanism is used, based on these two keys' data and process
|
|
// environment:
|
|
//
|
|
// The key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\hivelist
|
|
// contains list of registry entries corresponding to all users, like:
|
|
//
|
|
// Key Value
|
|
//----------------------------------------------------------------------------------------
|
|
// ..............
|
|
// \REGISTRY\USER\S-1-5-21-... ...\Documents And Settings\<username>[...]\NTUSER.DAT
|
|
// \REGISTRY\USER\S-1-5-21-..._Class ...\Documents And Settings\<username>[...]\.......
|
|
//
|
|
// The key fo the value with correct username (without _Class) is actually
|
|
// a reference to HKEY_CURRENT_USER registry key
|
|
//
|
|
// <username> can be easily obtained from the process environment
|
|
|
|
// Environment -> <username>
|
|
memset (UserNameBuf, L' ', sizeof (UserNameBuf)/sizeof (UserNameBuf[0]) - 1);
|
|
UserNameBuf[sizeof (UserNameBuf)/sizeof (UserNameBuf[0]) - 1] = L'\0';
|
|
RtlInitUnicodeString(&us_UserName, UserNameBuf);
|
|
RtlInitUnicodeString(&us_EnvVarUserName, L"USERNAME");
|
|
ret = RtlQueryEnvironmentVariable_U(NULL, &us_EnvVarUserName, &us_UserName);
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
swprintf(cmpbuf, L"\\%s", us_UserName.Buffer);
|
|
DBCODE (FALSE, BtlpPrintf("cmpbuf=%S, wcslen(cmpbuf)=%d\n", cmpbuf, wcslen(cmpbuf)));
|
|
}
|
|
else {
|
|
DBCODE (FALSE, BtlpPrintf("RtlQueryEnvironmentVariable_U failed: status=%X\n", ret));
|
|
}
|
|
|
|
// Go over the entries for HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\hivelist
|
|
swprintf(wBuf, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\hivelist");
|
|
RtlInitUnicodeString(&us_Buffer, wBuf);
|
|
InitializeObjectAttributes(&oa, &us_Buffer, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtOpenKey(&hHiveList, KEY_READ, &oa);
|
|
DBCODE (FALSE, BtlpPrintf ("NtOpenKey ret=0x%X\n",ret));
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
KEY_FULL_INFORMATION buf[128];
|
|
memset(buf, 0, sizeof(buf));
|
|
ret = NtQueryKey(hHiveList, KeyFullInformation, buf, sizeof(buf), &ret_len);
|
|
DBCODE (FALSE, BtlpPrintf ("NtQueryKey ret=0x%X\n",ret));
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
pkfi1 = (PKEY_FULL_INFORMATION)buf;
|
|
values1 = pkfi1->Values;
|
|
for ( i = 0; i < values1; i++ ) {
|
|
KEY_FULL_INFORMATION bufv[128];
|
|
memset(bufv, 0, sizeof(bufv));
|
|
ret = NtEnumerateValueKey(hHiveList, i, KeyValueFullInformation, bufv, sizeof(bufv), &ret_len);
|
|
DBCODE (FALSE, BtlpPrintf ("NtEnumerateValueKey ret=0x%X\n",ret));
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
WCHAR * foundp;
|
|
pkvfi1 = (PKEY_VALUE_FULL_INFORMATION)bufv;
|
|
DBCODE (FALSE, BtlpPrintf("name=%S data=%S nl=%d, do=%d, dl=%d\n",
|
|
pkvfi1->Name, (WCHAR *)((char *)pkvfi1 + pkvfi1->DataOffset),
|
|
pkvfi1->NameLength, pkvfi1->DataOffset, pkvfi1->DataLength);
|
|
BtlpPrintf("tail=%S\n",
|
|
(WCHAR *)((char *)pkvfi1 + pkvfi1->DataOffset + pkvfi1->DataLength - 2) - wcslen(cmpbuf) - 1));
|
|
|
|
// DataLength is in number of bytes, not number of WCHARs
|
|
// DataOffset is offset in bytes from the start of the data structure
|
|
DBCODE (FALSE, BtlpPrintf("User=%S, Compare cmpbuf=%S, HKEY_CURRENT_USER maps to %S\n", us_UserName.Buffer, cmpbuf, pkvfi1->Name));
|
|
foundp = wcsstr ((WCHAR *)((char *)pkvfi1 + pkvfi1->DataOffset), cmpbuf);
|
|
if (foundp
|
|
&& !iswalnum (foundp[wcslen(cmpbuf)])
|
|
&& foundp[wcslen(cmpbuf)] != L'_') {
|
|
DBCODE (FALSE, BtlpPrintf("User=%S, HKEY_CURRENT_USER maps to %S\n", us_UserName.Buffer, pkvfi1->Name));
|
|
|
|
// Found the entry in Users hive corresponding to the current user.
|
|
// Use its name to open the registry key HKEY_CURRENT_USER
|
|
swprintf(wBuf, L"%s\\Software\\Intel\\Btrans", pkvfi1->Name);
|
|
RtlInitUnicodeString(&us_Buffer, wBuf);
|
|
InitializeObjectAttributes(&oa, &us_Buffer, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtOpenKey(&hCurUser, KEY_READ, &oa);
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
KEY_FULL_INFORMATION bufv2[128];
|
|
memset(bufv2, 0, sizeof(bufv2));
|
|
ret = NtQueryKey(hCurUser, KeyFullInformation, bufv2, sizeof(bufv2), &ret_len);
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
pkfi2 = (PKEY_FULL_INFORMATION)bufv2;
|
|
values2 = pkfi2->Values;
|
|
for ( j = 0; j < values2; j++ ) {
|
|
KEY_FULL_INFORMATION bufi2[128];
|
|
memset(bufi2, 0, sizeof(bufi2));
|
|
ret = NtEnumerateValueKey(hCurUser, j, KeyValueFullInformation, bufi2, sizeof(bufi2), &ret_len);
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
pkvfi2 = (PKEY_VALUE_FULL_INFORMATION)bufi2;
|
|
DBCODE (FALSE, BtlpPrintf("name: %S value: %S\n", pkvfi2->Name, (WCHAR *)((char *)pkvfi2 + pkvfi2->DataOffset)));
|
|
|
|
// The entry contains the fullpath of the file
|
|
if (pkvfi2->Type == REG_SZ
|
|
&& wcsncmp(RegistryEntryName, pkvfi2->Name, wcslen(RegistryEntryName)) == 0 ) {
|
|
DBCODE (FALSE, BtlpPrintf("File in HKEY_CURRENT_USER: %S\n", (WCHAR *)((char *)pkvfi2 + pkvfi2->DataOffset)));
|
|
wcscpy(RegistryValueBuf, (WCHAR *)((char *)pkvfi2 + pkvfi2->DataOffset));
|
|
NtClose(hCurUser);
|
|
NtClose(hHiveList);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NtClose(hCurUser);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NtClose(hHiveList);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL BtlpRetrieveHKLMValue (
|
|
IN PWCHAR RegistryEntryName,
|
|
OUT PWCHAR RegistryValueBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve a registry value from HKLM
|
|
|
|
Arguments:
|
|
|
|
RegistryEntryName - IN Registry Entry Name
|
|
RegistryValueBuf - OUT Buffer for the result (pointer to WCHAR string)
|
|
|
|
Return Value:
|
|
|
|
Success/failure (TRUE/FALSE)
|
|
|
|
--*/
|
|
{
|
|
|
|
WCHAR wBuf[256], cmpbuf[128];
|
|
UNICODE_STRING us_Buffer;
|
|
OBJECT_ATTRIBUTES oa;
|
|
HANDLE hLocalMachine;
|
|
PKEY_FULL_INFORMATION pkfi1, pkfi2;
|
|
PKEY_VALUE_FULL_INFORMATION pkvfi1, pkvfi2;
|
|
ULONG ret_len, i, j, values1, values2;
|
|
NTSTATUS ret;
|
|
|
|
// Check the HKEY_LOCAL_MACHINE\Software\Intel\Btrans registry key for the
|
|
// 'RegistryEntryName' entry.
|
|
swprintf(wBuf, L"\\Registry\\Machine\\Software\\Intel\\Btrans");
|
|
RtlInitUnicodeString(&us_Buffer, wBuf);
|
|
InitializeObjectAttributes(&oa, &us_Buffer, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtOpenKey(&hLocalMachine, KEY_READ, &oa);
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
KEY_FULL_INFORMATION buf[128];
|
|
memset(buf, 0, sizeof(buf));
|
|
ret = NtQueryKey(hLocalMachine, KeyFullInformation, buf, sizeof(buf), &ret_len);
|
|
if ( ret == STATUS_SUCCESS) {
|
|
pkfi1 = (PKEY_FULL_INFORMATION)buf;
|
|
for ( j = 0; j < pkfi1->Values; j++ ) {
|
|
KEY_FULL_INFORMATION bufv[128];
|
|
memset(bufv, 0, sizeof(bufv));
|
|
ret = NtEnumerateValueKey(hLocalMachine, j, KeyValueFullInformation, bufv, sizeof(bufv), &ret_len);
|
|
if ( ret == STATUS_SUCCESS ) {
|
|
pkvfi1 = (PKEY_VALUE_FULL_INFORMATION)bufv;
|
|
if (pkvfi1->Type == REG_SZ
|
|
&& wcsncmp(RegistryEntryName, pkvfi1->Name, wcslen(RegistryEntryName)) == 0 ) {
|
|
wcscpy(RegistryValueBuf, (WCHAR *)((char *)pkvfi1 + pkvfi1->DataOffset));
|
|
NtClose(hLocalMachine);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NtClose(hLocalMachine);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL BtlpBtlibDirectory (
|
|
OUT PWCHAR ValueBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve the actual directory of the wowIA32X.dll and use it as
|
|
a last resort if no specific registry pointer
|
|
is found for IA32Exec.bin and BTrans.ini files.
|
|
|
|
Arguments:
|
|
|
|
ValueBuf - OUT Buffer for the result (pointer to WCHAR string)
|
|
|
|
Return Value:
|
|
|
|
Success/failure (TRUE/FALSE)
|
|
|
|
--*/
|
|
{
|
|
PPEB_LDR_DATA LdrP;
|
|
PLDR_DATA_TABLE_ENTRY LdrDtP;
|
|
|
|
RtlAcquirePebLock();
|
|
LdrP = BT_CURRENT_TEB()->ProcessEnvironmentBlock->Ldr;
|
|
LdrDtP = (PLDR_DATA_TABLE_ENTRY)(LdrP->InLoadOrderModuleList.Flink);
|
|
do {
|
|
// Our own address belongs to the wowIA32X.dll module
|
|
if ( (ULONG_PTR)BtlpBtlibDirectory >= (ULONG_PTR)LdrDtP->DllBase
|
|
&& (ULONG_PTR)BtlpBtlibDirectory < (ULONG_PTR)LdrDtP->DllBase + LdrDtP->SizeOfImage) {
|
|
// Found
|
|
WCHAR * EndPtr;
|
|
wcscpy(ValueBuf, LdrDtP->FullDllName.Buffer);
|
|
// Remove file name at the end (until '\\' inclusively)
|
|
EndPtr = wcsrchr(ValueBuf,L'\\');
|
|
if (EndPtr) {
|
|
*EndPtr = L'\0';
|
|
}
|
|
RtlReleasePebLock();
|
|
return TRUE;
|
|
}
|
|
LdrDtP = (PLDR_DATA_TABLE_ENTRY)(LdrDtP->InLoadOrderLinks.Flink);
|
|
} while (LdrDtP != (PLDR_DATA_TABLE_ENTRY)&(LdrP->InLoadOrderModuleList.Flink));
|
|
RtlReleasePebLock();
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef RELEASE
|
|
static int BtlpIniFileExists(
|
|
IN PWCHAR CurrentDir,
|
|
IN int fBTGenericHandle,
|
|
OUT PHANDLE phIniFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locate and open BTrans.ini file
|
|
|
|
Arguments:
|
|
|
|
CurrentDir - IN current directory
|
|
phIniFile - OUT Handle of the file
|
|
|
|
Return Value:
|
|
|
|
Success/failure (TRUE/FALSE)
|
|
|
|
--*/
|
|
{
|
|
|
|
WCHAR RegEntry[16] = L"SETUP_FILE";
|
|
WCHAR RegistryValueBuf[1024];
|
|
WCHAR IniFileFullPath[1024];
|
|
UNICODE_STRING us_IniFile;
|
|
OBJECT_ATTRIBUTES oa;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER AllocSz = { 0, 0 };
|
|
NTSTATUS ret;
|
|
|
|
// 1. Check for existence of Btrans.ini file in the current work directory
|
|
swprintf(IniFileFullPath, L"\\DosDevices\\%s\\BTrans.ini", CurrentDir);
|
|
RtlInitUnicodeString(&us_IniFile, IniFileFullPath);
|
|
InitializeObjectAttributes(&oa, &us_IniFile, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile(phIniFile, FILE_GENERIC_READ, &oa, &IoStatusBlock, &AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
if ( ret == STATUS_SUCCESS && (*phIniFile) != INVALID_HANDLE_VALUE ) {
|
|
//BtlpPrintf("Setup file in current work directory: %S\n", IniFileFullPath);
|
|
return F_CURRENT_DIR;
|
|
}
|
|
|
|
#ifndef NODEBUG
|
|
// 2. Check the HKEY_CURRENT_USER\Software\Intel\Btrans registry key for the
|
|
// SETUP_FILE entry that should contain the fullpath of the IA-32 Execution Layer setup file
|
|
// (only if IA32Exec.bin found there as well)
|
|
if (F_HKCU == fBTGenericHandle
|
|
&& BtlpRetrieveHKCUValue (RegEntry, RegistryValueBuf)) {
|
|
swprintf(IniFileFullPath, L"\\DosDevices\\%s", RegistryValueBuf);
|
|
RtlInitUnicodeString(&us_IniFile, IniFileFullPath);
|
|
InitializeObjectAttributes(&oa, &us_IniFile, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile(phIniFile, FILE_GENERIC_READ, &oa, &IoStatusBlock, &AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
if ( ret == STATUS_SUCCESS && (*phIniFile) != INVALID_HANDLE_VALUE ) {
|
|
return F_HKCU;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 3. Check the HKEY_LOCAL_MACHINE\Software\Intel\Btrans registry key for the
|
|
// SETUP_FILE entry that contains the fullpath of the IA-32 Execution Layer setup file
|
|
// (only if IA32Exec.bin found there as well)
|
|
if (F_HKLM == fBTGenericHandle
|
|
&& BtlpRetrieveHKLMValue (RegEntry, RegistryValueBuf)) {
|
|
swprintf(IniFileFullPath, L"\\DosDevices\\%s", RegistryValueBuf);
|
|
RtlInitUnicodeString(&us_IniFile, IniFileFullPath);
|
|
InitializeObjectAttributes(&oa, &us_IniFile, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile(phIniFile, FILE_GENERIC_READ, &oa, &IoStatusBlock, &AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
if ( ret == STATUS_SUCCESS && (*phIniFile) != INVALID_HANDLE_VALUE ) {
|
|
return F_HKLM;
|
|
}
|
|
}
|
|
|
|
// 4. Last resort - directory that wowIA32X.dll was loaded from
|
|
// (only if IA32Exec.bin found there as well)
|
|
if (F_BTLIB == fBTGenericHandle
|
|
&& BtlpBtlibDirectory (RegistryValueBuf)) {
|
|
swprintf(IniFileFullPath, L"\\DosDevices\\%s\\BTrans.ini", RegistryValueBuf);
|
|
RtlInitUnicodeString(&us_IniFile, IniFileFullPath);
|
|
InitializeObjectAttributes(&oa, &us_IniFile, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile(phIniFile, FILE_GENERIC_READ, &oa, &IoStatusBlock, &AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
if ( ret == STATUS_SUCCESS && (*phIniFile) != INVALID_HANDLE_VALUE ) {
|
|
return F_BTLIB;
|
|
}
|
|
}
|
|
|
|
*phIniFile = INVALID_HANDLE_VALUE;
|
|
return F_NOT_FOUND;
|
|
}
|
|
#endif /* RELEASE */
|
|
|
|
static int BtlpLoadBTGeneric(
|
|
IN PWCHAR CurrentDir,
|
|
OUT PHANDLE phBTGenericLibrary
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locate and load IA32Exec.bin component
|
|
|
|
Arguments:
|
|
|
|
CurrentDir - IN current directory
|
|
phBTGenericLibrary - OUT Handle of the IA32Exec.bin
|
|
|
|
Return Value:
|
|
|
|
Success/failure (TRUE/FALSE)
|
|
|
|
--*/
|
|
{
|
|
WCHAR RegistryValueBuf[1024];
|
|
UNICODE_STRING us_BTGenericLibrary;
|
|
WCHAR BTGenericLibraryFullPath[1024];
|
|
NTSTATUS ret;
|
|
|
|
#ifndef RELEASE
|
|
WCHAR RegEntry[16] = L"GENERIC_FILE";
|
|
|
|
// 1. Check for existence of IA32Exec.bin file in the current work directory
|
|
swprintf(BTGenericLibraryFullPath, L"%s\\%s.%s", CurrentDir, IA32EX_G_NAME,
|
|
IA32EX_G_SUFFIX);
|
|
RtlInitUnicodeString(&us_BTGenericLibrary, BTGenericLibraryFullPath);
|
|
ret = LdrLoadDll((PWSTR)NULL, (PULONG)0, &us_BTGenericLibrary, phBTGenericLibrary);
|
|
if ( ret == STATUS_SUCCESS && (*phBTGenericLibrary) != INVALID_HANDLE_VALUE ) {
|
|
//BtlpPrintf("IA32Exec.bin file in current work directory: %S\n", BTGenericLibraryFullPath);
|
|
return F_CURRENT_DIR;
|
|
}
|
|
|
|
#ifndef NODEBUG
|
|
// 2. Check the HKEY_CURRENT_USER\Software\Intel\Btrans registry key for the
|
|
// BTGENERIC_FILE entry that should contain the fullpath of the IA32Exec.bin file
|
|
if (BtlpRetrieveHKCUValue (RegEntry, RegistryValueBuf)) {
|
|
swprintf(BTGenericLibraryFullPath, L"%s", RegistryValueBuf);
|
|
RtlInitUnicodeString(&us_BTGenericLibrary, BTGenericLibraryFullPath);
|
|
ret = LdrLoadDll((PWSTR)NULL, (PULONG)0, &us_BTGenericLibrary, phBTGenericLibrary);
|
|
if ( ret == STATUS_SUCCESS && (*phBTGenericLibrary) != INVALID_HANDLE_VALUE ) {
|
|
return F_HKCU;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 3. Check the HKEY_LOCAL_MACHINE\Software\Intel\Btrans registry key for the
|
|
// BTGENERIC_FILE entry that contains the fullpath of the IA32Exec.bin file
|
|
if (BtlpRetrieveHKLMValue (RegEntry, RegistryValueBuf)) {
|
|
swprintf(BTGenericLibraryFullPath, L"%s", RegistryValueBuf);
|
|
RtlInitUnicodeString(&us_BTGenericLibrary, BTGenericLibraryFullPath);
|
|
ret = LdrLoadDll((PWSTR)NULL, (PULONG)0, &us_BTGenericLibrary, phBTGenericLibrary);
|
|
if ( ret == STATUS_SUCCESS && (*phBTGenericLibrary) != INVALID_HANDLE_VALUE ) {
|
|
return F_HKLM;
|
|
}
|
|
}
|
|
#endif /* RELEASE */
|
|
|
|
// 4. Last resort - directory that wowIA32X.dll was loaded from
|
|
// This is the only option in RELEASE mode
|
|
if (BtlpBtlibDirectory (RegistryValueBuf)) {
|
|
swprintf(BTGenericLibraryFullPath, L"%s\\%s.%s", RegistryValueBuf, IA32EX_G_NAME,
|
|
IA32EX_G_SUFFIX);
|
|
RtlInitUnicodeString(&us_BTGenericLibrary, BTGenericLibraryFullPath);
|
|
ret = LdrLoadDll((PWSTR)NULL, (PULONG)0, &us_BTGenericLibrary, phBTGenericLibrary);
|
|
if ( ret == STATUS_SUCCESS && (*phBTGenericLibrary) != INVALID_HANDLE_VALUE ) {
|
|
return F_BTLIB;
|
|
}
|
|
}
|
|
|
|
*phBTGenericLibrary = INVALID_HANDLE_VALUE;
|
|
return F_NOT_FOUND;
|
|
}
|
|
|
|
// Extract DOS header from NT executable (NULL if it is not one)
|
|
static PIMAGE_DOS_HEADER WINAPI BtlpExtractDosHeader (IN HANDLE hModule) {
|
|
PIMAGE_DOS_HEADER DosHeaderP;
|
|
|
|
DosHeaderP = (PIMAGE_DOS_HEADER) hModule;
|
|
assert (!((ULONG_PTR)DosHeaderP & 0xFFFF));
|
|
if (DosHeaderP->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
return NULL;
|
|
}
|
|
return DosHeaderP;
|
|
}
|
|
|
|
// Extract NT header from NT executable (abort if not one)
|
|
static PIMAGE_NT_HEADERS WINAPI BtlpExtractNTHeader (IN HINSTANCE hModule) {
|
|
PIMAGE_DOS_HEADER DosHeaderP;
|
|
PIMAGE_NT_HEADERS NTHeaderP;
|
|
|
|
DosHeaderP = BtlpExtractDosHeader ((HANDLE)hModule);
|
|
assert (DosHeaderP != NULL);
|
|
NTHeaderP = (PIMAGE_NT_HEADERS) ((ULONG_PTR)DosHeaderP + DosHeaderP->e_lfanew);
|
|
assert (NTHeaderP->Signature == IMAGE_NT_SIGNATURE);
|
|
assert ((NTHeaderP->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) != 0);
|
|
assert ((NTHeaderP->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE) != 0);
|
|
assert (NTHeaderP->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC);
|
|
assert (NTHeaderP->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64);
|
|
return NTHeaderP;
|
|
}
|
|
|
|
static void BtlpInitIA32Context(
|
|
BTGENERIC_IA32_CONTEXT * IA32ContextP,
|
|
PTEB32 pTEB32
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Initialize IA32 thread context
|
|
Arguments:
|
|
IA32ContextP - Pointer to IA32 context to be initialized
|
|
pTEB32 - Pointer to IA32 TEB of the thread whose context is
|
|
to be initialized
|
|
Return Value:
|
|
none
|
|
--*/
|
|
{
|
|
memset(IA32ContextP, 0, sizeof(*IA32ContextP) );
|
|
IA32ContextP->SegCs = CS_INIT_VAL;
|
|
IA32ContextP->SegDs = DS_INIT_VAL;
|
|
IA32ContextP->SegEs = ES_INIT_VAL;
|
|
IA32ContextP->SegFs = FS_INIT_VAL;
|
|
IA32ContextP->SegSs = SS_INIT_VAL;
|
|
|
|
IA32ContextP->EFlags = EFLAGS_INIT_VAL;
|
|
IA32ContextP->Esp = (U32)pTEB32->NtTib.StackBase - sizeof(U32);
|
|
|
|
IA32ContextP->FloatSave.ControlWord = FPCW_INIT_VAL;
|
|
IA32ContextP->FloatSave.TagWord = FPTW_INIT_VAL;
|
|
|
|
*(U32 *)&(IA32ContextP->ExtendedRegisters[24]) = MXCSR_INIT_VAL;
|
|
}
|
|
|
|
__inline NTSTATUS BtlpGetProcessInfo(
|
|
IN HANDLE ProcessHandle,
|
|
PROCESS_BASIC_INFORMATION * pInfo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Given a process handle, return basic process information
|
|
Arguments:
|
|
ProcessHandle - Process handle
|
|
pInfo - Buffer to receive PROCESS_BASIC_INFORMATION
|
|
Return Value:
|
|
NTSTATUS.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = NtQueryInformationProcess(
|
|
ProcessHandle,
|
|
ProcessBasicInformation,
|
|
pInfo,
|
|
sizeof(*pInfo),
|
|
0);
|
|
BTLP_REPORT_NT_FAILURE("NtQueryInformationProcess", status);
|
|
return status;
|
|
}
|
|
|
|
__inline NTSTATUS BtlpGetProcessUniqueIdByHandle(
|
|
IN HANDLE ProcessHandle,
|
|
OUT U64 * ProcessIdP
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Given a process handle, return unique (throughout the system) ID of the process
|
|
Arguments:
|
|
ProcessHandle - Process handle
|
|
ProcessIdP - Pointer to a variable that receives unique process ID
|
|
Return Value:
|
|
NTSTATUS.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PROCESS_BASIC_INFORMATION info;
|
|
|
|
status = BtlpGetProcessInfo(ProcessHandle,&info);
|
|
if (status == STATUS_SUCCESS) {
|
|
*ProcessIdP = (U64)(info.UniqueProcessId);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
__inline NTSTATUS BtlpGetThreadUniqueIdByHandle(
|
|
IN HANDLE ThreadHandle,
|
|
OUT U64 * ThreadIdP
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Given a thread handle, return unique (throughout the system) ID of the thread
|
|
Arguments:
|
|
ThreadHandle - Thread handle
|
|
ThreadIdP - Pointer to a variable that receives unique thread ID
|
|
Return Value:
|
|
NTSTATUS.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
THREAD_BASIC_INFORMATION info;
|
|
|
|
status = NtQueryInformationThread(
|
|
ThreadHandle,
|
|
ThreadBasicInformation,
|
|
&info,
|
|
sizeof(info),
|
|
0);
|
|
if (status == STATUS_SUCCESS) {
|
|
*ThreadIdP = (U64)(info.ClientId.UniqueThread);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
__inline BOOL BtlpIsCurrentProcess(
|
|
IN HANDLE ProcessHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Check to see if a process handle represents the current process
|
|
Arguments:
|
|
ProcessHandle - Process handle
|
|
Return Value:
|
|
TRUE if ProcessHandle represents the current process, FALSE - otherwise.
|
|
--*/
|
|
{
|
|
U64 ProcessId;
|
|
return ((ProcessHandle == NtCurrentProcess()) ||
|
|
((BtlpGetProcessUniqueIdByHandle(ProcessHandle, &ProcessId) == STATUS_SUCCESS) &&
|
|
(BT_CURRENT_PROC_UID() == ProcessId)));
|
|
}
|
|
|
|
|
|
__inline PVOID BtlpGetTlsPtr(
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB pTEB,
|
|
IN BOOL IsLocal
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Read BT_TLS pointer of a local or remote thread
|
|
Arguments:
|
|
ProcessHandle - Process handle
|
|
pTEB - TEB of a local/remote thread
|
|
IsLocal - TRUE for local thread
|
|
Return Value:
|
|
BT_TLS pointer. NULL if access failed.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PVOID GlstP;
|
|
if (IsLocal) {
|
|
GlstP = BT_TLS_OF(pTEB);
|
|
}
|
|
else {
|
|
status = NtReadVirtualMemory(ProcessHandle,
|
|
(VOID * )((UINT_PTR)pTEB + BT_TLS_OFFSET),
|
|
&GlstP,
|
|
sizeof(GlstP),
|
|
NULL);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtReadVirtualMemory", status);
|
|
GlstP = NULL;
|
|
}
|
|
}
|
|
return GlstP;
|
|
}
|
|
|
|
__inline NTSTATUS BtlpInitSharedInfo()
|
|
/*++
|
|
Routine Description:
|
|
Initialize thread shared info
|
|
Arguments:
|
|
None
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
|
|
BTLIB_INIT_SUSPENSION_PERMISSION();
|
|
BTLIB_INIT_SUSPEND_REQUEST();
|
|
BTLIB_SET_CONSISTENT_EXCEPT_STATE();
|
|
BTLIB_SET_SIGNATURE();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS BtlpReadSharedInfo(
|
|
IN HANDLE ProcessHandle,
|
|
IN PVOID pTLS,
|
|
IN BOOL IsLocal,
|
|
OUT BTLIB_SHARED_INFO_TYPE * SharedInfoP
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Read shared wowIA32X.dll info of a local or remote thread. The function
|
|
fails if wowIA32X.dll signature does not match.
|
|
Arguments:
|
|
ProcessHandle - Process handle
|
|
pTLS - pointer to BT_TLS of a local/remote thread
|
|
IsLocal - TRUE for local thread
|
|
SharedInfoP - buffer to read shared wowIA32X.dll info to
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if (IsLocal) {
|
|
*SharedInfoP = *((BTLIB_SHARED_INFO_TYPE *)BTLIB_MEMBER_PTR(pTLS, SharedInfo));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = NtReadVirtualMemory(ProcessHandle,
|
|
BTLIB_MEMBER_PTR(pTLS, SharedInfo),
|
|
SharedInfoP,
|
|
sizeof(*SharedInfoP),
|
|
NULL);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtReadVirtualMemory", status);
|
|
}
|
|
else if (!BTLIB_SI_CHECK_SIGNATURE(SharedInfoP)) {
|
|
DBCODE (TRUE, BtlpPrintf("\nwowIA32X.dll Signature mismatch!!!!\n"));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS BtlpWriteSuspendRequest(
|
|
IN HANDLE ProcessHandle,
|
|
IN PVOID pTLS,
|
|
IN BOOL IsLocal,
|
|
IN BTLIB_SUSPEND_REQUEST * SuspendRequestP
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Write BTLIB_SUSPEND_REQUEST to a local or remote thread.
|
|
Arguments:
|
|
ProcessHandle - Process handle
|
|
pTLS - pointer to BT_TLS of a local/remote thread
|
|
IsLocal - TRUE for local thread
|
|
SharedInfoP - buffer to write BTLIB_SUSPEND_REQUEST from
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
if (IsLocal) {
|
|
*((BTLIB_SUSPEND_REQUEST *)BTLIB_MEMBER_PTR(pTLS, SharedInfo.SuspendRequest)) = *SuspendRequestP;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = NtWriteVirtualMemory(ProcessHandle,
|
|
BTLIB_MEMBER_PTR(pTLS, SharedInfo.SuspendRequest),
|
|
SuspendRequestP,
|
|
sizeof(*SuspendRequestP),
|
|
NULL);
|
|
BTLP_REPORT_NT_FAILURE("NtWriteVirtualMemory", status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS BtlpSendSuspensionRequest(
|
|
IN HANDLE ProcessHandle,
|
|
IN HANDLE ThreadHandle,
|
|
IN PVOID pTLS,
|
|
IN BOOL IsLocal,
|
|
IN CONTEXT * ResumeContextP,
|
|
IN OUT PULONG PreviousSuspendCountP
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
The function is called when the target thread is ready to perform self-canonization
|
|
and exit simulation.
|
|
The function fills in BTLIB_SUSPEND_REQUEST, resumes target thread and suspends
|
|
it in a consistent state.
|
|
Caller is responsible for serialization of SUSPEND_REQUESTs.
|
|
Arguments:
|
|
ProcessHandle - target process handle
|
|
ThreadHandle - target thread handle
|
|
pTLS - pointer to BT_TLS of a local/remote thread
|
|
IsLocal - TRUE for local thread
|
|
ResumeContextP - IA64 context to resume target thread for self-canonization
|
|
PreviousSuspendCountP - IN OUT pointer to thread's previous suspend count.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BTLIB_SUSPEND_REQUEST SuspendRequest;
|
|
HANDLE ReadyEvent = INVALID_HANDLE_VALUE;
|
|
HANDLE ResumeEvent = INVALID_HANDLE_VALUE;
|
|
HANDLE WaitArray[2];
|
|
|
|
//This function call must be serialized
|
|
assert(*PreviousSuspendCountP ==0);
|
|
|
|
SuspendRequest.ReadyEvent = INVALID_HANDLE_VALUE;
|
|
SuspendRequest.ResumeEvent = INVALID_HANDLE_VALUE;
|
|
|
|
do {
|
|
//Prepare SUSPEND_REQUEST for sending to the target process
|
|
|
|
//Create pair of synchronization events in current process and duplicate
|
|
//them to the target process
|
|
status = NtCreateEvent(&ReadyEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtCreateEvent", status);
|
|
break;
|
|
}
|
|
|
|
status = NtCreateEvent(&ResumeEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtCreateEvent", status);
|
|
break;
|
|
}
|
|
|
|
status = NtDuplicateObject(NtCurrentProcess(),
|
|
ReadyEvent,
|
|
ProcessHandle,
|
|
&(SuspendRequest.ReadyEvent),
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtDuplicateObject", status);
|
|
break;
|
|
}
|
|
|
|
status = NtDuplicateObject(NtCurrentProcess(),
|
|
ResumeEvent,
|
|
ProcessHandle,
|
|
&(SuspendRequest.ResumeEvent),
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtDuplicateObject", status);
|
|
break;
|
|
}
|
|
|
|
|
|
// First write SUSPEND_REQUEST to the the target thread,
|
|
// tnen set ContextIA64 and resume target thread.
|
|
// The order is important! The BtlpEnsureSuspensionConsistency function
|
|
// relies on that.
|
|
SuspendRequest.Active = TRUE;
|
|
status = BtlpWriteSuspendRequest(ProcessHandle, pTLS, IsLocal, &SuspendRequest);
|
|
if (status != STATUS_SUCCESS) {
|
|
//Sending request failed, do not try to remove it.
|
|
SuspendRequest.Active = FALSE;
|
|
break;
|
|
}
|
|
|
|
status = NtSetContextThread(ThreadHandle, ResumeContextP);
|
|
if (status != STATUS_SUCCESS) {
|
|
//abort request and report failure.
|
|
BTLP_REPORT_NT_FAILURE("NtSetContextThread", status);
|
|
break;
|
|
}
|
|
|
|
status = NtResumeThread(ThreadHandle, NULL);
|
|
if (status != STATUS_SUCCESS) {
|
|
//abort request and report failure.
|
|
BTLP_REPORT_NT_FAILURE("NtResumeThread", status);
|
|
break;
|
|
}
|
|
|
|
//Wait until remote thread receives request or dies
|
|
WaitArray[0] = ThreadHandle;
|
|
WaitArray[1] = ReadyEvent;
|
|
|
|
status = NtWaitForMultipleObjects(2, WaitArray, WaitAny, FALSE, NULL);
|
|
if (status == STATUS_WAIT_0) {
|
|
//the target thread is died along with its event handles.
|
|
//No need to remove request.
|
|
BTLP_REPORT_NT_FAILURE("NtWaitForMultipleObjects", status);
|
|
SuspendRequest.ReadyEvent = INVALID_HANDLE_VALUE;
|
|
SuspendRequest.ResumeEvent = INVALID_HANDLE_VALUE;
|
|
SuspendRequest.Active = FALSE;
|
|
}
|
|
|
|
//Suspend target thread and signal the ResumeEvent event to release
|
|
//target thread after it will be awaken (later on)
|
|
status = NtSuspendThread(ThreadHandle, PreviousSuspendCountP);
|
|
if (status != STATUS_SUCCESS) {
|
|
//remove request and report failure.
|
|
BTLP_REPORT_NT_FAILURE("NtSuspendThread", status);
|
|
break;
|
|
}
|
|
status = NtSetEvent(ResumeEvent, NULL);
|
|
BTLP_REPORT_NT_FAILURE("NtSetEvent", status);
|
|
|
|
|
|
} while (FALSE);
|
|
|
|
//Close local event handles
|
|
if (ReadyEvent != INVALID_HANDLE_VALUE) {
|
|
NtClose(ReadyEvent);
|
|
}
|
|
if (ResumeEvent != INVALID_HANDLE_VALUE) {
|
|
NtClose(ResumeEvent);
|
|
}
|
|
|
|
//Close remote event handles if needed
|
|
if (status != STATUS_SUCCESS) {
|
|
if (SuspendRequest.ReadyEvent != INVALID_HANDLE_VALUE) {
|
|
NtDuplicateObject(ProcessHandle,
|
|
SuspendRequest.ReadyEvent,
|
|
NtCurrentProcess(),
|
|
&ReadyEvent,
|
|
EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
DUPLICATE_CLOSE_SOURCE);
|
|
NtClose(ReadyEvent);
|
|
SuspendRequest.ReadyEvent = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (SuspendRequest.ResumeEvent != INVALID_HANDLE_VALUE) {
|
|
NtDuplicateObject(ProcessHandle,
|
|
SuspendRequest.ResumeEvent,
|
|
NtCurrentProcess(),
|
|
&ResumeEvent,
|
|
EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
DUPLICATE_CLOSE_SOURCE);
|
|
NtClose(ResumeEvent);
|
|
SuspendRequest.ResumeEvent = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
}
|
|
|
|
//Close SUSPEND_REQUEST
|
|
if (SuspendRequest.Active) {
|
|
SuspendRequest.Active = FALSE;
|
|
BtlpWriteSuspendRequest(ProcessHandle, pTLS, IsLocal, &SuspendRequest);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS BtlpReceiveSuspensionRequest()
|
|
/*++
|
|
Routine Description:
|
|
The function handles active SUSPEND_REQUEST sent to this thread by
|
|
another thread. It notifies request sender about reaching the point
|
|
where suspension is possible and waits for real suspension to be
|
|
performed by the request's sender.
|
|
Arguments:
|
|
none
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE ReadyEvent;
|
|
HANDLE ResumeEvent;
|
|
|
|
//local SUSPEND_REQUEST should be active at this point
|
|
assert(BTLIB_INFO_PTR()->SharedInfo.SuspendRequest.Active);
|
|
|
|
//Copy events from SUSPEND_REQUEST to local vars. It is important
|
|
//because after suspend/resume the event handles should be closed but
|
|
//SUSPEND_REQUEST could be rewritten
|
|
ReadyEvent = BTLIB_INFO_PTR()->SharedInfo.SuspendRequest.ReadyEvent;
|
|
ResumeEvent = BTLIB_INFO_PTR()->SharedInfo.SuspendRequest.ResumeEvent;
|
|
|
|
assert ((ReadyEvent != INVALID_HANDLE_VALUE) && (ResumeEvent != INVALID_HANDLE_VALUE));
|
|
|
|
//Release ReadyEvent and wait for ResumeEvent. The thread can be
|
|
//suspended at this point
|
|
status = NtSignalAndWaitForSingleObject(ReadyEvent, ResumeEvent, FALSE, NULL);
|
|
|
|
//Close local event handles
|
|
NtClose(ReadyEvent);
|
|
NtClose(ResumeEvent);
|
|
|
|
return status;
|
|
}
|
|
|
|
// Wow64CPU forwarded APIs implementations:
|
|
// Note that all names are defined with PTAPI() macro which adds either BTCpu or Cpu prefix,
|
|
// depending on the mode we build the DLL.
|
|
// In release mode all names will be BTCpu only.
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(ProcessInit)(
|
|
PWSTR pImageName,
|
|
PSIZE_T pCpuThreadDataSize)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Per-process initialization code
|
|
Locates, reads and parses parameters file BTrans.ini
|
|
Locates, load and initializes IA32Exec.bin component
|
|
|
|
|
|
Arguments:
|
|
|
|
pImageName - IN the name of the image. The memory for this
|
|
is freed after the call, so if the callee wants
|
|
to keep the name around, they need to allocate space
|
|
and copy it. DON'T SAVE THE POINTER!
|
|
|
|
pCpuThreadSize - OUT ptr to number of bytes of memory the CPU
|
|
wants allocated for each thread.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
|
|
char * argv[128] = { NULL };
|
|
int argc = 1;
|
|
static WCHAR CurrentDir[1024];
|
|
HANDLE BTGenericHandle;
|
|
int fBTGenericFound;
|
|
#ifdef OVERRIDE_TIA
|
|
HANDLE BtlpOverrideTiaFile = INVALID_HANDLE_VALUE;
|
|
OBJECT_ATTRIBUTES oa;
|
|
IO_STATUS_BLOCK iosb;
|
|
LARGE_INTEGER as = { 0, 0 }, offst = { 0, 0 };
|
|
#endif /* OVERRIDE_TIA */
|
|
|
|
{
|
|
// Temporary workaround for IA32 debugging support.
|
|
//To be removed after fixing FlushIC(ProcessHandle) by MS.
|
|
NTSTATUS status;
|
|
PROCESS_BASIC_INFORMATION info;
|
|
//Check to see if current process is being debugged
|
|
status = BtlpGetProcessInfo(NtCurrentProcess(), &info);
|
|
BeingDebugged = ((status == STATUS_SUCCESS) &&
|
|
(info.PebBaseAddress->BeingDebugged));
|
|
}
|
|
|
|
RtlGetCurrentDirectory_U(512, CurrentDir);
|
|
|
|
// Load IA32Exec.bin ...
|
|
fBTGenericFound = BtlpLoadBTGeneric(CurrentDir, &BTGenericHandle);
|
|
if (F_NOT_FOUND == fBTGenericFound) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
#ifndef RELEASE
|
|
// Load parameters file
|
|
{
|
|
UNICODE_STRING IniFileName, LogFileName;
|
|
HANDLE IniFileHandle = INVALID_HANDLE_VALUE;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER AllocSz = { 0, 0 }, Offset = { 0, 0 };
|
|
char IniBuffer[8192] = "";
|
|
char IniArgs[8192], * IniArgsP = IniArgs;
|
|
WCHAR LogFileFullPath[1024], LogName[64];
|
|
|
|
if (wcslen (pImageName) >= sizeof (ImageName) / sizeof (ImageName[0])) {
|
|
swprintf (ImageName, L"%s", "dummyImage");
|
|
}
|
|
else {
|
|
swprintf (ImageName, L"%s", pImageName);
|
|
}
|
|
|
|
// Locate and scan INI file, extract parameters
|
|
if (F_NOT_FOUND != BtlpIniFileExists(CurrentDir, fBTGenericFound, &IniFileHandle)) {
|
|
int ret;
|
|
ret = NtReadFile(IniFileHandle, NULL, NULL, NULL, &IoStatusBlock, (VOID *)IniBuffer, 8192, &Offset, NULL);
|
|
if ( ret == STATUS_END_OF_FILE || ret == STATUS_SUCCESS ) {
|
|
char * CurrentChar;
|
|
static char logparm[] = "log";
|
|
static char logdirparm[] = "logdir";
|
|
#ifdef OVERRIDE_TIA
|
|
static char ovrtiaparm[] = "override_tia=";
|
|
#endif
|
|
#ifndef NODEBUG
|
|
static char PerthreadParm[] = "logfile_per_thread";
|
|
#endif
|
|
static char DebugBtransParm[] = "debug_btrans";
|
|
|
|
// Close file (assuming everything has been read in one piece)
|
|
NtClose(IniFileHandle);
|
|
LogDirName[0] = '\0';
|
|
LogDirName[1] = '\0';
|
|
|
|
// Mark end of the text
|
|
CurrentChar = IniBuffer;
|
|
CurrentChar[IoStatusBlock.Information] = '\0';
|
|
|
|
// Scan until end of file
|
|
while ( *CurrentChar != '\0' ) {
|
|
char *EndOfLineChar;
|
|
// Locate and handle end of line
|
|
if (EndOfLineChar = strchr(CurrentChar+1, '\n')) {
|
|
// Remove \r before, if one present
|
|
if (*(EndOfLineChar-1) == '\r') {
|
|
*(EndOfLineChar-1) = '\0';
|
|
}
|
|
// Replace \n by \0
|
|
*EndOfLineChar++ = '\0';
|
|
}
|
|
|
|
#ifndef NODEBUG
|
|
if (_strnicmp(CurrentChar, PerthreadParm, sizeof (PerthreadParm) - 1) == 0) {
|
|
CurrentChar += (sizeof (PerthreadParm) - 1);
|
|
if (BtlpWow64LogFile == INVALID_HANDLE_VALUE) {
|
|
BtlpLogFilePerThread = TRUE;
|
|
swprintf(BtlpLogFileFullPath, L"\\DosDevices\\%s\\%s", CurrentDir, pImageName);
|
|
}
|
|
} else
|
|
#endif
|
|
if (_strnicmp(CurrentChar, DebugBtransParm, sizeof (DebugBtransParm) - 1) == 0) {
|
|
CurrentChar += (sizeof (DebugBtransParm) - 1);
|
|
//ignore application debugging when debugging IA-32 Execution layer
|
|
BeingDebugged = FALSE;
|
|
}
|
|
else if ( _strnicmp(CurrentChar, logdirparm, sizeof (logdirparm) - 1) == 0 ) {
|
|
WCHAR *pl;
|
|
CurrentChar += (sizeof (logdirparm) - 1);
|
|
// log directory specified
|
|
if (*CurrentChar == '=' && *(CurrentChar+1) != '\0') {
|
|
// Name present as well
|
|
CurrentChar++;
|
|
pl = LogDirName;
|
|
while ( (WCHAR)*CurrentChar != (WCHAR)'\0' ) {
|
|
*pl = (WCHAR)*CurrentChar;
|
|
pl++;
|
|
CurrentChar++;
|
|
}
|
|
*pl = (WCHAR)'\0';
|
|
}
|
|
else {
|
|
BtlpPrintf("logdir specified without =<dirname>, IGNORED\n");
|
|
}
|
|
}
|
|
// Process and skip "log" parameter
|
|
else if ( _strnicmp(CurrentChar, logparm, sizeof (logparm) - 1) == 0 ) {
|
|
WCHAR *pl;
|
|
|
|
CurrentChar += (sizeof (logparm) - 1);
|
|
|
|
// log required - prepare log file name
|
|
if (*CurrentChar == '=' && *(CurrentChar+1) != '\0') {
|
|
// Name present as well
|
|
CurrentChar++;
|
|
pl = LogName;
|
|
while ( (WCHAR)*CurrentChar != (WCHAR)'\0') {
|
|
*pl++ = (WCHAR)*CurrentChar++;
|
|
}
|
|
*pl = (WCHAR)'\0';
|
|
}
|
|
else {
|
|
// No name - use ImageName.log
|
|
swprintf(LogName, L"%s.log", pImageName);
|
|
}
|
|
|
|
// Create log file
|
|
if (0==LogDirName[0] && 0==LogDirName[1]) {
|
|
swprintf(LogFileFullPath, L"\\DosDevices\\%s\\%s", CurrentDir, LogName);
|
|
}
|
|
else {
|
|
swprintf(LogFileFullPath, L"\\DosDevices\\%s\\%s", LogDirName, LogName);
|
|
}
|
|
|
|
RtlInitUnicodeString(&LogFileName, LogFileFullPath);
|
|
InitializeObjectAttributes(&ObjectAttributes, &LogFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile(&BtlpWow64LogFile, FILE_GENERIC_WRITE,
|
|
&ObjectAttributes, &IoStatusBlock,
|
|
&AllocSz, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_SUPERSEDE,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpWow64LogFile = INVALID_HANDLE_VALUE;
|
|
// BtlpPrintf("Can't create LOG file %S: status=%X\n", LogFileFullPath, ret);
|
|
}
|
|
#ifndef NODEBUG
|
|
BtlpLogFilePerThread = FALSE;
|
|
#endif
|
|
}
|
|
#ifdef OVERRIDE_TIA
|
|
// Process and skip "override_tia=..." parameter
|
|
else if ( _strnicmp(CurrentChar, ovrtiaparm, sizeof(ovrtiaparm) - 1) == 0 ) {
|
|
WCHAR * pl;
|
|
WCHAR OvrTiaFileName[64], OvrTiaFileFullPath[1024];
|
|
UNICODE_STRING OvrTiaFile;
|
|
|
|
CurrentChar += (sizeof(ovrtiaparm) - 1);
|
|
if ( *CurrentChar == '\0') {
|
|
BtlpPrintf("Name of override TIA file not specfied\n");
|
|
continue;
|
|
}
|
|
// prepare override TIA file name
|
|
pl = OvrTiaFileName;
|
|
while ( (WCHAR)*CurrentChar != (WCHAR)'\0') {
|
|
*pl++ = (WCHAR)*CurrentChar++;
|
|
}
|
|
*pl = (WCHAR)'\0';
|
|
|
|
// Open override TIA file; so far - only in current dir
|
|
swprintf(OvrTiaFileFullPath, L"\\DosDevices\\%s\\%s", CurrentDir, OvrTiaFileName);
|
|
|
|
RtlInitUnicodeString(&OvrTiaFile, OvrTiaFileFullPath);
|
|
InitializeObjectAttributes(&oa, &OvrTiaFile, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
//Do not use synchronized access as it blocks forever in case of thread suspension
|
|
ret = NtCreateFile(&BtlpOverrideTiaFile, FILE_GENERIC_READ, &oa, &iosb, &as,
|
|
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BtlpOverrideTiaFile = INVALID_HANDLE_VALUE;
|
|
DBCODE(TRUE, BtlpPrintf("Can't open override TIA file %S: status=%X\n", OvrTiaFileFullPath, ret));
|
|
} else {
|
|
DBCODE(TRUE, BtlpPrintf("Override TIA will be loaded from file %S\n", OvrTiaFileFullPath));
|
|
}
|
|
}
|
|
#endif /* OVERRIDE_TIA */
|
|
else if ( *CurrentChar != '\0' && *CurrentChar != ';' ) {
|
|
// Add next control directive to the pseudo-argv array
|
|
argv[argc] = IniArgsP;
|
|
*IniArgsP++ = '-';
|
|
strcpy(IniArgsP, CurrentChar);
|
|
IniArgsP += (strlen (IniArgsP) + 1);
|
|
argc++; // We assume there are no extra chars, spaces, etc!!!!
|
|
}
|
|
|
|
// Next parameter
|
|
if (EndOfLineChar) {
|
|
CurrentChar = EndOfLineChar;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BtlpPrintf("Can't read INI file: status=%X\n", ret);
|
|
}
|
|
}
|
|
else {
|
|
DBCODE (TRUE, BtlpPrintf("Can't open INI file\n"));
|
|
}
|
|
}
|
|
#endif /* RELEASE */
|
|
|
|
DBCODE(TRUE, BtlpPrintf("\nCpuProcessInit: Unique ProcessId = 0x%I64X\n", BT_CURRENT_PROC_UID()));
|
|
|
|
// Locate, load and initialize IA32Exec.bin component
|
|
{
|
|
BT_STATUS_CODE BtStatus;
|
|
NTSTATUS status;
|
|
static char APITableName[] = "BtgAPITable";
|
|
static ANSI_STRING APITableString = { sizeof (APITableName) - 1,
|
|
sizeof (APITableName) - 1,
|
|
APITableName };
|
|
PVOID BtransAPITableStart;
|
|
|
|
//DBCODE (TRUE, BtlpPrintf ("\nLOADED: HANDLE=%p\n", BTGenericHandle));
|
|
assert (F_NOT_FOUND != fBTGenericFound);
|
|
assert (INVALID_HANDLE_VALUE != BTGenericHandle);
|
|
|
|
// Locate IA32Exec.bin API table
|
|
status = LdrGetProcedureAddress(BTGenericHandle, &APITableString, 0, &BtransAPITableStart);
|
|
if (status != STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
//DBCODE (TRUE, BtlpPrintf ("\nTABLE AT: %p, status=%X\n", BtransAPITableStart, status));
|
|
|
|
// Perform the API table initialization
|
|
BtlInitializeTables(BtransAPITableStart);
|
|
|
|
// initialize IA32Exec.bin placeholder table
|
|
{
|
|
extern PLABEL_PTR_TYPE __imp_setjmp;
|
|
extern PLABEL_PTR_TYPE __imp_longjmp;
|
|
BtlAPITable.APITable[IDX_BTLIB_SETJMP].PLabelPtr = __imp_setjmp;
|
|
BtlAPITable.APITable[IDX_BTLIB_LONGJMP].PLabelPtr = __imp_longjmp;
|
|
// DBCODE (FALSE, BtlpPrintf ("SETJMP: %p [%p %p] = %p [%p %p]",
|
|
// BtlAPITable.APITable[IDX_BTLIB_SETJMP].PLabelPtr,
|
|
// ((VOID **)(BtlAPITable.APITable[IDX_BTLIB_SETJMP].PLabelPtr))[0],
|
|
// ((VOID **)(BtlAPITable.APITable[IDX_BTLIB_SETJMP].PLabelPtr))[1],
|
|
// __imp_setjmp,
|
|
// ((VOID **)__imp_setjmp)[0],
|
|
// ((VOID **)__imp_setjmp)[1]);
|
|
// BtlpPrintf ("LONGJMP: %p [%p %p] = %p [%p %p]",
|
|
// BtlAPITable.APITable[IDX_BTLIB_LONGJMP].PLabelPtr,
|
|
// ((VOID **)(BtlAPITable.APITable[IDX_BTLIB_LONGJMP].PLabelPtr))[0],
|
|
// ((VOID **)(BtlAPITable.APITable[IDX_BTLIB_LONGJMP].PLabelPtr))[1],
|
|
// __imp_longjmp,
|
|
// ((VOID **)__imp_longjmp)[0],
|
|
// ((VOID **)__imp_longjmp)[1]));
|
|
}
|
|
|
|
BtStatus = BTGENERIC_START(&BtlAPITable,
|
|
(ULONG_PTR)BTGenericHandle,
|
|
(ULONG_PTR)BTGenericHandle + BtlpExtractNTHeader(BTGenericHandle)->OptionalHeader.SizeOfImage,
|
|
BT_TLS_OFFSET,
|
|
&BtlpInfoOffset,
|
|
&BtlpGenericIA32ContextOffset);
|
|
if (BtStatus != BT_STATUS_SUCCESS) {
|
|
return (BtlpBt2NtStatusCode(BtStatus));
|
|
}
|
|
//DBCODE(TRUE, BtlpPrintf ("BTGENERIC_START returned size 0x%X\n", BtlpInfoOffset));
|
|
//DBCODE(TRUE, BtlpPrintf ("IA32Exec.bin will supply IA32 context on offset 0x%X\n", BtlpGenericIA32ContextOffset));
|
|
BtlpInfoOffset = (BtlpInfoOffset + BTLIB_INFO_ALIGNMENT - 1)&~((U32)(BTLIB_INFO_ALIGNMENT - 1));
|
|
//DBCODE(TRUE, BtlpPrintf ("Offset 0x%X\n", BtlpInfoOffset));
|
|
* pCpuThreadDataSize = BtlpInfoOffset + BTLIB_INFO_SIZE;
|
|
//DBCODE(TRUE, BtlpPrintf ("ProcessInit reports 0x%I64X\n",* pCpuThreadDataSize));
|
|
|
|
#ifdef OVERRIDE_TIA
|
|
if ( BtlpOverrideTiaFile != INVALID_HANDLE_VALUE ) {
|
|
unsigned char OvrTiaBuffer[0xffff],
|
|
* OverrideTIAData = (unsigned char *)NULL;
|
|
int ret;
|
|
|
|
ret = NtReadFile(BtlpOverrideTiaFile, NULL, NULL, NULL, &iosb, (VOID *)OvrTiaBuffer, 0xffff, &offst, NULL);
|
|
if ( ret == STATUS_SUCCESS || ret == STATUS_END_OF_FILE ) {
|
|
// File size is in iosb.Information
|
|
unsigned int OvrTiaSize = (unsigned int)iosb.Information;
|
|
|
|
DBCODE(TRUE, BtlpPrintf("Override TIA loaded successfully\n"));
|
|
OverrideTIAData = (unsigned char *)BtlMemoryAlloc(NULL, OvrTiaSize, MEM_READ|MEM_WRITE);
|
|
|
|
assert(OverrideTIAData != (unsigned char *)NULL);
|
|
memcpy(OverrideTIAData, OvrTiaBuffer, OvrTiaSize);
|
|
BTGENERIC_USE_OVERRIDE_TIA(OvrTiaSize, OverrideTIAData);
|
|
NtClose(BtlpOverrideTiaFile);
|
|
} else {
|
|
DBCODE(TRUE, BtlpPrintf("Override TIA data couldn't be loaded - read error!\n"));
|
|
}
|
|
}
|
|
#endif /* OVERRIDE_TIA */
|
|
|
|
BtStatus = BTGENERIC_DEBUG_SETTINGS(argc, argv);
|
|
if (BtStatus != BT_STATUS_SUCCESS) {
|
|
return (BtlpBt2NtStatusCode(BtStatus));
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(ProcessTerm)(
|
|
HANDLE hProcess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Per-process termination code. Note that this routine may not be called,
|
|
especially if the process is terminated by another process.
|
|
|
|
Arguments:
|
|
|
|
Process ID or 0.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
// NtTerminateProcess called
|
|
DBCODE (FALSE, BtlpPrintf ("\nCalled NtTerminateProcess (Handle=0x%X, Code=0x%X)\n",
|
|
((U32 *)UlongToPtr((BTLIB_CONTEXT_IA32_PTR()->Esp)))[1],
|
|
((U32 *)UlongToPtr((BTLIB_CONTEXT_IA32_PTR()->Esp)))[2]));
|
|
// Consider which call is it
|
|
switch (((U32 *)UlongToPtr((BTLIB_CONTEXT_IA32_PTR()->Esp)))[1]) {
|
|
case 0: // Prepare to terminate
|
|
BTGENERIC_NOTIFY_PREPARE_EXIT ();
|
|
break;
|
|
case NtCurrentProcess(): // Actually terminate
|
|
BTGENERIC_NOTIFY_EXIT ();
|
|
if ( BtlpWow64LogFile != INVALID_HANDLE_VALUE) {
|
|
NtClose(BtlpWow64LogFile);
|
|
}
|
|
break;
|
|
default:
|
|
assert (!"Should not get to here!");
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// IA32 JMPE instruction encoding
|
|
#pragma code_seg(".text")
|
|
static struct JMPECode {
|
|
U32 Align4;
|
|
U8 Align2[2];
|
|
U8 OpCode[2];
|
|
U32 CodeAddress;
|
|
U32 GPAddress;
|
|
} __declspec(allocate(".text")) const BtlpWow64JMPECode = {
|
|
0,
|
|
{0, 0},
|
|
{0x0f, 0xb8}, // 0F B8
|
|
0, // "code address"
|
|
0 // "GP address"
|
|
};
|
|
#define WOW64_JMPE ((VOID *)&(BtlpWow64JMPECode.OpCode)) // start of JMPE instruction
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(ThreadInit)(
|
|
PVOID pPerThreadData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Per-thread initialization code.
|
|
|
|
Arguments:
|
|
|
|
pPerThreadData - Pointer to zero-filled per-thread data with the
|
|
size returned from CpuProcessInit.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
BT_STATUS_CODE BtStatus;
|
|
NTSTATUS status;
|
|
PTEB pTEB;
|
|
PTEB32 pTEB32;
|
|
HANDLE ThreadHandle;
|
|
|
|
{
|
|
// Scan the workarea pages from top to bottom, so that higher addresses
|
|
// are accessed first. This enables correct handling of the IA64 stack
|
|
// guard page (workarea is actually allocated from IA64 stack).
|
|
char * ByteP = (char *)pPerThreadData + BtlpInfoOffset + BTLIB_INFO_SIZE;
|
|
*--ByteP = 0;
|
|
while (((ULONG_PTR)ByteP -= BtlMemoryPageSize ()) >= (ULONG_PTR)pPerThreadData) {
|
|
*ByteP = 0;
|
|
}
|
|
*(char *)pPerThreadData = 0;
|
|
}
|
|
pTEB = BT_CURRENT_TEB();
|
|
pTEB32 = BT_TEB32_OF(pTEB);
|
|
pTEB32->WOW32Reserved = (ULONG) ((UINT_PTR) WOW64_JMPE); // Below 4G always!
|
|
|
|
BTLIB_INIT_BLOCKED_LOG_FLAG();
|
|
|
|
#ifndef NODEBUG
|
|
if ( BtlpLogFilePerThread ) {
|
|
HANDLE hTemp;
|
|
WCHAR LogFileFullName[1024];
|
|
int ret;
|
|
static int SeqNum = 0;
|
|
UNICODE_STRING LogFileName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER AllocSz = { 0, 0 };
|
|
|
|
// Eric: Incrementing SeqNum should be replace with an atomatic operation in the future.
|
|
swprintf(LogFileFullName, L"%s.%x.log", BtlpLogFileFullPath, SeqNum++);
|
|
RtlInitUnicodeString(&LogFileName, LogFileFullName);
|
|
InitializeObjectAttributes(&ObjectAttributes, &LogFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ret = NtCreateFile(&hTemp, FILE_GENERIC_WRITE,
|
|
&ObjectAttributes, &IoStatusBlock,
|
|
&AllocSz, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_SUPERSEDE,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
BTLIB_SET_LOG_FILE(INVALID_HANDLE_VALUE);
|
|
BtlpPrintf("Can't create LOG file %S: status=%X\n", LogFileFullName, ret);
|
|
} else {
|
|
BTLIB_SET_LOG_FILE(hTemp);
|
|
}
|
|
BTLIB_SET_LOG_OFFSET(0);
|
|
}
|
|
#endif
|
|
status = NtDuplicateObject(NtCurrentProcess(),
|
|
NtCurrentThread(),
|
|
NtCurrentProcess(),
|
|
&ThreadHandle,
|
|
( THREAD_GET_CONTEXT
|
|
| THREAD_SET_CONTEXT
|
|
| THREAD_QUERY_INFORMATION
|
|
| THREAD_SET_INFORMATION
|
|
| THREAD_SUSPEND_RESUME),
|
|
0,
|
|
DUPLICATE_SAME_ATTRIBUTES
|
|
);
|
|
if (status == STATUS_SUCCESS) {
|
|
BTLIB_SET_EXTERNAL_HANDLE(ThreadHandle);
|
|
|
|
DBCODE(TRUE, BtlpPrintf("\nCpuThreadInit: TEB=%p GLST=%p\n", pTEB, BT_TLS_OF(pTEB)));
|
|
|
|
BtStatus = BTGENERIC_THREAD_INIT(pPerThreadData, 0 , pTEB32);
|
|
if (BtStatus != BT_STATUS_SUCCESS) {
|
|
DBCODE(TRUE, BtlpPrintf("\nCpuThreadInit: Failed to initialize, BtStatus=%d\n", BtStatus));
|
|
DBCODE(BtlpLogFilePerThread, NtClose(BTLIB_LOG_FILE()));
|
|
NtClose(BTLIB_EXTERNAL_HANDLE());
|
|
NtTerminateThread(NtCurrentThread(),BtlpBt2NtStatusCode(BtStatus));
|
|
return BtlpBt2NtStatusCode(BtStatus);
|
|
}
|
|
}
|
|
else {
|
|
BTLP_REPORT_NT_FAILURE("NtDuplicateObject", status);
|
|
DBCODE(BtlpLogFilePerThread, NtClose(BTLIB_LOG_FILE()));
|
|
NtTerminateThread(NtCurrentThread(), status);
|
|
return status;
|
|
}
|
|
|
|
// Initialize thread context
|
|
BtlpInitIA32Context(BTLIB_CONTEXT_IA32_PTR(), pTEB32);
|
|
|
|
//if everithing is Ok, initialize shared info and sign it with BTL_SIGNATURE
|
|
status = BtlpInitSharedInfo();
|
|
return status;
|
|
}
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(ThreadTerm)(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Per-thread termination code. Note that this routine may not be called,
|
|
especially if the thread is terminated abnormally.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
DBCODE(TRUE, BtlpPrintf("\nCpuThreadTerm: TEB=%p GLST=%p)\n", BT_CURRENT_TEB(), BT_CURRENT_TLS()));
|
|
|
|
BTGENERIC_THREAD_TERMINATED ();
|
|
DBCODE(BtlpLogFilePerThread, NtClose(BTLIB_LOG_FILE()));
|
|
NtClose(BTLIB_EXTERNAL_HANDLE());
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
WOW64BT_IMPL VOID BTAPI(NotifyDllLoad)(
|
|
LPWSTR DllName,
|
|
PVOID DllBase,
|
|
ULONG DllSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine get notified when application successfully load a dll.
|
|
|
|
Arguments:
|
|
|
|
DllName - Name of the Dll the application has loaded.
|
|
DllBase - BaseAddress of the dll.
|
|
DllSize - size of the Dll.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DBCODE (TRUE, BtlpPrintf ("\nModule %S loaded, base=0x%p, length=0x%08X\n", DllName, DllBase, DllSize));
|
|
BTGENERIC_REPORT_LOAD(DllBase, DllSize, DllName);
|
|
}
|
|
|
|
WOW64BT_IMPL VOID BTAPI(NotifyDllUnload)(
|
|
PVOID DllBase
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine get notified when application unload a dll.
|
|
|
|
Arguments:
|
|
|
|
DllBase - BaseAddress of the dll.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DBCODE (TRUE, BtlpPrintf ("\nModule unloaded, base=0x%p", DllBase));
|
|
BTGENERIC_REPORT_UNLOAD(DllBase, 0 /* ? */, "UNKNOWN" /* ? */);
|
|
}
|
|
|
|
WOW64BT_IMPL VOID BTAPI(FlushInstructionCache)(
|
|
#ifndef BUILD_3604
|
|
IN HANDLE ProcessHandle,
|
|
#endif
|
|
IN PVOID BaseAddress,
|
|
IN ULONG Length,
|
|
IN WOW64_FLUSH_REASON Reason
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify IA32Exec.bin that the specified range of addresses has become invalid,
|
|
since some external code has altered whole or part of this range
|
|
|
|
Arguments:
|
|
|
|
BaseAddress - start of range to flush
|
|
Length - number of bytes to flush
|
|
Reason - reason of the flush
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BT_FLUSH_REASON BtFlushReason;
|
|
DBCODE (TRUE,
|
|
BtlpPrintf ("\n CpuFlushInstructionCache(BaseAddress=0x%p, Length=0x%X, Reason=%d)\n",
|
|
BaseAddress, (DWORD)Length, Reason));
|
|
#ifndef BUILD_3604
|
|
if (!BtlpIsCurrentProcess(ProcessHandle)) {
|
|
DBCODE (TRUE, BtlpPrintf ("\nDifferent process' handle=0x%p - rejected for now", ProcessHandle));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
BtFlushReason = BtlpWow2BtFlushReason(Reason);
|
|
|
|
if ((Reason == WOW64_FLUSH_FREE) && (Length == 0)) {
|
|
//In case of NtFreeVirtualMemory(..., MEM_RELEASE) , the Length parameter is zero.
|
|
//Calculate real region size to be freed.
|
|
SIZE_T RegionSize;
|
|
BOOL IsCommited;
|
|
RegionSize = BtlpGetMemAllocSize(BaseAddress, &IsCommited);
|
|
//Report zero legth for already decommited region - do not need flush in this case
|
|
if (IsCommited) {
|
|
Length = (U32)RegionSize;
|
|
if (RegionSize > (SIZE_T)Length) {
|
|
//region size is too big, return max. U32 value aligned to the page size
|
|
Length = (U32)(-(int)BtlMemoryPageSize());
|
|
}
|
|
}
|
|
}
|
|
|
|
BTGENERIC_FLUSH_IA32_INSTRUCTION_CACHE(BaseAddress, Length, BtFlushReason);
|
|
}
|
|
|
|
|
|
// Suspension handling
|
|
// SUSPENSION SYNCHRONIZATION RULES:
|
|
// a) there are two suspension commands:
|
|
// BtlSuspendThread - internal IA-32 Execution Layer function to suspend in-process threads
|
|
// CpuSuspendThread - called by WOW64 when simulating the system call.
|
|
// b) WOW64 guarantees that call to CpuSuspendThread is protected by a machine-wide mutex
|
|
// c) IA32Exec.bin guarantees that BtlSuspendThread call is protected by a process-wide mutex and
|
|
// suspension in the middle of the call is disabled
|
|
// d) IA32Exec.bin guarantees that target thread can not call to the CpuThreadSuspend function
|
|
// while the BtlSuspendThread(target_thread) function executes. It can be done by
|
|
// closing "simulation gates" before call to BtlSuspendThread.
|
|
//
|
|
// Conclusions:
|
|
// a) while a thread executes suspension command, it can not be suspended by
|
|
// another thread.
|
|
// b) there are at most two concurrent suspension function calls for the same target
|
|
// thread: internal and simulated.
|
|
|
|
|
|
static NTSTATUS BtlpEnsureSuspensionConsistency (
|
|
IN HANDLE ThreadHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB pTEB,
|
|
IN U32 TryCounter,
|
|
IN OUT PULONG PreviousSuspendCountP)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper function for consistent suspension of the thread
|
|
Called when the thread is suspended, and it is guaranteed that the thread
|
|
is not the current one
|
|
Arguments:
|
|
|
|
ThreadHandle - IN thread's handle
|
|
ProcessHandle - IN process' handle
|
|
pTEB - IN pointer to thread's TEB
|
|
TryCounter - IN maximum attempts counter (0 means infinity)
|
|
PreviousSuspendCountP - IN OUT pointer to thread's previous suspend count.
|
|
The referenced value is updated if the function succeeds
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
U32 NumAttempts = 1;
|
|
ULONG PrevSuspended = *PreviousSuspendCountP;
|
|
PVOID GlstP;
|
|
BOOL IsLocal;
|
|
|
|
|
|
//Is this a local suspend function?
|
|
IsLocal = BtlpIsCurrentProcess(ProcessHandle);
|
|
|
|
// Temporary workaround for IA32 debugging support.
|
|
//When debugger calls SuspendThread, the remote thread is blocked
|
|
//(by kernel???) even after NtResumeThread, so an attempt to establish
|
|
//a handshake protocol with this thread fails.
|
|
//To be removed after MS fix.
|
|
if (!IsLocal) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//Get TLS pointer of the target thread
|
|
GlstP = BtlpGetTlsPtr(ProcessHandle, pTEB, IsLocal);
|
|
if (GlstP == NULL) {
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
for (;;) { //While target thread is not suspended in a consistent state
|
|
BT_THREAD_SUSPEND_STATE CanonizeResult;
|
|
CONTEXT ContextIA64;
|
|
BTLIB_SHARED_INFO_TYPE SharedInfo;
|
|
|
|
// First retrieve ContextIA64 and tnen get the shared info of the target thread.
|
|
// The order is important! It guarantees that either:
|
|
// a) we got ContextIA64 before context change in a concurrent SUSPENSION_REQUEST or
|
|
// b) BTLIB_SI_HAS_SUSPEND_REQUEST(&SharedInfo) == TRUE
|
|
|
|
ContextIA64.ContextFlags = CONTEXT_FULL;
|
|
status = NtGetContextThread(ThreadHandle, &ContextIA64);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtGetContextThread", status);
|
|
break;
|
|
}
|
|
status = BtlpReadSharedInfo(ProcessHandle, GlstP, IsLocal, &SharedInfo);
|
|
if (status != STATUS_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
//Check suspension conditions:
|
|
//a)do not suspend thread if SUSPENSION_DISABLED or in the middle of
|
|
// another SUSPEND_REQUEST
|
|
//b)ask IA32Exec.bin in all other cases
|
|
if (BTLIB_SI_SUSPENSION_DISABLED(&SharedInfo) ||
|
|
(BTLIB_SI_HAS_SUSPEND_REQUEST(&SharedInfo))) {
|
|
// suspension is currently impossible. Resume and try again
|
|
;
|
|
}
|
|
else {
|
|
//(BTLIB_SI_HAS_SUSPEND_REQUEST(&SharedInfo) == FALSE, so....
|
|
//Although real value of the BTLIB_SI_HAS_SUSPEND_REQUEST can be changed
|
|
//by a concurrent SUSPENSION_REQUEST, ContextIA64 has been taken before
|
|
//start of the SUSPENSION_REQUEST. If this is a case
|
|
//(there is a concurrent SUSPENSION_REQUEST), we will come up with
|
|
//( PrevSuspended > 0) && (CanonizeResult == BAD_SUSPEND_STATE).
|
|
//It means that we will wait for completion of the SUSPENSION_REQUEST,
|
|
//which is Ok.
|
|
|
|
// ask IA32Exec.bin to canonize context.Notice, if PrevSuspended>0, IA32Exec.bin
|
|
// only checks possibility of canonization but does not cahange thing
|
|
// in the thread context
|
|
CanonizeResult = (IsLocal ?
|
|
BTGENERIC_CANONIZE_SUSPEND_CONTEXT(GlstP, &ContextIA64, PrevSuspended) :
|
|
BTGENERIC_CANONIZE_SUSPEND_CONTEXT_REMOTE(ProcessHandle, GlstP, &ContextIA64, PrevSuspended));
|
|
|
|
// Analyze canonization result
|
|
if (CanonizeResult == SUSPEND_STATE_CONSISTENT) {
|
|
status = STATUS_SUCCESS; //confirm suspension
|
|
break;
|
|
}
|
|
else if (CanonizeResult == SUSPEND_STATE_CANONIZED) {
|
|
//BTGENERIC_CANONIZE_SUSPEND_CONTEXT guarantees that
|
|
//(CanonizeResult == SUSPEND_STATE_CANONIZED) -> ( PrevSuspended == 0)
|
|
assert( PrevSuspended == 0);
|
|
//record updated IA64 state
|
|
status = NtSetContextThread(ThreadHandle, &ContextIA64);
|
|
BTLP_REPORT_NT_FAILURE("NtSetContextThread", status);
|
|
break;
|
|
}
|
|
else if (CanonizeResult == SUSPEND_STATE_READY_FOR_CANONIZATION) {
|
|
//target thread is ready to canonize itself and exit simulation;
|
|
//lets send SUSPEND_REQUEST that will suspend the target thread
|
|
//just on the simulation exit
|
|
|
|
//BTGENERIC_CANONIZE_SUSPEND_CONTEXT guarantees that
|
|
//(CanonizeResult == SUSPEND_STATE_READY_FOR_CANONIZATION) -> ( PrevSuspended == 0)
|
|
assert( PrevSuspended == 0);
|
|
//the (PrevSuspended == 0) && !(BTLIB_SI_HAS_SUSPEND_REQUEST)
|
|
//condition guarantees serialization of SUSPEND_REQUESTs
|
|
//targeted to the same thread.
|
|
status = BtlpSendSuspensionRequest(ProcessHandle,
|
|
ThreadHandle,
|
|
GlstP,
|
|
IsLocal,
|
|
&ContextIA64,
|
|
&PrevSuspended);
|
|
break;
|
|
}
|
|
else if (CanonizeResult == SUSPEND_STATE_INACCESIBLE) {
|
|
//fatal error
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
else {
|
|
assert(CanonizeResult == BAD_SUSPEND_STATE);
|
|
// suspension is currently impossible. Resume and try again
|
|
}
|
|
}
|
|
if ((TryCounter != 0) && (NumAttempts >= TryCounter)) {
|
|
// TryCounter reached
|
|
status = STATUS_UNSUCCESSFUL; //reject suspension
|
|
break;
|
|
}
|
|
|
|
//Next attempt
|
|
status = NtResumeThread(ThreadHandle, NULL);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtResumeThread", status);
|
|
break;
|
|
}
|
|
|
|
DBCODE(TRUE, BtlpPrintf ("\n%s BtlpEnsureSuspensionConsistency: canonization failed, yield... ,"
|
|
"Target TEB = %p Caller TEB = %p PreviousSuspendCount=0x%I64X NumAttempts = %d\n",
|
|
(IsLocal ? "Local" : "Remote"), pTEB, BT_CURRENT_TEB(), PrevSuspended, NumAttempts));
|
|
|
|
NtYieldExecution ();
|
|
/*
|
|
{
|
|
LARGE_INTEGER DelayInterval = { -10000*1000, -1 }; // negative: 1000ms * 10000
|
|
NtDelayExecution (FALSE, &DelayInterval);
|
|
}
|
|
*/
|
|
// Stop the thread again
|
|
status = NtSuspendThread(ThreadHandle, &PrevSuspended);
|
|
// While the thread executes this function, it can not be suspended in a consistent
|
|
// state. It is guaranteed by the SUSPENSION SYNCHRONIZATION RULES(see above).
|
|
// It means that target thread, which is now in a non-consistent state,
|
|
// can not be in unintentionally "frozen" by a third thread.
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtSuspendThread", status);
|
|
break;
|
|
}
|
|
++NumAttempts;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
*PreviousSuspendCountP = PrevSuspended;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(SuspendThread)(
|
|
HANDLE ThreadHandle,
|
|
HANDLE ProcessHandle,
|
|
PTEB pTEB,
|
|
PULONG PreviousSuspendCountP)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is entered while the target thread is actually suspended, however, it's
|
|
not known if the target thread is in a consistent IA32 state.
|
|
It attempts to produce consistent IA32 state, and if unsuccessful, tries to
|
|
temporarily resume and then suspend the target thread again.
|
|
In wow64 call to this function is protected by a machine-wide mutex
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Handle of target thread to suspend
|
|
ProcessHandle - Handle of target thread's process
|
|
pTEB - Address of the target thread's TEB
|
|
PreviousSuspendCount - Previous suspend count
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//Do no block logging while the target thread is suspended in a non-consistent state
|
|
//Probably it is suspended in the middle of a logging
|
|
BTLIB_DISABLE_BLOCKED_LOG();
|
|
|
|
DBCODE(TRUE, BtlpPrintf ("\n%s CpuSuspendThread started: "
|
|
"Target TEB = %p Caller TEB = %p PreviousSuspendCount = 0x%lX\n",
|
|
(BtlpIsCurrentProcess(ProcessHandle) ? "Local" : "Remote"),
|
|
pTEB, BT_CURRENT_TEB(), *PreviousSuspendCountP));
|
|
|
|
status = BtlpEnsureSuspensionConsistency (
|
|
ThreadHandle,
|
|
ProcessHandle,
|
|
pTEB,
|
|
0, // INFINITE
|
|
PreviousSuspendCountP);
|
|
|
|
DBCODE(TRUE, BtlpPrintf ("\n%s CpuSuspendThread %s: "
|
|
"Target TEB = %p Caller TEB = %p PreviousSuspendCount = 0x%lX\n",
|
|
(BtlpIsCurrentProcess(ProcessHandle) ? "Local" : "Remote"),
|
|
((status == STATUS_SUCCESS) ? "completed successfully" : "failed"),
|
|
pTEB, BT_CURRENT_TEB(), *PreviousSuspendCountP));
|
|
|
|
BTLIB_ENABLE_BLOCKED_LOG();
|
|
|
|
return status;
|
|
}
|
|
|
|
BT_STATUS_CODE BtlSuspendThread(
|
|
IN U64 ThreadId,
|
|
IN U32 TryCounter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Suspend IA-32 Execution Layer thread for internal needs.
|
|
Caller of the BtlSuspendThread function must guarantee that target
|
|
thread can not call to the suspension function (CpuThreadSuspend or BtlSuspendThread)
|
|
while this function executes.
|
|
|
|
Arguments:
|
|
|
|
ThreadId - IN Thread ID
|
|
TryCounter - IN Maximum number of attempts to suspend thread or 0 (INFINITY)
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS_CODE
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE ThreadHandle;
|
|
ULONG PreviousSuspendCount;
|
|
|
|
DBCODE(TRUE, BtlpPrintf ("\nBtlSuspendThread started: Target TEB = %p Caller TEB = %p TryCounter=%d\n",
|
|
(PTEB)ThreadId, BT_CURRENT_TEB(), TryCounter));
|
|
|
|
// Remember, we use TEB address as a thread ID
|
|
assert (ThreadId != (U64)BT_CURRENT_TEB());
|
|
|
|
ThreadHandle = BTLIB_EXTERNAL_HANDLE_OF((PTEB)ThreadId);
|
|
|
|
//Do no block logging while the target thread is suspended in a non-consistent state
|
|
//Probably it is suspended in the middle of a logging
|
|
BTLIB_DISABLE_BLOCKED_LOG();
|
|
|
|
status = NtSuspendThread(ThreadHandle, &PreviousSuspendCount);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
status = BtlpEnsureSuspensionConsistency (
|
|
ThreadHandle,
|
|
NtCurrentProcess(),
|
|
(PTEB)ThreadId,
|
|
TryCounter,
|
|
&PreviousSuspendCount);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
NtResumeThread(ThreadHandle, NULL);
|
|
}
|
|
}
|
|
|
|
BTLIB_ENABLE_BLOCKED_LOG();
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\nBtlSuspendThread %s : Target TEB = %p Caller TEB = %p PreviousSuspendCount = 0x%lX\n",
|
|
((status == STATUS_SUCCESS) ? "completed successfully" : "failed"),
|
|
(PTEB)ThreadId, BT_CURRENT_TEB(), PreviousSuspendCount));
|
|
|
|
return ((status == STATUS_SUCCESS) ? BT_STATUS_SUCCESS : BT_STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
BT_STATUS_CODE BtlResumeThread(
|
|
IN U64 ThreadId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resume IA-32 Execution Layer thread suspended for internal needs
|
|
|
|
Arguments:
|
|
|
|
ThreadId - IN Thread ID
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS_CODE
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE ThreadHandle;
|
|
ULONG PreviousSuspendCount;
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\nBtlResumeThread started: Target TEB = %p Caller TEB = %p\n",
|
|
(PTEB)ThreadId, BT_CURRENT_TEB()));
|
|
|
|
// Remember, we use TEB address as a thread ID
|
|
assert (ThreadId != (U64)BT_CURRENT_TEB());
|
|
|
|
ThreadHandle = BTLIB_EXTERNAL_HANDLE_OF((PTEB)ThreadId);
|
|
status = NtResumeThread(ThreadHandle, &PreviousSuspendCount);
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\nBtlResumeThread %s : Target TEB = %p Caller TEB = %p PreviousSuspendCount = 0x%lX\n",
|
|
((status == STATUS_SUCCESS) ? "completed successfully" : "failed"),
|
|
(PTEB)ThreadId, BT_CURRENT_TEB(), PreviousSuspendCount));
|
|
|
|
return ((status == STATUS_SUCCESS) ? BT_STATUS_SUCCESS : BT_STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
|
|
// Exceptions handling
|
|
|
|
WOW64BT_IMPL VOID BTAPI(ResetToConsistentState)(
|
|
PEXCEPTION_POINTERS pExceptionPointers
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
After an exception occurs, WOW64 calls this routine to give the CPU
|
|
a chance to clean itself up and recover the CONTEXT32 at the time of
|
|
the fault.
|
|
The function also has to fill in BTLIB_SIM_EXIT_INFO to be eventually
|
|
analyzed/handled by the exception filter/handler of the BTGENERIC_RUN
|
|
function.
|
|
|
|
CpuResetToConsistantState() needs to:
|
|
|
|
0) Check if the exception was from ia32 or ia64
|
|
|
|
If exception was ia64, do nothing and return
|
|
If exception was ia32, needs to:
|
|
1) Needs to copy CONTEXT eip to the TLS (WOW64_TLS_EXCEPTIONADDR)
|
|
2) reset the CONTEXT struction to be a valid ia64 state for unwinding
|
|
this includes:
|
|
2a) reset CONTEXT ip to a valid ia64 ip (usually
|
|
the destination of the jmpe)
|
|
2b) reset CONTEXT sp to a valid ia64 sp (TLS
|
|
entry WOW64_TLS_STACKPTR64)
|
|
2c) reset CONTEXT gp to a valid ia64 gp
|
|
2d) reset CONTEXT teb to a valid ia64 teb
|
|
2e) reset CONTEXT psr.is (so exception handler runs as ia64 code)
|
|
|
|
|
|
Arguments:
|
|
|
|
pExceptionPointers - 64-bit exception information
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BT_EXCEPTION_RECORD BtExceptRecord;
|
|
BT_EXCEPTION_CODE BtExceptCode;
|
|
|
|
DBCODE (TRUE,
|
|
BtlpPrintf ("\n CpuResetToConsistentState (pExceptionPointers=0x%016I64X) started\n", pExceptionPointers);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: TEB=0x%I64X", BT_CURRENT_TEB());
|
|
BtlpPrintf ("\n CpuResetToConsistentState: TEB32=0x%I64X", BT_CURRENT_TEB32());
|
|
|
|
BtlpPrintf ("\n CpuResetToConsistentState: ExceptionCode=0x%X", pExceptionPointers->ExceptionRecord->ExceptionCode);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: ExceptionAddress=0x%I64X", pExceptionPointers->ExceptionRecord->ExceptionAddress);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: ExceptionFlags=0x%X", pExceptionPointers->ExceptionRecord->ExceptionFlags);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: NumberParameters=0x%X", pExceptionPointers->ExceptionRecord->NumberParameters);
|
|
{
|
|
unsigned int n;
|
|
for (n = 0; n < pExceptionPointers->ExceptionRecord->NumberParameters; ++n) {
|
|
BtlpPrintf ("\n CpuResetToConsistentState: ExceptionInformation[%d]=0x%I64X",
|
|
n, pExceptionPointers->ExceptionRecord->ExceptionInformation[n]);
|
|
}
|
|
}
|
|
|
|
BtlpPrintf ("\n\n CpuResetToConsistentState: ContextFlags: 0x%X", pExceptionPointers->ContextRecord->ContextFlags);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: StIIP =0x%I64X", pExceptionPointers->ContextRecord->StIIP);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: StIPSR=0x%I64X", pExceptionPointers->ContextRecord->StIPSR);
|
|
BtlpPrintf ("\n CpuResetToConsistentState: IntSp =0x%I64X", pExceptionPointers->ContextRecord->IntSp);
|
|
|
|
BtlpPrintf ("\n *** BRET=0x%016I64X", pExceptionPointers->ContextRecord->BrRp);
|
|
BtlpPrintf ("\n *** GP=0x%016I64X", pExceptionPointers->ContextRecord->IntGp);
|
|
BtlpPrintf ("\n *** SP=0x%016I64X", pExceptionPointers->ContextRecord->IntSp);
|
|
BtlpPrintf ("\n *** PREDS=0x%016I64X", pExceptionPointers->ContextRecord->Preds);
|
|
BtlpPrintf ("\n *** AR.PFS=0x%016I64X", pExceptionPointers->ContextRecord->RsPFS);
|
|
BtlpPrintf ("\n *** AR.BSP=0x%016I64X", pExceptionPointers->ContextRecord->RsBSP);
|
|
BtlpPrintf ("\n *** AR.BSPSTORE=0x%016I64X", pExceptionPointers->ContextRecord->RsBSPSTORE);
|
|
BtlpPrintf ("\n *** AR.RSC=0x%016I64X", pExceptionPointers->ContextRecord->RsRSC);
|
|
);
|
|
|
|
//Reconstruct consistent IA32 state
|
|
BtlpNt2BtExceptRecord (pExceptionPointers->ExceptionRecord, &BtExceptRecord);
|
|
BtExceptCode = BTGENERIC_IA32_CANONIZE_CONTEXT(BT_CURRENT_TLS(),
|
|
pExceptionPointers->ContextRecord,
|
|
&BtExceptRecord);
|
|
//BtExceptCode determines appearance of the exception in the simulated application:
|
|
//BT_NO_EXCEPT - ignore exception, otherwise raise exception with the returned code
|
|
|
|
//Fill in BTLIB_SIM_EXIT_INFO to be eventually analyzed/handled by the exception
|
|
//filter/handler of the BTGENERIC_RUN function.
|
|
if (BTLIB_INSIDE_CPU_SIMULATION()) {
|
|
// Exception occured during code simulation in the BTGENERIC_RUN function
|
|
if (BtExceptCode == BtExceptRecord.ExceptionCode) {
|
|
//IA32Exec.bin decided to raise exception as is.
|
|
if (BeingDebugged) {
|
|
// External debugger receives exception event before it is
|
|
// locally processed by the CpuResetToConsistentState function.
|
|
// In this case, BTLib silences current exception and re-raises an exact
|
|
// copy of this exception just after state canonization.
|
|
|
|
// Copy IA64 exception record to be used to re-raise the exception
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_IA64_EXCEPTION_CODE;
|
|
BTLIB_SIM_EXIT_INFO_PTR()->u.IA64Exception.ExceptionRecord =
|
|
*(pExceptionPointers->ExceptionRecord);
|
|
/*
|
|
BTLIB_SIM_EXIT_INFO_PTR()->u.IA64Exception.ExceptionContext =
|
|
*(pExceptionPointers->ContextRecord);
|
|
*/
|
|
}
|
|
else {
|
|
//Mark the exception as unhandled and pass it to higher-level exception handler
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_UNHANDLED_EXCEPTION_CODE;
|
|
}
|
|
}
|
|
else if (BtExceptCode == BT_NO_EXCEPT) {
|
|
//IA32Exec.bin decided to ignore exception. Restart code simulation.
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_RESTART_CODE;
|
|
}
|
|
else {
|
|
//IA32Exec.bin changed exception code, so wowIA32X.dll silences current exception
|
|
//and re-raises the new one.
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_EXCEPTION_CODE;
|
|
BTLIB_SIM_EXIT_INFO_PTR()->u.ExceptionRecord.ExceptionCode = BtExceptCode;
|
|
BTLIB_SIM_EXIT_INFO_PTR()->u.ExceptionRecord.ReturnAddr = BTLIB_CONTEXT_IA32_PTR()->Eip;
|
|
}
|
|
}
|
|
|
|
// Dump debug info for serious errors
|
|
DBCODE ((pExceptionPointers->ExceptionRecord->ExceptionCode & 0x80000000),
|
|
BTGENERIC_EXCEPTION_DEBUG_PRINT ());
|
|
|
|
BT_CURRENT_TEB()->TlsSlots[4] = (VOID *)((UINT_PTR) BTLIB_CONTEXT_IA32_PTR()->Eip);
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\n CpuResetToConsistentState (pExceptionPointers=0x%p) completed\n", pExceptionPointers));
|
|
|
|
}
|
|
|
|
WOW64BT_IMPL VOID BTAPI(ResetFloatingPoint)(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by the wow layer when a floating point exception
|
|
is taken just before returning back to ia32 mode. It is used to reset
|
|
the fp state to a non error condition if needed before running
|
|
the ia32 exception handler.
|
|
For IA-32 Execution Layer, this function is nop, because all handling of FP exceptions
|
|
have already been done in CpuSimulate/CpuResetToConsistentState.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
|
|
|
|
int BtlpMajorFilterException(
|
|
IN LPEXCEPTION_POINTERS pEP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exception filter for IA64 exceptions occured in the BTGENERIC_RUN function while
|
|
simulating the IA32 code.
|
|
The filter decides whether to handle the exception or to continue unwinding.
|
|
Called after the ResetToConsistentState function reconstructed IA32 state and filled
|
|
in BTLIB_SIM_EXIT_INFO.
|
|
|
|
Arguments:
|
|
|
|
pEP - IN Exception pointers structure
|
|
|
|
Return Value:
|
|
|
|
Decision: EXCEPTION_EXECUTE_HANDLER or EXCEPTION_CONTINUE_SEARCH.
|
|
|
|
--*/
|
|
{
|
|
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
//BTLIB_SIM_EXIT_INFO has been filled in by the ResetToConsistentState function.
|
|
//The SIM_EXIT_UNHANDLED_EXCEPTION_CODE code stands for BT-unhandled exception,
|
|
//that should be passed to higher-level handlers.
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpMajorFilterException: Exception code = 0x%lx", pEP->ExceptionRecord->ExceptionCode));
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpMajorFilterException: Exception address = 0x%p\n", pEP->ExceptionRecord->ExceptionAddress));
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpMajorFilterException: %s exception\n",
|
|
((BTLIB_SIM_EXIT_INFO_PTR()->ExitCode == SIM_EXIT_UNHANDLED_EXCEPTION_CODE) ?
|
|
"Unhandled" : "BT-handled")));
|
|
|
|
return ((BTLIB_SIM_EXIT_INFO_PTR()->ExitCode == SIM_EXIT_UNHANDLED_EXCEPTION_CODE) ?
|
|
EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER);
|
|
}
|
|
|
|
// System service exception filter
|
|
|
|
int BtlpSystemServiceFilterException(
|
|
IN LPEXCEPTION_POINTERS pEP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exception filter for exceptions during Wow64 NT system services
|
|
|
|
Arguments:
|
|
|
|
pEP - IN Exception pointers structure
|
|
|
|
Return Value:
|
|
|
|
Decision: EXCEPTION_CONTINUE_SEARCH always.
|
|
|
|
--*/
|
|
{
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpSystemServiceFilterException: Exception code = 0x%lx", pEP->ExceptionRecord->ExceptionCode));
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpSystemServiceFilterException: Exception address = 0x%p\n", pEP->ExceptionRecord->ExceptionAddress));
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpSystemServiceFilterException: Excepted system service = 0x%X, called from Eip=0x%X with ESP=0x%X\n",
|
|
BTLIB_CONTEXT_IA32_PTR()->Eax, BTLIB_CONTEXT_IA32_PTR()->Eip, BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
__inline VOID BtlpExitSimulation(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Exit 32-bit code simulation by performing longjmp to the current
|
|
setjmp addr stored in TLS
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Never returns.
|
|
|
|
--*/
|
|
{
|
|
longjmp (BTLIB_SIM_JMPBUF(), 1);
|
|
}
|
|
|
|
VOID BtlpMajorExceptionHandler (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exception handler for IA64 exceptions occured in the BTGENERIC_RUN function while
|
|
simulating the IA32 code.
|
|
Called after the filter decides to handle the IA64 exception.
|
|
|
|
Arguments:
|
|
|
|
BtExceptRecordP - IN Pointer to BT exception record
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
//BTLIB_SIM_EXIT_INFO has been filled in by the ResetToConsistentState function.
|
|
//Just exit simulation to process BTLIB_SIM_EXIT_INFO
|
|
assert((BTLIB_SIM_EXIT_INFO_PTR()->ExitCode == SIM_EXIT_EXCEPTION_CODE) ||
|
|
(BTLIB_SIM_EXIT_INFO_PTR()->ExitCode == SIM_EXIT_RESTART_CODE) ||
|
|
(BTLIB_SIM_EXIT_INFO_PTR()->ExitCode == SIM_EXIT_IA64_EXCEPTION_CODE));
|
|
DBCODE (TRUE, BtlpPrintf ("\n BtlpMajorExceptionHandler: %s exception\n",
|
|
((BTLIB_SIM_EXIT_INFO_PTR()->ExitCode == SIM_EXIT_RESTART_CODE)? "Ignore" : "Raise")));
|
|
BtlpExitSimulation();
|
|
}
|
|
|
|
// IA32 simulation API
|
|
|
|
static VOID BtlpSimulate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Simulate 32-bit code using the current 32-bit context. On return,
|
|
BTLIB_EXIT_INFO is filled in with an appropriate simulation
|
|
exit code and data.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
The function does not return normally, but rather longjmps to the current setjmp addr
|
|
stored in TLS .
|
|
--*/
|
|
{
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_RESTART_CODE;
|
|
_try {
|
|
BTGENERIC_RUN ();
|
|
} _except (BtlpMajorFilterException(GetExceptionInformation())) {
|
|
BtlpMajorExceptionHandler ();
|
|
}
|
|
}
|
|
|
|
|
|
VOID BtlIA32LCall (
|
|
IN OUT BTGENERIC_IA32_CONTEXT * ia32context,
|
|
IN U32 returnAddress,
|
|
IN U32 targetAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exit IA32 code simulation to "execute" LCALL instruction (should not happen in NT)
|
|
|
|
Arguments:
|
|
|
|
ia32context - IA32 context. ia32context->Eip points to the LCALL instruction.
|
|
returnAddress - return address of the LCALL instruction.
|
|
targetAddress - target address of the LCALL instruction.
|
|
|
|
Return Value:
|
|
|
|
Never returns.
|
|
|
|
--*/
|
|
{
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
assert (ia32context == BTLIB_CONTEXT_IA32_PTR());
|
|
//Fill in BTLIB_EXIT_INFO
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_LCALL_CODE;
|
|
//Currently LcallRecord is not used
|
|
BtlpExitSimulation();
|
|
}
|
|
|
|
static VOID BtlpRaiseException (
|
|
IN BT_EXCEPTION_CODE BtExceptCode,
|
|
IN U32 ReturnAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine either simulates an x86 software interrupt, or generates a
|
|
CPU exception depending on a specified exception code.
|
|
Exception code in the range 0-255 stands for software interrupt number.
|
|
All other BT-exception codes are converted to the corresponding OS-specific
|
|
exception code.
|
|
|
|
Arguments:
|
|
|
|
BtExceptCode - BT exception/interrupt code
|
|
ReturnAddr - address of the instruction to be executed after return from
|
|
exception handler
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
U32 ExceptionAddr;
|
|
|
|
//WOW64 and native Win32 provide different values for context32.Eip and
|
|
//ExceptionRecord.ExceptionAddress in exception handler. Current implementation
|
|
//resembles WOW64 behavior:
|
|
//context32.Eip = ExceptionRecord.ExceptionAddress = ReturnAddr
|
|
ExceptionAddr = ReturnAddr;
|
|
|
|
BTLIB_CONTEXT_IA32_PTR()->Eip = ExceptionAddr;
|
|
BT_CURRENT_TEB()->TlsSlots[4] = UlongToPtr(ExceptionAddr);
|
|
|
|
if (BtExceptCode > BT_MAX_INTERRUPT_NUMBER) {
|
|
//fill in EXCEPTION_RECORD and simulate exception
|
|
EXCEPTION_RECORD ExceptionRecord;
|
|
|
|
ExceptionRecord.ExceptionCode = BtlpBt2NtExceptCode(BtExceptCode);
|
|
ExceptionRecord.ExceptionAddress = UlongToPtr(ExceptionAddr);
|
|
ExceptionRecord.ExceptionFlags = 0;
|
|
ExceptionRecord.ExceptionRecord = NULL;
|
|
if (ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
|
//set exception information for the case of inaccessable IA32 code
|
|
ExceptionRecord.NumberParameters = 2;
|
|
ExceptionRecord.ExceptionInformation[0] = 0;
|
|
ExceptionRecord.ExceptionInformation[1] = ExceptionAddr;
|
|
}
|
|
else {
|
|
ExceptionRecord.NumberParameters = 0;
|
|
}
|
|
DBCODE (TRUE, BtlpPrintf ("\nWow64RaiseException simulates exception 0x%X at IP=0x%X ESP=0x%X\n",
|
|
ExceptionRecord.ExceptionCode,
|
|
BTLIB_CONTEXT_IA32_PTR()->Eip,
|
|
BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
Wow64RaiseException (-1, &ExceptionRecord);
|
|
}
|
|
else {
|
|
//simulate software interrupt
|
|
DBCODE (TRUE, BtlpPrintf ("\nWow64RaiseException simulates interrupt %d at IP=0x%X ESP=0x%X\n",
|
|
BtExceptCode,
|
|
BTLIB_CONTEXT_IA32_PTR()->Eip,
|
|
BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
Wow64RaiseException (BtExceptCode, NULL);
|
|
}
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\nReturned from Wow64RaiseException IP=0x%X ESP=0x%X",
|
|
BTLIB_CONTEXT_IA32_PTR()->Eip,
|
|
BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
}
|
|
|
|
|
|
VOID BtlIA32Interrupt(
|
|
IN OUT BTGENERIC_IA32_CONTEXT * ia32context,
|
|
IN BT_EXCEPTION_CODE exceptionCode,
|
|
IN U32 returnAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exit IA32 code simulation to raise exception/interrupt
|
|
|
|
Arguments:
|
|
|
|
ia32context - IA32 context. The ia32context->Eip register points to the next
|
|
instruction to be simulated
|
|
exceptionCode - exception/interrupt code
|
|
returnAddress - address of the instruction to be executed after return from
|
|
exception handler
|
|
|
|
|
|
Return Value:
|
|
|
|
Never returns.
|
|
|
|
Note:
|
|
For CPU faults: ia32context.Eip = returnAddress = fault inst. Eip
|
|
For CPU traps: ia32context.Eip = returnAddress = next Eip to execute
|
|
For software interrupts : ia32context.Eip points
|
|
to instruction caused interruption (not yet executed ) and returnAddress is the
|
|
next Eip.
|
|
--*/
|
|
{
|
|
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
assert (ia32context == BTLIB_CONTEXT_IA32_PTR());
|
|
//Fill in BTLIB_EXIT_INFO
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_EXCEPTION_CODE;
|
|
BTLIB_SIM_EXIT_INFO_PTR()->u.ExceptionRecord.ExceptionCode = exceptionCode;
|
|
BTLIB_SIM_EXIT_INFO_PTR()->u.ExceptionRecord.ReturnAddr = returnAddress;
|
|
BtlpExitSimulation();
|
|
}
|
|
|
|
VOID BtlIA32JmpIA64(
|
|
IN OUT BTGENERIC_IA32_CONTEXT * ia32context,
|
|
IN U32 returnAddress,
|
|
IN U32 targetAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exit IA32 code simulation to "execute" JMPE instruction.
|
|
In Wow64, JMPE instruction indicates call to system service. The only JMPE,
|
|
that can be reached during code simulation, is WOW64-provided JMPE,
|
|
since no other JMPE instructions should ever appear in IA32 applications
|
|
|
|
Arguments:
|
|
|
|
ia32context - IA32 context. ia32context->Eip points to the JMPE instruction.
|
|
returnAddress - Address of the next to JMPE instruction. In Wow64 it points to
|
|
global pointer value
|
|
targetAddress - target address of the JMPE instruction.
|
|
|
|
Return Value:
|
|
|
|
Never returns.
|
|
|
|
--*/
|
|
{
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
assert (ia32context == BTLIB_CONTEXT_IA32_PTR());
|
|
// Only WOW64-provided JMPE is acceptable
|
|
if (((VOID *)((UINT_PTR) ia32context->Eip)) != WOW64_JMPE) {
|
|
BtlpPrintf ("\nJMPE instruction detected in Wow64 application at 0x%X", ia32context->Eip);
|
|
BtlpPrintf ("\nWow64 JMPE is at 0x%p", WOW64_JMPE);
|
|
BTLIB_ABORT ();
|
|
}
|
|
//Fill in BTLIB_EXIT_INFO
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_JMPE_CODE;
|
|
//Currently JmpeRecord is not used
|
|
BtlpExitSimulation();
|
|
}
|
|
|
|
VOID BtlIA32Reenter(
|
|
IN OUT BTGENERIC_IA32_CONTEXT * ia32context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Exit and resume IA32 code simulation.
|
|
Called when IA32 thread has been suspended and then resumed, etc..
|
|
|
|
Arguments:
|
|
|
|
ia32context - IA32 context to resume code execution with
|
|
|
|
Return Value:
|
|
|
|
Never returns.
|
|
|
|
--*/
|
|
{
|
|
assert(BTLIB_INSIDE_CPU_SIMULATION());
|
|
assert (ia32context == BTLIB_CONTEXT_IA32_PTR());
|
|
BTLIB_SIM_EXIT_INFO_PTR()->ExitCode = SIM_EXIT_RESTART_CODE;
|
|
BtlpExitSimulation();
|
|
}
|
|
|
|
|
|
WOW64BT_IMPL VOID BTAPI(Simulate)(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RUn 32-bit code. The CONTEXT32 has already been set up to go.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None. Never returns.
|
|
|
|
--*/
|
|
{
|
|
DBCODE (FALSE, BtlpPrintf ("\nCpuSimulate: TEB=%p, EFLAGS=0x%X", BT_CURRENT_TEB(), BTLIB_CONTEXT_IA32_PTR()->EFlags));
|
|
for (;;) {
|
|
|
|
BTLIB_CPU_SIM_DATA CpuSimData;
|
|
|
|
BTLIB_ENTER_CPU_SIMULATION(&CpuSimData);
|
|
// If exception happens during code simulation, the IA32 context pointed by
|
|
// BTLIB_CONTEXT_IA32_PTR() may not correspond to the real exception context
|
|
BTLIB_CLEAR_CONSISTENT_EXCEPT_STATE();
|
|
|
|
if (setjmp(CpuSimData.Jmpbuf) == 0) {
|
|
BtlpSimulate(); // This function fills in BTLIB_SIM_EXIT_INFO and returns with longjmp
|
|
}
|
|
|
|
BTLIB_SET_CONSISTENT_EXCEPT_STATE();
|
|
BTLIB_LEAVE_CPU_SIMULATION();
|
|
|
|
//allow thread suspension at this point
|
|
if (BTLIB_HAS_SUSPEND_REQUEST()) {
|
|
BtlpReceiveSuspensionRequest();
|
|
}
|
|
//Take an action as specified in the BTLIB_SIM_EXIT_INFO
|
|
switch (CpuSimData.ExitData.ExitCode) {
|
|
|
|
case SIM_EXIT_JMPE_CODE:
|
|
// Call to system service
|
|
DBCODE (FALSE, BtlpPrintf ("\nArrived to JMPE: CONTEXT=%p", BTLIB_CONTEXT_IA32_PTR()));
|
|
DBCODE (FALSE, BtlpPrintf ("\nArrived with: IP=0x%X", BTLIB_CONTEXT_IA32_PTR()->Eip));
|
|
DBCODE (FALSE, BtlpPrintf ("\nArrived with: ESP=0x%X", BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
|
|
// Simulate RET instruction - pop return address
|
|
BTLIB_CONTEXT_IA32_PTR()->Eip = (*((U32 *)((UINT_PTR) BTLIB_CONTEXT_IA32_PTR()->Esp)));
|
|
BTLIB_CONTEXT_IA32_PTR()->Esp += sizeof (U32);
|
|
DBCODE (FALSE, BtlpPrintf ("\n Intend to return with: IP=0x%X ESP=0x%X", BTLIB_CONTEXT_IA32_PTR()->Eip, BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
DBCODE (FALSE, BtlpPrintf ("\n System Service 0x%X Context32=0x%p\n", BTLIB_CONTEXT_IA32_PTR()->Eax, BTLIB_CONTEXT_IA32_PTR()));
|
|
_try {
|
|
BTLIB_CONTEXT_IA32_PTR()->Eax = Wow64SystemService (BTLIB_CONTEXT_IA32_PTR()->Eax, BTLIB_CONTEXT_IA32_PTR());
|
|
} _except (BtlpSystemServiceFilterException(GetExceptionInformation())) {
|
|
BtlpPrintf ("\nShould never get to here - system service\n");
|
|
}
|
|
DBCODE (FALSE, BtlpPrintf ("\n Returned from System Service: Result=0x%X", BTLIB_CONTEXT_IA32_PTR()->Eax));
|
|
break;
|
|
|
|
case SIM_EXIT_RESTART_CODE:
|
|
// Restart code simulation
|
|
DBCODE (TRUE, BtlpPrintf ("\n Resuming thread simulation: TEB=%p EIP=0x%X ESP=0x%X ",
|
|
BT_CURRENT_TEB(), BTLIB_CONTEXT_IA32_PTR()->Eip, BTLIB_CONTEXT_IA32_PTR()->Esp));
|
|
break;
|
|
|
|
case SIM_EXIT_LCALL_CODE:
|
|
// Simulate LCALL
|
|
BtlpPrintf ("\n No LCALLs support in NT. Raise exception.");
|
|
BtlpRaiseException(IA32_GEN_PROT_FAULT_INTR, BTLIB_CONTEXT_IA32_PTR()->Eip);
|
|
break;
|
|
|
|
case SIM_EXIT_EXCEPTION_CODE:
|
|
// Raise IA32 exception/interrupt
|
|
BtlpRaiseException(CpuSimData.ExitData.u.ExceptionRecord.ExceptionCode,
|
|
CpuSimData.ExitData.u.ExceptionRecord.ReturnAddr);
|
|
break;
|
|
|
|
case SIM_EXIT_IA64_EXCEPTION_CODE:
|
|
// Raise IA64 exception
|
|
RtlRaiseException(&CpuSimData.ExitData.u.IA64Exception.ExceptionRecord);
|
|
break;
|
|
|
|
default:
|
|
BtlpPrintf ("\n Illegal simulation exit code %d. Aborting...", CpuSimData.ExitData.ExitCode);
|
|
BTLIB_ABORT();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// IA32 context manipulation
|
|
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(GetContext)(
|
|
HANDLE ThreadHandle,
|
|
HANDLE ProcessHandle,
|
|
PTEB pTEB,
|
|
BTGENERIC_IA32_CONTEXT * Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extracts the cpu context of the specified thread.
|
|
When entered, it is guaranteed that the target thread is suspended at
|
|
a proper CPU state.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Target thread handle to retreive the context for
|
|
ProcessHandle - Open handle to the process that the thread runs in
|
|
pTEB - Pointer to the target's thread TEB
|
|
Context - Context record to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
BT_STATUS_CODE BtStatus;
|
|
PVOID GlstP;
|
|
BOOL IsLocal;
|
|
|
|
if (pTEB == NULL) {
|
|
pTEB = BT_CURRENT_TEB();
|
|
IsLocal = TRUE;
|
|
}
|
|
else {
|
|
IsLocal = BtlpIsCurrentProcess(ProcessHandle);
|
|
}
|
|
|
|
DBCODE(FALSE, BtlpPrintf ("\n%s CpuGetContext: "
|
|
"Target TEB = %p Caller TEB = %p \n",
|
|
(IsLocal ? "Local" : "Remote"), pTEB, BT_CURRENT_TEB()));
|
|
|
|
GlstP = BtlpGetTlsPtr(ProcessHandle, pTEB, IsLocal);
|
|
if (GlstP == NULL) {
|
|
BtStatus = BT_STATUS_ACCESS_VIOLATION;
|
|
}
|
|
else {
|
|
BtStatus = (IsLocal ? BTGENERIC_IA32_CONTEXT_GET(GlstP, Context) :
|
|
BTGENERIC_IA32_CONTEXT_GET_REMOTE(ProcessHandle, GlstP, Context));
|
|
}
|
|
if (BtStatus != BT_STATUS_SUCCESS) {
|
|
return (BtlpBt2NtStatusCode(BtStatus));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
WOW64BT_IMPL NTSTATUS BTAPI(SetContext)(
|
|
HANDLE ThreadHandle,
|
|
HANDLE ProcessHandle,
|
|
PTEB pTEB,
|
|
BTGENERIC_IA32_CONTEXT * Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the cpu context for the specified thread.
|
|
When entered, if the target thread isn't the currently executing thread, then it is
|
|
guaranteed that the target thread is suspended at a proper CPU state.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Target thread handle to retreive the context for
|
|
ProcessHandle - Open handle to the process that the thread runs in
|
|
pTEB - Pointer to the target's thread TEB
|
|
Context - Context record to set
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
BT_STATUS_CODE BtStatus;
|
|
PVOID GlstP;
|
|
BOOL IsLocal;
|
|
|
|
if (pTEB == NULL) {
|
|
pTEB = BT_CURRENT_TEB();
|
|
IsLocal = TRUE;
|
|
}
|
|
else {
|
|
IsLocal = BtlpIsCurrentProcess(ProcessHandle);
|
|
}
|
|
|
|
DBCODE(FALSE, BtlpPrintf ("\n%s CpuSetContext: "
|
|
"Target TEB = %p Caller TEB = %p \n",
|
|
(IsLocal ? "Local" : "Remote"), pTEB, BT_CURRENT_TEB()));
|
|
|
|
GlstP = BtlpGetTlsPtr(ProcessHandle, pTEB, IsLocal);
|
|
if (GlstP == NULL) {
|
|
BtStatus = BT_STATUS_ACCESS_VIOLATION;
|
|
}
|
|
else {
|
|
BtStatus = (IsLocal ? BTGENERIC_IA32_CONTEXT_SET(GlstP, Context) :
|
|
BTGENERIC_IA32_CONTEXT_SET_REMOTE(ProcessHandle, GlstP, Context));
|
|
}
|
|
if (BtStatus != BT_STATUS_SUCCESS) {
|
|
return (BtlpBt2NtStatusCode(BtStatus));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
WOW64BT_IMPL ULONG BTAPI(GetStackPointer)(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the current 32-bit stack pointer value.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Value of 32-bit stack pointer.
|
|
|
|
--*/
|
|
{
|
|
DBCODE (FALSE, BtlpPrintf ("\nBTAPICpuGetStackPointer reports ESP=0x%X TEB=%p\n", BTLIB_CONTEXT_IA32_PTR()->Esp, BT_CURRENT_TEB()));
|
|
return BTLIB_CONTEXT_IA32_PTR()->Esp;
|
|
}
|
|
|
|
WOW64BT_IMPL VOID BTAPI(SetStackPointer)(
|
|
ULONG Value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Modifies the current 32-bit stack pointer value.
|
|
|
|
Arguments:
|
|
|
|
Value - new value to use for 32-bit stack pointer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BTLIB_CONTEXT_IA32_PTR()->Esp = Value;
|
|
DBCODE (FALSE, BtlpPrintf ("\nBTCpuSetStackPointer set ESP=0x%X TEB=%p\n", BTLIB_CONTEXT_IA32_PTR()->Esp, BT_CURRENT_TEB()));
|
|
}
|
|
|
|
WOW64BT_IMPL VOID BTAPI(SetInstructionPointer)(
|
|
ULONG Value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Modifies the current 32-bit instruction pointer value.
|
|
|
|
Arguments:
|
|
|
|
Value - new value to use for 32-bit instruction pointer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BTLIB_CONTEXT_IA32_PTR()->Eip = Value;
|
|
DBCODE (FALSE, BtlpPrintf ("\nBTCpuSetInstructionPointer set EIP=0x%X TEB=%p\n", BTLIB_CONTEXT_IA32_PTR()->Eip, BT_CURRENT_TEB()));
|
|
}
|
|
|
|
WOW64BT_IMPL BOOLEAN BTAPI(ProcessDebugEvent)(
|
|
IN LPDEBUG_EVENT DebugEventP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever a debug event needs to be processed.
|
|
This would indicate that the current thread is acting as a debugger.
|
|
This function gives CPU simulator (IA-32 Execution Layer) a chance to decide whether
|
|
this debug event should be dispatched to 32-bit code or not.
|
|
|
|
IA-32 Execution Layer uses this callback to ignore false 64-bit exceptions and
|
|
re-raise real first-chance exceptions that came to debugger before
|
|
restoring consistent state of the debuggee.
|
|
Arguments:
|
|
|
|
DebugEventP - Pointer to debug event to be processed
|
|
|
|
Return Value:
|
|
|
|
This function returns TRUE if it processed the debug event,
|
|
and doesn't wish to dispatch it to 32-bit code. Otherwise, it would
|
|
return FALSE, and it would dispatch the debug event to 32-bit code.
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN retval = FALSE;
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\nBTCpuProcessDebugEvent: DebugEventCode = %d", DebugEventP->dwDebugEventCode));
|
|
|
|
if ((DebugEventP->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) &&
|
|
DebugEventP->u.Exception.dwFirstChance) {
|
|
|
|
NTSTATUS status;
|
|
BOOL IsLocal;
|
|
HANDLE ProcessHandle;
|
|
HANDLE ThreadHandle;
|
|
CLIENT_ID Id;
|
|
static OBJECT_ATTRIBUTES Attributes = {sizeof(OBJECT_ATTRIBUTES), 0, 0, 0, 0, 0};
|
|
|
|
DBCODE (TRUE, BtlpPrintf ("\nBTCpuProcessDebugEvent: dwFirstChance= %d, ExceptionCode = 0x%x",
|
|
DebugEventP->u.Exception.dwFirstChance,
|
|
DebugEventP->u.Exception.ExceptionRecord.ExceptionCode));
|
|
|
|
//Open handles of the thread&process being debugged
|
|
Id.UniqueProcess = UlongToHandle(DebugEventP->dwProcessId);
|
|
Id.UniqueThread = UlongToHandle(DebugEventP->dwThreadId);
|
|
|
|
status = NtOpenProcess(&ProcessHandle,
|
|
PROCESS_VM_READ | PROCESS_VM_WRITE,
|
|
&Attributes,
|
|
&Id);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtOpenProcess", status);
|
|
}
|
|
else {
|
|
status = NtOpenThread(&ThreadHandle,
|
|
THREAD_QUERY_INFORMATION,
|
|
&Attributes,
|
|
&Id);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtOpenThread", status);
|
|
}
|
|
else {
|
|
PVOID GlstP;
|
|
BTLIB_SHARED_INFO_TYPE SharedInfo;
|
|
THREAD_BASIC_INFORMATION ThreadInfo;
|
|
|
|
//Retreive TEB of the thread being debugged
|
|
status = NtQueryInformationThread(
|
|
ThreadHandle,
|
|
ThreadBasicInformation,
|
|
&ThreadInfo,
|
|
sizeof(ThreadInfo),
|
|
0);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtQueryInformationThread", status);
|
|
}
|
|
else {
|
|
//Is this a local notification?
|
|
IsLocal = (DebugEventP->dwProcessId == BT_CURRENT_PROC_UID());
|
|
|
|
//Get TLS pointer of the thread being debugged
|
|
GlstP = BtlpGetTlsPtr(ProcessHandle, ThreadInfo.TebBaseAddress, IsLocal);
|
|
if (GlstP != NULL) {
|
|
//Check to see if the exception context is consistent
|
|
//If it is not, ignore current exception
|
|
status = BtlpReadSharedInfo(ProcessHandle, GlstP, IsLocal, &SharedInfo);
|
|
if (status == STATUS_SUCCESS) {
|
|
if (!BTLIB_SI_EXCEPT_STATE_CONSISTENT(&SharedInfo)) {
|
|
retval = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NtClose(ThreadHandle);
|
|
}
|
|
|
|
NtClose(ProcessHandle);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
|
|
// wowIA32X.dll APIs implementation (called by IA32Exec.bin):
|
|
|
|
U64 BtlGetThreadId(
|
|
VOID
|
|
) {
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reports thread handle/id to be recorded in IA32Exec.bin.
|
|
Will be passed to other wowIA32X.dll APIs.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Handle/Id.
|
|
|
|
--*/
|
|
return (U64)(BT_CURRENT_TEB()); // We will use TEB address as a thread ID
|
|
}
|
|
|
|
VOID BtlLockSignals(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
"Do not interrupt until furhter notice" (not used in NT).
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
// No actions in NT
|
|
}
|
|
VOID BtlUnlockSignals(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
"Can be interrupted" (not used in NT).
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
// No actions in NT
|
|
}
|
|
|
|
static U64 BtlpConvertPermissionsToBTLib (
|
|
IN DWORD flProtect
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert mempory permissions from NT-specific to wowIA32X.dll/IA32Exec.bin.
|
|
|
|
Arguments:
|
|
|
|
flProtect - IN NT-specific permissions.
|
|
|
|
Return Value:
|
|
|
|
wowIA32X.dll/IA32Exec.bin permissions
|
|
|
|
--*/
|
|
{
|
|
U64 Permissions = 0;
|
|
|
|
//Assuming that the system does not differentiate between read-only
|
|
//access and execute access
|
|
if (flProtect & ( PAGE_READONLY
|
|
| PAGE_READWRITE
|
|
| PAGE_WRITECOPY
|
|
| PAGE_EXECUTE
|
|
| PAGE_EXECUTE_READ
|
|
| PAGE_EXECUTE_READWRITE
|
|
| PAGE_EXECUTE_WRITECOPY )) {
|
|
Permissions |= (MEM_READ | MEM_EXECUTE);
|
|
}
|
|
|
|
if (flProtect & ( PAGE_READWRITE
|
|
| PAGE_WRITECOPY
|
|
| PAGE_EXECUTE_READWRITE
|
|
| PAGE_EXECUTE_WRITECOPY )) {
|
|
Permissions |= MEM_WRITE;
|
|
}
|
|
|
|
return Permissions;
|
|
}
|
|
|
|
static DWORD BtlpConvertPermissionsFromBTLib (
|
|
IN U64 Permissions
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert mempory permissions from wowIA32X.dll/IA32Exec.bin to NT-specific.
|
|
|
|
Arguments:
|
|
|
|
Permissions - IN wowIA32X.dll/IA32Exec.bin permissions.
|
|
|
|
Return Value:
|
|
|
|
NT-specific permissions
|
|
|
|
--*/
|
|
{
|
|
if (Permissions & MEM_READ) {
|
|
if (Permissions & MEM_WRITE) {
|
|
if (Permissions & MEM_EXECUTE) {
|
|
return PAGE_EXECUTE_READWRITE;
|
|
}
|
|
else {
|
|
return PAGE_READWRITE;
|
|
}
|
|
}
|
|
else {
|
|
if (Permissions & MEM_EXECUTE) {
|
|
return PAGE_EXECUTE_READ;
|
|
}
|
|
else {
|
|
return PAGE_READONLY;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
return PAGE_NOACCESS;
|
|
}
|
|
}
|
|
|
|
VOID * BtlMemoryAlloc(
|
|
IN VOID * startAddress,
|
|
IN U32 size,
|
|
IN U64 prot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate memory.
|
|
|
|
Arguments:
|
|
|
|
startAddress - IN suggested address or NULL, if any address will fit
|
|
size - IN requested memory size
|
|
prot - IN wowIA32X.dll/IA32Exec.bin permissions.
|
|
|
|
Return Value:
|
|
|
|
Memory address of the allocated block
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
LPVOID lpAddress;
|
|
SIZE_T dwSize = size;
|
|
DWORD permissions = BtlpConvertPermissionsFromBTLib (prot);
|
|
HANDLE processHandle = NtCurrentProcess();
|
|
|
|
if (startAddress == INITIAL_DATA_ALLOC) {
|
|
lpAddress = INITIAL_DATA_ADDRESS;
|
|
} else {
|
|
if (startAddress == INITIAL_CODE_ALLOC) {
|
|
lpAddress = INITIAL_CODE_ADDRESS;
|
|
} else {
|
|
lpAddress = startAddress;
|
|
}
|
|
}
|
|
|
|
status = NtAllocateVirtualMemory(processHandle,
|
|
&lpAddress,
|
|
0,
|
|
&dwSize,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
permissions
|
|
);
|
|
if (status != STATUS_SUCCESS) {
|
|
lpAddress = 0;
|
|
if (startAddress != 0) {
|
|
dwSize = size;
|
|
status = NtAllocateVirtualMemory(processHandle,
|
|
&lpAddress,
|
|
0,
|
|
&dwSize,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
permissions
|
|
);
|
|
}
|
|
}
|
|
return lpAddress;
|
|
}
|
|
|
|
BT_STATUS_CODE BtlMemoryFree(
|
|
IN VOID * startAddress,
|
|
IN U32 size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free memory.
|
|
|
|
Arguments:
|
|
|
|
startAddress - IN address of the area allocated by BtlMemoryAlloc
|
|
size - IN memory area size
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS_CODE
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
LPVOID lpAddress = startAddress;
|
|
SIZE_T dwSize = size;
|
|
status = NtFreeVirtualMemory(NtCurrentProcess (),
|
|
&lpAddress,
|
|
&dwSize,
|
|
MEM_DECOMMIT
|
|
);
|
|
if (status == STATUS_SUCCESS) {
|
|
lpAddress = startAddress;
|
|
dwSize = 0; // No size for release!
|
|
status = NtFreeVirtualMemory(NtCurrentProcess (),
|
|
&lpAddress,
|
|
&dwSize,
|
|
MEM_RELEASE
|
|
);
|
|
}
|
|
return ((status == STATUS_SUCCESS) ? BT_STATUS_SUCCESS : BT_STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
U32 BtlMemoryPageSize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report memory page size.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Page size in bytes
|
|
|
|
--*/
|
|
{
|
|
//It appears that NT-64 supports 4Kb page size for 32-bit system calls
|
|
//(VirtualAlloc, GetSystemInfo, etc.) and at the same time reports 8Kb page size to
|
|
//IA-32 Execution Layer. This inconsistency has been fixed by enforcing 4Kb page size in IA-32 Execution Layer.
|
|
|
|
#define MAX_IA32_APP_PAGE_SIZE 0x1000
|
|
static U32 sysPageSize = 0;
|
|
|
|
if (sysPageSize == 0) {
|
|
SYSTEM_BASIC_INFORMATION sysinfo;
|
|
SIZE_T ReturnLength = 0;
|
|
NTSTATUS status;
|
|
|
|
status = NtQuerySystemInformation (SystemBasicInformation,
|
|
&sysinfo,
|
|
sizeof(sysinfo),
|
|
(ULONG *)&ReturnLength
|
|
);
|
|
assert (status == STATUS_SUCCESS);
|
|
assert (ReturnLength == sizeof(sysinfo));
|
|
sysPageSize = ((sysinfo.PageSize < MAX_IA32_APP_PAGE_SIZE) ? (U32)sysinfo.PageSize : MAX_IA32_APP_PAGE_SIZE);
|
|
}
|
|
return sysPageSize;
|
|
}
|
|
|
|
U64 BtlMemoryChangePermissions(
|
|
IN VOID * startAddress,
|
|
IN U32 size,
|
|
IN U64 prot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change memory area permissions.
|
|
|
|
Arguments:
|
|
|
|
startAddress - IN memory address
|
|
size - IN memory size
|
|
prot - IN wowIA32X.dll/IA32Exec.bin permissions.
|
|
|
|
Return Value:
|
|
|
|
Former permissions value
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
LPVOID RegionAddress = startAddress;
|
|
SIZE_T RegionSize = size;
|
|
ULONG flOldProtection;
|
|
status = NtProtectVirtualMemory(NtCurrentProcess(),
|
|
&RegionAddress,
|
|
&RegionSize,
|
|
BtlpConvertPermissionsFromBTLib (prot),
|
|
&flOldProtection
|
|
);
|
|
return ((status == STATUS_SUCCESS) ? BtlpConvertPermissionsToBTLib (flOldProtection) : 0);
|
|
}
|
|
|
|
U64 BtlMemoryQueryPermissions(
|
|
IN VOID * address,
|
|
OUT VOID ** pRegionStart,
|
|
OUT U32 * pRegionSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provide information about a memory region that contains a specified address
|
|
and shares the same access permissions for all pages inside the region.
|
|
|
|
Arguments:
|
|
|
|
address - IN memory address to be queried
|
|
pRegionStart - OUT pointer to the returned starting address of the region
|
|
pRegionSize - OUT pointer to the returned size of the region in bytes
|
|
|
|
Return Value:
|
|
|
|
wowIA32X.dll/IA32Exec.bin access permission value shared by all pages in the region
|
|
|
|
--*/
|
|
{
|
|
|
|
extern int BtQueryRead(VOID * Address);
|
|
NTSTATUS status;
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
SIZE_T dwRetSize;
|
|
U64 permissions;
|
|
|
|
status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
address,
|
|
MemoryBasicInformation,
|
|
&memInfo,
|
|
sizeof (memInfo),
|
|
&dwRetSize
|
|
);
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
*pRegionStart = memInfo.BaseAddress;
|
|
*pRegionSize = (U32)(memInfo.RegionSize);
|
|
if (memInfo.RegionSize > (SIZE_T)(*pRegionSize)) {
|
|
//region size is too big, return max. U32 value aligned to the page size
|
|
*pRegionSize = (U32)(-(int)BtlMemoryPageSize());
|
|
}
|
|
|
|
if (memInfo.State == MEM_COMMIT) {
|
|
|
|
permissions = BtlpConvertPermissionsToBTLib(memInfo.Protect);
|
|
|
|
//Check an assumption that executable page is readable
|
|
if ((memInfo.Protect & PAGE_EXECUTE) && !BtQueryRead(address)) {
|
|
//Executable page is not readable - clear MEM_READ permission in order
|
|
//to prevent any attempt by IA32Exec.bin to read this memory
|
|
permissions &= (~((U64)MEM_READ));
|
|
BtlpPrintf("\nAddress %p in IA-32 process is located"
|
|
" in executable but unreadable page.\n",
|
|
address);
|
|
}
|
|
}
|
|
else {
|
|
permissions = 0;
|
|
}
|
|
|
|
// Temporary workaround for IA32 debugging support.
|
|
//To be removed after fixing FlushIC(ProcessHandle) by MS.
|
|
if (BeingDebugged && (permissions & MEM_EXECUTE)) {
|
|
permissions |= MEM_WRITE; //Code in the BeingDebugged process can be
|
|
//modified remotely without any notification
|
|
}
|
|
}
|
|
else {
|
|
// error in NtQueryVirtualMemory cosidered as a query of inaccessible memory
|
|
permissions = 0;
|
|
*pRegionStart = (VOID *)((ULONG_PTR)address & ~((ULONG_PTR)BtlMemoryPageSize() - 1));
|
|
*pRegionSize = BtlMemoryPageSize();
|
|
}
|
|
return permissions;
|
|
}
|
|
|
|
BT_STATUS_CODE BtlMemoryReadRemote(
|
|
IN BT_HANDLE ProcessHandle,
|
|
IN VOID * BaseAddress,
|
|
OUT VOID * Buffer,
|
|
IN U32 RequestedSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read virtual memory of another process
|
|
|
|
Arguments:
|
|
|
|
ProcessHandle - IN Process handle
|
|
BaseAddress - IN Memory region start
|
|
Buffer - OUT Buffer to read data
|
|
RequestedSize - IN Memory region size
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
status = NtReadVirtualMemory((HANDLE)ProcessHandle,
|
|
(PVOID)BaseAddress,
|
|
(PVOID)Buffer,
|
|
(SIZE_T)RequestedSize,
|
|
NULL);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtReadVirtualMemory", status);
|
|
return BT_STATUS_ACCESS_VIOLATION;
|
|
}
|
|
return BT_STATUS_SUCCESS;
|
|
}
|
|
|
|
BT_STATUS_CODE BtlMemoryWriteRemote(
|
|
IN BT_HANDLE ProcessHandle,
|
|
IN VOID * BaseAddress,
|
|
IN const VOID * Buffer,
|
|
IN U32 RequestedSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write virtual memory of another process
|
|
|
|
Arguments:
|
|
|
|
ProcessHandle - IN Process handle
|
|
BaseAddress - IN Memory region start
|
|
Buffer - IN Buffer to write data from
|
|
RequestedSize - IN Memory region size
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
status = NtWriteVirtualMemory((HANDLE)ProcessHandle,
|
|
(PVOID)BaseAddress,
|
|
(PVOID)Buffer,
|
|
(SIZE_T)RequestedSize,
|
|
NULL);
|
|
if (status != STATUS_SUCCESS) {
|
|
BTLP_REPORT_NT_FAILURE("NtWriteVirtualMemory", status);
|
|
return BT_STATUS_ACCESS_VIOLATION;
|
|
}
|
|
return BT_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Locking support (critical sections in NT)
|
|
|
|
BT_STATUS_CODE BtlInitAccessLock(
|
|
IN OUT VOID * lock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize lock (critical section)
|
|
|
|
Arguments:
|
|
|
|
lock - IN Pointer to the lock
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS_CODE.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
status = RtlInitializeCriticalSection ((PRTL_CRITICAL_SECTION) lock);
|
|
return ((status == STATUS_SUCCESS) ? BT_STATUS_SUCCESS : BT_STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
BT_STATUS_CODE BtlLockAccess(
|
|
IN OUT VOID * lock,
|
|
IN U64 flag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Access lock (enter or try to enter critical section)
|
|
|
|
Arguments:
|
|
|
|
lock - IN Pointer to the lock
|
|
flag - IN access flag (BLOCK - unconditional, otherwise - if available)
|
|
|
|
Return Value:
|
|
|
|
BT_STATUS_CODE
|
|
|
|
--*/
|
|
{
|
|
if (flag == BLOCK) {
|
|
NTSTATUS status;
|
|
status = RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION) lock);
|
|
return ((status == STATUS_SUCCESS) ? BT_STATUS_SUCCESS : BT_STATUS_UNSUCCESSFUL);
|
|
}
|
|
else if (RtlTryEnterCriticalSection((PRTL_CRITICAL_SECTION) lock)) {
|
|
return BT_STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
return BT_STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
VOID BtlUnlockAccess(
|
|
IN OUT VOID * lock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Release lock (critical section)
|
|
|
|
Arguments:
|
|
|
|
lock - IN Pointer to the lock
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
status = RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION) lock);
|
|
BTLP_REPORT_NT_FAILURE("RtlLeaveCriticalSection", status);
|
|
}
|
|
|
|
VOID BtlInvalidateAccessLock(
|
|
IN OUT VOID * lock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete lock (critical section)
|
|
|
|
Arguments:
|
|
|
|
lock - IN Pointer to the lock
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
status = RtlDeleteCriticalSection ((PRTL_CRITICAL_SECTION) lock);
|
|
BTLP_REPORT_NT_FAILURE("RtlDeleteCriticalSection", status);
|
|
}
|
|
|
|
// Longjmp support.
|
|
// Setjmp and longjmp must be supplied directly,
|
|
// otherwise setjmp does not work!
|
|
// Need only JMP buffer size
|
|
|
|
U32 BtlQueryJmpbufSize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report longjmp buffer size
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Buffer size in bytes.
|
|
|
|
--*/
|
|
{
|
|
return sizeof (_JBTYPE) * _JBLEN;
|
|
}
|
|
|
|
VOID BtlYieldThreadExecution(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Relinquish the remainder of the current thread's time slice
|
|
to any other thread that is ready to run
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NtYieldExecution();
|
|
}
|
|
|
|
VOID BtlFlushIA64InstructionCache(
|
|
IN VOID * Address,
|
|
IN U32 Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify kernel about a modification in IA64 code made within the given region
|
|
|
|
Arguments:
|
|
|
|
Address - IN Pointer to start of the region
|
|
Length - IN Size of the region
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NtFlushInstructionCache (NtCurrentProcess (), Address, (SIZE_T)Length);
|
|
}
|
|
#ifndef NODEBUG
|
|
//support Profiling debug mode
|
|
#define PROF_GEN
|
|
#endif
|
|
|
|
#ifdef PROF_GEN
|
|
// Define the method needed by IA-32 execution Layer profiling. They are
|
|
// 1. PGOFileOpen : Open a file for writing the profiling data
|
|
// 2. PGOFileClose : Close the file opened by PGOFileOpen
|
|
// 3. PGOFileWrite : Write profiling data
|
|
|
|
// Global handle use for containing the file handle openned by PGOFileOpen.
|
|
// This handle is defined as global instead of thread specific because PGOFileXXX operation
|
|
// is called only once per process.
|
|
HANDLE g_PGOFileHandle;
|
|
|
|
// The file offset associated with g_PGOFileHandle
|
|
LARGE_INTEGER g_PGOFileOffset;
|
|
|
|
// Function BtlPGOFileOpen
|
|
// Open a file for writing profiling data
|
|
// This is just a pseudo-function for C's fopen
|
|
// It output an (void *) type because the caller need to cast it into (FILE *) type.
|
|
VOID BtlPGOFileOpen(const char * filename,const char * mode,void ** pFileHandle)
|
|
{
|
|
UNICODE_STRING pgoFileName;
|
|
LARGE_INTEGER AllocSz = { 0, 0 };
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS ret;
|
|
WCHAR CurDirBuf[512],strInputFileName[64];
|
|
WCHAR CurrentDir[1024];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
int i;
|
|
|
|
DBCODE (TRUE,BtlpPrintf("PGO:fopen called: filename=%s, mode=%s\n",filename,mode));
|
|
RtlGetCurrentDirectory_U(512, CurrentDir);
|
|
for (i=0;filename[i] != '\0' && i<sizeof(strInputFileName)/sizeof(WCHAR)-1;i++)
|
|
strInputFileName[i]=(WCHAR)filename[i];
|
|
strInputFileName[i]=(WCHAR)0;
|
|
swprintf(CurDirBuf, L"\\DosDevices\\%s\\%s.%s", CurrentDir, ImageName, strInputFileName);
|
|
RtlInitUnicodeString(&pgoFileName, CurDirBuf);
|
|
InitializeObjectAttributes(&ObjectAttributes, &pgoFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
if (mode[0] == 'r' || mode[0] == 'R') {
|
|
//Read mode
|
|
ret = NtCreateFile (&g_PGOFileHandle,
|
|
GENERIC_READ,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
&AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_OPEN,//Use FILE_OPEN, if file doesn't exist, return fail
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS,
|
|
NULL, 0);
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
g_PGOFileHandle=NULL;
|
|
*pFileHandle=NULL;
|
|
return;
|
|
}
|
|
else {
|
|
g_PGOFileOffset.LowPart=0;
|
|
g_PGOFileOffset.HighPart=0;
|
|
*pFileHandle=&g_PGOFileHandle;
|
|
return;
|
|
}
|
|
}
|
|
else if (mode[0] == 'w' || mode[0] == 'W') {
|
|
// Write mode
|
|
ret = NtCreateFile (&g_PGOFileHandle,
|
|
FILE_GENERIC_WRITE,//GENERIC_WRITE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
&AllocSz,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_SUPERSEDE,
|
|
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, 0);
|
|
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
DBCODE (TRUE,BtlpPrintf("Open profile file for write fail, status: %#X\n",ret));
|
|
BTLIB_ABORT();
|
|
pFileHandle=NULL;
|
|
return;
|
|
}
|
|
g_PGOFileOffset.LowPart=0;
|
|
g_PGOFileOffset.HighPart=0;
|
|
*pFileHandle=&g_PGOFileHandle;
|
|
|
|
return;
|
|
}
|
|
else {
|
|
*pFileHandle=NULL;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
// Function BtlPGOFileClose
|
|
// A pseudo function for C's fclose
|
|
VOID BtlPGOFileClose(void * stream)
|
|
{
|
|
DBCODE (TRUE,BtlpPrintf("PGO:fclose called\n"));
|
|
assert(g_PGOFileHandle);
|
|
NtClose(g_PGOFileHandle);
|
|
g_PGOFileHandle=NULL;
|
|
g_PGOFileOffset.LowPart=0;
|
|
g_PGOFileOffset.HighPart=0;
|
|
}
|
|
|
|
// Function BtlPGOFileWrite
|
|
// A pseudo function for C's fwrite
|
|
VOID BtlPGOFileWrite(const void *buffer, size_t size, void *stream)
|
|
{
|
|
DBCODE (FALSE,BtlpPrintf("PGO:fwrite called\n"));
|
|
assert(g_PGOFileHandle);
|
|
assert(stream == &g_PGOFileHandle);
|
|
// Write
|
|
{
|
|
NTSTATUS ret;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER offset;
|
|
offset=g_PGOFileOffset;
|
|
g_PGOFileOffset.LowPart+=size;
|
|
ret = NtWriteFile(g_PGOFileHandle, NULL, NULL, NULL, &IoStatusBlock,
|
|
(void *)buffer, (ULONG)size, &offset, NULL);
|
|
if ( ret != STATUS_SUCCESS ) {
|
|
DBCODE (TRUE,BtlpPrintf("Writing profile file fail, status: %x\n",ret));
|
|
BTLIB_ABORT();
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
// Define dummy function
|
|
VOID BtlPGOFileOpen(void) {}
|
|
VOID BtlPGOFileClose(void) {}
|
|
VOID BtlPGOFileWrite(void) {}
|
|
#endif
|
|
|
|
// BtlAPITable
|
|
API_TABLE_TYPE BtlAPITable={
|
|
BTGENERIC_VERSION,
|
|
BTGENERIC_API_STRING,
|
|
NO_OF_APIS,
|
|
API_TABLE_START_OFFSET,
|
|
{L"BTLib First Test Version (API 0.1)"},
|
|
{
|
|
#define BTLIB_RECORD(NAME) { (PLABEL_PTR_TYPE)Btl##NAME }
|
|
BTLIB_RECORD(GetThreadId),
|
|
BTLIB_RECORD(IA32Reenter),
|
|
BTLIB_RECORD(IA32LCall),
|
|
BTLIB_RECORD(IA32Interrupt),
|
|
BTLIB_RECORD(IA32JmpIA64),
|
|
BTLIB_RECORD(LockSignals),
|
|
BTLIB_RECORD(UnlockSignals),
|
|
BTLIB_RECORD(MemoryAlloc),
|
|
BTLIB_RECORD(MemoryFree),
|
|
BTLIB_RECORD(MemoryPageSize),
|
|
BTLIB_RECORD(MemoryChangePermissions),
|
|
BTLIB_RECORD(MemoryQueryPermissions),
|
|
BTLIB_RECORD(MemoryReadRemote),
|
|
BTLIB_RECORD(MemoryWriteRemote),
|
|
{ (PLABEL_PTR_TYPE)NULL},//BTLIB_RECORD(Atomic_Misaligned_Load),
|
|
{ (PLABEL_PTR_TYPE)NULL},//BTLIB_RECORD(Atomic_Misaligned_Store),
|
|
BTLIB_RECORD(SuspendThread),
|
|
BTLIB_RECORD(ResumeThread),
|
|
BTLIB_RECORD(InitAccessLock),
|
|
BTLIB_RECORD(LockAccess),
|
|
BTLIB_RECORD(UnlockAccess),
|
|
BTLIB_RECORD(InvalidateAccessLock),
|
|
BTLIB_RECORD(QueryJmpbufSize),
|
|
{ (PLABEL_PTR_TYPE)NULL},//BTLIB_RECORD(Setjmp),
|
|
{ (PLABEL_PTR_TYPE)NULL},//BTLIB_RECORD(Longjmp),
|
|
BTLIB_RECORD(DebugPrint),
|
|
BTLIB_RECORD(Abort),
|
|
BTLIB_RECORD(VtuneCodeCreated),
|
|
BTLIB_RECORD(VtuneCodeDeleted),
|
|
BTLIB_RECORD(VtuneEnteringDynamicCode),
|
|
BTLIB_RECORD(VtuneExitingDynamicCode),
|
|
BTLIB_RECORD(VtuneCodeToTIADmpFile),
|
|
BTLIB_RECORD(SscPerfGetCounter64),
|
|
BTLIB_RECORD(SscPerfSetCounter64),
|
|
BTLIB_RECORD(SscPerfSendEvent),
|
|
BTLIB_RECORD(SscPerfEventHandle),
|
|
BTLIB_RECORD(SscPerfCounterHandle),
|
|
BTLIB_RECORD(YieldThreadExecution),
|
|
BTLIB_RECORD(FlushIA64InstructionCache),
|
|
BTLIB_RECORD(PGOFileOpen), // Indexed by IDX_BTLIB_PSEUDO_OPEN_FILE
|
|
BTLIB_RECORD(PGOFileClose), // Indexed by IDX_BTLIB_PSEUDO_CLOSE_FILE
|
|
BTLIB_RECORD(PGOFileWrite) // Indexed by IDX_BTLIB_PSEUDO_WRITE_FILE
|
|
}
|
|
};
|
|
|
|
// wowIA32X.dll placeholder table for IA32Exec.bin plabel pointers
|
|
|
|
PLABEL_PTR_TYPE BtlPlaceHolderTable[NO_OF_APIS];
|
|
|
|
// WINNT DLL initializer/terminator
|
|
BOOL APIENTRY DllMain(HANDLE hModule,
|
|
DWORD ul_reason_for_call,
|
|
LPVOID lpReserved )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|