DWORD PLH::AbstractDetour::CalculateLength(BYTE* Src, DWORD NeededLength) { //Grab First 100 bytes of function, disasm until invalid instruction cs_insn* InstructionInfo; size_t InstructionCount = cs_disasm(m_CapstoneHandle, Src, 0x100, (uint64_t)Src, 0, &InstructionInfo); //Loop over instructions until we have at least NeededLength's Size XTrace("\nORIGINAL:\n"); DWORD InstructionSize = 0; bool BigEnough = false; for (int i = 0; i < InstructionCount && !BigEnough; i++) { cs_insn* CurIns = (cs_insn*)&InstructionInfo[i]; InstructionSize += CurIns->size; if (InstructionSize >= NeededLength) BigEnough = true; XTrace("%I64X [%d]: ", CurIns->address, CurIns->size); for (int j = 0; j < CurIns->size; j++) XTrace("%02X ", CurIns->bytes[j]); XTrace("%s %s\n", CurIns->mnemonic, CurIns->op_str); } if (!BigEnough) InstructionSize = 0; cs_free(InstructionInfo, InstructionCount); return InstructionSize; }
void PLH::AbstractDetour::Initialize(cs_mode Mode) { if (cs_open(CS_ARCH_X86, Mode, &m_CapstoneHandle) != CS_ERR_OK) XTrace("Error Initializing Capstone x86\n"); cs_option(m_CapstoneHandle, CS_OPT_DETAIL, CS_OPT_ON); }
bool PLH::X86Detour::Hook() { DWORD OldProtection; m_hkLength = CalculateLength(m_hkSrc, 5); m_OriginalLength = m_hkLength; if (m_hkLength == 0) { XTrace("Function to small to hook\n"); return false; } m_Trampoline = new BYTE[m_hkLength + 30]; //Allocate Space for original plus extra to jump back and for jmp table VirtualProtect(m_Trampoline, m_hkLength + 5, PAGE_EXECUTE_READWRITE, &OldProtection); //Allow Execution m_NeedFree = true; memcpy(m_OriginalCode, m_hkSrc, m_hkLength); memcpy(m_Trampoline, m_hkSrc, m_hkLength); //Copy original into allocated space RelocateASM(m_Trampoline, m_hkLength, (DWORD)m_hkSrc, (DWORD)m_Trampoline); WriteRelativeJMP((DWORD)&m_Trampoline[m_hkLength], (DWORD)m_hkSrc + m_hkLength); //JMP back to original code //Change protection to allow write on original function MemoryProtect Protector(m_hkSrc, m_hkLength, PAGE_EXECUTE_READWRITE); //Encode Jump from Hooked Function to the Destination function WriteRelativeJMP((DWORD)m_hkSrc, (DWORD)m_hkDest); //Write nops over bytes of overwritten instructions for (int i = 5; i < m_OriginalLength; i++) m_hkSrc[i] = 0x90; FlushSrcInsCache(); //Revert to old protection on original function VirtualProtect(m_hkSrc, m_hkLength, OldProtection, &OldProtection); PostError(RuntimeError(RuntimeError::Severity::Warning, "PolyHook x86Detour: Some opcodes may not be relocated properly")); return true; /*Original -JMP Destination -NOP (extends to length of overwritten opcode) -Rest of function Destination -Do your shit -Return Trampoline (goes to trampoline) Trampoline -Execute Overwritten Opcodes -Patch original relative jmps to point to jump table (JE Jumptable entry 1) -JMP to rest of function (in original) -*BEGIN JUMPTABLE* -1)JMP to location of relative jmp one -2)...continue pattern for all relative jmps */ }
void PLH::AbstractDetour::_Relocate(cs_insn* CurIns, DWORD64 From, DWORD64 To, const uint8_t DispSize, const uint8_t DispOffset) { XTrace("Relocating...\n"); ASMHelper::DISP DispType = m_ASMInfo.GetDisplacementType(DispSize); if (DispType == ASMHelper::DISP::D_INT8) { int8_t Disp = m_ASMInfo.GetDisplacement<int8_t>(CurIns->bytes, DispOffset); Disp -= (To - From); *(int8_t*)(CurIns->address + DispOffset) = Disp; }else if (DispType == ASMHelper::DISP::D_INT16) { int16_t Disp = m_ASMInfo.GetDisplacement<int16_t>(CurIns->bytes, DispOffset); Disp -= (To - From); *(int16_t*)(CurIns->address + DispOffset) = Disp; }else if (DispType == ASMHelper::DISP::D_INT32) { int32_t Disp = m_ASMInfo.GetDisplacement<int32_t>(CurIns->bytes, DispOffset); Disp -= (To - From); *(int32_t*)(CurIns->address + DispOffset) = Disp; } }
void PLH::IHook::PrintError(const RuntimeError& Err) const { std::string Severity = ""; switch (Err.GetSeverity()) { case PLH::RuntimeError::Severity::Warning: Severity = "Warning"; break; case PLH::RuntimeError::Severity::Critical: Severity = "Critical"; break; case PLH::RuntimeError::Severity::UnRecoverable: Severity = "UnRecoverable"; break; case PLH::RuntimeError::Severity::NoError: Severity = "No Error"; break; default: Severity = "Unknown"; } XTrace("SEVERITY:[%s] %s\n", Severity.c_str(), Err.GetString().c_str()); }
bool PLH::IATHook::FindIATFunc(const char* LibraryName,const char* FuncName, PIMAGE_THUNK_DATA* pFuncThunkOut,const char* Module) { bool UseModuleName = true; if (Module == NULL || Module[0] == '\0') UseModuleName = false; HINSTANCE hInst = GetModuleHandleA(UseModuleName ? Module:NULL); if (!hInst) { PostError(RuntimeError(RuntimeError::Severity::UnRecoverable, "PolyHook IATHook:Failed to find Module")); return false; } ULONG Sz; PIMAGE_IMPORT_DESCRIPTOR pImports = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToDataEx(hInst, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Sz, nullptr); for (int i = 0; pImports[i].Characteristics != 0; i++) { char* _ModuleName = (char*)ResolveRVA(hInst, pImports[i].Name); if (_stricmp(_ModuleName, LibraryName) != 0) continue; //Original holds the API Names PIMAGE_THUNK_DATA pOriginalThunk = (PIMAGE_THUNK_DATA) ResolveRVA(hInst, pImports->OriginalFirstThunk); //FirstThunk is overwritten by loader with API addresses, we change this PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ResolveRVA(hInst, pImports->FirstThunk); if (!pOriginalThunk) { PostError(RuntimeError(RuntimeError::Severity::Critical, "PolyHook IATHook:PE Files without OriginalFirstThunk are unsupported")); return false; } //Table is null terminated, increment both tables for (; pOriginalThunk->u1.Function != NULL; pOriginalThunk++,pThunk++) { if (IMAGE_SNAP_BY_ORDINAL(pOriginalThunk->u1.Ordinal)) { XTrace("Import By Ordinal:[Ordinal:%d]\n",IMAGE_ORDINAL(pOriginalThunk->u1.Ordinal)); continue; } PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME) ResolveRVA(hInst, pOriginalThunk->u1.AddressOfData); XTrace("Import By Name: [Ordinal:%d] [Name:%s]\n", IMAGE_ORDINAL(pOriginalThunk->u1.Ordinal),pImport->Name); //Check the name of API given by OriginalFirthThunk if (_stricmp(FuncName, pImport->Name) != 0) continue; /*Name matched in OriginalFirstThunk, return FirstThunk so we can changed it's address*/ *pFuncThunkOut = pThunk; return true; } } PostError(RuntimeError(RuntimeError::Severity::UnRecoverable, "PolyHook IATHook:Failed to find import")); return false; }
void PLH::IHook::PrintError(const RuntimeError& Err) const { XTrace("%s %s\n", (Err.GetSeverity() == PLH::RuntimeError::Severity::NoError) ? "No Error" : "Error", Err.GetString().c_str()); }
void PLH::IHook::PostError(const RuntimeError& Err) { m_LastError = Err; XTrace("Posted Error [SEVERITY:%d]:\n" "%s\n",Err.GetSeverity(), Err.GetString().c_str()); }
void PLH::AbstractDetour::RelocateASM(BYTE* Code, DWORD& CodeSize, DWORD64 From, DWORD64 To) { cs_insn* InstructionInfo; size_t InstructionCount = cs_disasm(m_CapstoneHandle, Code, CodeSize, (uint64_t)Code, 0, &InstructionInfo); XTrace("\nTrampoline:\n"); for (int i = 0; i < InstructionCount; i++) { cs_insn* CurIns = (cs_insn*)&InstructionInfo[i]; cs_x86* x86 = &(CurIns->detail->x86); XTrace("%I64X: ", CurIns->address); for (int j = 0; j < CurIns->size; j++) XTrace("%02X ", CurIns->bytes[j]); XTrace("%s %s\n", CurIns->mnemonic,CurIns->op_str); for (int j = 0; j < x86->op_count; j++) { cs_x86_op* op = &(x86->operands[j]); if (op->type == X86_OP_MEM) { //MEM are types like lea rcx,[rip+0xdead] if (op->mem.base == X86_REG_INVALID) continue; //Are we relative to instruction pointer? if (op->mem.base != GetIpReg()) continue; _Relocate(CurIns, From, To, x86->offsets.displacement_size, x86->offsets.displacement_offset); }else if (op->type == X86_OP_IMM) { //IMM types are like call 0xdeadbeef if (x86->op_count > 1) //exclude types like sub rsp,0x20 continue; char* mnemonic = CurIns->mnemonic; if (m_ASMInfo.IsConditionalJump(CurIns->bytes,CurIns->size)) { RelocateConditionalJMP(CurIns, CodeSize, From, To, x86->offsets.imm_size, x86->offsets.imm_offset); continue; } //types like push 0x20 slip through, check mnemonic if (strcmp(mnemonic, "call") != 0 && strcmp(mnemonic, "jmp") != 0) //probably more types than just these, update list as they're found continue; _Relocate(CurIns, From, To, x86->offsets.imm_size, x86->offsets.imm_offset); } } } XTrace("\nFixed Trampoline\n"); InstructionCount = cs_disasm(m_CapstoneHandle, Code, CodeSize, (uint64_t)Code, 0, &InstructionInfo); for (int i = 0; i < InstructionCount; i++) { cs_insn* CurIns = (cs_insn*)&InstructionInfo[i]; XTrace("%I64X: ", CurIns->address); for (int j = 0; j < CurIns->size; j++) XTrace("%02X ", CurIns->bytes[j]); XTrace("%s %s\n", CurIns->mnemonic, CurIns->op_str); } cs_free(InstructionInfo, InstructionCount); }