brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.1 KiB · 3123542 Raw
316 lines · cpp
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 that14// is created.15 16 17union uint32_buf {18    uint8_t bytebuf[4];19    uint32_t val;20};21 22union uint64_buf {23    uint8_t bytebuf[8];24    uint64_t val;25};26 27void28add_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 36void37add_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 45std::vector<uint8_t>46x86_lc_thread_load_command ()47{48    std::vector<uint8_t> data;49    add_uint32 (data, LC_THREAD);                // thread_command.cmd50    add_uint32 (data, 184);                      // thread_command.cmdsize51    add_uint32 (data, x86_THREAD_STATE64);       // thread_command.flavor52    add_uint32 (data, x86_THREAD_STATE64_COUNT); // thread_command.count53    add_uint64 (data, 0x0000000000000000);       // rax54    add_uint64 (data, 0x0000000000000400);       // rbx55    add_uint64 (data, 0x0000000000000000);       // rcx56    add_uint64 (data, 0x0000000000000000);       // rdx57    add_uint64 (data, 0x0000000000000000);       // rdi58    add_uint64 (data, 0x0000000000000000);       // rsi59    add_uint64 (data, 0xffffff9246e2ba20);       // rbp60    add_uint64 (data, 0xffffff9246e2ba10);       // rsp61    add_uint64 (data, 0x0000000000000000);       // r8 62    add_uint64 (data, 0x0000000000000000);       // r9 63    add_uint64 (data, 0x0000000000000000);       // r1064    add_uint64 (data, 0x0000000000000000);       // r1165    add_uint64 (data, 0xffffff7f96ce5fe1);       // r1266    add_uint64 (data, 0x0000000000000000);       // r1367    add_uint64 (data, 0x0000000000000000);       // r1468    add_uint64 (data, 0xffffff9246e2bac0);       // r1569    add_uint64 (data, 0xffffff8015a8f6d0);       // rip70    add_uint64 (data, 0x0000000000011111);       // rflags71    add_uint64 (data, 0x0000000000022222);       // cs72    add_uint64 (data, 0x0000000000033333);       // fs73    add_uint64 (data, 0x0000000000044444);       // gs74    return data;75}76 77void78add_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.cmd86    add_uint32 (loadcmd_data, 40);               // note_command.cmdsize87    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_owner92    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 leave96    // 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.offset100    add_uint64 (loadcmd_data, 4 + ident.size() + 1);       // note_command.size101 102    loadcmds.push_back (loadcmd_data);103 104    add_uint32 (payload, 1);                 // kerneL_version_string.version105    for (int i = 0; i < ident.size() + 1; i++)106    {107        payload.push_back (ident[i]);108    }109}110 111void112add_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 sections120    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 138std::string139get_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    else190    {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 237int 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 large266    // 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    // Erase 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