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"
22 #include <sys/types.h>
26 #include <arpa/inet.h>
31 #define BCMTAG_EXE_USE 1
33 #include "heap_reap.h"
35 #include "firmware_extractor.h"
38 static unsigned int MESSAGE_SIZE = 1024;
40 static const char *image_type[] = {"IMGDEF", "ROMD", "HPNA"};
42 static char *filename = NULL;
43 static unsigned int opt_decompress, opt_list, opt_quiet;
44 static const char *msg_err_memalloc = "unable to allocate memory for %s (%lu bytes)\n";
45 static const char *lzw_prefix_string = COMPRESSED_CONFIG_HEADER;
51 "Usage: %s [options] [filename]\n"
52 " -h show additional help\n"
53 " -d extracts payloads to same directory as 'filename'\n"
63 pr_error_exit(unsigned int usage, const char *error, ...)
66 char error_message[MESSAGE_SIZE + 1];
72 va_start(args, error);
73 (void) vsnprintf(error_message, MESSAGE_SIZE + 1, error, args);
75 fprintf(stderr, "Error: %s\n", error_message);
78 if (usage) pr_usage(usage);
80 heap_and_reap(NULL-1, 0, 0);
84 /* calculate standard CRC32
86 * @param data pointer to start of input data
87 * @param len length in bytes of data to be checksummed
88 * @param crc32 seed value (Broadcom use 0xffffffff)
90 unsigned int crc32(const unsigned char *data, ssize_t len, unsigned int crc)
92 for ( ; len > 0; --len ) {
93 crc = (crc >> 8) ^ Crc32_table[ (crc ^ *data++) & 0xff ];
98 int decompress(int fd) {
100 unsigned int crc_header, crc_payload;
101 unsigned char *buffer = NULL;
102 unsigned long header_len = sizeof(FILE_TAG);
104 unsigned int offset = 0;
105 unsigned int count = 0;
106 unsigned int next = 1;
107 void *payload = NULL;
108 void *plaintext = NULL;
109 char *filename_lzw = NULL;
111 SINT32 bytes, total_bytes = 0;
112 unsigned int f_lzw_len = strlen(filename) + 8; // filename.00.lzw
113 mode_t fd_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
116 unsigned long len = 0;
117 crc_header = crc_payload = 0xffffffff;
119 if (count) // switch struct type after initial file header
120 header_len = sizeof(IMAGE_TAG);
122 lseek(fd, offset, SEEK_SET);
124 printf("Header Offset: 0x%08x (%u)\n", offset, offset);
126 if ( (buffer = heap_and_reap(NULL, header_len, 1)) != NULL) {
128 if ( (qty = read(fd, buffer, header_len)) < header_len) {
130 fprintf(stderr, "warning: only able to read %ld of %ld bytes\n", qty, header_len);
134 PFILE_TAG pfile = (PFILE_TAG) buffer;
135 PIMAGE_TAG pimage = (PIMAGE_TAG) buffer;
138 next = *(unsigned char *)(pfile->imageNext);
139 len = atol(pfile->totalImageLen);
142 next = *(unsigned char *)(pimage->imageNext);
143 len = atol(pimage->imageLen);
144 offset += sizeof(IMAGE_TAG);
147 crc_header = crc32(buffer, header_len - (count ? CRC_LEN : TOKEN_LEN), crc_header);
149 // read payload and calculate CRC32
150 lseek(fd, offset, SEEK_SET);
151 if ( (payload = heap_and_reap(NULL, len, 1)) != NULL) {
153 if ( (qty = read(fd, payload, len)) < len) {
154 fprintf(stderr, "skipping CRC calculation: only able to read %ld of %ld bytes\n", qty, len);
156 crc_payload = crc32(payload, len, crc_payload);
158 if (opt_decompress) {
160 // allocate memory first time it is needed
162 if((filename_lzw = heap_and_reap(NULL, f_lzw_len, 1)) == NULL)
163 pr_error_exit(0, msg_err_memalloc, "filename_lzw", f_lzw_len);
165 snprintf(filename_lzw, f_lzw_len, "%s.%02x.bin", filename, count);
167 if ((fd_out = open(filename_lzw, O_WRONLY | O_CREAT, fd_mode)) == -1)
168 pr_error_exit(0, "unable to open '%s' for writing\n", filename_lzw);
172 if (strncmp((char *)payload, lzw_prefix_string, strlen(lzw_prefix_string)) != 0) {
174 printf("Found %s payload %u\n", (count == 0 ? "ROMD" : "unknown"), count);
176 write(fd_out, payload, len);
179 printf("Found LZW compressed payload %u\n", count);
180 unsigned int plaintext_len = len * 100;
182 if ((plaintext = heap_and_reap(NULL, plaintext_len, 1)) != NULL) {
183 LZWDecoderState *decoder_state = NULL;
185 cmsLzw_initDecoder(&decoder_state, payload + COMPRESSED_CONFIG_HEADER_LENGTH, len - COMPRESSED_CONFIG_HEADER_LENGTH);
188 bytes = cmsLzw_decode(decoder_state, plaintext, plaintext_len);
189 total_bytes += bytes;
190 if (opt_decompress) {
191 write(fd_out, plaintext, bytes);
193 } while (bytes == plaintext_len);
195 cmsLzw_cleanupDecoder(&decoder_state);
196 heap_and_reap(plaintext, 0, 0);
198 printf(" compressed: %lu decompressed: %u bytes\n", len - COMPRESSED_CONFIG_HEADER_LENGTH, total_bytes);
201 pr_error_exit(0, msg_err_memalloc, "decompressor", plaintext_len);
204 if (opt_decompress) {
205 printf(" written to %s\n", filename_lzw);
209 heap_and_reap(payload, 0, 0);
213 pr_error_exit(0, msg_err_memalloc, "payload", len);
217 printf("Image Offset: 0x%08x (%u)\n", offset, offset);
218 printf("%04lx Tag Version: %s\n"
219 "%04lx Signature 1: %s (Model: %s)\n"
220 "%04lx Signature 2: %s\n"
221 "%04lx Chip ID: %s\n"
222 "%04lx Board ID: %s\n"
223 "%04lx Big Endian: %s\n"
224 "%04lx Image Len: %s (0x%08lx)\n"
225 "%04lx External Version: %s\n"
226 "%04lx Internal Version: %s\n"
227 "%04lx Image Next: %u\n"
228 "%04lx Image Validation Token: 0x%08x\n"
229 "%04lx Tag Validation Token: 0x%08x\n"
230 " Calculated Image CRC32: 0x%08x\n"
231 " Calculated Tag CRC32: 0x%08x\n"
233 offsetof(struct _FILE_TAG, tagVersion), pfile->tagVersion,
234 offsetof(struct _FILE_TAG, signiture_1), pfile->signiture_1, pfile->signiture_1 + strlen(pfile->signiture_1) + 1,
235 offsetof(struct _FILE_TAG, signiture_2), pfile->signiture_2,
236 offsetof(struct _FILE_TAG, chipId), pfile->chipId,
237 offsetof(struct _FILE_TAG, boardId), pfile->boardId,
238 offsetof(struct _FILE_TAG, bigEndian), *pfile->bigEndian == '1' ? "Yes" : "No",
239 offsetof(struct _FILE_TAG, totalImageLen), pfile->totalImageLen, len,
240 offsetof(struct _FILE_TAG, externalversion), pfile->externalversion,
241 offsetof(struct _FILE_TAG, internalversion), pfile->internalversion,
242 offsetof(struct _FILE_TAG, imageNext), next,
243 offsetof(struct _FILE_TAG, imageValidationToken), ntohl( *((unsigned int *)(pfile->imageValidationToken)) ),
244 offsetof(struct _FILE_TAG, tagValidationToken), ntohl( *((unsigned int *)(pfile->tagValidationToken)) ),
249 printf("Image Offset: 0x%08x (%u)\n", offset, offset);
250 printf("%04lx Image Next: %u\n"
251 "%04lx Image Type: %s (%lu)\n"
252 "%04lx Image Signature: %u\n"
253 "%04lx Image Len: %s (0x%08lx)\n"
254 "%04lx Image Validation Token: 0x%08x\n"
255 "%04lx Tag Validation Token: 0x%08x\n"
256 " Calculated Image CRC32: 0x%08x\n"
257 " Calculated Tag CRC32: 0x%08x\n"
259 offsetof(struct _IMAGE_TAG, imageNext), next,
260 offsetof(struct _IMAGE_TAG, imageType), image_type[atol(pimage->imageType)], atol(pimage->imageType),
261 offsetof(struct _IMAGE_TAG, imageSignature), (unsigned int)*pimage->imageSignature,
262 offsetof(struct _IMAGE_TAG, imageLen), pimage->imageLen, len,
263 offsetof(struct _IMAGE_TAG, imageValidationToken), ntohl( *((unsigned int *)(pimage->imageValidationToken)) ),
264 offsetof(struct _IMAGE_TAG, tagValidationToken), ntohl( *((unsigned int *)(pimage->tagValidationToken)) ),
269 // next seek point will be end of current payload
272 heap_and_reap(buffer, 0, 0);
278 pr_error_exit(0, "unable to allocate memory (%ld bytes)\n", header_len);
286 main(int argc, char **argv)
291 opt_decompress = opt_list = opt_quiet = 0;
293 fprintf(stderr, "%s\nVersion: %0.2f\n%s\n", title, VERSION, copyright);
295 for (arg = 1; arg < (unsigned) argc; ++arg) {
297 size_t arg_len = strlen(p);
306 case 'd': // decompress
309 case 'l': // list payloads
314 pr_error_exit(0, "cannot read data from stdin; provide a filename");
318 else if (!filename) { // remaining non-option must be the filename
322 fprintf(stderr, "Can only process one file; ignoring '%s'\n", p);
325 if (opt_decompress == 1) {
328 else if (opt_list == 1) {
336 if ((fd = open(filename, fd_mode)) > 0) {
340 fprintf(stderr, "Unable to open for %s (%s)\n", "reading" , filename );
347 heap_and_reap(NULL-1, 0, 0);