Example #1
0
        /// <summary>
        /// Retrieve columns using the basic RetrieveColumns API.
        /// </summary>
        private void RetrieveWithRetrieveColumns()
        {
            var boolColumn = new BoolColumnValue {
                Columnid = this.columnidDict["boolean"]
            };
            var int32Column = new Int32ColumnValue {
                Columnid = this.columnidDict["int32"]
            };
            var int64Column = new Int64ColumnValue {
                Columnid = this.columnidDict["int64"]
            };
            var guidColumn = new GuidColumnValue {
                Columnid = this.columnidDict["guid"]
            };
            var stringColumn = new StringColumnValue {
                Columnid = this.columnidDict["unicode"]
            };

            var retrievecolumns = new ColumnValue[]
            {
                boolColumn,
                int32Column,
                int64Column,
                guidColumn,
                stringColumn,
            };

            for (int i = 0; i < NumRetrieves; ++i)
            {
                Api.JetBeginTransaction(this.session);

                Api.RetrieveColumns(this.session, this.tableid, retrievecolumns);

                bool   actualBool   = (bool)boolColumn.Value;
                int    actualInt32  = (int)int32Column.Value;
                long   actualInt64  = (long)int64Column.Value;
                Guid   actualGuid   = (Guid)guidColumn.Value;
                string actualString = stringColumn.Value;

                Assert.AreEqual(this.expectedBool, actualBool);
                Assert.AreEqual(this.expectedInt32, actualInt32);
                Assert.AreEqual(this.expectedInt64, actualInt64);
                Assert.AreEqual(this.expectedGuid, actualGuid);
                Assert.AreEqual(this.expectedString, actualString);

                Api.JetCommitTransaction(this.session, CommitTransactionGrbit.None);
            }
        }
        private IEnumerable <UnspentTx> ReadUnspentTransactionsInner()
        {
            using (SetSessionContext())
            {
                Api.JetSetCurrentIndex(this.jetSession, this.unspentTxTableId, "IX_TxHash");

                if (Api.TryMoveFirst(this.jetSession, this.unspentTxTableId))
                {
                    do
                    {
                        var txHashColumn = new BytesColumnValue {
                            Columnid = this.txHashColumnId
                        };
                        var blockIndexColumn = new Int32ColumnValue {
                            Columnid = this.blockIndexColumnId
                        };
                        var txIndexColumn = new Int32ColumnValue {
                            Columnid = this.txIndexColumnId
                        };
                        var txVersionColumn = new UInt32ColumnValue {
                            Columnid = this.txVersionColumnId
                        };
                        var isCoinbaseColumn = new BoolColumnValue {
                            Columnid = this.isCoinbaseColumnId
                        };
                        var outputStatesColumn = new BytesColumnValue {
                            Columnid = this.outputStatesColumnId
                        };
                        var txOutputBytesColumn = new BytesColumnValue {
                            Columnid = this.txOutputBytesColumnId
                        };
                        Api.RetrieveColumns(this.jetSession, this.unspentTxTableId, txHashColumn, blockIndexColumn, txIndexColumn, txVersionColumn, isCoinbaseColumn, outputStatesColumn, txOutputBytesColumn);

                        var txHash       = DbEncoder.DecodeUInt256(txHashColumn.Value);
                        var blockIndex   = blockIndexColumn.Value.Value;
                        var txIndex      = txIndexColumn.Value.Value;
                        var txVersion    = txVersionColumn.Value.Value;
                        var isCoinbase   = isCoinbaseColumn.Value.Value;
                        var outputStates = DataDecoder.DecodeOutputStates(outputStatesColumn.Value);

                        yield return(new UnspentTx(txHash, blockIndex, txIndex, txVersion, isCoinbase, outputStates));
                    }while (Api.TryMoveNext(this.jetSession, this.unspentTxTableId));
                }
            }
        }
        public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx)
        {
            CheckTransaction();

            using (SetSessionContext())
            {
                Api.JetSetCurrentIndex(this.jetSession, this.unspentTxTableId, "IX_TxHash");
                Api.MakeKey(this.jetSession, this.unspentTxTableId, DbEncoder.EncodeUInt256(txHash), MakeKeyGrbit.NewKey);
                if (Api.TrySeek(this.jetSession, this.unspentTxTableId, SeekGrbit.SeekEQ))
                {
                    var blockIndexColumn = new Int32ColumnValue {
                        Columnid = this.blockIndexColumnId
                    };
                    var txIndexColumn = new Int32ColumnValue {
                        Columnid = this.txIndexColumnId
                    };
                    var txVersionColumn = new UInt32ColumnValue {
                        Columnid = this.txVersionColumnId
                    };
                    var isCoinbaseColumn = new BoolColumnValue {
                        Columnid = this.isCoinbaseColumnId
                    };
                    var outputStatesColumn = new BytesColumnValue {
                        Columnid = this.outputStatesColumnId
                    };
                    var txOutputBytesColumn = new BytesColumnValue {
                        Columnid = this.txOutputBytesColumnId
                    };
                    Api.RetrieveColumns(this.jetSession, this.unspentTxTableId, blockIndexColumn, txIndexColumn, txVersionColumn, isCoinbaseColumn, outputStatesColumn, txOutputBytesColumn);

                    var blockIndex   = blockIndexColumn.Value.Value;
                    var txIndex      = txIndexColumn.Value.Value;
                    var txVersion    = txVersionColumn.Value.Value;
                    var isCoinbase   = isCoinbaseColumn.Value.Value;
                    var outputStates = DataDecoder.DecodeOutputStates(outputStatesColumn.Value);

                    unspentTx = new UnspentTx(txHash, blockIndex, txIndex, txVersion, isCoinbase, outputStates);
                    return(true);
                }

                unspentTx = default(UnspentTx);
                return(false);
            }
        }
Example #4
0
        private void InitColumns()
        {
            // The main entity Table
            _colIdEntityLabel = Api.GetTableColumnid(Sesid, Table, colNameEntityLabel);

            _colIdIfcType        = Api.GetTableColumnid(Sesid, Table, colNameIfcType);
            _colIdEntityData     = Api.GetTableColumnid(Sesid, Table, colNameEntityData);
            _colIdIsIndexedClass = Api.GetTableColumnid(Sesid, Table, colNameIsIndexedClass);
            _colValEntityLabel   = new Int32ColumnValue {
                Columnid = _colIdEntityLabel
            };
            _colValTypeId = new Int16ColumnValue {
                Columnid = _colIdIfcType
            };

            _colValData = new BytesColumnValue {
                Columnid = _colIdEntityData
            };
            _colValIsIndexedClass = new BoolColumnValue {
                Columnid = _colIdIsIndexedClass
            };
            _colValues = new ColumnValue[] { _colValEntityLabel, _colValTypeId, _colValData, _colValIsIndexedClass };

            //The index table
            _colIdIdxIfcType  = Api.GetTableColumnid(Sesid, _indexTable, colNameIfcType);
            _colValIdxIfcType = new Int16ColumnValue {
                Columnid = _colIdIdxIfcType
            };
            _colIdIdxKey  = Api.GetTableColumnid(Sesid, _indexTable, colNameSecondaryKey);
            _colValIdxKey = new Int32ColumnValue {
                Columnid = _colIdIdxKey
            };
            _colIdIdxEntityLabel  = Api.GetTableColumnid(Sesid, _indexTable, colNameEntityLabel);
            _colValIdxEntityLabel = new Int32ColumnValue {
                Columnid = _colIdIdxEntityLabel
            };
            _colIdxValues = new ColumnValue[] { _colValIdxIfcType, _colValIdxKey, _colValIdxEntityLabel };
        }
Example #5
0
        private static DatatableRow[] EnumerateDatatableTable(JetDb db, IReadOnlyDictionary <string, string> ldapDisplayNameToDatatableColumnNameDictionary, bool dumpHashes, bool includeHistoryHashes)
        {
            Stopwatch stopwatch = null;

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateDatatableTable)}");
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }

            var datatable    = new List <DatatableRow>();
            var deletedCount = 0;

            using (var table = db.OpenJetDbTable(DATATABLE))
            {
                // Get a dictionary mapping column names to column ids
                var columnDictionary = table.GetColumnDictionary();

                // Loop over the table
                table.MoveBeforeFirst();
                while (table.TryMoveNext())
                {
                    var accountExpiresColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["accountExpires"]]
                    };
                    var displayNameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["displayName"]]
                    };
                    var distinguishedNameTagColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["DNT_col"]
                    };
                    var groupTypeColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["groupType"]]
                    };
                    var isDeletedColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["isDeleted"]]
                    };
                    var lastLogonColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lastLogonTimestamp"]]
                    };
                    var lmColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["dBCSPwd"]]
                    };
                    var lmHistoryColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lmPwdHistory"]]
                    };
                    var nameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["name"]]
                    };
                    var ntColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["unicodePwd"]]
                    };
                    var ntHistoryColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["ntPwdHistory"]]
                    };
                    var objColumn = new BoolColumnValue {
                        Columnid = columnDictionary["OBJ_col"]
                    };
                    var objectCategoryColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectCategory"]]
                    };
                    var objectSidColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectSid"]]
                    };
                    var parentDistinguishedNameTagColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["PDNT_col"]
                    };
                    var passwordLastSetColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pwdLastSet"]]
                    };
                    var pekListColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pekList"]]
                    };
                    var primaryGroupIdColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["primaryGroupID"]]
                    };
                    var rdnTypeColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["RDNtyp_col"]
                    };                                                                                      // The RDNTyp_col holds the Attribute-ID for the attribute being used as the RDN, such as CN, OU, DC
                    var samAccountNameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["sAMAccountName"]]
                    };
                    var timeColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary["time_col"]
                    };
                    var userAccountControlColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["userAccountControl"]]
                    };
                    var supplementalCredentialsColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["supplementalCredentials"]]
                    };

                    var columns = new List <ColumnValue>
                    {
                        accountExpiresColumn,
                        displayNameColumn,
                        distinguishedNameTagColumn,
                        groupTypeColumn,
                        isDeletedColumn,
                        lastLogonColumn,
                        nameColumn,
                        objColumn,
                        objectCategoryColumn,
                        objectSidColumn,
                        parentDistinguishedNameTagColumn,
                        passwordLastSetColumn,
                        primaryGroupIdColumn,
                        rdnTypeColumn,
                        samAccountNameColumn,
                        timeColumn,
                        userAccountControlColumn,
                    };

                    if (dumpHashes)
                    {
                        columns.Add(pekListColumn);
                        columns.Add(lmColumn);
                        columns.Add(ntColumn);
                        columns.Add(supplementalCredentialsColumn);

                        if (includeHistoryHashes)
                        {
                            columns.Add(lmHistoryColumn);
                            columns.Add(ntHistoryColumn);
                        }
                    }

                    table.RetrieveColumns(columns.ToArray());

                    // Skip deleted objects
                    if (isDeletedColumn.Value.HasValue && isDeletedColumn.Value != 0)
                    {
                        deletedCount++;
                        continue;
                    }

                    // Some deleted objects do not have the isDeleted flag but do have a string appended to the DN (https://support.microsoft.com/en-us/help/248047/phantoms--tombstones-and-the-infrastructure-master)
                    if (nameColumn.Value?.Contains("\nDEL:") ?? false)
                    {
                        deletedCount++;
                        continue;
                    }

                    SecurityIdentifier sid = null;
                    uint rid = 0;
                    if (objectSidColumn.Error == JET_wrn.Success)
                    {
                        var sidBytes = objectSidColumn.Value;
                        var ridBytes = sidBytes.Skip(sidBytes.Length - sizeof(int)).Take(sizeof(int)).Reverse().ToArray();
                        sidBytes = sidBytes.Take(sidBytes.Length - sizeof(int)).Concat(ridBytes).ToArray();
                        rid      = BitConverter.ToUInt32(ridBytes, 0);
                        sid      = new SecurityIdentifier(sidBytes, 0);
                    }

                    var row = new DatatableRow
                    {
                        AccountExpires          = accountExpiresColumn.Value,
                        DisplayName             = displayNameColumn.Value,
                        Dnt                     = distinguishedNameTagColumn.Value,
                        GroupType               = groupTypeColumn.Value,
                        LastLogon               = lastLogonColumn.Value,
                        Name                    = nameColumn.Value,
                        ObjectCategoryDnt       = objectCategoryColumn.Value,
                        Rid                     = rid,
                        Sid                     = sid,
                        ParentDnt               = parentDistinguishedNameTagColumn.Value,
                        Phantom                 = objColumn.Value == false,
                        LastPasswordChange      = passwordLastSetColumn.Value,
                        PrimaryGroupDnt         = primaryGroupIdColumn.Value,
                        RdnType                 = rdnTypeColumn.Value,
                        SamAccountName          = samAccountNameColumn.Value,
                        UserAccountControlValue = userAccountControlColumn.Value,
                    };

                    if (dumpHashes)
                    {
                        if (pekListColumn.Value != null)
                        {
                            row.PekList = pekListColumn.Value;
                        }

                        if (lmColumn.Value != null)
                        {
                            row.EncryptedLmHash = lmColumn.Value;
                        }

                        if (ntColumn.Value != null)
                        {
                            row.EncryptedNtHash = ntColumn.Value;
                        }

                        if (includeHistoryHashes)
                        {
                            if (lmHistoryColumn.Value != null)
                            {
                                row.EncryptedLmHistory = lmHistoryColumn.Value;
                            }

                            if (ntHistoryColumn.Value != null)
                            {
                                row.EncryptedNtHistory = ntHistoryColumn.Value;
                            }
                        }

                        if (supplementalCredentialsColumn.Value != null)
                        {
                            row.SupplementalCredentialsBlob = supplementalCredentialsColumn.Value;
                        }
                    }

                    datatable.Add(row);
                }
            }

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"  Skipped {deletedCount} deleted objects");
                ConsoleEx.WriteDebug($"  Enumerated {datatable.Count} objects");

                stopwatch.Stop();
                ConsoleEx.WriteDebug($"  Completed in {stopwatch.Elapsed}");
            }

            return(datatable.ToArray());
        }
Example #6
0
        private IEnumerable<UnspentTx> ReadUnspentTransactionsInner()
        {
            using (SetSessionContext())
            {
                Api.JetSetCurrentIndex(this.jetSession, this.unspentTxTableId, "IX_TxHash");

                if (Api.TryMoveFirst(this.jetSession, this.unspentTxTableId))
                {
                    do
                    {
                        var txHashColumn = new BytesColumnValue { Columnid = this.txHashColumnId };
                        var blockIndexColumn = new Int32ColumnValue { Columnid = this.blockIndexColumnId };
                        var txIndexColumn = new Int32ColumnValue { Columnid = this.txIndexColumnId };
                        var txVersionColumn = new UInt32ColumnValue { Columnid = this.txVersionColumnId };
                        var isCoinbaseColumn = new BoolColumnValue { Columnid = this.isCoinbaseColumnId };
                        var outputStatesColumn = new BytesColumnValue { Columnid = this.outputStatesColumnId };
                        var txOutputBytesColumn = new BytesColumnValue { Columnid = this.txOutputBytesColumnId };
                        Api.RetrieveColumns(this.jetSession, this.unspentTxTableId, txHashColumn, blockIndexColumn, txIndexColumn, txVersionColumn, isCoinbaseColumn, outputStatesColumn, txOutputBytesColumn);

                        var txHash = DbEncoder.DecodeUInt256(txHashColumn.Value);
                        var blockIndex = blockIndexColumn.Value.Value;
                        var txIndex = txIndexColumn.Value.Value;
                        var txVersion = txVersionColumn.Value.Value;
                        var isCoinbase = isCoinbaseColumn.Value.Value;
                        var outputStates = DataDecoder.DecodeOutputStates(outputStatesColumn.Value);

                        yield return new UnspentTx(txHash, blockIndex, txIndex, txVersion, isCoinbase, outputStates);
                    }
                    while (Api.TryMoveNext(this.jetSession, this.unspentTxTableId));
                }
            }
        }
Example #7
0
        public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx)
        {
            CheckTransaction();

            using (SetSessionContext())
            {
                Api.JetSetCurrentIndex(this.jetSession, this.unspentTxTableId, "IX_TxHash");
                Api.MakeKey(this.jetSession, this.unspentTxTableId, DbEncoder.EncodeUInt256(txHash), MakeKeyGrbit.NewKey);
                if (Api.TrySeek(this.jetSession, this.unspentTxTableId, SeekGrbit.SeekEQ))
                {
                    var blockIndexColumn = new Int32ColumnValue { Columnid = this.blockIndexColumnId };
                    var txIndexColumn = new Int32ColumnValue { Columnid = this.txIndexColumnId };
                    var txVersionColumn = new UInt32ColumnValue { Columnid = this.txVersionColumnId };
                    var isCoinbaseColumn = new BoolColumnValue { Columnid = this.isCoinbaseColumnId };
                    var outputStatesColumn = new BytesColumnValue { Columnid = this.outputStatesColumnId };
                    var txOutputBytesColumn = new BytesColumnValue { Columnid = this.txOutputBytesColumnId };
                    Api.RetrieveColumns(this.jetSession, this.unspentTxTableId, blockIndexColumn, txIndexColumn, txVersionColumn, isCoinbaseColumn, outputStatesColumn, txOutputBytesColumn);

                    var blockIndex = blockIndexColumn.Value.Value;
                    var txIndex = txIndexColumn.Value.Value;
                    var txVersion = txVersionColumn.Value.Value;
                    var isCoinbase = isCoinbaseColumn.Value.Value;
                    var outputStates = DataDecoder.DecodeOutputStates(outputStatesColumn.Value);

                    unspentTx = new UnspentTx(txHash, blockIndex, txIndex, txVersion, isCoinbase, outputStates);
                    return true;
                }

                unspentTx = default(UnspentTx);
                return false;
            }
        }
Example #8
0
        private DatatableRow[] EnumerateDatatableTable(Dictionary <string, string> ldapDisplayNameToDatatableColumnNameDictionary)
        {
            var datatable    = new List <DatatableRow>();
            var deletedCount = 0;

            wrn = Api.JetOpenTable(sesId, dbId, DATATABLE, null, 0, OpenTableGrbit.ReadOnly | OpenTableGrbit.Sequential, out _tableid);
            if (wrn == JET_wrn.Success)
            {
                // 获取将列名称映射到列ID的字典
                var columnDictionary = Api.GetColumnDictionary(sesId, _tableid);
                // 遍历所有表
                Api.MoveBeforeFirst(sesId, _tableid);
                while (Api.TryMoveNext(sesId, _tableid))
                {
                    var accountExpiresColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["accountExpires"]]
                    };
                    var displayNameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["displayName"]]
                    };
                    var distinguishedNameTagColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["DNT_col"]
                    };
                    var groupTypeColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["groupType"]]
                    };
                    var isDeletedColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["isDeleted"]]
                    };
                    var lastLogonColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lastLogonTimestamp"]]
                    };
                    var lmColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["dBCSPwd"]]
                    };
                    var lmHistoryColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lmPwdHistory"]]
                    };
                    var nameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["name"]]
                    };
                    var ntColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["unicodePwd"]]
                    };
                    var ntHistoryColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["ntPwdHistory"]]
                    };
                    var objColumn = new BoolColumnValue {
                        Columnid = columnDictionary["OBJ_col"]
                    };
                    var objectCategoryColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectCategory"]]
                    };
                    var objectSidColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectSid"]]
                    };
                    var parentDistinguishedNameTagColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["PDNT_col"]
                    };
                    var passwordLastSetColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pwdLastSet"]]
                    };
                    var pekListColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pekList"]]
                    };
                    var primaryGroupIdColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["primaryGroupID"]]
                    };
                    var rdnTypeColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["RDNtyp_col"]
                    };                                                                                      // The RDNTyp_col holds the Attribute-ID for the attribute being used as the RDN, such as CN, OU, DC
                    var samAccountNameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["sAMAccountName"]]
                    };
                    var timeColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary["time_col"]
                    };
                    var userAccountControlColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["userAccountControl"]]
                    };
                    var supplementalCredentialsColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["supplementalCredentials"]]
                    };

                    var columns = new List <ColumnValue>
                    {
                        accountExpiresColumn,
                        displayNameColumn,
                        distinguishedNameTagColumn,
                        groupTypeColumn,
                        isDeletedColumn,
                        lastLogonColumn,
                        nameColumn,
                        objColumn,
                        objectCategoryColumn,
                        objectSidColumn,
                        parentDistinguishedNameTagColumn,
                        passwordLastSetColumn,
                        primaryGroupIdColumn,
                        rdnTypeColumn,
                        samAccountNameColumn,
                        timeColumn,
                        userAccountControlColumn,
                        // dumpHashes
                        pekListColumn,
                        lmColumn,
                        ntColumn,
                        supplementalCredentialsColumn,
                        // includeHistoryHashes
                        lmHistoryColumn,
                        ntHistoryColumn,
                    };
                    Api.RetrieveColumns(sesId, _tableid, columns.ToArray());

                    // 跳过删除的对象
                    if (isDeletedColumn.Value.HasValue && isDeletedColumn.Value != 0)
                    {
                        deletedCount++;
                        continue;
                    }

                    // 一些已删除的对象没有isDeleted标志,但确实在DN后面附加了一个字符串 (https://support.microsoft.com/en-us/help/248047/phantoms--tombstones-and-the-infrastructure-master)
                    if (nameColumn.Value?.Contains("\nDEL:") ?? false)
                    {
                        deletedCount++;
                        continue;
                    }

                    SecurityIdentifier sid = null;
                    uint rid = 0;
                    if (objectSidColumn.Error == JET_wrn.Success)
                    {
                        var sidBytes = objectSidColumn.Value;
                        var ridBytes = sidBytes.Skip(sidBytes.Length - sizeof(int)).Take(sizeof(int)).Reverse().ToArray();
                        sidBytes = sidBytes.Take(sidBytes.Length - sizeof(int)).Concat(ridBytes).ToArray();
                        rid      = BitConverter.ToUInt32(ridBytes, 0);
                        sid      = new SecurityIdentifier(sidBytes, 0);
                    }
                    var row = new DatatableRow
                    {
                        AccountExpires          = accountExpiresColumn.Value,
                        DisplayName             = displayNameColumn.Value,
                        Dnt                     = distinguishedNameTagColumn.Value,
                        GroupType               = groupTypeColumn.Value,
                        LastLogon               = lastLogonColumn.Value,
                        Name                    = nameColumn.Value,
                        ObjectCategoryDnt       = objectCategoryColumn.Value,
                        Rid                     = rid,
                        Sid                     = sid,
                        ParentDnt               = parentDistinguishedNameTagColumn.Value,
                        Phantom                 = objColumn.Value == false,
                        LastPasswordChange      = passwordLastSetColumn.Value,
                        PrimaryGroupDnt         = primaryGroupIdColumn.Value,
                        RdnType                 = rdnTypeColumn.Value,
                        SamAccountName          = samAccountNameColumn.Value,
                        UserAccountControlValue = userAccountControlColumn.Value,
                    };

                    if (pekListColumn.Value != null)
                    {
                        row.PekList = pekListColumn.Value;
                    }

                    if (lmColumn.Value != null)
                    {
                        row.EncryptedLmHash = lmColumn.Value;
                    }

                    if (ntColumn.Value != null)
                    {
                        row.EncryptedNtHash = ntColumn.Value;
                    }

                    if (lmHistoryColumn.Value != null)
                    {
                        row.EncryptedLmHistory = lmHistoryColumn.Value;
                    }

                    if (ntHistoryColumn.Value != null)
                    {
                        row.EncryptedNtHistory = ntHistoryColumn.Value;
                    }

                    if (supplementalCredentialsColumn.Value != null)
                    {
                        row.SupplementalCredentialsBlob = supplementalCredentialsColumn.Value;
                    }

                    datatable.Add(row);
                }
            }
            return(datatable.ToArray());
        }
        /// <summary>
        /// Retrieve columns using the basic RetrieveColumns API.
        /// </summary>
        private void RetrieveWithRetrieveColumns()
        {
            var boolColumn = new BoolColumnValue { Columnid = this.columnidDict["boolean"] };
            var int32Column = new Int32ColumnValue { Columnid = this.columnidDict["int32"] };
            var int64Column = new Int64ColumnValue { Columnid = this.columnidDict["int64"] };
            var guidColumn = new GuidColumnValue { Columnid = this.columnidDict["guid"] };
            var stringColumn = new StringColumnValue { Columnid = this.columnidDict["unicode"] };

            var retrievecolumns = new ColumnValue[]
            {
                boolColumn,
                int32Column,
                int64Column,
                guidColumn,
                stringColumn,
            };

            for (int i = 0; i < NumRetrieves; ++i)
            {
                Api.JetBeginTransaction(this.session);

                Api.RetrieveColumns(this.session, this.tableid, retrievecolumns);

                bool actualBool = (bool)boolColumn.Value;
                int actualInt32 = (int)int32Column.Value;
                long actualInt64 = (long)int64Column.Value;
                Guid actualGuid = (Guid)guidColumn.Value;
                string actualString = stringColumn.Value;

                Assert.AreEqual(this.expectedBool, actualBool);
                Assert.AreEqual(this.expectedInt32, actualInt32);
                Assert.AreEqual(this.expectedInt64, actualInt64);
                Assert.AreEqual(this.expectedGuid, actualGuid);
                Assert.AreEqual(this.expectedString, actualString);

                Api.JetCommitTransaction(this.session, CommitTransactionGrbit.None);
            }
        }