From 68ddbfb220d95fba392f665db9bef2b7d9fe2604 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 10 Feb 2026 21:20:28 +0900 Subject: [PATCH] Fixed parsing of 32KiB page ESE databases from Windows Server 2025 Windows Server 2025 introduced optional 32KiB ESE database pages. This fixes two issues that prevented libesedb from parsing such databases: 1. In the 32KiB page format the upper 4 bits of available_page_tag are reserved (ctagReserved) and only the lower 12 bits contain the actual number of page tags. Masked the reserved bits when page_size >= 32768. 2. The leaf page backward and forward walk functions did not validate the IS_LEAF page flag. In 32KiB databases some pages in the leaf chain may not be actual leaf pages. Added IS_LEAF check with proper error handling. --- libesedb/libesedb_page_header.c | 9 +++++++ libesedb/libesedb_page_tree.c | 47 +++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/libesedb/libesedb_page_header.c b/libesedb/libesedb_page_header.c index 923f854..6eb57c1 100644 --- a/libesedb/libesedb_page_header.c +++ b/libesedb/libesedb_page_header.c @@ -240,6 +240,15 @@ int libesedb_page_header_read_data( ( (esedb_page_header_t *) data )->available_page_tag, page_header->available_page_tag ); + /* In the 32KiB page format the upper 4 bits of the available page tag + * are reserved (ctagReserved) and the lower 12 bits contain the actual + * number of page tags + */ + if( io_handle->page_size >= 32768 ) + { + page_header->available_page_tag &= 0x0fff; + } + byte_stream_copy_to_uint32_little_endian( ( (esedb_page_header_t *) data )->page_flags, page_header->flags ); diff --git a/libesedb/libesedb_page_tree.c b/libesedb/libesedb_page_tree.c index eee5c75..ba80601 100644 --- a/libesedb/libesedb_page_tree.c +++ b/libesedb/libesedb_page_tree.c @@ -1453,8 +1453,6 @@ int libesedb_page_tree_get_get_first_leaf_page_number( return( -1 ); } - last_leaf_page_number = safe_leaf_page_number; - if( libfdata_vector_get_element_value_by_index( page_tree->pages_vector, (intptr_t *) file_io_handle, @@ -1474,6 +1472,29 @@ int libesedb_page_tree_get_get_first_leaf_page_number( return( -1 ); } + /* Stop backward walk if the page is not a leaf page + */ + if( libesedb_page_get_flags( + page, + &page_flags, + error ) != 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve page flags from page: %" PRIu32 ".", + function, + safe_leaf_page_number ); + + return( -1 ); + } + if( ( page_flags & LIBESEDB_PAGE_FLAG_IS_LEAF ) == 0 ) + { + break; + } + last_leaf_page_number = safe_leaf_page_number; + if( libesedb_page_get_previous_page_number( page, &safe_leaf_page_number, @@ -1671,6 +1692,7 @@ int libesedb_page_tree_get_number_of_leaf_values( libesedb_page_t *page = NULL; static char *function = "libesedb_page_tree_get_number_of_leaf_values"; uint32_t leaf_page_number = 0; + uint32_t page_flags = 0; int number_of_leaf_pages = 0; int safe_number_of_leaf_values = 0; int value_index = 0; @@ -1763,6 +1785,27 @@ int libesedb_page_tree_get_number_of_leaf_values( goto on_error; } + /* Stop forward walk if the page is not a leaf page + */ + if( libesedb_page_get_flags( + page, + &page_flags, + error ) != 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve page flags from page: %" PRIu32 ".", + function, + leaf_page_number ); + + goto on_error; + } + if( ( page_flags & LIBESEDB_PAGE_FLAG_IS_LEAF ) == 0 ) + { + break; + } if( libesedb_page_tree_get_number_of_leaf_values_from_leaf_page( page_tree, page,