Created
November 22, 2019 20:49
-
-
Save williballenthin/eeeb2796c112b9b12f09af782e7b91fb to your computer and use it in GitHub Desktop.
010 Editor template for parsing Windows Registry TxR (.regtrans-ms) files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------------ | |
//--- 010 Editor v8.0.1 Binary Template | |
// | |
// File: Transactional Registry Transaction Logs (.TxR) | |
// Authors: Willi Ballenthin <[email protected]> | |
// Version: 0.1 | |
// Reference: https://www.fireeye.com/blog/threat-research/2019/01/digging-up-the-past-windows-registry-forensics-revisited.html | |
//------------------------------------------------ | |
LittleEndian(); | |
typedef unsigned char uint8_t; | |
typedef unsigned short uint16_t; | |
typedef unsigned int uint32_t; | |
typedef uint64 uint64_t; | |
typedef enum <uint16_t> TxrOperationType { | |
OperationCreateKey = 1, | |
OperationDeleteKey = 2, | |
// "Operation types 3-8 are value write or delete. | |
// It is not known what the different types signify." | |
OperationUnkValue3 = 3, | |
OperationUnkValue4 = 4, | |
OperationUnkValue5 = 5, | |
OperationUnkValue6 = 6, | |
OperationUnkValue7 = 7, | |
OperationUnkValue8 = 8, | |
} TxrOperationType; | |
// from: https://github.com/williballenthin/python-registry/blob/master/Registry/RegistryParse.py | |
typedef enum <uint32_t> RegValueType { | |
RegNone = 0x0000, | |
RegSZ = 0x0001, | |
RegExpandSZ = 0x0002, | |
RegBin = 0x0003, | |
RegDWord = 0x0004, | |
RegBigEndian = 0x0005, | |
RegLink = 0x0006, | |
RegMultiSZ = 0x0007, | |
RegResourceList = 0x0008, | |
RegFullResourceDescriptor = 0x0009, | |
RegResourceRequirementsList = 0x000A, | |
RegQWord = 0x000B, | |
RegFileTime = 0x0010, | |
RegUint8 = 0x101, | |
RegInt16 = 0x102, | |
RegUint16 = 0x103, | |
RegInt32 = 0x104, | |
RegUint32 = 0x105, | |
RegInt64 = 0x106, | |
RegUint64 = 0x107, | |
RegFloat = 0x108, | |
RegDouble = 0x109, | |
RegUnicodeChar = 0x10A, | |
RegBoolean = 0x10B, | |
RegUnicodeString = 0x10C, | |
RegCompositeValue = 0x10D, | |
RegDateTimeOffset = 0x10E, | |
RegTimeSpan = 0x10F, | |
RegGUID = 0x110, | |
RegUnk111 = 0x111, | |
RegUnk112 = 0x112, | |
RegUnk113 = 0x113, | |
RegBytesArray = 0x114, | |
RegInt16Array = 0x115, | |
RegUint16Array = 0x116, | |
RegInt32Array = 0x117, | |
RegUInt32Array = 0x118, | |
RegInt64Array = 0x119, | |
RegUInt64Array = 0x11A, | |
RegFloatArray = 0x11B, | |
RegDoubleArray = 0x11C, | |
RegUnicodeCharArray = 0x11D, | |
RegBooleanArray = 0x11E, | |
RegUnicodeStringArray = 0x11F, | |
} RegValueType; | |
typedef struct TxrRecord { | |
uint32_t magic; | |
Assert(magic == 0x00280000, "unexpected magic"); | |
uint32_t unk1; | |
uint32_t unk2; | |
uint32_t record_size; | |
uint32_t record_type; // 0x1 or 0x4 or 0x10 | |
TxrOperationType operation_type; | |
uint16_t padding1; | |
// this value seems to be common across multiple records | |
// maybe the transaction GUID | |
uint8_t maybe_id[0x10]; | |
// only support recort_type == 1 | |
// which seems to contain key and value creation and deletion | |
if (record_type != 1) { | |
return; | |
} | |
// offset 0x28 | |
uint16_t key_name_length1; | |
uint16_t key_name_length2; | |
Assert(key_name_length1 == key_name_length2, "unexpected key length"); | |
uint16_t name_unk1; | |
uint16_t name_unk2; | |
uint64_t name_u64; | |
if (operation_type == OperationCreateKey || | |
operation_type == OperationDeleteKey) { | |
uint32_t unk5; | |
uint32_t unk6; | |
uint64_t unk_u64; // seems to correlate with name_u64 | |
wchar_t key_name[key_name_length1 / sizeof(wchar_t)]<optimize=false>; | |
} else if (operation_type == OperationUnkValue3 || | |
operation_type == OperationUnkValue4 || | |
operation_type == OperationUnkValue5 || | |
operation_type == OperationUnkValue6 || | |
operation_type == OperationUnkValue7 || | |
operation_type == OperationUnkValue8) { | |
uint16_t value_name_length1; | |
uint16_t value_name_length2; | |
uint16_t value_unk1; | |
uint16_t value_unk2; | |
uint64_t value_u64; // seems to correlate with name_u64 | |
RegValueType data_type; | |
uint32_t data_size; | |
uint64_t data_u64; // seems to correlate with name_u64 | |
wchar_t key_name[key_name_length1 / sizeof(wchar_t)]<optimize=false>; | |
wchar_t value_name[value_name_length1 / sizeof(wchar_t)]<optimize=false>; | |
switch (data_type) { | |
case RegSZ: | |
case RegExpandSZ: | |
case RegMultiSZ: | |
wchar_t value[data_size / sizeof(wchar_t)]<optimize=false>; | |
break; | |
case RegDWord: | |
Assert(data_size == 4, "unexpected data size"); | |
uint32_t value; | |
break; | |
case RegQWord: | |
Assert(data_size == 8, "unexpected data size"); | |
uint64_t value; | |
break; | |
case RegNone: | |
case RegBin: | |
/* ...and everything else */ | |
uint8_t value[data_size]<optimize=false>; | |
} | |
} else { | |
Assert(false, "unexpected operation type"); | |
} | |
} TxrRecord; | |
// parse at a specific, selected location by the cursor. | |
// the record starts with the bytes | 00 00 28 00 | | |
FSeek(GetCursorPos()); | |
TxrRecord record; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment