Root/tools/wrt350nv2-builder/src/wrt350nv2-builder.c

1/*
2
3    WRT350Nv2-Builder 2.3 (previously called buildimg)
4    Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5    Copyright (C) 2009-2010 Matthias Buecher (http://www.maddes.net/)
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21    A lot of thanks to Kaloz and juhosg from OpenWRT and Lennert Buytenhek from
22    marvell for helping me figure this one out. This code is based on bash
23    scripts wrote by Peter van Valderen so the real credit should go to him.
24
25    This program reads the provided parameter file and creates an image which can
26    be used to flash a Linksys WRT350N v2 from stock firmware.
27    The trick is to fill unused space in the bin file with random, so that the
28    resulting zip file passes the size check of the stock firmware.
29
30    The parameter file layout for an original Linksys firmware:
31        :kernel 0x001A0000 /path/to/uImage
32        :rootfs 0 /path/to/root.squashfs
33        :u-boot 0 /path/to/u-boot.bin
34        #version 0x2020
35
36    args:
37        1 wrt350nv2.par parameter file describing the image layout
38        2 wrt350nv2.img output file for linksys style image
39
40    A u-boot image inside the bin file is not necessary.
41    The version is not important.
42    The name of the bin file is not important, but still "wrt350n.bin" is used to
43    keep as close as possible to the stock firmware.
44
45    Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
46    of the mtd are abused to define the length of the next mtd content (4 bytes for
47    size + 12 pad bytes).
48
49    At the end of "rootfs" additional 16 bytes are abused for some data and a
50    highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
51
52    At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
53    highly important sErCoMm identifier.
54
55
56    This program uses a special GNU scanf modifier to allocate
57    sufficient memory for a strings with unknown length.
58    See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
59
60
61    To extract everything from a Linksys style firmware image see
62    https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
63
64    Changelog:
65    v2.3 - allow jffs by adding its magic number (0x8519)
66           added parameter option -i to ignore unknown magic numbers
67    v2.2 - fixed checksum byte calculation for other versions than 0x2019
68           fixed rare problem with padsize
69           updated info to stock firmware 2.00.20
70           fixed typos
71    v2.1 - used "wrt350n.bin" for the created image (closer to stock)
72        added option to create the image in two separate steps (-b / -z)
73    v2.0 - complete re-write
74
75*/
76
77// includes
78#define _GNU_SOURCE // for GNU's basename()
79#include <assert.h>
80#include <errno.h> // errno
81#include <stdarg.h>
82#include <stdio.h> // fopen(), fread(), fclose(), etc.
83#include <stdlib.h> // system(), etc.
84#include <string.h> // basename(), strerror(), strdup(), etc.
85#include <unistd.h> // optopt(), access(), etc.
86#include <libgen.h>
87#include <sys/wait.h> // WEXITSTATUS, etc.
88
89// custom includes
90#include "md5.h" // MD5 routines
91#include "upgrade.h" // Linksys definitions from firmware 2.0.19 (unchanged up to 2.0.20)
92
93
94// version info
95#define VERSION "2.3"
96char program_info[] = "WRT350Nv2-Builder v%s by Dirk Teurlings <info@upexia.nl> and Matthias Buecher (http://www.maddes.net/)\n";
97
98// verbosity
99#define DEBUG 1
100#define DEBUG_LVL2 2
101int verbosity = 0;
102
103// mtd info
104typedef struct {
105    char *name;
106    int offset;
107    int size;
108    char *filename;
109    long int filesize;
110    unsigned char magic[2];
111} mtd_info;
112
113mtd_info mtd_kernel = { "kernel", 0, 0, NULL, 0L, { 0, 0 } };
114mtd_info mtd_rootfs = { "rootfs", 0, 0, NULL, 0L, { 0, 0 } };
115mtd_info mtd_uboot = { "u-boot", 0, 0, NULL, 0L, { 0, 0 } };
116
117#define ROOTFS_END_OFFSET 0x00760000
118#define ROOTFS_MIN_OFFSET 0x00640000 // should be filled up to here, to make sure that the zip file is big enough to pass the size check of the stock firmware
119                        // 2.0.17: filled up to 0x00640000
120                        // 2.0.19: filled up to 0x00670000
121                        // 2.0.20: filled up to 0x00670000
122
123// rootfs statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x0075FFE0 -n 16 "wrt350n.bin" ; echo -en "\n"
124unsigned char product_id[] = { 0x00, 0x03 }; // seems to be a fixed value
125unsigned char protocol_id[] = { 0x00, 0x00 }; // seems to be a fixed value
126unsigned char fw_version[] = { 0x20, 0x20 };
127unsigned char rootfs_unknown[] = { 0x90, 0xF7 }; // seems to be a fixed value
128unsigned char sign[] = { 0x65, 0x52, 0x63, 0x4F, 0x6D, 0x4D, 0x00, 0x00 }; // eRcOmM
129
130// u-boot statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x007FFF80 -n 128 "wrt350n.bin" ; echo -en "\n"
131//unsigned char sn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (12) seems to be an unused value
132//unsigned char pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (8) seems to be an unused value
133//unsigned char node[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (25) seems to be an unused value
134// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
135//unsigned char checksum[] = { 0xE9 }; // (1) is calculated, does it belong to node?
136unsigned char pid[] = { 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, // (70) seems to be a fixed value, except for fw version
137                0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, // protocol id?
139                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // protocol id?
140                0x12, 0x34, // firmware version, same as in rootfs
141                0x00, 0x00, 0x00, 0x04,
142                0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D }; // sErCoMm
143
144// img statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0 -n 512 "WRT350N-EU-ETSI-2.00.19.img" ; echo -en "\n" (unchanged up to 2.0.20)
145unsigned char img_hdr[] = { 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
148                0x00, 0x00,
149                0x12, 0x34, // firmware version, same as in rootfs
150                0x00, 0x00, 0x00, 0x04, 0x61, 0x44, 0x6D, 0x42, 0x6C, 0x4B, 0x3D, 0x00,
151                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177                0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // md5 checksum
178                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
179
180unsigned char img_eof[] = { 0xFF };
181
182
183void lprintf(int outputlevel, char *fmt, ...) {
184    va_list argp;
185    if (outputlevel <= verbosity) {
186        va_start(argp, fmt);
187        vprintf(fmt, argp);
188        va_end(argp);
189    }
190}
191
192
193int parse_par_file(FILE *f_par) {
194    int exitcode = 0;
195
196    char *buffer;
197    size_t buffer_size;
198    char *line;
199
200    int lineno;
201    int count;
202
203    char string1[256];
204    char string2[256];
205    int value;
206
207    mtd_info *mtd;
208    FILE *f_in;
209    int f_exitcode = 0;
210
211    // read all lines
212    buffer_size = 1000;
213    buffer = NULL;
214    lineno = 0;
215    while (!feof(f_par)) {
216        // read next line into memory
217        do {
218            // allocate memory for input line
219            if (!buffer) {
220                buffer = malloc(buffer_size);
221            }
222            if (!buffer) {
223                exitcode = 1;
224                printf("parse_par_file: can not allocate %i bytes\n", (int) buffer_size);
225                break;
226            }
227
228            line = fgets(buffer, buffer_size, f_par);
229            if (!line) {
230                exitcode = ferror(f_par);
231                if (exitcode) {
232                    printf("parse_par_file: %s\n", strerror(exitcode));
233                }
234                break;
235            }
236
237            // if buffer was not completely filled, then assume that line is complete
238            count = strlen(buffer) + 1;
239            if (count-- < buffer_size) {
240                break;
241            }
242
243            // otherwise....
244
245            // reset file position to line start
246            value = fseek(f_par, -count, SEEK_CUR);
247            if (value == -1) {
248                exitcode = errno;
249                printf("parse_par_file: %s\n", strerror(exitcode));
250                break;
251            }
252
253            // double buffer size
254            free(buffer);
255            buffer = NULL;
256            buffer_size *= 2;
257            lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
258        } while (1);
259        if ((!line) || (exitcode)) {
260            break;
261        }
262
263        lineno++; // increase line number
264
265        lprintf(DEBUG_LVL2, " line %i (%i) %s", lineno, count, line);
266
267        value = 0;
268        mtd = NULL;
269
270        // split line if starting with a colon
271        switch (line[0]) {
272            case ':':
273                count = sscanf(line, ":%255s %i %255s", string1, &value, string2);
274                if (count != 3) {
275                    printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
276                } else {
277                    // populate mtd_info if supported mtd names
278                    if (!strcmp(string1, mtd_kernel.name)) {
279                        mtd = &mtd_kernel;
280                    } else if (!strcmp(string1, mtd_rootfs.name)) {
281                        mtd = &mtd_rootfs;
282                    } else if (!strcmp(string1, mtd_uboot.name)) {
283                        mtd = &mtd_uboot;
284                    }
285
286                    if (!mtd) {
287                        printf("unknown mtd %s in line %i\n", string1, lineno);
288                    } else if (mtd->filename) {
289                        f_exitcode = 1;
290                        printf("mtd %s in line %i multiple definitions\n", string1, lineno);
291                    } else {
292                        mtd->size = value;
293                        mtd->filename = strdup(string2);
294
295                        // Get file size
296                        f_in = fopen(mtd->filename, "rb");
297                        if (!f_in) {
298                            f_exitcode = errno;
299                            printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
300                        } else {
301                            value = fread(&mtd->magic, 1, 2, f_in);
302                            if (value < 2) {
303                                if (ferror(f_in)) {
304                                    f_exitcode = ferror(f_in);
305                                    printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
306                                } else {
307                                    f_exitcode = 1;
308                                    printf("input file %s: smaller than two bytes, no magic code\n", mtd->filename);
309                                }
310                            }
311
312                            value = fseek(f_in, 0, SEEK_END);
313                            if (value == -1) {
314                                f_exitcode = errno;
315                                printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
316                            } else {
317                                mtd->filesize = ftell(f_in);
318                                if (mtd->filesize == -1) {
319                                    f_exitcode = errno;
320                                    printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
321                                }
322                            }
323
324                            fclose(f_in);
325                        }
326
327                        lprintf(DEBUG, "mtd %s in line %i: size=0x%08X, filesize=0x%08lX, magic=0x%02X%02X, file=%s\n", mtd->name, lineno, mtd->size, mtd->filesize, mtd->magic[0], mtd->magic[1], mtd->filename);
328                    }
329                }
330                break;
331            case '#': // integer values
332                count = sscanf(line, "#%255s %i", string1, &value);
333                if (count != 2) {
334                    printf("line %i does not meet defined format (#<variable name> <integer>\n", lineno);
335                } else {
336                    if (!strcmp(string1, "version")) {
337                        // changing version
338                        fw_version[0] = 0x000000FF & ( value >> 8 );
339                        fw_version[1] = 0x000000FF & value;
340                    } else {
341                        printf("unknown integer variable %s in line %i\n", string1, lineno);
342                    }
343
344                    lprintf(DEBUG, "integer variable %s in line %i: 0x%08X\n", string1, lineno, value);
345                }
346                break;
347            case '$': // strings
348                count = sscanf(line, "$%255s %255s", string1, string2);
349                if (count != 2) {
350                    printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
351                } else {
352/*
353                    if (!strcmp(string1, "something")) {
354                        something = strdup(string2);
355                    } else {
356*/
357                        printf("unknown string variable %s in line %i\n", string1, lineno);
358// }
359                    lprintf(DEBUG, "string variable %s in line %i: %s\n", string1, lineno, string2);
360                }
361                break;
362            default:
363                break;
364        }
365    }
366    free(buffer);
367
368    if (!exitcode) {
369        exitcode = f_exitcode;
370    }
371
372    return exitcode;
373}
374
375
376int create_bin_file(char *bin_filename) {
377    int exitcode = 0;
378
379    unsigned char *buffer;
380
381    int i;
382    mtd_info *mtd;
383    int addsize;
384    int padsize;
385
386    char *rand_filename = "/dev/urandom";
387    FILE *f_in;
388    int size;
389
390    unsigned long int csum;
391    unsigned char checksum;
392
393    FILE *f_out;
394
395    // allocate memory for bin file
396    buffer = malloc(KERNEL_CODE_OFFSET + FLASH_SIZE);
397    if (!buffer) {
398        exitcode = 1;
399        printf("create_bin_file: can not allocate %i bytes\n", FLASH_SIZE);
400    } else {
401        // initialize with zero
402        memset(buffer, 0, KERNEL_CODE_OFFSET + FLASH_SIZE);
403    }
404
405    // add files
406    if (!exitcode) {
407        for (i = 1; i <= 3; i++) {
408            addsize = 0;
409            padsize = 0;
410
411            switch (i) {
412                case 1:
413                    mtd = &mtd_kernel;
414                    break;
415                case 2:
416                    mtd = &mtd_rootfs;
417                    addsize = mtd->filesize;
418                    padsize = ROOTFS_MIN_OFFSET - mtd_kernel.size - mtd->filesize;
419                    break;
420                case 3:
421                    mtd = &mtd_uboot;
422                    addsize = mtd->filesize;
423                    break;
424                default:
425                    mtd = NULL;
426                    exitcode = 1;
427                    printf("create_bin_file: unknown mtd %i\n", i);
428                    break;
429            }
430            if (!mtd) {
431                break;
432            }
433            if (!mtd->filename) {
434                continue;
435            }
436
437            lprintf(DEBUG, "adding mtd %s file %s\n", mtd->name, mtd->filename);
438
439            // adding file size
440            if (addsize) {
441                buffer[KERNEL_CODE_OFFSET + mtd->offset - 16] = 0x000000FFL & ( addsize >> 24 );
442                buffer[KERNEL_CODE_OFFSET + mtd->offset - 15] = 0x000000FFL & ( addsize >> 16 );
443                buffer[KERNEL_CODE_OFFSET + mtd->offset - 14] = 0x000000FFL & ( addsize >> 8 );
444                buffer[KERNEL_CODE_OFFSET + mtd->offset - 13] = 0x000000FFL & addsize;
445            }
446
447            // adding file content
448            f_in = fopen(mtd->filename, "rb");
449            if (!f_in) {
450                exitcode = errno;
451                printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
452            } else {
453                size = fread(&buffer[KERNEL_CODE_OFFSET + mtd->offset], mtd->filesize, 1, f_in);
454                if (size < 1) {
455                    if (ferror(f_in)) {
456                        exitcode = ferror(f_in);
457                        printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
458                    } else {
459                        exitcode = 1;
460                        printf("input file %s: smaller than before *doh*\n", mtd->filename);
461                    }
462                }
463                fclose(f_in);
464            }
465
466            // padding
467            if (padsize > 0) {
468                addsize = padsize & 0x0000FFFF; // start on next 64KB border
469                padsize -= addsize;
470            }
471            if (padsize > 0) {
472                printf("mtd %s input file %s is too small (0x%08lX), adding 0x%08X random bytes\n", mtd->name, mtd->filename, mtd->filesize, padsize);
473
474                addsize += KERNEL_CODE_OFFSET + mtd->offset + mtd->filesize; // get offset
475                lprintf(DEBUG, " padding offset 0x%08X length 0x%08X\n", addsize, padsize);
476
477                f_in = fopen(rand_filename, "rb");
478                if (!f_in) {
479                    exitcode = errno;
480                    printf("input file %s: %s\n", rand_filename, strerror(exitcode));
481                } else {
482                    size = fread(&buffer[addsize], padsize, 1, f_in);
483                    if (size < 1) {
484                        if (ferror(f_in)) {
485                            exitcode = ferror(f_in);
486                            printf("input file %s: %s\n", rand_filename, strerror(exitcode));
487                        } else {
488                            exitcode = 1;
489                            printf("input file %s: smaller than before *doh*\n", rand_filename);
490                        }
491                    }
492                }
493                fclose(f_in);
494            }
495        }
496    }
497
498    // add special contents
499    if (!exitcode) {
500        lprintf(DEBUG, "adding rootfs special data\n");
501        memcpy(&buffer[KERNEL_CODE_OFFSET + PRODUCT_ID_OFFSET], product_id, 2);
502        memcpy(&buffer[KERNEL_CODE_OFFSET + PROTOCOL_ID_OFFSET], protocol_id, 2);
503        memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET], fw_version, 2);
504        memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET + 2], rootfs_unknown, 2);
505        memcpy(&buffer[KERNEL_CODE_OFFSET + SIGN_OFFSET], sign, 8); // eRcOmM
506
507        lprintf(DEBUG, "adding u-boot special data\n");
508// memcpy(&buffer[KERNEL_CODE_OFFSET + SN_OFF], sn, 12); // ToDo: currently zero, find out what's this for?
509// memcpy(&buffer[KERNEL_CODE_OFFSET + PIN_OFF], pin, 8); // ToDo: currently zero, find out what's this for?
510// memcpy(&buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF], node, 25); // ToDo: currently zero, find out what's this for?
511        memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET], pid, 70); // sErCoMm
512        memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET + 57], fw_version, 2);
513
514        lprintf(DEBUG, "adding checksum byte\n");
515        csum = 0;
516        for (i = 0; i < KERNEL_CODE_OFFSET + FLASH_SIZE; i++) {
517            csum += buffer[i];
518        }
519        lprintf(DEBUG_LVL2, " checksum 0x%016lX (%li)\n", csum, csum);
520
521        buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25] = ~csum + 1;
522        lprintf(DEBUG, " byte 0x%02X\n", buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25]);
523    }
524
525    // write bin file
526    if (!exitcode) {
527        lprintf(DEBUG, "writing file %s\n", bin_filename);
528        f_out = fopen(bin_filename, "wb");
529        if (!f_out) {
530            exitcode = errno;
531            printf("output file %s: %s\n", bin_filename, strerror(exitcode));
532        } else {
533            size = fwrite(buffer, KERNEL_CODE_OFFSET + FLASH_SIZE, 1, f_out);
534            if (size < 1) {
535                if (ferror(f_out)) {
536                    exitcode = ferror(f_out);
537                    printf("output file %s: %s\n", bin_filename, strerror(exitcode));
538                } else {
539                    exitcode = 1;
540                    printf("output file %s: unspecified write error\n", bin_filename);
541                }
542            }
543            fclose(f_out);
544        }
545    }
546
547    return exitcode;
548}
549
550
551int create_zip_file(char *zip_filename, char *bin_filename) {
552    int exitcode = 0;
553
554    char *buffer;
555    size_t buffer_size;
556    int count;
557
558    buffer_size = 1000;
559    buffer = NULL;
560    do {
561        // allocate memory for command line
562        if (!buffer) {
563            buffer = malloc(buffer_size);
564        }
565        if (!buffer) {
566            exitcode = 1;
567            printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size);
568            break;
569        }
570
571        // if buffer was not completely filled, then line fit in completely
572        count = snprintf(buffer, buffer_size, "zip \"%s\" \"%s\"", zip_filename, bin_filename);
573        if ((count > -1) && (count < buffer_size)) {
574            break;
575        }
576
577        // otherwise try again with more space
578        if (count > -1) { // glibc 2.1
579            buffer_size = count + 1; // precisely what is needed
580        } else { // glibc 2.0
581            buffer_size *= 2; // twice the old size
582        }
583        free(buffer);
584        buffer = NULL;
585        lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
586    } while (1);
587
588    if (!exitcode) {
589        // zipping binfile
590        lprintf(DEBUG, "%s\n", buffer);
591        count = system(buffer);
592        if ((count < 0) || (WEXITSTATUS(count))) {
593            exitcode = 1;
594            printf("create_zip_file: can not execute %s bytes\n", buffer);
595        }
596    }
597
598    return exitcode;
599}
600
601
602int create_img_file(FILE *f_out, char *out_filename, char *zip_filename) {
603    int exitcode = 0;
604
605    md5_state_t state;
606    md5_byte_t digest[16];
607
608    int i;
609    int size;
610
611    FILE *f_in;
612    unsigned char buffer[1];
613
614    // copy firmware version
615    memcpy(&img_hdr[50], fw_version, 2);
616
617    // clear md5 checksum
618    memset(&img_hdr[480], 0, 16);
619
620    // prepare md5 checksum calculation
621    md5_init(&state);
622
623    // add img header
624    lprintf(DEBUG_LVL2, " adding img header\n");
625    for (i = 0; i < 512; i++) {
626        size = fputc(img_hdr[i], f_out);
627        if (size == EOF) {
628            exitcode = ferror(f_out);
629            printf("output file %s: %s\n", out_filename, strerror(exitcode));
630            break;
631        }
632        md5_append(&state, (const md5_byte_t *)&img_hdr[i], 1);
633    }
634
635    // adding zip file
636    if (!exitcode) {
637        lprintf(DEBUG_LVL2, " adding zip file\n");
638        f_in = fopen(zip_filename, "rb");
639        if (!f_in) {
640            exitcode = errno;
641            printf("input file %s: %s\n", zip_filename, strerror(exitcode));
642        } else {
643            while ((size = fgetc(f_in)) != EOF) {
644                buffer[0] = size;
645
646                size = fputc(buffer[0], f_out);
647                if (size == EOF) {
648                    exitcode = ferror(f_out);
649                    printf("output file %s: %s\n", out_filename, strerror(exitcode));
650                    break;
651                }
652                md5_append(&state, (const md5_byte_t *)buffer, 1);
653            }
654            if (ferror(f_in)) {
655                exitcode = ferror(f_in);
656                printf("input file %s: %s\n", zip_filename, strerror(exitcode));
657            }
658        }
659
660    }
661
662    // add end byte
663    if (!exitcode) {
664        lprintf(DEBUG_LVL2, " adding img eof byte\n");
665        size = fputc(img_eof[0], f_out);
666        if (size == EOF) {
667            exitcode = ferror(f_out);
668            printf("output file %s: %s\n", out_filename, strerror(exitcode));
669        }
670        md5_append(&state, (const md5_byte_t *)img_eof, 1);
671    }
672
673    // append salt to md5 checksum
674    md5_append(&state, (const md5_byte_t *)"A^gU*<>?RFY@#DR&Z", 17);
675
676    // finish md5 checksum calculation
677    md5_finish(&state, digest);
678
679    // write md5 checksum into img header
680    if (!exitcode) {
681        lprintf(DEBUG_LVL2, " writing md5 checksum into img header of file\n");
682
683        size = fseek(f_out, 480, SEEK_SET);
684        if (size == -1) {
685            exitcode = errno;
686            printf("output file %s: %s\n", out_filename, strerror(exitcode));
687        } else {
688            size = fwrite(digest, 16, 1, f_out);
689            if (size < 1) {
690                if (ferror(f_out)) {
691                    exitcode = ferror(f_out);
692                    printf("output file %s: %s\n", out_filename, strerror(exitcode));
693                } else {
694                    exitcode = 1;
695                    printf("output file %s: unspecified write error\n", out_filename);
696                }
697            }
698        }
699
700        fclose(f_in);
701    }
702
703    return exitcode;
704}
705
706
707int main(int argc, char *argv[]) {
708    int exitcode = 0;
709
710    int help;
711    int onlybin;
712    int havezip;
713    int ignoremagic;
714    char option;
715    char *par_filename = NULL;
716    char *img_filename = NULL;
717    char *base_filename = NULL;
718    char *bin_filename = NULL;
719    char *zip_filename = NULL;
720
721    FILE *f_par = NULL;
722    FILE *f_img = NULL;
723
724    int i;
725    mtd_info *mtd;
726    int mandatory;
727    int noupdate;
728    int sizecheck;
729    int magiccheck;
730    int magicerror;
731
732
733// display program header
734    printf(program_info, VERSION);
735
736
737// command line processing
738    // options
739    help = 0;
740    onlybin = 0;
741    havezip = 0;
742    ignoremagic = 0;
743    while ((option = getopt(argc, argv, "hbzif:v")) != -1) {
744        switch(option) {
745            case 'h':
746                help = 1;
747                break;
748            case 'b':
749                onlybin = 1;
750                break;
751            case 'z':
752                havezip = 1;
753                break;
754            case 'i':
755                ignoremagic = 1;
756                break;
757            case 'f':
758                sizecheck = sscanf(optarg, "%i", &i);
759                if (sizecheck != 1) {
760                    printf("Firmware version of -f option not a valid integer\n");
761                    exitcode = 1;
762                } else {
763                    fw_version[0] = 0x000000FF & ( i >> 8 );
764                    fw_version[1] = 0x000000FF & i;
765                }
766                break;
767            case 'v':
768                verbosity++;
769                break;
770            case ':': // option with missing operand
771                printf("Option -%c requires an operand\n", optopt);
772                exitcode = 1;
773                break;
774            case '?':
775                printf("Unrecognized option: -%c\n", optopt);
776                exitcode = 1;
777                break;
778        }
779    }
780
781    // files
782    for ( ; optind < argc; optind++) {
783        if (!par_filename) {
784            par_filename = argv[optind];
785
786            if (access(par_filename, R_OK)) {
787                if (havezip) {
788                    printf("No read access to zip file %s\n", par_filename);
789                } else {
790                    printf("No read access to parameter or zip file %s\n", par_filename);
791                }
792                exitcode = 1;
793            }
794
795            continue;
796        }
797
798        if ((!onlybin) && (!img_filename)) {
799            img_filename = argv[optind];
800
801            if (!access(img_filename, F_OK)) { // if file already exists then check write access
802                if (access(img_filename, W_OK)) {
803                    printf("No write access to image file %s\n", img_filename);
804                    exitcode = 1;
805                }
806            }
807
808            continue;
809        }
810
811        printf("Too many files stated\n");
812        exitcode = 1;
813        break;
814    }
815
816    // file name checks
817    if (!par_filename) {
818        if (havezip) {
819            printf("Zip file not stated\n");
820        } else {
821            printf("Parameter file not stated\n");
822        }
823        exitcode = 1;
824    } else {
825        base_filename = basename(par_filename);
826        if (!base_filename) {
827            if (havezip) {
828                printf("Zip file is a directory\n");
829            } else {
830                printf("Parameter file is a directory\n");
831            }
832            exitcode = 1;
833        }
834    }
835
836    if (!onlybin) {
837        if (!img_filename) {
838            printf("Image file not stated\n");
839            exitcode = 1;
840        } else {
841            base_filename = basename(img_filename);
842            if (!base_filename) {
843                printf("Image file is a directory\n");
844                exitcode = 1;
845            }
846        }
847    }
848
849    // check for mutually exclusive options
850    if ((onlybin) && (havezip)) {
851        printf("Option -b and -z are mutually exclusive\n");
852        exitcode = 1;
853    }
854
855    // react on option problems or help request, then exit
856    if ((exitcode) || (help)) {
857        if (help) {
858            printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
859        }
860        printf(" Usage:\n\
861  %s [-h] [-b] [-z] [-i] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
862  Options:\n\
863  -h - Show this help\n\
864  -b - Create only bin file, no img or zip file is created\n\
865  -z - Have zip file, the img file will be directly created from it\n\
866  -i - Ignore unknown magic numbers\n\
867  -f <version> - Wanted firmware version to use with -z\n\
868                   Default firmware version is 0x2020 = 2.00.20.\n\
869                   Note: version from parameter file will supersede this\n\
870  -v - Increase debug verbosity level\n\n\
871  Example:\n\
872  %s wrt350nv2.par wrt350nv2.img\n\n", argv[0], argv[0]);
873        return exitcode;
874    }
875
876    // handle special case when zipfile is stated
877    if (havezip) {
878        zip_filename = par_filename;
879        par_filename = NULL;
880    }
881
882    lprintf(DEBUG_LVL2, " Verbosity: %i\n", verbosity);
883    lprintf(DEBUG_LVL2, " Program: %s\n", argv[0]);
884
885    if (par_filename) {
886        lprintf(DEBUG, "Parameter file: %s\n", par_filename);
887    }
888    if (zip_filename) {
889        lprintf(DEBUG, "Zip file: %s\n", zip_filename);
890    }
891    if (img_filename) {
892        lprintf(DEBUG, "Image file: %s\n", img_filename);
893    }
894
895
896// open files from command line
897    // parameter/zip file
898    if (par_filename) {
899        f_par = fopen(par_filename, "rt");
900        if (!f_par) {
901            exitcode = errno;
902            printf("Input file %s: %s\n", par_filename, strerror(exitcode));
903        }
904    }
905
906    // image file
907    if (img_filename) {
908        f_img = fopen(img_filename, "wb");
909        if (!f_img) {
910            exitcode = errno;
911            printf("Output file %s: %s\n", img_filename, strerror(exitcode));
912        }
913    }
914
915    if (exitcode) {
916        return exitcode;
917    }
918
919
920// parameter file processing
921    if ((!exitcode) && (f_par)) {
922        lprintf(DEBUG, "parsing parameter file...\n");
923
924        exitcode = parse_par_file(f_par);
925
926        lprintf(DEBUG, "...done parsing file\n");
927    }
928    if (f_par) {
929        fclose(f_par);
930    }
931
932
933// check all input data
934    if ((!exitcode) && (par_filename)) {
935        lprintf(DEBUG, "checking mtd data...\n");
936
937        for (i = 1; i <= 3; i++) {
938            mandatory = 0;
939            noupdate = 0;
940            sizecheck = 0;
941            magiccheck = 0;
942
943            switch (i) {
944                case 1:
945                    mtd = &mtd_kernel;
946                    mandatory = 1;
947                    sizecheck = mtd_kernel.size - 16;
948                    magiccheck = 1;
949                    break;
950                case 2:
951                    mtd = &mtd_rootfs;
952                    mtd->offset = mtd_kernel.size;
953                    mtd->size = ROOTFS_END_OFFSET - mtd_kernel.size;
954                    mandatory = 1;
955                    sizecheck = PRODUCT_ID_OFFSET - mtd_kernel.size;
956                    magiccheck = 1;
957                    break;
958                case 3:
959                    mtd = &mtd_uboot;
960                    mtd->offset = BOOT_ADDR_BASE_OFF;
961                    noupdate = 1;
962                    sizecheck = SN_OFF - BOOT_ADDR_BASE_OFF;
963                    break;
964                default:
965                    mtd = NULL;
966                    exitcode = 1;
967                    printf("unknown mtd check %i\n", i);
968                    break;
969            }
970            if (!mtd) {
971                break;
972            }
973
974            lprintf(DEBUG_LVL2, " checking mtd %s\n", mtd->name);
975
976            // general checks
977            if ((mandatory) && (!mtd->filename)) {
978                exitcode = 1;
979                printf("mtd %s not specified correctly or at all in parameter file\n", mtd->name);
980            }
981
982            // no further checks if no file data present
983            if (!mtd->filename) {
984                continue;
985            }
986
987            // not updated by stock firmware
988            if (noupdate) {
989                printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd->name);
990            }
991
992            // general magic number check
993            magicerror = 0;
994            if (magiccheck) {
995                switch (i) {
996                    case 1: // kernel
997                        if (!(
998                               ((mtd->magic[0] == 0x27) && (mtd->magic[1] == 0x05)) // uImage
999                        )) {
1000                            magicerror = 1;
1001                        }
1002                        break;
1003                    case 2: // rootfs
1004                        if (!(
1005                               ((mtd->magic[0] == 0x68) && (mtd->magic[1] == 0x73)) // squashfs
1006                            || ((mtd->magic[0] == 0x85) && (mtd->magic[1] == 0x19)) // jffs
1007                        )) {
1008                            magicerror = 1;
1009                        }
1010                        break;
1011                    default:
1012                        magicerror = 1;
1013                        break;
1014                }
1015                if (magicerror) {
1016                    printf("mtd %s input file %s has unknown magic number (0x%02X%02X)", mtd->name, mtd->filename, mtd->magic[0], mtd->magic[1]);
1017                    if (ignoremagic) {
1018                        printf("...ignoring");
1019                    } else {
1020                        exitcode = 1;
1021                    }
1022                    printf("\n");
1023                }
1024            }
1025
1026            // mtd specific size check
1027            if (mtd == &mtd_kernel) {
1028                if (mtd->filesize < 0x00050000) {
1029                    exitcode = 1;
1030                    printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1031                }
1032            }
1033
1034            // general size check
1035            if (sizecheck) {
1036                if (sizecheck <= 0) {
1037                    exitcode = 1;
1038                    printf("mtd %s bad file size check (%i) due to input data\n", mtd->name, sizecheck);
1039                } else {
1040                    if (mtd->filesize > sizecheck) {
1041                        exitcode = 1;
1042                        printf("mtd %s input file %s too big (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1043                    }
1044                }
1045            }
1046        }
1047        lprintf(DEBUG, "...done checking mtd data\n");
1048    }
1049
1050
1051// bin creation in memory
1052    if ((!exitcode) && (par_filename)) {
1053        bin_filename = "wrt350n.bin";
1054
1055        lprintf(DEBUG, "creating bin file %s...\n", bin_filename);
1056
1057        exitcode = create_bin_file(bin_filename);
1058
1059        lprintf(DEBUG, "...done creating bin file\n");
1060    }
1061
1062// zip file creation
1063    if ((!exitcode) && (!onlybin) && (!zip_filename)) {
1064        zip_filename = "wrt350n.zip";
1065
1066        lprintf(DEBUG, "creating zip file %s...\n", zip_filename);
1067
1068        exitcode = create_zip_file(zip_filename, bin_filename);
1069
1070        lprintf(DEBUG, "...done creating zip file\n");
1071    }
1072
1073
1074// img file creation
1075    if ((!exitcode) && (f_img)) {
1076        lprintf(DEBUG, "creating img file...\n");
1077
1078        exitcode = create_img_file(f_img, img_filename, zip_filename);
1079
1080        lprintf(DEBUG, "...done creating img file\n");
1081    }
1082
1083// clean up
1084    if (f_img) {
1085        fclose(f_img);
1086    }
1087
1088// end program
1089    return exitcode;
1090}
1091

Archive Download this file



interactive