| #include <stdio.h> |
| #include <stdlib.h> |
| #include <mach-o/loader.h> |
| #include <vector> |
| #include <string> |
| #include <mach/thread_status.h> |
| #include <string.h> |
| #include <uuid/uuid.h> |
| |
| // Create an empty corefile with a "kern ver str" LC_NOTE. |
| // If an existing binary is given as an optional 2nd argument on the cmd line, |
| // the UUID from that binary will be encoded in the corefile. |
| // Otherwise a pre-set UUID will be put in the corefile that |
| // is created. |
| |
| |
| union uint32_buf { |
| uint8_t bytebuf[4]; |
| uint32_t val; |
| }; |
| |
| union uint64_buf { |
| uint8_t bytebuf[8]; |
| uint64_t val; |
| }; |
| |
| void |
| add_uint64(std::vector<uint8_t> &buf, uint64_t val) |
| { |
| uint64_buf conv; |
| conv.val = val; |
| for (int i = 0; i < 8; i++) |
| buf.push_back(conv.bytebuf[i]); |
| } |
| |
| void |
| add_uint32(std::vector<uint8_t> &buf, uint32_t val) |
| { |
| uint32_buf conv; |
| conv.val = val; |
| for (int i = 0; i < 4; i++) |
| buf.push_back(conv.bytebuf[i]); |
| } |
| |
| std::vector<uint8_t> |
| x86_lc_thread_load_command () |
| { |
| std::vector<uint8_t> data; |
| add_uint32 (data, LC_THREAD); // thread_command.cmd |
| add_uint32 (data, 184); // thread_command.cmdsize |
| add_uint32 (data, x86_THREAD_STATE64); // thread_command.flavor |
| add_uint32 (data, x86_THREAD_STATE64_COUNT); // thread_command.count |
| add_uint64 (data, 0x0000000000000000); // rax |
| add_uint64 (data, 0x0000000000000400); // rbx |
| add_uint64 (data, 0x0000000000000000); // rcx |
| add_uint64 (data, 0x0000000000000000); // rdx |
| add_uint64 (data, 0x0000000000000000); // rdi |
| add_uint64 (data, 0x0000000000000000); // rsi |
| add_uint64 (data, 0xffffff9246e2ba20); // rbp |
| add_uint64 (data, 0xffffff9246e2ba10); // rsp |
| add_uint64 (data, 0x0000000000000000); // r8 |
| add_uint64 (data, 0x0000000000000000); // r9 |
| add_uint64 (data, 0x0000000000000000); // r10 |
| add_uint64 (data, 0x0000000000000000); // r11 |
| add_uint64 (data, 0xffffff7f96ce5fe1); // r12 |
| add_uint64 (data, 0x0000000000000000); // r13 |
| add_uint64 (data, 0x0000000000000000); // r14 |
| add_uint64 (data, 0xffffff9246e2bac0); // r15 |
| add_uint64 (data, 0xffffff8015a8f6d0); // rip |
| add_uint64 (data, 0x0000000000011111); // rflags |
| add_uint64 (data, 0x0000000000022222); // cs |
| add_uint64 (data, 0x0000000000033333); // fs |
| add_uint64 (data, 0x0000000000044444); // gs |
| return data; |
| } |
| |
| void |
| add_lc_note_kern_ver_str_load_command (std::vector<std::vector<uint8_t> > &loadcmds, |
| std::vector<uint8_t> &payload, |
| int payload_file_offset, |
| std::string ident) |
| { |
| std::vector<uint8_t> loadcmd_data; |
| |
| add_uint32 (loadcmd_data, LC_NOTE); // note_command.cmd |
| add_uint32 (loadcmd_data, 40); // note_command.cmdsize |
| char lc_note_name[16]; |
| memset (lc_note_name, 0, 16); |
| strcpy (lc_note_name, "kern ver str"); |
| |
| // lc_note.data_owner |
| for (int i = 0; i < 16; i++) |
| loadcmd_data.push_back (lc_note_name[i]); |
| |
| // we start writing the payload at payload_file_offset to leave |
| // room at the start for the header & the load commands. |
| uint64_t current_payload_offset = payload.size() + payload_file_offset; |
| |
| add_uint64 (loadcmd_data, current_payload_offset); // note_command.offset |
| add_uint64 (loadcmd_data, 4 + ident.size() + 1); // note_command.size |
| |
| loadcmds.push_back (loadcmd_data); |
| |
| add_uint32 (payload, 1); // kerneL_version_string.version |
| for (int i = 0; i < ident.size() + 1; i++) |
| { |
| payload.push_back (ident[i]); |
| } |
| } |
| |
| void |
| add_lc_segment (std::vector<std::vector<uint8_t> > &loadcmds, |
| std::vector<uint8_t> &payload, |
| int payload_file_offset) |
| { |
| std::vector<uint8_t> loadcmd_data; |
| struct segment_command_64 seg; |
| seg.cmd = LC_SEGMENT_64; |
| seg.cmdsize = sizeof (struct segment_command_64); // no sections |
| memset (seg.segname, 0, 16); |
| seg.vmaddr = 0xffffff7f96400000; |
| seg.vmsize = 4096; |
| seg.fileoff = payload.size() + payload_file_offset; |
| seg.filesize = 0; |
| seg.maxprot = 1; |
| seg.initprot = 1; |
| seg.nsects = 0; |
| seg.flags = 0; |
| |
| uint8_t *p = (uint8_t*) &seg; |
| for (int i = 0; i < sizeof (struct segment_command_64); i++) |
| { |
| loadcmd_data.push_back (*(p + i)); |
| } |
| loadcmds.push_back (loadcmd_data); |
| } |
| |
| std::string |
| get_uuid_from_binary (const char *fn) |
| { |
| FILE *f = fopen(fn, "r"); |
| if (f == nullptr) |
| { |
| fprintf (stderr, "Unable to open binary '%s' to get uuid\n", fn); |
| exit(1); |
| } |
| uint32_t num_of_load_cmds = 0; |
| uint32_t size_of_load_cmds = 0; |
| std::string uuid; |
| off_t file_offset = 0; |
| |
| uint8_t magic[4]; |
| if (::fread (magic, 1, 4, f) != 4) |
| { |
| fprintf (stderr, "Failed to read magic number from input file %s\n", fn); |
| exit (1); |
| } |
| uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce}; |
| uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe}; |
| uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf}; |
| uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe}; |
| |
| if (memcmp (magic, magic_32_be, 4) == 0 || memcmp (magic, magic_64_be, 4) == 0) |
| { |
| fprintf (stderr, "big endian corefiles not supported\n"); |
| exit (1); |
| } |
| |
| ::fseeko (f, 0, SEEK_SET); |
| if (memcmp (magic, magic_32_le, 4) == 0) |
| { |
| struct mach_header mh; |
| if (::fread (&mh, 1, sizeof (mh), f) != sizeof (mh)) |
| { |
| fprintf (stderr, "error reading mach header from input file\n"); |
| exit (1); |
| } |
| if (mh.cputype != CPU_TYPE_X86_64) |
| { |
| fprintf (stderr, "This tool creates an x86_64 corefile but " |
| "the supplied binary '%s' is cputype 0x%x\n", |
| fn, (uint32_t) mh.cputype); |
| exit (1); |
| } |
| num_of_load_cmds = mh.ncmds; |
| size_of_load_cmds = mh.sizeofcmds; |
| file_offset += sizeof (struct mach_header); |
| } |
| else |
| { |
| struct mach_header_64 mh; |
| if (::fread (&mh, 1, sizeof (mh), f) != sizeof (mh)) |
| { |
| fprintf (stderr, "error reading mach header from input file\n"); |
| exit (1); |
| } |
| if (mh.cputype != CPU_TYPE_X86_64) |
| { |
| fprintf (stderr, "This tool creates an x86_64 corefile but " |
| "the supplied binary '%s' is cputype 0x%x\n", |
| fn, (uint32_t) mh.cputype); |
| exit (1); |
| } |
| num_of_load_cmds = mh.ncmds; |
| size_of_load_cmds = mh.sizeofcmds; |
| file_offset += sizeof (struct mach_header_64); |
| } |
| |
| off_t load_cmds_offset = file_offset; |
| |
| for (int i = 0; i < num_of_load_cmds && (file_offset - load_cmds_offset) < size_of_load_cmds; i++) |
| { |
| ::fseeko (f, file_offset, SEEK_SET); |
| uint32_t cmd; |
| uint32_t cmdsize; |
| ::fread (&cmd, sizeof (uint32_t), 1, f); |
| ::fread (&cmdsize, sizeof (uint32_t), 1, f); |
| if (cmd == LC_UUID) |
| { |
| struct uuid_command uuidcmd; |
| ::fseeko (f, file_offset, SEEK_SET); |
| if (::fread (&uuidcmd, 1, sizeof (uuidcmd), f) != sizeof (uuidcmd)) |
| { |
| fprintf (stderr, "Unable to read LC_UUID load command.\n"); |
| exit (1); |
| } |
| uuid_string_t uuidstr; |
| uuid_unparse (uuidcmd.uuid, uuidstr); |
| uuid = uuidstr; |
| break; |
| } |
| file_offset += cmdsize; |
| } |
| return uuid; |
| } |
| |
| int main (int argc, char **argv) |
| { |
| if (argc != 2 && argc != 3) |
| { |
| fprintf (stderr, "usage: create-empty-corefile <output-core-name> [binary-to-copy-uuid-from]\n"); |
| fprintf (stderr, "Create a Mach-O corefile with an LC_NOTE 'kern ver str' load command/payload\n"); |
| fprintf (stderr, "If a binary is given as a second argument, the Mach-O UUID of that file will\n"); |
| fprintf (stderr, "be read and used in the corefile's LC_NOTE payload.\n"); |
| exit (1); |
| } |
| |
| std::string ident = "EFI UUID=3F9BA21F-55EA-356A-A349-BBA6F51FE8B1"; |
| if (argc == 3) |
| { |
| std::string uuid_from_file = get_uuid_from_binary (argv[2]); |
| if (!uuid_from_file.empty()) |
| { |
| ident = "EFI UUID="; |
| ident += uuid_from_file; |
| } |
| } |
| |
| // An array of load commands (in the form of byte arrays) |
| std::vector<std::vector<uint8_t> > load_commands; |
| |
| // An array of corefile contents (page data, lc_note data, etc) |
| std::vector<uint8_t> payload; |
| |
| // First add all the load commands / payload so we can figure out how large |
| // the load commands will actually be. |
| load_commands.push_back (x86_lc_thread_load_command()); |
| add_lc_note_kern_ver_str_load_command (load_commands, payload, 0, ident); |
| add_lc_segment (load_commands, payload, 0); |
| |
| int size_of_load_commands = 0; |
| for (const auto &lc : load_commands) |
| size_of_load_commands += lc.size(); |
| |
| int header_and_load_cmd_room = sizeof (struct mach_header_64) + size_of_load_commands; |
| |
| // Erease the load commands / payload now that we know how much space is needed, |
| // redo it. |
| load_commands.clear(); |
| payload.clear(); |
| |
| load_commands.push_back (x86_lc_thread_load_command()); |
| add_lc_note_kern_ver_str_load_command (load_commands, payload, header_and_load_cmd_room, ident); |
| add_lc_segment (load_commands, payload, header_and_load_cmd_room); |
| |
| struct mach_header_64 mh; |
| mh.magic = MH_MAGIC_64; |
| mh.cputype = CPU_TYPE_X86_64; |
| mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL; |
| mh.filetype = MH_CORE; |
| mh.ncmds = load_commands.size(); |
| mh.sizeofcmds = size_of_load_commands; |
| mh.flags = 0; |
| mh.reserved = 0; |
| |
| |
| FILE *f = fopen (argv[1], "w"); |
| |
| if (f == nullptr) |
| { |
| fprintf (stderr, "Unable to open file %s for writing\n", argv[1]); |
| exit (1); |
| } |
| |
| fwrite (&mh, sizeof (struct mach_header_64), 1, f); |
| |
| for (const auto &lc : load_commands) |
| fwrite (lc.data(), lc.size(), 1, f); |
| |
| fseek (f, header_and_load_cmd_room, SEEK_SET); |
| |
| fwrite (payload.data(), payload.size(), 1, f); |
| |
| fclose (f); |
| } |