bsnes/ananke/nall/windows/detour.hpp

193 lines
5.8 KiB
C++
Raw Normal View History

Update to v091r11 release. byuu says: This release refines HSU1 support as a bidirectional protocol, nests SFC manifests as "release/cartridge" and "release/information" (but release/ is not guaranteed to be finalized just yet), removes the database integration, and adds support for ananke. ananke represents inevitability. It's a library that, when installed, higan can use to load files from the command-line, and also from a new File -> Load Game menu option. I need to change the build rules a bit for it to work on Windows (need to make phoenix a DLL, basically), but it works now on Linux. Right now, it only takes *.sfc file names, looks them up in the included database, converts them to game folders, and returns the game folder path for higan to load. The idea is to continue expanding it to support everything we can that I don't want in the higan core: - load *.sfc, *.smc, *.swc, *.fig files - remove SNES copier headers - split apart merged firmware files - pull in external firmware files (eg dsp1b.rom - these are staying merged, just as SPC7110 prg+dat are merged) - load *.zip and *.7z archives - prompt for selection on multi-file archives - generate manifest files based on heuristics - apply BPS patches The "Load" menu option has been renamed to "Library", to represent games in your library. I'm going to add some sort of suffix to indicate unverified games, and use a different folder icon for those (eg manifests built on heuristics rather than from the database.) So basically, to future end users: File -> Load Game will be how they play games. Library -> (specific system) can be thought of as an infinitely-sized recent games list. purify will likely become a simple stub that invokes ananke's functions. No reason to duplicate all that code.
2012-11-05 08:22:50 +00:00
#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