Skip to content

Instantly share code, notes, and snippets.

@wh1te4ever
Created June 24, 2023 08:41
Show Gist options
  • Select an option

  • Save wh1te4ever/d43970844c99a7bab89c5975a5cb6afa to your computer and use it in GitHub Desktop.

Select an option

Save wh1te4ever/d43970844c99a7bab89c5975a5cb6afa to your computer and use it in GitHub Desktop.

Revisions

  1. wh1te4ever created this gist Jun 24, 2023.
    297 changes: 297 additions & 0 deletions dumptest.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,297 @@
    #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;
    }