How is ntds.dit structured?


    All Active Directory data is stored in the database in the ntds.dit file. The vast majority of applications interact with the directory through the DSA layer implemented in ntdsa.dll. In turn, the functions from ntdsa.dll do not work directly with ntds.dit, their functionality is limited by the needs of the directory service and they cannot give us an idea of ​​the internal structure of the Active Directory database. However, ntds.dit is nothing more than a JET Blue database . Each version of windows (starting with Windows 2000) has everything you need to work with this database.

    In the article below I will try to highlight the following issues:
    • What is the structure of the database?
    • How does the data in ntds.dit produce a "tree"?
    • How is group membership implemented?
    • What is the format of the replPropertyMetaData attribute and how accurately are timestamps stored in replication metadata?




    What do you need to look at ntds.dit?

    At a minimum: esent.dll
    For "comfortable" use from various programming languages ​​over the ESENT API, there are various "wrappers". I used Meneged Esent in conjunction with C #. There are many examples on the project’s website - so I’ll try to concentrate on the contents of ntds.dit below.

    It should also be noted that the JET Blue database has such a parameter as PageSize. By default, it is 4096 (for those versions of esent.dll that I have encountered). So in ntds.dit, the page size is 8192 and this parameter should be correctly set before opening the database.
    The ESENT API has several versions. These versions are incompatible! So ntds.dit from Windows Server 2008 R2 does not open on windows xp, only on Windoes 7. (I did not check backward compatibility)

    Question one: What is the structure of the database?

    The GetTableNames function will return a list of tables in the database:
    datatable - the main table with all the data in the
    hiddentable link_table
    directory - according to the article on technet a table with information about related attributes (for example MemberOf)
    quota_rebuild_progress_table
    quota_table
    sdpropcounttable
    sd_table - according to the article on technet, the table contains information about inherited access rules for each directory object.

    Let us dwell on the datatable table. The list of its columns looks like (in the screenshot the first few lines):

    Deadlock? Not!
    Most column names are in ATT format <single latin letter> <numeric code>. So this digital code is a unique identifier for the Active Directory attribute. There is one row in the table in which the value of the digital code of the column matches its value. If we deduce all values ​​of type Text from this line, we will see that this is the definition of the attribute “attributeID”.

    Now, nothing prevents getting all the columns of the table to match Active Directory attributes (the table is an example - it is shortened, otherwise it did not fit in the post)
    JET_COLUMNIDColumn NameJet column typeAD atribute Name
    JET_COLUMNID (0x6)ab_cnt_colLongSingle-value
    JET_COLUMNID (0x100)Ancestors_colLongbinarySingle-value
    JET_COLUMNID (0x536)ATTb131079LongsubRefsMulti-value
    JET_COLUMNID (0x121)ATTb131088LongnCNameMulti-value
    JET_COLUMNID (0x2dd)ATTb131108LongdMDLocationMulti-value
    JET_COLUMNID (0x42c)ATTb1376270LongdocumentAuthorMulti-value
    JET_COLUMNID (0x169)ATTb1376277LongsecretaryMulti-value
    JET_COLUMNID (0x2b8)ATTb1376294LongassociatedNameMulti-value
    JET_COLUMNID (0x470)ATTb33LongroleOccupantMulti-value
    JET_COLUMNID (0x203)ATTb34LongseeAlsoMulti-value
    JET_COLUMNID (0x2d2)ATTb49LongdistinguishedNameMulti-value
    JET_COLUMNID (0x4cc)ATTb50LonguniqueMemberMulti-value
    JET_COLUMNID (0x206)ATTb589856LongdomainPolicyObjectMulti-value
    JET_COLUMNID (0x250)ATTb589864LongfromServerMulti-value
    JET_COLUMNID (0x205)ATTb589881LongdefaultLocalPolicyObjectMulti-value
    JET_COLUMNID (0x1cc)ATTb589921LongpreferredOUMulti-value
    JET_COLUMNID (0x5a6)ATTb590037LongdefaultClassStoreMulti-value
    JET_COLUMNID (0x270)ATTb590038LongnextLevelStoreMulti-value
    JET_COLUMNID (0x183)ATTb590127LongnotificationListMulti-value
    JET_COLUMNID (0x238)ATTb590192LongrIDManagerReferenceMulti-value
    JET_COLUMNID (0x57c)ATTb590193LongfSMORoleOwnerMulti-value
    JET_COLUMNID (0x3c8)ATTb590246LongdomainPolicyReferenceMulti-value
    JET_COLUMNID (0x566)ATTb590281LonglocalPolicyReferenceMulti-value
    JET_COLUMNID (0x3c1)ATTb590295LongtrustParentMulti-value
    JET_COLUMNID (0x4c1)ATTb590296LongdomainCrossRefMulti-value
    JET_COLUMNID (0x5bc)ATTb590304LongdefaultGroupMulti-value
    JET_COLUMNID (0x57f)ATTb590318LongsiteServerMulti-value
    JET_COLUMNID (0x532)ATTb590338LongphysicalLocationObjectMulti-value
    JET_COLUMNID (0x563)ATTb590341LongipsecPolicyReferenceMulti-value
    JET_COLUMNID (0x24f)ATTb590361LongdynamicLDAPServerMulti-value
    JET_COLUMNID (0x1a4)ATTb590381LongparentCAMulti-value
    JET_COLUMNID (0x233)ATTb590448LongipsecOwnersReferenceMulti-value
    JET_COLUMNID (0x345)ATTq590722CurrencyaCSNonReservedTxSizeMulti-value
    JET_COLUMNID (0x398)ATTq591137CurrencyaCSMaxTokenBucketPerFlowMulti-value
    JET_COLUMNID (0x19b)ATTq591138CurrencyaCSMaximumSDUSizeMulti-value
    JET_COLUMNID (0x4d3)ATTq591139CurrencyaCSMinimumPolicedSizeMulti-value
    JET_COLUMNID (0x244)ATTq591140CurrencyaCSMinimumLatencyMulti-value
    JET_COLUMNID (0x12f)ATTq591141CurrencyaCSMinimumDelayVariationMulti-value
    JET_COLUMNID (0x16e)ATTq591142CurrencyaCSNonReservedPeakRateMulti-value
    JET_COLUMNID (0x344)ATTq591143CurrencyaCSNonReservedTokenSizeMulti-value
    JET_COLUMNID (0x4d4)ATTq591144CurrencyaCSNonReservedMaxSDUSizeMulti-value
    JET_COLUMNID (0x343)ATTq591145CurrencyaCSNonReservedMinPolicedSizeMulti-value
    JET_COLUMNID (0x5ac)ATTq591191CurrencymS-SQL-MemoryMulti-value
    JET_COLUMNID (0x213)ATTq591204CurrencymS-SQL-StatusMulti-value
    JET_COLUMNID (0x4d5)ATTq591220CurrencymS-SQL-SizeMulti-value
    JET_COLUMNID (0x2c5)ATTq591266CurrencymsDS-Cached-Membership-Time-StampMulti-value
    JET_COLUMNID (0x548)ATTq591456CurrencymsWMI-Int8DefaultMulti-value
    JET_COLUMNID (0x52a)ATTq591457CurrencymsWMI-Int8MaxMulti-value
    JET_COLUMNID (0x53f)ATTq591458CurrencymsWMI-Int8MinMulti-value
    JET_COLUMNID (0x214)ATTq591459CurrencymsWMI-Int8ValidValuesMulti-value
    JET_COLUMNID (0x2c1)ATTq591520CurrencylastLogonTimestampMulti-value
    JET_COLUMNID (0x2cc)ATTq591794CurrencymsDS-LastSuccessfulInteractiveLogonTimeMulti-value
    JET_COLUMNID (0x462)ATTq591795CurrencymsDS-LastFailedInteractiveLogonTimeMulti-value
    JET_COLUMNID (0x440)ATTq591835CurrencymsDS-MaximumPasswordAgeMulti-value
    JET_COLUMNID (0x2a5)ATTq591836CurrencymsDS-MinimumPasswordAgeMulti-value
    JET_COLUMNID (0x200)ATTq591841CurrencymsDS-LockoutObservationWindowMulti-value
    JET_COLUMNID (0x2e7)ATTq591842CurrencymsDS-LockoutDurationMulti-value
    JET_COLUMNID (0x3d0)ATTq591879CurrencymsDS-USNLastSyncSuccessMulti-value
    JET_COLUMNID (0x49a)ATTq591922CurrencymsDS-ClaimValueTypeMulti-value
    JET_COLUMNID (0x476)ATTq592002CurrencymsKds-UseStartTimeMulti-value
    JET_COLUMNID (0x477)ATTq592003CurrencymsKds-CreateTimeMulti-value
    JET_COLUMNID (0x3a0)ATTq592007CurrencymsDS-GeoCoordinatesAltitudeMulti-value
    JET_COLUMNID (0x3a1)ATTq592008CurrencymsDS-GeoCoordinatesLatitudeMulti-value
    JET_COLUMNID (0x3a2)ATTq592009CurrencymsDS-GeoCoordinatesLongitudeMulti-value
    JET_COLUMNID (0x43b)ATTr589945LongbinarysecurityIdentifierMulti-value
    JET_COLUMNID (0x1c9)ATTr589970LongbinaryobjectSidMulti-value
    JET_COLUMNID (0x276)ATTr590433LongbinarysIDHistoryMulti-value
    JET_COLUMNID (0x443)ATTr590491LongbinarysyncWithSIDMulti-value
    JET_COLUMNID (0x5e4)ATTr591234LongbinarymS-DS-CreatorSIDMulti-value
    JET_COLUMNID (0x50a)ATTr591668LongbinarymsDS-QuotaTrusteeMulti-value
    JET_COLUMNID (0x1c2)ATTr591978LongbinarymsAuthz-CentralAccessPolicyIDMulti-value
    JET_COLUMNID (0x5)cnt_colLongSingle-value
    JET_COLUMNID (0x1)DNT_colLongSingle-value
    JET_COLUMNID (0x5f1)extendedprocesslinks_colLongbinarySingle-value
    JET_COLUMNID (0x9)IsVisibleInABUnsignedByteSingle-value
    JET_COLUMNID (0x8)NCDNT_colLongSingle-value
    JET_COLUMNID (0x3)Obj_colUnsignedByteSingle-value
    JET_COLUMNID (0x2)PDNT_colLongSingle-value
    JET_COLUMNID (0x4)RDNtyp_colLongSingle-value
    JET_COLUMNID (0xa)recycle_time_colCurrencySingle-value
    JET_COLUMNID (0x7)time_colCurrencySingle-value



    There remains one “unencrypted” ATTc0 attribute - this is a reference to “objectClass”.
    Please note that all columns that store Active Directory attributes are set up as Multi-value regardless of the scheme.

    Question two: How does the data in ntds.dit produce a "tree"?

    The column name “DNT_col” provokes that it is somehow related to the distinguishedName of the object (as it turned out later, they are equal).
    The values ​​of the DNT_col column begin with 1, and the line with DNT_col = 1 corresponds to an interesting object with the attribute value name = "$ NOT_AN_OBJECT1 $ "
    The line with DNT_col = 2 contains the attributes of an object with the name" $ ​​ROOT_OBJECT $ "(Not to be confused with RootDSE)
    With DNT_col = 6, objectClass definitions start
    again Is it a dead end? No again!
    Let's go the other way.
    Searching the ATTm589825 (name) column for a value of Text type “Administrator” returned a record with DNT_col = 3841 and PDNT_col = 1951
    Search in the ATTm589825 (name) column for a value of the Text "Users" type (the container in which the standard administrator account is located) returned a record with DNT_col = 1951 and PDNT_col = 1944
    Here it is! the PDNT_col column contains the DNT_col identifier of the parent object.
    In the line with DNT_col = 1944 (PDNT_col = 1943) - there was a second-level domain object (ntds.dit was taken from the second-level domain controller)
    In a line with DNT_col = 1943 (PDNT_col = 2) - a first-level domain object.


    If you ever wondered: “Why are objects from cn = Configuration, dc = contoso, dc = com not displayed when connected to dc = contoso, dc = com?” Then you should pay attention to the NCDNT_col column - it contains links on the naming context object. (Moreover, the first-level domain object does not have a naming context - can it be so that it is not visible?). For Objects in the naming contexts “dc = contoso, dc = com” and “cn = Configuration, dc = contoso, dc = com”, the value of this column is different.

    No less interesting is how from these numbers the complete distinguishedName of the object is obtained? Which attribute of the object is a relative unique name (RDN) and is used to build the full distinguishedName.
    The RDNtyp_col column contains the attribute identifier (attributeID) containing the RDN.
    The CNT_col column contains the number of objects associated with the current. This column is used by the Link Cleaner mechanism.
    If the bit in the OBJ_col column is set to 1, then this line describes the directory object. Otherwise, it is a phantom object.

    How is SubTree search implemented?
    It is clear that with such a structure, for searching OneLavel it is enough to search among the records with PDNT_col equal to the DN of the search base, but how to search by SubTree? Go around all branches? No it's too complicated.
    Let's look at the values ​​in the column with the speaking name Ancestors_col. It is striking that the deeper the object is in the hierarchy, the longer the value in this column. Each nesting level adds 4 bytes to the length. This is nothing more than a list of parent objects in order of precedence.
    Moreover, the list starts with dn = 2, i.e. with "$ ROOT_OBJECT $"
    Who is involved in keeping Ancestors_col up to date? According to the document, when moving an object to a new place, only its PDNT_col changes, and the Ancestors_col value is updated by the SDProp mechanism at the same time as the new rules for accessing the object are recounted.

    Question three: How is group membership implemented?

    Why since 2003 the server has the opportunity to include in the group a unlimited number of users, and even more than ~ 1200 values ​​cannot be written to the string or numeric multi-value attribute?
    Yes, because “member” and “memberOf” are defined as links. There are no columns in the datatable table matching these attributes. The values ​​of these attributes are implemented as mappings in a separate link_table.
    Perhaps this is why when we delete a group (without using RecucleBin) we lose information about its members - the object of the remote group receives a new identifier in DNT_col when moving deleted objects into the container (and the connection of the group and its member is built using this identifier)
    Let's look at the columns of the link_table
    JET_COLUMNID (0x2)backlink_DNTLongSingle-value
    JET_COLUMNID (0x3)link_baseLongSingle-value
    JET_COLUMNID (0x100)link_dataLongbinarySingle-value
    JET_COLUMNID (0x4)link_deactivetimeCurrencySingle-value
    JET_COLUMNID (0x5)link_deltimeCurrencySingle-value
    JET_COLUMNID (0x1)link_DNTLongSingle-value
    JET_COLUMNID (0x80)link_metadataBinarySingle-value
    JET_COLUMNID (0x7)link_ncdntLongSingle-value
    JET_COLUMNID (0x101)link_ndescLongSingle-value
    JET_COLUMNID (0x6)link_usnchangedCurrencySingle-value



    The names and type of columns are hinted, and the analysis of the data contained in them confirms that:
    link_DNT - Contains the identifier dn (corresponds to DNT_col from the datatable) of the object whose relationship is described (for example, a group object)
    backlink_DNT - Contains the identifier dn of the associated object (for example, user object)
    link_ncdnt - Contains the identifier dn of the naming context in which the communication participants are located.
    link_usnchanged - Contains the USN of the last change.
    link_data - I saw this field filled out only for NTDS Settings object links. Yes! NTDS Settings objects have links with directory partition objects. Apparently, through these links, replication parameters of various directory sections are set.
    link_deltime - Contains the time the link was removed.
    link_metadata - contains the metadata necessary to replicate the changes made. I saw the filled values ​​of this column only on Windows 2012. Christoffer Andersson writes that the column contains the structure DS_REPL_VALUE_META_DATAand here I strongly disagree with him. The DS_REPL_VALUE_META_DATA structure is used at a higher level - ntdsa.dll calls return it. The data contained in this column is smaller than necessary under the DS_REPL_VALUE_META_DATA structure. While this column remains a mystery to me. In principle, replication metadata for link attributes can be obtained from the constructed msDS-ReplValueMetaData attribute, but this does not reveal the internal mechanism for processing and storing this data.

    Fourth question: What is the format of the replPropertyMetaData attribute and how accurately are timestamps stored in replication metadata?


    This attribute stores binary replication metadata:

    I have not figured out what is stored in the first 8 bytes of this structure.
    You should pay attention to the fact that only replicated attributes with values ​​are listed in the replPropertyMetaData structure. So, for example, you will not see the logonTimestamp attribute identifier in replPropertyMetaData.
    There is an interesting point: in the replPropertyMetaData structure for Active Directory security objects (users, groups, and computers), the objectSid attribute is present.
    By the way, in order to decipher the value of this attribute - it’s not necessary to strain so much - there is a built attribute msDS-ReplAttributeMetaData - it represents nothing but the parsed value of replPropertyMetaData.

    Active Directory timestamps are 64-bit unsigned integers that indicate the number of seconds that have passed since 00:00 on January 1, 1601. An interesting nuance follows from this: when editing one attribute of the same object on two domain controllers for 1 second (for example, when batch processing a large number of users), an unexpected result can be obtained.
    According to an article on technet , the version of the attribute from the controller with a lower GUID wins.

    Information used:

    technet.microsoft.com/en-us/library/cc772829%28v=ws.10%29.aspx
    blogs.chrisse.se/2012/02/11/how-the-active-directory-data-store-really-works -inside-ntds-dit-part-1
    blogs.chrisse.se/2012/02/15/how-the-active-directory-data-store-really-works-inside-ntds-dit-part-2
    blogs.chrisse .se / 2012/02/20 / how-the-active-directory-data-store-really-works-inside-ntds-dit-part-3
    blogs.chrisse.se/2012/02/28/how-the- active-directory-data-store-really-works-inside-ntds-dit-part-4
    gexeg.blogspot.ru/2009/12/active-directory.html
    www.ntdsxtract.com

    All experiments were performed on ntds.dit from Windows Server 2012 Eng

    UPD . For starters, a small utility that I wrote to study ntds.dit (maybe someone will do).

    Also popular now: