1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
|
#include <getopt.h>
#include <mach-o/loader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
using namespace std;
[[noreturn]] void print_help(void) {
fprintf(stderr, "Append an LC_NOTE to a corefile. Usage: \n");
fprintf(stderr, " -i|--input <corefile>\n");
fprintf(stderr, " -o|--output <corefile>\n");
fprintf(stderr, " -n|--name <LC_NOTE name>\n");
fprintf(
stderr,
" -r|--remove-dups remove existing LC_NOTEs with this same name\n");
fprintf(stderr, " One of:\n");
fprintf(stderr, " -f|--file <file to embed as LC_NOTE payload>\n");
fprintf(stderr, " -s|--str <string to embed as LC_NOTE payload>\n");
exit(1);
}
void parse_args(int argc, char **argv, string &infile, string &outfile,
string ¬e_name, vector<uint8_t> &payload,
bool &remove_dups) {
const char *const short_opts = "i:o:n:f:s:hr";
const option long_opts[] = {{"input", required_argument, nullptr, 'i'},
{"output", required_argument, nullptr, 'o'},
{"name", required_argument, nullptr, 'n'},
{"file", required_argument, nullptr, 'f'},
{"str", required_argument, nullptr, 's'},
{"remove-dups", no_argument, nullptr, 'r'},
{"help", no_argument, nullptr, 'h'},
{nullptr, no_argument, nullptr, 0}};
optional<string> infile_str, outfile_str, name_str, payload_file_str,
payload_str;
remove_dups = false;
while (true) {
const auto opt = getopt_long(argc, argv, short_opts, long_opts, nullptr);
if (opt == -1)
break;
switch (opt) {
case 'i':
infile_str = optarg;
break;
case 'o':
outfile_str = optarg;
break;
case 'n':
name_str = optarg;
break;
case 'f':
payload_file_str = optarg;
break;
case 's':
payload_str = optarg;
break;
case 'r':
remove_dups = true;
break;
case 'h':
print_help();
}
}
if (!infile_str || !outfile_str || !name_str ||
(!payload_file_str && !payload_str))
print_help();
infile = *infile_str;
outfile = *outfile_str;
note_name = *name_str;
if (payload_str) {
payload.resize(payload_str->size(), 0);
memcpy(payload.data(), payload_str->c_str(), payload_str->size());
} else {
struct stat sb;
if (stat(payload_file_str->c_str(), &sb)) {
fprintf(stderr, "File '%s' does not exist.\n", payload_file_str->c_str());
exit(1);
}
payload.resize(sb.st_size, 0);
FILE *f = fopen(payload_file_str->c_str(), "r");
fread(payload.data(), 1, sb.st_size, f);
fclose(f);
}
}
struct all_image_infos_header {
uint32_t version; // currently 1
uint32_t imgcount; // number of binary images
uint64_t entries_fileoff; // file offset in the corefile of where the array of
// struct entry's begin.
uint32_t entry_size; // size of 'struct entry'.
uint32_t unused; // set to 0
};
struct image_entry {
uint64_t filepath_offset; // corefile offset of the c-string filepath,
// if available, else this should be set
// to UINT64_MAX.
uuid_t uuid; // uint8_t[16]. should be set to all zeroes if
// uuid is unknown.
uint64_t
load_address; // virtual addr of mach-o header, UINT64_MAX if unknown.
uint64_t seg_addrs_offset; // corefile offset to the array of struct
// segment_vmaddr's, UINT64_MAX if none.
uint32_t segment_count; // The number of segments for this binary, 0 if none.
uint32_t
executing; // Set to 0 if executing status is unknown by corefile
// creator.
// Set to 1 if this binary was executing on any thread,
// so it can be force-loaded by the corefile reader.
// Set to 2 if this binary was not executing on any thread.
};
int count_lc_notes_with_name(FILE *in, std::string name) {
fseeko(in, 0, SEEK_SET);
uint8_t magic[4];
if (fread(magic, 1, 4, in) != 4) {
printf("Failed to read magic number\n");
return 0;
}
uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe};
uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe};
if (memcmp(magic, magic_32_le, 4) != 0 &&
memcmp(magic, magic_64_le, 4) != 0) {
return 0;
}
fseeko(in, 0, SEEK_SET);
int number_of_load_cmds = 0;
size_t size_of_mach_header = 0;
if (memcmp(magic, magic_64_le, 4) == 0) {
struct mach_header_64 mh;
size_of_mach_header = sizeof(mh);
if (fread(&mh, sizeof(mh), 1, in) != 1) {
fprintf(stderr, "unable to read mach header\n");
return 0;
}
number_of_load_cmds = mh.ncmds;
} else {
struct mach_header mh;
size_of_mach_header = sizeof(mh);
if (fread(&mh, sizeof(mh), 1, in) != 1) {
fprintf(stderr, "unable to read mach header\n");
return 0;
}
number_of_load_cmds = mh.ncmds;
}
int notes_seen = 0;
fseeko(in, size_of_mach_header, SEEK_SET);
for (int i = 0; i < number_of_load_cmds; i++) {
off_t cmd_start = ftello(in);
uint32_t cmd, cmdsize;
fread(&cmd, sizeof(uint32_t), 1, in);
fread(&cmdsize, sizeof(uint32_t), 1, in);
fseeko(in, cmd_start, SEEK_SET);
off_t next_cmd = cmd_start + cmdsize;
if (cmd == LC_NOTE) {
struct note_command note;
fread(¬e, sizeof(note), 1, in);
if (strncmp(name.c_str(), note.data_owner, 16) == 0)
notes_seen++;
}
fseeko(in, next_cmd, SEEK_SET);
}
return notes_seen;
}
void copy_and_add_note(FILE *in, FILE *out, std::string lc_note_name,
vector<uint8_t> payload_data, bool remove_dups) {
int number_of_load_cmds = 0;
off_t header_start = ftello(in);
int notes_to_remove = 0;
if (remove_dups)
notes_to_remove = count_lc_notes_with_name(in, lc_note_name);
fseeko(in, header_start, SEEK_SET);
uint8_t magic[4];
if (fread(magic, 1, 4, in) != 4) {
printf("Failed to read magic number\n");
return;
}
uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe};
uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe};
if (memcmp(magic, magic_32_le, 4) != 0 &&
memcmp(magic, magic_64_le, 4) != 0) {
return;
}
fseeko(in, header_start, SEEK_SET);
off_t end_of_infine_loadcmds;
size_t size_of_mach_header = 0;
if (memcmp(magic, magic_64_le, 4) == 0) {
struct mach_header_64 mh;
size_of_mach_header = sizeof(mh);
if (fread(&mh, sizeof(mh), 1, in) != 1) {
fprintf(stderr, "unable to read mach header\n");
return;
}
number_of_load_cmds = mh.ncmds;
end_of_infine_loadcmds = sizeof(mh) + mh.sizeofcmds;
mh.ncmds += 1;
mh.ncmds -= notes_to_remove;
mh.sizeofcmds += sizeof(struct note_command);
mh.sizeofcmds -= notes_to_remove * sizeof(struct note_command);
fseeko(out, header_start, SEEK_SET);
fwrite(&mh, sizeof(mh), 1, out);
} else {
struct mach_header mh;
size_of_mach_header = sizeof(mh);
if (fread(&mh, sizeof(mh), 1, in) != 1) {
fprintf(stderr, "unable to read mach header\n");
return;
}
number_of_load_cmds = mh.ncmds;
end_of_infine_loadcmds = sizeof(mh) + mh.sizeofcmds;
mh.ncmds += 1;
mh.ncmds -= notes_to_remove;
mh.sizeofcmds += sizeof(struct note_command);
mh.sizeofcmds -= notes_to_remove * sizeof(struct note_command);
fseeko(out, header_start, SEEK_SET);
fwrite(&mh, sizeof(mh), 1, out);
}
off_t start_of_infile_load_cmds = ftello(in);
fseek(in, 0, SEEK_END);
off_t infile_size = ftello(in);
// LC_SEGMENT may be aligned to 4k boundaries, let's maintain
// that alignment by putting 4096 minus the size of the added
// LC_NOTE load command after the output file's load commands.
off_t end_of_outfile_loadcmds =
end_of_infine_loadcmds - (notes_to_remove * sizeof(struct note_command)) +
4096 - sizeof(struct note_command);
off_t slide = end_of_outfile_loadcmds - end_of_infine_loadcmds;
off_t all_image_infos_infile_offset = 0;
fseek(in, start_of_infile_load_cmds, SEEK_SET);
fseek(out, start_of_infile_load_cmds, SEEK_SET);
// Copy all the load commands from IN to OUT, updating any file offsets by
// SLIDE.
for (int cmd_num = 0; cmd_num < number_of_load_cmds; cmd_num++) {
off_t cmd_start = ftello(in);
uint32_t cmd, cmdsize;
fread(&cmd, sizeof(uint32_t), 1, in);
fread(&cmdsize, sizeof(uint32_t), 1, in);
fseeko(in, cmd_start, SEEK_SET);
off_t next_cmd = cmd_start + cmdsize;
switch (cmd) {
case LC_SEGMENT: {
struct segment_command segcmd;
fread(&segcmd, sizeof(segcmd), 1, in);
segcmd.fileoff += slide;
fwrite(&segcmd, cmdsize, 1, out);
} break;
case LC_SEGMENT_64: {
struct segment_command_64 segcmd;
fread(&segcmd, sizeof(segcmd), 1, in);
segcmd.fileoff += slide;
fwrite(&segcmd, cmdsize, 1, out);
} break;
case LC_NOTE: {
struct note_command notecmd;
fread(¬ecmd, sizeof(notecmd), 1, in);
if ((strncmp(lc_note_name.c_str(), notecmd.data_owner, 16) == 0) &&
remove_dups) {
fseeko(in, next_cmd, SEEK_SET);
continue;
}
if (strncmp("all image infos", notecmd.data_owner, 16) == 0)
all_image_infos_infile_offset = notecmd.offset;
notecmd.offset += slide;
fwrite(¬ecmd, cmdsize, 1, out);
} break;
default: {
vector<uint8_t> buf(cmdsize);
fread(buf.data(), cmdsize, 1, in);
fwrite(buf.data(), cmdsize, 1, out);
}
}
fseeko(in, next_cmd, SEEK_SET);
}
// Now add our additional LC_NOTE load command.
struct note_command note;
note.cmd = LC_NOTE;
note.cmdsize = sizeof(struct note_command);
memset(¬e.data_owner, 0, 16);
// data_owner may not be nul terminated if all 16 characters
// are used, intentionally using strncpy here.
strncpy(note.data_owner, lc_note_name.c_str(), 16);
note.offset = infile_size + slide;
note.size = payload_data.size();
fwrite(¬e, sizeof(struct note_command), 1, out);
fseeko(in, end_of_infine_loadcmds, SEEK_SET);
fseeko(out, end_of_outfile_loadcmds, SEEK_SET);
// Copy the rest of the corefile contents
vector<uint8_t> data_buf(1024 * 1024);
while (!feof(in)) {
size_t read_bytes = fread(data_buf.data(), 1, data_buf.size(), in);
if (read_bytes > 0) {
fwrite(data_buf.data(), read_bytes, 1, out);
} else {
break;
}
}
fwrite(payload_data.data(), payload_data.size(), 1, out);
// The "all image infos" LC_NOTE payload has file offsets hardcoded
// in it, unfortunately. We've shifted the contents of the corefile
// and these offsets need to be updated in the ouput file.
// Re-copy them into the outfile with corrected file offsets.
off_t infile_image_entry_base = 0;
if (all_image_infos_infile_offset != 0) {
off_t all_image_infos_outfile_offset =
all_image_infos_infile_offset + slide;
fseeko(in, all_image_infos_infile_offset, SEEK_SET);
struct all_image_infos_header header;
fread(&header, sizeof(header), 1, in);
infile_image_entry_base = header.entries_fileoff;
header.entries_fileoff += slide;
fseeko(out, all_image_infos_outfile_offset, SEEK_SET);
fwrite(&header, sizeof(header), 1, out);
for (int i = 0; i < header.imgcount; i++) {
off_t infile_entries_fileoff = header.entries_fileoff - slide;
off_t outfile_entries_fileoff = header.entries_fileoff;
struct image_entry ent;
fseeko(in, infile_entries_fileoff + (header.entry_size * i), SEEK_SET);
fread(&ent, sizeof(ent), 1, in);
ent.filepath_offset += slide;
ent.seg_addrs_offset += slide;
fseeko(out, outfile_entries_fileoff + (header.entry_size * i), SEEK_SET);
fwrite(&ent, sizeof(ent), 1, out);
}
}
}
int main(int argc, char **argv) {
string infile, outfile, name;
vector<uint8_t> payload;
bool remove_dups;
parse_args(argc, argv, infile, outfile, name, payload, remove_dups);
FILE *in = fopen(infile.c_str(), "r");
if (!in) {
fprintf(stderr, "Unable to open %s for reading\n", infile.c_str());
exit(1);
}
FILE *out = fopen(outfile.c_str(), "w");
if (!out) {
fprintf(stderr, "Unable to open %s for reading\n", outfile.c_str());
exit(1);
}
copy_and_add_note(in, out, name, payload, remove_dups);
fclose(in);
fclose(out);
}
|