Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 38 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
16 pmbaty 1
// dump.c -- information dumping routines for ifstool
2
 
3
// standard C includes
4
#include <stdint.h>
5
#include <stdbool.h>
6
#include <stdlib.h>
7
#include <stdarg.h>
8
#include <stdio.h>
9
#include <string.h>
10
#include <errno.h>
11
#include <sys/stat.h>
12
#include <ctype.h>
13
#include <time.h>
14
 
15
// platform-specific includes
16
#ifdef _MSC_VER
17
#include <direct.h>
18
#include <sys/utime.h>
19
#else // !_MSC_VER
20
#include <sys/param.h>
21
#include <unistd.h>
22
#include <utime.h>
23
#endif // _MSC_VER
24
 
25
// own includes
26 pmbaty 26
#include "ucl/ucl.h"
27
#include "minilzo.h"
16 pmbaty 28
#include "buffer.h"
29
#include "sha512.h"
30
#include "ifsfile.h"
31
#include "elffile.h"
32
#include "utility.h"
33
 
34
 
35
// imported global variables
36
extern int verbose_level; // from ifstool.c
37
 
38
 
39
// exported function prototypes
26 pmbaty 40
int dump_ifs_info (const char *ifs_pathname, bool want_everything, bool hide_filename); // dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
16 pmbaty 41
int dump_ifs_contents (const char *ifs_pathname, const char *outdir); // dumps the IFS filesystem contents in outdir, returns 0 on success and >0 on error
42
int dump_file_hex (const char *pathname); // hexdump the content of pathname, returns 0 on success and != 0 on error
43
 
44
 
45
// prototypes of local functions
46
static int create_intermediate_dirs (const char *file_pathname); // creates all intermediate directories from root (or cwd) up to file_path, returns 0 on success and != 0 on error
47
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...); // hexdump-style formatted output to a file stream (which may be stdout/stderr)
48
static char *binary (const uint8_t x, char char_for_zero, char char_for_one); // returns the binary representation of x as a string
49
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8]); // returns the ORed description of byte 'x' according to the description strings for each bit
50
 
51
 
52
// imported function prototypes
53
extern int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // from ifstool.c
54
 
55
 
56
static int create_intermediate_dirs (const char *file_pathname)
57
{
58
   // creates all intermediate directories from root (or cwd) up to file_path
59
 
60
   char *temp_pathname;
61
   char *separator;
62
   char *ctx;
63
   size_t string_index;
64
   size_t length;
65
 
66
   temp_pathname = strdup (file_pathname); // have a working copy of file_pathname
67
   if (temp_pathname == NULL)
68
      return (-1); // on strdup() failure, return an error value (errno is set)
69
   length = strlen (temp_pathname);
70
   for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows
71
      if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\'))
72
         break; // look for the last directory separator and stop as soon as we find it
73
   if (string_index != SIZE_MAX)
74
   {
75
      for (; string_index < length; string_index++)
76
         temp_pathname[string_index] = 0; // if we found one, break there so as to have just the path and clear the rest of the string
77
      separator = strtok_r (&temp_pathname[1], "/\\", &ctx); // for each separator in the remaining string past the first one...
78
      while (separator != NULL)
79
      {
80
         (void) mkdir (temp_pathname, 0755); // create directories recursively
81
         temp_pathname[strlen (temp_pathname)] = '/'; // put the separator back
82
         separator = strtok_r (NULL, "/\\", &ctx); // and look for the next one
83
      }
84
   }
85
 
86
   free (temp_pathname); // release our working copy of file_pathname
87
   return (0);
88
}
89
 
90
 
91
static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
92
{
93
   // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
94
 
95
   va_list argptr;
96
   size_t index;
97
   int i;
98
 
99
   // concatenate all the arguments in one string and write it to the file
100
   va_start (argptr, fmt);
101
   vfprintf (fp, fmt, argptr);
102
   va_end (argptr);
103
 
104
   // for each row of howmany_columns bytes of data...
105
   for (index = 0; index < data_size; index += howmany_columns)
106
   {
107
      fprintf (fp, "    %05zu  ", index); // print array address of row
108
      for (i = 0; i < howmany_columns; i++)
109
         if (index + i < data_size)
110
            fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes
111
         else
112
            fprintf (fp, "   "); // else fill the space with blanks
113
      fprintf (fp, "   ");
114
      for (i = 0; i < howmany_columns; i++)
115
         if (index + i < data_size)
116
            fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII
117
         else
118
            fputc (' ', fp); // else fill the space with blanks
119
      fputc ('\n', fp);
120
   }
121
 
122
   return; // and return
123
}
124
 
125
 
126
static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
127
{
128
   // returns the binary representation of x as a string
129
 
130
   static thread_local char outstr[9] = "00000000";
131
   for (int i = 0; i < 8; i++)
132
      outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
133
   return (outstr);
134
}
135
 
136
 
137
static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
138
{
139
   // returns the ORed description of byte 'x' according to the description strings for each bit
140
 
141
   static char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
142
   static thread_local char outstr[8 * 64] = "";
143
 
144
   outstr[0] = 0;
145
   for (int i = 0; i < 8; i++)
146
      if (x & (1 << i))
147
      {
148
         if (outstr[0] != 0)
149
            strcat_s (outstr, sizeof (outstr), "|");
150
         strcat_s (outstr, sizeof (outstr), ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
151
      }
152
   return (outstr);
153
}
154
 
155
 
26 pmbaty 156
int dump_ifs_info (const char *ifs_pathname, bool want_everything, bool hide_filename)
16 pmbaty 157
{
158
   #define hex_printf(buf,size,...) do { \
159
      if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\
160
         hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \
161
      else { \
162
         printf (__VA_ARGS__); \
163
         hex_fprintf (stdout, (buf), 1024, 16, "   first kilobyte:\n"); \
164
      } \
165
   } while (0)
166
   #define BINARY(x) binary ((x), '-', 'x')
167
 
168
   static const char *startupheader_flags1_strings[8] = {
169
      "VIRTUAL", // bit 0
170
      "BIGENDIAN", // bit 1
171
      "COMPRESS_BIT1", // bit 2
172
      "COMPRESS_BIT2", // bit 3
173
      "COMPRESS_BIT3", // bit 4
174
      "TRAILER_V2", // bit 5
175
      "", // bit 6
176
      "", // bit 7
177
   };
178
   static const char *imageheader_flags_strings[8] = {
179
      "BIGENDIAN", // bit 0
180
      "READONLY", // bit 1
181
      "INO_BITS", // bit 2
182
      "SORTED", // bit 3
183
      "TRAILER_V2", // bit 4
184
      "", // bit 5
185
      "", // bit 6
186
      "", // bit 7
187
   };
188
 
189
   startup_header_t *startup_header = NULL;
190
   size_t startupheader_offset = 0;
191
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
192
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
193
   size_t startuptrailer_offset = 0;
194
   image_header_t *image_header = NULL;
195
   size_t imageheader_offset = 0;
196
   image_trailer_v1_t *image_trailer_v1 = NULL;
197
   image_trailer_v2_t *image_trailer_v2 = NULL;
198
   size_t imagetrailer_offset = 0;
199
   fsentry_t **fsentries = NULL; // mallocated
200
   size_t fsentry_count = 0;
201
   fsentry_t *current_fsentry = NULL;
26 pmbaty 202
   buffer_t decompression_dst;
16 pmbaty 203
   char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
204
   char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
33 pmbaty 205
   char *executable_format = NULL;
16 pmbaty 206
   size_t startupfile_blobsize = 0;
26 pmbaty 207
   size_t compressed_blocksize;
16 pmbaty 208
   void *reallocated_ptr;
209
   bool is_foreign_endianness;
26 pmbaty 210
   uint8_t *decompressor_out;
211
   uint8_t *decompressor_in;
212
   size_t decompressor_outlen;
16 pmbaty 213
   size_t bootfile_blobsize = 0;
214
   size_t current_offset;
215
   size_t fsentry_index;
216
   size_t nearest_distance;
217
   size_t nearest_index;
218
   size_t byte_index;
219
   uint32_t recorded_checksum;
220
   uint32_t computed_checksum;
221
   buffer_t file;
222
   time_t mtime;
26 pmbaty 223
   int cf;
16 pmbaty 224
 
225
   // open and read IFS file
226
   if (!Buffer_ReadFromFile (&file, ifs_pathname))
227
      DIE_WITH_EXITCODE (1, "can't open \"%s\" for reading: %s", ifs_pathname, strerror (errno));
228
 
229
   printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
26 pmbaty 230
   if (hide_filename)
231
      printf ("IFS file - size 0x%zx (%zd) bytes\n", file.size, file.size);
232
   else
233
      printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, file.size, file.size);
16 pmbaty 234
 
235
   // parse file from start to end
236
   current_offset = 0;
26 pmbaty 237
   cf = STARTUP_HDR_FLAGS1_COMPRESS_NONE;
16 pmbaty 238
   for (;;)
239
   {
240
      // does a startup header start here ?
26 pmbaty 241
      if ((current_offset + sizeof (startup_header_t) < file.size)
242
          && (startup_header == NULL)
243
          && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
16 pmbaty 244
      {
245
         startupheader_offset = current_offset;
246
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
247
 
248
         // layout:
249
         // [STARTUP HEADER]
250
         // (startup file blob)
251
         // [STARTUP TRAILER v1 or v2]
252
 
253
         printf ("\n");
254
         printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset);
255
         printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]);
256
         printf ("   version       = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???"));
26 pmbaty 257
         cf = startup_header->flags1 & STARTUP_HDR_FLAGS1_COMPRESS_MASK;
258
         printf ("   flags1        = 0x%02x (%s) - %s\n", startup_header->flags1, describe_uint8 (startup_header->flags1, startupheader_flags1_strings), (cf == STARTUP_HDR_FLAGS1_COMPRESS_NONE ? "uncompressed image" : (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB ? "Zlib-compressed image (non-bootable)" : (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO ? "LZO-compressed image" : (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL ? "UCL-compressed image" : "compressed image (unknown algorithm)")))));
16 pmbaty 259
         printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???"));
260
         printf ("   header_size   = 0x%04x (%d) - %s\n", startup_header->header_size, startup_header->header_size, (startup_header->header_size == sizeof (startup_header_t) ? "looks good" : "BAD"));
261
         printf ("   machine       = 0x%04x (%d) - %s\n", startup_header->machine, startup_header->machine, (startup_header->machine == ELF_MACHINE_X86_64 ? "x86_64" : (startup_header->machine == ELF_MACHINE_AARCH64 ? "aarch64" : "unknown")));
262
         printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr);
263
         printf ("   paddr_bias    = 0x%08x (%d) - value to add to physical addresses to get an indirectable pointer value\n", startup_header->paddr_bias, startup_header->paddr_bias);
264
         printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr);
265
         printf ("   ram_paddr     = 0x%08x (%d) - physical address of RAM to copy image to (startup_size bytes copied)\n", startup_header->ram_paddr, startup_header->ram_paddr);
266
         printf ("   ram_size      = 0x%08x (%d) - amount of RAM used by the startup program and executables in the fs\n", startup_header->ram_size, startup_header->ram_size);
267
         printf ("   startup_size  = 0x%08x (%d) - size of startup (never compressed) - %s\n", startup_header->startup_size, startup_header->startup_size, (current_offset + sizeof (image_header_t) + startup_header->startup_size + (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)) < file.size ? "looks good" : "BAD (IFS file too short)"));
26 pmbaty 268
         printf ("   stored_size   = 0x%08x (%d) - stored size of image - %s\n", startup_header->stored_size, startup_header->stored_size, (startup_header->stored_size <= startup_header->ram_size ? "looks good" : "???"));
16 pmbaty 269
         printf ("   imagefs_paddr = 0x%08x (%d) - set by IPL when startup runs - %s\n", startup_header->imagefs_paddr, startup_header->imagefs_paddr, (startup_header->imagefs_paddr == 0 ? "looks good" : "??? should be zero"));
270
         printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size);
271
         printf ("   preboot_size  = 0x%04x (%d) - size of loaded before header - %s\n", startup_header->preboot_size, startup_header->preboot_size, (startup_header->preboot_size == current_offset ? "looks good" : "???"));
272
         printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero"));
273
         printf ("   zero[0]       = 0x%08x (%d) - zeros - %s\n", startup_header->zero[0], startup_header->zero[0], (startup_header->zero[0] == 0 ? "looks good" : "??? should be zero"));
274
         printf ("   addr_off      = 0x%016llx (%lld) - offset for startup_vaddr and [image|ram|imagefs]_paddr - %s\n", (unsigned long long) startup_header->addr_off, (unsigned long long) startup_header->addr_off, (startup_header->addr_off == 0 ? "looks good" : "??? should be zero"));
275
         hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
276
 
277
         // validate that the file can contain up to the startup trailer
278
         if (current_offset + startup_header->startup_size > file.size)
279
         {
280
            LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)");
281
            goto endofdata;
282
         }
283
 
284
         // check if this endianness is ours
285
         if (   ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
286
             || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
287
            is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
288
         else
289
            is_foreign_endianness = false; // else this header is for the same endianness as us
290
 
291
         // locate the right startup trailer at the right offset
292
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
293
         {
294
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t);
295
            startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
296
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
297
         }
298
         else // old V1 trailer
299
         {
300
            startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t);
301
            startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
302
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
303
         }
304
 
305
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
306
         printf ("\n");
307
         printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
308
         printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize);
309
         printf ("   checksum 0x%08x\n", update_checksum (&file.bytes[current_offset], startupfile_blobsize, is_foreign_endianness));
310
 
311
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
312
         printf ("\n");
313
         printf ("Startup trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? 2 : 1));
314
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
315
         {
316
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
317
               sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", startup_trailer_v2->sha512[byte_index]);
38 pmbaty 318
            strcpy_s (computed_sha512, sizeof (computed_sha512), (const char *) SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL));
16 pmbaty 319
            recorded_checksum = startup_trailer_v2->cksum;
320
            computed_checksum = update_checksum (startup_header, startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness);
321
            printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", startupheader_offset, startuptrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
322
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset + SHA512_DIGEST_LENGTH, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
323
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
324
               printf ("Computed SHA-512: %s\n", computed_sha512);
325
            if (computed_checksum != recorded_checksum)
326
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
327
         }
328
         else // old v1 trailer
329
         {
330
            recorded_checksum = startup_trailer_v1->cksum;
331
            computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
332
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
333
            if (computed_checksum != recorded_checksum)
334
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
335
         }
336
 
337
         current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // now reach the next segment
338
      }
339
 
340
      // else does an image header start here ?
26 pmbaty 341
      else if ((current_offset + sizeof (image_header_t) < file.size)
342
               && (image_header == NULL)
343
               && (   ((cf == STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0))
344
                   || (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (startup_header->imagefs_size > 0)))
16 pmbaty 345
      {
346
         imageheader_offset = current_offset;
347
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
348
 
26 pmbaty 349
         // should we decompress it ?
350
         if (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE)
351
         {
352
            // it appears mkifs compresses data in blocks, prefixed by 2-byte block size in BIG ENDIAN
353
            Buffer_InitWithSize (&decompression_dst, startup_header->imagefs_size * 11 / 10); // mallocate and add 10% for safety
354
            decompression_dst.size = 0;
355
 
356
            if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
357
               ASSERT (ucl_init () == UCL_E_OK, "UCL library initialization failed -- please recompile this tool with less aggressive optimizations");
358
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
359
               ASSERT (lzo_init () == LZO_E_OK, "LZO library initialization failed -- please recompile this tool with less aggressive optimizations");
360
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
361
            {
362
               LOG_WARNING ("unimplemented compression scheme: zlib (FIXME)");
363
               goto endofdata;
364
            }
365
            else
366
            {
367
               LOG_WARNING ("unsupported compression flags: 0x%2x", cf);
368
               goto endofdata;
369
            }
370
 
371
            // run the compressed payload (the imagefs) through the right decompression algorithm
372
            for (;;)
373
            {
374
               compressed_blocksize = (file.bytes[current_offset + 0] << 8) | (file.bytes[current_offset + 1] << 0); // read block size word (in big engian)
375
               current_offset += 2; // skip it
376
               if (compressed_blocksize == 0)
377
                  break; // a nil block size means end of stream is reached
30 pmbaty 378
               //LOG_DEBUG ("about to decompress block of %zd bytes", compressed_blocksize);
26 pmbaty 379
               decompressor_in = &file.bytes[current_offset];
380
               decompressor_out = &decompression_dst.bytes[decompression_dst.size];
381
               decompressor_outlen = 0;
382
 
383
               if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
384
               {
385
                  // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8 / ucl_nrv2b_decompress_le16 / ucl_nrv2b_decompress_le32"
386
                  static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch
387
                  if (ucl_nrv2b_decompress_8 (decompressor_in, (ucl_uint) compressed_blocksize, decompressor_out, &ucl_outlen, NULL) != UCL_E_OK)
388
                  {
389
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
390
                     goto endofdata;
391
                  }
392
                  decompressor_outlen = ucl_outlen;
393
               }
394
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
395
               {
396
                  // LZO decompression. NOTE: mkifs uses the full LZO package, whereas I use minilzo.
397
                  static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch
398
                  if (lzo1x_decompress (decompressor_in, (lzo_uint) compressed_blocksize, decompressor_out, &lzo_outlen, NULL) != LZO_E_OK)
399
                  {
400
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
401
                     goto endofdata;
402
                  }
403
                  decompressor_outlen = lzo_outlen;
404
               }
405
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
406
                  ; // TODO
407
 
408
               current_offset += compressed_blocksize;
409
               decompression_dst.size += decompressor_outlen;
410
            }
411
 
412
            LOG_INFO ("decompressed %zd bytes into %zd bytes\n", file.size - imageheader_offset, decompression_dst.size);
413
 
414
            // now place the decompressed buffer in the payload at the imagefs offset
415
            ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&file, imageheader_offset, &decompression_dst));
416
            current_offset = imageheader_offset; // jump back to where we put the uncompressed data
417
            file.size = imageheader_offset + decompression_dst.size; // update IFS data size
418
 
419
            startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; // fix the pointers that might have changed
420
            if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
421
               startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset];
422
            else // old V1 trailer
423
               startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset];
424
            image_header = (image_header_t *) &file.bytes[imageheader_offset]; // fix the pointers that might have changed
425
         }
426
 
16 pmbaty 427
         // layout:
428
         // [IMAGE HEADER]
429
         // [image directory entries]
430
         // [smallest file blobs up to KERNEL]
431
         // [padding]
432
         // [KERNEL]
433
         // [rest of file blobs]
434
         // [IMAGE FOOTER]
435
 
436
         printf ("\n");
437
         printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset);
438
         printf ("   signature    = %02x %02x %02x %02x %02x %02x %02x (\"%.7s\") - good\n", image_header->signature[0], image_header->signature[1], image_header->signature[2], image_header->signature[3], image_header->signature[4], image_header->signature[5], image_header->signature[6], image_header->signature);
439
         printf ("   flags        = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings));
440
         printf ("   image_size   = 0x%08x (%d) - size from header to end of trailer - %s\n", image_header->image_size, image_header->image_size, (current_offset + image_header->image_size <= file.size ? "looks good" : "BAD (IFS file too short)"));
441
         printf ("   hdr_dir_size = 0x%08x (%d) - size from header to last dirent - %s\n", image_header->hdr_dir_size, image_header->hdr_dir_size, (current_offset + image_header->hdr_dir_size < file.size ? "looks good" : "BAD (IFS file too short)"));
442
         printf ("   dir_offset   = 0x%08x (%d) - offset from header to first dirent - %s\n", image_header->dir_offset, image_header->dir_offset, (current_offset + image_header->dir_offset >= file.size ? "BAD (IFS file too short)" : (image_header->dir_offset > image_header->hdr_dir_size ? "BAD" : "looks good")));
443
         printf ("   boot_ino[4]  = { 0x%08x, 0x%08x, 0x%08x, 0x%08x }\n", image_header->boot_ino[0], image_header->boot_ino[1], image_header->boot_ino[2], image_header->boot_ino[3]);
444
         printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino);
445
         printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr);
446
         hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
447
         printf ("   mountflags   = 0x%08x (%s %s %s %s)\n", image_header->mountflags, BINARY (((uint8_t *) &image_header->mountflags)[0]), BINARY (((uint8_t *) &image_header->mountflags)[1]), BINARY (((uint8_t *) &image_header->mountflags)[2]), BINARY (((uint8_t *) &image_header->mountflags)[3]));
448
         printf ("   mountpoint   = \"%s\"\n", image_header->mountpoint);
449
 
450
         // validate that the file can contain up to the image trailer
451
         if (current_offset + image_header->image_size > file.size)
452
         {
453
            LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
454
            goto endofdata;
455
         }
456
 
457
         // check if this endianness is ours
458
         if (   ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
459
             || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
460
            is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
461
         else
462
            is_foreign_endianness = false; // else this header is for the same endianness as us
463
 
464
         // locate the image trailer at the right offset
465
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
466
         {
467
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
468
            image_trailer_v2 = (image_trailer_v2_t *) &file.bytes[imagetrailer_offset];
469
         }
470
         else // old V1 trailer
471
         {
472
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
473
            image_trailer_v1 = (image_trailer_v1_t *) &file.bytes[imagetrailer_offset];
474
         }
475
 
476
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
477
 
478
         // there may be padding before the first directory entry
479
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
480
            hex_printf (&file.bytes[current_offset], image_header->dir_offset - sizeof (image_header_t), "\n" "%zd padding bytes at offset 0x%zd (%zd):\n", image_header->dir_offset - sizeof (image_header_t), current_offset, current_offset);
481
         current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
482
 
483
         // dump all directory entries until the last one included
484
         fsentries = NULL;
485
         fsentry_count = 0;
486
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
487
         {
488
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
489
 
490
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
491
               break; // end padding reached
492
 
493
            // stack up the filesystem entry pointers in an array while we read them
494
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
495
            ASSERT_WITH_ERRNO (reallocated_ptr);
496
            fsentries = reallocated_ptr;
497
            fsentries[fsentry_count] = current_fsentry;
498
            fsentry_count++;
499
 
500
            printf ("\n");
501
            printf ("Filesystem entry at offset 0x%zx (%zd) - last one at 0x%zx (%zd):\n", current_offset, current_offset, imageheader_offset + image_header->hdr_dir_size, imageheader_offset + image_header->hdr_dir_size);
502
            printf ("   size           = 0x%04x (%d) - size of dirent - %s\n", current_fsentry->header.size, current_fsentry->header.size, ((current_fsentry->header.size > 0) && (current_offset + current_fsentry->header.size < file.size) ? "looks good" : "BAD"));
503
            printf ("   extattr_offset = 0x%04x (%d) - %s\n", current_fsentry->header.extattr_offset, current_fsentry->header.extattr_offset, (current_fsentry->header.extattr_offset == 0 ? "no extattr" : "has extattr"));
504
            printf ("   ino            = 0x%08x (%d) - inode number (%s%s%s%s)\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "is" : "nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : ""));
505
            printf ("   mode           = 0x%08x (%d) - %s (0%o), POSIX permissions 0%o\n", current_fsentry->header.mode, current_fsentry->header.mode, (S_ISDIR (current_fsentry->header.mode) ? "directory" : (S_ISREG (current_fsentry->header.mode) ? "file" : (S_ISLNK (current_fsentry->header.mode) ? "symlink" : "device"))), (current_fsentry->header.mode & 0xF000) >> 12, current_fsentry->header.mode & 0xFFF);
506
            printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : ""));
507
            printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : ""));
508
            mtime = (time_t) current_fsentry->header.mtime;
509
            printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline
510
            if (S_ISDIR (current_fsentry->header.mode))
511
               printf ("   [DIRECTORY] path = \"%s\"\n", (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
512
            else if (S_ISREG (current_fsentry->header.mode))
513
            {
514
               printf ("   [FILE] offset = 0x%08x (%d) - %s\n", current_fsentry->u.file.offset, current_fsentry->u.file.offset, (imageheader_offset + current_fsentry->u.file.offset < file.size ? "looks good" : "BAD (IFS file too short)"));
515
               printf ("   [FILE] size   = 0x%08x (%d) - %s\n", current_fsentry->u.file.size, current_fsentry->u.file.size, (imageheader_offset + current_fsentry->u.file.offset + current_fsentry->u.file.size < file.size ? "looks good" : "BAD (IFS file too short)"));
516
               printf ("   [FILE] path   = \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
517
            }
518
            else if (S_ISLNK (current_fsentry->header.mode))
519
            {
520
               printf ("   [SYMLINK] sym_offset = 0x%04x (%d) - %s\n", current_fsentry->u.symlink.sym_offset, current_fsentry->u.symlink.sym_offset, (sizeof (current_fsentry->header) + 2 * sizeof (uint16_t) + current_fsentry->u.symlink.sym_offset <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)"));
521
               printf ("   [SYMLINK] sym_size   = 0x%04x (%d) - %s\n", current_fsentry->u.symlink.sym_size, current_fsentry->u.symlink.sym_size, (sizeof (current_fsentry->header) + 2 * sizeof (uint16_t) + current_fsentry->u.symlink.sym_offset + current_fsentry->u.symlink.sym_size <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)"));
522
               printf ("   [SYMLINK] path       = \"%s\"\n", (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
523
               printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &current_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array
524
            }
525
            else // can only be a device
526
            {
527
               printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev);
528
               printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev);
529
               printf ("   [DEVICE] path = \"%s\"\n", (char *) &current_fsentry->u.device.path); // convert from pointer to char array
530
            }
531
 
532
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
533
            {
534
               LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
535
               goto endofdata;
536
            }
537
 
538
            current_offset += current_fsentry->header.size;
539
         }
540
         if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
541
            hex_printf (&file.bytes[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset);
542
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
543
 
544
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
545
         if (fsentry_count > 0)
546
         {
547
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
548
            {
549
               nearest_distance = SIZE_MAX;
550
               nearest_index = SIZE_MAX;
551
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
552
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
553
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
554
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
555
                  {
556
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
557
                     nearest_index = fsentry_index;
558
                  }
559
               if (nearest_index == SIZE_MAX)
560
                  break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
561
 
562
               fsentry_index = nearest_index;
563
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
564
 
565
               // there may be padding before the file data
566
               if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
567
                  hex_printf (&file.bytes[current_offset], imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, current_offset, current_offset);
568
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
569
 
570
               printf ("\n");
571
               printf ("File data blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
572
               printf ("   corresponding dirent index: %zd/%zd\n", fsentry_index, fsentry_count);
573
               printf ("   corresponding inode 0x%08x (%d) -%s%s%s%s\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "" : " nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : ""));
574
               printf ("   corresponding path: \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
575
               printf ("   size 0x%zx (%zd) bytes\n", (size_t) current_fsentry->u.file.size, (size_t) current_fsentry->u.file.size);
576
               if (current_offset + 4 < file.size)
577
                  hex_printf (&file.bytes[current_offset], current_fsentry->u.file.size, "   data:\n");
578
               if (current_offset + current_fsentry->u.file.size < file.size)
579
                  printf ("   checksum %d\n", update_checksum (&file.bytes[current_offset], current_fsentry->u.file.size, is_foreign_endianness));
580
               else
581
               {
582
                  LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
583
                  goto endofdata;
584
               }
585
 
586
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
587
            }
588
         }
589
 
590
         // ad this point we're past the last file data, there may be padding before the image trailer
591
         if (imagetrailer_offset - current_offset > 0)
592
            hex_printf (&file.bytes[current_offset], imagetrailer_offset - current_offset, "\n" "%zd padding bytes at offset %zx (%zd):\n", imagetrailer_offset - current_offset, current_offset, current_offset);
593
         current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
594
 
595
         printf ("\n");
596
         printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? 2 : 1));
597
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
598
         {
599
            for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
600
               sprintf_s (&recorded_sha512[2 * byte_index], 3, "%02x", image_trailer_v2->sha512[byte_index]);
38 pmbaty 601
            strcpy_s (computed_sha512, sizeof (computed_sha512), (const char *) SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL));
16 pmbaty 602
            recorded_checksum = image_trailer_v2->cksum;
603
            computed_checksum = update_checksum (image_header, imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness);
604
            printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", imageheader_offset, imagetrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
605
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset + SHA512_DIGEST_LENGTH, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
606
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
607
               printf ("Computed SHA-512: %s\n", computed_sha512);
608
            if (computed_checksum != recorded_checksum)
609
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
610
         }
611
         else // old v1 trailer
612
         {
613
            recorded_checksum = image_trailer_v1->cksum;
614
            computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
615
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
616
            if (computed_checksum != recorded_checksum)
617
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
618
         }
619
 
620
         current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now reach the next segment (typically end of file)
621
      }
622
 
623
      // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header
624
      else
625
      {
33 pmbaty 626
         // if the first two bytes are Mark Zbikowski's initials, it's a PE executable, which means this is an EFI image
627
         executable_format = ((file.size > 2) && (memcmp (file.bytes, "MZ", 2) == 0) ? "UEFI" : "BIOS"); // anything else is a BIOS image
628
 
629
         // scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1)
16 pmbaty 630
         for (byte_index = current_offset; byte_index < file.size - 6; byte_index++)
631
            if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
632
               break; // stop as soon as we find it
633
 
634
         if (byte_index >= file.size - 6)
635
            break; // if not found, stop scanning
636
 
637
         bootfile_blobsize = byte_index - current_offset;
33 pmbaty 638
         printf ("%s boot code at offset 0x%zx (%zd):\n", executable_format, current_offset, current_offset);
639
         if ((strcmp (executable_format, "UEFI") == 0) && (byte_index >= sizeof (uefi64_header_t)))
640
         {
641
            uefi64_header_t *uefi_header = (uefi64_header_t *) file.bytes; // dump UEFI header details
642
            printf ("   MZ header: signature: 5a 4d - 'MZ', good\n"); // we already validated this
643
            printf ("   MZ header: number of bytes in last page: %d - %s\n", uefi_header->dos_header.bytes_in_last_page, (uefi_header->dos_header.bytes_in_last_page == 144 ? "good" : "unexpected value!?"));
644
            printf ("   MZ header: number of 512-byte pages: %d - %s\n", uefi_header->dos_header.number_of_pages, (uefi_header->dos_header.number_of_pages == 3 ? "good" : "unexpected value!?"));
645
            printf ("   MZ header: number of relocations: %d - %s\n", uefi_header->dos_header.number_of_relocations, (uefi_header->dos_header.number_of_relocations == 0 ? "good" : "unexpected value!?"));
646
            printf ("   MZ header: header size in 16-byte paragraphs: %d - %s\n", uefi_header->dos_header.header_size_in_paragraphs, (uefi_header->dos_header.header_size_in_paragraphs == 4 ? "good" : "unexpected value!?"));
647
            printf ("   MZ header: required/requested 16-byte paragraphs: %d/%d - %s\n", uefi_header->dos_header.required_paragraphs, uefi_header->dos_header.requested_paragraphs, (memcmp (&uefi_header->dos_header.required_paragraphs, "\0\0\xff\xff", 4) == 0 ? "good" : "unexpected value!?"));
648
            printf ("   MZ header: initial relative relocatable segment offset fo SS: 0x%x - %s\n", uefi_header->dos_header.initial_relative_relocatable_segment_address_for_ss, (uefi_header->dos_header.initial_relative_relocatable_segment_address_for_ss == 0 ? "good" : "unexpected value!?"));
649
            printf ("   MZ header: initial stack pointer value: 0x%04x - %s\n", uefi_header->dos_header.initial_stack_pointer_value, (uefi_header->dos_header.initial_stack_pointer_value == 184 ? "good" : "unexpected value!?"));
650
            printf ("   MZ header: checksum (unused): 0x%x - %s\n", uefi_header->dos_header.unused_checksum, (uefi_header->dos_header.unused_checksum == 0 ? "good" : "unexpected value!?"));
651
            printf ("   MZ header: initial instruction pointer value: 0x%04x - %s\n", uefi_header->dos_header.initial_instruction_pointer_value, (uefi_header->dos_header.initial_instruction_pointer_value == 0 ? "good" : "unexpected value!?"));
652
            printf ("   MZ header: initial relative relocatable segment offset fo CS: 0x%x - %s\n", uefi_header->dos_header.initial_relative_relocatable_segment_address_for_cs, (uefi_header->dos_header.initial_relative_relocatable_segment_address_for_cs == 0 ? "good" : "unexpected value!?"));
653
            printf ("   MZ header: absolute offset to relocation table: 0x%x - %s\n", uefi_header->dos_header.absolute_offset_to_relocation_table, (uefi_header->dos_header.absolute_offset_to_relocation_table == 64 ? "good" : "unexpected value!?"));
654
            printf ("   MZ header: reserved_words1[4]: %s\n", (memcmp (uefi_header->dos_header.reserved_words1, "\0\0\0\0\0\0\0\0", 4 * sizeof (uint16_t)) == 0 ? "zerofilled, good" : "contains data, unexpected value!?"));
655
            printf ("   MZ header: OEM identifier: 0x%04x - %s\n", uefi_header->dos_header.oem_identifier, (uefi_header->dos_header.oem_identifier == 0 ? "good" : "unexpected value!?"));
656
            printf ("   MZ header: OEM info: 0x%04x - %s\n", uefi_header->dos_header.oem_info, (uefi_header->dos_header.oem_info == 0 ? "good" : "unexpected value!?"));
657
            printf ("   MZ header: reserved_words2[10]: %s\n", (memcmp (uefi_header->dos_header.reserved_words2, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 10 * sizeof (uint16_t)) == 0 ? "zerofilled, good" : "contains data, unexpected value!?"));
658
            printf ("   MZ header: absolute offset to PE header: 0x%08x (%d) - %s\n", uefi_header->dos_header.absolute_offset_to_pe_header, uefi_header->dos_header.absolute_offset_to_pe_header, (uefi_header->dos_header.absolute_offset_to_pe_header == 128 ? "good" : "unexpected value!?"));
659
            hex_printf (uefi_header->dos_stub_bytes, 64, "   MZ header: DOS stub code bytes (64 bytes):\n");
660
            printf ("   PE header: signature: %02x %02x %02x %02x - %s\n", uefi_header->pe_header.signature[0], uefi_header->pe_header.signature[1], uefi_header->pe_header.signature[2], uefi_header->pe_header.signature[3], (memcmp (uefi_header->pe_header.signature, "PE\0\0", 4) == 0 ? "'PE\\0\\0' - good" : "unexpected value!?"));
661
            printf ("   PE header: machine type: 0x%04x - %s\n", uefi_header->pe_header.machine_type, (uefi_header->pe_header.machine_type == 0x8664 ? "AMD64, good" : "unexpected value!?"));
662
            printf ("   PE header: number of sections: %d - %s\n", uefi_header->pe_header.number_of_sections, (uefi_header->pe_header.number_of_sections == 1 ? "good" : "unexpected value!?"));
663
            printf ("   PE header: symbols table offset: 0x%x (%d) - %s\n", uefi_header->pe_header.offset_to_symbols_table, uefi_header->pe_header.offset_to_symbols_table, (uefi_header->pe_header.offset_to_symbols_table == 0 ? "good" : "unexpected value!?"));
664
            printf ("   PE header: number of symbols: %d - %s\n", uefi_header->pe_header.number_of_symbols, (uefi_header->pe_header.number_of_symbols == 0 ? "good" : "unexpected value!?"));
665
            printf ("   PE header: size of optional header: %d - %s\n", uefi_header->pe_header.size_of_optional_header, (uefi_header->pe_header.size_of_optional_header == 240 ? "good" : "unexpected value!?"));
666
            printf ("   PE header: characteristics bitmap: 0x%04x - %s\n", uefi_header->pe_header.characteristics_bitmap, (uefi_header->pe_header.characteristics_bitmap == 0x223 ? "executable + uses large addresses + relocs stripped + debug info stripped, good" : "unexpected value!?"));
667
            printf ("   optional header: signature: %02x %02x - %s\n", uefi_header->optional_header64.signature[0], uefi_header->optional_header64.signature[1], (memcmp (uefi_header->optional_header64.signature, "\x0b\x02", 2) == 0 ? "64-bit optional header magic, good" : "unexpected value!?"));
668
            printf ("   optional header: linker version: %u.%u - %s\n", uefi_header->optional_header64.linker_version_major, uefi_header->optional_header64.linker_version_minor, (memcmp (&uefi_header->optional_header64.linker_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));
669
            printf ("   optional header: code size: 0x%x (%d) - %s\n", uefi_header->optional_header64.code_size, uefi_header->optional_header64.code_size, (byte_index + uefi_header->optional_header64.code_size == file.size ? "good" : "BAD (should equal file size minus UEFI boot prefix size)"));
670
            printf ("   optional header: size of initialized data: 0x%x (%d) - %s\n", uefi_header->optional_header64.size_of_initialized_data, uefi_header->optional_header64.size_of_initialized_data, (uefi_header->optional_header64.size_of_initialized_data == 0 ? "good" : "unexpected value!?"));
671
            printf ("   optional header: size of uninitialized data: 0x%x (%d) - %s\n", uefi_header->optional_header64.size_of_uninitialized_data, uefi_header->optional_header64.size_of_uninitialized_data, (uefi_header->optional_header64.size_of_uninitialized_data == 0 ? "good" : "unexpected value!?"));
672
            printf ("   optional header: entrypoint address: 0x%x (%d)\n", uefi_header->optional_header64.entrypoint_address, uefi_header->optional_header64.entrypoint_address); // TODO: validate
673
            printf ("   optional header: code base: 0x%x (%d) - %s\n", uefi_header->optional_header64.code_base, uefi_header->optional_header64.code_base, (uefi_header->optional_header64.code_base == 0 ? "good" : "unexpected value!?"));
38 pmbaty 674
            printf ("   optional header: image base: 0x%llx (%llu)\n", uefi_header->optional_header64.image_base, uefi_header->optional_header64.image_base);
33 pmbaty 675
            printf ("   optional header: section alignment: 0x%x (%d) - %s\n", uefi_header->optional_header64.section_alignment, uefi_header->optional_header64.section_alignment, ((uefi_header->pe_header.machine_type == 0x8664) && (uefi_header->optional_header64.section_alignment == 4096) ? "good" : "unexpected value!? (should equal system page size)"));
676
            printf ("   optional header: file alignment: 0x%x (%d) - %s\n", uefi_header->optional_header64.file_alignment, uefi_header->optional_header64.file_alignment, (uefi_header->optional_header64.file_alignment == 512 ? "good" : "unexpected value!? (must be a power of 2 between 512 and 65536)"));
677
            printf ("   optional header: OS version: %u.%u - %s\n", uefi_header->optional_header64.os_version_major, uefi_header->optional_header64.os_version_minor, (memcmp (&uefi_header->optional_header64.os_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));
678
            printf ("   optional header: image version: %u.%u - %s\n", uefi_header->optional_header64.image_version_major, uefi_header->optional_header64.image_version_minor, (memcmp (&uefi_header->optional_header64.image_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));
679
            printf ("   optional header: subsystem version: %u.%u - %s\n", uefi_header->optional_header64.subsystem_version_major, uefi_header->optional_header64.subsystem_version_minor, (memcmp (&uefi_header->optional_header64.subsystem_version_major, "\0\0", 2) == 0 ? "good" : "unexpected value!?"));
680
            printf ("   optional header: Win32 version value: 0x%x (%d) - %s\n", uefi_header->optional_header64.win32_version_value, uefi_header->optional_header64.win32_version_value, (uefi_header->optional_header64.win32_version_value == 0 ? "good" : "unexpected value!?"));
681
            printf ("   optional header: image size: 0x%x (%d) - %s\n", uefi_header->optional_header64.image_size, uefi_header->optional_header64.image_size, (uefi_header->optional_header64.image_size == file.size ? "good" : "BAD (should equal file size)"));
682
            printf ("   optional header: size of headers: 0x%x (%d) - %s\n", uefi_header->optional_header64.size_of_headers, uefi_header->optional_header64.size_of_headers, (uefi_header->optional_header64.size_of_headers == byte_index ? "good" : "BAD (should equal UEFI boot prefix size)"));
683
            printf ("   optional header: checksum (unused): 0x%x - %s\n", uefi_header->optional_header64.unused_checksum, (uefi_header->optional_header64.unused_checksum == 0 ? "good" : "unexpected value!?"));
684
            printf ("   optional header: subsystem type: 0x%x (%d) - %s\n", uefi_header->optional_header64.subsystem_type, uefi_header->optional_header64.subsystem_type, (uefi_header->optional_header64.subsystem_type == 10 ? "IMAGE_SUBSYSTEM_EFI_APPLICATION, good" : "BAD (should be IMAGE_SUBSYSTEM_EFI_APPLICATION i.e. 10 decimal)"));
685
            printf ("   optional header: DLL characteristics bitmap: 0x%x (%d) - %s\n", uefi_header->optional_header64.dll_characteristics_bitmap, uefi_header->optional_header64.dll_characteristics_bitmap, (uefi_header->optional_header64.dll_characteristics_bitmap == 0 ? "nil, good" : "BAD (should be 0)"));
38 pmbaty 686
            printf ("   optional header: stack commit/reserve size: %llu/%llu - %s\n", uefi_header->optional_header64.stack_commit_size, uefi_header->optional_header64.stack_reserve_size, ((uefi_header->optional_header64.stack_commit_size == uefi_header->optional_header64.section_alignment) && (uefi_header->optional_header64.stack_reserve_size == uefi_header->optional_header64.section_alignment) ? "good" : "unexpected value!? (both should equal system page size)"));
687
            printf ("   optional header: heap commit/reserve size: %llu/%llu - %s\n", uefi_header->optional_header64.heap_commit_size, uefi_header->optional_header64.heap_reserve_size, ((uefi_header->optional_header64.heap_commit_size == 0) && (uefi_header->optional_header64.heap_reserve_size == 0) ? "good" : "unexpected value!? (both should be zero)"));
33 pmbaty 688
            printf ("   optional header: loader flags: 0x%x (%d) - %s\n", uefi_header->optional_header64.unused_loader_flags, uefi_header->optional_header64.unused_loader_flags, (uefi_header->optional_header64.unused_loader_flags == 0 ? "good" : "BAD (should be zero)"));
689
            printf ("   optional header: number of data directories: %d - %s\n", uefi_header->optional_header64.number_of_data_directories, (uefi_header->optional_header64.number_of_data_directories == 16 ? "good" : "unexpected value!?"));
690
            printf ("   optional header: data directories: %s\n", (memcmp (uefi_header->optional_header64.data_directories, "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0", 8 * 16) == 0 ? "zerofilled, good" : "contains data, unexpected value!?"));
691
            printf ("   image section header: section name: '%.8s' - %s\n", uefi_header->unique_section.section_name, (memcmp (uefi_header->unique_section.section_name, "image\0\0\0", 8) == 0 ? "good" : "unexpected value!?"));
692
            printf ("   image section header: virtual size: 0x%x (%d) - %s\n", uefi_header->unique_section.virtual_size, uefi_header->unique_section.virtual_size, (uefi_header->unique_section.virtual_size == uefi_header->optional_header64.code_size ? "good" : "BAD (should equal code size)"));
693
            printf ("   image section header: virtual address: 0x%x (%d) - %s\n", uefi_header->unique_section.virtual_address, uefi_header->unique_section.virtual_address, (uefi_header->unique_section.virtual_address == byte_index ? "good" : "BAD (should equal UEFI boot prefix size)"));
694
            printf ("   image section header: raw data size: 0x%x (%d) - %s\n", uefi_header->unique_section.rawdata_size, uefi_header->unique_section.rawdata_size, (uefi_header->unique_section.rawdata_size == uefi_header->optional_header64.code_size ? "good" : "BAD (should equal code size)"));
695
            printf ("   image section header: raw data offset: 0x%x (%d) - %s\n", uefi_header->unique_section.rawdata_offset, uefi_header->unique_section.rawdata_offset, (uefi_header->unique_section.rawdata_offset == byte_index ? "good" : "BAD (should equal UEFI boot prefix size)"));
696
            printf ("   image section header: offset to relocations: 0x%x (%d) - %s\n", uefi_header->unique_section.offset_to_relocations, uefi_header->unique_section.offset_to_relocations, (uefi_header->unique_section.offset_to_relocations == 0 ? "good" : "unexpected value!?"));
697
            printf ("   image section header: offset to line numbers: 0x%x (%d) - %s\n", uefi_header->unique_section.offset_to_linenos, uefi_header->unique_section.offset_to_linenos, (uefi_header->unique_section.offset_to_linenos == 0 ? "good" : "unexpected value!?"));
698
            printf ("   image section header: number of relocations: 0x%x (%d) - %s\n", uefi_header->unique_section.relocation_count, uefi_header->unique_section.relocation_count, (uefi_header->unique_section.relocation_count == 0 ? "good" : "unexpected value!?"));
699
            printf ("   image section header: number of line numbers: 0x%x (%d) - %s\n", uefi_header->unique_section.lineno_count, uefi_header->unique_section.lineno_count, (uefi_header->unique_section.lineno_count == 0 ? "good" : "unexpected value!?"));
700
            printf ("   image section header: characteristics bitmap: 0x%04x - %s\n", uefi_header->unique_section.characteristics_bitmap, (uefi_header->unique_section.characteristics_bitmap == 0x60 ? "image contains code + image contains initialized data, good" : "unexpected value!?"));
34 pmbaty 701
            hex_printf (&file.bytes[sizeof (uefi64_header_t)], byte_index - sizeof (uefi64_header_t), "   trailing padding (%zd bytes):\n", byte_index - sizeof (uefi64_header_t));
33 pmbaty 702
         }
703
         else // BIOS boot prefixes are opaque so far (FIXME: decompile to ASM?)
704
         {
705
            printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize);
706
            printf ("   checksum 0x%08x\n", update_checksum (&file.bytes[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same
707
         }
16 pmbaty 708
 
709
         current_offset = byte_index; // now reach the next segment
710
      }
711
   }
712
 
713
endofdata:
714
   // at this point there's nothing left we're able to parse
715
   if (current_offset < file.size)
716
   {
717
      printf ("End of identifiable data reached.\n");
718
      hex_printf (&file.bytes[current_offset], file.size - current_offset, "\n" "%zd extra bytes at offset %zx (%zd):\n", file.size - current_offset, current_offset, current_offset);
719
   }
720
 
721
   printf ("End of file reached at offset 0x%zx (%zd)\n", file.size, file.size);
722
   printf ("IFS dissecation complete.\n");
723
   return (0);
724
}
725
 
726
 
727
int dump_ifs_contents (const char *ifs_pathname, const char *outdir)
728
{
729
   static char outfile_pathname[MAXPATHLEN] = "";
730
 
731
   startup_header_t *startup_header = NULL;
732
   size_t startupheader_offset = 0;
733
   image_header_t *image_header = NULL;
734
   size_t imageheader_offset = 0;
735
   size_t imagetrailer_offset = 0;
736
   fsentry_t **fsentries = NULL; // mallocated
737
   size_t fsentry_count = 0;
738
   fsentry_t *current_fsentry = NULL;
27 pmbaty 739
   buffer_t decompression_dst;
16 pmbaty 740
   size_t startupfile_blobsize = 0;
27 pmbaty 741
   size_t compressed_blocksize;
16 pmbaty 742
   struct utimbuf file_times = { 0, 0 };
33 pmbaty 743
   char *exeformat_suffix = "";
16 pmbaty 744
   void *reallocated_ptr;
27 pmbaty 745
   uint8_t *decompressor_out;
746
   uint8_t *decompressor_in;
747
   size_t decompressor_outlen;
16 pmbaty 748
   size_t bootfile_blobsize = 0;
749
   size_t current_offset;
750
   size_t fsentry_index;
751
   size_t nearest_distance;
752
   size_t nearest_index;
753
   size_t byte_index;
754
   buffer_t file;
755
   FILE *fp;
27 pmbaty 756
   int cf;
16 pmbaty 757
 
758
   // open and read IFS file
759
   if (!Buffer_ReadFromFile (&file, ifs_pathname))
760
      DIE_WITH_EXITCODE (1, "can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
761
 
762
   // create the output directory
763
   create_intermediate_dirs (outdir);
764
   (void) mkdir (outdir, 0755);
765
 
766
   // parse file from start to end
767
   current_offset = 0;
768
   for (;;)
769
   {
770
      // does a startup header start here ?
27 pmbaty 771
      if ((current_offset + sizeof (startup_header_t) < file.size)
772
          && (startup_header == NULL)
773
          && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
16 pmbaty 774
      {
775
         startupheader_offset = current_offset;
776
         startup_header = (startup_header_t *) &file.bytes[startupheader_offset];
777
 
778
         // layout:
779
         // [STARTUP HEADER]
780
         // (startup file blob)
781
         // [STARTUP TRAILER v1 or v2]
782
 
783
         // validate that the file can contain up to the startup trailer
784
         if (current_offset + startup_header->startup_size > file.size)
785
         {
786
            LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)");
787
            goto endofdata;
788
         }
789
 
27 pmbaty 790
         // take note of the image compression flags
791
         cf = startup_header->flags1 & STARTUP_HDR_FLAGS1_COMPRESS_MASK;
792
 
16 pmbaty 793
         // locate the right startup trailer at the right offset
794
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
795
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
796
         else // old V1 trailer
797
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
798
 
799
         current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
800
 
801
         // write startup blob
33 pmbaty 802
         sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/startup%s.bin", outdir, exeformat_suffix);
16 pmbaty 803
         fopen_s (&fp, outfile_pathname, "wb");
804
         ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
805
         fwrite (&file.bytes[current_offset], 1, startupfile_blobsize, fp);
806
         fclose (fp);
807
 
808
         current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
809
         current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // jump over the startup trailer and reach the next segment
810
      }
811
 
812
      // else does an image header start here ?
27 pmbaty 813
      else if ((current_offset + sizeof (image_header_t) < file.size)
814
               && (image_header == NULL)
815
               && (((cf == STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0))
816
               || (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE) && (startup_header->imagefs_size > 0)))
16 pmbaty 817
      {
818
         imageheader_offset = current_offset;
819
         image_header = (image_header_t *) &file.bytes[imageheader_offset];
820
 
27 pmbaty 821
         // should we decompress it ?
822
         if (cf != STARTUP_HDR_FLAGS1_COMPRESS_NONE)
823
         {
824
            // it appears mkifs compresses data in blocks, prefixed by 2-byte block size in BIG ENDIAN
825
            Buffer_InitWithSize (&decompression_dst, startup_header->imagefs_size * 11 / 10); // mallocate and add 10% for safety
826
            decompression_dst.size = 0;
827
 
828
            if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
829
               ASSERT (ucl_init () == UCL_E_OK, "UCL library initialization failed -- please recompile this tool with less aggressive optimizations");
830
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
831
               ASSERT (lzo_init () == LZO_E_OK, "LZO library initialization failed -- please recompile this tool with less aggressive optimizations");
832
            else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
833
            {
834
               LOG_WARNING ("unimplemented compression scheme: zlib (FIXME)");
835
               goto endofdata;
836
            }
837
            else
838
            {
839
               LOG_WARNING ("unsupported compression flags: 0x%2x", cf);
840
               goto endofdata;
841
            }
842
 
843
            // run the compressed payload (the imagefs) through the right decompression algorithm
844
            for (;;)
845
            {
846
               compressed_blocksize = (file.bytes[current_offset + 0] << 8) | (file.bytes[current_offset + 1] << 0); // read block size word (in big engian)
847
               current_offset += 2; // skip it
848
               if (compressed_blocksize == 0)
849
                  break; // a nil block size means end of stream is reached
30 pmbaty 850
               //LOG_DEBUG ("about to decompress block of %zd bytes", compressed_blocksize);
27 pmbaty 851
               decompressor_in = &file.bytes[current_offset];
852
               decompressor_out = &decompression_dst.bytes[decompression_dst.size];
853
               decompressor_outlen = 0;
854
 
855
               if (cf == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
856
               {
857
                  // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8 / ucl_nrv2b_decompress_le16 / ucl_nrv2b_decompress_le32"
858
                  static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch
859
                  if (ucl_nrv2b_decompress_8 (decompressor_in, (ucl_uint) compressed_blocksize, decompressor_out, &ucl_outlen, NULL) != UCL_E_OK)
860
                  {
861
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
862
                     goto endofdata;
863
                  }
864
                  decompressor_outlen = ucl_outlen;
865
               }
866
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
867
               {
868
                  // LZO decompression. NOTE: mkifs uses the full LZO package, whereas I use minilzo.
869
                  static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch
870
                  if (lzo1x_decompress (decompressor_in, (lzo_uint) compressed_blocksize, decompressor_out, &lzo_outlen, NULL) != LZO_E_OK)
871
                  {
872
                     LOG_WARNING ("this IFS file is corrupted (UCL decompression failed)");
873
                     goto endofdata;
874
                  }
875
                  decompressor_outlen = lzo_outlen;
876
               }
877
               else if (cf == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
878
                  ; // TODO
879
 
880
               current_offset += compressed_blocksize;
881
               decompression_dst.size += decompressor_outlen;
882
            }
883
 
884
            LOG_INFO ("decompressed %zd bytes into %zd bytes\n", file.size - imageheader_offset, decompression_dst.size);
885
 
886
            // now place the decompressed buffer in the payload at the imagefs offset
887
            ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&file, imageheader_offset, &decompression_dst));
888
            current_offset = imageheader_offset; // jump back to where we put the uncompressed data
889
            file.size = imageheader_offset + decompression_dst.size; // update IFS data size
890
 
891
            startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; // fix the pointers that might have changed
892
            image_header = (image_header_t *) &file.bytes[imageheader_offset]; // fix the pointers that might have changed
893
         }
894
 
16 pmbaty 895
         // layout:
896
         // [IMAGE HEADER]
897
         // [image directory entries]
898
         // [smallest file blobs up to KERNEL]
899
         // [padding]
900
         // [KERNEL]
901
         // [rest of file blobs]
902
         // [IMAGE FOOTER]
903
 
904
         // validate that the file can contain up to the image trailer
905
         if (current_offset + image_header->image_size > file.size)
906
         {
907
            LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)");
908
            goto endofdata;
909
         }
910
 
911
         // locate the image trailer at the right offset
912
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
913
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
914
         else // old V1 trailer
915
            imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
916
 
917
         current_offset += sizeof (image_header_t); // jump over the image header
918
         current_offset += image_header->dir_offset - sizeof (image_header_t); // jump over possible padding
919
 
920
         // dump all directory entries until the last one included
921
         fsentries = NULL;
922
         fsentry_count = 0;
923
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
924
         {
925
            current_fsentry = (fsentry_t *) &file.bytes[current_offset];
926
 
927
            if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
928
               break; // end padding reached
929
 
930
            // stack up the filesystem entry pointers in an array while we read them
931
            reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
932
            ASSERT_WITH_ERRNO (reallocated_ptr);
933
            fsentries = reallocated_ptr;
934
            fsentries[fsentry_count] = current_fsentry;
935
            fsentry_count++;
936
 
937
            if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.size))
938
            {
939
               LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)");
940
               goto endofdata;
941
            }
942
 
943
            current_offset += current_fsentry->header.size;
944
         }
945
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // jump over possible padding
946
 
947
         // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
948
         if (fsentry_count > 0)
949
         {
950
            while (current_offset < imagetrailer_offset) // and parse data up to the trailer
951
            {
952
               nearest_distance = SIZE_MAX;
953
               nearest_index = SIZE_MAX;
954
               for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
955
                  if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
956
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
957
                      && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
958
                  {
959
                     nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
960
                     nearest_index = fsentry_index;
961
                  }
962
               if (nearest_index == SIZE_MAX)
963
                  break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
964
 
965
               fsentry_index = nearest_index;
966
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
967
 
968
               current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // jump over possible padding
969
 
970
               if (current_offset + current_fsentry->u.file.size >= file.size)
971
               {
972
                  LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)");
973
                  goto endofdata;
974
               }
975
 
976
               // write filesystem data entry
977
               if (S_ISDIR (current_fsentry->header.mode))
978
               {
979
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
980
                  create_intermediate_dirs (outfile_pathname);
981
                  (void) mkdir (outfile_pathname, current_fsentry->header.mode & 0777);
982
               }
983
               else if (S_ISLNK (current_fsentry->header.mode))
984
               {
985
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
986
                  create_intermediate_dirs (outfile_pathname);
987
#ifdef _WIN32
988
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
989
                  ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
990
                  fwrite ((char *) &current_fsentry->u.symlink.path + current_fsentry->u.symlink.sym_offset, 1, current_fsentry->u.symlink.sym_size, fp); // convert from pointer to char array
991
                  fclose (fp);
992
#else // !_WIN32, thus POSIX
993
                  symlink (current_fsentry->u.symlink.contents, outfile_pathname); // on UNIX systems, just create the symlink for real
994
#endif // _WIN32
995
               }
996
               else if (S_ISREG (current_fsentry->header.mode))
997
               {
998
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.file.path); // convert from pointer to char array
999
                  create_intermediate_dirs (outfile_pathname);
1000
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
1001
                  ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
1002
                  fwrite (&file.bytes[current_offset], 1, current_fsentry->u.file.size, fp);
1003
                  fclose (fp);
1004
               }
1005
               else // must be a device node. Since we might not be the super-user and/or on Win32, create plain file with "X:Y" as data
1006
               {
1007
                  sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/%s", outdir, (char *) &current_fsentry->u.device.path); // convert from pointer to char array
1008
                  create_intermediate_dirs (outfile_pathname);
1009
                  fopen_s (&fp, outfile_pathname, "wb"); // on Windows create symlinks as plain files
1010
                  ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
1011
                  fprintf (fp, "%u:%u", current_fsentry->u.device.dev, current_fsentry->u.device.rdev);
1012
                  fclose (fp);
1013
               }
1014
 
1015
               // set created file mtime
1016
               file_times.actime = current_fsentry->header.mtime;
1017
               file_times.modtime = current_fsentry->header.mtime;
1018
               utime (outfile_pathname, &file_times);
1019
 
1020
               // set created file mode
1021
#ifndef _WIN32
1022
               (void) chmod (outfile_pathname, current_fsentry->header.mode & 0777); // only on POSIX systems
1023
#endif // !_WIN32
1024
 
1025
               current_offset += current_fsentry->u.file.size; // now jump over this file's data
1026
            }
1027
         }
1028
 
1029
         // ad this point we're past the last file data, there may be padding before the image trailer
1030
         current_offset += imagetrailer_offset - current_offset; // jump over possible padding and reach the image trailer
1031
         current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now jump over the image trailer and reach the next segment (typically end of file)
1032
      }
1033
 
1034
      // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header
1035
      else
1036
      {
33 pmbaty 1037
         // if the first two bytes are Mark Zbikowski's initials, it's a PE executable, which means this is an EFI image
1038
         exeformat_suffix = ((file.size > 2) && (memcmp (file.bytes, "MZ", 2) == 0) ? "-uefi" : "-bios"); // anything else is a BIOS image
1039
 
1040
         // scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1)
16 pmbaty 1041
         for (byte_index = current_offset; byte_index < file.size - 6; byte_index++)
1042
            if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
1043
               break; // stop as soon as we find it
1044
 
1045
         if (byte_index >= file.size - 6)
1046
            break; // if not found, stop scanning
1047
 
1048
         bootfile_blobsize = byte_index - current_offset;
1049
 
1050
         // write boot blob
33 pmbaty 1051
         sprintf_s (outfile_pathname, sizeof (outfile_pathname), "%s/boot%s.bin", outdir, exeformat_suffix);
16 pmbaty 1052
         fopen_s (&fp, outfile_pathname, "wb");
1053
         ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno));
1054
         fwrite (&file.bytes[current_offset], 1, bootfile_blobsize, fp);
1055
         fclose (fp);
1056
 
1057
         current_offset = byte_index; // now reach the next segment
1058
      }
1059
   }
1060
 
1061
endofdata:
1062
   return (0);
1063
}
1064
 
1065
 
1066
int dump_file_hex (const char *pathname)
1067
{
1068
   buffer_t file;
1069
 
1070
   ASSERT_WITH_ERRNO (Buffer_ReadFromFile (&file, pathname));
44 pmbaty 1071
   hex_fprintf (stdout, file.bytes, file.size, 16, "%s (%zd bytes):\n", &pathname[strcspn (pathname, "/\\")], file.size);
16 pmbaty 1072
   return (0);
1073
}