Coverage Report

Created: 2026-01-20 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libevtx/libevtx/libevtx_chunk.c
Line
Count
Source
1
/*
2
 * Chunk functions
3
 *
4
 * Copyright (C) 2011-2025, 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
 */
46
int libevtx_chunk_initialize(
47
     libevtx_chunk_t **chunk,
48
     libcerror_error_t **error )
49
5.14k
{
50
5.14k
  static char *function = "libevtx_chunk_initialize";
51
52
5.14k
  if( chunk == NULL )
53
0
  {
54
0
    libcerror_error_set(
55
0
     error,
56
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
57
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
58
0
     "%s: invalid chunk.",
59
0
     function );
60
61
0
    return( -1 );
62
0
  }
63
5.14k
  if( *chunk != NULL )
64
0
  {
65
0
    libcerror_error_set(
66
0
     error,
67
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
68
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
69
0
     "%s: invalid chunk value already set.",
70
0
     function );
71
72
0
    return( -1 );
73
0
  }
74
5.14k
  *chunk = memory_allocate_structure(
75
5.14k
            libevtx_chunk_t );
76
77
5.14k
  if( *chunk == NULL )
78
0
  {
79
0
    libcerror_error_set(
80
0
     error,
81
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
82
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
83
0
     "%s: unable to create chunk.",
84
0
     function );
85
86
0
    goto on_error;
87
0
  }
88
5.14k
  if( memory_set(
89
5.14k
       *chunk,
90
5.14k
       0,
91
5.14k
       sizeof( libevtx_chunk_t ) ) == NULL )
92
0
  {
93
0
    libcerror_error_set(
94
0
     error,
95
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
96
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
97
0
     "%s: unable to clear chunk.",
98
0
     function );
99
100
0
    memory_free(
101
0
     *chunk );
102
103
0
    *chunk = NULL;
104
105
0
    return( -1 );
106
0
  }
107
5.14k
  if( libcdata_array_initialize(
108
5.14k
       &( ( *chunk )->records_array ),
109
5.14k
       0,
110
5.14k
       error ) != 1 )
111
0
  {
112
0
    libcerror_error_set(
113
0
     error,
114
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
115
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
116
0
     "%s: unable to create chunk records array.",
117
0
     function );
118
119
0
    goto on_error;
120
0
  }
121
5.14k
  if( libcdata_array_initialize(
122
5.14k
       &( ( *chunk )->recovered_records_array ),
123
5.14k
       0,
124
5.14k
       error ) != 1 )
125
0
  {
126
0
    libcerror_error_set(
127
0
     error,
128
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
129
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
130
0
     "%s: unable to create chunk recovered records array.",
131
0
     function );
132
133
0
    goto on_error;
134
0
  }
135
5.14k
  return( 1 );
136
137
0
on_error:
138
0
  if( *chunk != NULL )
139
0
  {
140
0
    if( ( *chunk )->records_array != NULL )
141
0
    {
142
0
      libcdata_array_free(
143
0
       &( ( *chunk )->records_array ),
144
0
       NULL,
145
0
       NULL );
146
0
    }
147
0
    memory_free(
148
0
     *chunk );
149
150
0
    *chunk = NULL;
151
0
  }
152
0
  return( -1 );
153
5.14k
}
154
155
/* Frees a chunk
156
 * Returns 1 if successful or -1 on error
157
 */
158
int libevtx_chunk_free(
159
     libevtx_chunk_t **chunk,
160
     libcerror_error_t **error )
161
5.14k
{
162
5.14k
  static char *function = "libevtx_chunk_free";
163
5.14k
  int result            = 1;
164
165
5.14k
  if( chunk == NULL )
166
0
  {
167
0
    libcerror_error_set(
168
0
     error,
169
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
170
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
171
0
     "%s: invalid chunk.",
172
0
     function );
173
174
0
    return( -1 );
175
0
  }
176
5.14k
  if( *chunk != NULL )
177
5.14k
  {
178
5.14k
    if( libcdata_array_free(
179
5.14k
         &( ( *chunk )->recovered_records_array ),
180
5.14k
         (int (*)(intptr_t **, libcerror_error_t **)) &libevtx_record_values_free,
181
5.14k
         error ) != 1 )
182
0
    {
183
0
      libcerror_error_set(
184
0
       error,
185
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
186
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
187
0
       "%s: unable to free the chunk recovered records array.",
188
0
       function );
189
190
0
      result = -1;
191
0
    }
192
5.14k
    if( libcdata_array_free(
193
5.14k
         &( ( *chunk )->records_array ),
194
5.14k
         (int (*)(intptr_t **, libcerror_error_t **)) &libevtx_record_values_free,
195
5.14k
         error ) != 1 )
196
0
    {
197
0
      libcerror_error_set(
198
0
       error,
199
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
200
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
201
0
       "%s: unable to free the chunk records array.",
202
0
       function );
203
204
0
      result = -1;
205
0
    }
206
5.14k
    if( ( *chunk )->data != NULL )
207
4.97k
    {
208
4.97k
      memory_free(
209
4.97k
       ( *chunk )->data );
210
4.97k
    }
211
5.14k
    memory_free(
212
5.14k
     *chunk );
213
214
5.14k
    *chunk = NULL;
215
5.14k
  }
216
5.14k
  return( result );
217
5.14k
}
218
219
/* Reads the chunk
220
 * Returns 1 if successful, 0 if the chunk is 0-byte filled or -1 on error
221
 */
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
5.14k
{
229
5.14k
  libevtx_record_values_t *record_values      = NULL;
230
5.14k
  uint8_t *chunk_data                         = NULL;
231
5.14k
  static char *function                       = "libevtx_chunk_read";
232
5.14k
  size_t chunk_data_offset                    = 0;
233
5.14k
  size_t chunk_data_size                      = 0;
234
5.14k
  size_t xml_data_offset                      = 0;
235
5.14k
  size_t xml_data_size                        = 0;
236
5.14k
  ssize_t read_count                          = 0;
237
5.14k
  uint64_t calculated_number_of_event_records = 0;
238
5.14k
  uint64_t first_event_record_identifier      = 0;
239
5.14k
  uint64_t first_event_record_number          = 0;
240
5.14k
  uint64_t last_event_record_identifier       = 0;
241
5.14k
  uint64_t last_event_record_number           = 0;
242
5.14k
  uint64_t number_of_event_records            = 0;
243
5.14k
  uint32_t calculated_checksum                = 0;
244
5.14k
  uint32_t event_records_checksum             = 0;
245
5.14k
  uint32_t free_space_offset                  = 0;
246
5.14k
  uint32_t header_size                        = 0;
247
5.14k
  uint32_t last_event_record_offset           = 0;
248
5.14k
  uint32_t stored_checksum                    = 0;
249
5.14k
  int entry_index                             = 0;
250
5.14k
  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
5.14k
  if( chunk == NULL )
261
0
  {
262
0
    libcerror_error_set(
263
0
     error,
264
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
265
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
266
0
     "%s: invalid chunk.",
267
0
     function );
268
269
0
    return( -1 );
270
0
  }
271
5.14k
  if( chunk->data != NULL )
272
0
  {
273
0
    libcerror_error_set(
274
0
     error,
275
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
276
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
277
0
     "%s: invalid chunk data already set.",
278
0
     function );
279
280
0
    return( -1 );
281
0
  }
282
5.14k
  if( io_handle == NULL )
283
0
  {
284
0
    libcerror_error_set(
285
0
     error,
286
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
287
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
288
0
     "%s: invalid IO handle.",
289
0
     function );
290
291
0
    return( -1 );
292
0
  }
293
5.14k
  if( ( io_handle->chunk_size < 4 )
294
5.14k
   || ( io_handle->chunk_size > (size_t) MEMORY_MAXIMUM_ALLOCATION_SIZE ) )
295
0
  {
296
0
    libcerror_error_set(
297
0
     error,
298
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
299
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
300
0
     "%s: invalid IO handle - invalid chunk size value out of bounds.",
301
0
     function );
302
303
0
    goto on_error;
304
0
  }
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
5.14k
  chunk->file_offset = file_offset;
309
310
5.14k
  chunk->data = (uint8_t *) memory_allocate(
311
5.14k
                             (size_t) io_handle->chunk_size );
312
313
5.14k
  if( chunk->data == NULL )
314
0
  {
315
0
    libcerror_error_set(
316
0
     error,
317
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
318
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
319
0
     "%s: unable to create chunk data.",
320
0
     function );
321
322
0
    goto on_error;
323
0
  }
324
5.14k
  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
5.14k
  read_count = libbfio_handle_read_buffer_at_offset(
338
5.14k
                file_io_handle,
339
5.14k
                chunk->data,
340
5.14k
                chunk->data_size,
341
5.14k
                file_offset,
342
5.14k
                error );
343
344
5.14k
  if( read_count != (ssize_t) chunk->data_size )
345
0
  {
346
0
    libcerror_error_set(
347
0
     error,
348
0
     LIBCERROR_ERROR_DOMAIN_IO,
349
0
     LIBCERROR_IO_ERROR_READ_FAILED,
350
0
     "%s: unable to read chunk data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
351
0
     function,
352
0
     file_offset,
353
0
     file_offset );
354
355
0
    goto on_error;
356
0
  }
357
5.14k
  chunk_data      = chunk->data;
358
5.14k
  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
5.14k
  result = libevtx_byte_stream_check_for_zero_byte_fill(
373
5.14k
            chunk_data,
374
5.14k
            chunk_data_size,
375
5.14k
            error );
376
377
5.14k
  if( result == -1 )
378
0
  {
379
0
    libcerror_error_set(
380
0
     error,
381
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
382
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
383
0
     "%s: unable to determine of chunk is 0-byte filled.",
384
0
     function );
385
386
0
    goto on_error;
387
0
  }
388
5.14k
  else if( result != 0 )
389
265
  {
390
265
    return( 0 );
391
265
  }
392
4.87k
  if( memory_compare(
393
4.87k
       ( (evtx_chunk_header_t *) chunk_data )->signature,
394
4.87k
       evtx_chunk_signature,
395
4.87k
       8 ) != 0 )
396
1.25k
  {
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
1.25k
    chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
406
1.25k
  }
407
3.61k
  else
408
3.61k
  {
409
3.61k
    byte_stream_copy_to_uint64_little_endian(
410
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->first_event_record_number,
411
3.61k
     first_event_record_number );
412
413
3.61k
    byte_stream_copy_to_uint64_little_endian(
414
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->last_event_record_number,
415
3.61k
     last_event_record_number );
416
417
3.61k
    byte_stream_copy_to_uint64_little_endian(
418
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->first_event_record_identifier,
419
3.61k
     first_event_record_identifier );
420
421
3.61k
    byte_stream_copy_to_uint64_little_endian(
422
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->last_event_record_identifier,
423
3.61k
     last_event_record_identifier );
424
425
3.61k
    byte_stream_copy_to_uint32_little_endian(
426
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->header_size,
427
3.61k
     header_size );
428
429
3.61k
    byte_stream_copy_to_uint32_little_endian(
430
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->last_event_record_offset,
431
3.61k
     last_event_record_offset );
432
433
3.61k
    byte_stream_copy_to_uint32_little_endian(
434
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->free_space_offset,
435
3.61k
     free_space_offset );
436
437
3.61k
    byte_stream_copy_to_uint32_little_endian(
438
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->event_records_checksum,
439
3.61k
     event_records_checksum );
440
441
3.61k
    byte_stream_copy_to_uint32_little_endian(
442
3.61k
     ( (evtx_chunk_header_t *) chunk_data )->checksum,
443
3.61k
     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
3.61k
    if( header_size != 128 )
527
76
    {
528
76
      libcerror_error_set(
529
76
       error,
530
76
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
531
76
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
532
76
       "%s: unsupported header size: %" PRIu32 ".",
533
76
       function,
534
76
       header_size );
535
536
76
      goto on_error;
537
76
    }
538
3.54k
    if( libevtx_checksum_calculate_little_endian_crc32(
539
3.54k
         &calculated_checksum,
540
3.54k
         chunk_data,
541
3.54k
         120,
542
3.54k
         0,
543
3.54k
         error ) != 1 )
544
0
    {
545
0
      libcerror_error_set(
546
0
       error,
547
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
548
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
549
0
       "%s: unable to calculate CRC-32 checksum.",
550
0
       function );
551
552
0
      goto on_error;
553
0
    }
554
3.54k
    if( libevtx_checksum_calculate_little_endian_crc32(
555
3.54k
         &calculated_checksum,
556
3.54k
         &( chunk_data[ 128 ] ),
557
3.54k
         384,
558
3.54k
         calculated_checksum,
559
3.54k
         error ) != 1 )
560
0
    {
561
0
      libcerror_error_set(
562
0
       error,
563
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
564
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
565
0
       "%s: unable to calculate CRC-32 checksum.",
566
0
       function );
567
568
0
      goto on_error;
569
0
    }
570
3.54k
    if( stored_checksum != calculated_checksum )
571
3.33k
    {
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
3.33k
      chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
584
3.33k
    }
585
3.54k
    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
3.54k
    chunk_data_offset += 384;
600
/* TODO can free_space_offset be 0 ? */
601
602
3.54k
    if( ( free_space_offset < chunk_data_offset )
603
3.53k
     || ( free_space_offset > chunk_data_size ) )
604
92
    {
605
92
      libcerror_error_set(
606
92
       error,
607
92
       LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
608
92
       LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
609
92
       "%s: invalid free space offset value out of bounds.",
610
92
       function );
611
612
92
      goto on_error;
613
92
    }
614
3.45k
    if( libevtx_checksum_calculate_little_endian_crc32(
615
3.45k
         &calculated_checksum,
616
3.45k
         &( chunk_data[ 512 ] ),
617
3.45k
         free_space_offset - chunk_data_offset,
618
3.45k
         0,
619
3.45k
         error ) != 1 )
620
0
    {
621
0
      libcerror_error_set(
622
0
       error,
623
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
624
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
625
0
       "%s: unable to calculate CRC-32 checksum.",
626
0
       function );
627
628
0
      goto on_error;
629
0
    }
630
3.45k
    if( event_records_checksum != calculated_checksum )
631
3.34k
    {
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
3.34k
      chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
644
3.34k
    }
645
33.7k
    while( chunk_data_offset <= last_event_record_offset )
646
33.2k
    {
647
33.2k
      if( libevtx_record_values_initialize(
648
33.2k
           &record_values,
649
33.2k
           error ) != 1 )
650
0
      {
651
0
        libcerror_error_set(
652
0
         error,
653
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
654
0
         LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
655
0
         "%s: unable to create record values.",
656
0
         function );
657
658
0
        goto on_error;
659
0
      }
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
33.2k
      result = libevtx_record_values_read_header(
671
33.2k
          record_values,
672
33.2k
          io_handle,
673
33.2k
          chunk_data,
674
33.2k
          chunk_data_size,
675
33.2k
          chunk_data_offset,
676
33.2k
          error );
677
678
33.2k
      if( result == -1 )
679
2.87k
      {
680
2.87k
        libcerror_error_set(
681
2.87k
         error,
682
2.87k
         LIBCERROR_ERROR_DOMAIN_IO,
683
2.87k
         LIBCERROR_IO_ERROR_READ_FAILED,
684
2.87k
         "%s: unable to read record values header at offset: %" PRIi64 ".",
685
2.87k
         function,
686
2.87k
         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
2.87k
        libcerror_error_free(
700
2.87k
         error );
701
2.87k
      }
702
33.2k
      if( result != 1 )
703
2.95k
      {
704
2.95k
        break;
705
2.95k
      }
706
30.3k
      chunk_data_offset += record_values->data_size;
707
708
30.3k
      if( libcdata_array_append_entry(
709
30.3k
           chunk->records_array,
710
30.3k
           &entry_index,
711
30.3k
           (intptr_t *) record_values,
712
30.3k
           error ) != 1 )
713
0
      {
714
0
        libcerror_error_set(
715
0
         error,
716
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
717
0
         LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
718
0
         "%s: unable to append record values to records array.",
719
0
         function );
720
721
0
        goto on_error;
722
0
      }
723
30.3k
      record_values = NULL;
724
725
30.3k
      number_of_event_records++;
726
30.3k
    }
727
3.45k
    if( first_event_record_number > last_event_record_number )
728
865
    {
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
865
      chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
741
865
    }
742
2.58k
    else if( result == 1 )
743
462
    {
744
462
      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
462
      if( number_of_event_records != calculated_number_of_event_records )
756
374
      {
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
374
        chunk->flags |= LIBEVTX_CHUNK_FLAG_IS_CORRUPTED;
769
374
      }
770
462
    }
771
3.45k
    if( first_event_record_identifier > last_event_record_identifier )
772
722
    {
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
722
    }
786
3.45k
  }
787
4.71k
  if( chunk_data_offset < chunk_data_size )
788
4.71k
  {
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
64.3M
    while( chunk_data_offset < ( chunk_data_size - 4 ) )
804
64.3M
    {
805
/* TODO optimize scan ? */
806
64.3M
      if( memory_compare(
807
64.3M
           &( chunk_data[ chunk_data_offset ] ),
808
64.3M
           evtx_event_record_signature,
809
64.3M
           4 ) == 0 )
810
168k
      {
811
168k
        if( record_values == NULL )
812
100k
        {
813
100k
          if( libevtx_record_values_initialize(
814
100k
               &record_values,
815
100k
               error ) != 1 )
816
0
          {
817
0
            libcerror_error_set(
818
0
             error,
819
0
             LIBCERROR_ERROR_DOMAIN_RUNTIME,
820
0
             LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
821
0
             "%s: unable to create record values.",
822
0
             function );
823
824
0
            goto on_error;
825
0
          }
826
100k
        }
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
168k
        if( libevtx_record_values_read_header(
838
168k
             record_values,
839
168k
             io_handle,
840
168k
             chunk_data,
841
168k
             chunk_data_size,
842
168k
             chunk_data_offset,
843
168k
             error ) != 1 )
844
58.5k
        {
845
58.5k
          libcerror_error_set(
846
58.5k
           error,
847
58.5k
           LIBCERROR_ERROR_DOMAIN_IO,
848
58.5k
           LIBCERROR_IO_ERROR_READ_FAILED,
849
58.5k
           "%s: unable to read record values header at offset: %" PRIi64 ".",
850
58.5k
           function,
851
58.5k
           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
58.5k
          libcerror_error_free(
865
58.5k
           error );
866
58.5k
        }
867
110k
        else
868
110k
        {
869
110k
          xml_data_offset = chunk_data_offset + sizeof( evtx_event_record_header_t );
870
110k
          xml_data_size   = 0;
871
872
110k
          if( record_values->data_size > ( sizeof( evtx_event_record_header_t ) + 4 ) )
873
108k
          {
874
108k
            xml_data_size = record_values->data_size - ( sizeof( evtx_event_record_header_t ) + 4 );
875
108k
          }
876
110k
          result = 0;
877
878
110k
          if( xml_data_size > 0 )
879
108k
          {
880
108k
            if( ( xml_data_size >= 5 )
881
106k
             && ( chunk_data[ xml_data_offset ] == 0x0a ) )
882
1.23k
            {
883
1.23k
              result = 1;
884
1.23k
            }
885
107k
            else if( ( xml_data_size >= 4 )
886
106k
                  && ( chunk_data[ xml_data_offset ] == 0x0f )
887
101k
                  && ( chunk_data[ xml_data_offset + 1 ] == 0x01 )
888
100k
                  && ( chunk_data[ xml_data_offset + 2 ] == 0x01 )
889
99.2k
                  && ( chunk_data[ xml_data_offset + 3 ] == 0x00 ) )
890
98.2k
            {
891
98.2k
              result = 1;
892
98.2k
            }
893
/* TODO what about 0x00 allow it ? */
894
108k
          }
895
110k
          if( result != 0 )
896
99.4k
          {
897
99.4k
            chunk_data_offset += record_values->data_size - 4;
898
899
99.4k
            if( libcdata_array_append_entry(
900
99.4k
                 chunk->recovered_records_array,
901
99.4k
                 &entry_index,
902
99.4k
                 (intptr_t *) record_values,
903
99.4k
                 error ) != 1 )
904
0
            {
905
0
              libcerror_error_set(
906
0
               error,
907
0
               LIBCERROR_ERROR_DOMAIN_RUNTIME,
908
0
               LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
909
0
               "%s: unable to append record values to recovered records array.",
910
0
               function );
911
912
0
              goto on_error;
913
0
            }
914
99.4k
            record_values = NULL;
915
99.4k
          }
916
110k
        }
917
168k
      }
918
64.3M
      chunk_data_offset += 4;
919
64.3M
    }
920
4.71k
    if( record_values != NULL )
921
4.22k
    {
922
4.22k
      if( libevtx_record_values_free(
923
4.22k
           &record_values,
924
4.22k
           error ) != 1 )
925
0
      {
926
0
        libcerror_error_set(
927
0
         error,
928
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
929
0
         LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
930
0
         "%s: unable to free record values.",
931
0
         function );
932
933
0
        goto on_error;
934
0
      }
935
4.22k
    }
936
4.71k
  }
937
4.71k
  return( 1 );
938
939
168
on_error:
940
168
  if( record_values != NULL )
941
0
  {
942
0
    libevtx_record_values_free(
943
0
     &record_values,
944
0
     NULL );
945
0
  }
946
168
  if( chunk->data != NULL )
947
168
  {
948
168
    memory_free(
949
168
     chunk->data );
950
951
168
    chunk->data = NULL;
952
168
  }
953
168
  return( -1 );
954
4.71k
}
955
956
/* Retrieves the number of records
957
 * Returns 1 if successful or -1 on error
958
 */
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
4.71k
{
964
4.71k
  static char *function       = "libevtx_chunk_get_number_of_records";
965
4.71k
  int chunk_number_of_records = 0;
966
967
4.71k
  if( chunk == NULL )
968
0
  {
969
0
    libcerror_error_set(
970
0
     error,
971
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
972
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
973
0
     "%s: invalid chunk.",
974
0
     function );
975
976
0
    return( -1 );
977
0
  }
978
4.71k
  if( number_of_records == NULL )
979
0
  {
980
0
    libcerror_error_set(
981
0
     error,
982
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
983
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
984
0
     "%s: invalid number of records.",
985
0
     function );
986
987
0
    return( -1 );
988
0
  }
989
4.71k
  if( libcdata_array_get_number_of_entries(
990
4.71k
       chunk->records_array,
991
4.71k
       &chunk_number_of_records,
992
4.71k
       error ) != 1 )
993
0
  {
994
0
    libcerror_error_set(
995
0
     error,
996
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
997
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
998
0
     "%s: unable to retrieve number of records.",
999
0
     function );
1000
1001
0
    return( -1 );
1002
0
  }
1003
4.71k
  if( chunk_number_of_records > (int) UINT16_MAX )
1004
0
  {
1005
0
    libcerror_error_set(
1006
0
     error,
1007
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1008
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
1009
0
     "%s: invalid number of chunk records value exceeds maximum.",
1010
0
     function );
1011
1012
0
    return( -1 );
1013
0
  }
1014
4.71k
  *number_of_records = (uint16_t) chunk_number_of_records;
1015
1016
4.71k
  return( 1 );
1017
4.71k
}
1018
1019
/* Retrieves the record at the index
1020
 * Returns 1 if successful or -1 on error
1021
 */
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
26.7k
{
1028
26.7k
  static char *function = "libevtx_chunk_get_record";
1029
1030
26.7k
  if( chunk == NULL )
1031
0
  {
1032
0
    libcerror_error_set(
1033
0
     error,
1034
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1035
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1036
0
     "%s: invalid chunk.",
1037
0
     function );
1038
1039
0
    return( -1 );
1040
0
  }
1041
26.7k
  if( libcdata_array_get_entry_by_index(
1042
26.7k
       chunk->records_array,
1043
26.7k
       (int) record_index,
1044
26.7k
       (intptr_t **) record_values,
1045
26.7k
       error ) != 1 )
1046
0
  {
1047
0
    libcerror_error_set(
1048
0
     error,
1049
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
1050
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1051
0
     "%s: unable to retrieve record: %" PRIu16 ".",
1052
0
     function,
1053
0
     record_index );
1054
1055
0
    return( -1 );
1056
0
  }
1057
26.7k
  return( 1 );
1058
26.7k
}
1059
1060
/* Retrieves the number of recovered records
1061
 * Returns 1 if successful or -1 on error
1062
 */
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
3.24k
{
1068
3.24k
  static char *function       = "libevtx_chunk_get_number_of_recovered_records";
1069
3.24k
  int chunk_number_of_records = 0;
1070
1071
3.24k
  if( chunk == NULL )
1072
0
  {
1073
0
    libcerror_error_set(
1074
0
     error,
1075
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1076
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1077
0
     "%s: invalid chunk.",
1078
0
     function );
1079
1080
0
    return( -1 );
1081
0
  }
1082
3.24k
  if( number_of_records == NULL )
1083
0
  {
1084
0
    libcerror_error_set(
1085
0
     error,
1086
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1087
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1088
0
     "%s: invalid number of records.",
1089
0
     function );
1090
1091
0
    return( -1 );
1092
0
  }
1093
3.24k
  if( libcdata_array_get_number_of_entries(
1094
3.24k
       chunk->recovered_records_array,
1095
3.24k
       &chunk_number_of_records,
1096
3.24k
       error ) != 1 )
1097
0
  {
1098
0
    libcerror_error_set(
1099
0
     error,
1100
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
1101
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1102
0
     "%s: unable to retrieve number of records.",
1103
0
     function );
1104
1105
0
    return( -1 );
1106
0
  }
1107
3.24k
  if( chunk_number_of_records > (int) UINT16_MAX )
1108
0
  {
1109
0
    libcerror_error_set(
1110
0
     error,
1111
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1112
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
1113
0
     "%s: invalid number of chunk records value exceeds maximum.",
1114
0
     function );
1115
1116
0
    return( -1 );
1117
0
  }
1118
3.24k
  *number_of_records = (uint16_t) chunk_number_of_records;
1119
1120
3.24k
  return( 1 );
1121
3.24k
}
1122
1123
/* Retrieves the recovered record at the index
1124
 * Returns 1 if successful or -1 on error
1125
 */
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
83.2k
{
1132
83.2k
  static char *function = "libevtx_chunk_get_recovered_record";
1133
1134
83.2k
  if( chunk == NULL )
1135
0
  {
1136
0
    libcerror_error_set(
1137
0
     error,
1138
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
1139
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
1140
0
     "%s: invalid chunk.",
1141
0
     function );
1142
1143
0
    return( -1 );
1144
0
  }
1145
83.2k
  if( libcdata_array_get_entry_by_index(
1146
83.2k
       chunk->recovered_records_array,
1147
83.2k
       (int) record_index,
1148
83.2k
       (intptr_t **) record_values,
1149
83.2k
       error ) != 1 )
1150
0
  {
1151
0
    libcerror_error_set(
1152
0
     error,
1153
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
1154
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
1155
0
     "%s: unable to retrieve record: %" PRIu16 ".",
1156
0
     function,
1157
0
     record_index );
1158
1159
0
    return( -1 );
1160
0
  }
1161
83.2k
  return( 1 );
1162
83.2k
}
1163