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=®ister_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