Windows2000/private/windbg64/debugger/dm/i386thnk.c
2020-09-30 17:12:32 +02:00

391 lines
12 KiB
C

#include "precomp.h"
#pragma hdrstop
BOOL
FIsVCallThunk(
BYTE * rgbBuffer,
DWORD cbBuff,
HTHDX hthd,
UOFFSET uoffEIP,
UOFFSET * lpuoffThunkDest,
LPDWORD lpdwThunkSize
)
{
static BYTE rgbVCall[] = {
0x8B, 0x44, 0x24, 0x04, // MOV EAX, [ESP+4]
0x8B, 0x00, // MOV EAX, [EAX]
};
BOOL fRet = FALSE;
UOFF32 uoffEAX;
BYTE * pbNextInstr = NULL;
UOFF32 uoffTemp;
assert( sizeof( rgbVCall ) <= CB_THUNK_MAX );
assert( sizeof( UOFF32 ) == sizeof( long ) );
// this_ptr in register
if ( *(short UNALIGNED *)&rgbBuffer[ 0 ] == 0x018B ) { // MOV EAX, [ECX]
// MOV EAX, [ECX]
fRet = DbgReadMemory(hthd->hprc, hthd->context.Ecx, (LPVOID)&uoffEAX, sizeof( uoffEAX ), NULL);
pbNextInstr = &rgbBuffer[ 2 ];
}
else if ( cbBuff >= sizeof( rgbVCall ) && !memcmp( rgbBuffer, rgbVCall, sizeof( rgbVCall ) )) {
// MOV EAX, [ESP+4]
fRet = DbgReadMemory(hthd->hprc, ( hthd->context.Esp + 4 ), (LPVOID)&uoffEAX, sizeof( uoffEAX ), NULL) &&
// MOV EAX, [EAX]
DbgReadMemory(hthd->hprc, uoffEAX, (LPVOID)&uoffEAX, sizeof( uoffEAX ), NULL);
pbNextInstr = &rgbBuffer[ sizeof( rgbVCall ) ];
}
// Everything is fine so far, now get the last instruction and return address
if ( fRet ) {
UOFF32 uoffDisp;
// JMP [EAX+disp] (get displacement)
if ( *(short UNALIGNED *)pbNextInstr == 0x60FF) {
uoffDisp = (UOFF32)*( pbNextInstr + 2 );
pbNextInstr += 3;
}
// JMP[EAX+disp] (disp is 4 btyes)
else if (*(short UNALIGNED *)pbNextInstr == 0xA0FF) {
uoffDisp = *(UOFF32 UNALIGNED *)(pbNextInstr + 2);
pbNextInstr += 6;
}
// JMP [EAX]
else if ( *(short UNALIGNED *)pbNextInstr == 0x20FF ) {
uoffDisp = 0;
pbNextInstr += 2;
}
// Don't care, but this will force the compare
// below to fail
else {
cbBuff = (DWORD)0;
}
// If we have made it here, then pbNextInstr must have been reset to non-null
assert( pbNextInstr );
assert( pbNextInstr != rgbBuffer );
// If the buffer is smaller than the number of bytes
// consumed in this thunk or we can read the address fail ([eap+disp])
if ( cbBuff >= (DWORD)( pbNextInstr - rgbBuffer - 1 ) && DbgReadMemory(hthd->hprc, ( uoffEAX + uoffDisp ), &uoffTemp, sizeof( UOFF32 ), NULL)) {
*lpuoffThunkDest = uoffTemp;
} else {
fRet = FALSE;
}
}
if (fRet) {
*lpdwThunkSize = (DWORD)(pbNextInstr - rgbBuffer);
}
return fRet;
}
BOOL
FIsVTDispAdjustorThunk(
BYTE * rgbBuffer,
DWORD cbBuff,
HTHDX hthd,
UOFFSET uoffEIP,
UOFFSET * lpuoffThunkDest,
LPDWORD lpdwThunkSize
)
{
BOOL fThisOnStack = (BOOL)( *(long UNALIGNED *)&rgbBuffer[ 0 ] == 0x04244C8BL );
BYTE * pbNextInstr = &rgbBuffer[ 0 ];
// If on the stack skip over this instruction
if ( fThisOnStack ) {
pbNextInstr += 4;
}
// Required SUB ECX, [ECX + dvtordisp]
if ( *pbNextInstr == 0x2B ) {
++pbNextInstr;
// SUB ECX, [ECX + byte/sign-extend]
if ( *pbNextInstr == 0x49 ) {
pbNextInstr += 2;
}
// SUB ECX, [ECX + dword]
else if ( *pbNextInstr == 0x89 ) {
pbNextInstr += 5;
}
else {
return FALSE;
}
// Maybe an adjust (byte/sign extended)
if ( *(short UNALIGNED *)pbNextInstr == 0xE983 ) {
pbNextInstr += 3;
}
// Maybe an adjust (dword)
else if ( *(short UNALIGNED *)pbNextInstr == 0xE981 ) {
pbNextInstr += 6;
}
// If this is on the stack there must be a MOV [ESP + 4 ],ECX
if ( fThisOnStack ) {
if ( *(long UNALIGNED *)pbNextInstr != 0x04244C89L ) {
return FALSE;
}
pbNextInstr += 4;
}
// Now make sure that the number of bytes consumed in this
// thunk is valid. By now, we know that we needed everything
// up to this instruction and including the (hopefully) jmp
// instruction number of bytes.
// Now we should have a direct relative jump (JMP xxxxxxxx )
if ( cbBuff >= (DWORD)( pbNextInstr - rgbBuffer + 5 ) && *pbNextInstr == 0xE9) {
*lpuoffThunkDest = uoffEIP + pbNextInstr - rgbBuffer + *(DWORD UNALIGNED *)( pbNextInstr + 1 ) + 5;
*lpdwThunkSize = (DWORD)(pbNextInstr - rgbBuffer + 5);
return TRUE;
}
}
return FALSE;
}
BOOL
FIsAdjustorThunk(
BYTE * rgbBuffer,
DWORD cbBuff,
HTHDX hthd,
UOFFSET uoffEIP,
UOFFSET * lpuoffThunkDest,
LPDWORD lpdwThunkSize
)
{
BYTE * pbNextInstr = &rgbBuffer[ 0 ];
BOOL fRet = FALSE;
// this adjustor ADD (byte/sign extended)
if ( *pbNextInstr == 0x83 ) {
++pbNextInstr;
// this in ECX: ECX, byte const
if ( *pbNextInstr == 0xC1 || *pbNextInstr == 0xE9 ) {
pbNextInstr += 2;
}
// this on stack: ADD DWORD PTR [ESP+4],byte const
else {
DWORD dwInstrCode = (*(DWORD UNALIGNED *)pbNextInstr & 0x00FFFFFF );
if ( dwInstrCode == 0x0004246CL || dwInstrCode == 0x00042444L ) {
pbNextInstr += 4;
}
else {
// not an adjustor thunk
return FALSE;
}
}
fRet = TRUE;
}
// this adjustor ADD (dword)
else if ( *pbNextInstr == 0x81 ) {
++pbNextInstr;
// this in ECX: ECX, dword const
if ( *pbNextInstr == 0xC1 || *pbNextInstr == 0xE9 ) {
pbNextInstr += 5;
}
// this on stack: ADD DWORD PTR [ESP+4],byte const
else {
DWORD dwInstrCode = (*(DWORD UNALIGNED *)pbNextInstr & 0x00FFFFFF );
if ( dwInstrCode == 0x0004246CL || dwInstrCode == 0x00042444L ) {
pbNextInstr += 7;
}
else {
// not an adjustor thunk
return FALSE;
}
}
fRet = TRUE;
}
if ( fRet ) {
// Now make sure that the number of bytes consumed in this
// thunk is valid. By now, we know that we needed everything
// up to this instruction and including the (hopefully) jmp
// instruction number of bytes.
// Now we should have a direct relative jump (JMP xxxxxxxx )
if ( cbBuff >= (DWORD)( pbNextInstr - rgbBuffer + 5 ) && *pbNextInstr == 0xE9) {
*lpuoffThunkDest = uoffEIP + pbNextInstr - rgbBuffer + *( DWORD UNALIGNED * )( pbNextInstr + 1 ) + 5;
*lpdwThunkSize = (DWORD)(pbNextInstr - rgbBuffer + 5);
}
else {
fRet = FALSE;
}
}
return fRet;
}
BOOL
FIsIndirectJump(
BYTE * rgbBuffer,
DWORD cbBuff,
HTHDX hthd,
UOFFSET uoffEIP,
UOFFSET * lpuoffThunkDest,
LPDWORD lpdwThunkSize
)
{
UOFF32 uoffTemp;
// FF 25 xxxxxxxx is indirect jump (jmp [xxxxxxxx])
// Buffer needs to be opcode + 32-bit offset
if ( cbBuff >= 6 && *(short *)rgbBuffer == 0x25FF ) {
if ( DbgReadMemory (hthd->hprc, *(DWORD UNALIGNED *)&rgbBuffer[ 2 ], &uoffTemp, sizeof(UOFF32), NULL)) {
*lpuoffThunkDest = uoffTemp;
*lpdwThunkSize = 6;
return TRUE;
}
}
return FALSE;
}
BOOL
FIsDirectJump(
BYTE * rgbBuffer,
DWORD cbBuff,
HTHDX hthd,
UOFFSET uoffEIP,
UOFFSET * lpuoffThunkDest,
LPDWORD lpdwThunkSize
)
{
// E9 = direct jump. (jmp xxxxxxxx).
// Buffer must be large enough for opcode plus 32-bit offset
if ( cbBuff >= 5 && rgbBuffer[ 0 ] == 0xE9 ) {
*lpuoffThunkDest = uoffEIP + *( DWORD UNALIGNED * )( rgbBuffer + sizeof ( BYTE ) ) + 5;
*lpdwThunkSize = 5;
return TRUE;
}
return FALSE;
}
/*** GETPMFDEST
* PURPOSE:
* Given the this pointer and a SI PMF figure out the address of the member
* function being called.
* INPUT:
* uThis - value of the this pointer.
* uPMF - the ptr to mbr function.
* lpuoffDest - Where the PMF is going to
* OUTPUT:
* EXCEPTIONS:
* IMPLEMENTATION:
*/
BOOL GetPMFDest(HTHDX hthd, UOFFSET uThis, UOFFSET uPMF, UOFFSET *lpuOffDest)
{
static BYTE rgbVCall[] = {
0x8B, 0x44, 0x24, 0x04, // MOV EAX, [ESP+4]
0x8B, 0x00, // MOV EAX, [EAX]
};
UOFFSET uOffCurr;
UOFFSET uOffNext = uPMF;
BYTE rgbBuffer[CB_THUNK_MAX];
DWORD dwLength;
int cThunks = 8; // Look for a max of 8 to avoid any infinite loop.
UOFFSET uVtable; // The v-table ptr.
UOFFSET uOffDisp; // The disp in the vtable where the func ptr is.
BYTE * pbNextInstr = NULL;
DWORD dwSize;
UOFF32 uoffTemp;
*lpuOffDest = (UOFFSET)0;
// First skip past any ilink thunks so we get to the
// vcall thunk.
do
{
uOffCurr = uOffNext;
dwLength = CB_THUNK_MAX;
// Read until a read succeeds or there's no room left to read
if (--cThunks == 0 || !DbgReadMemory (hthd->hprc, uOffCurr, rgbBuffer, dwLength, &dwLength)) {
// If we couldn't read anything at the location OR
// we have already looked at 8 thunks, just return
// indicating we couldn't find it.
return FALSE;
}
} while ( FIsDirectJump( rgbBuffer, dwLength, hthd, uOffCurr, &uOffNext, &dwSize) || FIsIndirectJump( rgbBuffer, dwLength, hthd, uOffCurr, &uOffNext, &dwSize ) );
// At this point we should still have a valid set of code bytes
// in rgbBuffer, and uOffCurr will have the address to it.
assert(dwLength);
// There is code here which is essentially duplication of the
// logic in FIsVCallThunk.
if ((dwLength >= 2) && *(short UNALIGNED *)rgbBuffer == 0x018B ) { // MOV EAX, [ECX]
pbNextInstr = &rgbBuffer[ 2 ];
dwLength -= 2;
}
else if ( dwLength >= sizeof( rgbVCall) && !memcmp(rgbBuffer, rgbVCall, sizeof(rgbVCall) )) {
pbNextInstr = &rgbBuffer[ sizeof( rgbVCall ) ];
dwLength -= sizeof( rgbVCall );
}
else {
// This must be a pointer to a non-virtual member func.
*lpuOffDest = uOffCurr;
return TRUE;
}
assert( pbNextInstr );
// JMP [EAX+disp] (get displacement)
if ( dwLength >= 3 && *(short UNALIGNED *)pbNextInstr == 0x60FF ) {
uOffDisp = *(pbNextInstr + 2);
}
// JMP [EAX+disp] (4 byte displacement)
else if ( dwLength >= 6 && *(short UNALIGNED *)pbNextInstr == 0xA0FF ) {
uOffDisp = *(UOFF32 UNALIGNED *)(pbNextInstr + 2);
}
else if ( dwLength >=2 && *(short UNALIGNED *)pbNextInstr == 0x20FF ) {
uOffDisp = (UOFF32) 0;
}
else { // Not one of the jump variants, must not be a thunk.
assert(FALSE);
return FALSE;
}
// Get the vtable ptr
if ( !DbgReadMemory(hthd->hprc, uThis, &uoffTemp, sizeof(uoffTemp), NULL)) {
return FALSE;
}
uVtable = uoffTemp;
// Use the vtable pointer and uOffDisp to get the vtable entry corresponding to this call.
if ( DbgReadMemory(hthd->hprc, ( uVtable + uOffDisp ), &uoffTemp, sizeof ( uoffTemp ), NULL)) {
*lpuOffDest = uoffTemp;
return TRUE;
}
return FALSE;
}