Add missing files.

This commit is contained in:
Ruben Gonzalez 2016-09-03 09:06:10 -04:00
parent 9437b653fa
commit 56a273b8c7
5 changed files with 652 additions and 0 deletions

420
core/detourhook/asm/asm.c Normal file
View File

@ -0,0 +1,420 @@
#include "asm.h"
#ifndef WIN32
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define REG_EAX 0
#define REG_ECX 1
#define REG_EDX 2
#define REG_EBX 3
#define IA32_MOV_REG_IMM 0xB8 // encoding is +r <imm32>
#endif
/**
* Checks if a call to a fpic thunk has just been written into dest.
* If found replaces it with a direct mov that sets the required register to the value of pc.
*
* @param dest Destination buffer where a call opcode + addr (5 bytes) has just been written.
* @param pc The program counter value that needs to be set (usually the next address from the source).
* @noreturn
*/
void check_thunks(unsigned char *dest, unsigned char *pc)
{
#ifndef WIN32
/* Step write address back 4 to the start of the function address */
unsigned char *writeaddr = dest - 4;
unsigned char *calloffset = *(unsigned char **)writeaddr;
unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset);
/* Lookup name of function being called */
if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3))
{
//a thunk maybe?
char movByte = IA32_MOV_REG_IMM;
/* Calculate the correct mov opcode */
switch (*(calladdr+1))
{
case 0x04:
{
movByte += REG_EAX;
break;
}
case 0x1C:
{
movByte += REG_EBX;
break;
}
case 0x0C:
{
movByte += REG_ECX;
break;
}
case 0x14:
{
movByte += REG_EDX;
break;
}
default:
{
printf("Unknown thunk: %c\n", *(calladdr+1));
#ifndef NDEBUG
abort();
#endif
break;
}
}
/* Move our write address back one to where the call opcode was */
writeaddr--;
/* Write our mov */
*writeaddr = movByte;
writeaddr++;
/* Write the value - The provided program counter value */
*(void **)writeaddr = (void *)pc;
writeaddr += 4;
}
#endif
}
//if dest is NULL, returns minimum number of bytes needed to be copied
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
//http://www.devmaster.net/forums/showthread.php?t=2311
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) {
int bytecount = 0;
while(bytecount < required_len && *func != 0xCC)
{
// prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h
int operandSize = 4;
int FPU = 0;
int twoByte = 0;
unsigned char opcode = 0x90;
unsigned char modRM = 0xFF;
while(*func == 0xF0 ||
*func == 0xF2 ||
*func == 0xF3 ||
(*func & 0xFC) == 0x64 ||
(*func & 0xF8) == 0xD8 ||
(*func & 0x7E) == 0x62)
{
if(*func == 0x66)
{
operandSize = 2;
}
else if((*func & 0xF8) == 0xD8)
{
FPU = *func;
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
break;
}
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
// two-byte opcode byte
if(*func == 0x0F)
{
twoByte = 1;
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
// opcode byte
opcode = *func++;
if (dest) *dest++ = opcode;
bytecount++;
// mod R/M byte
modRM = 0xFF;
if(FPU)
{
if((opcode & 0xC0) != 0xC0)
{
modRM = opcode;
}
}
else if(!twoByte)
{
if((opcode & 0xC4) == 0x00 ||
((opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09)) ||
(opcode & 0xF0) == 0x80 ||
((opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02) ||
(opcode & 0xFC) == 0xD0 ||
(opcode & 0xF6) == 0xF6)
{
modRM = *func++;
if (dest) *dest++ = modRM;
bytecount++;
}
}
else
{
if(((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D) ||
(opcode & 0xF0) == 0x30 ||
opcode == 0x77 ||
(opcode & 0xF0) == 0x80 ||
((opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02) ||
(opcode & 0xF8) == 0xC8)
{
// No mod R/M byte
}
else
{
modRM = *func++;
if (dest) *dest++ = modRM;
bytecount++;
}
}
// SIB
if((modRM & 0x07) == 0x04 &&
(modRM & 0xC0) != 0xC0)
{
if (dest)
*dest++ = *func++; //SIB
else
func++;
bytecount++;
}
// mod R/M displacement
// Dword displacement, no base
if((modRM & 0xC5) == 0x05) {
if (dest) {
*(unsigned int*)dest = *(unsigned int*)func;
dest += 4;
}
func += 4;
bytecount += 4;
}
// Byte displacement
if((modRM & 0xC0) == 0x40) {
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
// Dword displacement
if((modRM & 0xC0) == 0x80) {
if (dest) {
*(unsigned int*)dest = *(unsigned int*)func;
dest += 4;
}
func += 4;
bytecount += 4;
}
// immediate
if(FPU)
{
// Can't have immediate operand
}
else if(!twoByte)
{
if((opcode & 0xC7) == 0x04 ||
(opcode & 0xFE) == 0x6A || // PUSH/POP/IMUL
(opcode & 0xF0) == 0x70 || // Jcc
opcode == 0x80 ||
opcode == 0x83 ||
(opcode & 0xFD) == 0xA0 || // MOV
opcode == 0xA8 || // TEST
(opcode & 0xF8) == 0xB0 || // MOV
(opcode & 0xFE) == 0xC0 || // RCL
opcode == 0xC6 || // MOV
opcode == 0xCD || // INT
(opcode & 0xFE) == 0xD4 || // AAD/AAM
(opcode & 0xF8) == 0xE0 || // LOOP/JCXZ
opcode == 0xEB ||
(opcode == 0xF6 && (modRM & 0x30) == 0x00)) // TEST
{
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
else if((opcode & 0xF7) == 0xC2) // RET
{
if (dest) {
*(unsigned short*)dest = *(unsigned short*)func;
dest += 2;
}
func += 2;
bytecount += 2;
}
else if((opcode & 0xFC) == 0x80 ||
(opcode & 0xC7) == 0x05 ||
(opcode & 0xF8) == 0xB8 ||
(opcode & 0xFE) == 0xE8 || // CALL/Jcc
(opcode & 0xFE) == 0x68 ||
(opcode & 0xFC) == 0xA0 ||
(opcode & 0xEE) == 0xA8 ||
opcode == 0xC7 ||
(opcode == 0xF7 && (modRM & 0x30) == 0x00))
{
if (dest) {
//Fix CALL/JMP offset
if ((opcode & 0xFE) == 0xE8) {
if (operandSize == 4)
{
*(long*)dest = ((func + *(long*)func) - dest);
//pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc
check_thunks(dest+4, func+4);
}
else
*(short*)dest = ((func + *(short*)func) - dest);
} else {
if (operandSize == 4)
*(unsigned long*)dest = *(unsigned long*)func;
else
*(unsigned short*)dest = *(unsigned short*)func;
}
dest += operandSize;
}
func += operandSize;
bytecount += operandSize;
}
}
else
{
if(opcode == 0xBA || // BT
opcode == 0x0F || // 3DNow!
(opcode & 0xFC) == 0x70 || // PSLLW
(opcode & 0xF7) == 0xA4 || // SHLD
opcode == 0xC2 ||
opcode == 0xC4 ||
opcode == 0xC5 ||
opcode == 0xC6)
{
if (dest)
*dest++ = *func++;
else
func++;
}
else if((opcode & 0xF0) == 0x80) // Jcc -i
{
if (dest) {
if (operandSize == 4)
*(unsigned long*)dest = *(unsigned long*)func;
else
*(unsigned short*)dest = *(unsigned short*)func;
dest += operandSize;
}
func += operandSize;
bytecount += operandSize;
}
}
}
return bytecount;
}
//insert a specific JMP instruction at the given location
void inject_jmp(void* src, void* dest) {
*(unsigned char*)src = OP_JMP;
*(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE));
}
//fill a given block with NOPs
void fill_nop(void* src, unsigned int len) {
unsigned char* src2 = (unsigned char*)src;
while (len) {
*src2++ = OP_NOP;
--len;
}
}
void* eval_jump(void* src) {
unsigned char* addr = (unsigned char*)src;
if (!addr) return 0;
//import table jump
if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) {
addr += 2;
addr = *(unsigned char**)addr;
//TODO: if addr points into the IAT
return *(void**)addr;
}
//8bit offset
else if (addr[0] == OP_JMP_BYTE) {
addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1];
//mangled 32bit jump?
if (addr[0] == OP_JMP) {
addr = addr + *(int*)&addr[1];
}
return addr;
}
/*
//32bit offset
else if (addr[0] == OP_JMP) {
addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1];
}
*/
return addr;
}
/*
from ms detours package
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
if (pbAddress >= ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pbAddress < ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
return true;
}
return false;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
return false;
}
}
*/

40
core/detourhook/asm/asm.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef __ASM_H__
#define __ASM_H__
#define OP_JMP 0xE9
#define OP_JMP_SIZE 5
#define OP_NOP 0x90
#define OP_NOP_SIZE 1
#define OP_PREFIX 0xFF
#define OP_JMP_SEG 0x25
#define OP_JMP_BYTE 0xEB
#define OP_JMP_BYTE_SIZE 2
#ifdef __cplusplus
extern "C" {
#endif
void check_thunks(unsigned char *dest, unsigned char *pc);
//if dest is NULL, returns minimum number of bytes needed to be copied
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
//http://www.devmaster.net/forums/showthread.php?t=2311
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len);
//insert a specific JMP instruction at the given location
void inject_jmp(void* src, void* dest);
//fill a given block with NOPs
void fill_nop(void* src, unsigned int len);
//evaluate a JMP at the target
void* eval_jump(void* src);
#ifdef __cplusplus
}
#endif
#endif //__ASM_H__

View File

@ -0,0 +1,101 @@
#include "detourhook_impl.h"
#include <metamod.h>
#include "asm\asm.h"
//#include "asmjit\asmjit.h"
using namespace DetourHook;
//using namespace asmjit;
//using namespace asmjit::x86;
int CDetourHookImpl::GetIfaceVersion()
{
return 1;
}
CDetourHook::CDetourHook(void *addr, void *callback)
{
this->pFunc = addr;
this->pCallback = callback;
this->bEnabled = false;
this->bytes = copy_bytes((unsigned char *)this->pFunc, NULL, OP_JMP_SIZE + 1);
this->pOrginalBytes = malloc(this->bytes);
SourceHook::SetMemAccess(this->pFunc, this->bytes, SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
memcpy(this->pOrginalBytes, this->pFunc, this->bytes);
this->pTrampoline = malloc(this->bytes + OP_JMP_SIZE + 1); //Allocate space
SourceHook::SetMemAccess(this->pTrampoline, bytes + OP_JMP_SIZE + 1, SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
//Copy our original bytes to our trampoline
//We use this to fix JMP's and whatever else is relative to the function.
copy_bytes((unsigned char *)this->pFunc, (unsigned char *)this->pTrampoline, this->bytes);
//JMP from trampoline +bytes to function + bytes
inject_abs_jmp((unsigned char*)this->pTrampoline + this->bytes, (unsigned char*)this->pFunc + this->bytes);
}
CDetourHook::~CDetourHook()
{
this->DisableDetour();
//Clean up
free(this->pTrampoline);
free(this->pOrginalBytes);
}
void *CDetourHook::GetTrampoline()
{
return this->pTrampoline;
}
void CDetourHook::DisableDetour()
{
if (this->bEnabled)
{
this->bEnabled = false;
memcpy(this->pFunc, this->pOrginalBytes, this->bytes);
}
}
void CDetourHook::EnableDetour()
{
if (!this->bEnabled)
{
//Nop all the bytes needed
fill_nop(this->pFunc, bytes);
//JMP from function to our callback
inject_abs_jmp((unsigned char *)this->pFunc, &this->pCallback);
this->bEnabled = true;
}
}
CDetourHook *CDetourHookImpl::CreateDetour(void *addr, void *callback)
{
CDetourHook *ret = new CDetourHook(addr, callback);
return ret;
}
void *CDetourHookImpl::GetTrampoline(CDetourHook *det)
{
return det->GetTrampoline();
}
void CDetourHookImpl::EnableDetour(CDetourHook *det)
{
det->EnableDetour();
}
void CDetourHookImpl::DisableDetour(CDetourHook *det)
{
det->DisableDetour();
}
void CDetourHookImpl::DeleteDetour(CDetourHook *det)
{
delete det;
}

View File

@ -0,0 +1,45 @@
#pragma once
#ifndef DH_GLOB_DHPTR
#define DH_GLOB_DHPTR g_DHPtr
#endif
#define DH_STATIC_DETOURHOOK(name, ret, ...) typedef ret(*name##FN)(__VA_ARGS__);
#define DH_STATIC_CALL(name, detourhook) ((name##FN)g_DHPtr->GetTrampoline(detourhook))
#define DH_MEMBER_DETOURHOOK(name, ret, ...) typedef ret(__thiscall *name##ThisFn)(void *thisptr, __VA_ARGS__);
#define DH_MEMBER_CALL(name, detourhook) ((name##ThisFn)g_DHPtr->GetTrampoline(detourhook))
class GenericClass {};
typedef void (GenericClass::*VoidFunc)();
inline void *GetCodeAddr(VoidFunc mfp)
{
return *(void **)&mfp;
}
/**
* Converts a member function pointer to a void pointer.
* This relies on the assumption that the code address lies at mfp+0
* This is the case for both g++ and later MSVC versions on non virtual functions but may be different for other compilers
* Based on research by Don Clugston : http://www.codeproject.com/cpp/FastDelegate.asp
*/
#define GetCodeAddress(mfp) GetCodeAddr(reinterpret_cast<VoidFunc>(mfp))
namespace DetourHook
{
class CDetourHook;
class IDetourHook
{
public:
virtual int GetIfaceVersion() = 0;
virtual CDetourHook *CreateDetour(void *addr, void *callback) = 0;
virtual void *GetTrampoline(CDetourHook *det) = 0;
virtual void EnableDetour(CDetourHook *det) = 0;
virtual void DisableDetour(CDetourHook *det) = 0;
virtual void DeleteDetour(CDetourHook *det) = 0;
};
};

View File

@ -0,0 +1,46 @@
#pragma once
#include "detourhook.h"
#include <sourcehook.h>
#include <sh_memory.h>
namespace DetourHook
{
class CDetourHook
{
public:
CDetourHook(void *addr, void *callback);
~CDetourHook();
void EnableDetour();
void DisableDetour();
void *GetTrampoline();
private:
void *pTrampoline;
void *pFunc;
void *pCallback;
void *pOrginalBytes;
int bytes;
bool bEnabled;
};
class CDetourHookImpl : public IDetourHook
{
int GetIfaceVersion();
CDetourHook *CreateDetour(void *addr, void *callback);
void *GetTrampoline(CDetourHook *det);
void EnableDetour(CDetourHook *det);
void DisableDetour(CDetourHook *det);
void DeleteDetour(CDetourHook *det);
};
}
#define OP_JMP_SIZE 5
inline void inject_abs_jmp(unsigned char *target, void *dest)
{
SourceHook::SetMemAccess(target, OP_JMP_SIZE + 1, SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
target[0] = 0xFF; /* JMP */
target[1] = 0x25; /* MEM32 */
*(void **)(&target[2]) = dest;
}