windows-nt/Source/XPSP1/NT/sdktools/debuggers/minidump/win.c

892 lines
18 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1999-2001 Microsoft Corporation
Module Name:
win.c
Abstract:
This module exports windows specific apis
Algorithm:
Unfortunately, implementing OpenThread cannot be done in a simple
manner. What follows is a very system dependent hack. If the structure
of the TDB or the implementation of OpenProcess change very much, this
function will break.
To have any idea of what we are doing here, you should be familiar with
Win9x internals. If you are not familiar with the Win9x source, consult
the book "Windows 95 System Programming SECRETS" by Matt Pietrek. Things
are not exactly the same for Win98 -- but pretty close.
OpenThread is a very simple function. If we were compiled withing the
Win9x source code base, the code would be simple:
OpenThread:
pObj = TidToTDB (dwThreadId);
return AllocHandle (GetCurrentPdb(), pObj, Flags);
Since we are not, the challenge is implementing the functions TidToTDB()
and AllocHandle().
Our approach is as follows:
1) We reverse-engineer TidToTDB since it is simple. TidToTDB is just
the thread-id xor'd with the Win9x Obfuscator.
2) We search through the code of OpenProcess until we find the address
of AllocHandle. We use this to allocate new handles in the
process's handle database.
3) OpenThread is then implemented in terms of the above primitives.
Author:
Matthew D Hendel (math) 01-Sept-1999
Revision History:
--*/
#include "pch.h"
#include "impl.h"
//
// Windows 9x support is x86 only.
//
#ifdef _X86_
typedef struct _MATCH_BUFFER {
ULONG Offset;
BYTE Byte;
} MATCH_BUFFER, *PMATCH_BUFFER;
typedef struct _OS_INFORMATION {
PMATCH_BUFFER MatchBuffer;
ULONG AllocHandleOffset;
} OS_INFORMATION, POS_INFORMATION;
/*++
Operating System:
Win95
Description:
This is the disasm of the OpenProcess routine on Win95. We attempt to
match this routine and pull out the value for AllocHandle from the code
for this routine. In this case, AllocHande is called by the third call in
this function.
The instructions marked by '*' are those we use for matching.
OpenProcess:
* BFF9404C: FF 74 24 0C push dword ptr [esp+0Ch]
* BFF94050: E8 2D 87 FE FF call BFF7C782
BFF94055: 85 C0 test eax,eax
BFF94057: 75 04 jne BFF9405D
BFF94059: 33 C0 xor eax,eax
BFF9405B: EB 56 jmp BFF940B3
BFF9405D: 83 38 05 cmp dword ptr [eax],5
BFF94060: 74 0E je BFF94070
BFF94062: 6A 57 push 57h
* BFF94064: E8 BC 68 FE FF call BFF7A925
BFF94069: B9 FF FF FF FF mov ecx,0FFFFFFFFh
BFF9406E: EB 33 jmp BFF940A3
BFF94070: B9 00 00 00 00 mov ecx,0
BFF94075: 8B 54 24 04 mov edx,dword ptr [esp+4]
BFF94079: 83 7C 24 08 01 cmp dword ptr [esp+8],1
BFF9407E: 83 D1 FF adc ecx,0FFFFFFFFh
BFF94081: 81 E2 BF FF 1F 00 and edx,1FFFBFh
BFF94087: 81 E1 00 00 00 80 and ecx,80000000h
BFF9408D: 0B CA or ecx,edx
BFF9408F: 8B 15 7C C2 FB BF mov edx,dword ptr ds:[BFFBC27Ch]
BFF94095: 80 C9 40 or cl,40h
BFF94098: 51 push ecx
BFF94099: 50 push eax
BFF9409A: FF 32 push dword ptr [edx]
* BFF9409C: E8 6E 76 FE FF call BFF7B70F
BFF940A1: 8B C8 mov ecx,eax
BFF940A3: 8D 41 01 lea eax,[ecx+1]
BFF940A6: 83 F8 01 cmp eax,1
BFF940A9: B8 00 00 00 00 mov eax,0
BFF940AE: 83 D0 FF adc eax,0FFFFFFFFh
BFF940B1: 23 C1 and eax,ecx
BFF940B3: C2 0C 00 ret 0Ch
--*/
MATCH_BUFFER Win95AllocHandleMatch [] = {
//
// ret 0x0C at offset 103
//
{ 103, 0xC2 },
{ 104, 0x0C },
{ 105, 0x00 },
//
// push dword ptr [exp 0x0C] at offset 0
//
{ 0, 0xFF },
{ 1, 0x74 },
{ 2, 0x24 },
{ 3, 0x0C },
//
// call at offset 4
//
{ 4, 0xE8 },
//
// call at offset 24
//
{ 24, 0xE8 },
//
// call at offset 80
//
{ 80, 0xE8 },
//
// End of match list.
//
{ -1, -1 }
};
/*++
Operating system:
Win98
Description:
See comments above regarding OpenProcess.
OpenProcess:
* BFF95C4D: FF 74 24 0C push dword ptr [esp+0Ch]
* BFF95C51: E8 C9 8E FE FF call BFF7EB1F
BFF95C56: 85 C0 test eax,eax
BFF95C58: 75 04 jne BFF95C5E
BFF95C5A: 33 C0 xor eax,eax
BFF95C5C: EB 53 jmp BFF95CB1
BFF95C5E: 80 38 06 cmp byte ptr [eax],6
BFF95C61: 74 0E je BFF95C71
BFF95C63: 6A 57 push 57h
* BFF95C65: E8 27 6D FE FF call BFF7C991
BFF95C6A: B9 FF FF FF FF mov ecx,0FFFFFFFFh
BFF95C6F: EB 30 jmp BFF95CA1
BFF95C71: B9 00 00 00 00 mov ecx,0
BFF95C76: 8B 54 24 04 mov edx,dword ptr [esp+4]
BFF95C7A: 83 7C 24 08 01 cmp dword ptr [esp+8],1
BFF95C7F: 83 D1 FF adc ecx,0FFFFFFFFh
BFF95C82: 81 E2 FF 0F 1F 00 and edx,1F0FFFh
BFF95C88: 81 E1 00 00 00 80 and ecx,80000000h
BFF95C8E: 0B CA or ecx,edx
BFF95C90: 8B 15 DC 9C FC BF mov edx,dword ptr ds:[BFFC9CDCh]
BFF95C96: 51 push ecx
BFF95C97: 50 push eax
BFF95C98: FF 32 push dword ptr [edx]
* BFF95C9A: E8 5A 7E FE FF call BFF7DAF9
BFF95C9F: 8B C8 mov ecx,eax
BFF95CA1: 8D 41 01 lea eax,[ecx+1]
BFF95CA4: 83 F8 01 cmp eax,1
BFF95CA7: B8 00 00 00 00 mov eax,0
BFF95CAC: 83 D0 FF adc eax,0FFFFFFFFh
BFF95CAF: 23 C1 and eax,ecx
* BFF95CB1: C2 0C 00 ret 0Ch
--*/
MATCH_BUFFER Win98AllocHandleMatch [] = {
//
// ret 0x0C at offset 100
//
{ 100, 0xC2 },
{ 101, 0x0C },
{ 102, 0x00 },
//
// push dword ptr [exp 0x0C] at offset 0
//
{ 0, 0xFF },
{ 1, 0x74 },
{ 2, 0x24 },
{ 3, 0x0C },
//
// call at offset 4
//
{ 4, 0xE8 },
//
// call at offset 24
//
{ 24, 0xE8 },
//
// call at offset 77
//
{ 77, 0xE8 },
//
// End of match list.
//
{ -1, -1 }
};
OS_INFORMATION SupportedSystems [] =
{
{ Win95AllocHandleMatch, 81 },
{ Win98AllocHandleMatch, 78 }
};
typedef
HANDLE
(__stdcall * ALLOC_HANDLE_ROUTINE) (
PVOID Pdb,
PVOID Obj,
DWORD Flags
);
//
// Global variables
//
ALLOC_HANDLE_ROUTINE WinpAllocHandle = NULL;
DWORD WinpObfuscator = 0;
#pragma warning (disable:4035)
//
// OffsetTib is NOT dependent on the OS. The compiler uses this value.
//
#define OffsetTib 0x18
_inline
PVOID
WinpGetCurrentTib(
)
{
#if defined(_X86_)
__asm mov eax, fs:[OffsetTib]
#else
return NULL;
#endif
}
#pragma warning (default:4035)
BOOL
WinpGetAllocHandleFromStream(
IN PBYTE Buffer,
IN PVOID BaseOfBuffer,
IN PMATCH_BUFFER MatchBuffer,
IN ULONG Offset,
IN ULONG * Val
)
/*++
Routine Description:
Find the address of the AllocHandle routine. This is done by searching
through the code of the OpenProcess routine, looking for the third
call instruction in that function. The third call calls AllocHandle().
Arguments:
Buffer - Buffer of instructions to search through.
BaseOfBuffer - The base address of the buffer.
MatchBuffer - The match buffer to compare against.
Offset - The offset of call destination.
Val - A buffer to return the value of AllocHandle.
Return Values:
TRUE - Success.
FALSE - Failure.
--*/
{
UINT i;
for (i = 0; MatchBuffer [i].Offset != -1; i++) {
if (Buffer [MatchBuffer[i].Offset] != MatchBuffer[i].Byte) {
return FALSE;
}
}
//
// This assumes that the call instruction is a near, relative call (E8).
// If this is not the case, the calculation below is incorrect.
//
// The calculation gives us the destination relative to the next
// instruction after the call.
//
*Val = (ULONG) BaseOfBuffer + Offset + *(PLONG) &Buffer [Offset] + 4;
return TRUE;
}
ULONG
WinGetModuleSize(
PVOID Base
)
/*++
Routine Description:
Get the SizeOfImage field given the base address of a module.
Return Values:
SizeOfImage field of the specified module on success.
NULL on failure.
--*/
{
ULONG Size;
PIMAGE_NT_HEADERS NtHeaders;
NtHeaders = ImageNtHeader ( Base );
if ( NtHeaders ) {
Size = NtHeaders->OptionalHeader.SizeOfImage;
} else {
Size = 0;
}
return Size;
}
BOOL
WinpInitAllocHandle (
)
/*++
Routine Description:
Initialize the global variable WxAllocHandle to the value of the Win9x
internal routine, AllocHandle.
Arguments:
None
Return Values:
TRUE - If we were able to successfully obtain a pointer to AllocHandle.
FALSE - Otherwise.
Comments:
The client of this routine should verify that this handle is correct by
calling WxCheckOpenThread() before blindly assuming the pointer is
correct.
--*/
{
ULONG i;
BOOL Succ;
PVOID OpenProcessPtr;
ULONG Kernel32Base;
ULONG Kernel32Size;
ULONG AllocHandle;
BYTE Buffer [ 200 ];
if ( WinpAllocHandle ) {
return TRUE;
}
Kernel32Base = (ULONG) GetModuleHandle ( "kernel32.dll" );
ASSERT ( Kernel32Base );
if (!Kernel32Base)
{
return FALSE;
}
Kernel32Size = WinGetModuleSize ( (PVOID) Kernel32Base );
ASSERT ( Kernel32Size != 0 );
OpenProcessPtr = GetProcAddress (
(HINSTANCE) Kernel32Base,
"OpenProcess"
);
if (!OpenProcessPtr)
{
return FALSE;
}
//
// Win9x thunks out functions when a debugger is present. To work around
// this we undo the thunk when it looks like its been thunked.
//
if ( (ULONG) OpenProcessPtr < Kernel32Base ||
(ULONG) OpenProcessPtr > Kernel32Base + Kernel32Size ) {
OpenProcessPtr = (PVOID) *(PULONG)( (PBYTE)OpenProcessPtr + 1 );
}
if ( (ULONG) OpenProcessPtr < Kernel32Base ||
(ULONG) OpenProcessPtr > Kernel32Base + Kernel32Size ) {
return FALSE;
}
CopyMemory (Buffer, OpenProcessPtr, sizeof (Buffer));
//
// Check the buffer
//
for ( i = 0; i < ARRAY_COUNT (SupportedSystems); i++) {
Succ = WinpGetAllocHandleFromStream (
Buffer,
OpenProcessPtr,
SupportedSystems[i].MatchBuffer,
SupportedSystems[i].AllocHandleOffset,
&AllocHandle
);
if ( Succ ) {
//
// Verify WinpAllocHandle within range of Kernel32.
//
if (AllocHandle > Kernel32Base &&
AllocHandle < Kernel32Base + Kernel32Size) {
WinpAllocHandle = (ALLOC_HANDLE_ROUTINE) AllocHandle;
break;
}
}
}
if ( !Succ ) {
WinpAllocHandle = NULL;
}
return Succ;
}
//
// This value is basically FIELD_OFFSET (TDB, Tib). It is dependent on the
// specific version of the OS (95, 98).
//
#define WIN95_TDB_OFFSET (0x10)
#define WIN98_TDB_OFFSET (0x08)
DWORD
WinpGetObfuscator(
)
/*++
Routine Description:
Get the Obfuscator DWORD.
Arguments:
None.
Return Values:
The Obfuscator or 0 on failure.
Comments:
This routine depends on internal structures from the Win9x sources. If
another major revision of windows changes many of these structures, this
function may break.
--*/
{
ULONG Tib;
ULONG Type;
ULONG Major;
if (WinpObfuscator != 0) {
return WinpObfuscator;
}
GenGetSystemType (&Type, &Major, NULL, NULL, NULL);
ASSERT ( Type == Win9x );
Tib = (DWORD)WinpGetCurrentTib ();
if ( Major == 95 ) {
WinpObfuscator = (GetCurrentThreadId () ^ (Tib - WIN95_TDB_OFFSET));
} else {
//
// If a windows-based system that is not 95 or 98 comes along,
// we should make sure the WINxx_TDB_OFFSET is correct.
//
ASSERT ( Major == 98 );
WinpObfuscator = (GetCurrentThreadId () ^ (Tib - WIN98_TDB_OFFSET));
}
return WinpObfuscator;
}
LPVOID
WinpTidToTDB(
IN DWORD ThreadId
)
{
return (PVOID) (ThreadId ^ WinpGetObfuscator ());
}
LPVOID
WinpGetCurrentPdb(
)
{
return (LPVOID) (GetCurrentProcessId () ^ WinpGetObfuscator ());
}
HANDLE
WinpOpenThreadInternal(
DWORD dwAccess,
BOOL bInheritHandle,
DWORD ThreadId
)
{
HANDLE hThread;
PVOID ThreadObj;
ASSERT (WinpAllocHandle);
//
// Convert the ThreadId to a Thread Object
//
ThreadObj = WinpTidToTDB (ThreadId);
if (ThreadObj == NULL) {
return NULL;
}
//
// NB: we do not check that the handle really is a thread handle.
// The type varies from version to version of the OS, so it is not
// correct to check it.
//
try {
hThread = WinpAllocHandle (
WinpGetCurrentPdb (),
ThreadObj,
dwAccess
);
}
except (EXCEPTION_EXECUTE_HANDLER) {
hThread = NULL;
}
if (hThread == (HANDLE) (-1)) {
hThread = NULL;
}
return hThread;
}
#if _MSC_FULL_VER >= 13008827
#pragma warning(push)
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
#endif
DWORD
WINAPI
WinpCheckThread(
PVOID unused
)
{
for (;;) {
}
return 0;
}
#if _MSC_FULL_VER >= 13008827
#pragma warning(pop)
#endif
BOOL
WinpCheckOpenThread(
)
/*++
Routine Description:
Check that WxOpenThread actually works.
Arguments:
None.
Return Values:
TRUE - If WxOpenThread works properly.
FALSE - Otherwise.
--*/
{
BOOL Succ;
HANDLE hThread1;
HANDLE hThread2;
DWORD ThreadId;
CONTEXT Context1;
CONTEXT Context2;
LONG SuspendCount;
SuspendCount = 0;
hThread1 = NULL;
hThread2 = NULL;
hThread1 = CreateThread (NULL,
0,
WinpCheckThread,
0,
0,
&ThreadId
);
if ( hThread1 == NULL ) {
return FALSE;
}
hThread2 = WinpOpenThreadInternal (
THREAD_ALL_ACCESS,
FALSE,
ThreadId
);
if ( hThread2 == NULL ) {
Succ = FALSE;
goto Exit;
}
Succ = TRUE;
try {
//
// First we check that we can suspend the thread. If that is
// successful, then get the context using the read thread
// handle and the newly opened thread handle and check that
// they are the same.
//
SuspendCount = SuspendThread ( hThread2 );
if ( SuspendCount == -1 ) {
Succ = FALSE;
leave;
}
Context1.ContextFlags = CONTEXT_FULL;
Succ = GetThreadContext ( hThread2, &Context1 );
if ( !Succ ) {
leave;
}
Context2.ContextFlags = CONTEXT_FULL;
Succ = GetThreadContext ( hThread1, &Context2 );
if ( !Succ ) {
leave;
}
if ( Context1.Eip != Context2.Eip ) {
Succ = FALSE;
leave;
}
}
except ( EXCEPTION_EXECUTE_HANDLER ) {
Succ = FALSE;
}
Exit:
if ( SuspendCount > 0 ) {
ResumeThread ( hThread2 );
}
TerminateThread ( hThread1, 0xDEAD );
if ( hThread1 ) {
CloseHandle ( hThread1 );
}
if ( hThread2 ) {
CloseHandle ( hThread2 );
}
return Succ;
}
BOOL
WinInitialize(
)
{
if ( WinpAllocHandle == NULL ) {
if (!WinpInitAllocHandle ()) {
SetLastError (ERROR_NOT_SUPPORTED);
return FALSE;
}
if (!WinpCheckOpenThread ()) {
SetLastError (ERROR_NOT_SUPPORTED);
return FALSE;
}
}
return TRUE;
}
VOID
WinFree(
)
{
WinpAllocHandle = NULL;
WinpObfuscator = 0;
}
HANDLE
WINAPI
WinOpenThread(
DWORD dwAccess,
BOOL bInheritHandle,
DWORD ThreadId
)
/*++
Routine Description:
Obtain a thread handle from a thread id on Win9x platform.x
Arguments:
dwAccess - Thread access requested.
bInheritHandle - ALWAYS IGNORED.
ThreadId - The identifier of the thread for which a handle is to
be returned.
Return Values:
A handle to the open thread on success or NULL on failure.
--*/
{
HANDLE Handle;
//
// It is necessary to call WinInitialize() before calling this function.
// If this was not called, return failure.
//
if ( WinpAllocHandle == NULL ) {
SetLastError ( ERROR_DLL_INIT_FAILED );
return FALSE;
}
Handle = WinpOpenThreadInternal (
dwAccess,
bInheritHandle,
ThreadId
);
return Handle;
}
#endif // _X86_