|
|
|
@ -119,6 +119,8 @@ void EmuX86_IOWrite(xbaddr addr, uint32_t value, int size)
|
|
|
|
|
|
|
|
|
|
uint32_t EmuX86_Mem_Read(xbaddr addr, int size)
|
|
|
|
|
{
|
|
|
|
|
__try {
|
|
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
|
case sizeof(uint32_t) :
|
|
|
|
|
return *(uint32_t*)addr;
|
|
|
|
@ -131,9 +133,15 @@ uint32_t EmuX86_Mem_Read(xbaddr addr, int size)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
__except (true) {
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Mem_Read Failed (0x%08X, %d)", addr, size);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmuX86_Mem_Write(xbaddr addr, uint32_t value, int size)
|
|
|
|
|
{
|
|
|
|
|
__try {
|
|
|
|
|
switch (size) {
|
|
|
|
|
case sizeof(uint32_t) :
|
|
|
|
|
*(uint32_t*)addr = (uint32_t)value;
|
|
|
|
@ -149,6 +157,10 @@ void EmuX86_Mem_Write(xbaddr addr, uint32_t value, int size)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
__except (true) {
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Mem_Write Failed (0x%08X, 0x%08X, %d)", addr, value, size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t EmuFlash_Read32(xbaddr addr) // TODO : Move to EmuFlash.cpp
|
|
|
|
|
{
|
|
|
|
@ -192,13 +204,8 @@ uint32_t EmuX86_Read(xbaddr addr, int size)
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_bEmuException) {
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Read(0x%08X, %d) [Unknown address]", addr, size);
|
|
|
|
|
value = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// Outside EmuException, pass the memory-access through to normal memory :
|
|
|
|
|
//pass the memory-access through to normal memory :
|
|
|
|
|
value = EmuX86_Mem_Read(addr, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DbgPrintf(LOG_PREFIX, "Read(0x%08X, %d) = 0x%08X\n", addr, size, value);
|
|
|
|
|
}
|
|
|
|
@ -224,12 +231,7 @@ void EmuX86_Write(xbaddr addr, uint32_t value, int size)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_bEmuException) {
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Write(0x%08X, 0x%08X) [Unknown address]", addr, value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Outside EmuException, pass the memory-access through to normal memory :
|
|
|
|
|
// Pass the memory-access through to normal memory :
|
|
|
|
|
DbgPrintf(LOG_PREFIX, "Write(0x%.8X, 0x%.8X, %d)\n", addr, value, size);
|
|
|
|
|
EmuX86_Mem_Write(addr, value, size);
|
|
|
|
|
}
|
|
|
|
@ -862,6 +864,33 @@ bool EmuX86_Opcode_INC(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_JMP(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
OperandAddress opAddr;
|
|
|
|
|
EmuX86_Operand_Addr_ForReadOnly(e, info, 0, opAddr);
|
|
|
|
|
|
|
|
|
|
e->ContextRecord->Eip += opAddr.addr;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Jump if condition is met
|
|
|
|
|
// Returns true if branch was taken
|
|
|
|
|
bool EmuX86_Opcode_JXX(LPEXCEPTION_POINTERS e, _DInst& info, DWORD flag, bool condition)
|
|
|
|
|
{
|
|
|
|
|
OperandAddress opAddr;
|
|
|
|
|
EmuX86_Operand_Addr_ForReadOnly(e, info, 0, opAddr);
|
|
|
|
|
|
|
|
|
|
// Branch if flag = condition
|
|
|
|
|
bool flagValue = (e->ContextRecord->EFlags & flag);
|
|
|
|
|
if (flagValue == condition) {
|
|
|
|
|
e->ContextRecord->Eip += opAddr.addr;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_MOV(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// MOV reads value from source :
|
|
|
|
@ -924,6 +953,32 @@ bool EmuX86_Opcode_MOVZX(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_NOT(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// NOT reads and writes the same operand :
|
|
|
|
|
OperandAddress opAddr;
|
|
|
|
|
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
uint32_t dest = EmuX86_Addr_Read(opAddr);
|
|
|
|
|
|
|
|
|
|
// NOT Destination with
|
|
|
|
|
uint32_t result = ~dest;
|
|
|
|
|
|
|
|
|
|
// Write back the result
|
|
|
|
|
EmuX86_Addr_Write(opAddr, result);
|
|
|
|
|
|
|
|
|
|
// The OF and CF flags are cleared; the SF, ZF, and PF flags are set according to the result. The state of the AF flag is undefined.
|
|
|
|
|
EmuX86_SetFlags_OSZPC(e,
|
|
|
|
|
/*EMUX86_EFLAG_OF*/0,
|
|
|
|
|
/*EMUX86_EFLAG_SF*/SFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_PF*/PFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_CF*/0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_OR(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// Read value from Source and Destination
|
|
|
|
@ -974,6 +1029,78 @@ bool EmuX86_Opcode_OUT(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_SBB(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// Read value from Source and Destination
|
|
|
|
|
uint32_t src = 0;
|
|
|
|
|
if (!EmuX86_Operand_Read(e, info, 1, &src))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// SBB reads and writes the same operand :
|
|
|
|
|
OperandAddress opAddr;
|
|
|
|
|
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
uint32_t dest = EmuX86_Addr_Read(opAddr);
|
|
|
|
|
|
|
|
|
|
// SUB Destination with src
|
|
|
|
|
uint64_t result = (uint64_t)dest - (uint64_t)src;
|
|
|
|
|
|
|
|
|
|
// If the carry flag is set, subtract an additional 1
|
|
|
|
|
if (e->ContextRecord->EFlags & BITMASK(EMUX86_EFLAG_CF)) {
|
|
|
|
|
result -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write result back
|
|
|
|
|
EmuX86_Addr_Write(opAddr, static_cast<uint32_t>(result));
|
|
|
|
|
|
|
|
|
|
// The OF, SF, ZF, AF, PF, and CF flags are set according to the result.
|
|
|
|
|
EmuX86_SetFlags_OSZAPC(e,
|
|
|
|
|
/*EMUX86_EFLAG_OF*/OF_Sub(result, src, dest),
|
|
|
|
|
/*EMUX86_EFLAG_SF*/SFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_AF*/AFCalc(result, src, dest),
|
|
|
|
|
/*EMUX86_EFLAG_PF*/PFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_CF*/CFCalc(result));
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_SHR(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// Read value from Source and Destination
|
|
|
|
|
uint32_t src = 0;
|
|
|
|
|
if (!EmuX86_Operand_Read(e, info, 1, &src))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// SHR reads and writes the same operand :
|
|
|
|
|
OperandAddress opAddr;
|
|
|
|
|
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
uint32_t dest = EmuX86_Addr_Read(opAddr);
|
|
|
|
|
|
|
|
|
|
// Shift Destination with src
|
|
|
|
|
uint8_t carryBit = dest & 1;
|
|
|
|
|
uint64_t result = (uint64_t)dest >> (uint64_t)src;
|
|
|
|
|
|
|
|
|
|
// Write result back
|
|
|
|
|
EmuX86_Addr_Write(opAddr, static_cast<uint32_t>(result));
|
|
|
|
|
|
|
|
|
|
// The OF, SF, ZF, AF, PF, and CF flags are set according to the result.
|
|
|
|
|
EmuX86_SetFlags_OSZAPC(e,
|
|
|
|
|
/*EMUX86_EFLAG_OF*/OF_Sub(result, src, dest),
|
|
|
|
|
/*EMUX86_EFLAG_SF*/SFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_AF*/AFCalc(result, src, dest),
|
|
|
|
|
/*EMUX86_EFLAG_PF*/PFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_CF*/carryBit);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_SUB(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// Read value from Source and Destination
|
|
|
|
@ -1035,6 +1162,37 @@ bool EmuX86_Opcode_TEST(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EmuX86_Opcode_XOR(LPEXCEPTION_POINTERS e, _DInst& info)
|
|
|
|
|
{
|
|
|
|
|
// Read value from Source and Destination
|
|
|
|
|
uint32_t src = 0;
|
|
|
|
|
if (!EmuX86_Operand_Read(e, info, 1, &src))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// XOR reads and writes the same operand :
|
|
|
|
|
OperandAddress opAddr;
|
|
|
|
|
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
uint32_t dest = EmuX86_Addr_Read(opAddr);
|
|
|
|
|
|
|
|
|
|
// XOR Destination with src
|
|
|
|
|
uint32_t result = dest ^ src;
|
|
|
|
|
|
|
|
|
|
// Write back the result
|
|
|
|
|
EmuX86_Addr_Write(opAddr, result);
|
|
|
|
|
|
|
|
|
|
// The OF and CF flags are cleared; the SF, ZF, and PF flags are set according to the result. The state of the AF flag is undefined.
|
|
|
|
|
EmuX86_SetFlags_OSZPC(e,
|
|
|
|
|
/*EMUX86_EFLAG_OF*/0,
|
|
|
|
|
/*EMUX86_EFLAG_SF*/SFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_PF*/PFCalc(result),
|
|
|
|
|
/*EMUX86_EFLAG_CF*/0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmuX86_Opcode_CLI()
|
|
|
|
|
{
|
|
|
|
|
g_bEnableAllInterrupts = false;
|
|
|
|
@ -1082,12 +1240,58 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
|
|
|
|
|
// However, if for any reason, an opcode operand cannot be read from or written to,
|
|
|
|
|
// that case may be logged, but it shouldn't fail the opcode handler.
|
|
|
|
|
_DInst info;
|
|
|
|
|
if (!EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, OUT info)) {
|
|
|
|
|
|
|
|
|
|
// Execute op-codes until we hit an unhandled instruction, or an error occurs
|
|
|
|
|
while (true) {
|
|
|
|
|
if (!EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, info)) {
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Error decoding opcode at 0x%08X", e->ContextRecord->Eip);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (info.opcode) { // Keep these cases alphabetically ordered and condensed
|
|
|
|
|
// Exit and branch Opcodes come first, for clarity/visibility
|
|
|
|
|
case I_JA:
|
|
|
|
|
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF) | BITMASK(EMUX86_EFLAG_ZF), false)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_JAE:
|
|
|
|
|
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF), false)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_JB:
|
|
|
|
|
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF), true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_JBE:
|
|
|
|
|
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF) | BITMASK(EMUX86_EFLAG_ZF), true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_JNZ:
|
|
|
|
|
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_ZF), false)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_JMP:
|
|
|
|
|
if (EmuX86_Opcode_JMP(e, info)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_JZ:
|
|
|
|
|
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_ZF), true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case I_CALL: case I_RET:
|
|
|
|
|
// RET and CALL always signify the end of a code block
|
|
|
|
|
return true;
|
|
|
|
|
case I_PUSH: case I_POP:
|
|
|
|
|
// TODO: Implement these instructions
|
|
|
|
|
// currently stubbed to prevent firing the unimplemented instruction handler
|
|
|
|
|
return true;
|
|
|
|
|
case I_ADD:
|
|
|
|
|
if (EmuX86_Opcode_ADD(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
@ -1133,6 +1337,9 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
|
|
|
|
|
case I_MOVZX:
|
|
|
|
|
if (EmuX86_Opcode_MOVZX(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
|
case I_NOT:
|
|
|
|
|
if (EmuX86_Opcode_NOT(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
|
case I_OR:
|
|
|
|
|
if (EmuX86_Opcode_OR(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
@ -1144,6 +1351,12 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
|
|
|
|
|
EmuX86_Opcode_STI();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case I_SBB:
|
|
|
|
|
if (EmuX86_Opcode_SBB(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
|
case I_SHR:
|
|
|
|
|
if (EmuX86_Opcode_SHR(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
|
case I_SUB:
|
|
|
|
|
if (EmuX86_Opcode_SUB(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
@ -1159,20 +1372,19 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
|
|
|
|
|
// Chase: Hollywood Stunt Driver hits this
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "WRMSR instruction ignored");
|
|
|
|
|
break;
|
|
|
|
|
case I_XOR:
|
|
|
|
|
if (EmuX86_Opcode_XOR(e, info)) break;
|
|
|
|
|
goto opcode_error;
|
|
|
|
|
default:
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unhandled instruction : %u", info.opcode);
|
|
|
|
|
e->ContextRecord->Eip += info.size;
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When falling through here, the instruction was handled correctly,
|
|
|
|
|
// skip over the instruction and continue execution :
|
|
|
|
|
e->ContextRecord->Eip += info.size;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opcode_error:
|
|
|
|
|
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "0x%08X: Error while handling instruction %u", e->ContextRecord->Eip, info.opcode); // TODO : format decodedInstructions[0]
|
|
|
|
|
e->ContextRecord->Eip += info.size;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|