Created
June 24, 2023 08:41
-
-
Save wh1te4ever/d43970844c99a7bab89c5975a5cb6afa to your computer and use it in GitHub Desktop.
Dump memory of __DATA, __LINKEDIT segments from running process
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 <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, ®ion_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, ®ion_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, ®ion_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, ®ion_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, ®ion_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