1 /*
2  * Chunk functions
3  *
4  * Copyright (C) 2011-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <memory.h>
24 #include <types.h>
25 
26 #include "libevtx_byte_stream.h"
27 #include "libevtx_checksum.h"
28 #include "libevtx_chunk.h"
29 #include "libevtx_definitions.h"
30 #include "libevtx_io_handle.h"
31 #include "libevtx_libbfio.h"
32 #include "libevtx_libcdata.h"
33 #include "libevtx_libcerror.h"
34 #include "libevtx_libcnotify.h"
35 #include "libevtx_record_values.h"
36 
37 #include "evtx_chunk.h"
38 #include "evtx_event_record.h"
39 
40 const uint8_t *evtx_chunk_signature = (uint8_t *) "ElfChnk";
41 
42 /* Creates a chunk
43  * Make sure the value chunk is referencing, is set to NULL
44  * Returns 1 if successful or -1 on error
45  */
libevtx_chunk_initialize(libevtx_chunk_t ** chunk,libcerror_error_t ** error)46 int libevtx_chunk_initialize(
47      libevtx_chunk_t **chunk,
48      libcerror_error_t **error )
49 {
50 	static char *function = "libevtx_chunk_initialize";
51 
52 	if( chunk == NULL )
53 	{
54 		libcerror_error_set(
55 		 error,
56 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
57 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
58 		 "%s: invalid chunk.",
59 		 function );
60 
61 		return( -1 );
62 	}
63 	if( *chunk != NULL )
64 	{
65 		libcerror_error_set(
66 		 error,
67 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
68 		 LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
69 		 "%s: invalid chunk value already set.",
70 		 function );
71 
72 		return( -1 );
73 	}
74 	*chunk = memory_allocate_structure(
75 	          libevtx_chunk_t );
76 
77 	if( *chunk == NULL )
78 	{
79 		libcerror_error_set(
80 		 error,
81 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
82 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
83 		 "%s: unable to create chunk.",
84 		 function );
85 
86 		goto on_error;
87 	}
88 	if( memory_set(
89 	     *chunk,
90 	     0,
91 	     sizeof( libevtx_chunk_t ) ) == NULL )
92 	{
93 		libcerror_error_set(
94 		 error,
95 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
96 		 LIBCERROR_MEMORY_ERROR_SET_FAILED,
97 		 "%s: unable to clear chunk.",
98 		 function );
99 
100 		memory_free(
101 		 *chunk );
102 
103 		*chunk = NULL;
104 
105 		return( -1 );
106 	}
107 	if( libcdata_array_initialize(
108 	     &( ( *chunk )->records_array ),
109 	     0,
110 	     error ) != 1 )
111 	{
112 		libcerror_error_set(
113 		 error,
114 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
115 		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
116 		 "%s: unable to create chunk records array.",
117 		 function );
118 
119 		goto on_error;
120 	}
121 	if( libcdata_array_initialize(
122 	     &( ( *chunk )->recovered_records_array ),
123 	     0,
124 	     error ) != 1 )
125 	{
126 		libcerror_error_set(
127 		 error,
128 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
129 		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
130 		 "%s: unable to create chunk recovered records array.",
131 		 function );
132 
133 		goto on_error;
134 	}
135 	return( 1 );
136 
137 on_error:
138 	if( *chunk != NULL )
139 	{
140 		if( ( *chunk )->records_array != NULL )
141 		{
142 			libcdata_array_free(
143 			 &( ( *chunk )->records_array ),
144 			 NULL,
145 			 NULL );
146 		}
147 		memory_free(
148 		 *chunk );
149 
150 		*chunk = NULL;
151 	}
152 	return( -1 );
153 }
154 
155 /* Frees a chunk
156  * Returns 1 if successful or -1 on error
157  */
libevtx_chunk_free(libevtx_chunk_t ** chunk,libcerror_error_t ** error)158 int libevtx_chunk_free(
159      libevtx_chunk_t **chunk,
160      libcerror_error_t **error )
161 {
162 	static char *function = "libevtx_chunk_free";
163 	int result            = 1;
164 
165 	if( chunk == NULL )
166 	{
167 		libcerror_error_set(
168 		 error,
169 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
170 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
171 		 "%s: invalid chunk.",
172 		 function );
173 
174 		return( -1 );
175 	}
176 	if( *chunk != NULL )
177 	{
178 		if( libcdata_array_free(
179 		     &( ( *chunk )->recovered_records_array ),
180 		     (int (*)(intptr_t **, libcerror_error_t **)) &libevtx_record_values_free,
181 		     error ) != 1 )
182 		{
183 			libcerror_error_set(
184 			 error,
185 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
186 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
187 			 "%s: unable to free the chunk recovered records array.",
188 			 function );
189 
190 			result = -1;
191 		}
192 		if( libcdata_array_free(
193 		     &( ( *chunk )->records_array ),
194 		     (int (*)(intptr_t **, libcerror_error_t **)) &libevtx_record_values_free,
195 		     error ) != 1 )
196 		{
197 			libcerror_error_set(
198 			 error,
199 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
200 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
201 			 "%s: unable to free the chunk records array.",
202 			 function );
203 
204 			result = -1;
205 		}
206 		if( ( *chunk )->data != NULL )
207 		{
208 			memory_free(
209 			 ( *chunk )->data );
210 		}
211 		memory_free(
212 		 *chunk );
213 
214 		*chunk = NULL;
215 	}
216 	return( result );
217 }
218 
219 /* Reads the chunk
220  * Returns 1 if successful, 0 if the chunk is 0-byte filled or -1 on error
221  */
libevtx_chunk_read(libevtx_chunk_t * chunk,libevtx_io_handle_t * io_handle,libbfio_handle_t * file_io_handle,off64_t file_offset,libcerror_error_t ** error)222 int libevtx_chunk_read(
223      libevtx_chunk_t *chunk,
224      libevtx_io_handle_t *io_handle,
225      libbfio_handle_t *file_io_handle,
226      off64_t file_offset,
227      libcerror_error_t **error )
228 {
229 	libevtx_record_values_t *record_values      = NULL;
230 	uint8_t *chunk_data                         = NULL;
231 	static char *function                       = "libevtx_chunk_read";
232 	size_t chunk_data_offset                    = 0;
233 	size_t chunk_data_size                      = 0;
234 	size_t xml_data_offset                      = 0;
235 	size_t xml_data_size                        = 0;
236 	ssize_t read_count                          = 0;
237 	uint64_t calculated_number_of_event_records = 0;
238 	uint64_t first_event_record_identifier      = 0;
239 	uint64_t first_event_record_number          = 0;
240 	uint64_t last_event_record_identifier       = 0;
241 	uint64_t last_event_record_number           = 0;
242 	uint64_t number_of_event_records            = 0;
243 	uint32_t calculated_checksum                = 0;
244 	uint32_t event_records_checksum             = 0;
245 	uint32_t free_space_offset                  = 0;
246 	uint32_t header_size                        = 0;
247 	uint32_t last_event_record_offset           = 0;
248 	uint32_t stored_checksum                    = 0;
249 	int entry_index                             = 0;
250 	int result                                  = 0;
251 
252 #if defined( HAVE_DEBUG_OUTPUT ) || defined( HAVE_VERBOSE_OUTPUT )
253 	uint64_t calculated_chunk_number            = 0;
254 #endif
255 #if defined( HAVE_DEBUG_OUTPUT )
256 	ssize_t free_space_size                     = 0;
257 	uint32_t value_32bit                        = 0;
258 #endif
259 
260 	if( chunk == NULL )
261 	{
262 		libcerror_error_set(
263 		 error,
264 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
265 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
266 		 "%s: invalid chunk.",
267 		 function );
268 
269 		return( -1 );
270 	}
271 	if( chunk->data != NULL )
272 	{
273 		libcerror_error_set(
274 		 error,
275 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
276 		 LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
277 		 "%s: invalid chunk data already set.",
278 		 function );
279 
280 		return( -1 );
281 	}
282 	if( io_handle == NULL )
283 	{
284 		libcerror_error_set(
285 		 error,
286 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
287 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
288 		 "%s: invalid IO handle.",
289 		 function );
290 
291 		return( -1 );
292 	}
293 	if( ( io_handle->chunk_size < 4 )
294 	 || ( io_handle->chunk_size > (size_t) MEMORY_MAXIMUM_ALLOCATION_SIZE ) )
295 	{
296 		libcerror_error_set(
297 		 error,
298 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
299 		 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
300 		 "%s: invalid IO handle - invalid chunk size value out of bounds.",
301 		 function );
302 
303 		goto on_error;
304 	}
305 #if defined( HAVE_DEBUG_OUTPUT ) || defined( HAVE_VERBOSE_OUTPUT )
306 	calculated_chunk_number = (uint64_t) ( ( file_offset - io_handle->chunk_size ) / io_handle->chunk_size );
307 #endif
308 	chunk->file_offset = file_offset;
309 
310 	chunk->data = (uint8_t *) memory_allocate(
311 	                           (size_t) io_handle->chunk_size );
312 
313 	if( chunk->data == NULL )
314 	{
315 		libcerror_error_set(
316 		 error,
317 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
318 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
319 		 "%s: unable to create chunk data.",
320 		 function );
321 
322 		goto on_error;
323 	}
324 	chunk->data_size = (size_t) io_handle->chunk_size;
325 
326 #if defined( HAVE_DEBUG_OUTPUT )
327 	if( libcnotify_verbose != 0 )
328 	{
329 		libcnotify_printf(
330 		 "%s: reading chunk: %" PRIu64 " at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
331 		 function,
332 		 calculated_chunk_number,
333 		 file_offset,
334 		 file_offset );
335 	}
336 #endif
337 	read_count = libbfio_handle_read_buffer_at_offset(
338 	              file_io_handle,
339 	              chunk->data,
340 	              chunk->data_size,
341 	              file_offset,
342 	              error );
343 
344 	if( read_count != (ssize_t) chunk->data_size )
345 	{
346 		libcerror_error_set(
347 		 error,
348 		 LIBCERROR_ERROR_DOMAIN_IO,
349 		 LIBCERROR_IO_ERROR_READ_FAILED,
350 		 "%s: unable to read chunk data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
351 		 function,
352 		 file_offset,
353 		 file_offset );
354 
355 		goto on_error;
356 	}
357 	chunk_data      = chunk->data;
358 	chunk_data_size = chunk->data_size;
359 
360 #if defined( HAVE_DEBUG_OUTPUT )
361 	if( libcnotify_verbose != 0 )
362 	{
363 		libcnotify_printf(
364 		 "%s: chunk header data:\n",
365 		 function );
366 		libcnotify_print_data(
367 		 chunk_data,
368 		 sizeof( evtx_chunk_header_t ),
369 		 LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
370 	}
371 #endif
372 	result = libevtx_byte_stream_check_for_zero_byte_fill(
373 	          chunk_data,
374 	          chunk_data_size,
375 	          error );
376 
377 	if( result == -1 )
378 	{
379 		libcerror_error_set(
380 		 error,
381 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
382 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
383 		 "%s: unable to determine of chunk is 0-byte filled.",
384 		 function );
385 
386 		goto on_error;
387 	}
388 	else if( result != 0 )
389 	{
390 		return( 0 );
391 	}
392 	if( memory_compare(
393 	     ( (evtx_chunk_header_t *) chunk_data )->signature,
394 	     evtx_chunk_signature,
395 	     8 ) != 0 )
396 	{
397 #if defined( HAVE_VERBOSE_OUTPUT )
398 		if( libcnotify_verbose != 0 )
399 		{
400 			libcnotify_printf(
401 			 "%s: unsupported chunk signature.\n",
402 			 function );
403 		}
404 #endif
405 		chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
406 	}
407 	else
408 	{
409 		byte_stream_copy_to_uint64_little_endian(
410 		 ( (evtx_chunk_header_t *) chunk_data )->first_event_record_number,
411 		 first_event_record_number );
412 
413 		byte_stream_copy_to_uint64_little_endian(
414 		 ( (evtx_chunk_header_t *) chunk_data )->last_event_record_number,
415 		 last_event_record_number );
416 
417 		byte_stream_copy_to_uint64_little_endian(
418 		 ( (evtx_chunk_header_t *) chunk_data )->first_event_record_identifier,
419 		 first_event_record_identifier );
420 
421 		byte_stream_copy_to_uint64_little_endian(
422 		 ( (evtx_chunk_header_t *) chunk_data )->last_event_record_identifier,
423 		 last_event_record_identifier );
424 
425 		byte_stream_copy_to_uint32_little_endian(
426 		 ( (evtx_chunk_header_t *) chunk_data )->header_size,
427 		 header_size );
428 
429 		byte_stream_copy_to_uint32_little_endian(
430 		 ( (evtx_chunk_header_t *) chunk_data )->last_event_record_offset,
431 		 last_event_record_offset );
432 
433 		byte_stream_copy_to_uint32_little_endian(
434 		 ( (evtx_chunk_header_t *) chunk_data )->free_space_offset,
435 		 free_space_offset );
436 
437 		byte_stream_copy_to_uint32_little_endian(
438 		 ( (evtx_chunk_header_t *) chunk_data )->event_records_checksum,
439 		 event_records_checksum );
440 
441 		byte_stream_copy_to_uint32_little_endian(
442 		 ( (evtx_chunk_header_t *) chunk_data )->checksum,
443 		 stored_checksum );
444 
445 #if defined( HAVE_DEBUG_OUTPUT )
446 		if( libcnotify_verbose != 0 )
447 		{
448 			libcnotify_printf(
449 			 "%s: signature\t\t\t\t\t\t: %c%c%c%c%c%c%c\\x%02x\n",
450 			 function,
451 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 0 ],
452 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 1 ],
453 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 2 ],
454 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 3 ],
455 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 4 ],
456 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 5 ] ,
457 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 6 ],
458 			 ( (evtx_chunk_header_t *) chunk_data )->signature[ 7 ] );
459 
460 			libcnotify_printf(
461 			 "%s: first event record number\t\t\t\t: %" PRIu64 "\n",
462 			 function,
463 			 first_event_record_number );
464 
465 			libcnotify_printf(
466 			 "%s: last event record number\t\t\t\t: %" PRIu64 "\n",
467 			 function,
468 			 last_event_record_number );
469 
470 			libcnotify_printf(
471 			 "%s: first event record identifier\t\t\t: %" PRIu64 "\n",
472 			 function,
473 			 first_event_record_identifier );
474 
475 			libcnotify_printf(
476 			 "%s: last event record identifier\t\t\t: %" PRIu64 "\n",
477 			 function,
478 			 last_event_record_identifier );
479 
480 			libcnotify_printf(
481 			 "%s: header size\t\t\t\t\t\t: %" PRIu32 "\n",
482 			 function,
483 			 header_size );
484 
485 			libcnotify_printf(
486 			 "%s: last event record offset\t\t\t\t: 0x%08" PRIx32 "\n",
487 			 function,
488 			 last_event_record_offset );
489 
490 			libcnotify_printf(
491 			 "%s: free space offset\t\t\t\t\t: 0x%08" PRIx32 "\n",
492 			 function,
493 			 free_space_offset );
494 
495 			libcnotify_printf(
496 			 "%s: event records checksum\t\t\t\t: 0x%08" PRIx32 "\n",
497 			 function,
498 			 event_records_checksum );
499 
500 			libcnotify_printf(
501 			 "%s: unknown1:\n",
502 			 function );
503 			libcnotify_print_data(
504 			 ( (evtx_chunk_header_t *) chunk_data )->unknown1,
505 			 64,
506 			 LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
507 
508 			byte_stream_copy_to_uint32_little_endian(
509 			 ( (evtx_chunk_header_t *) chunk_data )->unknown2,
510 			 value_32bit );
511 			libcnotify_printf(
512 			 "%s: unknown2\t\t\t\t\t\t: 0x%08" PRIx32 "\n",
513 			 function,
514 			 value_32bit );
515 
516 			libcnotify_printf(
517 			 "%s: checksum\t\t\t\t\t\t: 0x%08" PRIx32 "\n",
518 			 function,
519 			 stored_checksum );
520 
521 			libcnotify_printf(
522 			 "\n" );
523 		}
524 #endif /* defined( HAVE_DEBUG_OUTPUT ) */
525 
526 		if( header_size != 128 )
527 		{
528 			libcerror_error_set(
529 			 error,
530 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
531 			 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
532 			 "%s: unsupported header size: %" PRIu32 ".",
533 			 function,
534 			 header_size );
535 
536 			goto on_error;
537 		}
538 		if( libevtx_checksum_calculate_little_endian_crc32(
539 		     &calculated_checksum,
540 		     chunk_data,
541 		     120,
542 		     0,
543 		     error ) != 1 )
544 		{
545 			libcerror_error_set(
546 			 error,
547 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
548 			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
549 			 "%s: unable to calculate CRC-32 checksum.",
550 			 function );
551 
552 			goto on_error;
553 		}
554 		if( libevtx_checksum_calculate_little_endian_crc32(
555 		     &calculated_checksum,
556 		     &( chunk_data[ 128 ] ),
557 		     384,
558 		     calculated_checksum,
559 		     error ) != 1 )
560 		{
561 			libcerror_error_set(
562 			 error,
563 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
564 			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
565 			 "%s: unable to calculate CRC-32 checksum.",
566 			 function );
567 
568 			goto on_error;
569 		}
570 		if( stored_checksum != calculated_checksum )
571 		{
572 #if defined( HAVE_VERBOSE_OUTPUT )
573 			if( libcnotify_verbose != 0 )
574 			{
575 				libcnotify_printf(
576 				 "%s: mismatch in chunk: %" PRIu64 " header CRC-32 checksum ( 0x%08" PRIx32 " != 0x%08" PRIx32 " ).\n",
577 				 function,
578 				 calculated_chunk_number,
579 				 stored_checksum,
580 				 calculated_checksum );
581 			}
582 #endif
583 			chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
584 		}
585 		chunk_data_offset = sizeof( evtx_chunk_header_t );
586 
587 #if defined( HAVE_DEBUG_OUTPUT )
588 		if( libcnotify_verbose != 0 )
589 		{
590 			libcnotify_printf(
591 			 "%s: chunk table data:\n",
592 			 function );
593 			libcnotify_print_data(
594 			 &( chunk_data[ chunk_data_offset ] ),
595 			 384,
596 			 LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
597 		}
598 #endif
599 		chunk_data_offset += 384;
600 /* TODO can free_space_offset be 0 ? */
601 
602 		if( ( free_space_offset < chunk_data_offset )
603 		 || ( free_space_offset > chunk_data_size ) )
604 		{
605 			libcerror_error_set(
606 			 error,
607 			 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
608 			 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
609 			 "%s: invalid free space offset value out of bounds.",
610 			 function );
611 
612 			goto on_error;
613 		}
614 		if( libevtx_checksum_calculate_little_endian_crc32(
615 		     &calculated_checksum,
616 		     &( chunk_data[ 512 ] ),
617 		     free_space_offset - chunk_data_offset,
618 		     0,
619 		     error ) != 1 )
620 		{
621 			libcerror_error_set(
622 			 error,
623 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
624 			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
625 			 "%s: unable to calculate CRC-32 checksum.",
626 			 function );
627 
628 			goto on_error;
629 		}
630 		if( event_records_checksum != calculated_checksum )
631 		{
632 #if defined( HAVE_VERBOSE_OUTPUT )
633 			if( libcnotify_verbose != 0 )
634 			{
635 				libcnotify_printf(
636 				 "%s: mismatch in chunk: %" PRIu64 " event records CRC-32 checksum ( 0x%08" PRIx32 " != 0x%08" PRIx32 " ).\n",
637 				 function,
638 				 calculated_chunk_number,
639 				 event_records_checksum,
640 				 calculated_checksum );
641 			}
642 #endif
643 			chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
644 		}
645 		while( chunk_data_offset <= last_event_record_offset )
646 		{
647 			if( libevtx_record_values_initialize(
648 			     &record_values,
649 			     error ) != 1 )
650 			{
651 				libcerror_error_set(
652 				 error,
653 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
654 				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
655 				 "%s: unable to create record values.",
656 				 function );
657 
658 				goto on_error;
659 			}
660 #if defined( HAVE_DEBUG_OUTPUT )
661 			if( libcnotify_verbose != 0 )
662 			{
663 				libcnotify_printf(
664 				 "%s: reading record at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
665 				 function,
666 				 file_offset + chunk_data_offset,
667 				 file_offset + chunk_data_offset );
668 			}
669 #endif
670 			result = libevtx_record_values_read_header(
671 				  record_values,
672 				  io_handle,
673 				  chunk_data,
674 				  chunk_data_size,
675 				  chunk_data_offset,
676 				  error );
677 
678 			if( result == -1 )
679 			{
680 				libcerror_error_set(
681 				 error,
682 				 LIBCERROR_ERROR_DOMAIN_IO,
683 				 LIBCERROR_IO_ERROR_READ_FAILED,
684 				 "%s: unable to read record values header at offset: %" PRIi64 ".",
685 				 function,
686 				 file_offset + chunk_data_offset );
687 
688 #if defined( HAVE_DEBUG_OUTPUT )
689 				if( libcnotify_verbose != 0 )
690 				{
691 					if( ( error != NULL )
692 					 && ( *error != NULL ) )
693 					{
694 						libcnotify_print_error_backtrace(
695 						 *error );
696 					}
697 				}
698 #endif
699 				libcerror_error_free(
700 				 error );
701 			}
702 			if( result != 1 )
703 			{
704 				break;
705 			}
706 			chunk_data_offset += record_values->data_size;
707 
708 			if( libcdata_array_append_entry(
709 			     chunk->records_array,
710 			     &entry_index,
711 			     (intptr_t *) record_values,
712 			     error ) != 1 )
713 			{
714 				libcerror_error_set(
715 				 error,
716 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
717 				 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
718 				 "%s: unable to append record values to records array.",
719 				 function );
720 
721 				goto on_error;
722 			}
723 			record_values = NULL;
724 
725 			number_of_event_records++;
726 		}
727 		if( first_event_record_number > last_event_record_number )
728 		{
729 #if defined( HAVE_VERBOSE_OUTPUT )
730 			if( libcnotify_verbose != 0 )
731 			{
732 				libcnotify_printf(
733 				 "%s: invalid chunk: %" PRIu64 " first event record number: %" PRIu64 " exceeds last event record number: %" PRIu64 ".\n",
734 				 function,
735 				 calculated_chunk_number,
736 				 first_event_record_number,
737 				 last_event_record_number );
738 			}
739 #endif
740 			chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
741 		}
742 		else if( result == 1 )
743 		{
744 			calculated_number_of_event_records = last_event_record_number - first_event_record_number + 1;
745 
746 #if defined( HAVE_DEBUG_OUTPUT )
747 			if( libcnotify_verbose != 0 )
748 			{
749 				libcnotify_printf(
750 				 "%s: calculated number of records\t\t\t: %" PRIu64 "\n",
751 				 function,
752 				 calculated_number_of_event_records );
753 			}
754 #endif
755 			if( number_of_event_records != calculated_number_of_event_records )
756 			{
757 #if defined( HAVE_VERBOSE_OUTPUT )
758 				if( libcnotify_verbose != 0 )
759 				{
760 					libcnotify_printf(
761 					 "%s: mismatch in chunk: %" PRIu64 " number of event records ( %" PRIu64 " != %" PRIu64 " ).\n",
762 					 function,
763 					 calculated_chunk_number,
764 					 number_of_event_records,
765 					 calculated_number_of_event_records );
766 				}
767 #endif
768 				chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
769 			}
770 		}
771 		if( first_event_record_identifier > last_event_record_identifier )
772 		{
773 #if defined( HAVE_VERBOSE_OUTPUT )
774 			if( libcnotify_verbose != 0 )
775 			{
776 				libcnotify_printf(
777 				 "%s: in chunk: %" PRIu64 " first event record identifier: %" PRIu64 " exceeds last event record identifier: %" PRIu64 ".\n",
778 				 function,
779 				 calculated_chunk_number,
780 				 first_event_record_identifier,
781 				 last_event_record_identifier );
782 			}
783 #endif
784 			/* TODO mark this as corruption ? */
785 		}
786 	}
787 	if( chunk_data_offset < chunk_data_size )
788 	{
789 #if defined( HAVE_DEBUG_OUTPUT )
790 		free_space_size = chunk_data_size - chunk_data_offset;
791 
792 		if( libcnotify_verbose != 0 )
793 		{
794 			libcnotify_printf(
795 			 "%s: free space data:\n",
796 			 function );
797 			libcnotify_print_data(
798 			 &( chunk_data[ chunk_data_offset ] ),
799 			 free_space_size,
800 			 LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
801 		}
802 #endif
803 		while( chunk_data_offset < ( chunk_data_size - 4 ) )
804 		{
805 /* TODO optimize scan ? */
806 			if( memory_compare(
807 			     &( chunk_data[ chunk_data_offset ] ),
808 			     evtx_event_record_signature,
809 			     4 ) == 0 )
810 			{
811 				if( record_values == NULL )
812 				{
813 					if( libevtx_record_values_initialize(
814 					     &record_values,
815 					     error ) != 1 )
816 					{
817 						libcerror_error_set(
818 						 error,
819 						 LIBCERROR_ERROR_DOMAIN_RUNTIME,
820 						 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
821 						 "%s: unable to create record values.",
822 						 function );
823 
824 						goto on_error;
825 					}
826 				}
827 #if defined( HAVE_DEBUG_OUTPUT )
828 				if( libcnotify_verbose != 0 )
829 				{
830 					libcnotify_printf(
831 					 "%s: reading recovered record at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
832 					 function,
833 					 file_offset + chunk_data_offset,
834 					 file_offset + chunk_data_offset );
835 				}
836 #endif
837 				if( libevtx_record_values_read_header(
838 				     record_values,
839 				     io_handle,
840 				     chunk_data,
841 				     chunk_data_size,
842 				     chunk_data_offset,
843 				     error ) != 1 )
844 				{
845 					libcerror_error_set(
846 					 error,
847 					 LIBCERROR_ERROR_DOMAIN_IO,
848 					 LIBCERROR_IO_ERROR_READ_FAILED,
849 					 "%s: unable to read record values header at offset: %" PRIi64 ".",
850 					 function,
851 					 file_offset + chunk_data_offset );
852 
853 #if defined( HAVE_DEBUG_OUTPUT )
854 					if( libcnotify_verbose != 0 )
855 					{
856 						if( ( error != NULL )
857 						 && ( *error != NULL ) )
858 						{
859 							libcnotify_print_error_backtrace(
860 							 *error );
861 						}
862 					}
863 #endif
864 					libcerror_error_free(
865 					 error );
866 				}
867 				else
868 				{
869 					xml_data_offset = chunk_data_offset + sizeof( evtx_event_record_header_t );
870 					xml_data_size   = 0;
871 
872 					if( record_values->data_size > ( sizeof( evtx_event_record_header_t ) + 4 ) )
873 					{
874 						xml_data_size = record_values->data_size - ( sizeof( evtx_event_record_header_t ) + 4 );
875 					}
876 					result = 0;
877 
878 					if( xml_data_size > 0 )
879 					{
880 						if( ( xml_data_size >= 5 )
881 						 && ( chunk_data[ xml_data_offset ] == 0x0a ) )
882 						{
883 							result = 1;
884 						}
885 						else if( ( xml_data_size >= 4 )
886 						      && ( chunk_data[ xml_data_offset ] == 0x0f )
887 						      && ( chunk_data[ xml_data_offset + 1 ] == 0x01 )
888 						      && ( chunk_data[ xml_data_offset + 2 ] == 0x01 )
889 						      && ( chunk_data[ xml_data_offset + 3 ] == 0x00 ) )
890 						{
891 							result = 1;
892 						}
893 /* TODO what about 0x00 allow it ? */
894 					}
895 					if( result != 0 )
896 					{
897 						chunk_data_offset += record_values->data_size - 4;
898 
899 						if( libcdata_array_append_entry(
900 						     chunk->recovered_records_array,
901 						     &entry_index,
902 						     (intptr_t *) record_values,
903 						     error ) != 1 )
904 						{
905 							libcerror_error_set(
906 							 error,
907 							 LIBCERROR_ERROR_DOMAIN_RUNTIME,
908 							 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
909 							 "%s: unable to append record values to recovered records array.",
910 							 function );
911 
912 							goto on_error;
913 						}
914 						record_values = NULL;
915 					}
916 				}
917 			}
918 			chunk_data_offset += 4;
919 		}
920 		if( record_values != NULL )
921 		{
922 			if( libevtx_record_values_free(
923 			     &record_values,
924 			     error ) != 1 )
925 			{
926 				libcerror_error_set(
927 				 error,
928 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
929 				 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
930 				 "%s: unable to free record values.",
931 				 function );
932 
933 				goto on_error;
934 			}
935 		}
936 	}
937 	return( 1 );
938 
939 on_error:
940 	if( record_values != NULL )
941 	{
942 		libevtx_record_values_free(
943 		 &record_values,
944 		 NULL );
945 	}
946 	if( chunk->data != NULL )
947 	{
948 		memory_free(
949 		 chunk->data );
950 
951 		chunk->data = NULL;
952 	}
953 	return( -1 );
954 }
955 
956 /* Retrieves the number of records
957  * Returns 1 if successful or -1 on error
958  */
libevtx_chunk_get_number_of_records(libevtx_chunk_t * chunk,uint16_t * number_of_records,libcerror_error_t ** error)959 int libevtx_chunk_get_number_of_records(
960      libevtx_chunk_t *chunk,
961      uint16_t *number_of_records,
962      libcerror_error_t **error )
963 {
964 	static char *function       = "libevtx_chunk_get_number_of_records";
965 	int chunk_number_of_records = 0;
966 
967 	if( chunk == NULL )
968 	{
969 		libcerror_error_set(
970 		 error,
971 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
972 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
973 		 "%s: invalid chunk.",
974 		 function );
975 
976 		return( -1 );
977 	}
978 	if( number_of_records == NULL )
979 	{
980 		libcerror_error_set(
981 		 error,
982 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
983 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
984 		 "%s: invalid number of records.",
985 		 function );
986 
987 		return( -1 );
988 	}
989 	if( libcdata_array_get_number_of_entries(
990 	     chunk->records_array,
991 	     &chunk_number_of_records,
992 	     error ) != 1 )
993 	{
994 		libcerror_error_set(
995 		 error,
996 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
997 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
998 		 "%s: unable to retrieve number of records.",
999 		 function );
1000 
1001 		return( -1 );
1002 	}
1003 	if( chunk_number_of_records > (int) UINT16_MAX )
1004 	{
1005 		libcerror_error_set(
1006 		 error,
1007 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1008 		 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
1009 		 "%s: invalid number of chunk records value exceeds maximum.",
1010 		 function );
1011 
1012 		return( -1 );
1013 	}
1014 	*number_of_records = (uint16_t) chunk_number_of_records;
1015 
1016 	return( 1 );
1017 }
1018 
1019 /* Retrieves the record at the index
1020  * Returns 1 if successful or -1 on error
1021  */
libevtx_chunk_get_record(libevtx_chunk_t * chunk,uint16_t record_index,libevtx_record_values_t ** record_values,libcerror_error_t ** error)1022 int libevtx_chunk_get_record(
1023      libevtx_chunk_t *chunk,
1024      uint16_t record_index,
1025      libevtx_record_values_t **record_values,
1026      libcerror_error_t **error )
1027 {
1028 	static char *function = "libevtx_chunk_get_record";
1029 
1030 	if( chunk == NULL )
1031 	{
1032 		libcerror_error_set(
1033 		 error,
1034 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1035 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1036 		 "%s: invalid chunk.",
1037 		 function );
1038 
1039 		return( -1 );
1040 	}
1041 	if( libcdata_array_get_entry_by_index(
1042 	     chunk->records_array,
1043 	     (int) record_index,
1044 	     (intptr_t **) record_values,
1045 	     error ) != 1 )
1046 	{
1047 		libcerror_error_set(
1048 		 error,
1049 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1050 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1051 		 "%s: unable to retrieve record: %" PRIu16 ".",
1052 		 function,
1053 		 record_index );
1054 
1055 		return( -1 );
1056 	}
1057 	return( 1 );
1058 }
1059 
1060 /* Retrieves the number of recovered records
1061  * Returns 1 if successful or -1 on error
1062  */
libevtx_chunk_get_number_of_recovered_records(libevtx_chunk_t * chunk,uint16_t * number_of_records,libcerror_error_t ** error)1063 int libevtx_chunk_get_number_of_recovered_records(
1064      libevtx_chunk_t *chunk,
1065      uint16_t *number_of_records,
1066      libcerror_error_t **error )
1067 {
1068 	static char *function       = "libevtx_chunk_get_number_of_recovered_records";
1069 	int chunk_number_of_records = 0;
1070 
1071 	if( chunk == NULL )
1072 	{
1073 		libcerror_error_set(
1074 		 error,
1075 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1076 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1077 		 "%s: invalid chunk.",
1078 		 function );
1079 
1080 		return( -1 );
1081 	}
1082 	if( number_of_records == NULL )
1083 	{
1084 		libcerror_error_set(
1085 		 error,
1086 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1087 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1088 		 "%s: invalid number of records.",
1089 		 function );
1090 
1091 		return( -1 );
1092 	}
1093 	if( libcdata_array_get_number_of_entries(
1094 	     chunk->recovered_records_array,
1095 	     &chunk_number_of_records,
1096 	     error ) != 1 )
1097 	{
1098 		libcerror_error_set(
1099 		 error,
1100 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1101 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1102 		 "%s: unable to retrieve number of records.",
1103 		 function );
1104 
1105 		return( -1 );
1106 	}
1107 	if( chunk_number_of_records > (int) UINT16_MAX )
1108 	{
1109 		libcerror_error_set(
1110 		 error,
1111 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1112 		 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
1113 		 "%s: invalid number of chunk records value exceeds maximum.",
1114 		 function );
1115 
1116 		return( -1 );
1117 	}
1118 	*number_of_records = (uint16_t) chunk_number_of_records;
1119 
1120 	return( 1 );
1121 }
1122 
1123 /* Retrieves the recovered record at the index
1124  * Returns 1 if successful or -1 on error
1125  */
libevtx_chunk_get_recovered_record(libevtx_chunk_t * chunk,uint16_t record_index,libevtx_record_values_t ** record_values,libcerror_error_t ** error)1126 int libevtx_chunk_get_recovered_record(
1127      libevtx_chunk_t *chunk,
1128      uint16_t record_index,
1129      libevtx_record_values_t **record_values,
1130      libcerror_error_t **error )
1131 {
1132 	static char *function = "libevtx_chunk_get_recovered_record";
1133 
1134 	if( chunk == NULL )
1135 	{
1136 		libcerror_error_set(
1137 		 error,
1138 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1139 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1140 		 "%s: invalid chunk.",
1141 		 function );
1142 
1143 		return( -1 );
1144 	}
1145 	if( libcdata_array_get_entry_by_index(
1146 	     chunk->recovered_records_array,
1147 	     (int) record_index,
1148 	     (intptr_t **) record_values,
1149 	     error ) != 1 )
1150 	{
1151 		libcerror_error_set(
1152 		 error,
1153 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
1154 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1155 		 "%s: unable to retrieve record: %" PRIu16 ".",
1156 		 function,
1157 		 record_index );
1158 
1159 		return( -1 );
1160 	}
1161 	return( 1 );
1162 }
1163 
1164