-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
mboot.c
executable file
·337 lines (292 loc) · 9.4 KB
/
mboot.c
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
/* mboot.c - unpack and repack Intel boot.img for Android
**
** Based on https://github.com/osm0sis/mboot_py/blob/master/mboot.py
**
** Copyright 2014 Jocelyn Falempe (Intel Corporation)
** Copyright 2019 Chris Renshaw (osm0sis @ xda-developers)
** Shaka Huang (shakalaca @ xda-developers / ASUS ZenTalk)
**
** This program is free software; you can redistribute it and/or modify it
** under the terms and conditions of the GNU General Public License,
** version 2, as published by the Free Software Foundation.
**
** This program is distributed in the hope it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
char *directory = "./";
char *filename = "boot.img";
int debug = 0;
int usage(int val)
{
fprintf(stderr,
"Usage: mboot.py [-u] [-f FILE] [-d DIR]\n\n"
"Unpack an Intel boot image into separate files, OR,\n"
"pack a directory with kernel/ramdisk/bootstub into an Intel boot image.\n\n"
"Options:\n"
" -h, --help show this help message and exit\n"
" -u, --unpack split boot image into kernel, ramdisk, bootstub, etc.\n"
" -f, --file FILE use FILE to unpack/repack (default: boot.img)\n"
" -d, --dir DIR use DIR to unpack/repack (default: ./)\n"
);
return val;
}
// use custom functions since it seems libc isalnum() cannot be trusted cross-platform
int xisalpha(int c) { return ((unsigned int)(c|('A'^'a')) - 'a') <= 'z'-'a'; }
int xisdigit(int c) { return ((unsigned int)(c - '0')) < 10; }
int xisalnum(int c) { return (xisalpha(c) || xisdigit(c)); }
int check_byte(FILE *f, int size, int min)
{
unsigned char *tmp = malloc(size);
int bytes = 0;
if (fread(tmp, size, 1, f)) {};
fseek(f, -size, SEEK_CUR);
int i;
for (i = 0; i < size; i++) {
bytes = bytes + xisalnum((int)tmp[i]);
if (debug > 1) {
printf("%d 0x%02X\n", bytes, tmp[i]);
}
}
free(tmp);
if (debug) {
printf("%4ld: %d\n", ftell(f), bytes);
}
// add custom fault tolerance to try and avoid false positives
return (bytes >= min);
}
void write_buffer(FILE *f, int size, char *name)
{
char outpath[PATH_MAX];
unsigned char *buffer = malloc(size);
sprintf(outpath, "%s/%s", directory, name);
FILE *t = fopen(outpath, "wb");
if (fread(buffer, size, 1, f)) {};
fwrite(buffer, size, 1, t);
fclose(t);
free(buffer);
}
void write_string(char *string, char *name)
{
char outpath[PATH_MAX];
sprintf(outpath, "%s/%s", directory, name);
FILE *t = fopen(outpath, "w");
fwrite(string, strlen(string), 1, t);
fclose(t);
}
int unpack()
{
FILE *f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "mboot: cannot open input file '%s': %s\n", filename, strerror(errno));
return 1;
}
// header is 512 bytes but may rarely not exist on some devices
if (!check_byte(f, 1, 1)) {
write_buffer(f, 512, "hdr");
}
int hdr_size = ftell(f);
printf("header size %d\n", hdr_size);
// header may have 480, 728 or 1024 bytes of signature appended on some devices
int sig_deltas[] = { 0, 480, 248, 296 };
int i;
for (i = 0; i < (sizeof(sig_deltas) / sizeof(sig_deltas[0])); i++) {
fseek(f, sig_deltas[i], SEEK_CUR);
if (check_byte(f, 4, 4)) {
break;
}
}
int sig_size = ftell(f) - hdr_size;
if (sig_size > 0) {
fseek(f, -sig_size, SEEK_CUR);
write_buffer(f, sig_size, "sig");
}
printf("sig size %d\n", sig_size);
// cmdline is up to 1024 bytes padded with \x00
char *cmdline[1024];
if (fread(cmdline, 1024, 1, f)) {};
write_string((char *)cmdline, "cmdline.txt");
// image info is the next 16 bytes padded out to 3072 bytes
uint8_t kernel_size_buffer[4];
if (fread(kernel_size_buffer, 4, 1, f)) {};
uint8_t ramdisk_size_buffer[4];
if (fread(ramdisk_size_buffer, 4, 1, f)) {};
write_buffer(f, 8, "parameter");
fseek(f, 3072 - 16, SEEK_CUR);
// bootstub is 4096 bytes but can be 8192 bytes on some devices
fseek(f, 4096, SEEK_CUR);
if (check_byte(f, 2, 1)) {
fseek(f, 4096, SEEK_CUR);
}
int bootstub_size = ftell(f) - hdr_size - sig_size - 4096;
fseek(f, -bootstub_size, SEEK_CUR);
write_buffer(f, bootstub_size, "bootstub");
printf("bootstub size %d\n", bootstub_size);
uint32_t kernel_size = *(uint32_t *)kernel_size_buffer;
if (kernel_size < 500000 || kernel_size > 15000000) {
fprintf(stderr, "mboot: unpacking error: kernel size likely wrong\n");
return 1;
}
write_buffer(f, kernel_size, "kernel");
printf("kernel size %d\n", kernel_size);
uint32_t ramdisk_size = *(uint32_t *)ramdisk_size_buffer;
if (ramdisk_size < 10000 || ramdisk_size > 300000000) {
fprintf(stderr, "mboot: unpacking error: ramdisk size likely wrong\n");
return 1;
}
write_buffer(f, ramdisk_size, "ramdisk.cpio.gz");
printf("ramdisk size %d\n", ramdisk_size);
fclose(f);
return 0;
}
void *read_file(char *name, unsigned *_size)
{
char inpath[PATH_MAX];
sprintf(inpath, "%s/%s", directory, name);
FILE *t = fopen(inpath, "rb");
if (!t) {
return 0;
}
fseek(t, 0, SEEK_END);
int size = ftell(t);
unsigned char *data = malloc(size);
fseek(t, 0, SEEK_SET);
if (fread(data, size, 1, t)) {};
fwrite(data, size, 1, t);
fclose(t);
if (_size) {
*_size = size;
}
return data;
}
int pack()
{
uint32_t hdr_size = 0;
void *hdr_data = read_file("hdr", &hdr_size);
uint32_t sig_size = 0;
void *sig_data = read_file("sig", &sig_size);
char *required_file[] = { "cmdline.txt", "parameter", "bootstub", "kernel", "ramdisk.cpio.gz" };
uint32_t required_size[5];
void *required_data[5] = { 0, 0, 0, 0, 0 };
int i;
for (i = 0; i < (sizeof(required_file) / sizeof(required_file[0])); i++) {
required_data[i] = read_file(required_file[i], &required_size[i]);
if (!required_data[i]) {
fprintf(stderr, "mboot: cannot open input file '%s': %s\n", required_file[i], strerror(errno));
return 1;
}
}
FILE *f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "mboot: cannot open output file '%s': %s\n", filename, strerror(errno));
return 1;
}
// calculate image size and size of padding to next full 512 byte sector
int img_size = hdr_size + sig_size + 4096 + required_size[2] + required_size[3] + required_size[4];
int padding_size = 512 - (img_size % 512) < 512 ? 512 - (img_size % 512) : 0;
unsigned char *bootimg = malloc(img_size + padding_size);
// add header if present
if (hdr_data) {
memcpy(bootimg, hdr_data, hdr_size);
}
// add signature if present and add parameter padding magic for signed image
if (sig_data) {
memcpy(bootimg + hdr_size, sig_data, sig_size);
memcpy(bootimg + (hdr_size + sig_size + 1024 + 16), "\xBD\x02\xBD\x02\xBD\x12\xBD\x12", 8);
// adjust header imgtype based on signature presence
} else if (hdr_data) {
unsigned char *imgtype_buffer = malloc(4);
memcpy(imgtype_buffer, bootimg + 52, 4);
uint32_t imgtype = *(uint32_t *)imgtype_buffer|0x01;
memcpy(bootimg + 52, &imgtype, 4);
free(imgtype_buffer);
}
// add cmdline, image info (kernel and ramdisk sizes), and parameter to their 4096 byte block
memcpy(bootimg + (hdr_size + sig_size), required_data[0], required_size[0]);
memcpy(bootimg + (hdr_size + sig_size + 1024), &required_size[3], sizeof(required_size[3]));
memcpy(bootimg + (hdr_size + sig_size + 1024 + 4), &required_size[4], sizeof(required_size[4]));
memcpy(bootimg + (hdr_size + sig_size + 1024 + 8), required_data[1], required_size[1]);
// add bootstub, kernel and ramdisk
memcpy(bootimg + (hdr_size + sig_size + 4096), required_data[2], required_size[2]);
memcpy(bootimg + (hdr_size + sig_size + 4096 + required_size[2]), required_data[3], required_size[3]);
memcpy(bootimg + (hdr_size + sig_size + 4096 + required_size[2] + required_size[3]), required_data[4], required_size[4]);
// add trailing padding
memset(bootimg + img_size, (int)'\xFF', padding_size);
// update sector count and xor checksum in header
if (hdr_data) {
uint32_t sectors = ((img_size + padding_size) / 512 - 1);
memcpy(bootimg + 48, §ors, 4);
uint8_t xor = 0;
unsigned char *hdr_calc = malloc(56);
memcpy(hdr_calc, bootimg, 56);
memcpy(hdr_calc + 7, "\x00", 1);
for (i = 0; i < 56; i++) {
xor ^= hdr_calc[i];
}
memcpy(bootimg + 7, &xor, 1);
free(hdr_calc);
}
fwrite(bootimg, img_size + padding_size, 1, f);
fclose(f);
free(bootimg);
return 0;
}
int main(int argc, char **argv)
{
int unpackimg = 0;
argc--;
argv++;
while (argc > 0) {
char *arg = argv[0];
if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
return usage(0);
} else if (!strcmp(arg, "-u") || !strcmp(arg, "--unpack")) {
unpackimg = 1;
argc -= 1;
argv += 1;
} else if (!strcmp(arg, "--debug")) {
debug = 1;
argc -= 1;
argv += 1;
} else if (!strcmp(arg, "--debug-more")) {
debug = 2;
argc -= 1;
argv += 1;
} else if (argc >= 2) {
char *val = argv[1];
argc -= 2;
argv += 2;
if (!strcmp(arg, "-f") || !strcmp(arg, "--file")) {
filename = val;
} else if (!strcmp(arg, "-d") || !strcmp(arg, "--dir")) {
directory = val;
} else {
return usage(1);
}
} else {
return usage(1);
}
}
struct stat st;
if (stat(directory, &st) == (-1)) {
fprintf(stderr, "mboot: cannot access '%s': %s\n", directory, strerror(errno));
return 1;
}
if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "mboot: cannot access '%s': Is not a directory\n", directory);
return 1;
}
if (unpackimg) {
return unpack();
} else {
return pack();
}
}