-
-
Save ARMdealer/61d9a8738b7f4625707ad7d52ef34977 to your computer and use it in GitHub Desktop.
ARM64 Injection on MacOS M1
This file contains 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 <thread> | |
#include <utility> | |
#include <cstdint> | |
#include <string> | |
#include <sys/types.h> | |
class Injector | |
{ | |
public: | |
static bool Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept; | |
}; | |
// Implementation | |
#if defined(__APPLE__) | |
#include <dlfcn.h> | |
#include <sys/sysctl.h> | |
#include <mach/mach.h> | |
#include <mach/mach_vm.h> | |
#include <mach-o/loader.h> | |
#include <mach-o/dyld_images.h> | |
#include <mach-o/nlist.h> | |
#include <ptrauth.h> | |
#include <pthread.h> | |
#include <cstdint> | |
#include <string> | |
#endif | |
#if defined(__APPLE__) | |
//Strip the module name from its path | |
auto strip_path = [](const std::string &path_to_strip) -> std::string { | |
std::string::size_type pos = path_to_strip.find_last_of("\\/"); | |
if (pos != std::string::npos) | |
{ | |
//Strip path components | |
std::string path = path_to_strip.substr(pos + 1); | |
pos = path.find_last_of('.'); | |
//Strip extension | |
return pos > 0 && pos != std::string::npos ? path.substr(0, pos) : path; | |
} | |
return path_to_strip; | |
}; | |
// Find shared cache slide | |
auto find_shared_cache_slide = [](mach_port_t task) -> std::intptr_t { | |
task_dyld_info_data_t info; | |
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; | |
task_info(task, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&info), &count); | |
//Get the loaded dylibs/images | |
dyld_all_image_infos infos = {0}; | |
mach_vm_size_t size = info.all_image_info_size; | |
kern_return_t err = mach_vm_read_overwrite(task, info.all_image_info_addr, info.all_image_info_size, reinterpret_cast<mach_vm_address_t>(&infos), &size); | |
return err == KERN_SUCCESS ? infos.sharedCacheSlide : NULL; | |
}; | |
//Find address of a dynamic library | |
auto find_library = [](mach_port_t task, const char* library) -> std::intptr_t { | |
task_dyld_info_data_t info; | |
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; | |
task_info(task, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&info), &count); | |
//Get the loaded dylibs/images | |
dyld_all_image_infos infos = {0}; | |
mach_vm_size_t size = info.all_image_info_size; | |
kern_return_t err = mach_vm_read_overwrite(task, info.all_image_info_addr, info.all_image_info_size, reinterpret_cast<mach_vm_address_t>(&infos), &size); | |
if (size <= 0 || err != KERN_SUCCESS) | |
{ | |
return NULL; | |
} | |
//Get the info for each dylib/image | |
size = sizeof(dyld_all_image_infos) * infos.infoArrayCount; | |
std::unique_ptr<dyld_image_info[]> image_infos = std::make_unique<dyld_image_info[]>(size); | |
err = mach_vm_read_overwrite(task, reinterpret_cast<mach_vm_address_t>(infos.infoArray), size, reinterpret_cast<mach_vm_address_t>(image_infos.get()), &size); | |
if (size <= 0 || err != KERN_SUCCESS) | |
{ | |
return NULL; | |
} | |
//Get each image's file paths | |
for (std::uint32_t i = 0; i < infos.infoArrayCount; ++i) | |
{ | |
char buffer[512] = {0}; | |
mach_vm_size_t size = sizeof(buffer); | |
kern_return_t err = mach_vm_read_overwrite(task, reinterpret_cast<mach_vm_address_t>(image_infos[i].imageFilePath), size, reinterpret_cast<mach_vm_address_t>(&buffer[0]), &size); | |
if (err == KERN_SUCCESS && size > 0) | |
{ | |
std::string path = strip_path(buffer); | |
if (!strcasecmp(path.c_str(), library)) | |
{ | |
return reinterpret_cast<std::uintptr_t>(image_infos[i].imageLoadAddress); | |
} | |
} | |
} | |
return NULL; | |
}; | |
//Find address of a symbol within a dynamic library | |
auto find_symbol = [](mach_port_t task, mach_vm_address_t library_header_address, const char* symbol) -> std::intptr_t { | |
mach_vm_size_t size = sizeof(mach_header_64); | |
struct mach_header_64 header = {0}; | |
mach_vm_read_overwrite(task, library_header_address, size, reinterpret_cast<mach_vm_address_t>(&header), &size); | |
//64-bit mach-o module.. | |
if (header.magic == MH_MAGIC_64) | |
{ | |
std::intptr_t shared_image_cache_slide = 0; | |
if ((header.flags & 0x80000000) == 0x80000000) | |
{ | |
shared_image_cache_slide = find_shared_cache_slide(task); | |
} | |
load_command command = {0}; | |
mach_vm_size_t size = sizeof(command); | |
//stackoverflow.com/a/40793165/1462718 | |
//stackoverflow.com/questions/20481058/find-pathname-from-dlopen-handle-on-osx | |
mach_vm_address_t seg_linkedit_addr = reinterpret_cast<mach_vm_address_t>(nullptr); | |
mach_vm_address_t seg_text_addr = reinterpret_cast<mach_vm_address_t>(nullptr); | |
mach_vm_address_t symtab_addr = reinterpret_cast<mach_vm_address_t>(nullptr); | |
mach_vm_address_t load_command_address = library_header_address + sizeof(struct mach_header_64); | |
//Iterate through all the load commands in the header.. | |
for (uint32_t i = 0; i < header.ncmds; ++i) | |
{ | |
mach_vm_read_overwrite(task, load_command_address, size, reinterpret_cast<mach_vm_address_t>(&command), &size); | |
switch(command.cmd) | |
{ | |
case LC_SEGMENT: | |
case LC_SEGMENT_64: | |
{ | |
char name[512] = {0}; | |
mach_vm_size_t size = sizeof(name); | |
//Read the name of each segment in the header | |
std::intptr_t segname_offset = offsetof(segment_command_64, segname); | |
mach_vm_read_overwrite(task, load_command_address + segname_offset, size, reinterpret_cast<mach_vm_address_t>(&name[0]), &size); | |
if (std::string(name) == SEG_TEXT) | |
{ | |
seg_text_addr = load_command_address; | |
} | |
else if (std::string(name) == SEG_LINKEDIT) | |
{ | |
seg_linkedit_addr = load_command_address; | |
} | |
} | |
break; | |
case LC_SYMTAB: | |
{ | |
symtab_addr = load_command_address; | |
} | |
break; | |
} | |
load_command_address += command.cmdsize; | |
} | |
if (!seg_text_addr || !seg_linkedit_addr || !symtab_addr) | |
{ | |
return NULL; | |
} | |
//Read each segment | |
segment_command_64 seg_linkedit = {0}; | |
segment_command_64 seg_text = {0}; | |
symtab_command symtab = {0}; | |
size = sizeof(seg_linkedit); | |
mach_vm_read_overwrite(task, seg_linkedit_addr, size, reinterpret_cast<mach_vm_address_t>(&seg_linkedit), &size); | |
size = sizeof(seg_text); | |
mach_vm_read_overwrite(task, seg_text_addr, size, reinterpret_cast<mach_vm_address_t>(&seg_text), &size); | |
size = sizeof(symtab); | |
mach_vm_read_overwrite(task, symtab_addr, size, reinterpret_cast<mach_vm_address_t>(&symtab), &size); | |
//ASLR slide calculations | |
std::intptr_t file_slide = seg_linkedit.vmaddr - seg_text.vmaddr - seg_linkedit.fileoff; | |
std::intptr_t strings = library_header_address + symtab.stroff + file_slide; | |
std::intptr_t sym_addr = library_header_address + symtab.symoff + file_slide; | |
//Iterate through all the symbols in that segment | |
for (uint32_t i = 0; i < symtab.nsyms; ++i) | |
{ | |
nlist_64 sym = {0}; | |
size = sizeof(sym); | |
mach_vm_read_overwrite(task, sym_addr, size, reinterpret_cast<mach_vm_address_t>(&sym), &size); | |
if (/*(sym.n_type & N_EXT) == N_EXT &&*/ sym.n_value) | |
{ | |
char name[512] = {0}; | |
mach_vm_size_t size = sizeof(name); | |
std::intptr_t symname_offset = strings + sym.n_un.n_strx; | |
mach_vm_read_overwrite(task, symname_offset, size, reinterpret_cast<mach_vm_address_t>(&name[0]), &size); | |
if (std::string(name).substr(1) == symbol) | |
{ | |
if (sym.n_value < 0x1000) | |
{ | |
return sym.n_value + library_header_address; | |
} | |
if (shared_image_cache_slide) | |
{ | |
return sym.n_value + shared_image_cache_slide; | |
} | |
return sym.n_value + library_header_address; | |
} | |
} | |
sym_addr += size; | |
} | |
} | |
return NULL; | |
}; | |
//Calls dlopen inside of a pthread in the remote target | |
auto remote_load_library = [](std::size_t* instructions_size) -> std::uint8_t* { | |
#if defined(__clang__) | |
static std::uint8_t assembly[] = { | |
0xFD, 0x7B, 0xBD, 0xA9, //stp x29, x30, [sp, #-48]! | |
0xF5, 0x0B, 0x00, 0xF9, //str x21, [sp, #16] | |
0xF4, 0x4F, 0x02, 0xA9, //stp x20, x19, [sp, #32] | |
0xFD, 0x03, 0x00, 0x91, //mov x29, sp | |
0x02, 0x4C, 0x40, 0xA9, //ldp x2, x19, [x0] //_dlopen_pointer/_pthread_callback = data[0] | |
0x08, 0x50, 0x41, 0xA9, //ldp x8, x20, [x0, #16] //_pthread_create_from_mach_thread = data[2] | |
0x15, 0x10, 0x40, 0xF9, //ldr x21, [x0, #32] //_mach_thread_self = data[4] | |
0xBF, 0x0F, 0x00, 0xF9, //str xzr, [x29, #24] //pthread_t thread_id = 0; | |
0xE3, 0x03, 0x01, 0xAA, //mov x3, x1 //param | |
0xE1, 0x03, 0x1F, 0xAA, //mov x1, xzr //nullptr | |
0xA0, 0x63, 0x00, 0x91, //add x0, x29, #24 //&thread_id | |
0x00, 0x01, 0x3F, 0xD6, //blr x8 //_pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param) | |
0xEA, 0x03, 0x00, 0xAA, //mov x10, x0 //store return value in x10 | |
0xA0, 0x0F, 0x40, 0xF9, //ldr x0, [x29, #24] //thread_id | |
0xEB, 0x03, 0x00, 0xAA, //mov x11, x0 //store thread_id in x11 | |
0x60, 0x02, 0x3F, 0xD6, //blr x19 //_pthread_set_self(thread_id) | |
0xA0, 0x02, 0x3F, 0xD6, //blr x21 //_mach_thread_self() | |
0x80, 0x02, 0x3F, 0xD6, //blr x20 //_thread_suspend(_mach_thread_self()) | |
0xA0, 0x0F, 0x40, 0xF9, //ldr x0, [x29, #24] //return thread_id | |
0xF4, 0x4F, 0x42, 0xA9, //ldp x20, x19, [sp, #32] | |
0xF5, 0x0B, 0x40, 0xF9, //ldr x21, [sp, #16] | |
0xFD, 0x7B, 0xC3, 0xA8, //ldp x29, x30, [sp], #48 | |
0xC0, 0x03, 0x5F, 0xD6, //ret | |
0x1F, 0x20, 0x03, 0xD5, | |
0x1F, 0x20, 0x03, 0xD5, | |
0x1F, 0x20, 0x03, 0xD5, //nop | |
0x1F, 0x20, 0x03, 0xD5, | |
0x1F, 0x20, 0x03, 0xD5, | |
0x41, 0x00, 0x80, 0x52, //mov w1, #2 //RTLD_LAZY = 0x1 -- RTLD_NOW = 0x02 | |
0xE2, 0xDD, 0x97, 0xD2, //mov x2, #0xBEEF //address of dlopen | |
0xA2, 0xD5, 0xBB, 0xF2, //movk x2, #0xDEAD, lsl #16 //address of dlopen | |
0x02, 0x00, 0xD6, 0xF2, //movk x2, #0xB000, lsl #32 //address of dlopen | |
0x02, 0x00, 0xF4, 0xF2, //movk x2, #0xA000, lsl #48 //address of dlopen | |
0x40, 0x00, 0x1F, 0xD6, //br x2 //call dlopen(pthread_parameters, RTLD_LAZY) | |
}; | |
#else | |
static std::uint8_t assembly[] = { | |
0xFD, 0x7B, 0xBC, 0xA9, //stp x29, x30, [sp, -64]! | |
0xE4, 0x03, 0x00, 0xAA, //mov x4, x0 //_pthread_set_self = data[0] | |
0xF5, 0x13, 0x00, 0xF9, //str x21, [sp, 32] | |
0x95, 0x04, 0x40, 0xF9, //ldr x21, [x4, 8] //pthread_set_self = data[1] | |
0xF3, 0x53, 0x01, 0xA9, //stp x19, x20, [sp, 16] | |
0x85, 0x4C, 0x41, 0xA9, //ldp x5, x19, [x4, 16] //_thread_suspend = data[3] | |
0x94, 0x10, 0x40, 0xF9, //ldr x20, [x4, 32] //_mach_thread_self = data[4] | |
0xFD, 0x03, 0x00, 0x91, //mov x29, sp | |
0x82, 0x00, 0x40, 0xF9, //ldr x2, [x4] | |
0xFF, 0x1F, 0x00, 0xF9, //str xzr, [sp, 56] //pthread_t thread_id = 0; | |
0xE3, 0x03, 0x01, 0xAA, //mov x3, x1 //param | |
0x01, 0x00, 0x80, 0xD2, //mov x1, 0 //nullptr | |
0xE0, 0xE3, 0x00, 0x91, //add x0, sp, 56 //&thread_id | |
0xA0, 0x00, 0x3F, 0xD6, //blr x5 //_pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param) | |
0xEA, 0x03, 0x00, 0xAA, //mov x10, x0 //store return value in x10 | |
0xE0, 0x1F, 0x40, 0xF9, //ldr x0, [sp, 56] //thread_id | |
0xEB, 0x03, 0x00, 0xAA, //mov x11, x0 //store thread_id in x11 | |
0xA0, 0x02, 0x3F, 0xD6, //blr x21 //_pthread_set_self(thread_id); | |
0x80, 0x02, 0x3F, 0xD6, //blr x20 //_mach_thread_self(); | |
0x60, 0x02, 0x3F, 0xD6, //blr x19 //_thread_suspend(_mach_thread_self()); | |
0xF3, 0x53, 0x41, 0xA9, //ldp x19, x20, [sp, 16] | |
0xF5, 0x13, 0x40, 0xF9, //ldr x21, [sp, 32] | |
0xE0, 0x1F, 0x40, 0xF9, //ldr x0, [sp, 56] | |
0xFD, 0x7B, 0xC4, 0xA8, //ldp x29, x30, [sp], 64 | |
0xC0, 0x03, 0x5F, 0xD6, //ret | |
0x1F, 0x20, 0x03, 0xD5, | |
0x1F, 0x20, 0x03, 0xD5, | |
0x1F, 0x20, 0x03, 0xD5, //nop | |
0x1F, 0x20, 0x03, 0xD5, | |
0x1F, 0x20, 0x03, 0xD5, | |
0x41, 0x00, 0x80, 0x52, //mov w1, 2 //RTLD_LAZY = 0x1 -- RTLD_NOW = 0x02 | |
0xE2, 0xDD, 0x97, 0xD2, //mov x2, 0xBEEF //address of dlopen | |
0xA2, 0xD5, 0xBB, 0xF2, //movk x2, 0xDEAD, lsl 16 //address of dlopen | |
0x02, 0x00, 0xD6, 0xF2, //movk x2, 0xB000, lsl 32 //address of dlopen | |
0x02, 0x00, 0xF4, 0xF2, //movk x2, 0xA000, lsl 48 //address of dlopen | |
0x40, 0x00, 0x1F, 0xD6, //br x2 //call dlopen(pthread_parameters, RTLD_LAZY) | |
}; | |
#endif | |
*instructions_size = sizeof(assembly); | |
return &assembly[0]; | |
}; | |
bool Injector::Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept | |
{ | |
std::size_t assembly_size = 0; | |
std::uint64_t stack_size = 16 * 1024; | |
std::uint8_t* assembly = remote_load_library(&assembly_size); | |
//Retrieve a task port for the remote process.. | |
mach_port_t remote_task = 0; | |
mach_error_t err = task_for_pid(mach_task_self(), pid, &remote_task); | |
if (err == 5) | |
{ | |
fprintf(stderr, "Could not access task for pid %d. You probably need to add user to procmod group\n", pid); | |
return false; | |
} | |
std::intptr_t libdyld = find_library(remote_task, "libdyld"); | |
if (!libdyld) | |
{ | |
fprintf(stderr, "Cannot find libdyld in remote process\n"); | |
return false; | |
} | |
std::intptr_t libsystem_pthread = find_library(remote_task, "libsystem_pthread"); | |
if (!libsystem_pthread) | |
{ | |
fprintf(stderr, "Cannot find libsystem_pthread in remote process\n"); | |
return false; | |
} | |
std::intptr_t libsystem_kernel = find_library(remote_task, "libsystem_kernel"); | |
if (!libsystem_kernel) | |
{ | |
fprintf(stderr, "Cannot find libsystem_kernel in remote process\n"); | |
return false; | |
} | |
std::intptr_t dlopen = find_symbol(remote_task, libdyld, "dlopen"); | |
if (!dlopen) | |
{ | |
fprintf(stderr, "Cannot find dlopen in remote process\n"); | |
return false; | |
} | |
std::intptr_t pthread_create_from_mach_thread = find_symbol(remote_task, libsystem_pthread, "pthread_create_from_mach_thread"); | |
if (!pthread_create_from_mach_thread) | |
{ | |
fprintf(stderr, "Cannot find pthread_create_from_mach_thread in remote process\n"); | |
return false; | |
} | |
std::intptr_t pthread_set_self = find_symbol(remote_task, libsystem_pthread, "_pthread_set_self"); | |
if (!pthread_set_self) | |
{ | |
fprintf(stderr, "Cannot find pthread_set_self in remote process\n"); | |
return false; | |
} | |
std::intptr_t thread_suspend = find_symbol(remote_task, libsystem_kernel, "thread_suspend"); | |
if (!thread_suspend) | |
{ | |
fprintf(stderr, "Cannot find thread_suspend in remote process\n"); | |
return false; | |
} | |
std::intptr_t mach_thread_self = find_symbol(remote_task, libsystem_kernel, "mach_thread_self"); | |
if (!mach_thread_self) | |
{ | |
fprintf(stderr, "Cannot find mach_thread_self in remote process\n"); | |
return false; | |
} | |
// Allocate and write the path size.. | |
mach_vm_address_t remote_path = reinterpret_cast<mach_vm_address_t>(nullptr); | |
kern_return_t ret = mach_vm_allocate(remote_task, &remote_path, module_path.size() + 1, VM_FLAGS_ANYWHERE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Allocate Remote Module Path\n"); | |
return false; | |
} | |
ret = mach_vm_write(remote_task, remote_path, reinterpret_cast<mach_vm_offset_t>(module_path.c_str()), static_cast<mach_msg_type_number_t>(module_path.size())); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Write Remote Module Path\n"); | |
return false; | |
} | |
ret = mach_vm_protect(remote_task, remote_path, module_path.size() + 1, 0, VM_PROT_READ | VM_PROT_WRITE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Protect Remote Module Path\n"); | |
return false; | |
} | |
// Bits 21 to 5 = imm of the MOV wide immediate instruction | |
// https://developer.arm.com/documentation/ddi0602/2023-03/Base-Instructions/MOV--wide-immediate---Move--wide-immediate---an-alias-of-MOVZ- | |
auto copy_bits = [](std::uint32_t ®, std::uint16_t value) { | |
for (int bit = 20, valueBit = 15; bit >= 5; --bit, --valueBit) | |
{ | |
std::uint32_t bit_to_set = ((value >> valueBit) & 1); | |
reg ^= (-bit_to_set ^ reg) & (static_cast<std::uint32_t>(1) << bit); | |
} | |
}; | |
// Convert the instruction bytes to an instruction | |
auto decode_instruction = [](std::uint8_t instructions[]) -> std::uint32_t { | |
return (static_cast<std::uint32_t>(instructions[3]) << 24) | | |
(static_cast<std::uint32_t>(instructions[2]) << 16) | | |
(static_cast<std::uint32_t>(instructions[1]) << 8) | | |
(static_cast<std::uint32_t>(instructions[0]) << 0); | |
}; | |
// Convert the instruction back into instruction bytes | |
auto encode_instruction = [](std::uint32_t instruction, std::uint8_t (&instructions)[4]) { | |
instructions[3] = (instruction & 0xFF000000) >> 24; | |
instructions[2] = (instruction & 0x00FF0000) >> 16; | |
instructions[1] = (instruction & 0x0000FF00) >> 8; | |
instructions[0] = (instruction & 0x000000FF) >> 0; | |
}; | |
// Get the instructions offset, and write the address of dlopen to each part, 16-bits at a time | |
auto write_instruction_address = [&](std::uint32_t address_intermediate, std::uint8_t assembly[], std::size_t offset) { | |
std::uint8_t instructions[] = {0x00, 0x00, 0x00, 0x00}; | |
memcpy(&instructions, &assembly[assembly_size + offset], sizeof(instructions)); | |
std::uint32_t instruction = decode_instruction(instructions); | |
copy_bits(instruction, address_intermediate); | |
encode_instruction(instruction, instructions); | |
memcpy(&assembly[assembly_size + offset], &instructions, sizeof(instructions)); | |
}; | |
// Convert the dlopen address to its 16-bit parts | |
std::uintptr_t dlopen_address = static_cast<std::uintptr_t>(dlopen); | |
std::uint32_t beef = ((dlopen_address & 0x000000000000FFFF) >> 0); | |
std::uint32_t dead = ((dlopen_address & 0x00000000FFFF0000) >> 16); | |
std::uint32_t b000 = ((dlopen_address & 0x0000FFFF00000000) >> 32); | |
std::uint32_t a000 = ((dlopen_address & 0xFFFF000000000000) >> 48); | |
// Write the encoded instructions back into the assembly | |
write_instruction_address(a000, assembly, -8); | |
write_instruction_address(b000, assembly, -12); | |
write_instruction_address(dead, assembly, -16); | |
write_instruction_address(beef, assembly, -20); | |
//Allocate and write our remote code | |
mach_vm_address_t remote_code = reinterpret_cast<mach_vm_address_t>(nullptr); | |
ret = mach_vm_allocate(remote_task, &remote_code, assembly_size, VM_FLAGS_ANYWHERE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Allocate Remote Code\n"); | |
return false; | |
} | |
ret = mach_vm_write(remote_task, remote_code, reinterpret_cast<mach_vm_offset_t>(&assembly[0]), static_cast<mach_msg_type_number_t>(assembly_size)); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Write Remote Code\n"); | |
return false; | |
} | |
ret = mach_vm_protect(remote_task, remote_code, assembly_size, false, VM_PROT_READ | VM_PROT_EXECUTE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Protect Remote Code\n"); | |
return false; | |
} | |
//Allocate remote stack.. | |
mach_vm_address_t remote_stack = reinterpret_cast<mach_vm_address_t>(nullptr); | |
ret = mach_vm_allocate(remote_task, &remote_stack, stack_size, VM_FLAGS_ANYWHERE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Allocate Remote Stack\n"); | |
return false; | |
} | |
ret = mach_vm_protect(remote_task, remote_stack, stack_size, true, VM_PROT_READ | VM_PROT_WRITE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Protect Remote Stack\n"); | |
return false; | |
} | |
//Allocate & write parameters.. | |
void* parameters[] = { | |
(void*)(remote_code + (assembly_size - 24)), | |
(void*)pthread_set_self, | |
(void*)pthread_create_from_mach_thread, | |
(void*)thread_suspend, | |
(void*)mach_thread_self | |
}; | |
mach_vm_address_t remote_parameters = reinterpret_cast<mach_vm_address_t>(nullptr); | |
ret = mach_vm_allocate(remote_task, &remote_parameters, sizeof(parameters), VM_FLAGS_ANYWHERE); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Allocate Remote Parameters\n"); | |
return false; | |
} | |
ret = mach_vm_write(remote_task, remote_parameters, reinterpret_cast<mach_vm_offset_t>(¶meters[0]), static_cast<mach_msg_type_number_t>(sizeof(parameters))); | |
if (ret != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "Failed to Write Remote Parameters\n"); | |
return false; | |
} | |
//Offset stack pointer.. | |
mach_vm_address_t local_stack = remote_stack; | |
remote_stack += (stack_size / 2); //stack location | |
// To support ARMv7 and ARMv8, we use arm_unified_thread_state_t intead of arm_thread_state64_t | |
arm_unified_thread_state_t state = {0}; | |
memset(&state, 0, sizeof(state)); | |
//Parameter order for aarch64: x0, x1, x2, x3, x4, x5 | |
state.ash.flavor = ARM_THREAD_STATE64; | |
state.ash.count = ARM_THREAD_STATE64_COUNT; | |
state.ts_64.__x[0] = remote_parameters; //pointers to functions | |
state.ts_64.__x[1] = remote_path; //path of module | |
state.ts_64.__pc = (mach_vm_address_t)remote_code; //code to execute | |
state.ts_64.__sp = remote_stack; | |
state.ts_64.__lr = local_stack; | |
//Create our remote thread | |
thread_act_t thread; | |
err = thread_create_running(remote_task, ARM_THREAD_STATE64, (thread_state_t) &state.ts_64, ARM_THREAD_STATE64_COUNT, &thread); | |
if (err != KERN_SUCCESS) | |
{ | |
fprintf(stderr, "ERROR Creating Running Thread!\n"); | |
return 0; | |
} | |
//Give the remote thread some time to run (Maybe a better way than this???) | |
std::this_thread::sleep_for(std::chrono::seconds(2)); | |
//Check to make sure our remote thread is suspended, and finished its job | |
thread_basic_info_data_t basic_info; | |
mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT; | |
err = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basic_info, &info_count); | |
if (err == KERN_SUCCESS) | |
{ | |
bool is_suspended = (basic_info.suspend_count > 0); | |
if (is_suspended) | |
{ | |
//Retrieve our remote thread state. | |
arm_thread_state64_t state = {0}; | |
mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; | |
thread_get_state(thread, ARM_THREAD_STATE64, reinterpret_cast<thread_state_t>(&state), &count); | |
//x10 contains the result of pthread_create_from_mach_thread | |
//x11 contains the pthread_t thread_id | |
int result = static_cast<int>(state.__x[10]); | |
pthread_t thread_id = reinterpret_cast<pthread_t>(state.__x[11]); | |
if (result == 0 && thread_id != 0) | |
{ | |
printf("INJECTION SUCCESSFUL!!!\n"); | |
//Give some time for pthread_create_from_mach_thread to run, and call dlopen | |
std::this_thread::sleep_for(std::chrono::seconds(2)); | |
//Thread should be finished its job. | |
thread_terminate(thread); | |
return true; | |
} | |
} | |
} | |
return true; | |
} | |
#endif | |
int main(int argc, const char * argv[]) { | |
printf("Running\n"); | |
const char* dll_path = "/users/brandon/Desktop/test.dylib"; | |
Injector::Inject(dll_path, 40280, nullptr); | |
while(true) | |
{ | |
sleep(1); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment