├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 AlexDenisov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DIR?=./build 2 | 3 | CC=clang 4 | 5 | all: segment_dumper 6 | 7 | segment_dumper: $(BUILD_DIR) 8 | $(CC) main.c -o $(BUILD_DIR)/segment_dumper 9 | 10 | $(BUILD_DIR): 11 | mkdir -p $(BUILD_DIR) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # segment_dumper 2 | 3 | Simple example of a Mach-O parser 4 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* -*- C -*- main.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* hack until arm64 headers are worked out */ 12 | #ifndef CPU_TYPE_ARM64 13 | # define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 14 | #endif /* !CPU_TYPE_ARM64 */ 15 | 16 | extern void dump_segments(FILE *obj_file); 17 | 18 | int main(int argc, char *argv[]) { 19 | if (argc < 2 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { 20 | printf("Usage: %s \n", argv[0]); 21 | return 1; 22 | } 23 | const char *filename = argv[1]; 24 | FILE *obj_file = fopen(filename, "rb"); 25 | if (obj_file == NULL) { 26 | printf("'%s' could not be opened.\n", argv[1]); 27 | return 1; 28 | } 29 | dump_segments(obj_file); 30 | fclose(obj_file); 31 | 32 | (void)argc; 33 | return 0; 34 | } 35 | 36 | static uint32_t read_magic(FILE *obj_file, off_t offset) { 37 | uint32_t magic; 38 | fseek(obj_file, offset, SEEK_SET); 39 | fread(&magic, sizeof(uint32_t), 1, obj_file); 40 | return magic; 41 | } 42 | 43 | static int is_magic_64(uint32_t magic) { 44 | return magic == MH_MAGIC_64 || magic == MH_CIGAM_64; 45 | } 46 | 47 | static int should_swap_bytes(uint32_t magic) { 48 | return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM; 49 | } 50 | 51 | static int is_fat(uint32_t magic) { 52 | return magic == FAT_MAGIC || magic == FAT_CIGAM; 53 | } 54 | 55 | struct _cpu_type_names { 56 | cpu_type_t cputype; 57 | const char *cpu_name; /* FIXME: -Wpadded from clang */ 58 | }; 59 | 60 | static struct _cpu_type_names cpu_type_names[] = { 61 | { CPU_TYPE_I386, "i386" }, 62 | { CPU_TYPE_X86_64, "x86_64" }, 63 | { CPU_TYPE_ARM, "arm" }, 64 | { CPU_TYPE_ARM64, "arm64" } 65 | }; 66 | 67 | static const char *cpu_type_name(cpu_type_t cpu_type) { 68 | static int cpu_type_names_size = sizeof(cpu_type_names) / sizeof(struct _cpu_type_names); 69 | for (int i = 0; i < cpu_type_names_size; i++ ) { 70 | if (cpu_type == cpu_type_names[i].cputype) { 71 | return cpu_type_names[i].cpu_name; 72 | } 73 | } 74 | 75 | return "unknown"; 76 | } 77 | 78 | static void *load_bytes(FILE *obj_file, off_t offset, size_t size) { 79 | void *buf = calloc(1, size); 80 | fseek(obj_file, offset, SEEK_SET); 81 | fread(buf, size, 1, obj_file); 82 | return buf; 83 | } 84 | 85 | static void dump_segment_commands(FILE *obj_file, off_t offset, bool is_swap, uint32_t ncmds) { 86 | off_t actual_offset = offset; 87 | for (uint32_t i = 0U; i < ncmds; i++) { 88 | struct load_command *cmd = load_bytes(obj_file, actual_offset, sizeof(struct load_command)); 89 | if (is_swap) { 90 | swap_load_command(cmd, 0); 91 | } 92 | 93 | if (cmd->cmd == LC_SEGMENT_64) { 94 | struct segment_command_64 *segment = load_bytes(obj_file, actual_offset, sizeof(struct segment_command_64)); 95 | if (is_swap) { 96 | swap_segment_command_64(segment, 0); 97 | } 98 | 99 | printf("segname: %s\n", segment->segname); 100 | 101 | free(segment); 102 | } else if (cmd->cmd == LC_SEGMENT) { 103 | struct segment_command *segment = load_bytes(obj_file, actual_offset, sizeof(struct segment_command)); 104 | if (is_swap) { 105 | swap_segment_command(segment, 0); 106 | } 107 | 108 | printf("segname: %s\n", segment->segname); 109 | 110 | free(segment); 111 | } 112 | 113 | actual_offset += cmd->cmdsize; 114 | 115 | free(cmd); 116 | } 117 | } 118 | 119 | static void dump_mach_header(FILE *obj_file, off_t offset, bool is_64, bool is_swap) { 120 | uint32_t ncmds; 121 | off_t load_commands_offset = offset; 122 | 123 | if (is_64) { 124 | size_t header_size = sizeof(struct mach_header_64); 125 | struct mach_header_64 *header = load_bytes(obj_file, offset, header_size); 126 | if (is_swap) { 127 | swap_mach_header_64(header, 0); 128 | } 129 | ncmds = header->ncmds; 130 | load_commands_offset += header_size; 131 | 132 | printf("%s\n", cpu_type_name(header->cputype)); 133 | 134 | free(header); 135 | } else { 136 | size_t header_size = sizeof(struct mach_header); 137 | struct mach_header *header = load_bytes(obj_file, offset, header_size); 138 | if (is_swap) { 139 | swap_mach_header(header, 0); 140 | } 141 | 142 | ncmds = header->ncmds; 143 | load_commands_offset += header_size; 144 | 145 | printf("%s\n", cpu_type_name(header->cputype)); 146 | 147 | free(header); 148 | } 149 | 150 | dump_segment_commands(obj_file, load_commands_offset, is_swap, ncmds); 151 | } 152 | 153 | static void dump_fat_header(FILE *obj_file, int is_swap) { 154 | size_t header_size = sizeof(struct fat_header); 155 | size_t arch_size = sizeof(struct fat_arch); 156 | 157 | struct fat_header *header = load_bytes(obj_file, 0, header_size); 158 | if (is_swap) { 159 | swap_fat_header(header, 0); 160 | } 161 | 162 | off_t arch_offset = (off_t)header_size; 163 | for (uint32_t i = 0U; i < header->nfat_arch; i++) { 164 | struct fat_arch *arch = load_bytes(obj_file, arch_offset, arch_size); 165 | 166 | if (is_swap) { 167 | swap_fat_arch(arch, 1, 0); 168 | } 169 | 170 | off_t mach_header_offset = (off_t)arch->offset; 171 | free(arch); 172 | arch_offset += arch_size; 173 | 174 | uint32_t magic = read_magic(obj_file, mach_header_offset); 175 | int is_64 = is_magic_64(magic); 176 | int is_swap_mach = should_swap_bytes(magic); 177 | dump_mach_header(obj_file, mach_header_offset, is_64, is_swap_mach); 178 | } 179 | free(header); 180 | } 181 | 182 | void dump_segments(FILE *obj_file) { 183 | uint32_t magic = read_magic(obj_file, 0); 184 | int is_64 = is_magic_64(magic); 185 | int is_swap = should_swap_bytes(magic); 186 | int fat = is_fat(magic); 187 | if (fat) { 188 | dump_fat_header(obj_file, is_swap); 189 | } else { 190 | dump_mach_header(obj_file, 0, is_64, is_swap); 191 | } 192 | } 193 | 194 | /* EOF */ 195 | --------------------------------------------------------------------------------