Skip to content

Instantly share code, notes, and snippets.

@williballenthin
Created November 22, 2019 20:49
Show Gist options
  • Save williballenthin/eeeb2796c112b9b12f09af782e7b91fb to your computer and use it in GitHub Desktop.
Save williballenthin/eeeb2796c112b9b12f09af782e7b91fb to your computer and use it in GitHub Desktop.
010 Editor template for parsing Windows Registry TxR (.regtrans-ms) files
//------------------------------------------------
//--- 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