1 /*
2 
3     File: file_exe.c
4 
5     Copyright (C) 1998-2005,2007 Christophe GRENIER <grenier@cgsecurity.org>
6 
7     This software is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write the Free Software Foundation, Inc., 51
19     Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #endif
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32 #include <stdio.h>
33 #include "types.h"
34 #include "common.h"
35 #include "filegen.h"
36 #include "pe.h"
37 #include "log.h"
38 
39 static void register_header_check_exe(file_stat_t *file_stat);
40 static int header_check_exe(const unsigned char *buffer, const unsigned int buffer_size, const unsigned int safe_header_only, const file_recovery_t *file_recovery, file_recovery_t *file_recovery_new);
41 static void file_rename_pe_exe(file_recovery_t *file_recovery);
42 
43 const file_hint_t file_hint_exe= {
44   .extension="exe",
45   .description="MS Windows executable",
46   .max_filesize=PHOTOREC_MAX_FILE_SIZE,
47   .recover=1,
48   .enable_by_default=1,
49   .register_header_check=&register_header_check_exe
50 };
51 
52 static const unsigned char exe_header[2]  = {'M','Z'};
53 
register_header_check_exe(file_stat_t * file_stat)54 static void register_header_check_exe(file_stat_t *file_stat)
55 {
56   register_header_check(0, exe_header,sizeof(exe_header), &header_check_exe, file_stat);
57 }
58 
header_check_exe(const unsigned char * buffer,const unsigned int buffer_size,const unsigned int safe_header_only,const file_recovery_t * file_recovery,file_recovery_t * file_recovery_new)59 static int header_check_exe(const unsigned char *buffer, const unsigned int buffer_size, const unsigned int safe_header_only, const file_recovery_t *file_recovery, file_recovery_t *file_recovery_new)
60 {
61   const struct dos_image_file_hdr *dos_hdr=(const struct dos_image_file_hdr*)buffer;
62   const struct pe_image_file_hdr *pe_hdr;
63   if(memcmp(buffer,exe_header,sizeof(exe_header))!=0)
64     return 0;
65   pe_hdr=(const struct pe_image_file_hdr *)(buffer+le32(dos_hdr->e_lfanew));
66   if(le32(dos_hdr->e_lfanew)>0 &&
67       le32(dos_hdr->e_lfanew) <= buffer_size-sizeof(struct pe_image_file_hdr) &&
68       (le32(pe_hdr->Magic) & 0xffff) == IMAGE_WIN16_SIGNATURE)
69   {
70     /* NE Win16 */
71     reset_file_recovery(file_recovery_new);
72     file_recovery_new->extension=file_hint_exe.extension;
73     return 1;
74   }
75   if(le32(dos_hdr->e_lfanew)>0 &&
76       le32(dos_hdr->e_lfanew) <= buffer_size-sizeof(struct pe_image_file_hdr) &&
77       (le32(pe_hdr->Magic) & 0xffff) == IMAGE_NT_SIGNATURE)
78   {
79     /* Windows PE */
80     if(le16(pe_hdr->Characteristics) & 0x2000)
81     {
82       /* Dynamic Link Library */
83       reset_file_recovery(file_recovery_new);
84       file_recovery_new->extension="dll";
85     }
86     else if(le16(pe_hdr->Characteristics) & 0x02)
87     {
88       reset_file_recovery(file_recovery_new);
89       file_recovery_new->extension=file_hint_exe.extension;
90     }
91     else
92     {
93 #ifdef DEBUG_EXE
94       log_warning("EXE rejected, bad characteristics %02x\n", le16(pe_hdr->Characteristics));
95 #endif
96       return 0;
97     }
98     file_recovery_new->time=le32(pe_hdr->TimeDateStamp);
99 #ifdef DEBUG_EXE
100     {
101       const struct pe_image_optional_hdr32 *pe_image_optional32=(const struct pe_image_optional_hdr32 *)
102 	(((const unsigned char*)pe_hdr + sizeof(struct pe_image_file_hdr)));
103       if(le16(pe_image_optional32->Magic)==IMAGE_NT_OPTIONAL_HDR_MAGIC)
104       {
105 	log_debug("SizeOfCode %lx\n", (long unsigned)le32(pe_image_optional32->SizeOfCode));
106 	log_debug("SizeOfImage %lx\n", (long unsigned)le32(pe_image_optional32->SizeOfImage));
107       }
108       else if(le16(pe_image_optional32->Magic)==IMAGE_NT_OPTIONAL_HDR64_MAGIC)
109       {
110 	const struct pe_image_optional_hdr64 *pe_image_optional64=(const struct pe_image_optional_hdr64 *)
111 	  (((const unsigned char*)pe_hdr + sizeof(struct pe_image_file_hdr)));
112       }
113       log_debug("PE image opt 0x%lx-0x%lx\n", (long unsigned)sizeof(struct pe_image_file_hdr),
114 	  (long unsigned)(sizeof(struct pe_image_file_hdr) + le16(pe_hdr->SizeOfOptionalHeader) - 1));
115     }
116 #endif
117     {
118       unsigned int i;
119       uint64_t sum=0;
120       const struct pe_image_section_hdr *pe_image_section=(const struct pe_image_section_hdr*)
121 	((const unsigned char*)pe_hdr + sizeof(struct pe_image_file_hdr) + le16(pe_hdr->SizeOfOptionalHeader));
122       for(i=0;
123 	  i<le16(pe_hdr->NumberOfSections) &&
124 	  (const unsigned char*)(pe_image_section+1) <= buffer+buffer_size;
125 	  i++,pe_image_section++)
126       {
127 	if(le32(pe_image_section->SizeOfRawData)>0)
128 	{
129 #ifdef DEBUG_EXE
130 	  log_debug("%s 0x%lx-0x%lx\n", pe_image_section->Name,
131 	      (unsigned long)le32(pe_image_section->PointerToRawData),
132 	      (unsigned long)le32(pe_image_section->PointerToRawData)+le32(pe_image_section->SizeOfRawData)-1);
133 #endif
134 	  if(le32(pe_image_section->SizeOfRawData)%32==0)
135 	  {
136 	    if(sum < le32(pe_image_section->PointerToRawData) + le32(pe_image_section->SizeOfRawData))
137 	      sum=le32(pe_image_section->PointerToRawData) + le32(pe_image_section->SizeOfRawData);
138 	  }
139 	}
140 	if(le16(pe_image_section->NumberOfRelocations)>0)
141 	{
142 #ifdef DEBUG_EXE
143 	  log_debug("relocations 0x%lx-0x%lx\n",
144 	      (unsigned long)le32(pe_image_section->PointerToRelocations),
145 	      (unsigned long)le32(pe_image_section->PointerToRelocations)+1*le16(pe_image_section->NumberOfRelocations)-1);
146 #endif
147 	  if(sum < le32(pe_image_section->PointerToRelocations)+ 1*le16(pe_image_section->NumberOfRelocations))
148 	    sum = le32(pe_image_section->PointerToRelocations)+ 1*le16(pe_image_section->NumberOfRelocations);
149 	}
150       }
151       if(le32(pe_hdr->NumberOfSymbols)>0)
152       {
153 #ifdef DEBUG_EXE
154 	log_debug("Symboles 0x%lx-0x%lx\n", (long unsigned)le32(pe_hdr->PointerToSymbolTable),
155 	    (long unsigned)(le32(pe_hdr->PointerToSymbolTable)+ IMAGE_SIZEOF_SYMBOL*le32(pe_hdr->NumberOfSymbols))-1);
156 #endif
157 	if(le32(pe_hdr->NumberOfSymbols)<0x10000)
158 	{
159 	  if(sum < le32(pe_hdr->PointerToSymbolTable)+ IMAGE_SIZEOF_SYMBOL*le32(pe_hdr->NumberOfSymbols))
160 	    sum = le32(pe_hdr->PointerToSymbolTable)+ IMAGE_SIZEOF_SYMBOL*le32(pe_hdr->NumberOfSymbols);
161 	}
162       }
163       /* It's not perfect, EXE overlay are not recovered */
164       file_recovery_new->calculated_file_size=sum;
165     }
166     file_recovery_new->data_check=&data_check_size;
167     file_recovery_new->file_check=&file_check_size;
168     file_recovery_new->file_rename=&file_rename_pe_exe;
169     return 1;
170   }
171   if(le16(dos_hdr->bytes_in_last_block) <= 512 &&
172       le16(dos_hdr->blocks_in_file) > 0 &&
173       le16(dos_hdr->min_extra_paragraphs) <= le16(dos_hdr->max_extra_paragraphs)
174     )
175   {
176     /* MSDOS EXE */
177     uint64_t coff_offset=le16(dos_hdr->blocks_in_file)*512;
178     if(le16(dos_hdr->bytes_in_last_block))
179       coff_offset-=512-le16(dos_hdr->bytes_in_last_block);
180 
181     if(coff_offset < buffer_size-1 &&
182 	buffer[coff_offset]==0x4c && buffer[coff_offset+1]==0x01)
183     { /*  COFF_I386MAGIC */
184       reset_file_recovery(file_recovery_new);
185       file_recovery_new->extension=file_hint_exe.extension;
186       return 1;
187     }
188 #ifdef DEBUG_EXE
189     {
190       unsigned int i;
191       const struct exe_reloc *exe_reloc;
192       log_info("Maybe a DOS EXE\n");
193       log_info("blocks %llu\n", (long long unsigned)coff_offset);
194       log_info("data start %llx\n", (long long unsigned)16*le16(dos_hdr->header_paragraphs));
195       log_info("reloc %u\n", le16(dos_hdr->num_relocs));
196       for(i=0, exe_reloc=(const struct exe_reloc *)(buffer+le16(dos_hdr->reloc_table_offset));
197 	  i < le16(dos_hdr->num_relocs) &&
198 	  le16(dos_hdr->reloc_table_offset)+ (i+1)*sizeof(struct exe_reloc) < buffer_size;
199 	  i++, exe_reloc++)
200       {
201 	log_info("offset %x, segment %x\n",
202 	    le16(exe_reloc->offset), le16(exe_reloc->segment));
203       }
204     }
205 #endif
206   }
207   return 0;
208 }
209 
210 struct rsrc_entries_s
211 {
212   uint32_t Type;
213   uint32_t Pos;
214 } __attribute__ ((gcc_struct, __packed__));
215 
216 struct PE_index
217 {
218   uint16_t len;
219   uint16_t val_len;
220   uint16_t type;
221 } __attribute__ ((gcc_struct, __packed__));
222 
223 static char vs_version_info[32]={
224   'V', 0x0, 'S', 0x0, '_', 0x0, 'V', 0x0, 'E', 0x0, 'R', 0x0, 'S', 0x0, 'I', 0x0,
225   'O', 0x0, 'N', 0x0, '_', 0x0, 'I', 0x0, 'N', 0x0, 'F', 0x0, 'O', 0x0, 0x0, 0x0
226 };
227 
228 static char StringFileInfo[30]={
229   'S', 0x0, 't', 0x0, 'r', 0x0, 'i', 0x0, 'n', 0x0, 'g', 0x0, 'F', 0x0, 'i', 0x0,
230   'l', 0x0, 'e', 0x0, 'I', 0x0, 'n', 0x0, 'f', 0x0, 'o', 0x0, 0x0, 0x0
231 };
232 
233 static char OriginalFilename[34]={
234   'O', 0x0, 'r', 0x0, 'i', 0x0, 'g', 0x0, 'i', 0x0, 'n', 0x0, 'a', 0x0, 'l', 0x0,
235   'F', 0x0, 'i', 0x0, 'l', 0x0, 'e', 0x0, 'n', 0x0, 'a', 0x0, 'm', 0x0, 'e', 0x0,
236   0x0, 0x0
237 };
238 
239 static char InternalName[24]={
240   'I', 0x0, 'n', 0x0, 't', 0x0, 'e', 0x0, 'r', 0x0, 'n', 0x0, 'a', 0x0, 'l', 0x0,
241   'N', 0x0, 'a', 0x0, 'm', 0x0, 'e', 0x0
242 };
243 
ReadUnicodeStr(const char * buffer,unsigned int pos,const unsigned int len)244 static unsigned int ReadUnicodeStr(const char *buffer, unsigned int pos, const unsigned int len)
245 {
246   for(; pos+2<len && (buffer[pos]!='\0' || buffer[pos+1]!='\0'); pos+=2)
247   {
248 #ifdef DEBUG_EXE
249     log_info("%c", buffer[pos]);
250 #endif
251   }
252   pos+=2;
253   if((pos & 0x03)!=0)
254     pos+=2;
255   return pos;
256 }
257 
PEVersion_aux(file_recovery_t * file_recovery,const char * buffer,const unsigned int end,const char * needle,const unsigned int needle_len,const int force_ext)258 static int PEVersion_aux(file_recovery_t *file_recovery, const char*buffer, const unsigned int end, const char *needle, const unsigned int needle_len, const int force_ext)
259 {
260   unsigned int pos=0;
261   while(1)
262   {
263     const struct PE_index *PE_index;
264     pos=(pos + 3) & 0xfffffffc;  /* align on a 4-byte boundary */
265     if(pos + 6 > end)
266     {
267       return -1;
268     }
269     PE_index=(const struct PE_index*)&buffer[pos];
270     if(le16(PE_index->len)==0 && le16(PE_index->val_len)==0)
271     {
272       return -1;
273     }
274     {
275       const char *stringName=&buffer[pos+6];
276       if(pos + 6 + sizeof(vs_version_info) < end &&
277 	  memcmp(stringName, vs_version_info, sizeof(vs_version_info))==0)
278       {
279 	pos+=6+sizeof(vs_version_info);
280 	if((pos & 0x03)!=0)
281 	  pos+=2;
282 	pos+=le16(PE_index->val_len);
283       }
284       else if(pos + 6 + sizeof(StringFileInfo) < end &&
285 	  memcmp(stringName, StringFileInfo, sizeof(StringFileInfo))==0 &&
286 	  le16(PE_index->val_len)==0)
287       {
288 	unsigned int i;
289 	unsigned int pt=pos+6+sizeof(StringFileInfo);
290 	pos+=le16(PE_index->len);
291 	for(i=0; pt + 6 < pos; i++)
292 	{
293 	  if(i==0)
294 	  {
295 	    pt=ReadUnicodeStr(buffer, pt+6, pos);
296 	  }
297 	  else
298 	  {
299 	    int do_rename=0;
300 	    PE_index=(const struct PE_index*)&buffer[pt];
301 	    if(pt+6+needle_len < end &&
302 		memcmp(&buffer[pt+6], needle, needle_len)==0)
303 	    {
304 	      do_rename=1;
305 	    }
306 	    pt=ReadUnicodeStr(buffer, pt+6, pos);
307 	    if(le16(PE_index->val_len)>0)
308 	    {
309 	      if(do_rename)
310 	      {
311 		file_rename_unicode(file_recovery, buffer, end, pt, NULL, force_ext);
312 		return 0;
313 	      }
314 #ifdef DEBUG_EXE
315 	      log_info(": ");
316 #endif
317 	      pt=ReadUnicodeStr(buffer, pt, pos);
318 	    }
319 	  }
320 #ifdef DEBUG_EXE
321 	  log_info("\n");
322 #endif
323 	}
324       }
325       else
326       {
327 	pos+=le16(PE_index->len)+le16(PE_index->val_len);
328       }
329     }
330   }
331 }
332 
PEVersion(FILE * file,const unsigned int offset,const unsigned int length,file_recovery_t * file_recovery)333 static void PEVersion(FILE *file, const unsigned int offset, const unsigned int length, file_recovery_t *file_recovery)
334 {
335   char *buffer;
336   if(length==0 || length > 1024*1024)
337     return;
338   if(fseek(file, offset, SEEK_SET)<0)
339     return ;
340   buffer=(char*)MALLOC(length);
341   if(fread(buffer, length, 1, file) != 1)
342   {
343     free(buffer);
344     return ;
345   }
346   if(PEVersion_aux(file_recovery, buffer, length, OriginalFilename, sizeof(OriginalFilename), 0)==0)
347   {
348     free(buffer);
349     return;
350   }
351   PEVersion_aux(file_recovery, buffer, length, InternalName, sizeof(InternalName), 1);
352   free(buffer);
353 }
354 
file_exe_ressource(FILE * file,const unsigned int base,const unsigned int dir_start,const unsigned int size,const unsigned int rsrcType,const unsigned int level,const struct pe_image_section_hdr * pe_sections,unsigned int nbr_sections,file_recovery_t * file_recovery)355 static void file_exe_ressource(FILE *file, const unsigned int base, const unsigned int dir_start, const unsigned int size, const unsigned int rsrcType, const unsigned int level, const struct pe_image_section_hdr *pe_sections, unsigned int nbr_sections, file_recovery_t *file_recovery)
356 {
357   struct rsrc_entries_s *rsrc_entries;
358   struct rsrc_entries_s *rsrc_entry;
359   unsigned char buffer[16];
360   int buffer_size;
361   unsigned int nameEntries;
362   unsigned idEntries;
363   unsigned int count;
364   unsigned int i;
365 #ifdef DEBUG_EXE
366   log_info("file_exe_ressource(file, %u, %u, %u, %u)\n", base, dir_start, size, level);
367 #endif
368   if(level > 2)
369     return ;
370   if(fseek(file, base + dir_start, SEEK_SET)<0)
371     return ;
372   buffer_size=fread(buffer, 1, sizeof(buffer), file);
373   if(buffer_size<16)
374     return ;
375   nameEntries = buffer[12]+(buffer[13]<<8);
376   idEntries =  buffer[14]+(buffer[15]<<8);
377   count = nameEntries + idEntries;
378   if(count==0 || count > 1024)
379     return ;
380   rsrc_entries=(struct rsrc_entries_s *)MALLOC(count * sizeof(struct rsrc_entries_s));
381   if(fread(rsrc_entries, sizeof(struct rsrc_entries_s), count, file) != count)
382   {
383     free(rsrc_entries);
384     return ;
385   }
386   for(i=0, rsrc_entry=rsrc_entries; i<count; i++, rsrc_entry++)
387   {
388     const unsigned int rsrcType_new=(level==0?le32(rsrc_entry->Type):rsrcType);
389 #ifdef DEBUG_EXE
390     log_info("ressource %u, %x, offset %u\n",
391 	rsrcType_new,
392 	le32(rsrc_entry->Pos),
393 	base + (le32(rsrc_entry->Pos) & 0x7fffffff));
394 #endif
395     /* Only intersted by version resources */
396     if(rsrcType_new==16)
397     {
398       if((le32(rsrc_entry->Pos) & 0x80000000)!=0)
399       {
400 	file_exe_ressource(file,
401 	    base,
402 	    le32(rsrc_entry->Pos) & 0x7fffffff,
403 	    size,
404 	    (level==0?le32(rsrc_entry->Type):rsrcType),
405 	    level + 1,
406 	    pe_sections, nbr_sections, file_recovery);
407       }
408       if(level==2)
409       {
410 	unsigned int off;
411 	unsigned int len;
412 	if(fseek(file, base + (le32(rsrc_entry->Pos) & 0x7fffffff), SEEK_SET)<0)
413 	{
414 	  free(rsrc_entries);
415 	  return ;
416 	}
417 	buffer_size=fread(buffer, 1, sizeof(buffer), file);
418 	if(buffer_size<16)
419 	{
420 	  free(rsrc_entries);
421 	  return ;
422 	}
423 	off=buffer[0]+ (buffer[1]<<8) + (buffer[2]<<16) + (buffer[3]<<24);
424 	len=buffer[4]+ (buffer[5]<<8) + (buffer[6]<<16) + (buffer[7]<<24);
425 	{
426 	  const struct pe_image_section_hdr *pe_section;
427 	  for(i=0, pe_section=pe_sections; i<nbr_sections; i++,pe_section++)
428 	  {
429 	    if(le32(pe_section->VirtualAddress) <= off
430 	      && off < le32(pe_section->VirtualAddress) + le32(pe_section->SizeOfRawData))
431 	    {
432 	      PEVersion(file, off - le32(pe_section->VirtualAddress) + base, len, file_recovery);
433 	      free(rsrc_entries);
434 	      return ;
435 	    }
436 	  }
437 	}
438 	free(rsrc_entries);
439 	return ;
440       }
441     }
442   }
443   free(rsrc_entries);
444 }
445 
file_rename_pe_exe(file_recovery_t * file_recovery)446 static void file_rename_pe_exe(file_recovery_t *file_recovery)
447 {
448   unsigned char buffer[4096];
449   FILE *file;
450   int buffer_size;
451   const struct dos_image_file_hdr *dos_hdr=(const struct dos_image_file_hdr*)buffer;
452   const struct pe_image_file_hdr *pe_hdr;
453   if((file=fopen(file_recovery->filename, "rb"))==NULL)
454     return;
455   buffer_size=fread(buffer, 1, sizeof(buffer), file);
456   if(buffer_size < (int)sizeof(struct dos_image_file_hdr))
457   {
458     fclose(file);
459     return ;
460   }
461   if(memcmp(buffer,exe_header,sizeof(exe_header))!=0)
462   {
463     fclose(file);
464     return ;
465   }
466   if((unsigned int)buffer_size < le32(dos_hdr->e_lfanew)+sizeof(struct pe_image_file_hdr))
467   {
468     fclose(file);
469     return ;
470   }
471   pe_hdr=(const struct pe_image_file_hdr *)(buffer+le32(dos_hdr->e_lfanew));
472   if(le32(dos_hdr->e_lfanew)==0 ||
473       le32(dos_hdr->e_lfanew) > buffer_size-sizeof(struct pe_image_file_hdr) ||
474       le32(pe_hdr->Magic) != IMAGE_NT_SIGNATURE)
475   {
476     fclose(file);
477     return ;
478   }
479   {
480     unsigned int i;
481     const struct pe_image_section_hdr *pe_sections;
482     const struct pe_image_section_hdr *pe_section;
483     unsigned int nbr_sections;
484     pe_sections=(const struct pe_image_section_hdr*)
485       ((const unsigned char*)pe_hdr + sizeof(struct pe_image_file_hdr) + le16(pe_hdr->SizeOfOptionalHeader));
486     for(i=0, pe_section=pe_sections;
487 	i<le16(pe_hdr->NumberOfSections) && (const unsigned char*)pe_section < buffer+buffer_size;
488 	i++, pe_section++)
489     {
490 #ifdef DEBUG_EXE
491       if(le32(pe_section->SizeOfRawData)>0)
492       {
493 	log_info("%s 0x%lx-0x%lx\n", pe_section->Name,
494 	    (unsigned long)le32(pe_section->VirtualAddress),
495 	    (unsigned long)le32(pe_section->VirtualAddress)+le32(pe_section->VirtualSize)-1);
496       }
497 #endif
498     }
499     nbr_sections=i;
500     for(i=0, pe_section=pe_sections;
501 	i<le16(pe_hdr->NumberOfSections) && (const unsigned char*)pe_section < buffer+buffer_size;
502 	i++, pe_section++)
503     {
504       if(le32(pe_section->SizeOfRawData)>0)
505       {
506 	if(memcmp((const char*)pe_section->Name, ".rsrc", 6)==0)
507 	{
508 	  file_exe_ressource(file,
509 	      le32(pe_section->PointerToRawData),
510 	      0,
511 	      le32(pe_section->SizeOfRawData),
512 	      0,
513 	      0,
514 	      pe_sections, nbr_sections, file_recovery);
515 	  fclose(file);
516 	  return;
517 	}
518       }
519     }
520   }
521   fclose(file);
522 }
523