Skip to content

Instantly share code, notes, and snippets.

@ARMdealer
Forked from Brandon-T/MacOSInject.cxx
Created March 15, 2024 08:52
Show Gist options
  • Save ARMdealer/61d9a8738b7f4625707ad7d52ef34977 to your computer and use it in GitHub Desktop.
Save ARMdealer/61d9a8738b7f4625707ad7d52ef34977 to your computer and use it in GitHub Desktop.
ARM64 Injection on MacOS M1
#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 &reg, 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>(&parameters[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