Rizin
unix-like reverse engineering framework and cli tools
cabinfo.c
Go to the documentation of this file.
1 /* cabinfo -- dumps useful information from cabinets
2  * (C) 2000-2018 Stuart Caie <kyzer@cabextract.org.uk>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 
27 /* include some headers from the cut-down copy of libmspack
28  * - mspack.h for MSCAB_ATTRIB_?? defines
29  * - macros.h for LD and EndGetI?? macros
30  * - cab.h for cab structure offsets
31  * cabinfo does not use the system-wide <mspack.h> nor does it
32  * link with any libmspack functions. It's a standalone program.
33  */
34 #include "mspack/mspack.h"
35 #include "mspack/macros.h"
36 #include "mspack/cab.h"
37 
38 #if HAVE_FSEEKO
39 # define FSEEK fseeko
40 # define FTELL ftello
41 # define FILELEN off_t
42 #else
43 # define FSEEK fseek
44 # define FTELL ftell
45 # define FILELEN long
46 #endif
47 
48 void search();
49 void getinfo(FILELEN base_offset);
50 char *read_name();
51 
53 char *filename;
55 
56 int main(int argc, char *argv[]) {
57  int i;
58 
59  printf("Cabinet information dumper by Stuart Caie <kyzer@cabextract.org.uk>\n");
60 
61  if (argc <= 1) {
62  printf("Usage: %s <file.cab>\n", argv[0]);
63  return 1;
64  }
65 
66  for (i = 1; i < argc; i++) {
67  if ((fh = fopen((filename = argv[i]), "rb"))) {
68  search();
69  fclose(fh);
70  }
71  else {
72  perror(filename);
73  }
74  }
75  return 0;
76 }
77 
78 #define MIN(a,b) ((a)<(b)?(a):(b))
79 #define GETOFFSET (FTELL(fh))
80 #define READ(buf,len) if (myread((void *)(buf),(len))) return
81 #define SKIP(offset) if (FSEEK(fh,(offset),SEEK_CUR)) return
82 #define SEEK(offset) if (FSEEK(fh,(offset),SEEK_SET)) return
83 int myread(void *buf, size_t length) {
84  length = MIN(length, (int)(filelen - GETOFFSET));
85  return fread(buf, 1, length, fh) != length;
86 }
87 
88 #define SEARCH_SIZE (32*1024)
89 unsigned char search_buf[SEARCH_SIZE];
90 
91 void search() {
92  unsigned char *pstart = &search_buf[0], *pend, *p;
93  FILELEN offset, caboff, length;
94  unsigned long cablen32 = 0, foffset32 = 0;
95  int state = 0;
96 
97  if (FSEEK(fh, 0, SEEK_END) != 0) {
98  perror(filename);
99  return;
100  }
101  filelen = FTELL(fh);
102  if (FSEEK(fh, 0, SEEK_SET) != 0) {
103  perror(filename);
104  return;
105  }
106 
107  printf("Examining file \"%s\" (%"LD" bytes)...\n", filename, filelen);
108 
109  for (offset = 0; offset < filelen; offset += length) {
110  /* search length is either the full length of the search buffer,
111  * or the amount of data remaining to the end of the file,
112  * whichever is less. */
113  length = filelen - offset;
115 
116  /* fill the search buffer with data from disk */
117  SEEK(offset);
118  READ(&search_buf[0], length);
119 
120  /* read through the entire buffer. */
121  p = pstart;
122  pend = &search_buf[length];
123  while (p < pend) {
124  switch (state) {
125  /* starting state */
126  case 0:
127  /* we spend most of our time in this while loop, looking for
128  * a leading 'M' of the 'MSCF' signature */
129  while (*p++ != 0x4D && p < pend);
130  if (p < pend) state = 1; /* if we found the 'M', advance state */
131  break;
132 
133  /* verify that the next 3 bytes are 'S', 'C' and 'F' */
134  case 1: state = (*p++ == 0x53) ? 2 : 0; break;
135  case 2: state = (*p++ == 0x43) ? 3 : 0; break;
136  case 3: state = (*p++ == 0x46) ? 4 : 0; break;
137 
138  /* we don't care about bytes 4-7 */
139  /* bytes 8-11 are the overall length of the cabinet */
140  case 8: cablen32 = *p++; state++; break;
141  case 9: cablen32 |= *p++ << 8; state++; break;
142  case 10: cablen32 |= *p++ << 16; state++; break;
143  case 11: cablen32 |= *p++ << 24; state++; break;
144 
145  /* we don't care about bytes 12-15 */
146  /* bytes 16-19 are the offset within the cabinet of the filedata */
147  case 16: foffset32 = *p++; state++; break;
148  case 17: foffset32 |= *p++ << 8; state++; break;
149  case 18: foffset32 |= *p++ << 16; state++; break;
150  case 19: foffset32 |= *p++ << 24;
151  /* now we have recieved 20 bytes of potential cab header. */
152  /* work out the offset in the file of this potential cabinet */
153  caboff = offset + (p-pstart) - 20;
154  /* check that the files offset is less than the alleged length
155  * of the cabinet */
156  if (foffset32 < cablen32) {
157  /* found a potential result - try loading it */
158  getinfo(caboff);
159  offset = caboff + (FILELEN) cablen32;
160  length = 0;
161  p = pend;
162  }
163  state = 0;
164  break;
165 
166  default:
167  p++, state++; break;
168  } /* switch state */
169  } /* while p < pend */
170  } /* while offset < filelen */
171 }
172 
173 #define GETLONG(n) EndGetI32(&buf[n])
174 #define GETWORD(n) EndGetI16(&buf[n])
175 #define GETBYTE(n) ((int)buf[n])
176 
177 void getinfo(FILELEN base_offset) {
178  unsigned char buf[64];
179  int header_res = 0, folder_res = 0, data_res = 0;
180  int num_folders, num_files, flags, i, j;
181  FILELEN files_offset, min_data_offset = filelen;
182 
183  SEEK(base_offset);
185 
186  files_offset = base_offset + GETLONG(cfhead_FileOffset);
187  num_folders = GETWORD(cfhead_NumFolders),
188  num_files = GETWORD(cfhead_NumFiles),
190 
191  printf("CABINET HEADER @%"LD":\n", base_offset);
192  printf("- signature = '%4.4s'\n", buf);
193  printf("- overall length = %u bytes\n", GETLONG(cfhead_CabinetSize));
194  printf("- files offset = %"LD"\n", files_offset);
195  printf("- format version = %d.%d\n",
197  printf("- folder count = %u\n", num_folders);
198  printf("- file count = %u\n", num_files);
199  printf("- header flags = 0x%04x%s%s%s\n", flags,
200  ((flags & cfheadPREV_CABINET) ? " PREV_CABINET" : ""),
201  ((flags & cfheadNEXT_CABINET) ? " NEXT_CABINET" : ""),
202  ((flags & cfheadRESERVE_PRESENT) ? " RESERVE_PRESENT" : ""));
203  printf("- set ID = %u\n", GETWORD(cfhead_SetID));
204  printf("- set index = %u\n", GETWORD(cfhead_CabinetIndex));
205 
208  header_res = GETWORD(cfheadext_HeaderReserved);
209  folder_res = GETBYTE(cfheadext_FolderReserved);
210  data_res = GETBYTE(cfheadext_DataReserved);
211  printf("- header reserve = %u bytes (@%"LD")\n",
212  header_res, GETOFFSET);
213  printf("- folder reserve = %u bytes\n", folder_res);
214  printf("- data reserve = %u bytes\n", data_res);
215  SKIP(header_res);
216  }
217  if (flags & cfheadPREV_CABINET) {
218  printf("- prev cabinet = %s\n", read_name());
219  printf("- prev disk = %s\n", read_name());
220  }
221  if (flags & cfheadNEXT_CABINET) {
222  printf("- next cabinet = %s\n", read_name());
223  printf("- next disk = %s\n", read_name());
224  }
225 
226  printf("FOLDERS SECTION @%"LD":\n", GETOFFSET);
227  for (i = 0; i < num_folders; i++) {
228  FILELEN folder_offset, data_offset;
229  int comp_type, num_blocks, offset_ok;
230  char *type_name;
231 
232  folder_offset = GETOFFSET;
234  data_offset = base_offset + GETLONG(cffold_DataOffset);
235  num_blocks = GETWORD(cffold_NumBlocks),
236  comp_type = GETWORD(cffold_CompType);
237 
238  min_data_offset = MIN(data_offset, min_data_offset);
239  offset_ok = data_offset < filelen;
240 
241  switch (comp_type & cffoldCOMPTYPE_MASK) {
242  case cffoldCOMPTYPE_NONE: type_name = "stored"; break;
243  case cffoldCOMPTYPE_MSZIP: type_name = "MSZIP"; break;
244  case cffoldCOMPTYPE_QUANTUM: type_name = "Quantum"; break;
245  case cffoldCOMPTYPE_LZX: type_name = "LZX"; break;
246  default: type_name = "unknown"; break;
247  }
248 
249  printf("- folder 0x%04x @%"LD" %u data blocks @%"LD"%s %s compression (0x%04x)\n",
250  i, folder_offset, num_blocks, data_offset,
251  offset_ok ? "" : " [INVALID OFFSET]",
252  type_name, comp_type);
253 
254  SEEK(data_offset);
255  for (j = 0; j < num_blocks; j++) {
256  int clen, ulen;
257  if (GETOFFSET > filelen) {
258  printf(" - datablock %d @%"LD" [INVALID OFFSET]\n", j, GETOFFSET);
259  break;
260  }
264  printf(" - datablock %d @%"LD" csum=%08x c=%5u u=%5u%s\n",
265  j, data_offset, GETLONG(cfdata_CheckSum), clen, ulen,
266  ((clen > (32768+6144)) || (ulen > 32768)) ? " INVALID" : "");
267  data_offset += cfdata_SIZEOF + data_res + clen;
268  SKIP(data_res + clen);
269  }
270  SEEK(folder_offset + cffold_SIZEOF + folder_res);
271  }
272 
273  printf("FILES SECTION @%"LD":\n", GETOFFSET);
274  if (files_offset != GETOFFSET) {
275  printf("INVALID: file offset in header %"LD
276  " doesn't match start of files %"LD"\n",
277  files_offset, GETOFFSET);
278  }
279 
280  for (i = 0; i < num_files; i++) {
281  FILELEN file_offset = GETOFFSET;
282  char *folder_type;
283  int attribs, folder;
284 
285  if (file_offset > filelen) return;
286 
288  folder = GETWORD(cffile_FolderIndex);
289  attribs = GETWORD(cffile_Attribs);
290 
291  switch (folder) {
293  folder_type = "continued from prev and to next cabinet"; break;
295  folder_type = "continued from prev cabinet"; break;
297  folder_type = "continued to next cabinet"; break;
298  default:
299  folder_type = folder >= num_folders
300  ? "INVALID FOLDER INDEX"
301  : "normal folder";
302  break;
303  }
304 
305  printf("- file %-5d @%-12"LD"%s\n", i, file_offset,
306  (file_offset > min_data_offset ? " [INVALID FILE OFFSET]" : ""));
307  printf(" - name = %s%s\n", read_name(),
308  (attribs & MSCAB_ATTRIB_UTF_NAME) ? " (UTF-8)" : "");
309  printf(" - folder = 0x%04x [%s]\n", folder, folder_type);
310  printf(" - length = %u bytes\n", GETLONG(cffile_UncompressedSize));
311  printf(" - offset = %u bytes\n", GETLONG(cffile_FolderOffset));
312  printf(" - date = %02d/%02d/%4d %02d:%02d:%02d\n",
313  (GETWORD(cffile_Date)) & 0x1f,
314  (GETWORD(cffile_Date) >> 5) & 0xf,
315  (GETWORD(cffile_Date) >> 9) + 1980,
316  (GETWORD(cffile_Time) >> 11),
317  (GETWORD(cffile_Time) >> 5) & 0x3f,
318  (GETWORD(cffile_Time) << 1) & 0x3e);
319  printf(" - attrs = 0x%02x %s%s%s%s%s%s\n",
320  attribs,
321  (attribs & MSCAB_ATTRIB_RDONLY) ? "RDONLY " : "",
322  (attribs & MSCAB_ATTRIB_HIDDEN) ? "HIDDEN " : "",
323  (attribs & MSCAB_ATTRIB_SYSTEM) ? "SYSTEM " : "",
324  (attribs & MSCAB_ATTRIB_ARCH) ? "ARCH " : "",
325  (attribs & MSCAB_ATTRIB_EXEC) ? "EXEC " : "",
326  (attribs & MSCAB_ATTRIB_UTF_NAME) ? "UTF-8" : "");
327  }
328 }
329 
330 #define CAB_NAMEMAX (1024)
332 char *read_name() {
333  FILELEN name_start = GETOFFSET;
334  int i;
335  if (myread(&namebuf, CAB_NAMEMAX)) return "READ FAILED";
336  for (i = 0; i <= 256; i++) {
337  if (!namebuf[i]) {
338  FSEEK(fh, name_start + i + 1, SEEK_SET);
339  return namebuf;
340  }
341  }
342  printf("INVALID: name length > 256 for at offset %"LD"\n", name_start);
343  namebuf[256] = 0;
344  FSEEK(fh, name_start + 257, SEEK_SET);
345  return namebuf;
346 }
lzma_index ** i
Definition: index.h:629
#define cffile_Attribs
Definition: cab.h:40
#define cffoldCOMPTYPE_MSZIP
Definition: cab.h:50
#define cffold_NumBlocks
Definition: cab.h:32
#define cffile_Time
Definition: cab.h:39
#define cffold_SIZEOF
Definition: cab.h:34
#define cffile_SIZEOF
Definition: cab.h:41
#define cffileCONTINUED_FROM_PREV
Definition: cab.h:56
#define cfheadext_HeaderReserved
Definition: cab.h:27
#define cfheadRESERVE_PRESENT
Definition: cab.h:55
#define cfhead_FileOffset
Definition: cab.h:18
#define cffile_FolderOffset
Definition: cab.h:36
#define cffile_FolderIndex
Definition: cab.h:37
#define cfheadext_DataReserved
Definition: cab.h:29
#define cffoldCOMPTYPE_LZX
Definition: cab.h:52
#define cfhead_MajorVersion
Definition: cab.h:20
#define cffoldCOMPTYPE_MASK
Definition: cab.h:48
#define cffileCONTINUED_PREV_AND_NEXT
Definition: cab.h:58
#define cfdata_UncompressedSize
Definition: cab.h:44
#define cffoldCOMPTYPE_QUANTUM
Definition: cab.h:51
#define cffile_Date
Definition: cab.h:38
#define cfheadNEXT_CABINET
Definition: cab.h:54
#define cffileCONTINUED_TO_NEXT
Definition: cab.h:57
#define cffile_UncompressedSize
Definition: cab.h:35
#define cfhead_CabinetIndex
Definition: cab.h:25
#define cfhead_SetID
Definition: cab.h:24
#define cfhead_NumFiles
Definition: cab.h:22
#define cfhead_SIZEOF
Definition: cab.h:26
#define cfhead_MinorVersion
Definition: cab.h:19
#define cfhead_NumFolders
Definition: cab.h:21
#define cfhead_CabinetSize
Definition: cab.h:17
#define cfdata_CheckSum
Definition: cab.h:42
#define cfheadext_FolderReserved
Definition: cab.h:28
#define cfheadext_SIZEOF
Definition: cab.h:30
#define cffoldCOMPTYPE_NONE
Definition: cab.h:49
#define cffold_DataOffset
Definition: cab.h:31
#define cffold_CompType
Definition: cab.h:33
#define cfhead_Flags
Definition: cab.h:23
#define cfdata_SIZEOF
Definition: cab.h:45
#define cfheadPREV_CABINET
Definition: cab.h:53
#define cfdata_CompressedSize
Definition: cab.h:43
#define MSCAB_ATTRIB_HIDDEN
Definition: mspack.h:921
#define MSCAB_ATTRIB_EXEC
Definition: mspack.h:927
#define MSCAB_ATTRIB_UTF_NAME
Definition: mspack.h:929
#define MSCAB_ATTRIB_ARCH
Definition: mspack.h:925
#define MSCAB_ATTRIB_RDONLY
Definition: mspack.h:919
#define MSCAB_ATTRIB_SYSTEM
Definition: mspack.h:923
int main(int argc, char *argv[])
Definition: cabinfo.c:56
#define GETWORD(n)
Definition: cabinfo.c:174
char * read_name()
Definition: cabinfo.c:332
#define SEARCH_SIZE
Definition: cabinfo.c:88
#define FTELL
Definition: cabinfo.c:44
#define MIN(a, b)
Definition: cabinfo.c:78
#define GETBYTE(n)
Definition: cabinfo.c:175
#define GETLONG(n)
Definition: cabinfo.c:173
FILE * fh
Definition: cabinfo.c:52
#define CAB_NAMEMAX
Definition: cabinfo.c:330
#define READ(buf, len)
Definition: cabinfo.c:80
#define SEEK(offset)
Definition: cabinfo.c:82
#define GETOFFSET
Definition: cabinfo.c:79
#define FILELEN
Definition: cabinfo.c:45
#define SKIP(offset)
Definition: cabinfo.c:81
void search()
Definition: cabinfo.c:91
char namebuf[CAB_NAMEMAX]
Definition: cabinfo.c:331
FILELEN filelen
Definition: cabinfo.c:54
void getinfo(FILELEN base_offset)
Definition: cabinfo.c:177
#define FSEEK
Definition: cabinfo.c:43
char * filename
Definition: cabinfo.c:53
unsigned char search_buf[SEARCH_SIZE]
Definition: cabinfo.c:89
int myread(void *buf, size_t length)
Definition: cabinfo.c:83
_Use_decl_annotations_ int __cdecl printf(const char *const _Format,...)
Definition: cs_driver.c:93
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len static semflg const void static shmflg const struct timespec struct timespec static rem const char static group const void length
Definition: sflib.h:133
const char * filename
Definition: ioapi.h:137
voidpf uLong offset
Definition: ioapi.h:144
voidpf void * buf
Definition: ioapi.h:138
void * p
Definition: libc.cpp:67
#define LD
Definition: macros.h:27
static static fork const void static count static fd const char const char static newpath char char argv
Definition: sflib.h:40
string FILE
Definition: benchmark.py:21
static struct sockaddr static addrlen static backlog const void static flags void flags
Definition: sfsocketcall.h:123
Definition: dis.h:43
#define SEEK_SET
Definition: zip.c:88
#define SEEK_END
Definition: zip.c:84