Skip to content

Instantly share code, notes, and snippets.

@jndok
Last active April 27, 2024 05:19
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jndok/2b0ce89521d3fc5fd61e404465c04218 to your computer and use it in GitHub Desktop.
Save jndok/2b0ce89521d3fc5fd61e404465c04218 to your computer and use it in GitHub Desktop.
MachOMan - a basic Mach-O parsing library
//
// machoman.c
// machoman
//
// Created by jndok on 14/05/16.
// Copyright © 2016 jndok. All rights reserved.
//
#include "machoman.h"
macho_map_t *map_macho_with_path(const char *path)
{
if (!path) return NULL;
if (access(path, R_OK) == -1) return NULL;
int32_t fd = open(path, O_RDONLY);
if (fd < 0)
return NULL;
struct stat st;
if(fstat(fd, &st) != 0)
goto fail;
macho_map_t *map = (macho_map_t *)malloc(sizeof(macho_map_t));
if((map->map_data = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0x0)) == MAP_FAILED)
goto fail;
map->map_magic = MACHO_MAP_MAGIC;
map->map_size = (mach_vm_size_t)st.st_size;
map->unique_id = (uint32_t)(((uint64_t)map << 32) >> 32);
return map;
fail:
close(fd);
return NULL;
}
void free_macho_map(macho_map_t *map)
{
if (!is_valid_macho_map(map)) {
return;
}
munmap(map->map_data, map->map_size);
free(map);
map = NULL;
}
__attribute__((always_inline))
boolean_t is_valid_macho_file(const char *path)
{
if (!path) return FALSE;
if (access(path, R_OK) == -1) return FALSE;
int32_t fd = open(path, O_RDONLY);
if (fd < 0)
return FALSE;
uint32_t magic = 0;
if (read(fd, (void*)&magic, sizeof(uint32_t)) == -1)
return FALSE;
if ((magic == MH_MAGIC) || (magic == MH_MAGIC_64))
return TRUE;
else
return FALSE;
}
__attribute__((always_inline))
boolean_t is_valid_macho_map(macho_map_t *map)
{
if (!map) return FALSE;
if (!map->map_data) FALSE;
if (map->map_magic != MACHO_MAP_MAGIC) return FALSE;
return TRUE;
}
__attribute__((always_inline))
struct mach_header_64 *get_mach_header_64(macho_map_t *map)
{
if (!is_valid_macho_map(map)) return NULL;
return (struct mach_header_64*)(map->map_data);
}
__attribute__((always_inline))
struct load_command **find_all_load_commands(struct mach_header_64 *mh)
{
struct load_command **all_lcmds = (struct load_command **)malloc(sizeof(struct load_command *) * mh->ncmds);
struct load_command *lcmd = (struct load_command *)(mh + 1);
for (uint32_t i=0; i<mh->ncmds; i++, lcmd += (lcmd->cmdsize / sizeof(struct load_command))) {
all_lcmds[i] = lcmd;
}
return all_lcmds;
}
__attribute__((always_inline))
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t lc)
{
struct load_command *lcmd = (struct load_command *)(mh + 1);
for (uint32_t i=0; i<mh->ncmds; i++, lcmd += (lcmd->cmdsize / sizeof(struct load_command))) {
if (lcmd->cmd == lc)
return lcmd;
}
return NULL;
}
__attribute__((always_inline))
struct segment_command_64 *find_segment_command64(struct mach_header_64 *mh, const char *segname)
{
struct load_command *lcmd = (struct load_command *)(mh + 1);
for (uint32_t i=0; i<mh->ncmds; i++, lcmd += (lcmd->cmdsize / sizeof(struct load_command))) {
if (lcmd->cmd == LC_SEGMENT_64) {
struct segment_command_64 *seg64 = (struct segment_command_64*)(lcmd);
if (strcmp(seg64->segname, segname) == 0)
return seg64;
}
}
return NULL;
}
__attribute__((always_inline))
struct section_64 *find_section64(struct segment_command_64 *seg64, const char *sectname)
{
struct section_64 *sect64 = (struct section_64 *)(seg64 + 1);
for (uint32_t i=0; i<seg64->nsects; i++, sect64++) {
if (strcmp(sect64->sectname, sectname) == 0)
return sect64;
}
return NULL;
}
__attribute__((always_inline))
struct symtab_command *find_symtab_command(struct mach_header_64 *mh)
{
return (struct symtab_command *)find_load_command(mh, LC_SYMTAB);
}
__attribute__((always_inline))
struct dysymtab_command *find_dysymtab_command(struct mach_header_64 *mh)
{
return (struct dysymtab_command *)find_load_command(mh, LC_DYSYMTAB);
}
/*
* == libmachoman v0.1.0 ==
*
* A simple library providing all you need
* for generic Mach-O parsing.
* I found myself rewriting this fucking code
* in every project, so I finally decided to
* do it right, once and for all!
*
*/
//
// machoman.h
// machoman
//
// Created by jndok on 26/05/16.
// Copyright © 2016 jndok. All rights reserved.
//
#ifndef machoman_h
#define machoman_h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#define MACHO_MAP_MAGIC 0xDEADC0DE
#define MACHO_MAP_SLIDE_OFFSET(map, off) ((uint64_t)(map->map_data) + (uint64_t)off)
#define MACHO_MAP_UNSLIDE_OFFSET(map, off) ((uint64_t)off > (uint64_t)(map->map_data)) ? ((uint64_t)off - (uint64_t)(map->map_data)) : ((uint64_t)off)
enum {
MMRC_ErrGen = 1
};
typedef struct macho_map {
uint32_t map_magic;
void *map_data;
mach_vm_size_t map_size;
uint32_t unique_id;
} macho_map_t;
macho_map_t *map_macho_with_path(const char *path);
void free_macho_map(macho_map_t *map);
__attribute__((always_inline)) boolean_t is_valid_macho_file(const char *path); /* before you map */
__attribute__((always_inline)) boolean_t is_valid_macho_map(macho_map_t *map);
__attribute__((always_inline)) struct mach_header_64 *get_mach_header_64(macho_map_t *map);
__attribute__((always_inline)) struct load_command **find_all_load_commands(struct mach_header_64 *mh);
__attribute__((always_inline)) struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t lc);
__attribute__((always_inline)) struct segment_command_64 *find_segment_command64(struct mach_header_64 *mh, const char *segname);
__attribute__((always_inline)) struct section_64 *find_section64(struct segment_command_64 *seg64, const char *sectname);
__attribute__((always_inline)) struct symtab_command *find_symtab_command(struct mach_header_64 *mh);
__attribute__((always_inline)) struct dysymtab_command *find_dysymtab_command(struct mach_header_64 *mh);
#endif /* machoman_h */
all:
clang machoman.c -dynamiclib -o libmachoman.dylib
clean:
rm -rf libmachoman.dylib
@sferrini
Copy link

sferrini commented Jul 5, 2016

Hey @jndok have a look at my revisions, I've fixed a bug in free_macho_map 👍

@Razzile
Copy link

Razzile commented Jul 5, 2016

wouldn't is_valid_macho_file() return false for a FAT macho file? I'm guessing the check isn't there because it's expected that you pass a thin macho but just letting you know 👍

@jndok
Copy link
Author

jndok commented Jul 21, 2016

@Razzile: yep, that's it. I'll add fat mach-o parsing in future, but this was meant to be super bare-bones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment