└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # CLFS Internals 2 | 3 | With the large number of Common Log File System (CLFS) CVEs issued in the last few months, and the removal of the respective section in the latest Windows Internals, 7th Edition, Part 2 (not that it went into this level of detail to begin with), I figured some unofficial 'documentation' would be helpful to those playing with this previously obscure component, associated with the Kernel Transaction Manager (KTM) that powers TxF (Transactional NTFS) and TxR (Transactional Registry). 4 | 5 | All of this research was based on public information cleverly obtained from the Windows 11 SDK and symbol data for `clfs.sys`. 6 | 7 | # Past References 8 | 9 | There are two public sources of information on CLFS that I want to partially credit and refer readers to, which can help in digesting this information, somewhat. I will warn that these authors reverse-engineered many of the data structures (likely not having realized symbol data is available), but there are some nice diagrams and code. 10 | 11 | The first is the *libclfs* project, which contains source code and some [documentation](https://github.com/libyal/libfsclfs/blob/7b3d245a837351d825f25a6e6f14882a03e843b7/documenation/Common%20Log%20File%20System%20(CLFS).asciidoc) for parsing parts of CLFS files. 12 | 13 | The second is the *DeathNote of Microsoft Windows Kernel*, a [presentation](https://www.slideshare.net/PeterHlavaty/deathnote-of-microsoft-windows-kernel) by Peter Hlavaty on his initial fuzzing of CLFS files more than 5 years ago. 14 | 15 | # Data Structures 16 | 17 | CLFS data structures can be roughly divided into three types: 18 | * Those that are stored in kernel-mode memory and attached to an object and follow its lifetime (such as an FCB attached to a File Object). 19 | 20 | * Those that are ephemerally stored in the memory of a *user*- or *kernel*- mode caller that uses the CLFS Public API interface to parse, scan, or use a CLFS log file for their own purposes. 21 | 22 | * Those that are persistently stored on disk, in the persistent Base Log File (BLF), and then parsed in memory. Some of these structures have in-memory versions that are sometimes, but not always, flushed back to disk. 23 | 24 | Every such data structure is preceded by a `CLFS_NODE_ID`, which is documented in the `clfs.h` header file: 25 | ``` 26 | // 27 | // Common log file system node identifier. Every CLFS file system 28 | // structure has a node identity and type. The node type is a signature 29 | // field while the size is used in for consistency checking. 30 | // 31 | typedef struct _CLFS_NODE_ID 32 | { 33 | ULONG cType; // CLFS node type. 34 | ULONG cbNode; // CLFS node size. 35 | } CLFS_NODE_ID, *PCLFS_NODE_ID; 36 | ``` 37 | ## Node Types 38 | 39 | All CLFS data structures are represented by a Node Type Code (NTC) that contains the letters CLFD (CLF Driver) in "leetspeak", ie `C1FD` followed by an identifier starting with `F000`. 40 | 41 | The usual data structures seen in any File System (File Control Block, Volume Control Block, and Cache Control Block) are seen below: 42 | 43 | ``` 44 | const ULONG CLFS_NODE_TYPE_FCB = 0xC1FDF001; 45 | const ULONG CLFS_NODE_TYPE_VCB = 0xC1FDF002; 46 | const ULONG CLFS_NODE_TYPE_CCB = 0xC1FDF003; 47 | ``` 48 | The FCB is attached to the `FILE_OBJECT` through the `FsContext` field, and the CCB is attached through the `FsContext2`. Since CLFS does not handle volumes, no VCB is actually ever used. 49 | 50 | When parsing IRPs, CLFS builds a Request (`CClfsRequest`) structure: 51 | ``` 52 | const ULONG CLFS_NODE_TYPE_REQ = 0xC1FDF004; 53 | ``` 54 | Note, however, that the structure does not actually have an NTC field, so the above is never used. 55 | 56 | This next NTC is unused and it is unclear what the abbreviation CCA stands for: 57 | ``` 58 | const ULONG CLFS_NODE_TYPE_CCA = 0xC1FDF005; 59 | ``` 60 | The next set of NTCs are found in the on-disk data structures that are present inside of Base Log Files (BLF): 61 | ``` 62 | const ULONG CLFS_NODE_TYPE_SYMBOL = 0xC1FDF006; 63 | const ULONG CLFS_NODE_TYPE_CLIENT_CONTEXT = 0xC1FDF007; 64 | const ULONG CLFS_NODE_TYPE_CONTAINER_CONTEXT = 0xC1FDF008; 65 | const ULONG CLFS_NODE_TYPE_SHARED_SECURITY_CONTEXT = 0xC1FDF00D; 66 | ``` 67 | This next NTC is associated with the CLFS Device Extension that is attached to the `\Device\clfs` Device Object: 68 | ``` 69 | const ULONG CLFS_NODE_TYPE_DEVICE_EXTENSION = 0xC1FDF009; 70 | ``` 71 | Marshalling contexts, identified by `CClfsKernelMarshallingContext` or `CClfsMarshallingContext` are represented by the following NTC. 72 | ``` 73 | const ULONG CLFS_NODE_TYPE_MARSHALING_AREA = 0xC1FDF00A; 74 | ``` 75 | Such data structures are created through the `ClfsCreateMarshallingArea` API, which is documented on MSDN and not relevant to our purposes. 76 | 77 | When using the `ScanLogContainers` and `PrepareLogArchive` user-mode APIs, an Archive Context (`CLFS_LOG_ARCHIVE_CONTEXT`) and Scan Context (`CLFS_LOG_SCAN_CONTEXT`) can be created, with the following NTCs: 78 | ``` 79 | const ULONG CLFS_NODE_TYPE_ARCHIVE_CONTEXT = 0xC1FDF00C; 80 | const ULONG CLFS_NODE_TYPE_SCAN_CONTEXT = 0xC1FDF00E; 81 | ``` 82 | These data structures are documented on MSDN and not interesting for our purposes. 83 | 84 | Finally, when using log restart areas, with either the `ReadLogRestartArea` or `WriteLogRestartArea` APIs, the following two NTCs are used for the `pvContext` data structure that is allocated and used. 85 | ``` 86 | const ULONG CLFS_NODE_TYPE_LOG_READ_IOCB = 0xC1FDF00F; 87 | const ULONG CLFS_NODE_TYPE_LOG_WRITE_IOCB = 0xC1FDF010; 88 | ``` 89 | Once again, these two ephemeral data structures are not relevant to our purposes. 90 | 91 | ## Log Blocks and Sectors 92 | Every Base Log File is made up various *records*. These records are stored in *sectors*, which are written to in units of I/O called *log blocks*. These log blocks are always read and written in an atomic fashion to guarantee consistency. 93 | 94 | ### Sectors 95 | 96 | The sector size is always 512 bytes, defined by the following constants: 97 | ``` 98 | const ULONG CLFS_SECTOR_SIZE = 0x00000200; 99 | const UCHAR SECTORSHIFT = 9; 100 | ``` 101 | #### Sector Types 102 | A sector can belong to one of these types: 103 | ``` 104 | const UCHAR SECTOR_BLOCK_NONE = 0x00; 105 | const UCHAR SECTOR_BLOCK_DATA = 0x04; 106 | const UCHAR SECTOR_BLOCK_OWNER = 0x08; 107 | const UCHAR SECTOR_BLOCK_BASE = 0x10; 108 | ``` 109 | In the case of Base Log File records, the sectors will always be of the `SECTOR_BLOCK_BASE` type. 110 | 111 | #### Sector Signatures 112 | At the end of every sector will be a *signature*, which is used to guarantee consistency. This signature is composed of the following bytes: 113 | ``` 114 | [Sector Block Type][Usn] 115 | ``` 116 | The *sector block type* corresponds to one of the types define above, with an additional flag to identify the first sector and/or the last sector, defined below: 117 | ``` 118 | const UCHAR SECTOR_BLOCK_END = 0x20; 119 | const UCHAR SECTOR_BLOCK_BEGIN = 0x40; 120 | ``` 121 | The *update sequence number* (USN), on the other hand, comes from the log block header (see below) at the beginning of the first sector, and identifies all sectors as having been part of the same non-torn atomic I/O. 122 | 123 | ### Log Block Header 124 | Every log block starts with a *log block header*, called the `CLFS_LOG_BLOCK_HEADER`. Unfortunately its definition is not available in any public sources, but the `CreateMetadataBlock` and `ClfsStampLogBlock` functions give us a fairly clear overview of the fields. 125 | 126 | All offsets, both in the log block header itself, as well as in records part of the block, are always based on the beginning of the log block (the first sector). 127 | ``` 128 | typedef struct _CLFS_LOG_BLOCK_HEADER 129 | { 130 | UCHAR MajorVersion; 131 | UCHAR MinorVersion; 132 | UCHAR Usn; 133 | CLFS_CLIENT_ID ClientId; 134 | USHORT TotalSectorCount; 135 | USHORT ValidSectorCount; 136 | ULONG Padding; 137 | ULONG Checksum; 138 | ULONG Flags; 139 | CLFS_LSN CurrentLsn; 140 | CLFS_LSN NextLsn; 141 | ULONG RecordOffsets[16]; 142 | ULONG SignaturesOffset; 143 | } CLFS_LOG_BLOCK_HEADER, *PCLFS_LOG_BLOCK_HEADER; 144 | ``` 145 | The *major* and *minor* version numbers are always set according to the following constants: 146 | ``` 147 | const UCHAR MAJORVERSION = 0x15; 148 | const UCHAR MINORVERSION = 0x00; 149 | ``` 150 | The *update count* is incremented each time a consistent write has completed. 151 | 152 | The *client ID* corresponds to the client identifier associated with the block, also called the *stream identifier*. For metadata blocks such as the ones in the Base Log File, this is irrelevant, and will always be set to `0`. 153 | 154 | The *total sector count* and *valid sector count* correspond to the number of sectors in the block, both initially as well as after truncation. For the Base Log File, these numbers will always be fixed as shown in the next section. 155 | 156 | The *checksum* is the checksum of the contents of the log block, using the fixed polynomial `0x04C11DB7`. 157 | 158 | The *flags* are defined as follows: 159 | ``` 160 | const ULONG CLFS_BLOCK_RESET = 0x00000000; 161 | const ULONG CLFS_BLOCK_ENCODED = 0x00000001; 162 | const ULONG CLFS_BLOCK_DECODED = 0x00000002; 163 | const ULONG CLFS_BLOCK_LATCHED = 0x00000004; 164 | const ULONG CLFS_BLOCK_TRUNCATE_DISCARD = 0x00000008; 165 | ``` 166 | You should always expect to see the blocks of the Base Log File as being *encoded* when stored in their on-disk format. 167 | 168 | The *current LSN* and *next LSN*, for base blocks, are always set to `CLFS_LSN_INVALID`. 169 | 170 | The first *record offset* always starts at `sizeof(CLFS_LOG_BLOCK_HEADER)`. No other record offsets are used for base blocks. 171 | 172 | Finally, the *signatures offset* corresponds to an in-memory array that is used to store all of sector bytes that were overwritten by the sector signatures. The array is located on the last sector, and must be large enough to hold `TotalSectorCount * overwritten sector data` bytes , remembering to leave space for the ULONG-aligned signature data of the last sector itself. When reading the Base Log File from disk, it is critical to parse this array, and take each 2 bytes and overlay them on top of the signature bytes of each corresponding sector (restoring the original data bytes). 173 | 174 | ## Metadata Blocks 175 | 176 | The Base Log File is composed of 6 different *metadata blocks*, which are all examples of *base log blocks* as shown earlier. Each of them will first therefore have a log block header. These count of metadata blocks is defined by the following constant: 177 | ``` 178 | const USHORT CLFS_METADATA_BLOCK_COUNT = 6; 179 | ``` 180 | 181 | The metadata blocks always only have a single record, so `RecordOffsets[0]` is always used from the log block header to identify the record starting offset. This fact was indicated in the earlier section as well, and is repeated here for edification. 182 | 183 | ### Metadata Records 184 | The three types of records that exist in such blocks are as follows: 185 | |Record Type |Metadata Block Type | Description | 186 | |--|--|--| 187 | |Control Record|Control Metadata Block |Contains information about the *layout*, the *extend* area, and the *truncate* area | 188 | |Base Record|General Metadata Block|Contains the *symbol tables* that store information on the various *client*, *container* and *security* contexts associated with the Base Log File, as well as accounting information on these. | 189 | |Truncate Record|Scratch Metadata Block|Contains information on every client (stream) that needs to have sectors changed as a result of a truncate operation, and the relevant sector byte changes.| 190 | 191 | #### Metadata Record Header 192 | All of these metadata records will be described in the next section, but first note that they all begin with the same header, shown below: 193 | ``` 194 | typedef struct _CLFS_METADATA_RECORD_HEADER 195 | { 196 | ULONGLONG ullDumpCount; 197 | } CLFS_METADATA_RECORD_HEADER, *PCLFS_METADATA_RECORD_HEADER; 198 | ``` 199 | This *dump count* corresponds to a sort of sequence number for the metadata record, and can be used to identify the newest, or "freshest" copy of a record. 200 | 201 | ### Shadow Blocks 202 | You may have realized that only 3 metadata records were defined in the table above, yet 6 metadata blocks exist (and we said each metadata block only contains one record). This is due to *shadow blocks*, which are yet another technique used for consistency. These shadow blocks contain the previous copy of the metadata that was written, and by using the dump count in the record header, can be used to restore previously known good data in case of torn writes. 203 | 204 | ### Metadata Block Types 205 | Taking everything we have explained above, we can use the following enumeration to describe the 6 types of metadata blocks. 206 | ``` 207 | typedef enum _CLFS_METADATA_BLOCK_TYPE 208 | { 209 | ClfsMetaBlockControl, 210 | ClfsMetaBlockControlShadow, 211 | ClfsMetaBlockGeneral, 212 | ClfsMetaBlockGeneralShadow, 213 | ClfsMetaBlockScratch, 214 | ClfsMetaBlockScratchShadow 215 | } CLFS_METADATA_BLOCK_TYPE, *PCLFS_METADATA_BLOCK_TYPE; 216 | ``` 217 | The function `CClfsBaseFilePersisted::CreateImage` can be useful for seeing how the various metadata blocks are created, written, and flushed in the Base Log File. 218 | 219 | We will now take a look at each metadata block type in detail. 220 | 221 | ## Control Record 222 | 223 | The *control record* is always composed of 2 sectors, as defined by the constant below: 224 | ``` 225 | const USHORT CLFS_CONTROL_BLOCK_RAW_SECTORS = 2; 226 | ``` 227 | It is defined by the structure `CLFS_CONTROL_RECORD`, which is shown below: 228 | ``` 229 | typedef struct _CLFS_CONTROL_RECORD 230 | { 231 | CLFS_METADATA_RECORD_HEADER hdrControlRecord; 232 | ULONGLONG ullMagicValue; 233 | UCHAR Version; 234 | CLFS_EXTEND_STATE eExtendState; 235 | USHORT iExtendBlock; 236 | USHORT iFlushBlock; 237 | ULONG cNewBlockSectors; 238 | ULONG cExtendStartSectors; 239 | ULONG cExtendSectors; 240 | CLFS_TRUNCATE_CONTEXT cxTruncate; 241 | USHORT cBlocks; 242 | ULONG cReserved; 243 | CLFS_METADATA_BLOCK rgBlocks[ANYSIZE_ARRAY]; 244 | } CLFS_CONTROL_RECORD, *PCLFS_CONTROL_RECORD; 245 | ``` 246 | ### Version and Magic 247 | 248 | Apart from starting with the previously described standard header, control records also have the CLFS string in "leetspeak" (`C1F5`) repeated twice and then inverted as per the following *magic value* constant: 249 | ``` 250 | const ULONGLONG CLFS_CONTROL_RECORD_MAGIC_VALUE = 0xC1F5C1F500005F1C; 251 | ``` 252 | The *version number* is currently defined to `1`, as per the constant shown here: 253 | ``` 254 | const UCHAR MAJORVERSION_CONTROL = 0x01; 255 | ``` 256 | ### Extend Context 257 | After the version, the next set of fields are all related to CLFS Log Extension. This data could potentially be non-zero in memory, but for a stable Base Log File on disk, you should expect all of these fields to be zero. This does not, of course, imply the CLFS driver or code necessarily makes this assumption. 258 | 259 | The first such field identifies the current *extend state* for the file, using the enumeration below: 260 | ``` 261 | typedef enum _CLFS_EXTEND_STATE 262 | { 263 | ClfsExtendStateNone, 264 | ClfsExtendStateExtendingFsd, 265 | ClfsExtendStateFlushingBlock 266 | } CLFS_EXTEND_STATE, *PCLFS_EXTEND_STATE; 267 | ``` 268 | The next two values identify the index of the block being *extended*, followed by the block being *flushed* -- the latter of which will normally be the shadow block. 269 | 270 | Next, the sector size of the *new block* is stored, as well as the original sector size *before the extend* operation, follow. 271 | 272 | Finally, the number of sectors that were added (*extended*) is present. 273 | 274 | ### Truncate Context 275 | Unlike the extend context, the *truncate context* is stored in its own data structure (`CLFS_TRUNCATE_CONTEXT`), shown below. Once again, you should expect these fields to be all zeroed out when present on disk, with the same caveat on the CLFS driver's own assumptions. 276 | ``` 277 | typedef struct _CLFS_TRUNCATE_CONTEXT 278 | { 279 | CLFS_TRUNCATE_STATE eTruncateState; 280 | CLFS_CLIENT_ID cClients; 281 | CLFS_CLIENT_ID iClient; 282 | CLFS_LSN lsnOwnerPage; 283 | CLFS_LSN lsnLastOwnerPage; 284 | ULONG cInvalidSector; 285 | } CLFS_TRUNCATE_CONTEXT, *PCLFS_TRUNCATE_CONTEXT; 286 | ``` 287 | The *truncation state* is represented by the values in this enumeration: 288 | ``` 289 | typedef enum _CLFS_TRUNCATE_STATE 290 | { 291 | ClfsTruncateStateNone, 292 | ClfsTruncateStateModifyingStream, 293 | ClfsTruncateStateSavingOwner, 294 | ClfsTruncateStateModifyingOwner, 295 | ClfsTruncateStateSavingDiscardBlock, 296 | ClfsTruncateStateModifyingDiscardBlock 297 | } CLFS_TRUNCATE_STATE, *PCLFS_TRUNCATE_STATE; 298 | ``` 299 | Next, the *number of clients* being truncated is stored, followed by the precise *client index* that is currently being truncated. 300 | 301 | Then, the LSN of the *current owner page* being worked on, as well as the LSN of the *last owner page*, are stored if the owner block is being saved or modified. 302 | 303 | Finally, if the *discard block* is being saved or modified, then the `cInvalidSector` field identifies the sector index currently being processed as part of that block. 304 | 305 | ### Block Context 306 | The control record ends with the `rgBlocks` array which defines the set of metadata blocks that exist in the Base Log File. Although we know that this is expected to be 6, there could potentially exist additional metadate blocks, and so for forward support, the `cBlocks` field indicates the number of blocks in the array. 307 | 308 | Each array entry is identified by the `CLFS_METADATA_BLOCK` structure, shown below: 309 | ``` 310 | typedef struct _CLFS_METADATA_BLOCK 311 | { 312 | union 313 | { 314 | PUCHAR pbImage; 315 | ULONGLONG ullAlignment; 316 | }; 317 | ULONG cbImage; 318 | ULONG cbOffset; 319 | CLFS_METADATA_BLOCK_TYPE eBlockType; 320 | } CLFS_METADATA_BLOCK, *PCLFS_METADATA_BLOCK; 321 | ``` 322 | On disk, the `cbOffset` field indicates the offset, starting from the control metadata block (i.e.: the first sector in the Base Log File). of where the metadata block can be found. The `cbImage` field, on the other hand, contains the size of the corresponding block, while the `eBlockType` corresponds to the previously shown enumeration of possible metadata block types. 323 | 324 | In memory, an additional field, `pbImage`, is used to store a pointer to the data in kernel-mode memory. You should expect this field to be zeroed out when stored on disk, and hopefully not trusted when loaded from an existing Base Log File. 325 | 326 | ## Base Record 327 | The base record contains information about the *clients* and *containers* associated with the Base Log File, as well as their related *contexts*. Furthermore, the *shared security context* for each container is also stored here. Finally, some state information is also present. 328 | 329 | This information is stored in a combination of data structures described as *symbols*, as well as fields in a header at the beginning of the record. 330 | 331 | ### Clients 332 | 333 | A client is a user of a CLFS log. For the Base Log File, a single *metadata client* exists initially. The maximum number of clients is defined as follows: 334 | ``` 335 | const UCHAR MAX_CLIENTS_DEFAULT = 124; 336 | ``` 337 | Each client has an identifier, whose highest value is defined as such: 338 | ``` 339 | const UCHAR HIGHEST_CLIENT_ID = 96; 340 | ``` 341 | All clients can be looked up through a *client symbol table*, which has a bucket size defined by the constant shown below: 342 | ``` 343 | const UCHAR CLIENT_SYMTBL_SIZE = 11; 344 | ``` 345 | ### Containers 346 | A container is the entity that is holding the log records for particular data subject to CLFS semantics. The maximum number of containers supported by CLFS is shown below: 347 | ``` 348 | const ULONG MAX_CONTAINERS_DEFAULT = 1024; 349 | ``` 350 | Just like clients, containers can be looked up in their own *container symbol table*, with the following bucket size: 351 | ``` 352 | const UCHAR CONTAINER_SYMTBL_SIZE = 11; 353 | ``` 354 | ### Shared Security Context 355 | Shared security contexts are used to store the security descriptors for the containers that are described by the Base Log File. 356 | 357 | No maximum value seems to exist, and, just like the previously shown symbol tables, the bucket size for the *shared security context symbol table* is as follows: 358 | ``` 359 | const UCHAR SHARED_SECURITY_SYMTBL_SIZE = 11; 360 | ``` 361 | 362 | ### Base Record Header 363 | In order to describe these elements, the base record begins with a header (`CLFS_BASE_RECORD_HEADER`), which is described by the following structure: 364 | ``` 365 | typedef struct _CLFS_BASE_RECORD_HEADER 366 | { 367 | CLFS_METADATA_RECORD_HEADER hdrBaseRecord; 368 | CLFS_LOG_ID cidLog; 369 | ULONGLONG rgClientSymTbl[CLIENT_SYMTBL_SIZE]; 370 | ULONGLONG rgContainerSymTbl[CONTAINER_SYMTBL_SIZE]; 371 | ULONGLONG rgSecuritySymTbl[SHARED_SECURITY_SYMTBL_SIZE]; 372 | ULONG cNextContainer; 373 | CLFS_CLIENT_ID cNextClient; 374 | ULONG cFreeContainers; 375 | ULONG cActiveContainers; 376 | ULONG cbFreeContainers; 377 | ULONG cbBusyContainers; 378 | ULONG rgClients[MAX_CLIENTS_DEFAULT]; 379 | ULONG rgContainers[MAX_CONTAINERS_DEFAULT]; 380 | ULONG cbSymbolZone; 381 | ULONG cbSector; 382 | USHORT bUnused; 383 | CLFS_LOG_STATE eLogState; 384 | UCHAR cUsn; 385 | UCHAR cClients; 386 | } CLFS_BASE_RECORD_HEADER, *PCLFS_BASE_RECORD_HEADER; 387 | ``` 388 | Apart from the standard metadata header, the base record header also includes the *log identifier* which is a randomly generated UUID at log creation time. 389 | 390 | Next, the following three fields contain the symbol tables (which are *hash tables*) for the 3 contexts described above. Each entry is a 64-bit offset to a *hash symbol*, which will be described in the next section. Collisions are handled using a binary search tree. Following each non-zero hash symbol, the appropriate context structure would follow. 391 | 392 | Continuing, the next available *container index* and *client index* are stored, followed by the count of *free containers* and *active containers*. The former field (`cFreeContainers`), however, seems to never be used. The next two fields, `cbFreeContainers` and `cbBusyContainers` are zeroed out and never used. 393 | 394 | We then encounter the array of 32-bit offsets that point to each client context and container context, respectively. Unlike the hash symbol table, these offsets point directly to the appropriate context structure (i.e.: you would expect to find their associated hash symbol above). The `rgContainers` array should have the same number of entries as the `cActiveContainers` field described above. 395 | 396 | The `cbSymbolZone` field contains the size of the *symbol zone* where all symbols (i.e.: the 3 types of contexts) can be found, or in other words, the next free available offset for a new symbol. 397 | 398 | Although named otherwise, both the `cbSector` and `bUnused` fields are unused. 399 | 400 | Near the end of the header, we find the current state of the log, which can be composed of the following constants: 401 | ``` 402 | typedef UCHAR CLFS_LOG_STATE, *PCLFS_LOG_STATE; 403 | const CLFS_LOG_STATE CLFS_LOG_UNINITIALIZED = 0x01; 404 | const CLFS_LOG_STATE CLFS_LOG_INITIALIZED = 0x02; 405 | const CLFS_LOG_STATE CLFS_LOG_ACTIVE = 0x04; 406 | const CLFS_LOG_STATE CLFS_LOG_PENDING_DELETE = 0x08; 407 | const CLFS_LOG_STATE CLFS_LOG_PENDING_ARCHIVE = 0x10; 408 | const CLFS_LOG_STATE CLFS_LOG_SHUTDOWN = 0x20; 409 | const CLFS_LOG_STATE CLFS_LOG_MULTIPLEXED = 0x40; 410 | const CLFS_LOG_STATE CLFS_LOG_SECURE = 0x80; 411 | ``` 412 | This state is then followed by the *next USN* to use for a container (which will match the *current USN* of that container context), and finally, the number of clients stored in the Base Log File (which should match the number of entries in the `rgClients` array). 413 | 414 | With an understanding of the header, we can now look at how symbols are defined in the base record. 415 | 416 | ## Symbols 417 | 418 | Clients, containers, and shared security contexts in the Base Log File are represented by *symbols*, which are preceded by the `CLFSHASHSYM` structure shown below. Each 64-bit offset in the 3 tables shown earlier points to one of these structures. 419 | ``` 420 | typedef struct _CLFSHASHSYM 421 | { 422 | CLFS_NODE_ID cidNode; 423 | ULONG ulHash; 424 | ULONG cbHash; 425 | ULONGLONG ulBelow; 426 | ULONGLONG ulAbove; 427 | LONG cbSymName; 428 | LONG cbOffset; 429 | BOOLEAN fDeleted; 430 | } CLFSHASHSYM, *PCLFSHASHSYM; 431 | ``` 432 | You should expect consistent nodes to always be of size `sizeof(CLFSHASHSYM)` and of type `CLFS_NODE_TYPE_SYMBOL`. 433 | 434 | The `ulHash` field contains the *hash code*, which is computed using Hollub's version (the one that's often misprinted and causes poor results) of the PJW Hash of a `UNICODE_STRING` that stores the symbol's name, with each letter upper cased. See `ClfsHashPJW` for the implementation (note the XOR'ing of the high byte instead of the masking, as in `ElfHash` and the correct PJW Hash). The full name of the symbol is referenced by its offset, in the `cbSymName` field. 435 | 436 | The `cbHash` field contains the size of the symbol data (without the header), while `cbOffset` contains the offset of where the data begins. `fDeleted` is a flag indicating if the symbol has been deleted. 437 | 438 | Finally, in the case of hash collisions, the `ulBelow` and `ulAbove` are the 64-bit offsets of the preceding and following symbol, respectively, treating collisions as a binary search tree. If there are no collisions, these fields will be zeroed out. 439 | 440 | > **Note:** All these offsets continue to be based on the beginning of the general block (i.e.: the base record). 441 | 442 | ## Client Context 443 | The first context stored in the symbol tables is the *client context*, which identifies a user, or client, of a log file. There will always be at least one such *metadata client* created in every Base Log File. 444 | 445 | Each client context is described by the `CLFS_CLIENT_CONTEXT` structure, shown below: 446 | ``` 447 | typedef struct _CLFS_CLIENT_CONTEXT 448 | { 449 | CLFS_NODE_ID cidNode; 450 | CLFS_CLIENT_ID cidClient; 451 | USHORT fAttributes; 452 | ULONG cbFlushThreshold; 453 | ULONG cShadowSectors; 454 | ULONGLONG cbUndoCommitment; 455 | LARGE_INTEGER llCreateTime; 456 | LARGE_INTEGER llAccessTime; 457 | LARGE_INTEGER llWriteTime; 458 | CLFS_LSN lsnOwnerPage; 459 | CLFS_LSN lsnArchiveTail; 460 | CLFS_LSN lsnBase; 461 | CLFS_LSN lsnLast; 462 | CLFS_LSN lsnRestart; 463 | CLFS_LSN lsnPhysicalBase; 464 | CLFS_LSN lsnUnused1; 465 | CLFS_LSN lsnUnused2; 466 | CLFS_LOG_STATE eState; 467 | union 468 | { 469 | HANDLE hSecurityContext; 470 | ULONGLONG ullAlignment; 471 | }; 472 | } CLFS_CLIENT_CONTEXT, *PCLFS_CLIENT_CONTEXT; 473 | ``` 474 | You should expect consistent nodes to always be of size `sizeof(CLFS_CLIENT_CONTEXT)` and of type `CLFS_NODE_TYPE_CLIENT_CONTEXT`. 475 | 476 | Most of the other fields, such as the various *times* and *LSNs* are self-describing and related to actual CLFS operations being done on the log file. For the standard on-disk metadata client, these will usually all be zeroed out or set to `CLFS_INVALID_LSN`, with a few exceptions: 477 | 478 | * `cbFlushTreshold` comes from the registry key `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CLFS\Parameters\` and `REG_DWORD` value `FlushThreshold` and is usually set to `40000`. 479 | * `fAttributes` corresponds to the set of `FILE_ATTRIBUTE` flags associated with the Base Log File (such as `System` and `Hidden`). 480 | 481 | In memory, the `hSecurityContext` field is the offset pointing to the shared security context for the client, since you may have noticed no array of shared security contexts exists in the base record header. 482 | 483 | 484 | ## Container Context 485 | 486 | The second type of context stored in the base record is the *container context*, which is described by the `CLFS_CONTAINER_CONTEXT` structure shown below: 487 | ``` 488 | typedef struct _CLFS_CONTAINER_CONTEXT 489 | { 490 | CLFS_NODE_ID cidNode; 491 | ULONGLONG cbContainer; 492 | CLFS_CONTAINER_ID cidContainer; 493 | CLFS_CONTAINER_ID cidQueue; 494 | union 495 | { 496 | CClfsContainer* pContainer; 497 | ULONGLONG ullAlignment; 498 | }; 499 | CLFS_USN usnCurrent; 500 | CLFS_CONTAINER_STATE eState; 501 | ULONG cbPrevOffset; 502 | ULONG cbNextOffset; 503 | } CLFS_CONTAINER_CONTEXT, *PCLFS_CONTAINER_CONTEXT; 504 | ``` 505 | You should expect consistent nodes to always be of size `sizeof(CLFS_CONTAINER_CONTEXT)` and of type `CLFS_NODE_TYPE_CONTAINER_CONTEXT`. 506 | 507 | The first following field contains the 64-bit *size* of the container, followed by its *container identifier* (starting at 0). If the container is in a container queue, the next field will contain its identifier in the *queue*. Normally, these two numbers will be the same in the on-disk structures. 508 | 509 | On disk, the next field should always be zeroed out, but in memory, it actually contains the kernel pointer to the `CClfsContainer` class describing the container at runtime. Just like other in-memory fields, the CLFS driver should take care not to ever write out this value (to avoid information leaks), as well as to never incorrectly use it off the disk (to avoid privilege escalation). 510 | 511 | The `usnCurrent` field was introduced a bit earlier -- and corresponds to the USN for the container, which should be monotonically increasing based on the `cUsn` of the base record header. 512 | 513 | The next field describes the *container state* and is actually documented on MSDN, as the public APIs allow querying this information, while the final two are never used. On disk, the container state will usually be `ClfsContainerInactive`. 514 | 515 | ## Shared Security Context 516 | The final context structure in the base record is the *shared security context*, and should normally only ever be found in the in-memory representation, and never on disk. 517 | 518 | It is described by the `CLFS_SHARED_SECURITY_CONTEXT` structure, shown below: 519 | ``` 520 | typedef struct _CLFS_SHARED_SECURITY_CONTEXT 521 | { 522 | CLFS_NODE_ID cidNode; 523 | ULONG cRef; 524 | ULONG cRefActive; 525 | ULONG coffDescriptor; 526 | ULONG cbDescriptor; 527 | UCHAR rgbSecurityDescriptor[ANYSIZE_ARRAY]; 528 | } CLFS_SHARED_SECURITY_CONTEXT, *PCLFS_SHARED_SECURITY_CONTEXT; 529 | ``` 530 | You should expect consistent nodes to always be of size `sizeof(CLFS_SHARED_SECURITY_CONTEXT)` and of type `CLFS_NODE_TYPE_SHARED_SECURITY_CONTEXT`. 531 | 532 | Two reference counts are used, one to store the number of callers using the shared context, while the other keeps track of the security descriptors associated with the shared context. 533 | 534 | The `coffDescriptor` field points to the security descriptor bytes themselves, and should normally always be set to `offsetof(CLFS_SHARED_SECURITY_CONTEXT, rgbSecurityDescriptor);` 535 | 536 | Finally, `cbDescriptor` stores the size of the security descriptor, and the final field corresponds to the byte data backing the `SECURITY_DESCRIPTOR` structure itself. 537 | 538 | ## CLFS In-Memory Class 539 | Once in memory, a CLFS Base Log File is represented by a `CClfsBaseFile` class, which can be further extended by a `CClfsBaseFilePersisted`. The definition for the former can be found in public symbols and is shown below: 540 | ``` 541 | struct _CClfsBaseFile 542 | { 543 | ULONG m_cRef; 544 | PUCHAR m_pbImage; 545 | ULONG m_cbImage; 546 | PERESOURCE m_presImage; 547 | USHORT m_cBlocks; 548 | PCLFS_METADATA_BLOCK m_rgBlocks; 549 | PUSHORT m_rgcBlockReferences; 550 | CLFSHASHTBL m_symtblClient; 551 | CLFSHASHTBL m_symtblContainer; 552 | CLFSHASHTBL m_symtblSecurity; 553 | ULONGLONG m_cbContainer; 554 | ULONG m_cbRawSectorSize; 555 | BOOLEAN m_fGeneralBlockReferenced; 556 | } CClfsBaseFile, *PCLFSBASEFILE; 557 | ``` 558 | These fields mainly represent data we've already seen earlier, such as the size of the container, the sector size, the array of metadata blocks and their number, as well as the size of the whole Base Log File and its location in kernel mode memory. 559 | 560 | Additionally, the class is reference counted, and almost any access to any of its fields is protected by the `m_presImage` lock, which is an *executive resource* accessed in either shared or exclusive mode. 561 | 562 | Finally, each block itself is also referenced in the `m_rgcBlockReferences` array, noting there's a limit of `65535` references. A casual look at `AcquireMetadataBlock`, for example, shows no meaningful protection is done against overflow or underflow (although one would need to recursively cause acquisitions without releases). When the general block has been referenced at least once, the `m_fGeneralBlockReferenced` boolean is used to indicate the fact. 563 | 564 | ### In-memory Hash Table 565 | You may note that in the in-memory class, the symbol hash tables are represented by this structure, shown below: 566 | ``` 567 | typedef struct _CLFSHASHTBL 568 | { 569 | PULONGLONG rgSymHash; 570 | LONG cHashElt; 571 | CClfsBaseFile* pBaseFile; 572 | } CLFSHASHTBL, *PCLFSHASHTBL; 573 | ``` 574 | This allows the offsets from the base record header to be stored as pointers in-memory, along with the bucket count and a pointer back to the `CClfsBaseFile`. 575 | ## Truncate Record 576 | The *truncate record* is the last record type and is used during log truncation. The data structure (`CLFS_TRUNCATE_RECORD_HEADER`) is not present in the public symbols, but the `ValidateTruncateRecord`, `TruncateLogRewriteOwnerPages`, and `TruncateLogStart` functions can be used to infer the fields. 577 | 578 | Its best guess definition is given below: 579 | ``` 580 | typedef struct _CLFS_TRUNCATE_RECORD_HEADER 581 | { 582 | CLFS_METADATA_RECORD_HEADER hdrBaseRecord; 583 | ULONG coffClientChange; 584 | ULONG coffOwnerPage; 585 | } CLFS_TRUNCATE_RECORD_HEADER, *PCLFS_TRUNCATE_RECORD_HEADER; 586 | ``` 587 | Apart from the usual header, the offset to the first `CLFS_TRUNCATE_CLIENT_CHANGE` structure is indicated, followed by the offset of the first owner page. Both of these are present in the scratch metadata block. 588 | 589 | ### Client Change Descriptor 590 | 591 | The `CLFS_TRUNCATE_CLIENT_CHANGE` structure *is* defined in the symbols, and is shown below: 592 | 593 | ``` 594 | typedef struct _CLFS_TRUNCATE_CLIENT_CHANGE 595 | { 596 | CLFS_CLIENT_ID cidClient; 597 | CLFS_LSN lsn; 598 | CLFS_LSN lsnClient; 599 | CLFS_LSN lsnRestart; 600 | USHORT cLength; 601 | USHORT cOldLength; 602 | ULONG cSectors; 603 | CLFS_SECTOR_CHANGE rgSectors[ANYSIZE_ARRAY]; 604 | } CLFS_TRUNCATE_CLIENT_CHANGE, *PCLFS_TRUNCATE_CLIENT_CHANGE; 605 | ``` 606 | This structure specifies which client identifier (stream identifier) is being modified, and the physical and virtual LSN of the replacement block. 607 | 608 | Once truncation is completed, the `lsnRestart` field will contain the LSN of the new restart area. 609 | 610 | The next two fields host the size of the new replaced block, as well as the size of the old block. 611 | 612 | Finally, for each sector subject to changes as part of truncation, an array of `cSectors` is present at the end of the structure (`rgSectors`), which is made up of `CLFS_SECTOR_CHANGE` structures. 613 | 614 | #### Sector Change Descriptor 615 | ``` 616 | typedef struct _CLFS_SECTOR_CHANGE 617 | { 618 | ULONG iSector; 619 | ULONG ulUnused; 620 | BYTE rgbSector[CLFS_SECTOR_SIZE]; 621 | } CLFS_SECTOR_CHANGE, *PCLFS_SECTOR_CHANGE; 622 | ``` 623 | Quite simply, these structures indicate the target sector index, and the new sector data to write. 624 | --------------------------------------------------------------------------------