1 static const char *title =\
2 "Broadcom Consumer Router Firmware payload extractor"
4 static const float VERSION = 1.00f;
6 static const char *copyright = \
7 "Copyright 2015-2016 TJ <hacker@iam.tj>\n"
8 "Licensed on the terms of the GNU General Public License version 2 or later\n"
9 "Includes FFMPEG LZW library code with Broadcom CMS modifications\n"
12 static const char *help = \
13 "For routers using the Broadcom CFE ((Customer Premises Equipment) CPE Firmware Environment) and firmware update files.\n\n"
15 "Extracts ROMD and LZW payloads from firmware files.\n\n"
17 "The program will list and optionally extract/decompress the payloads:\n\n"
18 "$ ./fwex -d /tmp/zyxel/V100AAKL14C0.bin\n"
19 "Broadcom Consumer Router Firmware payload extractor\n"
21 "Copyright 2015-2016 TJ <hacker@iam.tj>\n"
22 "Licensed on the terms of the GNU General Public License version 2 or later\n"
23 "Includes FFMPEG LZW library code with Broadcom CMS modifications\n"
25 "Found ROMD payload 0\n"
26 " written to /tmp/zyxel/V100AAKL14C0.bin.00.bin\n"
27 "Image Offset: 0x00020000 (131072)\n"
28 "0000 Tag Version: 6\n"
29 "0004 Signature 1: MSTC_6006 (Model: 6006)\n"
30 "0018 Signature 2: ver. 2.0\n"
31 "0026 Chip ID: 63268\n"
32 "002c Board ID: 963168VX\n"
33 "003c Big Endian: Yes\n"
34 "003e Image Len: 25952256 (0x018c0000)\n"
35 "008e External Version: 1.00(AAKL.14)C0\n"
36 "00ae Internal Version: 1.00(AAKL.14)C0\n"
37 "00ce Image Next: 1\n"
38 "00d8 Image Validation Token: 0x1c633f00\n"
39 "00ec Tag Validation Token: 0xbdde4316\n"
40 " Calculated Image CRC32: 0x1c633f00\n"
41 " Calculated Tag CRC32: 0xbdde4316\n"
43 "Found LZW compressed payload 1\n"
44 " compressed: 23265 decompressed: 80030 bytes\n"
45 " written to /tmp/zyxel/V100AAKL14C0.bin.01.bin\n"
46 "Image Offset: 0x018e0020 (26083360)\n"
47 "0000 Image Next: 0\n"
48 "0001 Image Type: IMGDEF (0)\n"
49 "0003 Image Signature: 0\n"
50 "0005 Image Len: 23305 (0x00005b09)\n"
51 "0018 Image Validation Token: 0xd78f9ceb\n"
52 "001c Tag Validation Token: 0x8681f430\n"
53 " Calculated Image CRC32: 0xd78f9ceb\n"
54 " Calculated Tag CRC32: 0x8681f430\n"
62 #include <sys/types.h>
66 #include <arpa/inet.h>
71 #define BCMTAG_EXE_USE 1
73 #include "heap_reap.h"
75 #include "firmware_extractor.h"
78 static unsigned int MESSAGE_SIZE = 1024;
80 static const char *image_type[] = {"IMGDEF", "ROMD", "HPNA"};
82 static char *filename = NULL;
83 static unsigned int opt_decompress, opt_list, opt_quiet;
84 static const char *msg_err_memalloc = "unable to allocate memory for %s (%lu bytes)\n";
85 static const char *lzw_prefix_string = COMPRESSED_CONFIG_HEADER;
91 "Usage: %s [options] [filename]\n"
92 " -h show additional help\n"
93 " -d extracts payloads to same directory as 'filename'\n"
103 pr_error_exit(unsigned int usage, const char *error, ...)
106 char error_message[MESSAGE_SIZE + 1];
112 va_start(args, error);
113 (void) vsnprintf(error_message, MESSAGE_SIZE + 1, error, args);
115 fprintf(stderr, "Error: %s\n", error_message);
118 if (usage) pr_usage(usage);
120 heap_and_reap(NULL-1, 0, 0);
124 /* calculate standard CRC32
126 * @param data pointer to start of input data
127 * @param len length in bytes of data to be checksummed
128 * @param crc32 seed value (Broadcom use 0xffffffff)
130 unsigned int crc32(const unsigned char *data, ssize_t len, unsigned int crc)
132 for ( ; len > 0; --len ) {
133 crc = (crc >> 8) ^ Crc32_table[ (crc ^ *data++) & 0xff ];
138 int decompress(int fd) {
140 unsigned int crc_header, crc_payload;
141 unsigned char *buffer = NULL;
142 unsigned long header_len = sizeof(FILE_TAG);
144 unsigned int offset = 0;
145 unsigned int count = 0;
146 unsigned int next = 1;
147 void *payload = NULL;
148 void *plaintext = NULL;
149 char *filename_lzw = NULL;
151 SINT32 bytes, total_bytes = 0;
152 unsigned int f_lzw_len = strlen(filename) + 8; // filename.00.lzw
153 mode_t fd_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
156 unsigned long len = 0;
157 crc_header = crc_payload = 0xffffffff;
159 if (count) // switch struct type after initial file header
160 header_len = sizeof(IMAGE_TAG);
162 lseek(fd, offset, SEEK_SET);
164 printf("Header Offset: 0x%08x (%u)\n", offset, offset);
166 if ( (buffer = heap_and_reap(NULL, header_len, 1)) != NULL) {
168 if ( (qty = read(fd, buffer, header_len)) < header_len) {
170 fprintf(stderr, "warning: only able to read %ld of %ld bytes\n", qty, header_len);
174 PFILE_TAG pfile = (PFILE_TAG) buffer;
175 PIMAGE_TAG pimage = (PIMAGE_TAG) buffer;
178 next = *(unsigned char *)(pfile->imageNext);
179 len = atol(pfile->totalImageLen);
182 next = *(unsigned char *)(pimage->imageNext);
183 len = atol(pimage->imageLen);
184 offset += sizeof(IMAGE_TAG);
187 crc_header = crc32(buffer, header_len - (count ? CRC_LEN : TOKEN_LEN), crc_header);
189 // read payload and calculate CRC32
190 lseek(fd, offset, SEEK_SET);
191 if ( (payload = heap_and_reap(NULL, len, 1)) != NULL) {
193 if ( (qty = read(fd, payload, len)) < len) {
194 fprintf(stderr, "skipping CRC calculation: only able to read %ld of %ld bytes\n", qty, len);
196 crc_payload = crc32(payload, len, crc_payload);
198 if (opt_decompress) {
200 // allocate memory first time it is needed
202 if((filename_lzw = heap_and_reap(NULL, f_lzw_len, 1)) == NULL)
203 pr_error_exit(0, msg_err_memalloc, "filename_lzw", f_lzw_len);
205 snprintf(filename_lzw, f_lzw_len, "%s.%02x.bin", filename, count);
207 if ((fd_out = open(filename_lzw, O_WRONLY | O_CREAT, fd_mode)) == -1)
208 pr_error_exit(0, "unable to open '%s' for writing\n", filename_lzw);
212 if (strncmp((char *)payload, lzw_prefix_string, strlen(lzw_prefix_string)) != 0) {
214 printf("Found %s payload %u\n", (count == 0 ? "ROMD" : "unknown"), count);
216 write(fd_out, payload, len);
219 printf("Found LZW compressed payload %u\n", count);
220 unsigned int plaintext_len = len * 100;
222 if ((plaintext = heap_and_reap(NULL, plaintext_len, 1)) != NULL) {
223 LZWDecoderState *decoder_state = NULL;
225 cmsLzw_initDecoder(&decoder_state, payload + COMPRESSED_CONFIG_HEADER_LENGTH, len - COMPRESSED_CONFIG_HEADER_LENGTH);
228 bytes = cmsLzw_decode(decoder_state, plaintext, plaintext_len);
229 total_bytes += bytes;
230 if (opt_decompress) {
231 write(fd_out, plaintext, bytes);
233 } while (bytes == plaintext_len);
235 cmsLzw_cleanupDecoder(&decoder_state);
236 heap_and_reap(plaintext, 0, 0);
238 printf(" compressed: %lu decompressed: %u bytes\n", len - COMPRESSED_CONFIG_HEADER_LENGTH, total_bytes);
241 pr_error_exit(0, msg_err_memalloc, "decompressor", plaintext_len);
244 if (opt_decompress) {
245 printf(" written to %s\n", filename_lzw);
249 heap_and_reap(payload, 0, 0);
253 pr_error_exit(0, msg_err_memalloc, "payload", len);
257 printf("Image Offset: 0x%08x (%u)\n", offset, offset);
258 printf("%04lx Tag Version: %s\n"
259 "%04lx Signature 1: %s (Model: %s)\n"
260 "%04lx Signature 2: %s\n"
261 "%04lx Chip ID: %s\n"
262 "%04lx Board ID: %s\n"
263 "%04lx Big Endian: %s\n"
264 "%04lx Image Len: %s (0x%08lx)\n"
265 "%04lx External Version: %s\n"
266 "%04lx Internal Version: %s\n"
267 "%04lx Image Next: %u\n"
268 "%04lx Image Validation Token: 0x%08x\n"
269 "%04lx Tag Validation Token: 0x%08x\n"
270 " Calculated Image CRC32: 0x%08x\n"
271 " Calculated Tag CRC32: 0x%08x\n"
273 offsetof(struct _FILE_TAG, tagVersion), pfile->tagVersion,
274 offsetof(struct _FILE_TAG, signiture_1), pfile->signiture_1, pfile->signiture_1 + strlen(pfile->signiture_1) + 1,
275 offsetof(struct _FILE_TAG, signiture_2), pfile->signiture_2,
276 offsetof(struct _FILE_TAG, chipId), pfile->chipId,
277 offsetof(struct _FILE_TAG, boardId), pfile->boardId,
278 offsetof(struct _FILE_TAG, bigEndian), *pfile->bigEndian == '1' ? "Yes" : "No",
279 offsetof(struct _FILE_TAG, totalImageLen), pfile->totalImageLen, len,
280 offsetof(struct _FILE_TAG, externalversion), pfile->externalversion,
281 offsetof(struct _FILE_TAG, internalversion), pfile->internalversion,
282 offsetof(struct _FILE_TAG, imageNext), next,
283 offsetof(struct _FILE_TAG, imageValidationToken), ntohl( *((unsigned int *)(pfile->imageValidationToken)) ),
284 offsetof(struct _FILE_TAG, tagValidationToken), ntohl( *((unsigned int *)(pfile->tagValidationToken)) ),
289 printf("Image Offset: 0x%08x (%u)\n", offset, offset);
290 printf("%04lx Image Next: %u\n"
291 "%04lx Image Type: %s (%lu)\n"
292 "%04lx Image Signature: %u\n"
293 "%04lx Image Len: %s (0x%08lx)\n"
294 "%04lx Image Validation Token: 0x%08x\n"
295 "%04lx Tag Validation Token: 0x%08x\n"
296 " Calculated Image CRC32: 0x%08x\n"
297 " Calculated Tag CRC32: 0x%08x\n"
299 offsetof(struct _IMAGE_TAG, imageNext), next,
300 offsetof(struct _IMAGE_TAG, imageType), image_type[atol(pimage->imageType)], atol(pimage->imageType),
301 offsetof(struct _IMAGE_TAG, imageSignature), (unsigned int)*pimage->imageSignature,
302 offsetof(struct _IMAGE_TAG, imageLen), pimage->imageLen, len,
303 offsetof(struct _IMAGE_TAG, imageValidationToken), ntohl( *((unsigned int *)(pimage->imageValidationToken)) ),
304 offsetof(struct _IMAGE_TAG, tagValidationToken), ntohl( *((unsigned int *)(pimage->tagValidationToken)) ),
309 // next seek point will be end of current payload
312 heap_and_reap(buffer, 0, 0);
318 pr_error_exit(0, "unable to allocate memory (%ld bytes)\n", header_len);
326 main(int argc, char **argv)
331 opt_decompress = opt_list = opt_quiet = 0;
333 fprintf(stderr, "%s\nVersion: %0.2f\n%s\n", title, VERSION, copyright);
335 for (arg = 1; arg < (unsigned) argc; ++arg) {
337 size_t arg_len = strlen(p);
346 case 'd': // decompress
349 case 'l': // list payloads
354 pr_error_exit(0, "cannot read data from stdin; provide a filename");
358 else if (!filename) { // remaining non-option must be the filename
362 fprintf(stderr, "Can only process one file; ignoring '%s'\n", p);
365 if (opt_decompress == 1) {
368 else if (opt_list == 1) {
376 if ((fd = open(filename, fd_mode)) > 0) {
380 fprintf(stderr, "Unable to open for %s (%s)\n", "reading" , filename );
387 heap_and_reap(NULL-1, 0, 0);