#include        "defs.h"

unsigned long   emuJcc(__in context *pregs, __in px86dis px86, __in void *ptr){
        unsigned long b_ret = FALSE;
        unsigned long eflags;
        unsigned long dest;
        
        eflags = pregs->reg_eflags;
                
        switch (px86->iclass)
        {
        case XED_ICLASS_JB:
                if (TestBit(eflags, F_CF)) b_ret = TRUE;         
                break;	
        case XED_ICLASS_JBE:
                if (TestBit(eflags, F_CF) || TestBit(eflags, F_ZF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JL:
                if (TestBit(eflags, F_SF) != TestBit(eflags, F_OF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JLE:
                if (TestBit(eflags, F_ZF) || (TestBit(eflags, F_OF) != TestBit(eflags, F_SF))) b_ret = TRUE;
                break;
        case XED_ICLASS_JNB:
                if (!TestBit(eflags, F_CF)) b_ret = TRUE;
                break;	
        case XED_ICLASS_JNBE:
                if (!TestBit(eflags, F_CF) && !TestBit(eflags, F_ZF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JNL:
                if (TestBit(eflags, F_OF) == TestBit(eflags, F_SF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JNLE:
                if (!TestBit(eflags, F_ZF) && TestBit(eflags, F_SF) == TestBit(eflags, F_OF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JNO:
                if (!TestBit(eflags, F_OF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JNP:
                if (!TestBit(eflags, F_PF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JNS:
                if (!TestBit(eflags, F_SF)) b_ret = TRUE;
                break;	
        case XED_ICLASS_JNZ:
                if (!TestBit(eflags, F_ZF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JO:
                if (TestBit(eflags, F_OF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JP:
                if (TestBit(eflags, F_PF)) b_ret = TRUE;
                break; 		
        case XED_ICLASS_JS:
                if (TestBit(eflags, F_SF)) b_ret = TRUE;
                break; 	
        case XED_ICLASS_JZ:
                if (TestBit(eflags, F_ZF)) b_ret = TRUE;
                break;
        case XED_ICLASS_JRCXZ:
                if (pregs->reg_ecx == 0) b_ret = TRUE;
                break;
        default:
                DbgPrint(("%s -- failed to emulate jcc from address : %.08X\n", __FUNCTION__, ptr));
                __debugbreak();
        } 
        
        if (b_ret == TRUE){
                dest = (unsigned long)ptr + px86->len + px86->operand1;
        }else{
                dest = (unsigned long)ptr + px86->len;
        }
        
        return dest;
}

/*********************************************************
 * helper to extract regs from px86regs
 *********************************************************/
unsigned long emuXed2Reg(__in context *pregs, __in unsigned long xed_reg){
        switch (xed_reg)
        {
        case    XED_REG_EAX: return pregs->reg_eax;
        case    XED_REG_ECX: return pregs->reg_ecx;
        case    XED_REG_EDX: return pregs->reg_edx;            
        case    XED_REG_EBX: return pregs->reg_ebx;
        case    XED_REG_ESP: return pregs->reg_esp;
        case    XED_REG_EBP: return pregs->reg_ebp;
        case    XED_REG_ESI: return pregs->reg_esi;    
        case    XED_REG_EDI: return pregs->reg_edi;
        default:
                __debugbreak();
        }   
        return -1;
}
/******************************************
 * very important to process properly call
 * as it can be call reg/disp/mem etc...
 * same goes for jmp
 ******************************************/
unsigned long emuCallAndJmp(__in context *pregs, __in px86dis px86, __in void *ptr){
        unsigned long dest;
        unsigned long *pesp;
        unsigned long ExceptionCode;
        
        //we need some XED instructions... bah...        
          
        dest = 0;
        if (px86->mem_read_write != 0){
                if (px86->operand1_flags & C_INDEX_USED)
                        dest = emuXed2Reg(pregs, px86->index);             
                if (px86->operand1_flags & C_SCALE_USED)
                        dest = dest * px86->scale; //(px86->scale << 1);
                if (px86->operand1_flags & C_BASE_USED)
                        dest += emuXed2Reg(pregs, px86->base);
                if (px86->operand1_flags & C_DISPLACEMENT_USED)
                        dest += px86->displacement; 
                
                /*******************************************************
                 * check if we can read here, if not, we throw exception
                 * to an application...
                 *******************************************************/                
                if (px86->operand1_flags & C_SEG_USED && px86->seg == XED_REG_FS){
                        DbgPrint(("%s -- call jmp via FS found..." ,__FUNCTION__));
                        if (dest+4 > 0x1000){
                                traceInjectException(pregs, EXCEPTION_ACCESS_VIOLATION, dest, 0);
                        }
                        dest = __readfsdword(dest);
                }else{
                        ExceptionCode = ProbeForRead((PVOID)dest, 1, sizeof(ULONG));
                        if (ExceptionCode != STATUS_SUCCESS){
                                traceInjectException(pregs, ExceptionCode, dest, 0);
                        }
                        dest = *(unsigned long *)dest;
                }             
        }else if (px86->operand1_flags & C_REG){
                dest = emuXed2Reg(pregs, px86->operand1);
        }else{
                dest = (ULONG_PTR)ptr + px86->len + px86->operand1;
        }    
        
        if (dest == 0x1 || dest == 0x0){
                DbgPrint(("%s -- faulting data... dest = 0x1", __FUNCTION__));
                DbgPrint(("%s -- faulting eip : %.08X", __FUNCTION__, pregs->reg_eip));
                __asm _emit 0xEB
                __asm _emit 0xFE
        }
        
        /*******************************************************************
         * now I can return next address, and in case of JMP I do nothing...
         * just return jmp destination...
         *******************************************************************/
        if (px86->iclass == XED_ICLASS_CALL_NEAR){
                pregs->reg_esp -= 4;
                ExceptionCode = ProbeForWrite((PVOID)pregs->reg_esp, 1, sizeof(ULONG));
                if (ExceptionCode != STATUS_SUCCESS){
                        traceInjectException(pregs, ExceptionCode, pregs->reg_esp, TRUE);
                }
                *(unsigned long *)pregs->reg_esp = (unsigned long)ptr + px86->len;                              
        } 
        return dest;
}

unsigned long emuRet(__in context *pregs, __in px86dis px86, __in void *ptr){
        unsigned long stack_inc = 0;
        unsigned long dest;
        unsigned long *pesp;
        unsigned long ExceptionCode;
                
        if (px86->num_of_operands != 0)
                stack_inc = px86->operand1;
        
        pesp = (unsigned long *)pregs->reg_esp;
        
        ExceptionCode = ProbeForRead((PVOID)pesp, 1, sizeof(ULONG));
        if (ExceptionCode != STATUS_SUCCESS){
                traceInjectException(pregs, ExceptionCode, pregs->reg_esp, 0);
        }
        
        dest = *pesp;
        
        pregs->reg_esp += (stack_inc + 4);
        return dest;
}

unsigned long emuLoop(__in context *pregs, __in px86dis px86, __in void *ptr){
        unsigned long dest;
        unsigned long b_taken = 0;
        
        pregs->reg_ecx--;
        
        if (px86->iclass == XED_ICLASS_LOOP){
                if (pregs->reg_ecx != 0)
                        b_taken = 1;
        }else if (px86->iclass == XED_ICLASS_LOOPE){
                if (pregs->reg_ecx != 0 && TestBit(pregs->reg_eflags, F_ZF))
                        b_taken = 1;
        }else if (px86->iclass == XED_ICLASS_LOOPNE){
                if (pregs->reg_ecx != 0 && !TestBit(pregs->reg_eflags, F_ZF))
                        b_taken = 1;
        }                
        
        if (b_taken){
                dest = (unsigned long)ptr + px86->operand1 + px86->len;
        }else{
                dest = (unsigned long)ptr + px86->len;
        }
        
        return dest;
}