mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-24 23:22:25 +01:00
byuu says: Basically just a project rename, with s/bsnes/higan and the new icon from lowkee added in. It won't compile on Windows because I forgot to update the resource.rc file, and a path transform command isn't working on Windows. It was really just meant as a starting point, so that v091 WIPs can flow starting from .00 with the new name (it overshadows bsnes v091, so publicly speaking this "shouldn't exist" and will probably be deleted from Google Code when v092 is ready.)
193 lines
5.8 KiB
C++
Executable File
193 lines
5.8 KiB
C++
Executable File
#ifndef NALL_WINDOWS_DETOUR_HPP
|
|
#define NALL_WINDOWS_DETOUR_HPP
|
|
|
|
#include <nall/foreach.hpp>
|
|
#include <nall/platform.hpp>
|
|
#include <nall/stdint.hpp>
|
|
#include <nall/string.hpp>
|
|
#include <nall/utf8.hpp>
|
|
|
|
namespace nall {
|
|
|
|
#define Copy 0
|
|
#define RelNear 1
|
|
|
|
struct detour {
|
|
static bool insert(const string &moduleName, const string &functionName, void *&source, void *target);
|
|
static bool remove(const string &moduleName, const string &functionName, void *&source);
|
|
|
|
protected:
|
|
static unsigned length(const uint8_t *function);
|
|
static unsigned mirror(uint8_t *target, const uint8_t *source);
|
|
|
|
struct opcode {
|
|
uint16_t prefix;
|
|
unsigned length;
|
|
unsigned mode;
|
|
uint16_t modify;
|
|
};
|
|
static opcode opcodes[];
|
|
};
|
|
|
|
//TODO:
|
|
//* fs:, gs: should force another opcode copy
|
|
//* conditional branches within +5-byte range should fail
|
|
detour::opcode detour::opcodes[] = {
|
|
{ 0x50, 1 }, //push eax
|
|
{ 0x51, 1 }, //push ecx
|
|
{ 0x52, 1 }, //push edx
|
|
{ 0x53, 1 }, //push ebx
|
|
{ 0x54, 1 }, //push esp
|
|
{ 0x55, 1 }, //push ebp
|
|
{ 0x56, 1 }, //push esi
|
|
{ 0x57, 1 }, //push edi
|
|
{ 0x58, 1 }, //pop eax
|
|
{ 0x59, 1 }, //pop ecx
|
|
{ 0x5a, 1 }, //pop edx
|
|
{ 0x5b, 1 }, //pop ebx
|
|
{ 0x5c, 1 }, //pop esp
|
|
{ 0x5d, 1 }, //pop ebp
|
|
{ 0x5e, 1 }, //pop esi
|
|
{ 0x5f, 1 }, //pop edi
|
|
{ 0x64, 1 }, //fs:
|
|
{ 0x65, 1 }, //gs:
|
|
{ 0x68, 5 }, //push dword
|
|
{ 0x6a, 2 }, //push byte
|
|
{ 0x74, 2, RelNear, 0x0f84 }, //je near -> je far
|
|
{ 0x75, 2, RelNear, 0x0f85 }, //jne near -> jne far
|
|
{ 0x89, 2 }, //mov reg,reg
|
|
{ 0x8b, 2 }, //mov reg,reg
|
|
{ 0x90, 1 }, //nop
|
|
{ 0xa1, 5 }, //mov eax,[dword]
|
|
{ 0xeb, 2, RelNear, 0xe9 }, //jmp near -> jmp far
|
|
};
|
|
|
|
bool detour::insert(const string &moduleName, const string &functionName, void *&source, void *target) {
|
|
HMODULE module = GetModuleHandleW(utf16_t(moduleName));
|
|
if(!module) return false;
|
|
|
|
uint8_t *sourceData = (uint8_t*)GetProcAddress(module, functionName);
|
|
if(!sourceData) return false;
|
|
|
|
unsigned sourceLength = detour::length(sourceData);
|
|
if(sourceLength < 5) {
|
|
//unable to clone enough bytes to insert hook
|
|
#if 1
|
|
string output = { "detour::insert(", moduleName, "::", functionName, ") failed: " };
|
|
for(unsigned n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " ");
|
|
output.rtrim<1>(" ");
|
|
MessageBoxA(0, output, "nall::detour", MB_OK);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
uint8_t *mirrorData = new uint8_t[512]();
|
|
detour::mirror(mirrorData, sourceData);
|
|
|
|
DWORD privileges;
|
|
VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges);
|
|
VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
|
|
uintmax_t address = (uintmax_t)target - ((uintmax_t)sourceData + 5);
|
|
sourceData[0] = 0xe9; //jmp target
|
|
sourceData[1] = address >> 0;
|
|
sourceData[2] = address >> 8;
|
|
sourceData[3] = address >> 16;
|
|
sourceData[4] = address >> 24;
|
|
VirtualProtect((void*)sourceData, 256, privileges, &privileges);
|
|
|
|
source = (void*)mirrorData;
|
|
return true;
|
|
}
|
|
|
|
bool detour::remove(const string &moduleName, const string &functionName, void *&source) {
|
|
HMODULE module = GetModuleHandleW(utf16_t(moduleName));
|
|
if(!module) return false;
|
|
|
|
uint8_t *sourceData = (uint8_t*)GetProcAddress(module, functionName);
|
|
if(!sourceData) return false;
|
|
|
|
uint8_t *mirrorData = (uint8_t*)source;
|
|
if(mirrorData == sourceData) return false; //hook was never installed
|
|
|
|
unsigned length = detour::length(256 + mirrorData);
|
|
if(length < 5) return false;
|
|
|
|
DWORD privileges;
|
|
VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
|
|
for(unsigned n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n];
|
|
VirtualProtect((void*)sourceData, 256, privileges, &privileges);
|
|
|
|
source = (void*)sourceData;
|
|
delete[] mirrorData;
|
|
return true;
|
|
}
|
|
|
|
unsigned detour::length(const uint8_t *function) {
|
|
unsigned length = 0;
|
|
while(length < 5) {
|
|
detour::opcode *opcode = 0;
|
|
foreach(op, detour::opcodes) {
|
|
if(function[length] == op.prefix) {
|
|
opcode = &op;
|
|
break;
|
|
}
|
|
}
|
|
if(opcode == 0) break;
|
|
length += opcode->length;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
unsigned detour::mirror(uint8_t *target, const uint8_t *source) {
|
|
const uint8_t *entryPoint = source;
|
|
for(unsigned n = 0; n < 256; n++) target[256 + n] = source[n];
|
|
|
|
unsigned size = detour::length(source);
|
|
while(size) {
|
|
detour::opcode *opcode = 0;
|
|
foreach(op, detour::opcodes) {
|
|
if(*source == op.prefix) {
|
|
opcode = &op;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(opcode->mode) {
|
|
case Copy:
|
|
for(unsigned n = 0; n < opcode->length; n++) *target++ = *source++;
|
|
break;
|
|
case RelNear: {
|
|
source++;
|
|
uintmax_t sourceAddress = (uintmax_t)source + 1 + (int8_t)*source;
|
|
*target++ = opcode->modify;
|
|
if(opcode->modify >> 8) *target++ = opcode->modify >> 8;
|
|
uintmax_t targetAddress = (uintmax_t)target + 4;
|
|
uintmax_t address = sourceAddress - targetAddress;
|
|
*target++ = address >> 0;
|
|
*target++ = address >> 8;
|
|
*target++ = address >> 16;
|
|
*target++ = address >> 24;
|
|
source += 2;
|
|
} break;
|
|
}
|
|
|
|
size -= opcode->length;
|
|
}
|
|
|
|
uintmax_t address = (entryPoint + detour::length(entryPoint)) - (target + 5);
|
|
*target++ = 0xe9; //jmp entryPoint
|
|
*target++ = address >> 0;
|
|
*target++ = address >> 8;
|
|
*target++ = address >> 16;
|
|
*target++ = address >> 24;
|
|
|
|
return source - entryPoint;
|
|
}
|
|
|
|
#undef Implied
|
|
#undef RelNear
|
|
|
|
}
|
|
|
|
#endif
|