distorm starts to work

This commit is contained in:
PatrickvL 2017-01-09 18:27:32 +01:00
parent a6ec272604
commit 5acc167a07
3 changed files with 294 additions and 158 deletions

Binary file not shown.

View File

@ -145,83 +145,13 @@ extern "C" CXBXKRNL_API void NTAPI EmuWarning(const char *szWarningMessage, ...)
} }
#endif #endif
// exception handler void EmuExceptionPrintDebugInformation(LPEXCEPTION_POINTERS e, bool IsBreakpointException)
extern int EmuException(LPEXCEPTION_POINTERS e)
{ {
g_bEmuException = true;
// notify user
{
char buffer[256];
if(e->ExceptionRecord->ExceptionCode == 0x80000003)
{
// print debug information // print debug information
{ {
if (IsBreakpointException)
printf("EmuMain (0x%X): Recieved Breakpoint Exception (int 3)\n", GetCurrentThreadId()); printf("EmuMain (0x%X): Recieved Breakpoint Exception (int 3)\n", GetCurrentThreadId());
printf("\n"
" EIP := 0x%.08X EFL := 0x%.08X\n"
" EAX := 0x%.08X EBX := 0x%.08X ECX := 0x%.08X EDX := 0x%.08X\n"
" ESI := 0x%.08X EDI := 0x%.08X ESP := 0x%.08X EBP := 0x%.08X\n"
" CR2 := 0x%.08X\n"
"\n",
e->ContextRecord->Eip, e->ContextRecord->EFlags,
e->ContextRecord->Eax, e->ContextRecord->Ebx, e->ContextRecord->Ecx, e->ContextRecord->Edx,
e->ContextRecord->Esi, e->ContextRecord->Edi, e->ContextRecord->Esp, e->ContextRecord->Ebp,
e->ContextRecord->Dr2);
#ifdef _DEBUG
CONTEXT Context = *(e->ContextRecord);
EmuPrintStackTrace(&Context);
#endif
}
fflush(stdout);
sprintf(buffer,
"Recieved Breakpoint Exception (int 3) @ EIP := 0x%.08X\n"
"\n"
" Press Abort to terminate emulation.\n"
" Press Retry to debug.\n"
" Press Ignore to continue emulation.",
e->ContextRecord->Eip);
e->ContextRecord->Eip += 1;
int ret = MessageBox(g_hEmuWindow, buffer, "Cxbx-Reloaded", MB_ICONSTOP | MB_ABORTRETRYIGNORE);
if(ret == IDABORT)
{
printf("EmuMain (0x%X): Aborting Emulation\n", GetCurrentThreadId());
fflush(stdout);
if(CxbxKrnl_hEmuParent != NULL)
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0);
ExitProcess(1);
}
else if(ret == IDIGNORE)
{
printf("EmuMain (0x%X): Ignored Breakpoint Exception\n", GetCurrentThreadId());
g_bEmuException = false;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
else else
{
// Pass the exception to our X86 implementation, to try and execute the failing instruction
if (EmuX86_DecodeException(e))
{
g_bEmuException = false;
return EXCEPTION_CONTINUE_EXECUTION;
}
// print debug information
{
printf("EmuMain (0x%X): Recieved Exception (Code := 0x%.08X)\n", GetCurrentThreadId(), e->ExceptionRecord->ExceptionCode); printf("EmuMain (0x%X): Recieved Exception (Code := 0x%.08X)\n", GetCurrentThreadId(), e->ExceptionRecord->ExceptionCode);
printf("\n" printf("\n"
@ -242,7 +172,55 @@ extern int EmuException(LPEXCEPTION_POINTERS e)
} }
fflush(stdout); fflush(stdout);
}
void EmuExceptionExitProcess()
{
printf("EmuMain (0x%X): Aborting Emulation\n", GetCurrentThreadId());
fflush(stdout);
if (CxbxKrnl_hEmuParent != NULL)
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0);
ExitProcess(1);
}
bool EmuExceptionBreakpointAsk(LPEXCEPTION_POINTERS e)
{
EmuExceptionPrintDebugInformation(e, /*IsBreakpointException=*/true);
char buffer[256];
sprintf(buffer,
"Recieved Breakpoint Exception (int 3) @ EIP := 0x%.08X\n"
"\n"
" Press Abort to terminate emulation.\n"
" Press Retry to debug.\n"
" Press Ignore to continue emulation.",
e->ContextRecord->Eip);
int ret = MessageBox(g_hEmuWindow, buffer, "Cxbx-Reloaded", MB_ICONSTOP | MB_ABORTRETRYIGNORE);
if (ret == IDABORT)
{
EmuExceptionExitProcess();
}
else if (ret == IDIGNORE)
{
printf("EmuMain (0x%X): Ignored Breakpoint Exception\n", GetCurrentThreadId());
fflush(stdout);
e->ContextRecord->Eip += 1; // TODO : Skip actual instruction size bytes
return true;
}
return false;
}
void EmuExceptionNonBreakpointUnhandledShow(LPEXCEPTION_POINTERS e)
{
EmuExceptionPrintDebugInformation(e, /*IsBreakpointException=*/false);
char buffer[256];
sprintf(buffer, sprintf(buffer,
"Recieved Exception Code 0x%.08X @ EIP := 0x%.08X\n" "Recieved Exception Code 0x%.08X @ EIP := 0x%.08X\n"
"\n" "\n"
@ -250,21 +228,42 @@ extern int EmuException(LPEXCEPTION_POINTERS e)
" Press \"Cancel\" to debug.", " Press \"Cancel\" to debug.",
e->ExceptionRecord->ExceptionCode, e->ContextRecord->Eip); e->ExceptionRecord->ExceptionCode, e->ContextRecord->Eip);
if(MessageBox(g_hEmuWindow, buffer, "Cxbx-Reloaded", MB_ICONSTOP | MB_OKCANCEL) == IDOK) if (MessageBox(g_hEmuWindow, buffer, "Cxbx-Reloaded", MB_ICONSTOP | MB_OKCANCEL) == IDOK)
{ {
printf("EmuMain (0x%X): Aborting Emulation\n", GetCurrentThreadId()); EmuExceptionExitProcess();
fflush(stdout);
if(CxbxKrnl_hEmuParent != NULL)
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0);
ExitProcess(1);
}
}
} }
}
// exception handler
extern int EmuException(LPEXCEPTION_POINTERS e)
{
g_bEmuException = true;
if (e->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
{
// notify user
if (EmuExceptionBreakpointAsk(e))
{
// We're allowed to continue :
g_bEmuException = false; g_bEmuException = false;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
else
{
// Pass the exception to our X86 implementation, to try and execute the failing instruction
if (EmuX86_DecodeException(e))
{
// We're allowed to continue :
g_bEmuException = false;
return EXCEPTION_CONTINUE_EXECUTION;
}
// notify user
EmuExceptionNonBreakpointUnhandledShow(e);
}
// Unhandled exception :
g_bEmuException = false;
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }

View File

@ -101,9 +101,9 @@ uint16_t EmuX86_Read16(uint32_t addr)
EmuWarning("EmuX86_Read16(0x%08X) Forwarding to EmuX86_Read32...", addr); EmuWarning("EmuX86_Read16(0x%08X) Forwarding to EmuX86_Read32...", addr);
uint16_t value; uint16_t value;
if (addr & 2) if (addr & 2)
value = (uint16_t)EmuX86_Read32(addr - 2); value = (uint16_t)(EmuX86_Read32(addr - 2) >> 16);
else else
value = (uint16_t)(EmuX86_Read32(addr) >> 16); value = (uint16_t)EmuX86_Read32(addr);
EmuWarning("EmuX86_Read16(0x%08X) = 0x%04X", addr, value); EmuWarning("EmuX86_Read16(0x%08X) = 0x%04X", addr, value);
return value; return value;
@ -114,9 +114,9 @@ uint8_t EmuX86_Read8(uint32_t addr)
EmuWarning("EmuX86_Read8(0x%08X) Forwarding to EmuX86_Read16...", addr); EmuWarning("EmuX86_Read8(0x%08X) Forwarding to EmuX86_Read16...", addr);
uint8_t value; uint8_t value;
if (addr & 1) if (addr & 1)
value = (uint8_t)EmuX86_Read16(addr - 1); value = (uint8_t)(EmuX86_Read16(addr - 1) >> 8);
else else
value = (uint8_t)(EmuX86_Read16(addr) >> 8); value = (uint8_t)EmuX86_Read16(addr);
EmuWarning("EmuX86_Read8(0x%08X) = 0x%02X", addr, value); EmuWarning("EmuX86_Read8(0x%08X) = 0x%02X", addr, value);
return value; return value;
@ -180,41 +180,31 @@ inline bool EmuX86_GetRegisterValue(uint32_t* output, LPEXCEPTION_POINTERS e, ui
return true; return true;
} }
inline bool EmuX86_DecodeMemoryOperand(uint32_t* output, LPEXCEPTION_POINTERS e, _DInst& info, int operand) uint32_t EmuX86_Distorm_O_SMEM_Addr(LPEXCEPTION_POINTERS e, _DInst& info, int operand)
{ {
uint32_t base = 0; uint32_t base;
uint32_t index = 0; EmuX86_GetRegisterValue(&base, e, info.ops[operand].index);
if (!EmuX86_GetRegisterValue(&base, e, info.ops[operand].index) || !EmuX86_GetRegisterValue(&index, e, info.ops[operand].index)) {
return false;
}
*output = base + (index * info.scale) + info.imm.dword; return base + info.disp;
return true;
} }
bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operand, OUT uint32_t *value) uint32_t EmuX86_Distorm_O_MEM_Addr(LPEXCEPTION_POINTERS e, _DInst& info, int operand)
{ {
switch (info.ops[operand].type) { uint32_t base = 0;
// TODO : other operand.type EmuX86_GetRegisterValue(&base, e, info.base);
case O_IMM:
{
*value = info.imm.dword;
break;
}
case O_REG:
{
if (!EmuX86_GetRegisterValue(value, e, info.ops[operand].index))
return false;
break; uint32_t index = 0;
} EmuX86_GetRegisterValue(&index, e, info.ops[operand].index);
case O_MEM:
{
uint32_t srcAddr = 0;
if (!EmuX86_DecodeMemoryOperand(&srcAddr, e, info, operand))
return false;
switch (info.ops[operand].size) { if (info.scale >= 2)
return base + (index * info.scale) + info.disp;
else
return base + index + info.disp;
}
void EmuX86_ReadAddr(uint32_t srcAddr, uint16_t size, OUT uint32_t *value)
{
switch (size) {
case 8: case 8:
*value = EmuX86_Read8(srcAddr); *value = EmuX86_Read8(srcAddr);
break; break;
@ -225,10 +215,97 @@ bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operan
*value = EmuX86_Read32(srcAddr); *value = EmuX86_Read32(srcAddr);
break; break;
default: default:
// TODO : Handle other sizes?
break;
}
}
void EmuX86_WriteAddr(uint32_t destAddr, uint16_t size, uint32_t value)
{
switch (size) {
case 8:
EmuX86_Write8(destAddr, value & 0xFF);
break;
case 16:
EmuX86_Write16(destAddr, value & 0xFFFF);
break;
case 32:
EmuX86_Write32(destAddr, value);
break;
default:
// TODO : Handle other sizes?
break;
}
}
bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operand, OUT uint32_t *value)
{
switch (info.ops[operand].type) {
case O_NONE:
{
// ignore operand
}
case O_REG:
{
if (!EmuX86_GetRegisterValue(value, e, info.ops[operand].index))
return false; return false;
break;
}
case O_IMM:
{
switch (info.ops[operand].size) {
case 8:
*value = info.imm.byte;
break;
case 16:
*value = info.imm.word;
break;
case 32:
*value = info.imm.dword;
break;
// TODO : Handle other sizes?
} }
break; break;
} }
case O_IMM1:
{
// TODO
return false;
}
case O_IMM2:
{
// TODO
return false;
}
case O_DISP:
{
uint32_t srcAddr = info.disp;
EmuX86_ReadAddr(srcAddr, info.ops[operand].size, value);
break;
}
case O_SMEM:
{
uint32_t srcAddr = EmuX86_Distorm_O_SMEM_Addr(e, info, operand);
EmuX86_ReadAddr(srcAddr, info.ops[operand].size, value);
break;
}
case O_MEM:
{
uint32_t srcAddr = EmuX86_Distorm_O_MEM_Addr(e, info, operand);
EmuX86_ReadAddr(srcAddr, info.ops[operand].size, value);
break;
}
case O_PC:
{
// TODO
return false;
}
case O_PTR:
{
// TODO
return false;
}
default: default:
return false; return false;
} }
@ -239,6 +316,10 @@ bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operan
bool EmuX86_WriteValueToDestination(LPEXCEPTION_POINTERS e, _DInst& info, int operand, uint32_t value) bool EmuX86_WriteValueToDestination(LPEXCEPTION_POINTERS e, _DInst& info, int operand, uint32_t value)
{ {
switch (info.ops[operand].type) { switch (info.ops[operand].type) {
case O_NONE:
{
// ignore operand
}
case O_REG: case O_REG:
{ {
DWORD* pDstReg = EmuX86_GetRegisterPointer(e, info.ops[operand].index); DWORD* pDstReg = EmuX86_GetRegisterPointer(e, info.ops[operand].index);
@ -260,27 +341,49 @@ bool EmuX86_WriteValueToDestination(LPEXCEPTION_POINTERS e, _DInst& info, int op
} }
break; break;
} }
case O_MEM: case O_IMM:
{ {
uint32_t destAddr = 0; // TODO
if (!EmuX86_DecodeMemoryOperand(&destAddr, e, info, operand))
return false;
switch (info.ops[operand].size) {
case 8:
EmuX86_Write8(destAddr, value & 0xFF);
break;
case 16:
EmuX86_Write16(destAddr, value & 0xFFFF);
break;
case 32:
EmuX86_Write32(destAddr, value);
break;
default:
return false; return false;
} }
case O_IMM1:
{
// TODO
return false;
}
case O_IMM2:
{
// TODO
return false;
}
case O_DISP:
{
uint32_t destAddr = info.disp;
EmuX86_WriteAddr(destAddr, info.ops[operand].size, value);
break; break;
} }
case O_SMEM:
{
uint32_t destAddr = EmuX86_Distorm_O_SMEM_Addr(e, info, operand);
EmuX86_WriteAddr(destAddr, info.ops[operand].size, value);
break;
}
case O_MEM:
{
uint32_t destAddr = EmuX86_Distorm_O_MEM_Addr(e, info, operand);
EmuX86_WriteAddr(destAddr, info.ops[operand].size, value);
break;
}
case O_PC:
{
// TODO
return false;
}
case O_PTR:
{
// TODO
return false;
}
default: default:
return false; return false;
} }
@ -306,6 +409,24 @@ bool EmuX86_MOV(LPEXCEPTION_POINTERS e, _DInst& info)
return true; return true;
} }
bool EmuX86_MOVZX(LPEXCEPTION_POINTERS e, _DInst& info)
{
// TODO : Test this refactoring
// MOV reads value from source :
uint32_t value = 0;
if (!EmuX86_ReadValueFromSource(e, info, 1, &value))
return false;
// MOV writes value to destination :
if (!EmuX86_WriteValueToDestination(e, info, 0, value))
return false;
// Note : MOV instructions never update CPU flags
return true;
}
inline void EmuX86_SetFlag(LPEXCEPTION_POINTERS e, int flag, int value) inline void EmuX86_SetFlag(LPEXCEPTION_POINTERS e, int flag, int value)
{ {
e->ContextRecord->EFlags ^= (-value ^ e->ContextRecord->EFlags) & (1 << flag); e->ContextRecord->EFlags ^= (-value ^ e->ContextRecord->EFlags) & (1 << flag);
@ -361,7 +482,13 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
ci.codeOffset = 0; ci.codeOffset = 0;
ci.dt = (_DecodeType)Decode32Bits; ci.dt = (_DecodeType)Decode32Bits;
ci.features = DF_NONE; ci.features = DF_NONE;
if (DECRES_SUCCESS != distorm_decompose(&ci, &info, 1, &decodedInstructionsCount))
// Checking for DECRES_SUCCESS won't work, since we're passing distorm_decompose
// a codeLen big enough to decode any instruction-length, plus distorm doesn't
// halt cleanly after reaching maxInstructions 1. So instead, just call distorm :
distorm_decompose(&ci, &info, /*maxInstructions=*/1, &decodedInstructionsCount);
// and check if it successfully decoded one instruction :
if (decodedInstructionsCount != 1)
{ {
EmuWarning("EmuX86: Error decoding opcode at 0x%08X\n", e->ContextRecord->Eip); EmuWarning("EmuX86: Error decoding opcode at 0x%08X\n", e->ContextRecord->Eip);
} }
@ -374,6 +501,12 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
break; break;
} }
goto unimplemented_opcode;
case I_MOVZX:
if (EmuX86_MOVZX(e, info)) {
break;
}
goto unimplemented_opcode; goto unimplemented_opcode;
case I_TEST: case I_TEST:
if (EmuX86_TEST(e, info)) { if (EmuX86_TEST(e, info)) {
@ -384,8 +517,12 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
case I_WBINVD: case I_WBINVD:
// We can safely ignore this // We can safely ignore this
break; break;
default:
goto unimplemented_opcode;
} }
// When falling through here, the instruction was handled correctly,
// skip over the instruction and continue execution :
e->ContextRecord->Eip += info.size; e->ContextRecord->Eip += info.size;
return true; return true;