/* OLEdecode - Decode Microsoft OLE files into its components. Copyright (C) 1998 Andrew Scriven This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Released under GPL, written by Andrew Scriven Copyright (C) 1998 Andrew Scriven */ /* ----------------------------------------------------------------------- Andrew Scriven Research and Engineering Electron Building, Windmill Hill, Whitehill Way, Swindon, SN5 6PB, UK Phone (44) 1793 896206, Fax (44) 1793 896251 ----------------------------------------------------------------------- */ /* Extremely modified by Arturo Tena */ /* The interface to OLEdecode now has int OLEdecode (FILE *OLEfile, pps_entry ** stream_list, U32 * root, U16 max_level); See the oledecod.h to see a description of the inputs and output */ #include #include #include #include #include /* #include */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wv.h" #include "oledecod.h" #define ENTRYCHUNK 20 /* number of entries in root_list and sbd_list will be added each time. must be at least 1 */ /* verbose pps tree */ static void verbosePPSTree (U32 root_pps, int level); /* reorder pps tree, from tree structure to a linear one, and write the level numbers */ static void reorder_pps_tree (pps_entry * root_pps, U16 level); /* free memory used (except the pps tree) */ static void ends (void); static U8 *Block; static U8 *BDepot, *SDepot, *Root; static pps_entry *pps_list = NULL; static U32 num_of_pps = 0; static FILE *sbfile = NULL; static char sbfilename[L_tmpnam]; static U32 *sbd_list; static U32 *root_list; int OLEdecode (FILE *input, pps_entry ** stream_list, U32 * root, U16 max_level) { int c; U32 num_bbd_blocks; U32 i, len; U8 *s, *p, *t; long FilePos; /* FilePos is long, not U32, because second argument of fseek is long */ /* initialize static variables */ Block = BDepot = SDepot = Root = NULL; pps_list = NULL; num_of_pps = 0; sbfilename[0] = 0; root_list = sbd_list = NULL; /* open input file */ test (input != NULL, 4, ends ()); /* cannot be opened */ /* fast check type of file */ verbose ("fast testing type of file"); test ((c = getc (input)) != EOF, 5, ends ()); test (ungetc (c, input) != EOF, 5, ends ()); test ( (c < 32 || c > 126) , 8, ends ()); /* We have a legible character, not good */ test (c != 0xdb, 2, ends ()); /* probably non-ole Word 2 file */ test (c == 0xd0, 9, ends ()); /* read header block */ verbose ("read header block"); Block = (U8 *) malloc (0x0200); test (Block != NULL, 10, ends ()); fread (Block, 0x0200, 1, input); test (!ferror (input), 5, ends ()); /* really check type of file */ rewind (input); verbose ("testing type of file"); test (fil_sreadU32 (Block) != 0xd0cf11e0UL, 9, ends ()); test (fil_sreadU32 (Block + 0x04) != 0xa1b11ae1UL, 9, ends ()); /* read big block depot */ verbose ("read big block depot (bbd)"); num_bbd_blocks = fil_sreadU32 (Block + 0x2c); verboseU32 (num_bbd_blocks); BDepot = malloc (0x0200 * num_bbd_blocks); test (BDepot != NULL, 10, ends ()); s = BDepot; for (i = 0; i < num_bbd_blocks; i++) { /* note: next line may be needed to be cast to long in right side */ FilePos = 0x0200 * (1 + fil_sreadU32 (Block + 0x4c + (i * 4))); assert (FilePos >= 0); test (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test (!ferror (input), 5, ends ()); s += 0x0200; } verboseU8Array (BDepot, num_bbd_blocks, 0x0200); /* extract the sbd block list */ verbose ("extract small block depot (sbd) block list"); sbd_list = malloc (ENTRYCHUNK * 4); test (sbd_list != NULL, 10, ends ()); sbd_list[0] = fil_sreadU32 (Block + 0x3c); /* -2 signed long int == 0xfffffffe unsinged long int */ for (len = 1; sbd_list[len - 1] != 0xfffffffeUL; len++) { test (len != 0, 5, ends ()); /* means file is too big */ /* if memory allocated in sbd_list is all used, allocate more memory */ if (!(len % ENTRYCHUNK)) { U32 *newspace; newspace = realloc (sbd_list, (1 + len / ENTRYCHUNK) * ENTRYCHUNK * 4); test (newspace != NULL, 10, ends ()); sbd_list = newspace; } if ( (sbd_list[len - 1] * 4) >= (0x0200 * num_bbd_blocks)) return(5); sbd_list[len] = fil_sreadU32 (BDepot + (sbd_list[len - 1] * 4)); test (sbd_list[len] != 0xfffffffdUL && sbd_list[len] != 0xffffffffUL, 5, ends ()); } len--; verboseU32Array (sbd_list, len); /* read in small block depot, if there's any small block */ if (len == 0) { SDepot = NULL; verbose ("not read small block depot (sbd): there's no small blocks"); } else { verbose ("read small block depot (sbd)"); SDepot = malloc (0x0200 * len); test (SDepot != NULL, 10, ends ()); s = SDepot; for (i = 0; i < len; i++) { FilePos = 0x0200 * (1 + sbd_list[i]); assert (FilePos >= 0); test (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test (!ferror (input), 5, ends ()); s += 0x200; } verboseU8Array (SDepot, len, 0x0200); } /* extract the root block list */ verbose ("extract root block depot (root) block list"); root_list = malloc (ENTRYCHUNK * 4); test (root_list != NULL, 10, ends ()); root_list[0] = fil_sreadU32 (Block + 0x30); for (len = 1; root_list[len - 1] != 0xfffffffeUL; len++) { test (len != 0, 5, ends ()); /* means file is too long */ /* if memory allocated in root_list is all used, allocate more memory */ if (!(len % ENTRYCHUNK)) { U32 *newspace; newspace = realloc (root_list, (1 + len / ENTRYCHUNK) * ENTRYCHUNK * 4); test (newspace != NULL, 10, ends ()); root_list = newspace; } root_list[len] = fil_sreadU32 (BDepot + (root_list[len - 1] * 4)); test (root_list[len] != 0xfffffffdUL && root_list[len] != 0xffffffffUL, 5, ends ()); } len--; verboseU32Array (root_list, len); /* read in root block depot */ verbose ("read in root block depot (Root)"); Root = malloc (0x0200 * len); test (Root != NULL, 10, ends ()); s = Root; for (i = 0; i < len; i++) { FilePos = 0x0200 * (root_list[i] + 1); assert (FilePos >= 0); test (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test (!ferror (input), 5, ends ()); s += 0x200; } verboseU8Array (Root, len, 0x0200); /* assign space for pps list */ verbose ("read pps list"); num_of_pps = len * 4; /* each sbd block have 4 pps */ *stream_list = pps_list = malloc (num_of_pps * sizeof (pps_entry)); test (pps_list != NULL, 10, ends ()); /* read pss entry details and look out for "Root Entry" */ verbose ("read pps entry details"); for (i = 0; i < num_of_pps; i++) { U16 size_of_name; s = Root + (i * 0x80); /* read the number */ pps_list[i].ppsnumber = i; /* read the name */ size_of_name = fil_sreadU16 (s + 0x40); pps_list[i].name[0] = 0; if (size_of_name == 0) continue; for (p = (unsigned char *) pps_list[i].name, t = s; t < s + size_of_name; t++) *p++ = *t++; /* makes visible the non printable first character */ if (!isprint (pps_list[i].name[0]) && pps_list[i].name[0]) pps_list[i].name[0] += 'a'; /* read the type */ pps_list[i].type = fil_sreadU8 (s + 0x42); if (pps_list[i].type == 5) { assert (i == 0); strcpy(pps_list[i].name,"Root Entry"); *root = i; /* this pps is the root */ } /* read the others fields */ pps_list[i].previous = fil_sreadU32 (s + 0x44); pps_list[i].next = fil_sreadU32 (s + 0x48); pps_list[i].dir = fil_sreadU32 (s + 0x4c); pps_list[i].start = fil_sreadU32 (s + 0x74); pps_list[i].size = fil_sreadU32 (s + 0x78); pps_list[i].seconds1 = fil_sreadU32 (s + 0x64); pps_list[i].seconds2 = fil_sreadU32 (s + 0x6c); pps_list[i].days1 = fil_sreadU32 (s + 0x68); pps_list[i].days2 = fil_sreadU32 (s + 0x70); } /* NEXT IS VERBOSE verbose */ #ifdef VERBOSE { U32 i; printf ("before reorder pps tree\n"); printf ("pps type prev next dir start level size name\n"); for (i = 0; i < num_of_pps; i++) { if (!pps_list[i].name[0]) continue; printf ("%08lx ", pps_list[i].ppsnumber); printf ("%d ", pps_list[i].type); printf ("%08lx ", pps_list[i].previous); printf ("%08lx ", pps_list[i].next); printf ("%08lx ", pps_list[i].dir); printf ("%08lx ", pps_list[i].start); printf ("%04x ", pps_list[i].level); printf ("%08lx ", pps_list[i].size); printf ("%s\n", pps_list[i].name); } } #endif /* go through the tree made with pps entries, and reorder it so only the next link is used (move the previous-link-children to the last visited next-link-children) */ reorder_pps_tree (&pps_list[*root], 0); #ifdef VERBOSE /* NEXT IS VERBOSE verbose */ { U32 i; printf ("after reorder pps tree\n"); printf ("pps type prev next dir start level size name\n"); for (i = 0; i < num_of_pps; i++) { if (!pps_list[i].name[0]) continue; printf ("%08lx ", pps_list[i].ppsnumber); printf ("%d ", pps_list[i].type); printf ("%08lx ", pps_list[i].previous); printf ("%08lx ", pps_list[i].next); printf ("%08lx ", pps_list[i].dir); printf ("%08lx ", pps_list[i].start); printf ("%04x ", pps_list[i].level); printf ("%08lx ", pps_list[i].size); printf ("%s\n", pps_list[i].name); } } /* NEXT IS VERBOSE verbose */ verbosePPSTree (*root, 0); #endif /* generates pps real files */ /* NOTE: by this moment, the pps tree, wich is made with pps_list entries, is reordered */ verbose ("create pps files"); { U8 *Depot; FILE *OLEfile, *infile; U16 BlockSize, Offset; size_t bytes_to_read; U32 pps_size, pps_start; #define THEMIN(a,b) ((a)<(b) ? (a) : (b)) for (i = 0; i < num_of_pps; i++) { /* storage pps and non-valid-pps does not need files */ if (pps_list[i].type == 1 || !pps_list[i].name[0]) { pps_list[i].filename[0] = 0; continue; } /* pps that have level > max_level will not be extracterd */ if (max_level != 0 && pps_list[i].level > max_level) { pps_list[i].filename[0] = 0; continue; } pps_start = pps_list[i].start; pps_size = pps_list[i].size; pps_list[i].filename[0] = 0; /* create the new file */ if (pps_list[i].type == 5) /* root entry, sbfile must be generated */ { assert (i == *root); assert (i == 0); wvTempName (sbfilename); test (sbfilename[0], 7, ends ()); sbfile = OLEfile = fopen (sbfilename, "wb+"); test (OLEfile != NULL, 7, ends ()); verboseS (sbfilename); } else /* other entry, save in a file */ { wvTempName (pps_list[i].filename); test (pps_list[i].filename[0], 7, ends ()); verbose (pps_list[i].name); OLEfile = fopen (pps_list[i].filename, "wb"); test (OLEfile != NULL, 7, ends ()); verbose (pps_list[i].filename); } if (pps_size >= 0x1000 /*is in bbd */ || OLEfile == sbfile /*is root */ ) { /* read from big block depot */ Offset = 1; BlockSize = 0x0200; infile = input; Depot = BDepot; } else { /* read from small block file */ Offset = 0; BlockSize = 0x40; infile = sbfile; Depot = SDepot; } /* -2 signed long int == 0xfffffffe unsinged long int */ while (pps_start != 0xfffffffeUL) { #ifdef VERBOSE printf ("reading pps %08lx block %08lx from %s\n", pps_list[i].ppsnumber, pps_start, Depot == BDepot ? "big block depot" : "small block depot"); #endif FilePos = (pps_start + Offset) * BlockSize; assert (FilePos >= 0); bytes_to_read = THEMIN (BlockSize, pps_size); fseek (infile, FilePos, SEEK_SET); fread (Block, bytes_to_read, 1, infile); test (!ferror (infile), 5, ends ()); fwrite (Block, bytes_to_read, 1, OLEfile); test (!ferror (infile), 5, ends ()); assert (Depot != NULL); /* may be sbd no contains blocks */ pps_start = fil_sreadU32 (Depot + (pps_start * 4)); pps_size -= THEMIN (BlockSize, pps_size); if (pps_size == 0) pps_start = 0xfffffffeUL; } if (OLEfile == sbfile) /* if small block file generated */ rewind (OLEfile); /* rewind because we will reader later */ else if (!fclose (OLEfile)) /* close the pps file */ /* don't know what to do here */ ; } if (sbfile != NULL) { fclose (sbfile); if (!remove (sbfilename)) /* don't know what to do here */ ; sbfile = NULL; } } ends (); return 0; } /* reorder pps tree and write levels */ /* not sure if it is safe declare last_next_link_visited inside reorder_pps_tree */ static U32 *last_next_link_visited; void reorder_pps_tree (pps_entry * node, U16 level) { static int depth; depth++; /* NOTE: in next, previous and dir link, 0xffffffff means point to nowhere (NULL) */ if (depth == 50) { wvError(("this ole tree appears far too deep\n")); depth--; return; } node->level = level; /* reorder subtrees, if there's any */ if (node->dir != 0xffffffffUL) { reorder_pps_tree (&pps_list[node->dir], (U16) (level+1) ); } /* reorder next-link subtree, saving the most next link visited */ if (node->next != 0xffffffffUL) reorder_pps_tree (&pps_list[node->next], level); else last_next_link_visited = &node->next; /* move the prev child to the next link and reorder it, if there's any */ if (node->previous != 0xffffffffUL) { *last_next_link_visited = node->previous; node->previous = 0xffffffffUL; reorder_pps_tree (&pps_list[*last_next_link_visited], level); } depth--; } /* verbose pps tree */ void verbosePPSTree (U32 start_entry, int level) { U32 entry; int i; for (entry = start_entry; entry != 0xffffffffUL; entry = pps_list[entry].next) { if (pps_list[entry].type == 2) { for (i = 0; i < level * 3; i++) printf (" "); printf ("FILE %02lx %5ld %s\n", pps_list[entry].ppsnumber, pps_list[entry].size, pps_list[entry].name); } else { for (i = 0; i < level * 3; i++) printf (" "); printf ("DIR %02lx %s\n", pps_list[entry].ppsnumber, pps_list[entry].name); verbosePPSTree (pps_list[entry].dir, level + 1); } } } #define freeNoNULL(x) { if ((x) != NULL) free (x); (x) = NULL; } void closeOLEtreefiles (pps_entry * tree, U32 root) { if (tree[root].previous != 0xffffffffUL) closeOLEtreefiles (tree, tree[root].previous); if (tree[root].next != 0xffffffffUL) closeOLEtreefiles (tree, tree[root].next); if ((tree[root].type != 2) && (tree[root].dir != 0xffffffffUL)) closeOLEtreefiles (tree, tree[root].dir); else if (!remove (tree[root].filename)) /* I don't know what to do here, may be print a message? */ ; } void freeOLEtree (pps_entry * tree) { if (tree) closeOLEtreefiles (tree, 0); freeNoNULL (tree); } /* free memory used (except the pps tree) */ void ends (void) { freeNoNULL (Block); freeNoNULL (BDepot); freeNoNULL (SDepot); freeNoNULL (Root); freeNoNULL (sbd_list); freeNoNULL (root_list); if (sbfile != NULL) { fclose (sbfile); if (!remove (sbfilename)) /* I don't know what to do here, may be print a message? */ ; } }