• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/sks7300-img.c

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  *  Copyright (C) 2025-2026 Andreas Boehler <dev@aboehler.at>
  4  *  Based on reverse-engineering by Jan Hoffmann (obfuscation)
  5  *
  6  * Create images for the XikeStor SKS7300 series
  7  */
  8 
  9 
 10 #include <stdio.h>
 11 #include <stdlib.h>
 12 #include <stdint.h>
 13 #include <string.h>
 14 #include <byteswap.h>
 15 #include <unistd.h>
 16 #include <libgen.h>
 17 #include <getopt.h>
 18 #include <sys/stat.h>
 19 #include <fcntl.h>
 20 #include <sys/mman.h>
 21 #include <zlib.h>
 22 
 23 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
 24 #  define HOST_TO_LE16(x)       (x)
 25 #  define HOST_TO_LE32(x)       (x)
 26 #  define LE16_TO_HOST(x)       (x)
 27 #  define LE32_TO_HOST(x)       (x)
 28 #  define HOST_TO_BE16(x)       bswap_16(x)
 29 #  define HOST_TO_BE32(x)       bswap_32(x)
 30 #  define BE16_TO_HOST(x)       bswap_16(x)
 31 #  define BE32_TO_HOST(x)       bswap_32(x)
 32 #else
 33 #  define HOST_TO_BE16(x)       (x)
 34 #  define HOST_TO_BE32(x)       (x)
 35 #  define BE16_TO_HOST(x)       (x)
 36 #  define BE32_TO_HOST(x)       (x)
 37 #  define HOST_TO_LE16(x)       bswap_16(x)
 38 #  define HOST_TO_LE32(x)       bswap_32(x)
 39 #  define LE16_TO_HOST(x)       bswap_16(x)
 40 #  define LE32_TO_HOST(x)       bswap_32(x)
 41 #endif
 42 
 43 #define ALIGN(x,y)      (((x)+((y)-1)) & ~((y)-1))
 44 
 45 /*
 46  * Message macros
 47  */
 48 #define ERR(fmt, ...) do { \
 49         fflush(0); \
 50         fprintf(stderr, "[%s] *** error: " fmt "\n", \
 51                         progname, ## __VA_ARGS__ ); \
 52 } while (0)
 53 
 54 
 55 #define MAX_ARG_COUNT   32
 56 #define MAX_ARG_LEN     1024
 57 
 58 struct sks7300_hdr {
 59         uint32_t image_magic;     // Image Magic 0xfe071301
 60         uint32_t hdr_crc;         // Header CRC
 61         uint32_t image_size;      // Image Size (Bytes)
 62         uint32_t timestamp;       // Image Timestamp (Unix Timestamp)
 63         uint32_t image00_offset;  // Image 00 Offset
 64         uint32_t image00_size;    // Image 00 size
 65         uint8_t image00_type;     // Image 00 Type; 0x52 = Kernel Image, 0x5B = Unknown Image, 0x53 = RAMDisk
 66         uint8_t image00_comp;     // Image 00 Compression; 0x67 = LZMA, 0x00 = uncompressed
 67         uint16_t unknown1;        // Unknown1
 68         uint32_t image01_offset;  // Image 01 Offset
 69         uint32_t image01_size;    // Image 01 size
 70         uint32_t unknown2;        // Unknown2
 71         uint32_t image02_offset;  // Image 02 Offset
 72         uint32_t image02_size;    // Image 02 size
 73         uint32_t unknown3;        // Unknown3
 74         uint8_t padding[60];      // unknown padding, probably for another 4 images
 75         char image_name[64];      // Image Name
 76         uint32_t image_id;        // Image ID 0xfe009300
 77         uint32_t unknown4;        // Unknown4
 78         uint32_t load_addr;       // Kernel Load Address
 79         uint32_t entry_point;     // Kernel Entry Point
 80         uint32_t payload_crc;     // CRC32 of entire payload
 81         uint8_t image_os;         // Image OS Type; 0x0f = Linux
 82         uint8_t image_arch;       // Image Arch; 0x37 = MIPS
 83         uint8_t unknown5[4];      // Unknown5
 84         uint8_t stk_header[6];    // STK Header 0xaa552288bb66
 85         uint8_t unknown6[84];     // Unknown6
 86         uint32_t image_id2;       // Image ID 0xfe009300
 87         uint8_t unknown7[20];     // Unknown7
 88         char version_string[396]; // Version string; probably shorter, the rest is padding
 89 } __attribute__((packed));
 90 
 91 char *ofname = NULL;
 92 char *ifname = NULL;
 93 char *image_name = NULL;
 94 char *image_version = NULL;
 95 
 96 void *input_file = NULL;
 97 char *progname;
 98 uint32_t load_addr = 0x80100000;
 99 uint32_t entry_point = 0x80100000;
100 
101 /*
102  * Helper routines
103  */
104 void
105 usage(int status)
106 {
107         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
108 
109         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
110         fprintf(stream, "\nOptions:\n");
111         fprintf(stream,
112 "  -i <file>\n"
113 "                  input file, e.g. OpenWrt firmware image\n"
114 "  -o <file>\n"
115 "                  write output to the file <file>\n"
116 "  -n <name>\n"
117 "                  image name (e.g. OpenWrt)\n"
118 "  -v <version>\n"
119 "                  version (e.g. 25.12.0)\n"
120 "  -h              show this screen\n"
121         );
122 
123         exit(status);
124 }
125 
126 static void
127 *map_input(const char *name, size_t *len)
128 {
129         struct stat stat;
130         void *mapped;
131         int fd;
132 
133         fd = open(name, O_RDONLY);
134         if (fd < 0)
135                 return NULL;
136         if (fstat(fd, &stat) < 0) {
137                 close(fd);
138                 return NULL;
139         }
140         *len = stat.st_size;
141         mapped = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
142         if (close(fd) < 0) {
143                 (void) munmap(mapped, stat.st_size);
144                 return NULL;
145         }
146         return mapped;
147 }
148 
149 int
150 parse_arg(char *arg, char *buf, char *argv[])
151 {
152         int res = 0;
153         size_t argl;
154         char *tok;
155         char **ap = &buf;
156         int i;
157 
158         memset(argv, 0, MAX_ARG_COUNT * sizeof(void *));
159 
160         if ((arg == NULL)) {
161                 /* no arguments */
162                 return 0;
163         }
164 
165         argl = strlen(arg);
166         if (argl == 0) {
167                 /* no arguments */
168                 return 0;
169         }
170 
171         if (argl >= MAX_ARG_LEN) {
172                 /* argument is too long */
173                 argl = MAX_ARG_LEN-1;
174         }
175 
176         memcpy(buf, arg, argl);
177         buf[argl] = '\0';
178 
179         for (i = 0; i < MAX_ARG_COUNT; i++) {
180                 tok = strsep(ap, ":");
181                 if (tok == NULL) {
182                         break;
183                 }
184                 argv[i] = tok;
185                 res++;
186         }
187 
188         return res;
189 }
190 
191 int
192 required_arg(char c, char *arg)
193 {
194         if (arg == NULL || *arg != '-')
195                 return 0;
196 
197         ERR("option -%c requires an argument\n", c);
198         return -1;
199 }
200 
201 int
202 parse_opt_name(char ch, char *arg, char **dest)
203 {
204 
205         if (*dest != NULL) {
206                 ERR("only one input/output file allowed");
207                 return -1;
208         }
209 
210         if (required_arg(ch, arg))
211                 return -1;
212 
213         *dest = arg;
214 
215         return 0;
216 }
217 
218 uint8_t*
219 obfuscate_file(uint8_t *input_file, size_t len) {
220         uint8_t *out_file = malloc(len+3); // The new header adds 3 bytes to the total size
221         uint8_t lc;
222         uint8_t lp;
223         uint8_t pb;
224         uint8_t ds1;
225         uint8_t ds2;
226         uint8_t ds3;
227         uint8_t ds4;
228         int pos;
229         int index;
230 
231         struct hdr {
232                 uint8_t props;
233                 uint32_t dicsize;
234                 uint64_t size;
235         } __attribute__((packed));
236 
237         struct newhdr {
238                 uint16_t magic;    // Magic Number
239                 uint16_t special;  // Special Numbers for obfuscation
240                 uint8_t pb;        // LZMA pb
241                 uint8_t lp;        // LZMA lp
242                 uint8_t lc;        // LZMA lc
243                 uint8_t pad;       // Padding
244                 uint8_t ds2;       // Data size byte 2
245                 uint8_t ds3;       // Data size byte 3
246                 uint8_t ds4;       // Data size byte 4
247                 uint8_t ds1;       // Data size byte 1
248                 uint32_t size;     // Size
249         } __attribute__((packed));
250 
251         struct hdr old_hdr;
252         struct newhdr new_hdr;
253 
254         if(out_file == NULL)
255                 return out_file;
256 
257         memcpy(&old_hdr, input_file, sizeof(old_hdr));
258         lc = old_hdr.props % 9;
259         lp = ((old_hdr.props - lc) / 9) % 5;
260         pb = (((old_hdr.props - lc) / 9) - lp) / 5;
261         lc ^= 0xb9;
262         lp ^= 0x5e;
263         pb ^= 0x37;
264 
265         ds1 = old_hdr.dicsize >> 24;
266         ds2 = old_hdr.dicsize >> 16;
267         ds3 = old_hdr.dicsize >> 8;
268         ds4 = old_hdr.dicsize;
269 
270         memset(&new_hdr, 0, sizeof(new_hdr));
271         new_hdr.magic = HOST_TO_BE16(0x5e71);
272         /* We set special 1 and special 2 to 1 so that the algorithm does not require any magic values */
273         new_hdr.special = 0x0101;
274         new_hdr.pb = pb;
275         new_hdr.lp = lp;
276         new_hdr.lc = lc;
277         new_hdr.ds1 = ds1;
278         new_hdr.ds2 = ds2;
279         new_hdr.ds3 = ds3;
280         new_hdr.ds4 = ds4;
281         /* This narrows the uint64_t to uint32_t; a file bigger than the 32 bits limit
282            wouldn't be accepted anyway */
283         new_hdr.size = HOST_TO_BE32(old_hdr.size); 
284 
285         memcpy(out_file, &new_hdr, sizeof(new_hdr));
286         memcpy(&out_file[sizeof(new_hdr)], &input_file[sizeof(old_hdr)], len - sizeof(old_hdr));
287 
288         pos = sizeof(new_hdr);
289         while(pos < (len - sizeof(old_hdr))) {
290                 for(int i=0; i<8; i++) {
291                         index = pos + i;
292                         if(index >= (len + sizeof(old_hdr)))
293                                 break;
294 
295                         out_file[index] = i ^ out_file[index];
296                 }
297                 pos += 0x4000;
298         }
299 
300         return out_file;
301 }
302 
303 int
304 is_empty_arg(char *arg)
305 {
306         int ret = 1;
307         if (arg != NULL) {
308                 if (*arg) ret = 0;
309         };
310         return ret;
311 }
312 
313 int main(int argc, char *argv[]) {
314         size_t file_len = 0;
315         size_t out_len = 0;
316         int optinvalid = 0;   /* flag for invalid option */
317         int res = EXIT_FAILURE;
318         int c;
319         uint8_t *obfuscated_file = NULL;
320 
321         struct sks7300_hdr sks_hdr;
322 
323         FILE *outfile;
324 
325         progname=basename(argv[0]);
326 
327         opterr = 0;  /* could not print standard getopt error messages */
328         while ( 1 ) {
329                 optinvalid = 0;
330 
331                 c = getopt(argc, argv, "i:o:n:v:h");
332                 if (c == -1)
333                         break;
334 
335                 switch (c) {
336                 case 'i':
337                         optinvalid = parse_opt_name(c,optarg,&ifname);
338                         break;
339                 case 'o':
340                         optinvalid = parse_opt_name(c,optarg,&ofname);
341                         break;
342                 case 'n':
343                         optinvalid = parse_opt_name(c,optarg,&image_name);
344                         break;
345                 case 'v':
346                         optinvalid = parse_opt_name(c,optarg,&image_version);
347                         break;
348                 case 'h':
349                         usage(EXIT_SUCCESS);
350                         break;
351                 default:
352                         optinvalid = 1;
353                         break;
354                 }
355                 if (optinvalid != 0 ) {
356                         ERR("invalid option: -%c", optopt);
357                         goto out;
358                 }
359         }
360 
361         if(!ifname) {
362                 ERR("input file is mandatory");
363                 goto out;
364         }
365 
366         if(!ofname) {
367                 ERR("output file is mandatory");
368                 goto out;
369         }
370 
371         if(!image_name) {
372                 ERR("image name is mandatory");
373                 goto out;
374         }
375 
376         if(!image_version) {
377                 ERR("image version is mandatory");
378                 goto out;
379         }
380 
381         input_file = map_input(ifname, &file_len);
382         if(!input_file) {
383                 ERR("input file not found.");
384                 goto out;
385         }
386 
387         obfuscated_file = obfuscate_file(input_file, file_len);
388         out_len = file_len + 3; // The obfuscation adds 3 bytes to the total length
389 
390         memset(&sks_hdr, 0, sizeof(sks_hdr));
391         sks_hdr.image_magic = HOST_TO_BE32(0xfe071301);
392         sks_hdr.image_id = HOST_TO_BE32(0xfe009300);
393         sks_hdr.load_addr = HOST_TO_BE32(load_addr);
394         sks_hdr.entry_point = HOST_TO_BE32(entry_point);
395         sks_hdr.stk_header[0] = 0xaa;
396         sks_hdr.stk_header[1] = 0x55;
397         sks_hdr.stk_header[2] = 0x22;
398         sks_hdr.stk_header[3] = 0x88;
399         sks_hdr.stk_header[4] = 0xbb;
400         sks_hdr.stk_header[5] = 0x66;
401         sks_hdr.image_id2 = HOST_TO_BE32(0xfe009300);
402         strcpy(sks_hdr.image_name, image_name);
403         strcpy(sks_hdr.version_string, image_version);
404         sks_hdr.image00_type = 0x52; // Linux Kernel
405         sks_hdr.image00_comp = 0x67; // LZMA compressed
406         sks_hdr.image_os = 0x0f; // Linux
407         sks_hdr.image_arch = 0x37; // MIPS
408 
409 /*
410         // The below values are set in the OEM upgrade file, but their meaning
411         // is unknown
412         sks_hdr.unknown4 = HOST_TO_BE32(0x01);
413         sks_hdr.unknown5[2] = 0x8c;
414         sks_hdr.unknown5[3] = 0x08;
415 
416         sks_hdr.unknown6[3] = 0x01;
417         sks_hdr.unknown6[7] = 0x84;
418         sks_hdr.unknown6[8] = 0x01;
419         sks_hdr.unknown6[10] = 0x01;
420         sks_hdr.unknown6[11] = 0x07;
421         sks_hdr.unknown6[15] = 0x74;
422         sks_hdr.unknown6[83] = 0x74;
423 
424         sks_hdr.unknown7[3] = 0x02;
425         sks_hdr.unknown7[7] = 0x16;
426 */
427         uint32_t crc = crc32(0, obfuscated_file, out_len);
428         sks_hdr.payload_crc = HOST_TO_BE32(crc);
429         sks_hdr.image00_size = HOST_TO_BE32(out_len);
430         sks_hdr.image_size = HOST_TO_BE32(out_len);
431         crc = crc32(0, (unsigned char *)&sks_hdr, sizeof(sks_hdr));
432         sks_hdr.hdr_crc = HOST_TO_BE32(crc);
433 
434         outfile = fopen(ofname, "w");
435         fwrite(&sks_hdr, sizeof(sks_hdr), 1, outfile);
436         fwrite(obfuscated_file, out_len, 1, outfile);
437         fflush(outfile);
438         fclose(outfile);
439 
440         res = EXIT_SUCCESS;
441 
442 out:
443         if (res != EXIT_SUCCESS) {
444                 unlink(ofname);
445         }
446         if(input_file)
447                 munmap(input_file, file_len);
448         return res;
449 }
450 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt