Skip to content

Instantly share code, notes, and snippets.

@wh1te4ever
Created June 24, 2023 08:41
Show Gist options
  • Save wh1te4ever/d43970844c99a7bab89c5975a5cb6afa to your computer and use it in GitHub Desktop.
Save wh1te4ever/d43970844c99a7bab89c5975a5cb6afa to your computer and use it in GitHub Desktop.
Dump memory of __DATA, __LINKEDIT segments from running process
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <mach-o/loader.h>
#include <mach-o/dyld_images.h>
#include <fcntl.h>
#define PATH_MAX 1024
kern_return_t
mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, mach_vm_size_t, mach_vm_address_t, mach_vm_size_t *);
kern_return_t
mach_vm_region(vm_map_read_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name);
kern_return_t
find_main_binary(pid_t pid, mach_vm_address_t *main_address)
{
vm_map_t targetTask = 0;
kern_return_t kr = 0;
if (task_for_pid(mach_task_self(), pid, &targetTask))
{
printf("[-] Can't execute task_for_pid! Do you have the right permissions/entitlements?\n");
return KERN_FAILURE;
}
vm_address_t iter = 0;
while (1)
{
struct mach_header mh = {0};
vm_address_t addr = iter;
vm_size_t lsize = 0;
uint32_t depth;
mach_vm_size_t bytes_read = 0;
struct vm_region_submap_info_64 info;
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
if (vm_region_recurse_64(targetTask, &addr, &lsize, &depth, (vm_region_info_t)&info, &count))
{
break;
}
kr = mach_vm_read_overwrite(targetTask, (mach_vm_address_t)addr, (mach_vm_size_t)sizeof(struct mach_header), (mach_vm_address_t)&mh, &bytes_read);
if (kr == KERN_SUCCESS && bytes_read == sizeof(struct mach_header))
{
/* only one image with MH_EXECUTE filetype */
if ( (mh.magic == MH_MAGIC || mh.magic == MH_MAGIC_64) && mh.filetype == MH_EXECUTE)
{
*main_address = addr;
break;
}
}
iter = addr + lsize;
}
return KERN_SUCCESS;
}
static kern_return_t
readmem(mach_vm_offset_t *buffer, mach_vm_address_t address, mach_vm_size_t size, pid_t pid, vm_region_basic_info_data_64_t *info)
{
// get task for pid
vm_map_t port;
kern_return_t kr;
if (task_for_pid(mach_task_self(), pid, &port))
{
fprintf(stderr, "[ERROR] Can't execute task_for_pid! Do you have the right permissions/entitlements?\n");
return KERN_FAILURE;
}
mach_msg_type_number_t info_cnt = sizeof (vm_region_basic_info_data_64_t);
mach_port_t object_name;
mach_vm_size_t size_info;
mach_vm_address_t address_info = address;
kr = mach_vm_region(port, &address_info, &size_info, VM_REGION_BASIC_INFO_64, (vm_region_info_t)info, &info_cnt, &object_name);
if (kr)
{
fprintf(stderr, "[ERROR] mach_vm_region failed with error %d\n", (int)kr);
return KERN_FAILURE;
}
/* read memory - vm_read_overwrite because we supply the buffer */
mach_vm_size_t nread;
kr = mach_vm_read_overwrite(port, address, size, (mach_vm_address_t)buffer, &nread);
if (kr)
{
fprintf(stderr, "[ERROR] vm_read failed! %d\n", kr);
return KERN_FAILURE;
}
else if (nread != size)
{
fprintf(stderr, "[ERROR] vm_read failed! requested size: 0x%llx read: 0x%llx\n", size, nread);
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
int64_t
get_image_size(mach_vm_address_t address, pid_t pid, uint64_t *vmaddr_slide)
{
vm_region_basic_info_data_64_t region_info = {0};
// allocate a buffer to read the header info
// NOTE: this is not exactly correct since the 64bit version has an extra 4 bytes
// but this will work for this purpose so no need for more complexity!
struct mach_header header = {0};
if (readmem((mach_vm_offset_t*)&header, address, sizeof(struct mach_header), pid, &region_info))
{
printf("Can't read header!\n");
return -1;
}
if (header.magic != MH_MAGIC && header.magic != MH_MAGIC_64)
{
printf("[ERROR] Target is not a mach-o binary!\n");
return -1;
}
int64_t imagefilesize = -1;
/* read the load commands */
uint8_t *loadcmds = (uint8_t*)malloc(header.sizeofcmds);
uint16_t mach_header_size = sizeof(struct mach_header);
if (header.magic == MH_MAGIC_64)
{
mach_header_size = sizeof(struct mach_header_64);
}
if (readmem((mach_vm_offset_t*)loadcmds, address+mach_header_size, header.sizeofcmds, pid, &region_info))
{
printf("Can't read load commands\n");
free(loadcmds);
return -1;
}
/* process and retrieve address and size of linkedit */
uint8_t *loadCmdAddress = 0;
loadCmdAddress = (uint8_t*)loadcmds;
struct load_command *loadCommand = NULL;
struct segment_command *segCmd = NULL;
struct segment_command_64 *segCmd64 = NULL;
for (uint32_t i = 0; i < header.ncmds; i++)
{
loadCommand = (struct load_command*)loadCmdAddress;
if (loadCommand->cmd == LC_SEGMENT)
{
segCmd = (struct segment_command*)loadCmdAddress;
if (strncmp(segCmd->segname, "__PAGEZERO", 16) != 0)
{
if (strncmp(segCmd->segname, "__TEXT", 16) == 0)
{
*vmaddr_slide = address - segCmd->vmaddr;
}
imagefilesize += segCmd->filesize;
}
}
else if (loadCommand->cmd == LC_SEGMENT_64)
{
segCmd64 = (struct segment_command_64*)loadCmdAddress;
if (strncmp(segCmd64->segname, "__PAGEZERO", 16) != 0)
{
if (strncmp(segCmd64->segname, "__TEXT", 16) == 0)
{
*vmaddr_slide = address - segCmd64->vmaddr;
}
imagefilesize += segCmd64->filesize;
}
}
// advance to next command
loadCmdAddress += loadCommand->cmdsize;
}
free(loadcmds);
return imagefilesize;
}
kern_return_t
dump_binary(mach_vm_address_t address, pid_t pid, uint8_t *buffer, uint64_t aslr_slide, char* segment_name, int *segment_size)
{
vm_region_basic_info_data_64_t region_info = {0};
// allocate a buffer to read the header info
// NOTE: this is not exactly correct since the 64bit version has an extra 4 bytes
// but this will work for this purpose so no need for more complexity!
struct mach_header header = {0};
if (readmem((mach_vm_offset_t*)&header, address, sizeof(struct mach_header), pid, &region_info))
{
printf("Can't read header!\n");
return KERN_FAILURE;
}
if (header.magic != MH_MAGIC && header.magic != MH_MAGIC_64)
{
printf("[ERROR] Target is not a mach-o binary!\n");
return KERN_FAILURE;
}
// read the header info to find the LINKEDIT
uint8_t *loadcmds = (uint8_t*)malloc(header.sizeofcmds);
uint16_t mach_header_size = sizeof(struct mach_header);
if (header.magic == MH_MAGIC_64)
{
mach_header_size = sizeof(struct mach_header_64);
}
// retrieve the load commands
if (readmem((mach_vm_offset_t*)loadcmds, address+mach_header_size, header.sizeofcmds, pid, &region_info))
{
printf("Can't read load commands\n");
free(loadcmds);
loadcmds = NULL;
return KERN_FAILURE;
}
// process and retrieve address and size of linkedit
uint8_t *loadCmdAddress = 0;
loadCmdAddress = (uint8_t*)loadcmds;
struct load_command *loadCommand = NULL;
struct segment_command *segCmd = NULL;
struct segment_command_64 *segCmd64 = NULL;
for (uint32_t i = 0; i < header.ncmds; i++)
{
loadCommand = (struct load_command*)loadCmdAddress;
if (loadCommand->cmd == LC_SEGMENT)
{
segCmd = (struct segment_command*)loadCmdAddress;
// printf("LC_SEGMENT segCmd->segname: %s\n", segCmd->segname);
}
else if (loadCommand->cmd == LC_SEGMENT_64)
{
segCmd64 = (struct segment_command_64*)loadCmdAddress;
// printf("LC_SEGMENT_64 segCmd->segname: %s\n", segCmd64->segname);
if(strcmp(segCmd64->segname, segment_name) == 0) {
// buffer += segCmd64->filesize;
printf("[+] Found %s at 0x%llx with size 0x%llx\n", segCmd64->segname, segCmd64->vmaddr+aslr_slide, segCmd64->filesize);
readmem((mach_vm_offset_t*)buffer, segCmd64->vmaddr+aslr_slide, segCmd64->filesize, pid, &region_info);
*segment_size = segCmd64->filesize;
}
}
loadCmdAddress += loadCommand->cmdsize;
}
free(loadcmds);
loadcmds = NULL;
return KERN_SUCCESS;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <pid>\n", argv[0]);
return 1;
}
pid_t targetPID = atoi(argv[1]);
mach_vm_address_t mainAddress = 0;
if (find_main_binary(targetPID, &mainAddress))
{
printf("[-] Failed to find main binary address!");
return 1;
}
uint64_t aslr_slide = 0;
uint64_t imagesize = 0;
if ( (imagesize = get_image_size(mainAddress, targetPID, &aslr_slide)) == 0 )
{
printf("[ERROR] Got image file size equal to 0!\n");
return 1;
}
printf("[+] image size: 0x%llx, aslr_slide: 0x%llx\n", imagesize, aslr_slide);
uint8_t *readbuffer = (uint8_t*)malloc(imagesize);
printf("[i] buffer allocated: %p, size: 0x%llx\n", readbuffer, imagesize);
int segment_size = 0;
if (dump_binary(mainAddress, targetPID, readbuffer, aslr_slide, "__LINKEDIT", &segment_size))
{
printf("Failed to dump memory of __LINKEDIT segment!\n");
free(readbuffer);
return 1;
}
char* filename = "/tmp/app_linkedit.bin";
remove(filename);
int fd=open(filename, O_CREAT|O_RDWR|O_TRUNC, 0666);
write(fd, readbuffer, segment_size);
close(fd);
if (dump_binary(mainAddress, targetPID, readbuffer, aslr_slide, "__DATA", &segment_size))
{
printf("Failed to dump memory of __DATA segment!\n");
free(readbuffer);
return 1;
}
filename = "/tmp/app_data.bin";
remove(filename);
fd=open(filename, O_CREAT|O_RDWR|O_TRUNC, 0666);
write(fd, readbuffer, segment_size);
close(fd);
free(readbuffer);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment