You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.8 KiB
138 lines
3.8 KiB
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2020 xerox
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#pragma once
|
|
#include <map>
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
|
|
#define PAGE_START(ptr) reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) >> 12 << 12)
|
|
#define ARM_JMP_CODE 0xE51FF004
|
|
|
|
namespace ligma
|
|
{
|
|
namespace hook
|
|
{
|
|
class detour
|
|
{
|
|
public:
|
|
detour(void* addr_to_hook, void* jmp_to, bool enable = true)
|
|
:
|
|
hook_addr(addr_to_hook),
|
|
detour_addr(jmp_to),
|
|
hook_installed(false)
|
|
{
|
|
reinterpret_cast<std::uint32_t*>(jmp_code)[0] = ARM_JMP_CODE; // LDR PC, [PC, #-4]
|
|
reinterpret_cast<void**>(jmp_code)[1] = jmp_to;
|
|
memcpy(org_bytes, hook_addr, sizeof(org_bytes));
|
|
if (enable) install();
|
|
}
|
|
|
|
void install()
|
|
{
|
|
if (hook_installed.load())
|
|
return;
|
|
|
|
if (!mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
{
|
|
memcpy((void*)((long)hook_addr), jmp_code, sizeof(jmp_code));
|
|
mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_EXEC);
|
|
cacheflush(reinterpret_cast<long>(hook_addr), reinterpret_cast<long>(hook_addr) + getpagesize(), NULL);
|
|
hook_installed.exchange(true);
|
|
}
|
|
}
|
|
|
|
void uninstall()
|
|
{
|
|
if (!hook_installed.load())
|
|
return;
|
|
|
|
if (!mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
{
|
|
memcpy(hook_addr, org_bytes, sizeof(jmp_code));
|
|
mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_EXEC);
|
|
cacheflush(reinterpret_cast<long>(hook_addr), reinterpret_cast<long>(hook_addr) + getpagesize(), NULL);
|
|
hook_installed.exchange(false);
|
|
}
|
|
}
|
|
|
|
~detour() { uninstall(); }
|
|
bool installed() { return hook_installed; }
|
|
void* hook_address() { return hook_addr; }
|
|
void* detour_address() { return detour_addr; }
|
|
private:
|
|
std::atomic<bool> hook_installed;
|
|
void* hook_addr, * detour_addr;
|
|
|
|
unsigned char jmp_code[8]{};
|
|
std::uint8_t org_bytes[sizeof(jmp_code)];
|
|
};
|
|
|
|
// this is jank, but needed because the OS isnt initalizing statics/inlined globals... :|
|
|
inline std::map<void*, std::unique_ptr<detour>>* get_hooks()
|
|
{
|
|
static std::map<void*, std::unique_ptr<detour>> hooks{};
|
|
return &hooks;
|
|
}
|
|
|
|
inline void make_hook(void* addr_to_hook, void* jmp_to_addr, bool enable = true)
|
|
{
|
|
if (!addr_to_hook)
|
|
return;
|
|
|
|
get_hooks()->insert({
|
|
addr_to_hook,
|
|
std::make_unique<detour>(
|
|
addr_to_hook,
|
|
jmp_to_addr,
|
|
enable
|
|
) }
|
|
);
|
|
}
|
|
|
|
inline void enable(void* addr)
|
|
{
|
|
if (!addr)
|
|
return;
|
|
get_hooks()->at(addr)->install();
|
|
}
|
|
|
|
inline void disable(void* addr)
|
|
{
|
|
if (!addr)
|
|
return;
|
|
get_hooks()->at(addr)->uninstall();
|
|
}
|
|
|
|
inline void remove(void* addr)
|
|
{
|
|
if (!addr)
|
|
return;
|
|
get_hooks()->erase(addr);
|
|
}
|
|
}
|
|
}
|