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
add_uint64(std::vector<uint8_t> & buf,uint64_t val)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
add_uint32(std::vector<uint8_t> & buf,uint32_t val)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>
x86_lc_thread_load_command()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
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)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
add_lc_segment(std::vector<std::vector<uint8_t>> & loadcmds,std::vector<uint8_t> & payload,int payload_file_offset)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
get_uuid_from_binary(const char * fn)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 
main(int argc,char ** argv)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