3 WRT350Nv2-Builder 2.1 (previously called buildimg)
4 Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5 Copyright (C) 2009-2010 Matthias Buecher (http://www.maddes.net/)
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.
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.
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
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.
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.
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
36 1 wrt350nv2.par parameter file describing the image layout
37 2 wrt350nv2.img output file for linksys style image
39 A u-boot image inside the bin file is not necessary.
40 The version is not important.
41 The name of the bin file is not important, but still "wrt350n.bin" is used to
42 keep as close as possible to the stock firmware.
44 Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
45 of the mtd are abused to define the length of the next mtd content (4 bytes for
48 At the end of "rootfs" additional 16 bytes are abused for some data and a
49 highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
51 At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
52 highly important sErCoMm identifier.
55 This program uses a special GNU scanf modifier to allocate
56 sufficient memory for a strings with unknown length.
57 See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
60 To extract everything from a Linksys style firmware image see
61 https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
66 // * Has NODE to be added to bin file *after* creating checksum byte?
69 #define _GNU_SOURCE // for GNU's basename()
71 #include <errno.h> // errno
73 #include <stdio.h> // fopen(), fread(), fclose(), etc.
74 #include <stdlib.h> // system(), etc.
75 #include <string.h> // basename(), strerror(), strdup(), etc.
76 #include <unistd.h> // optopt(), access(), etc.
78 #include <sys/wait.h> // WEXITSTATUS, etc.
81 #include "md5.h" // MD5 routines
82 #include "upgrade.h" // Linksys definitions from firmware 2.0.19
87 char program_info[] = "WRT350Nv2-Builder v%s by Dirk Teurlings <info@upexia.nl> and Matthias Buecher (http://www.maddes.net/)\n";
101 unsigned char magic[2];
104 mtd_info mtd_kernel = { "kernel", 0, 0, NULL, 0L, { 0, 0 } };
105 mtd_info mtd_rootfs = { "rootfs", 0, 0, NULL, 0L, { 0, 0 } };
106 mtd_info mtd_uboot = { "u-boot", 0, 0, NULL, 0L, { 0, 0 } };
108 #define ROOTFS_END_OFFSET 0x00760000
109 #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
110 // 2.0.17: filled up to 0x00640000, 2.0.19: filled up to 0x0670000
112 // rootfs statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x0075FFE0 -n 16 "wrt350n.bin" ; echo -en "\n"
113 unsigned char product_id[] = { 0x00, 0x03 }; // seems to be a fixed value
114 unsigned char protocol_id[] = { 0x00, 0x00 }; // seems to be a fixed value
115 unsigned char fw_version[] = { 0x20, 0x19 };
116 unsigned char rootfs_unknown[] = { 0x90, 0xF7 }; // seems to be a fixed value
117 unsigned char sign[] = { 0x65, 0x52, 0x63, 0x4F, 0x6D, 0x4D, 0x00, 0x00 }; // eRcOmM
119 // u-boot statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x007FFF80 -n 128 "wrt350n.bin" ; echo -en "\n"
120 //unsigned char sn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (12) seems to be an unused value
121 //unsigned char pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (8) seems to be an unused value
122 //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
123 // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
124 //unsigned char checksum[] = { 0xE9 }; // (1) is calculated, does it belong to node?
125 unsigned 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
126 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, // protocol id?
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // protocol id?
129 0x12, 0x34, // firmware version, same as in rootfs
130 0x00, 0x00, 0x00, 0x04,
131 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D }; // sErCoMm
133 // img statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0 -n 512 "WRT350N-EU-ETSI-2.00.19.img" ; echo -en "\n"
134 unsigned char img_hdr[] = { 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
138 0x12, 0x34, // firmware version, same as in rootfs
139 0x00, 0x00, 0x00, 0x04, 0x61, 0x44, 0x6D, 0x42, 0x6C, 0x4B, 0x3D, 0x00,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // md5 checksum
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
169 unsigned char img_eof[] = { 0xFF };
172 void lprintf(int outputlevel, char *fmt, ...) {
174 if (outputlevel <= verbosity) {
182 int parse_par_file(FILE *f_par) {
204 while (!feof(f_par)) {
205 // read next line into memory
207 // allocate memory for input line
209 buffer = malloc(buffer_size);
213 printf("parse_par_file: can not allocate %i bytes\n", (int) buffer_size);
217 line = fgets(buffer, buffer_size, f_par);
219 exitcode = ferror(f_par);
221 printf("parse_par_file: %s\n", strerror(exitcode));
226 // if buffer was not completely filled, then assume that line is complete
227 count = strlen(buffer) + 1;
228 if (count-- < buffer_size) {
234 // reset file position to line start
235 value = fseek(f_par, -count, SEEK_CUR);
238 printf("parse_par_file: %s\n", strerror(exitcode));
242 // double buffer size
246 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
248 if ((!line) || (exitcode)) {
252 lineno++; // increase line number
254 lprintf(DEBUG_LVL2, " line %i (%i) %s", lineno, count, line);
259 // split line if starting with a colon
262 count = sscanf(line, ":%255s %i %255s", string1, &value, string2);
264 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
266 // populate mtd_info if supported mtd names
267 if (!strcmp(string1, mtd_kernel.name)) {
269 } else if (!strcmp(string1, mtd_rootfs.name)) {
271 } else if (!strcmp(string1, mtd_uboot.name)) {
276 printf("unknown mtd %s in line %i\n", string1, lineno);
277 } else if (mtd->filename) {
279 printf("mtd %s in line %i multiple definitions\n", string1, lineno);
282 mtd->filename = strdup(string2);
285 f_in = fopen(mtd->filename, "rb");
288 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
290 value = fread(&mtd->magic, 1, 2, f_in);
293 f_exitcode = ferror(f_in);
294 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
297 printf("input file %s: smaller than two bytes, no magic code\n", mtd->filename);
301 value = fseek(f_in, 0, SEEK_END);
304 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
306 mtd->filesize = ftell(f_in);
307 if (mtd->filesize == -1) {
309 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
316 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);
320 case '#': // integer values
321 count = sscanf(line, "#%255s %i", string1, &value);
323 printf("line %i does not meet defined format (:<variable name> <integer>\n", lineno);
325 if (!strcmp(string1, "version")) {
327 fw_version[0] = 0x000000FF & ( value >> 8 );
328 fw_version[1] = 0x000000FF & value;
330 printf("unknown integer variable %s in line %i\n", string1, lineno);
333 lprintf(DEBUG, "integer variable %s in line %i: 0x%08X\n", string1, lineno, value);
337 count = sscanf(line, "$%255s %255s", string1, string2);
339 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
342 if (!strcmp(string1, "something")) {
343 something = strdup(string2);
346 printf("unknown string variable %s in line %i\n", string1, lineno);
348 lprintf(DEBUG, "string variable %s in line %i: %s\n", string1, lineno, string2);
358 exitcode = f_exitcode;
365 int create_bin_file(char *bin_filename) {
368 unsigned char *buffer;
375 char *rand_filename = "/dev/urandom";
379 unsigned long int csum;
380 unsigned char checksum;
384 // allocate memory for bin file
385 buffer = malloc(KERNEL_CODE_OFFSET + FLASH_SIZE);
388 printf("create_bin_file: can not allocate %i bytes\n", FLASH_SIZE);
390 // initialize with zero
391 memset(buffer, 0, KERNEL_CODE_OFFSET + FLASH_SIZE);
396 for (i = 1; i <= 3; i++) {
406 addsize = mtd->filesize;
407 padsize = ROOTFS_MIN_OFFSET - mtd_kernel.size - mtd->filesize;
411 addsize = mtd->filesize;
416 printf("create_bin_file: unknown mtd %i\n", i);
422 if (!mtd->filename) {
426 lprintf(DEBUG, "adding mtd %s file %s\n", mtd->name, mtd->filename);
430 buffer[KERNEL_CODE_OFFSET + mtd->offset - 16] = 0x000000FFL & ( addsize >> 24 );
431 buffer[KERNEL_CODE_OFFSET + mtd->offset - 15] = 0x000000FFL & ( addsize >> 16 );
432 buffer[KERNEL_CODE_OFFSET + mtd->offset - 14] = 0x000000FFL & ( addsize >> 8 );
433 buffer[KERNEL_CODE_OFFSET + mtd->offset - 13] = 0x000000FFL & addsize;
436 // adding file content
437 f_in = fopen(mtd->filename, "rb");
440 printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
442 size = fread(&buffer[KERNEL_CODE_OFFSET + mtd->offset], mtd->filesize, 1, f_in);
445 exitcode = ferror(f_in);
446 printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
449 printf("input file %s: smaller than before *doh*\n", mtd->filename);
457 printf("mtd %s input file %s is too small (0x%08lX), adding 0x%08X random bytes\n", mtd->name, mtd->filename, mtd->filesize, padsize);
459 addsize = padsize & 0x0000FFFF; // start on next 64KB border
461 addsize += KERNEL_CODE_OFFSET + mtd->offset + mtd->filesize; // get offset
462 lprintf(DEBUG, " padding offset 0x%08X length 0x%08X\n", addsize, padsize);
464 f_in = fopen(rand_filename, "rb");
467 printf("input file %s: %s\n", rand_filename, strerror(exitcode));
469 size = fread(&buffer[addsize], padsize, 1, f_in);
472 exitcode = ferror(f_in);
473 printf("input file %s: %s\n", rand_filename, strerror(exitcode));
476 printf("input file %s: smaller than before *doh*\n", rand_filename);
485 // add special contents
487 lprintf(DEBUG, "adding rootfs special data\n");
488 memcpy(&buffer[KERNEL_CODE_OFFSET + PRODUCT_ID_OFFSET], product_id, 2);
489 memcpy(&buffer[KERNEL_CODE_OFFSET + PROTOCOL_ID_OFFSET], protocol_id, 2);
490 memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET], fw_version, 2);
491 memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET + 2], rootfs_unknown, 2);
492 memcpy(&buffer[KERNEL_CODE_OFFSET + SIGN_OFFSET], sign, 8); // eRcOmM
494 lprintf(DEBUG, "adding u-boot special data 1/2\n"); // ToDo: or after creating the checksum byte?
495 // memcpy(&buffer[KERNEL_CODE_OFFSET + SN_OFF], sn, 12); // ToDo: find out what's this for?
496 // memcpy(&buffer[KERNEL_CODE_OFFSET + PIN_OFF], pin, 8); // ToDo: find out what's this for?
497 // memcpy(&buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF], node, 25); // ToDo: find out what's this for?
499 lprintf(DEBUG, "adding checksum byte\n");
501 for (i = 0; i < KERNEL_CODE_OFFSET + FLASH_SIZE; i++) {
504 lprintf(DEBUG_LVL2, " checksum 0x%016lX (%li)\n", csum, csum);
506 buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25] = ~(csum+108)+1;
507 lprintf(DEBUG, " byte 0x%02X\n", buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25]);
509 lprintf(DEBUG, "adding u-boot special data 2/2\n");
510 memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET], pid, 70); // sErCoMm
511 memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET + 57], fw_version, 2);
516 lprintf(DEBUG, "writing file %s\n", bin_filename);
517 f_out = fopen(bin_filename, "wb");
520 printf("output file %s: %s\n", bin_filename, strerror(exitcode));
522 size = fwrite(buffer, KERNEL_CODE_OFFSET + FLASH_SIZE, 1, f_out);
525 exitcode = ferror(f_out);
526 printf("output file %s: %s\n", bin_filename, strerror(exitcode));
529 printf("output file %s: unspecified write error\n", bin_filename);
540 int create_zip_file(char *zip_filename, char *bin_filename) {
550 // allocate memory for command line
552 buffer = malloc(buffer_size);
556 printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size);
560 // if buffer was not completely filled, then line fit in completely
561 count = snprintf(buffer, buffer_size, "zip \"%s\" \"%s\"", zip_filename, bin_filename);
562 if ((count > -1) && (count < buffer_size)) {
566 // otherwise try again with more space
567 if (count > -1) { // glibc 2.1
568 buffer_size = count + 1; // precisely what is needed
569 } else { // glibc 2.0
570 buffer_size *= 2; // twice the old size
574 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
579 lprintf(DEBUG, "%s\n", buffer);
580 count = system(buffer);
581 if ((count < 0) || (WEXITSTATUS(count))) {
583 printf("create_zip_file: can not execute %s bytes\n", buffer);
591 int create_img_file(FILE *f_out, char *out_filename, char *zip_filename) {
595 md5_byte_t digest[16];
601 unsigned char buffer[1];
603 // copy firmware version
604 memcpy(&img_hdr[50], fw_version, 2);
606 // clear md5 checksum
607 memset(&img_hdr[480], 0, 16);
609 // prepare md5 checksum calculation
613 lprintf(DEBUG_LVL2, " adding img header\n");
614 for (i = 0; i < 512; i++) {
615 size = fputc(img_hdr[i], f_out);
617 exitcode = ferror(f_out);
618 printf("output file %s: %s\n", out_filename, strerror(exitcode));
621 md5_append(&state, (const md5_byte_t *)&img_hdr[i], 1);
626 lprintf(DEBUG_LVL2, " adding zip file\n");
627 f_in = fopen(zip_filename, "rb");
630 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
632 while ((size = fgetc(f_in)) != EOF) {
635 size = fputc(buffer[0], f_out);
637 exitcode = ferror(f_out);
638 printf("output file %s: %s\n", out_filename, strerror(exitcode));
641 md5_append(&state, (const md5_byte_t *)buffer, 1);
644 exitcode = ferror(f_in);
645 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
653 lprintf(DEBUG_LVL2, " adding img eof byte\n");
654 size = fputc(img_eof[0], f_out);
656 exitcode = ferror(f_out);
657 printf("output file %s: %s\n", out_filename, strerror(exitcode));
659 md5_append(&state, (const md5_byte_t *)img_eof, 1);
662 // append salt to md5 checksum
663 md5_append(&state, (const md5_byte_t *)"A^gU*<>?RFY@#DR&Z", 17);
665 // finish md5 checksum calculation
666 md5_finish(&state, digest);
668 // write md5 checksum into img header
670 lprintf(DEBUG_LVL2, " writing md5 checksum into img header of file\n");
672 size = fseek(f_out, 480, SEEK_SET);
675 printf("output file %s: %s\n", out_filename, strerror(exitcode));
677 size = fwrite(digest, 16, 1, f_out);
680 exitcode = ferror(f_out);
681 printf("output file %s: %s\n", out_filename, strerror(exitcode));
684 printf("output file %s: unspecified write error\n", out_filename);
696 int main(int argc, char *argv[]) {
703 char *par_filename = NULL;
704 char *img_filename = NULL;
705 char *base_filename = NULL;
706 char *bin_filename = NULL;
707 char *zip_filename = NULL;
717 unsigned char magic[2];
720 // display program header
721 printf(program_info, VERSION);
724 // command line processing
729 while ((option = getopt(argc, argv, ":hbzf:v")) != -1) {
741 sizecheck = sscanf(optarg, "%i", &i);
742 if (sizecheck != 1) {
743 printf("Firmware version of -f option not a valid integer\n");
746 fw_version[0] = 0x000000FF & ( i >> 8 );
747 fw_version[1] = 0x000000FF & i;
753 case ':': // option with missing operand
754 printf("Option -%c requires an operand\n", optopt);
758 printf("Unrecognized option: -%c\n", optopt);
765 for ( ; optind < argc; optind++) {
767 par_filename = argv[optind];
769 if (access(par_filename, R_OK)) {
771 printf("No read access to zip file %s\n", par_filename);
773 printf("No read access to parameter or zip file %s\n", par_filename);
781 if ((!onlybin) && (!img_filename)) {
782 img_filename = argv[optind];
784 if (!access(img_filename, F_OK)) { // if file already exists then check write access
785 if (access(img_filename, W_OK)) {
786 printf("No write access to image file %s\n", img_filename);
794 printf("Too many files stated\n");
802 printf("Zip file not stated\n");
804 printf("Parameter file not stated\n");
808 base_filename = basename(par_filename);
809 if (!base_filename) {
811 printf("Zip file is a directory\n");
813 printf("Parameter file is a directory\n");
821 printf("Image file not stated\n");
824 base_filename = basename(img_filename);
825 if (!base_filename) {
826 printf("Image file is a directory\n");
832 // check for mutually exclusive options
833 if ((onlybin) && (havezip)) {
834 printf("Option -b and -z are mutually exclusive\n");
838 // react on option problems or help request, then exit
839 if ((exitcode) || (help)) {
841 printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
844 %s [-h] [-b] [-z] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
846 -h - Show this help\n\
847 -b - Create only bin file, no img or zip file is created\n\
848 -z - Have zip file, the img file will be directly created from it\n\
849 -f <version> - Wanted firmware version to use with -z\n\
850 Default firmware version is 0x2019 = 2.00.19.\n\
851 Note: version from parameter file will supersede this\n\
852 -v - Increase debug verbosity level\n\n\
854 %s wrt350nv2.par wrt350nv2.img\n\n", argv[0], argv[0]);
858 // handle special case when zipfile is stated
860 zip_filename = par_filename;
864 lprintf(DEBUG_LVL2, " Verbosity: %i\n", verbosity);
865 lprintf(DEBUG_LVL2, " Program: %s\n", argv[0]);
868 lprintf(DEBUG, "Parameter file: %s\n", par_filename);
871 lprintf(DEBUG, "Zip file: %s\n", zip_filename);
874 lprintf(DEBUG, "Image file: %s\n", img_filename);
878 // open files from command line
879 // parameter/zip file
881 f_par = fopen(par_filename, "rt");
884 printf("Input file %s: %s\n", par_filename, strerror(exitcode));
890 f_img = fopen(img_filename, "wb");
893 printf("Output file %s: %s\n", img_filename, strerror(exitcode));
902 // parameter file processing
903 if ((!exitcode) && (f_par)) {
904 lprintf(DEBUG, "parsing parameter file...\n");
906 exitcode = parse_par_file(f_par);
908 lprintf(DEBUG, "...done parsing file\n");
915 // check all input data
916 if ((!exitcode) && (par_filename)) {
917 lprintf(DEBUG, "checking mtd data...\n");
919 for (i = 1; i <= 3; i++) {
930 sizecheck = mtd_kernel.size - 16;
936 mtd->offset = mtd_kernel.size;
937 mtd->size = ROOTFS_END_OFFSET - mtd_kernel.size;
939 sizecheck = PRODUCT_ID_OFFSET - mtd_kernel.size;
945 mtd->offset = BOOT_ADDR_BASE_OFF;
947 sizecheck = SN_OFF - BOOT_ADDR_BASE_OFF;
952 printf("unknown mtd check %i\n", i);
959 lprintf(DEBUG_LVL2, " checking mtd %s\n", mtd->name);
962 if ((mandatory) && (!mtd->filename)) {
964 printf("mtd %s not specified correctly or at all in parameter file\n", mtd->name);
967 // end checks if no file data present
968 if (!mtd->filename) {
972 // not updated by stock firmware
974 printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd->name);
977 // general magic number check
979 if ((mtd->magic[0] != magic[0]) || (mtd->magic[1] != magic[1])) {
981 printf("mtd %s input file %s has wrong magic number (0x%02X%02X)\n", mtd->name, mtd->filename, mtd->magic[0], mtd->magic[1]);
985 // mtd specific size check
986 if (mtd == &mtd_kernel) {
987 if (mtd->filesize < 0x00050000) {
989 printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
993 // general size check
995 if (sizecheck <= 0) {
997 printf("mtd %s bad file size check (%i) due to input data\n", mtd->name, sizecheck);
999 if (mtd->filesize > sizecheck) {
1001 printf("mtd %s input file %s too big (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1006 lprintf(DEBUG, "...done checking mtd data\n");
1010 // bin creation in memory
1011 if ((!exitcode) && (par_filename)) {
1012 bin_filename = "wrt350n.bin";
1014 lprintf(DEBUG, "creating bin file %s...\n", bin_filename);
1016 exitcode = create_bin_file(bin_filename);
1018 lprintf(DEBUG, "...done creating bin file\n");
1021 // zip file creation
1022 if ((!exitcode) && (!onlybin) && (!zip_filename)) {
1023 zip_filename = "wrt350n.zip";
1025 lprintf(DEBUG, "creating zip file %s...\n", zip_filename);
1027 exitcode = create_zip_file(zip_filename, bin_filename);
1029 lprintf(DEBUG, "...done creating zip file\n");
1033 // img file creation
1034 if ((!exitcode) && (f_img)) {
1035 lprintf(DEBUG, "creating img file...\n");
1037 exitcode = create_img_file(f_img, img_filename, zip_filename);
1039 lprintf(DEBUG, "...done creating img file\n");