1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <mach-o/loader.h> 4 #include <vector> 5 #include <string> 6 #include <mach/thread_status.h> 7 #include <string.h> 8 #include <uuid/uuid.h> 9 10 // Create an empty corefile with a "kern ver str" LC_NOTE. 11 // If an existing binary is given as an optional 2nd argument on the cmd line, 12 // the UUID from that binary will be encoded in the corefile. 13 // Otherwise a pre-set UUID will be put in the corefile that 14 // is created. 15 16 17 union uint32_buf { 18 uint8_t bytebuf[4]; 19 uint32_t val; 20 }; 21 22 union uint64_buf { 23 uint8_t bytebuf[8]; 24 uint64_t val; 25 }; 26 27 void 28 add_uint64(std::vector<uint8_t> &buf, uint64_t val) 29 { 30 uint64_buf conv; 31 conv.val = val; 32 for (int i = 0; i < 8; i++) 33 buf.push_back(conv.bytebuf[i]); 34 } 35 36 void 37 add_uint32(std::vector<uint8_t> &buf, uint32_t val) 38 { 39 uint32_buf conv; 40 conv.val = val; 41 for (int i = 0; i < 4; i++) 42 buf.push_back(conv.bytebuf[i]); 43 } 44 45 std::vector<uint8_t> 46 x86_lc_thread_load_command () 47 { 48 std::vector<uint8_t> data; 49 add_uint32 (data, LC_THREAD); // thread_command.cmd 50 add_uint32 (data, 184); // thread_command.cmdsize 51 add_uint32 (data, x86_THREAD_STATE64); // thread_command.flavor 52 add_uint32 (data, x86_THREAD_STATE64_COUNT); // thread_command.count 53 add_uint64 (data, 0x0000000000000000); // rax 54 add_uint64 (data, 0x0000000000000400); // rbx 55 add_uint64 (data, 0x0000000000000000); // rcx 56 add_uint64 (data, 0x0000000000000000); // rdx 57 add_uint64 (data, 0x0000000000000000); // rdi 58 add_uint64 (data, 0x0000000000000000); // rsi 59 add_uint64 (data, 0xffffff9246e2ba20); // rbp 60 add_uint64 (data, 0xffffff9246e2ba10); // rsp 61 add_uint64 (data, 0x0000000000000000); // r8 62 add_uint64 (data, 0x0000000000000000); // r9 63 add_uint64 (data, 0x0000000000000000); // r10 64 add_uint64 (data, 0x0000000000000000); // r11 65 add_uint64 (data, 0xffffff7f96ce5fe1); // r12 66 add_uint64 (data, 0x0000000000000000); // r13 67 add_uint64 (data, 0x0000000000000000); // r14 68 add_uint64 (data, 0xffffff9246e2bac0); // r15 69 add_uint64 (data, 0xffffff8015a8f6d0); // rip 70 add_uint64 (data, 0x0000000000011111); // rflags 71 add_uint64 (data, 0x0000000000022222); // cs 72 add_uint64 (data, 0x0000000000033333); // fs 73 add_uint64 (data, 0x0000000000044444); // gs 74 return data; 75 } 76 77 void 78 add_lc_note_kern_ver_str_load_command (std::vector<std::vector<uint8_t> > &loadcmds, 79 std::vector<uint8_t> &payload, 80 int payload_file_offset, 81 std::string ident) 82 { 83 std::vector<uint8_t> loadcmd_data; 84 85 add_uint32 (loadcmd_data, LC_NOTE); // note_command.cmd 86 add_uint32 (loadcmd_data, 40); // note_command.cmdsize 87 char lc_note_name[16]; 88 memset (lc_note_name, 0, 16); 89 strcpy (lc_note_name, "kern ver str"); 90 91 // lc_note.data_owner 92 for (int i = 0; i < 16; i++) 93 loadcmd_data.push_back (lc_note_name[i]); 94 95 // we start writing the payload at payload_file_offset to leave 96 // room at the start for the header & the load commands. 97 uint64_t current_payload_offset = payload.size() + payload_file_offset; 98 99 add_uint64 (loadcmd_data, current_payload_offset); // note_command.offset 100 add_uint64 (loadcmd_data, 4 + ident.size() + 1); // note_command.size 101 102 loadcmds.push_back (loadcmd_data); 103 104 add_uint32 (payload, 1); // kerneL_version_string.version 105 for (int i = 0; i < ident.size() + 1; i++) 106 { 107 payload.push_back (ident[i]); 108 } 109 } 110 111 void 112 add_lc_segment (std::vector<std::vector<uint8_t> > &loadcmds, 113 std::vector<uint8_t> &payload, 114 int payload_file_offset) 115 { 116 std::vector<uint8_t> loadcmd_data; 117 struct segment_command_64 seg; 118 seg.cmd = LC_SEGMENT_64; 119 seg.cmdsize = sizeof (struct segment_command_64); // no sections 120 memset (seg.segname, 0, 16); 121 seg.vmaddr = 0xffffff7f96400000; 122 seg.vmsize = 4096; 123 seg.fileoff = payload.size() + payload_file_offset; 124 seg.filesize = 0; 125 seg.maxprot = 1; 126 seg.initprot = 1; 127 seg.nsects = 0; 128 seg.flags = 0; 129 130 uint8_t *p = (uint8_t*) &seg; 131 for (int i = 0; i < sizeof (struct segment_command_64); i++) 132 { 133 loadcmd_data.push_back (*(p + i)); 134 } 135 loadcmds.push_back (loadcmd_data); 136 } 137 138 std::string 139 get_uuid_from_binary (const char *fn) 140 { 141 FILE *f = fopen(fn, "r"); 142 if (f == nullptr) 143 { 144 fprintf (stderr, "Unable to open binary '%s' to get uuid\n", fn); 145 exit(1); 146 } 147 uint32_t num_of_load_cmds = 0; 148 uint32_t size_of_load_cmds = 0; 149 std::string uuid; 150 off_t file_offset = 0; 151 152 uint8_t magic[4]; 153 if (::fread (magic, 1, 4, f) != 4) 154 { 155 fprintf (stderr, "Failed to read magic number from input file %s\n", fn); 156 exit (1); 157 } 158 uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce}; 159 uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe}; 160 uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf}; 161 uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe}; 162 163 if (memcmp (magic, magic_32_be, 4) == 0 || memcmp (magic, magic_64_be, 4) == 0) 164 { 165 fprintf (stderr, "big endian corefiles not supported\n"); 166 exit (1); 167 } 168 169 ::fseeko (f, 0, SEEK_SET); 170 if (memcmp (magic, magic_32_le, 4) == 0) 171 { 172 struct mach_header mh; 173 if (::fread (&mh, 1, sizeof (mh), f) != sizeof (mh)) 174 { 175 fprintf (stderr, "error reading mach header from input file\n"); 176 exit (1); 177 } 178 if (mh.cputype != CPU_TYPE_X86_64) 179 { 180 fprintf (stderr, "This tool creates an x86_64 corefile but " 181 "the supplied binary '%s' is cputype 0x%x\n", 182 fn, (uint32_t) mh.cputype); 183 exit (1); 184 } 185 num_of_load_cmds = mh.ncmds; 186 size_of_load_cmds = mh.sizeofcmds; 187 file_offset += sizeof (struct mach_header); 188 } 189 else 190 { 191 struct mach_header_64 mh; 192 if (::fread (&mh, 1, sizeof (mh), f) != sizeof (mh)) 193 { 194 fprintf (stderr, "error reading mach header from input file\n"); 195 exit (1); 196 } 197 if (mh.cputype != CPU_TYPE_X86_64) 198 { 199 fprintf (stderr, "This tool creates an x86_64 corefile but " 200 "the supplied binary '%s' is cputype 0x%x\n", 201 fn, (uint32_t) mh.cputype); 202 exit (1); 203 } 204 num_of_load_cmds = mh.ncmds; 205 size_of_load_cmds = mh.sizeofcmds; 206 file_offset += sizeof (struct mach_header_64); 207 } 208 209 off_t load_cmds_offset = file_offset; 210 211 for (int i = 0; i < num_of_load_cmds && (file_offset - load_cmds_offset) < size_of_load_cmds; i++) 212 { 213 ::fseeko (f, file_offset, SEEK_SET); 214 uint32_t cmd; 215 uint32_t cmdsize; 216 ::fread (&cmd, sizeof (uint32_t), 1, f); 217 ::fread (&cmdsize, sizeof (uint32_t), 1, f); 218 if (cmd == LC_UUID) 219 { 220 struct uuid_command uuidcmd; 221 ::fseeko (f, file_offset, SEEK_SET); 222 if (::fread (&uuidcmd, 1, sizeof (uuidcmd), f) != sizeof (uuidcmd)) 223 { 224 fprintf (stderr, "Unable to read LC_UUID load command.\n"); 225 exit (1); 226 } 227 uuid_string_t uuidstr; 228 uuid_unparse (uuidcmd.uuid, uuidstr); 229 uuid = uuidstr; 230 break; 231 } 232 file_offset += cmdsize; 233 } 234 return uuid; 235 } 236 237 int main (int argc, char **argv) 238 { 239 if (argc != 2 && argc != 3) 240 { 241 fprintf (stderr, "usage: create-empty-corefile <output-core-name> [binary-to-copy-uuid-from]\n"); 242 fprintf (stderr, "Create a Mach-O corefile with an LC_NOTE 'kern ver str' load command/payload\n"); 243 fprintf (stderr, "If a binary is given as a second argument, the Mach-O UUID of that file will\n"); 244 fprintf (stderr, "be read and used in the corefile's LC_NOTE payload.\n"); 245 exit (1); 246 } 247 248 std::string ident = "EFI UUID=3F9BA21F-55EA-356A-A349-BBA6F51FE8B1"; 249 if (argc == 3) 250 { 251 std::string uuid_from_file = get_uuid_from_binary (argv[2]); 252 if (!uuid_from_file.empty()) 253 { 254 ident = "EFI UUID="; 255 ident += uuid_from_file; 256 } 257 } 258 259 // An array of load commands (in the form of byte arrays) 260 std::vector<std::vector<uint8_t> > load_commands; 261 262 // An array of corefile contents (page data, lc_note data, etc) 263 std::vector<uint8_t> payload; 264 265 // First add all the load commands / payload so we can figure out how large 266 // the load commands will actually be. 267 load_commands.push_back (x86_lc_thread_load_command()); 268 add_lc_note_kern_ver_str_load_command (load_commands, payload, 0, ident); 269 add_lc_segment (load_commands, payload, 0); 270 271 int size_of_load_commands = 0; 272 for (const auto &lc : load_commands) 273 size_of_load_commands += lc.size(); 274 275 int header_and_load_cmd_room = sizeof (struct mach_header_64) + size_of_load_commands; 276 277 // Erease the load commands / payload now that we know how much space is needed, 278 // redo it. 279 load_commands.clear(); 280 payload.clear(); 281 282 load_commands.push_back (x86_lc_thread_load_command()); 283 add_lc_note_kern_ver_str_load_command (load_commands, payload, header_and_load_cmd_room, ident); 284 add_lc_segment (load_commands, payload, header_and_load_cmd_room); 285 286 struct mach_header_64 mh; 287 mh.magic = MH_MAGIC_64; 288 mh.cputype = CPU_TYPE_X86_64; 289 mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL; 290 mh.filetype = MH_CORE; 291 mh.ncmds = load_commands.size(); 292 mh.sizeofcmds = size_of_load_commands; 293 mh.flags = 0; 294 mh.reserved = 0; 295 296 297 FILE *f = fopen (argv[1], "w"); 298 299 if (f == nullptr) 300 { 301 fprintf (stderr, "Unable to open file %s for writing\n", argv[1]); 302 exit (1); 303 } 304 305 fwrite (&mh, sizeof (struct mach_header_64), 1, f); 306 307 for (const auto &lc : load_commands) 308 fwrite (lc.data(), lc.size(), 1, f); 309 310 fseek (f, header_and_load_cmd_room, SEEK_SET); 311 312 fwrite (payload.data(), payload.size(), 1, f); 313 314 fclose (f); 315 } 316