1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * jcgimage - Create a JCG firmware image 4 * 5 * Copyright (C) 2015 Reinhard Max <reinhard@m4x.de> 6 * Copyright (C) 2019 Davide Fioravanti <pantanastyle@gmail.com> 7 */ 8 9 /* 10 * JCG firmware update images consist of a 512 byte header and a 11 * modified uImage (details below) as the payload. 12 * 13 * The payload is obfuscated by XORing it with a key that is generated 14 * from parts of the header. Fortunately only non-essential parts of 15 * the header are used for this and zeroing them results in a zero 16 * key, effectively disabling the obfuscation and allowing us to use 17 * clear text payloads. 18 * 19 * The mandatory parts of the header are: 20 * 21 * - A magic string of "YSZJ" at offset 0. 22 * - A value of 1 at offset 39 (header format version?) 23 * - A CRC32 checksum of the payload at offset 504. 24 * - A CRC32 checksum of the header at offset 508. 25 * 26 * An image constructed by these rules will be accepted by JCG's 27 * U-Boot in resuce mode via TFTP and the payload will be written to 28 * the flash starting at offset 0x00050000. 29 * 30 * JCG's U-Boot does check the content or size of the payload 31 * image. If it is too large, it wraps around and overwrites U-Boot, 32 * requiring JTAG to revive the board. To prevent such bricking from 33 * happening, this tool refuses to build such overlong images. 34 * 35 * Using -m is possible to set the maximum size of the payload. 36 * Otherwise the default MAXSIZE will be used. 37 * For an 8Mb flash, the corresponding maxsize is: 38 * 8 * 1024 * 1024 - 5 * 64 * 1024 = 8388608 - 327680 = 8060928 39 * 40 * Two more conditions have to be met for a JCG image to be accepted 41 * as a valid update by the web interface of the stock firware: 42 * 43 * - The bytes at offsets 109 and 111 in the header must be a binary 44 * representation of the first two components of the firmware 45 * version as displayed in the update web form, or it will be 46 * rejected as "incorrect product". 47 * 48 * - The payload must start with a valid uImage header whose data 49 * CRC checksum matches the whole rest of the update file rather 50 * than just the number of bytes specified in the size field of the 51 * header. 52 * 53 * This last condition is met by JCG's original firmware images, 54 * because they have both, kernel and rootfs inside the uImage and 55 * abuse the last four bytes of the name field to record the offset of 56 * the file system from the start of the uImage header. This tool 57 * produces such images when called with -k and -r, which are meant to 58 * repack the original firmware after modifying the file systen, 59 * e.g. to add debugging tools and enable shell access. 60 * 61 * In contrast, OpenWrt sysupgrade images consist of a uImage that 62 * only contains the kernel and has the rootfs appended to it. Hence, 63 * the CRC over kernel and file system does not match the one in the 64 * uImage header. Fixing this by adjusting the uImage header is not 65 * possible, because it makes the uImage unusable for booting. Instead 66 * we append four "patch" bytes to the end of the file system, that 67 * are calculated to force the checksum of kernel+fs to be the same as 68 * for the kernel alone. 69 * 70 */ 71 72 #include <zlib.h> 73 #include <stdio.h> 74 #include <string.h> 75 #include <sys/types.h> 76 #include <sys/stat.h> 77 #include <fcntl.h> 78 #include <unistd.h> 79 #include <libgen.h> 80 #include <stdlib.h> 81 #include <errno.h> 82 #include <err.h> 83 #include <time.h> 84 #include <sys/mman.h> 85 #include <arpa/inet.h> 86 #include <assert.h> 87 #include <inttypes.h> 88 89 /* 90 * JCG Firmware image header 91 */ 92 #define JH_MAGIC 0x59535a4a /* "YSZJ" */ 93 struct jcg_header { 94 uint32_t jh_magic; 95 uint8_t jh_version[32]; /* Firmware version string. 96 Fill with zeros to avoid encryption */ 97 uint32_t jh_type; /* must be 1 */ 98 uint8_t jh_info[64]; /* Firmware info string. Fill with 99 zeros to avoid encryption */ 100 uint32_t jh_time; /* Image creation time in seconds since 101 * the Epoch. Does not seem to be used 102 * by the stock firmware. */ 103 uint16_t jh_major; /* Major fimware version */ 104 uint16_t jh_minor; /* Minor fimrmware version */ 105 uint8_t jh_unknown[392]; /* Apparently unused and all zeros */ 106 uint32_t jh_dcrc; /* CRC checksum of the payload */ 107 uint32_t jh_hcrc; /* CRC checksum of the header */ 108 }; 109 110 /* 111 * JCG uses a modified uImage header that replaces the last four bytes 112 * of the image name with the length of the kernel in the image. 113 */ 114 #define IH_MAGIC 0x27051956 /* Image Magic Number */ 115 #define IH_NMLEN 28 /* Image Name Length */ 116 117 struct uimage_header { 118 uint32_t ih_magic; /* Image Header Magic Number */ 119 uint32_t ih_hcrc; /* Image Header CRC Checksum */ 120 uint32_t ih_time; /* Image Creation Timestamp */ 121 uint32_t ih_size; /* Image Data Size */ 122 uint32_t ih_load; /* Data Load Address */ 123 uint32_t ih_ep; /* Entry Point Address */ 124 uint32_t ih_dcrc; /* Image Data CRC Checksum */ 125 uint8_t ih_os; /* Operating System */ 126 uint8_t ih_arch; /* CPU architecture */ 127 uint8_t ih_type; /* Image Type */ 128 uint8_t ih_comp; /* Compression Type */ 129 uint8_t ih_name[IH_NMLEN];/* Image Name */ 130 uint32_t ih_fsoff; /* Offset of the file system 131 partition from the start of 132 the header */ 133 }; 134 135 /* 136 * Open the named file and return its size and file descriptor. 137 * Exit in case of errors. 138 */ 139 int 140 opensize(char *name, size_t *size) 141 { 142 struct stat s; 143 int fd = open(name, O_RDONLY); 144 if (fd < 0) 145 err(1, "cannot open \"%s\"", name); 146 147 if (fstat(fd, &s) == -1) 148 err(1, "cannot stat \"%s\"", name); 149 150 *size = s.st_size; 151 return fd; 152 } 153 154 static time_t source_date_epoch = -1; 155 static void set_source_date_epoch() { 156 char *env = getenv("SOURCE_DATE_EPOCH"); 157 char *endptr = env; 158 errno = 0; 159 if (env && *env) { 160 source_date_epoch = strtoull(env, &endptr, 10); 161 if (errno || (endptr && *endptr != '\0')) { 162 fprintf(stderr, "Invalid SOURCE_DATE_EPOCH"); 163 exit(1); 164 } 165 } 166 } 167 168 /* 169 * Write the JCG header 170 */ 171 void 172 mkjcgheader(struct jcg_header *h, size_t psize, char *version) 173 { 174 uLong crc; 175 uint16_t major = 0, minor = 0; 176 void *payload = (void *)h + sizeof(*h); 177 time_t t; 178 179 if (source_date_epoch != -1) 180 t = source_date_epoch; 181 else if ((time(&t) == (time_t)(-1))) 182 err(1, "time call failed"); 183 184 185 if (version != NULL) 186 if (sscanf(version, "%hu.%hu", &major, &minor) != 2) 187 err(1, "cannot parse version \"%s\"", version); 188 189 memset(h, 0, sizeof(*h)); 190 h->jh_magic = htonl(JH_MAGIC); 191 h->jh_type = htonl(1); 192 h->jh_time = htonl(t); 193 h->jh_major = htons(major); 194 h->jh_minor = htons(minor); 195 196 /* CRC over JCG payload (uImage) */ 197 crc = crc32(0L, Z_NULL, 0); 198 crc = crc32(crc, payload, psize); 199 h->jh_dcrc = htonl(crc); 200 201 /* CRC over JCG header */ 202 crc = crc32(0L, Z_NULL, 0); 203 crc = crc32(crc, (void *)h, sizeof(*h)); 204 h->jh_hcrc = htonl(crc); 205 } 206 207 /* 208 * Write the uImage header 209 */ 210 void 211 mkuheader(struct uimage_header *h, size_t ksize, size_t fsize) 212 { 213 uLong crc; 214 void *payload = (void *)h + sizeof(*h); 215 216 // printf("mkuheader: %p, %zd, %zd\n", h, ksize, fsize); 217 memset(h, 0, sizeof(*h)); 218 h->ih_magic = htonl(IH_MAGIC); 219 h->ih_time = htonl(time(NULL)); 220 h->ih_size = htonl(ksize + fsize); 221 h->ih_load = htonl(0x80000000); 222 h->ih_ep = htonl(0x80292000); 223 h->ih_os = 0x05; 224 h->ih_arch = 0x05; 225 h->ih_type = 0x02; 226 h->ih_comp = 0x03; 227 h->ih_fsoff = htonl(sizeof(*h) + ksize); 228 strcpy((char *)h->ih_name, "Linux Kernel Image"); 229 230 /* CRC over uImage payload (kernel and file system) */ 231 crc = crc32(0L, Z_NULL, 0); 232 crc = crc32(crc, payload, ntohl(h->ih_size)); 233 h->ih_dcrc = htonl(crc); 234 printf("CRC1: %08lx\n", crc); 235 236 /* CRC over uImage header */ 237 crc = crc32(0L, Z_NULL, 0); 238 crc = crc32(crc, (void *)h, sizeof(*h)); 239 h->ih_hcrc = htonl(crc); 240 printf("CRC2: %08lx\n", crc); 241 } 242 243 /* 244 * Calculate a "patch" value and write it into the last four bytes of 245 * buf, so that the CRC32 checksum of the whole buffer is dcrc. 246 * 247 * Based on: SAR-PR-2006-05: Reversing CRC – Theory and Practice. 248 * Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich. 249 * http://sar.informatik.hu-berlin.de/research/publications/#SAR-PR-2006-05 250 */ 251 void 252 craftcrc(uint32_t dcrc, uint8_t *buf, size_t len) 253 { 254 int i; 255 uint32_t a; 256 uint32_t patch = 0; 257 uint32_t crc = crc32(0L, Z_NULL, 0); 258 259 a = ~dcrc; 260 for (i = 0; i < 32; i++) { 261 if (patch & 1) 262 patch = (patch >> 1) ^ 0xedb88320L; 263 else 264 patch >>= 1; 265 266 if (a & 1) 267 patch ^= 0x5b358fd3L; 268 269 a >>= 1; 270 } 271 patch ^= ~crc32(crc, buf, len - 4); 272 for (i = 0; i < 4; i++) { 273 buf[len - 4 + i] = patch & 0xff; 274 patch >>= 8; 275 } 276 /* Verify that we actually get the desired result */ 277 crc = crc32(0L, Z_NULL, 0); 278 crc = crc32(crc, buf, len); 279 if (crc != dcrc) 280 errx(1, "CRC patching is broken: wanted %08x, but got %08x.", 281 dcrc, crc); 282 283 } 284 285 void 286 usage() { 287 fprintf(stderr, "Usage:\n" 288 "jcgimage -o outfile -u uImage [-m maxsize] [-v version]\n" 289 "jcgimage -o outfile -k kernel -f rootfs [-m maxsize] [-v version]\n"); 290 exit(1); 291 } 292 293 #define MODE_UNKNOWN 0 294 #define MODE_UIMAGE 1 295 #define MODE_KR 2 296 297 /* The output image must not be larger than 4MiB - 5*64kiB */ 298 #define MAXSIZE (size_t)(4 * 1024 * 1024 - 5 * 64 * 1024) 299 300 int 301 main(int argc, char **argv) 302 { 303 struct jcg_header *jh; 304 struct uimage_header *uh; 305 int c; 306 char *imagefile = NULL; 307 char *file1 = NULL; 308 char *file2 = NULL; 309 char *version = NULL; 310 size_t maxsize = MAXSIZE; 311 char *endptr; 312 int mode = MODE_UNKNOWN; 313 int fdo, fd1, fd2; 314 size_t size1, size2, sizeu, sizeo, off1, off2; 315 void *map; 316 317 /* Make sure the headers have the right size */ 318 assert(sizeof(struct jcg_header) == 512); 319 assert(sizeof(struct uimage_header) == 64); 320 set_source_date_epoch(); 321 322 while ((c = getopt(argc, argv, "o:k:f:u:v:m:h")) != -1) { 323 switch (c) { 324 case 'o': 325 imagefile = optarg; 326 break; 327 case 'k': 328 if (mode == MODE_UIMAGE) 329 errx(1,"-k cannot be combined with -u"); 330 331 mode = MODE_KR; 332 file1 = optarg; 333 break; 334 case 'f': 335 if (mode == MODE_UIMAGE) 336 errx(1,"-f cannot be combined with -u"); 337 338 mode = MODE_KR; 339 file2 = optarg; 340 break; 341 case 'u': 342 if (mode == MODE_KR) 343 errx(1,"-u cannot be combined with -k and -r"); 344 345 mode = MODE_UIMAGE; 346 file1 = optarg; 347 break; 348 case 'm': 349 if (optarg != NULL) 350 maxsize = strtoimax(optarg, &endptr, 10); 351 352 break; 353 case 'v': 354 version = optarg; 355 break; 356 case 'h': 357 default: 358 usage(); 359 } 360 } 361 if (optind != argc) 362 errx(1, "illegal arg \"%s\"", argv[optind]); 363 364 if (imagefile == NULL) 365 errx(1, "no output file specified"); 366 367 if (mode == MODE_UNKNOWN) 368 errx(1, "specify either -u or -k and -r"); 369 370 if (mode == MODE_KR) { 371 if (file1 == NULL || file2 == NULL) 372 errx(1, "need -k and -r"); 373 374 fd2 = opensize(file2, &size2); 375 } 376 fd1 = opensize(file1, &size1); 377 if (mode == MODE_UIMAGE) { 378 off1 = sizeof(*jh); 379 sizeu = size1 + 4; 380 sizeo = sizeof(*jh) + sizeu; 381 } else { 382 off1 = sizeof(*jh) + sizeof(*uh); 383 off2 = sizeof(*jh) + sizeof(*uh) + size1; 384 sizeu = sizeof(*uh) + size1 + size2; 385 sizeo = sizeof(*jh) + sizeu; 386 } 387 388 if (sizeo > maxsize) 389 errx(1, "payload too large: %zd > %zd\n", sizeo, maxsize); 390 391 392 fdo = open(imagefile, O_RDWR | O_CREAT | O_TRUNC, 00644); 393 if (fdo < 0) 394 err(1, "cannot open \"%s\"", imagefile); 395 396 397 if (ftruncate(fdo, sizeo) == -1) 398 err(1, "cannot grow \"%s\" to %zd bytes", imagefile, sizeo); 399 400 map = mmap(NULL, sizeo, PROT_READ|PROT_WRITE, MAP_SHARED, fdo, 0); 401 uh = map + sizeof(*jh); 402 if (map == MAP_FAILED) 403 err(1, "cannot mmap \"%s\"", imagefile); 404 405 406 if (read(fd1, map + off1, size1) != size1) 407 err(1, "cannot copy %s", file1); 408 409 410 if (mode == MODE_KR) { 411 if (read(fd2, map+off2, size2) != size2) 412 err(1, "cannot copy %s", file2); 413 414 mkuheader(uh, size1, size2); 415 } else if (mode == MODE_UIMAGE) 416 craftcrc(ntohl(uh->ih_dcrc), (void*)uh + sizeof(*uh), 417 sizeu - sizeof(*uh)); 418 419 mkjcgheader(map, sizeu, version); 420 munmap(map, sizeo); 421 close(fdo); 422 return 0; 423 } 424
This page was automatically generated by LXR 0.3.1. • OpenWrt