Created
March 1, 2021 00:00
-
-
Save ItsJustMeChris/1aeff828b43e6aa00a477b5c79da164c to your computer and use it in GitHub Desktop.
Code to perform a mid function hook using inline assembly on a unix / macos x86-64 system. GIST includes dummy program to test, offsets in main program should be valid when built.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include <iostream> | |
#include <sys/mman.h> | |
#include <mach/i386/kern_return.h> | |
#include <mach/mach_init.h> | |
#include <mach/mach_vm.h> | |
#include <mach/vm_region.h> | |
#include <mach/vm_types.h> | |
#include <unistd.h> | |
#include <mach/host_info.h> | |
#include <mach/mach_host.h> | |
#include <mach/shared_region.h> | |
#include <mach/mach.h> | |
#include <mach-o/dyld.h> | |
#include <errno.h> | |
bool ApplyJump(uintptr_t* toHook, void* ourFunct, int len) { | |
std::cout << "Hook Location: " << std::hex << toHook << std::endl; | |
std::cout << "Hook Function: " << std::hex << ourFunct << std::endl; | |
std::cout << "Hook Length: " << std::hex << len << std::endl; | |
mach_vm_protect(mach_task_self(), (mach_vm_address_t)toHook, (mach_vm_size_t)len, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE | VM_PROT_COPY); | |
memset(toHook, 0x90, len); | |
uintptr_t relativeAddress = ((uintptr_t)ourFunct - (uintptr_t)toHook) - 5; | |
*(uintptr_t*)toHook = 0xE9; | |
*(uintptr_t*)((uintptr_t)toHook + 1) = relativeAddress; | |
mach_vm_protect(mach_task_self(), (mach_vm_address_t)toHook, (mach_vm_size_t)len, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); | |
return true; | |
} | |
vm_address_t getBaseAddress(mach_port_t task) | |
{ | |
kern_return_t kret; | |
vm_region_basic_info_data_t info; | |
mach_vm_size_t size; | |
mach_port_t object_name; | |
mach_msg_type_number_t count; | |
vm_address_t firstRegionBegin; | |
vm_address_t lastRegionEnd; | |
vm_size_t fullSize = 0; | |
count = VM_REGION_BASIC_INFO_COUNT_64; | |
mach_vm_address_t address = 1; | |
int regionCount = 0; | |
int flag = 0; | |
while (flag == 0) { | |
//Attempts to get the region info for given task | |
kret = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object_name); | |
if (kret == KERN_SUCCESS) { | |
if (regionCount == 0) { | |
firstRegionBegin = address; | |
regionCount += 1; | |
} | |
fullSize += size; | |
address += size; | |
} | |
else | |
flag = 1; | |
} | |
lastRegionEnd = address; | |
return firstRegionBegin; | |
} | |
mach_port_t getTaskForPID(pid_t pid) | |
{ | |
kern_return_t kern_return; | |
mach_port_t task; | |
kern_return = task_for_pid(mach_task_self(), pid, &task); | |
if (kern_return != KERN_SUCCESS) { | |
return 0; | |
} | |
return task; | |
} | |
uintptr_t hookAddr; | |
void doStuff() { | |
asm volatile ( | |
"mov %%eax, %0\n" | |
: | |
:"a"(69) | |
); | |
((void (*)(void)) hookAddr)(); | |
} | |
__attribute__((constructor)) void ctor(void) { | |
std::cout << ">>> DYLIB LOADED <<<" << std::endl; | |
pid_t pid = getpid(); | |
mach_port_t task = getTaskForPID(pid); | |
vm_address_t base = getBaseAddress(task); | |
std::cout << "Base found 0x" << std::hex << base << std::endl; | |
hookAddr = (base + 0x3CE6) + 0x9; | |
std::cout << "JMP [addr]: " << std::hex << hookAddr << std::endl; | |
ApplyJump((uintptr_t*)(base + 0x3CE6), (void*)doStuff, 0x5); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gcc main.cpp -o inline.bin -lstdc++ | |
gcc -dynamiclib dylib.cpp -o inline.dylib -lstdc++ | |
sudo DYLD_INSERT_LIBRARIES=inline.dylib ./inline.bin |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <unistd.h> | |
int A = 50; | |
int main() { | |
while (1) { | |
A += 50; | |
std::cout << A << std::endl; | |
usleep(500); | |
} | |
} |
Thank you for the example and explanation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This code injects a code cave and assigns a value to the registrar of our choice and then jumps back to normal program execution and the continued execution maintains our overwritten value, proving the proof of concept. It can further be validated by observing the value of the global pointer.
Program execution intention (without our hook) would be to print
After our hook is applied the assembly is prove to have changed the registrar and affected programmatic outcome by writing
69
instead of incrementing the global pointer.It can further be proven by observing the global pointer at
processBase + 0x8058
Credits to @benphelps for https://gist.github.com/ItsJustMeChris/1aeff828b43e6aa00a477b5c79da164c#file-inline-assembly-mid-function-hooking-macos-unix-cpp-L36