1 /*
2 
3     File: file_riff.c
4 
5     Copyright (C) 1998-2005,2007-2011 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 #include <stdio.h>
30 #include "types.h"
31 #include "filegen.h"
32 #include "common.h"
33 #ifdef DEBUG_RIFF
34 #include "log.h"
35 #endif
36 
37 data_check_t data_check_avi_stream(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery);
38 static void register_header_check_riff(file_stat_t *file_stat);
39 
40 const file_hint_t file_hint_riff= {
41   .extension="riff",
42   .description="RIFF audio/video: wav, cdr, avi",
43   .max_filesize=PHOTOREC_MAX_FILE_SIZE,
44   .recover=1,
45   .enable_by_default=1,
46   .register_header_check=&register_header_check_riff
47 };
48 
49 typedef struct {
50   uint32_t dwFourCC;
51   uint32_t dwSize;
52 //  char data[dwSize];   // contains headers or video/audio data
53 } riff_chunk_header;
54 
55 typedef struct {
56   uint32_t dwList;
57   uint32_t dwSize;
58   uint32_t dwFourCC;
59 //  char data[dwSize-4];
60 } riff_list_header;
61 
62 #ifdef DEBUG_RIFF
log_riff_list(const uint64_t offset,const unsigned int depth,const riff_list_header * list_header)63 static void log_riff_list(const uint64_t offset, const unsigned int depth, const riff_list_header *list_header)
64 {
65   unsigned int i;
66   log_info("0x%08lx - 0x%08lx ", offset, offset + 8 - 1 + le32(list_header->dwSize));
67   for(i = 0; i < depth; i++)
68     log_info(" ");
69   log_info("%c%c%c%c %c%c%c%c 0x%x\n",
70       le32(list_header->dwList),
71       le32(list_header->dwList)>>8,
72       le32(list_header->dwList)>>16,
73       le32(list_header->dwList)>>24,
74       le32(list_header->dwFourCC),
75       le32(list_header->dwFourCC)>>8,
76       le32(list_header->dwFourCC)>>16,
77       le32(list_header->dwFourCC)>>24,
78       le32(list_header->dwSize));
79 }
80 
log_riff_chunk(const uint64_t offset,const unsigned int depth,const riff_list_header * list_header)81 static void log_riff_chunk(const uint64_t offset, const unsigned int depth, const riff_list_header *list_header)
82 {
83   unsigned int i;
84   if(le32(list_header->dwSize)==0)
85     return ;
86   log_info("0x%08lx - 0x%08lx ", offset, offset + 8 - 1 + le32(list_header->dwSize));
87   for(i = 0; i < depth; i++)
88     log_info(" ");
89   log_info("%c%c%c%c 0x%x\n",
90       le32(list_header->dwList),
91       le32(list_header->dwList)>>8,
92       le32(list_header->dwList)>>16,
93       le32(list_header->dwList)>>24,
94       le32(list_header->dwSize));
95 }
96 #endif
97 
check_riff_list(file_recovery_t * fr,const unsigned int depth,const uint64_t start,const uint64_t end)98 static void check_riff_list(file_recovery_t *fr, const unsigned int depth, const uint64_t start, const uint64_t end)
99 {
100   uint64_t file_size;
101   riff_list_header list_header;
102   if(depth>5)
103     return;
104   for(file_size=start; file_size < end;)
105   {
106     if(my_fseek(fr->handle, file_size, SEEK_SET)<0)
107     {
108       fr->offset_error=file_size;
109       return;
110     }
111     if (fread(&list_header, sizeof(list_header), 1, fr->handle)!=1)
112     {
113       fr->offset_error=file_size;
114       return;
115     }
116     if(memcmp(&list_header.dwList, "LIST", 4) == 0)
117     {
118 #ifdef DEBUG_RIFF
119       log_riff_list(file_size, depth, &list_header);
120 #endif
121       check_riff_list(fr, depth+1, file_size + sizeof(list_header), file_size + 8 - 1 + le32(list_header.dwSize));
122     }
123     else
124     {
125 #ifdef DEBUG_RIFF
126       /* It's a chunk */
127       log_riff_chunk(file_size, depth, &list_header);
128 #endif
129     }
130     file_size += (uint64_t)8 + le32(list_header.dwSize);
131     /* align to word boundary */
132     file_size += (file_size&1);
133   }
134 }
135 
file_check_avi(file_recovery_t * fr)136 static void file_check_avi(file_recovery_t *fr)
137 {
138   fr->file_size = 0;
139   fr->offset_error=0;
140   fr->offset_ok=0;
141   while(fr->file_size!=fr->calculated_file_size)
142   {
143     const uint64_t file_size=fr->file_size;
144     riff_list_header list_header;
145     if(my_fseek(fr->handle, fr->file_size, SEEK_SET)<0)
146     {
147       fr->file_size=0;
148       return ;
149     }
150     if (fread(&list_header, sizeof(list_header), 1, fr->handle)!=1)
151     {
152       fr->file_size=0;
153       return;
154     }
155 #ifdef DEBUG_RIFF
156     log_riff_list(file_size, 0, &list_header);
157 #endif
158     if(memcmp(&list_header.dwList, "RIFF", 4) != 0)
159     {
160       fr->offset_error=fr->file_size;
161       return;
162     }
163     check_riff_list(fr, 1, file_size + sizeof(list_header), file_size + 8 - 1 + le32(list_header.dwSize));
164     if(fr->offset_error > 0)
165     {
166       fr->file_size=0;
167       return;
168     }
169     fr->file_size=file_size + 8 + le32(list_header.dwSize);
170   }
171 }
172 
data_check_avi(const unsigned char * buffer,const unsigned int buffer_size,file_recovery_t * file_recovery)173 static data_check_t data_check_avi(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery)
174 {
175   while(file_recovery->calculated_file_size + buffer_size/2  >= file_recovery->file_size &&
176       file_recovery->calculated_file_size + 12 < file_recovery->file_size + buffer_size/2)
177   {
178     const unsigned int i=file_recovery->calculated_file_size - file_recovery->file_size + buffer_size/2;
179     const riff_chunk_header *chunk_header=(const riff_chunk_header*)&buffer[i];
180     if(memcmp(&buffer[i], "RIFF", 4)==0 && memcmp(&buffer[i+8], "AVIX", 4)==0)
181       file_recovery->calculated_file_size += (uint64_t)8 + le32(chunk_header->dwSize);
182     else
183       return DC_STOP;
184   }
185   return DC_CONTINUE;
186 }
187 
data_check_avi_stream(const unsigned char * buffer,const unsigned int buffer_size,file_recovery_t * file_recovery)188 data_check_t data_check_avi_stream(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery)
189 {
190   while(file_recovery->calculated_file_size + buffer_size/2  >= file_recovery->file_size &&
191       file_recovery->calculated_file_size + 8 < file_recovery->file_size + buffer_size/2)
192   {
193     const unsigned int i=file_recovery->calculated_file_size - file_recovery->file_size + buffer_size/2;
194     const riff_chunk_header *chunk_header=(const riff_chunk_header*)&buffer[i];
195     if(buffer[i+2]!='d' || buffer[i+3]!='b')	/* Video Data Binary ?*/
196     {
197 #ifdef DEBUG_RIFF
198       log_info("data_check_avi_stream stop\n");
199 #endif
200       return DC_STOP;
201     }
202     file_recovery->calculated_file_size += (uint64_t)8 + le32(chunk_header->dwSize);
203 #ifdef DEBUG_RIFF
204     log_info("data_check_avi_stream %llu\n", (long long unsigned)file_recovery->calculated_file_size);
205 #endif
206   }
207   return DC_CONTINUE;
208 }
209 
file_check_size_rifx(file_recovery_t * file_recovery)210 static void file_check_size_rifx(file_recovery_t *file_recovery)
211 {
212   if(file_recovery->file_size<file_recovery->calculated_file_size)
213     file_recovery->file_size=0;
214 }
215 
header_check_riff(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)216 static int header_check_riff(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)
217 {
218   uint64_t size;
219   if(!( buffer[8]>='A' && buffer[8]<='Z' &&
220 	buffer[9]>='A' && buffer[9]<='Z' &&
221 	buffer[10]>='A' && buffer[10]<='Z' &&
222 	((buffer[11]>='A' && buffer[11]<='Z') || buffer[11]==' ' ||
223 	 (buffer[11]>='0' && buffer[11]<='9'))))
224     return 0;
225   if(memcmp(&buffer[8],"NUND",4)==0)
226   {
227     /* Cubase Project File */
228     reset_file_recovery(file_recovery_new);
229     file_recovery_new->extension="cpr";
230     file_recovery_new->file_check=&file_check_size;
231     file_recovery_new->data_check=&data_check_size;
232     file_recovery_new->calculated_file_size=(((uint64_t)buffer[4])<<24) +
233       (((uint64_t)buffer[5])<<16) + (((uint64_t)buffer[6])<<8) +
234       (uint64_t)buffer[7] + 12;
235     return 1;
236   }
237   size=(uint64_t)buffer[4]+(((uint64_t)buffer[5])<<8)+(((uint64_t)buffer[6])<<16)+(((uint64_t)buffer[7])<<24);
238 
239   /* Windows Animated Cursor */
240   if(memcmp(&buffer[8],"ACON",4)==0)
241   {
242     if(size < 12)
243       return 0;
244     reset_file_recovery(file_recovery_new);
245     file_recovery_new->file_check=&file_check_size;
246     file_recovery_new->data_check=&data_check_size;
247     file_recovery_new->calculated_file_size=size;
248     file_recovery_new->extension="ani";
249     return 1;
250   }
251   size+=8;
252   if(memcmp(&buffer[8],"AVI ",4)==0)
253   {
254     const riff_list_header list_movi={
255       .dwList=be32(0x4c495354),	/* LIST */
256       .dwSize=le32(4),
257       .dwFourCC=be32(0x6d6f7669)	/* movi */
258     };
259     reset_file_recovery(file_recovery_new);
260     file_recovery_new->extension="avi";
261     /* Is it a raw avi stream with Data Binary chunks ? */
262     if(size < buffer_size - 4 &&
263 	memcmp(&buffer[size - sizeof(list_movi)], &list_movi, sizeof(list_movi)) ==0 &&
264 	buffer[size+2]=='d' &&
265 	buffer[size+3]=='b')
266     {
267       if(file_recovery_new->blocksize < 8)
268 	return 1;
269       file_recovery_new->data_check=&data_check_avi_stream;
270       file_recovery_new->file_check=&file_check_size_max;
271     }
272     else
273     {
274       if(file_recovery_new->blocksize < 12)
275 	return 1;
276       file_recovery_new->data_check=&data_check_avi;
277       file_recovery_new->file_check=&file_check_avi;
278     }
279     file_recovery_new->calculated_file_size=size;
280     return 1;
281   }
282   if(size < 12)
283     return 0;
284   reset_file_recovery(file_recovery_new);
285   file_recovery_new->calculated_file_size=size;
286   file_recovery_new->file_check=&file_check_size;
287   file_recovery_new->data_check=&data_check_size;
288   if(memcmp(&buffer[8],"CDDA",4)==0)
289     file_recovery_new->extension="cda";
290   else if(memcmp(&buffer[8],"CDR",3)==0 || memcmp(&buffer[8],"cdr6",4)==0)
291     file_recovery_new->extension="cdr";
292   else if(memcmp(&buffer[8],"RMP3",4)==0 || memcmp(&buffer[8],"WAVE",4)==0)
293     file_recovery_new->extension="wav";
294   /* MIDI sound file */
295   else if(memcmp(&buffer[8],"RMID",4)==0)
296     file_recovery_new->extension="mid";
297   /* MIDI Instruments Definition File */
298   else if(memcmp(&buffer[8],"IDF LIST",8)==0)
299     file_recovery_new->extension="idf";
300   /* Autogen http://www.fsdeveloper.com/wiki/index.php?title=AGN_%28FSX%29 */
301   else if(memcmp(&buffer[8],"AGNX",4)==0)
302     file_recovery_new->extension="agn";
303   /* http://www.fsdeveloper.com/wiki/index.php?title=MDL_file_format_%28FSX%29 */
304   else if(memcmp(&buffer[8],"MDLX",4)==0)
305     file_recovery_new->extension="mdl";
306   /* RFC3625  The QCP File Format and Media Types for Speech Data */
307   else if(memcmp(&buffer[8],"QLCM",4)==0)
308     file_recovery_new->extension="qcp";
309   /* https://en.wikipedia.org/wiki/WebP */
310   else if(memcmp(&buffer[8],"WEBP",4)==0)
311     file_recovery_new->extension="webp";
312   else
313     file_recovery_new->extension="avi";
314   return 1;
315 }
316 
header_check_rifx(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)317 static int header_check_rifx(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)
318 {
319   if(memcmp(&buffer[8],"Egg!",4)==0)
320   {
321     /* After Effects */
322     reset_file_recovery(file_recovery_new);
323     file_recovery_new->file_check=&file_check_size_rifx;
324     file_recovery_new->calculated_file_size=(uint64_t)buffer[7]+(((uint64_t)buffer[6])<<8)+(((uint64_t)buffer[5])<<16)+(((uint64_t)buffer[4])<<24)+8;
325     file_recovery_new->extension="aep";
326     return 1;
327   }
328   return 0;
329 }
330 
register_header_check_riff(file_stat_t * file_stat)331 static void register_header_check_riff(file_stat_t *file_stat)
332 {
333   register_header_check(0, "RIFF", 4, &header_check_riff, file_stat);
334   register_header_check(0, "RIFX", 4, &header_check_rifx, file_stat);
335 }
336