/* ======== SourceHook ======== * Copyright (C) 2004-2007 Metamod:Source Development Team * No warranties of any kind * * License: zlib/libpng * * Author(s): Pavol "PM OnoTo" Marko * Contributor(s): Borja "faluco" Ferav (many thanks for assitance!) * David "BAILOPAN" Anderson * ============================ */ // recommended literature: // http://www.cs.umbc.edu/~chang/cs313.s02/stack.shtml // http://www.angelcode.com/dev/callconv/callconv.html // http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-1.html #include "sourcehook_hookmangen.h" #include "sourcehook_hookmangen_x86.h" #include "sh_memory.h" // :TODO: test BIG vtable indices namespace SourceHook { namespace Impl { CPageAlloc GenBuffer::ms_Allocator; template jit_int32_t DownCastPtr(T ptr) { return reinterpret_cast(ptr); } jit_uint32_t DownCastSize(size_t size) { return static_cast(size); } GenContext::GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr) : m_Proto(proto), m_VtblOffs(vtbl_offs), m_VtblIdx(vtbl_idx), m_SHPtr(pSHPtr), m_pHI(NULL), m_HookfuncVfnptr(NULL), m_RegCounter(0) { m_pHI = new void*; m_HookfuncVfnptr = new void*; m_BuiltPI = new ProtoInfo; m_BuiltPI_Params = NULL; m_BuiltPI_Params2 = NULL; } GenContext::~GenContext() { Clear(); delete m_pHI; delete m_HookfuncVfnptr; delete m_BuiltPI; } void GenContext::Clear() { m_HookFunc.clear(); m_PubFunc.clear(); if (m_BuiltPI_Params) { delete m_BuiltPI_Params; m_BuiltPI_Params = NULL; } if (m_BuiltPI_Params2) { delete m_BuiltPI_Params2; m_BuiltPI_Params2 = NULL; } } void GenContext::BuildProtoInfo() { m_BuiltPI->convention = m_Proto.GetConvention(); m_BuiltPI->numOfParams = m_Proto.GetNumOfParams(); m_BuiltPI->retPassInfo.size = m_Proto.GetRet().size; m_BuiltPI->retPassInfo.type = m_Proto.GetRet().type; m_BuiltPI->retPassInfo.flags = m_Proto.GetRet().flags; m_BuiltPI->retPassInfo2.pNormalCtor = m_Proto.GetRet().pNormalCtor; m_BuiltPI->retPassInfo2.pCopyCtor = m_Proto.GetRet().pCopyCtor; m_BuiltPI->retPassInfo2.pDtor = m_Proto.GetRet().pDtor; m_BuiltPI->retPassInfo2.pAssignOperator = m_Proto.GetRet().pAssignOperator; if (m_BuiltPI_Params) delete m_BuiltPI_Params; m_BuiltPI_Params = new PassInfo[m_BuiltPI->numOfParams + 1]; if (m_BuiltPI_Params2) delete m_BuiltPI_Params2; m_BuiltPI_Params2 = new PassInfo::V2Info[m_BuiltPI->numOfParams + 1]; m_BuiltPI_Params[0].size = 1; // Version 1 m_BuiltPI_Params[0].type = 0; m_BuiltPI_Params[0].flags = 0; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { m_BuiltPI_Params[i+1].size = m_Proto.GetParam(i).size; m_BuiltPI_Params[i+1].type = m_Proto.GetParam(i).type; m_BuiltPI_Params[i+1].flags = m_Proto.GetParam(i).flags; m_BuiltPI_Params2[i+1].pNormalCtor = m_Proto.GetParam(i).pNormalCtor; m_BuiltPI_Params2[i+1].pCopyCtor = m_Proto.GetParam(i).pCopyCtor; m_BuiltPI_Params2[i+1].pDtor = m_Proto.GetParam(i).pDtor; m_BuiltPI_Params2[i+1].pAssignOperator = m_Proto.GetParam(i).pAssignOperator; } m_BuiltPI->paramsPassInfo = m_BuiltPI_Params; m_BuiltPI->paramsPassInfo2 = m_BuiltPI_Params2; } jit_int32_t GenContext::GetRealSize(const IntPassInfo &info) { if (info.flags & PassInfo::PassFlag_ByRef) { return SIZE_PTR; } return static_cast(info.size); } // Computes size on the stack jit_int32_t GenContext::GetStackSize(const IntPassInfo &info) { // Align up to 4 byte boundaries jit_int32_t rs = GetRealSize(info); if (rs % 4 != 0) rs = (rs & ~(3)) + 4; return rs; } jit_int8_t GenContext::NextRegEBX_ECX_EDX() { switch ((m_RegCounter++) % 3) { case 0: return REG_EBX; case 1: return REG_ECX; case 2: default: m_RegCounter = 0; return REG_EDX; } } void GenContext::BitwiseCopy_Setup() { //cld //push edi //push esi IA32_Cld(&m_HookFunc); IA32_Push_Reg(&m_HookFunc, REG_EDI); IA32_Push_Reg(&m_HookFunc, REG_ESI); } void GenContext::BitwiseCopy_Do(size_t size) { jit_uint32_t dwords = DownCastSize(size) / 4; jit_uint32_t bytes = DownCastSize(size) % 4; //if dwords // mov ecx, // rep movsd //if bytes // mov ecx, // rep movsb //pop esi //pop edi if (dwords) { IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, dwords); IA32_Rep(&m_HookFunc); IA32_Movsd(&m_HookFunc); } if (bytes) { IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, bytes); IA32_Rep(&m_HookFunc); IA32_Movsb(&m_HookFunc); } IA32_Pop_Reg(&m_HookFunc, REG_ESI); IA32_Pop_Reg(&m_HookFunc, REG_EDI); } jit_int32_t GenContext::PushRef(jit_int32_t param_offset, const IntPassInfo &pi) { // push [ebp+] IA32_Push_Rm_DispAuto(&m_HookFunc, REG_EBP, param_offset); return SIZE_PTR; } jit_int32_t GenContext::PushBasic(jit_int32_t param_offset, const IntPassInfo &pi) { int reg; int reg2; switch (pi.size) { default: SH_ASSERT(0, ("Unsupported!")); return 0; case 1: reg = NextRegEBX_ECX_EDX(); //movzx reg, BYTE PTR [ebp+] //push reg IA32_Movzx_Reg32_Rm8_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); return 4; case 2: reg = NextRegEBX_ECX_EDX(); //movzx reg, WORD PTR [ebp+] //push reg m_HookFunc.write_ubyte(IA32_16BIT_PREFIX); IA32_Movzx_Reg32_Rm16_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); return 4; case 4: reg = NextRegEBX_ECX_EDX(); //mov reg, DWORD PTR [ebp+] //push reg IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); return 4; case 8: reg = NextRegEBX_ECX_EDX(); reg2 = NextRegEBX_ECX_EDX(); //mov reg, DWORD PTR [ebp++4] //mov reg2, DWORD PTR [ebp+] //push reg //push reg2 IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset+4); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, reg2, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); IA32_Push_Reg(&m_HookFunc, reg2); return 8; } } jit_int32_t GenContext::PushFloat(jit_int32_t param_offset, const IntPassInfo &pi) { switch (pi.size) { default: SH_ASSERT(0, ("Unsupported!")); return 0; case 4: //fld DWORD PTR [ebp+] //push reg //fstp DWORD PTR [esp] IA32_Fld_Mem32_DispAuto(&m_HookFunc, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, NextRegEBX_ECX_EDX()); IA32_Fstp_Mem32_ESP(&m_HookFunc); return 4; case 8: //fld QWORD PTR [ebp+] //sub esp, 8 //fstp QWORD PTR [esp] IA32_Fld_Mem64_DispAuto(&m_HookFunc, REG_EBP, param_offset); IA32_Sub_Rm_Imm8(&m_HookFunc, REG_ESP, 8, MOD_REG); IA32_Fstp_Mem64_ESP(&m_HookFunc); return 8; } } jit_int32_t GenContext::PushObject(jit_int32_t param_offset, const IntPassInfo &pi) { // make room on the stack // sub esp, IA32_Sub_Rm_ImmAuto(&m_HookFunc, REG_ESP, GetStackSize(pi), MOD_REG); // if there is a copy constructor.. if (pi.pCopyCtor) { // save eax // push src addr // this= target addr = esp+12 // call copy constructor // restore eax IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Lea_Reg_DispRegMultImm8(&m_HookFunc, REG_ECX, REG_NOIDX, REG_ESP, NOSCALE, 4); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EAX, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, REG_EAX); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EDX, DownCastPtr(pi.pCopyCtor)); IA32_Call_Reg(&m_HookFunc, REG_EDX); IA32_Pop_Reg(&m_HookFunc, REG_EAX); } else { // bitwise copy BitwiseCopy_Setup(); //lea edi, [esp+8] //lea esi, [ebp+] IA32_Lea_Reg_DispRegMultImm8(&m_HookFunc, REG_EDI, REG_NOIDX, REG_ESP, NOSCALE, 8); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, param_offset); BitwiseCopy_Do(pi.size); } return DownCastSize(pi.size); } // May not touch eax! jit_int32_t GenContext::PushParams(jit_int32_t param_base_offset, jit_int32_t save_ret_to, int v_place_for_memret) { jit_int32_t added_to_stack = 0; jit_int32_t ret = 0; // compute the offset _after_ the last parameter jit_int32_t cur_offset = param_base_offset; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { cur_offset += GetStackSize(m_Proto.GetParam(i)); } // push parameters in reverse order for (int i = m_Proto.GetNumOfParams() - 1; i >= 0; --i) { const IntPassInfo &pi = m_Proto.GetParam(i); cur_offset -= GetStackSize(pi); if (pi.flags & PassInfo::PassFlag_ByVal) { switch (pi.type) { case PassInfo::PassType_Basic: ret = PushBasic(cur_offset, pi); break; case PassInfo::PassType_Float: ret = PushFloat(cur_offset, pi); break; case PassInfo::PassType_Object: ret = PushObject(cur_offset, pi); break; } } else if (pi.flags & PassInfo::PassFlag_ByRef) { PushRef(cur_offset, pi); } else { SH_ASSERT(0, ("Unsupported!")); } added_to_stack += ret; } // Memory return support if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { // push address where to save it! int reg = NextRegEBX_ECX_EDX(); IA32_Lea_DispRegImmAuto(&m_HookFunc, reg, REG_EBP, MemRetWithTempObj() ? v_place_for_memret : save_ret_to); IA32_Push_Reg(&m_HookFunc, reg); added_to_stack += SIZE_PTR; } return added_to_stack; } void GenContext::SaveRetVal(int v_where, int v_place_for_memret) { size_t size = GetRealSize(m_Proto.GetRet()); if (size == 0) { // No return value -> nothing return; } if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov [ebp + v_plugin_ret], eax IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_where); return; } // else: ByVal // Memory return: if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { if (MemRetWithTempObj()) { // *v_where = *v_place_for_memret // if we have an assign operator, use that if (m_Proto.GetRet().pAssignOperator) { // lea edx, [ebp + v_place_for_memret] <-- src addr // lea ecx, [ebp + v_where] <-- dest addr // gcc: push ecx // push edx // call it IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_place_for_memret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_where); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Push_Reg(&m_HookFunc, REG_EDX); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pAssignOperator)); IA32_Call_Reg(&m_HookFunc, REG_EAX); } else { // bitwise copy BitwiseCopy_Setup(); //lea edi, [evp+v_where] <-- destination //lea esi, [ebp+v_place_for_memret] <-- src IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDI, REG_EBP, v_where); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, v_place_for_memret); BitwiseCopy_Do(m_Proto.GetRet().size); } // Then: destruct *v_place_for_memret if required if (m_Proto.GetRet().pDtor) { //lea ecx, [ebp+v_place_for_memret] //gcc: push ecx //call it IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_place_for_memret); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); } } else { // Already copied to correct address -> we're done return; } } if (m_Proto.GetRet().type == PassInfo::PassType_Float) { if (size == 4) IA32_Fstp_Mem32_DispAuto(&m_HookFunc, REG_EBP, v_where); else if (size == 8) IA32_Fstp_Mem64_DispAuto(&m_HookFunc, REG_EBP, v_where); } else if (m_Proto.GetRet().type == PassInfo::PassType_Basic) { if (size <= 4) { // size <= 4: return in EAX // We align <4 sizes up to 4 // mov [ebp + v_plugin_ret], eax IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_where); } else if (size <= 8) { // size <= 4: return in EAX:EDX // We align 4 8 !")); } } } } bool GenContext::MemRetWithTempObj() { // Memory return AND (has destructor OR has assign operator) return ((m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) && (m_Proto.GetRet().flags & (PassInfo::PassFlag_ODtor | PassInfo::PassFlag_AssignOp))); } void GenContext::ProcessPluginRetVal(int v_cur_res, int v_pContext, int v_plugin_ret) { // only for non-void functions! if (m_Proto.GetRet().size == 0) return; // if (cur_res >= MRES_OVERRIDE) // *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; // eax = cur_res // cmp eax,MRES_OVERRIDE // jnge thelabel // pContext->GetOverrideRetPtr() --> overrideretptr in eax // *eax = plugin_ret // thelabel: // jitoffs_t tmppos, counter; IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_cur_res); IA32_Cmp_Rm_Imm32(&m_HookFunc, MOD_REG, REG_EAX, MRES_OVERRIDE); tmppos = IA32_Jump_Cond_Imm8(&m_HookFunc, CC_NGE, 0); m_HookFunc.start_count(counter); // eax = pContext->GetOverrideRetPtr() // ECX = pContext // gcc: push ecx // eax = [ecx] // eax = [eax + 4] // call eax IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif // vtbloffs=0, vtblidx=1 IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EAX, 4); IA32_Call_Reg(&m_HookFunc, REG_EAX); // *eax = plugin_ret if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov ecx, [ebp+v_plugin_ret] // mov [eax], ecx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_plugin_ret); IA32_Mov_Rm_Reg(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); } else { if (m_Proto.GetRet().pAssignOperator) { // lea edx, [ebp + v_plugin_ret] // msvc: ecx = eax <-- dest addr // gcc: push eax <-- dest addr // push edx <-- src addr // call it IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_plugin_ret); #if SH_COMP == SH_COMP_MSVC IA32_Mov_Reg_Rm(&m_HookFunc, REG_ECX, REG_EAX, MOD_REG); #elif SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_EAX); #endif IA32_Push_Reg(&m_HookFunc, REG_EDX); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pAssignOperator)); IA32_Call_Reg(&m_HookFunc, REG_EAX); } else { // bitwise copy BitwiseCopy_Setup(); //mov edi, eax <-- destination //lea esi, [ebp+v_plugin_ret] <-- src IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDI, REG_EAX, MOD_REG); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, v_plugin_ret); BitwiseCopy_Do(m_Proto.GetRet().size); } } m_HookFunc.end_count(counter); m_HookFunc.rewrite(tmppos, static_cast(counter)); } void GenContext::PrepareReturn(int v_status, int v_pContext, int v_retptr) { // only for non-void functions! if (m_Proto.GetRet().size == 0) return; // retptr = status >= MRES_OVERRIDE ? pContext->GetOverrideRetPtr() : pContext->GetOrigRetPtr() // OverrideRetPtr: vtblidx = 1 // OrigRetPtr: vtbldix = 2 // vtblidx = (status >= MRES_OVERRIDE) ? 1 : 2 // // eax = pContext->GetOverrideRetPtr() // ECX = pContext // gcc: push ecx // eax = (status < MRES_OVERRIDE) ? 1 : 0 // xor eax, eax // cmp [ebp + v_status], MRES_OVERRIDE // setl al <-- setcc optimization for ternary operators, // lea eax, [4*eax + 0x4] // edx = [ecx] // add edx, eax // mov edx, [edx] // call edx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Xor_Reg_Rm(&m_HookFunc, REG_EAX, REG_EAX, MOD_REG); IA32_Cmp_Rm_Disp8_Imm8(&m_HookFunc, REG_EBP, v_status, MRES_OVERRIDE); IA32_SetCC_Rm8(&m_HookFunc, REG_EAX, CC_L); IA32_Lea_Reg_RegMultImm32(&m_HookFunc, REG_EAX, REG_EAX, SCALE4, 4); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDX, REG_ECX, MOD_MEM_REG); IA32_Add_Reg_Rm(&m_HookFunc, REG_EDX, REG_EAX, MOD_REG); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDX, REG_EDX, MOD_MEM_REG); IA32_Call_Reg(&m_HookFunc, REG_EDX); IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_retptr); } void GenContext::DoReturn(int v_retptr, int v_memret_outaddr) { size_t size = m_Proto.GetRet().size; if (!size) return; // Get real ret pointer into ecx // mov ecx, [ebp + v_ret_ptr] IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_retptr); if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov eax, [ecx] IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); return; } // else: byval if (m_Proto.GetRet().type == PassInfo::PassType_Float) { if (size == 4) IA32_Fld_Mem32(&m_HookFunc, REG_ECX); else if (size == 8) IA32_Fld_Mem64(&m_HookFunc, REG_ECX); } else if (m_Proto.GetRet().type == PassInfo::PassType_Basic || ((m_Proto.GetRet().type == PassInfo::PassType_Object) && (m_Proto.GetRet().flags & PassInfo::PassFlag_RetReg)) ) { if (size <= 4) { // size <= 4: return in EAX // We align <4 sizes up to 4 // mov eax, [ecx] IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); } else if (size <= 8) { // size <= 4: return in EAX:EDX // We align 48: return in memory // handled later } } if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { // *memret_outaddr = plugin_ret if (m_Proto.GetRet().pCopyCtor) { // mov edx, ecx <-- src ( we set ecx to [ebp+v_retptr] before ) // msvc: ecx = [ebp + v_memret_outaddr] <-- dest addr // gcc: push [ebp + v_memret_outaddr] <-- dest addr // push edx <-- src addr // call it IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDX, REG_ECX, MOD_REG); #if SH_COMP == SH_COMP_MSVC IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_memret_outaddr); #elif SH_COMP == SH_COMP_GCC IA32_Push_Rm_DispAuto(&m_HookFunc, REG_EBP, v_memret_outaddr); #endif IA32_Push_Reg(&m_HookFunc, REG_EDX); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pCopyCtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); } else { // bitwise copy BitwiseCopy_Setup(); //mov edi, [ebp+v_memret_outaddr] <-- destination //mov esi, ecx <-- src ( we set ecx to [ebp+v_retptr] before ) IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EDI, REG_EBP, v_memret_outaddr); IA32_Mov_Reg_Rm(&m_HookFunc, REG_ESI, REG_ECX, MOD_REG); BitwiseCopy_Do(m_Proto.GetRet().size); } // In both cases: return the pointer in EAX // mov eax, [ebp + v_memret_outaddr] IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_memret_outaddr); } } void GenContext::GenerateCallHooks(int v_status, int v_prev_res, int v_cur_res, int v_iter, int v_pContext, int base_param_offset, int v_plugin_ret, int v_place_for_memret) { jitoffs_t counter, tmppos; jitoffs_t counter2, tmppos2; jitoffs_t loop_begin_counter; // prev_res = MRES_IGNORED IA32_Mov_Rm_Imm32_Disp8(&m_HookFunc, REG_EBP, MRES_IGNORED, v_prev_res); m_HookFunc.start_count(loop_begin_counter); // eax = pContext->GetNext() // ECX = pContext // gcc: push ecx // eax = [ecx] // eax = [eax] // call eax IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif // vtbloffs=0, vtblidx=0 IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_EAX, MOD_MEM_REG); IA32_Call_Reg(&m_HookFunc, REG_EAX); // quit on zero // test eax, eax // jz exit IA32_Test_Rm_Reg(&m_HookFunc, REG_EAX, REG_EAX, MOD_REG); tmppos = IA32_Jump_Cond_Imm32(&m_HookFunc, CC_Z, 0); m_HookFunc.start_count(counter); // prev_res = MRES_IGNORED IA32_Mov_Rm_Imm32_Disp8(&m_HookFunc, REG_EBP, MRES_IGNORED, v_cur_res); // iter->call() // push params // ecx = eax // gcc: push ecx // eax = [ecx] // eax = [eax+2*SIZE_PTR] // call eax jit_int32_t gcc_clean_bytes = PushParams(base_param_offset, v_plugin_ret, v_place_for_memret); IA32_Mov_Reg_Rm(&m_HookFunc, REG_ECX, REG_EAX, MOD_REG); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EAX, 2*SIZE_PTR); IA32_Call_Reg(&m_HookFunc, REG_EAX); SaveRetVal(v_plugin_ret, v_place_for_memret); // cleanup #if SH_COMP == SH_COMP_GCC // params + thisptr IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, gcc_clean_bytes + SIZE_PTR, MOD_REG); #endif // process meta return: // prev_res = cur_res // if (cur_res > status) status = cur_res; // // eax = cur_res // edx = status // prev_res = eax // cmp eax,edx // jng thelabel // status = eax // thelabel: // IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_cur_res); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EDX, REG_EBP, v_status); IA32_Mov_Rm_Reg_Disp8(&m_HookFunc, REG_EBP, REG_EAX, v_prev_res); IA32_Cmp_Reg_Rm(&m_HookFunc, REG_EAX, REG_EDX, MOD_REG); tmppos2 = IA32_Jump_Cond_Imm8(&m_HookFunc, CC_NG, 0); m_HookFunc.start_count(counter2); IA32_Mov_Rm_Reg_Disp8(&m_HookFunc, REG_EBP, REG_EAX, v_status); m_HookFunc.end_count(counter2); m_HookFunc.rewrite(tmppos2, static_cast(counter2)); // process retval for non-void functions ProcessPluginRetVal(v_cur_res, v_pContext, v_plugin_ret); // jump back to loop begin tmppos2 = IA32_Jump_Imm32(&m_HookFunc, 0); m_HookFunc.end_count(loop_begin_counter); m_HookFunc.rewrite(tmppos2, -static_cast(loop_begin_counter)); m_HookFunc.end_count(counter); m_HookFunc.rewrite(tmppos, static_cast(counter)); } short GenContext::GetParamsStackSize() { short acc = 0; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { acc += GetStackSize(m_Proto.GetParam(i)); } // Memory return: address is first param if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) acc += SIZE_PTR; // :TODO: cdecl: THIS POINTER AS FIRST PARAM!!! return acc; } void GenContext::GenerateCallOrig(int v_status, int v_pContext, int param_base_offs, int v_this, int v_vfnptr_origentry, int v_orig_ret, int v_override_ret, int v_place_for_memret) { jitoffs_t counter, tmppos; jitoffs_t counter2, tmppos2; jitoffs_t counter3, tmppos3; // if (status != MRES_SUPERCEDE && pConteext->ShouldCallOrig()) // *v_orig_ret = orig_call() // else // *v_orig_ret = *v_override_ret // mov eax, status // cmp eax, MRES_SUPERCEDE // je dont_call // call pContext->ShouldCallOrig() // test al, al !! important: al, not eax! bool is only stored in the LSbyte // jz dont_call // // orig_call() // SaveRet(v_orig_ret) // jmp skip_dont_call: // // dont_call: // *v_orig_ret = *v_override_ret // skip_dont_call: IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_status); IA32_Cmp_Rm_Imm32(&m_HookFunc, MOD_REG, REG_EAX, MRES_SUPERCEDE); tmppos = IA32_Jump_Cond_Imm32(&m_HookFunc, CC_E, 0); m_HookFunc.start_count(counter); // eax = pContext->ShouldCallOrig() // ECX = pContext // gcc: push ecx // eax = [ecx] // eax = [eax + 3*PTR_SIZE] // call eax IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif // vtbloffs=0, vtblidx=3 IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm_Disp8(&m_HookFunc, REG_EAX, REG_EAX, 3*SIZE_PTR); IA32_Call_Reg(&m_HookFunc, REG_EAX); IA32_Test_Rm_Reg8(&m_HookFunc, REG_EAX, REG_EAX, MOD_REG); tmppos2 = IA32_Jump_Cond_Imm32(&m_HookFunc, CC_Z, 0); m_HookFunc.start_count(counter2); // push params jit_int32_t gcc_clean_bytes = PushParams(param_base_offs, v_orig_ret, v_place_for_memret); // thisptr IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_this); #if SH_COMP == SH_COMP_GCC // on gcc/mingw, this is the first parameter IA32_Push_Reg(&m_HookFunc, REG_ECX); // on msvc, simply leave it in ecx #endif // call IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_vfnptr_origentry); IA32_Call_Reg(&m_HookFunc, REG_EAX); // cleanup #if SH_COMP == SH_COMP_GCC // params + thisptr IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, gcc_clean_bytes + SIZE_PTR, MOD_REG); #endif // save retval SaveRetVal(v_orig_ret, v_place_for_memret); // Skip don't call variant tmppos3 = IA32_Jump_Imm32(&m_HookFunc, 0); m_HookFunc.start_count(counter3); // don't call: m_HookFunc.end_count(counter); m_HookFunc.rewrite(tmppos, static_cast(counter)); m_HookFunc.end_count(counter2); m_HookFunc.rewrite(tmppos2, static_cast(counter2)); // *v_orig_ret = *v_override_ret if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov ecx, [ebp + v_override_ret] // mov [ebp + v_orig_ret], ecx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_override_ret); IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_ECX, v_orig_ret); } else { if (m_Proto.GetRet().pAssignOperator) { // lea edx, [ebp + v_override_ret] <-- src addr // lea ecx, [ebp + v_orig_ret] <-- dest addr // gcc: push ecx // push edx <-- src addr // call it IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_override_ret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_orig_ret); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Push_Reg(&m_HookFunc, REG_EDX); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pAssignOperator)); IA32_Call_Reg(&m_HookFunc, REG_EAX); } else { // bitwise copy BitwiseCopy_Setup(); //lea edi, [ebp+v_orig_ret] <-- destination //lea esi, [ebp+v_override_ret] <-- src IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDI, REG_EBP, v_orig_ret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, v_override_ret); BitwiseCopy_Do(m_Proto.GetRet().size); } } // skip don't call label target: m_HookFunc.end_count(counter3); m_HookFunc.rewrite(tmppos3, static_cast(counter3)); } // Sets *v_pContext to return value void GenContext::CallSetupHookLoop(int v_orig_ret, int v_override_ret, int v_cur_res, int v_prev_res, int v_status, int v_vfnptr_origentry, int v_this, int v_pContext) { // call shptr->SetupHookLoop(ms_HI, ourvfnptr, reinterpret_cast(this), // &vfnptr_origentry, &status, &prev_res, &cur_res, &orig_ret, &override_ret); // The last two params are null for void funcs. if (m_Proto.GetRet().size == 0) { // void IA32_Push_Imm8(&m_HookFunc, 0); // orig_ret IA32_Push_Imm8(&m_HookFunc, 0); // override_ret } else { // orig_ret and override_ret IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EAX, REG_EBP, v_override_ret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_orig_ret); IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); } // cur_res and prev_res IA32_Lea_DispRegImm8(&m_HookFunc, REG_EAX, REG_EBP, v_cur_res); IA32_Lea_DispRegImm8(&m_HookFunc, REG_EDX, REG_EBP, v_prev_res); IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); // status and vfnptr_origentry IA32_Lea_DispRegImm8(&m_HookFunc, REG_EAX, REG_EBP, v_status); IA32_Lea_DispRegImm8(&m_HookFunc, REG_EDX, REG_EBP, v_vfnptr_origentry); IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); // this IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_this); IA32_Push_Reg(&m_HookFunc, REG_EAX); // our vfn ptr // *(this + vtbloffs) + SIZE_PTR*vtblidx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_this); // get this into ecx (gcc!) IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_ECX, m_VtblOffs); IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_EAX, m_VtblIdx * SIZE_PTR, MOD_REG); IA32_Push_Reg(&m_HookFunc, REG_EAX); // *m_pHI IA32_Mov_Rm_Imm32(&m_HookFunc, REG_EDX, DownCastPtr(m_pHI), MOD_REG); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_EDX, MOD_MEM_REG); IA32_Push_Reg(&m_HookFunc, REG_EAX); // set up thisptr #if SH_COMP == SH_COMP_GCC // on gcc/mingw, this is the first parameter IA32_Push_Imm32(&m_HookFunc, DownCastPtr(m_SHPtr)); #elif SH_COMP == SH_COMP_MSVC // on msvc, it's ecx IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, DownCastPtr(m_SHPtr)); #endif // call the function. vtbloffs = 0, vtblidx = 19 // get vtptr into edx -- we know shptr on jit time -> dereference it here! IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, (*reinterpret_cast(m_SHPtr))[19]); IA32_Call_Reg(&m_HookFunc, REG_EAX); // on gcc/mingw, we have to clean up after the call #if SH_COMP == SH_COMP_GCC // 9 params + hidden thisptr param IA32_Add_Rm_Imm8(&m_HookFunc, REG_ESP, 10*SIZE_PTR, MOD_REG); #endif // store return value IA32_Mov_Rm_Reg_Disp8(&m_HookFunc, REG_EBP, REG_EAX, v_pContext); } void GenContext::CallEndContext(int v_pContext) { // call endcontext: // shptr->EndContext(pContex) IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_pContext); IA32_Push_Reg(&m_HookFunc, REG_EAX); // thisptr #if SH_COMP == SH_COMP_GCC // on gcc/mingw, this is the first parameter IA32_Push_Imm32(&m_HookFunc, DownCastPtr(m_SHPtr)); #elif SH_COMP == SH_COMP_MSVC // on msvc, it's ecx IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, DownCastPtr(m_SHPtr)); #endif // get vtptr into edx -- we know shptr on jit time -> dereference it here! IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, (*reinterpret_cast(m_SHPtr))[20]); IA32_Call_Reg(&m_HookFunc, REG_EAX); // on gcc/mingw, we have to clean up after the call #if SH_COMP == SH_COMP_GCC // 1 param + hidden thisptr param IA32_Add_Rm_Imm8(&m_HookFunc, REG_ESP, 2*SIZE_PTR, MOD_REG); #endif } void * GenContext::GenerateHookFunc() { // prologue IA32_Push_Reg(&m_HookFunc, REG_EBP); IA32_Push_Reg(&m_HookFunc, REG_EBX); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EBP, REG_ESP, MOD_REG); // on msvc, save thisptr #if SH_COMP == SH_COMP_MSVC const jit_int8_t v_this = -4; const int addstackoffset = -4; IA32_Push_Reg(&m_HookFunc, REG_ECX); #elif SH_COMP == SH_COMP_GCC const jit_int8_t v_this = 8; // first param const int addstackoffset = 0; #endif // ********************** stack frame ********************** // MSVC // second param (gcc: first real param) ebp + 16 // first param (gcc: thisptr) ebp + 12 // ret address: ebp + 8 // caller's ebp ebp + 4 // saved ebx ebp // MSVC ONLY: current this ebp - 4 // void *vfnptr_origentry ebp - 4 -4 // META_RES status = MRES_IGNORED ebp - 8 -4 // META_RES prev_res ebp - 12 -4 // META_RES cur_res ebp - 16 -4 // IMyDelegate *iter ebp - 20 -4 // IHookContext *pContext ebp - 24 -4 // == 3 ptrs + 3 enums = 24 bytes // // non-void: add: // my_rettype *ret_ptr ebp - 28 -4 // my_rettype orig_ret ebp - 28 - sizeof(my_rettype) -4 // my_rettype override_ret ebp - 28 - sizeof(my_rettype)*2 -4 // my_rettype plugin_ret ebp - 28 - sizeof(my_rettype)*3 -4 // == + 3 * sizeof(my_rettype) bytes // if required: // my_rettype place_for_memret ebp - 28 - sizeof(my_rettype)*4 -4 const jit_int8_t v_vfnptr_origentry = -4 + addstackoffset; const jit_int8_t v_status = -8 + addstackoffset; const jit_int8_t v_prev_res = -12 + addstackoffset; const jit_int8_t v_cur_res = -16 + addstackoffset; const jit_int8_t v_iter = -20 + addstackoffset; const jit_int8_t v_pContext = -24 + addstackoffset; #if SH_COMP == SH_COMP_GCC jit_int32_t param_base_offs = 16; #elif SH_COMP == SH_COMP_MSVC jit_int32_t param_base_offs = 12; #endif // Memory return: first param is the address jit_int32_t v_memret_addr = 0; if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { v_memret_addr = param_base_offs; param_base_offs += SIZE_PTR; } jit_int32_t v_ret_ptr = -28 + addstackoffset; jit_int32_t v_orig_ret = -28 + addstackoffset - GetStackSize(m_Proto.GetRet()) * 1; jit_int32_t v_override_ret = -28 + addstackoffset - GetStackSize(m_Proto.GetRet()) * 2; jit_int32_t v_plugin_ret = -28 + addstackoffset - GetStackSize(m_Proto.GetRet()) * 3; jit_int32_t v_place_for_memret = -28 + addstackoffset - GetStackSize(m_Proto.GetRet()) * 4; // Hash for temporary storage for byval params with copy constructors // (param, offset into stack) short usedStackBytes = 3*SIZE_MWORD + 3*SIZE_PTR // vfnptr_origentry, status, prev_res, cur_res, iter, pContext + 3 * GetStackSize(m_Proto.GetRet()) + (m_Proto.GetRet().size == 0 ? 0 : SIZE_PTR) // ret_ptr, orig_ret, override_ret, plugin_ret + (MemRetWithTempObj() ? GetStackSize(m_Proto.GetRet()) : 0) - addstackoffset; // msvc: current thisptr IA32_Sub_Rm_Imm32(&m_HookFunc, REG_ESP, usedStackBytes, MOD_REG); // init status localvar IA32_Mov_Rm_Imm32_Disp8(&m_HookFunc, REG_EBP, MRES_IGNORED, v_status); // Call constructors for ret vars if required if((m_Proto.GetRet().flags & PassInfo::PassFlag_ByVal) && m_Proto.GetRet().pNormalCtor) { // :TODO: Gcc version // orig_reg IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_orig_ret); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pNormalCtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); // override_reg IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_override_ret); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pNormalCtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); // plugin_ret IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_plugin_ret); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pNormalCtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); // _don't_ call a constructor for v_place_for_memret ! } // ********************** SetupHookLoop ********************** CallSetupHookLoop(v_orig_ret, v_override_ret, v_cur_res, v_prev_res, v_status, v_vfnptr_origentry, v_this, v_pContext); // ********************** call pre hooks ********************** GenerateCallHooks(v_status, v_prev_res, v_cur_res, v_iter, v_pContext, param_base_offs, v_plugin_ret, v_place_for_memret); // ********************** call orig func ********************** GenerateCallOrig(v_status, v_pContext, param_base_offs, v_this, v_vfnptr_origentry, v_orig_ret, v_override_ret, v_place_for_memret); // ********************** call post hooks ********************** GenerateCallHooks(v_status, v_prev_res, v_cur_res, v_iter, v_pContext, param_base_offs, v_plugin_ret, v_place_for_memret); // ********************** end context and return ********************** PrepareReturn(v_status, v_pContext, v_ret_ptr); CallEndContext(v_pContext); // Call destructors of byval object params which have a destructor jit_int32_t cur_param_pos = param_base_offs; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { const IntPassInfo &pi = m_Proto.GetParam(i); if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ODtor) && (pi.flags & PassInfo::PassFlag_ByVal)) { IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, cur_param_pos); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(pi.pDtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); } cur_param_pos += GetStackSize(pi); } DoReturn(v_ret_ptr, v_memret_addr); // Call destructors of orig_ret/ ... if((m_Proto.GetRet().flags & PassInfo::PassFlag_ByVal) && m_Proto.GetRet().pDtor) { // Preserve return value in EAX(:EDX) IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_plugin_ret); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_override_ret); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_orig_ret); #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_ECX); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); IA32_Call_Reg(&m_HookFunc, REG_EAX); IA32_Pop_Reg(&m_HookFunc, REG_EDX); IA32_Pop_Reg(&m_HookFunc, REG_EAX); } // epilogue IA32_Mov_Reg_Rm(&m_HookFunc, REG_ESP, REG_EBP, MOD_REG); IA32_Pop_Reg(&m_HookFunc, REG_EBX); IA32_Pop_Reg(&m_HookFunc, REG_EBP); IA32_Return_Popstack(&m_HookFunc, GetParamsStackSize()); // Store pointer for later use // m_HookfuncVfnPtr is a pointer to a void* because SH expects a pointer // into the hookman's vtable *m_HookfuncVfnptr = reinterpret_cast(m_HookFunc.GetData()); m_HookFunc.SetRE(); return m_HookFunc.GetData(); } // Pre-condition: GenerateHookFunc() has been called! void * GenContext::GeneratePubFunc() { jitoffs_t counter, tmppos; // The pubfunc is a static cdecl function. // C Code: // int HookManPubFunc( // bool store, ebp + 8 // IHookManagerInfo *hi ebp + 12 // ) // { // if (store) // *m_pHI = hi; // if (hi) // hi->SetInfo(HOOKMAN_VERSION, m_VtblOffs, m_VtblIdx, m_Proto.GetProto(), m_HookfuncVfnptr) // } // prologue IA32_Push_Reg(&m_PubFunc, REG_EBP); IA32_Mov_Reg_Rm(&m_PubFunc, REG_EBP, REG_ESP, MOD_REG); // save store in eax, hi in ecx IA32_Movzx_Reg32_Rm8_Disp8(&m_PubFunc, REG_EAX, REG_EBP, 8); IA32_Mov_Reg_Rm_DispAuto(&m_PubFunc, REG_ECX, REG_EBP, 12); // Check for store == 0 IA32_Test_Rm_Reg8(&m_PubFunc, REG_EAX, REG_EAX, MOD_REG); tmppos = IA32_Jump_Cond_Imm8(&m_PubFunc, CC_Z, 0); m_PubFunc.start_count(counter); // nonzero -> store hi IA32_Mov_Rm_Imm32(&m_PubFunc, REG_EDX, DownCastPtr(m_pHI), MOD_REG); IA32_Mov_Rm_Reg(&m_PubFunc, REG_EDX, REG_ECX, MOD_MEM_REG); // zero m_PubFunc.end_count(counter); m_PubFunc.rewrite(tmppos, static_cast(counter)); // check for hi == 0 IA32_Test_Rm_Reg(&m_PubFunc, REG_ECX, REG_ECX, MOD_REG); tmppos = IA32_Jump_Cond_Imm8(&m_PubFunc, CC_Z, 0); m_PubFunc.start_count(counter); // nonzero -> call vfunc // push params in reverse order IA32_Push_Imm32(&m_PubFunc, DownCastPtr(m_HookfuncVfnptr)); IA32_Push_Imm32(&m_PubFunc, DownCastPtr(m_BuiltPI)); IA32_Push_Imm32(&m_PubFunc, m_VtblIdx); IA32_Push_Imm32(&m_PubFunc, m_VtblOffs); IA32_Push_Imm32(&m_PubFunc, SH_HOOKMAN_VERSION); // hi == this is in ecx // on gcc/mingw, ecx is the first parameter #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_PubFunc, REG_ECX); #endif // call the function. vtbloffs = 0, vtblidx = 0 // get vtptr into edx IA32_Mov_Reg_Rm(&m_PubFunc, REG_EDX, REG_ECX, MOD_MEM_REG); // get funcptr into eax IA32_Mov_Reg_Rm(&m_PubFunc, REG_EAX, REG_EDX, MOD_MEM_REG); IA32_Call_Reg(&m_PubFunc, REG_EAX); // on gcc/mingw, we have to clean up after the call #if SH_COMP == SH_COMP_GCC // 5 params + hidden thisptr param IA32_Add_Rm_Imm8(&m_PubFunc, REG_ESP, 6*SIZE_MWORD, MOD_REG); #endif // zero m_PubFunc.end_count(counter); m_PubFunc.rewrite(tmppos, static_cast(counter)); // return value IA32_Xor_Reg_Rm(&m_PubFunc, REG_EAX, REG_EAX, MOD_REG); // epilogue IA32_Mov_Reg_Rm(&m_PubFunc, REG_ESP, REG_EBP, MOD_REG); IA32_Pop_Reg(&m_PubFunc, REG_EBP); IA32_Return(&m_PubFunc); m_PubFunc.SetRE(); return m_PubFunc; } bool GenContext::PassInfoSupported(const IntPassInfo &pi, bool is_ret) { if (pi.type != PassInfo::PassType_Basic && pi.type != PassInfo::PassType_Float && pi.type != PassInfo::PassType_Object) { return false; } if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ByVal)) { if ((pi.flags & PassInfo::PassFlag_CCtor) && !pi.pCopyCtor) return false; if ((pi.flags & PassInfo::PassFlag_ODtor) && !pi.pDtor) return false; if ((pi.flags & PassInfo::PassFlag_AssignOp) && !pi.pAssignOperator) return false; if ((pi.flags & PassInfo::PassFlag_OCtor) && !pi.pNormalCtor) return false; } if ((pi.flags & (PassInfo::PassFlag_ByVal | PassInfo::PassFlag_ByRef)) == 0) { return false; // Neither byval nor byref! } return true; } void GenContext::AutoDetectRetType() { IntPassInfo &pi = m_Proto.GetRet(); // Only relevant for byval types if (pi.flags & PassInfo::PassFlag_ByVal) { // Basic + float: if (pi.type == PassInfo::PassType_Basic || pi.type == PassInfo::PassType_Float) { // <= 8 bytes: // _always_ in registers, no matter what the user says if (pi.size <= 8) { pi.flags &= ~PassInfo::PassFlag_RetMem; pi.flags |= PassInfo::PassFlag_RetReg; } else { // Does this even exist? No idea, if it does: in memory! pi.flags &= ~PassInfo::PassFlag_RetReg; pi.flags |= PassInfo::PassFlag_RetMem; } } // Object: else if (pi.type == PassInfo::PassType_Object) { // If the user says nothing, auto-detect if ((pi.flags & (PassInfo::PassFlag_RetMem | PassInfo::PassFlag_RetReg)) == 0) { #if SH_COMP == SH_COMP_MSVC // MSVC seems to return _all_ structs, classes, unions in memory pi.flags |= PassInfo::PassFlag_RetMem; #elif SH_COMP == SH_COMP_GCC // Same goes for GCC :) pi.flags |= PassInfo::PassFlag_RetMem; #endif } } } else { // byref: make sure that the flag is _not_ set pi.flags &= ~PassInfo::PassFlag_RetMem; pi.flags |= PassInfo::PassFlag_RetReg; } } HookManagerPubFunc GenContext::Generate() { Clear(); // Check conditions: // -1) good proto version // 0) we don't support unknown passtypes, convention, ... // 1) we don't support functions which return objects by value or take parameters by value // that have a constructor, a destructor or an overloaded assignment op // (we wouldn't know how to call it!) if (m_Proto.GetVersion() < 1) { return NULL; } AutoDetectRetType(); if (m_Proto.GetConvention() != ProtoInfo::CallConv_Cdecl && m_Proto.GetConvention() != ProtoInfo::CallConv_ThisCall) { return NULL; } if (m_Proto.GetRet().size != 0 && !PassInfoSupported(m_Proto.GetRet(), true)) { return NULL; } for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { if (!PassInfoSupported(m_Proto.GetParam(i), false)) return NULL; } BuildProtoInfo(); GenerateHookFunc(); return fastdelegate::detail::horrible_cast(GeneratePubFunc()); } } }