/* * Parse NTDS file database and extract bitlocker related attributes * (Key package, recovery password, recovery guid, volume id) */ int NTDS_Bitlocker_ParseDatabase(s_parser *parser,ll_bitlockerAccountInfo *bitlockerAccountInfo) { s_bitlockerAccountInfo bitlockerAccountEntry; JET_TABLEID tableid; JET_ERR jet_err; int success = NTDS_SUCCESS; int retCode; jet_err = JetOpenTable(parser->sesid,parser->dbid,NTDS_TBL_OBJ,NULL,0,JET_bitTableReadOnly | JET_bitTableSequential,&tableid); if(jet_err!=JET_errSuccess) { NTDS_ErrorPrint(parser,"JetOpenTable",jet_err); return NTDS_API_ERROR; } /* Get attributes identifiers */ jet_err = JetGetTableColumnInfo(parser->sesid,tableid,ATT_BITLOCKER_MSFVE_KEY_PACKAGE,&parser->columndef[ID_MSFVE_KEY_PACKAGE],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_BITLOCKER_MSFVE_RECOVERY_GUID,&parser->columndef[ID_MSFVE_RECOVERY_GUID],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_BITLOCKER_MSFVE_RECOVERY_PASSWORD,&parser->columndef[ID_MSFVE_RECOVERY_PASSWORD],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_BITLOCKER_MSFVE_VOLUME_GUID,&parser->columndef[ID_MSFVE_VOLUME_GUID],sizeof(JET_COLUMNDEF),JET_ColInfo); if(jet_err!=JET_errSuccess) { NTDS_ErrorPrint(parser,"JetGetTableColumnInfo ",jet_err); JetCloseTable(parser->sesid,tableid); return NTDS_API_ERROR; } /* Parse datatable for Bitlocker accounts */ jet_err = JetMove(parser->sesid,tableid,JET_MoveFirst,0); do{ retCode = NTDS_Bitlocker_ParseRecord(parser,tableid,&bitlockerAccountEntry); if(retCode==NTDS_SUCCESS) { if(!bitlockerAccountInfoNew(bitlockerAccountInfo,&bitlockerAccountEntry)) { puts("Fatal error: not enough memory!"); return NTDS_MEM_ERROR; } } else if(retCode==NTDS_MEM_ERROR) { puts("Fatal error: not enough memory!"); return retCode; } }while(JetMove(parser->sesid,tableid,JET_MoveNext,0) == JET_errSuccess); if(!*bitlockerAccountInfo) success = NTDS_EMPTY_ERROR; jet_err = JetCloseTable(parser->sesid,tableid); if(jet_err!=JET_errSuccess) { NTDS_ErrorPrint(parser,"JetCloseTable",jet_err); return NTDS_API_ERROR; } return success; }
HRESULT Table::GetColName(const uint32_t colIndex, wchar_t * colNameBuf, uint32_t & colNameBufSizeInChars) { try { if (colNames_.empty()) { JET_COLUMNLIST colList = { sizeof(JET_COLUMNLIST) }; const JET_ERR jetErr = JetGetTableColumnInfo(sesId_, tableId_, nullptr, &colList, sizeof(colList), JET_ColInfoList); if (jetErr != JET_errSuccess) { throw Error("JetGetTableColumnInfo", jetErr, __FUNCTION__); } Table tableWithColNames(sesId_, dbId_, colList.tableid); for (size_t i = 0; i < colList.cRecord; ++i) { wchar_t buff[128] = L""; ULONG numActualBytes = 0; JET_ERR jetErr2 = JetRetrieveColumn(sesId_, colList.tableid, colList.columnidcolumnname, buff, sizeof(buff), &numActualBytes, 0, nullptr); if (jetErr2 != JET_errSuccess) { throw Error("JetRetrieveColumn", jetErr2, __FUNCTION__); } colNames_.emplace_back(buff); if (colNames_.size() < colList.cRecord) { tableWithColNames.MoveCursor(+1); } } } if (colIndex >= colNames_.size()) { throw HrError("invalid colIndex", E_INVALIDARG, __FUNCTION__); } if (colNameBufSizeInChars < colNames_[colIndex].size() + 1) { colNameBufSizeInChars = boost::numeric_cast<uint32_t>(colNames_[colIndex].size() + 1); throw HrError("colName buffer is too small.", E_INVALIDARG, __FUNCTION__); } wcscpy_s(colNameBuf, colNameBufSizeInChars, colNames_[colIndex].c_str()); return S_OK; } catch (const std::exception & e) { SetLastErrorDesc(e); } return E_FAIL; }
VOID ListTable( IN PCHAR TableName, IN PCHAR ColumnName, IN PCHAR IndexName) { JET_TABLEID TableId; JET_COLUMNDEF ColumnDef; Call( JetOpenTable( SesId, DbId, TableName, NULL, 0, JET_bitTableDenyWrite, &TableId)); Call( JetGetTableColumnInfo( SesId, TableId, ColumnName, &ColumnDef, sizeof( ColumnDef), JET_ColInfo)); RplDbgPrint(( "\tTable: %s\n", TableName)); Enum( TableId, ColumnDef.columnid, IndexName); Call( JetCloseTable( SesId, TableId)); }
IColumn * Table::GetColumn(const wchar_t * colName) { try { JET_COLUMNDEF colDef = { sizeof(JET_COLUMNDEF) }; const JET_ERR jetErr = JetGetTableColumnInfo(sesId_, tableId_, colName, &colDef, sizeof(JET_COLUMNDEF), JET_ColInfo); if (jetErr != JET_errSuccess) { throw Error("JetGetTableColumnInfo", jetErr, __FUNCTION__); } jet::ColumnDataTraits dataTraits(colDef); std::unique_ptr<IColumn> column(std::make_unique<Column>(sesId_, tableId_, colDef.columnid)); return column.release(); } catch (const std::exception & e) { SetLastErrorDesc(e); } return nullptr; }
HRESULT Table::GetNumCols(uint32_t & numCols) { try { JET_COLUMNLIST colList = { sizeof(JET_COLUMNLIST) }; const JET_ERR jetErr = JetGetTableColumnInfo(sesId_, tableId_, nullptr, &colList, sizeof(colList), JET_ColInfoList); if (jetErr != JET_errSuccess) { throw Error("JetGetTableColumnInfo", jetErr, __FUNCTION__); } numCols = colList.cRecord; const JET_ERR jetErr2 = JetCloseTable(sesId_, colList.tableid); if (jetErr2 != JET_errSuccess) { throw Error("JetCloseTable", jetErr, __FUNCTION__); } return S_OK; } catch (const std::exception & e) { SetLastErrorDesc(e); } return E_FAIL; }
/* * Parse NTDS.dit file and its "datatable" table * Lokk for PEK, SAM account name & type, hashes, SID and hashes history if asked */ BOOL NTDS_NTLM_ParseDatabase(s_parser *parser,ll_ldapAccountInfo *ldapAccountInfo,s_NTLM_pek_ciphered *pek_ciphered,BOOL with_history){ s_ldapAccountInfo ldapAccountEntry; JET_TABLEID tableid; JET_ERR jet_err; int success = NTDS_SUCCESS; int retCode; jet_err = JetOpenTable(parser->sesid,parser->dbid,NTDS_TBL_OBJ,NULL,0,JET_bitTableReadOnly | JET_bitTableSequential,&tableid); if(jet_err!=JET_errSuccess) { NTDS_ErrorPrint(parser,"JetOpenTable",jet_err); return NTDS_API_ERROR; } /* Get attributes identifiers */ jet_err = JetGetTableColumnInfo(parser->sesid,tableid,ATT_SAM_ACCOUNT_NAME,&parser->columndef[ID_SAM_ACCOUNT_NAME],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_OBJECT_SID,&parser->columndef[ID_OBJECT_SID],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_LM_HASH,&parser->columndef[ID_LM_HASH],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_NT_HASH,&parser->columndef[ID_NT_HASH],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_PEK,&parser->columndef[ID_PEK],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_SAM_ACCOUNT_TYPE,&parser->columndef[ID_SAM_ACCOUNT_TYPE],sizeof(JET_COLUMNDEF),JET_ColInfo); if(with_history) { jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_LM_HASH_HISTORY,&parser->columndef[ID_LM_HASH_HISTORY],sizeof(JET_COLUMNDEF),JET_ColInfo); jet_err |= JetGetTableColumnInfo(parser->sesid,tableid,ATT_NT_HASH_HISTORY,&parser->columndef[ID_NT_HASH_HISTORY],sizeof(JET_COLUMNDEF),JET_ColInfo); } if(jet_err!=JET_errSuccess) { NTDS_ErrorPrint(parser,"JetGetTableColumnInfo ",jet_err); JetCloseTable(parser->sesid,tableid); return NTDS_API_ERROR; } /* Parse datatable for SAM accounts */ jet_err = JetMove(parser->sesid,tableid,JET_MoveFirst,0); do{ retCode = NTDS_NTLM_ParseSAMRecord(parser,tableid,&ldapAccountEntry,with_history); if(retCode==NTDS_SUCCESS) { if(!ldapAccountInfoNew(ldapAccountInfo,&ldapAccountEntry)) { puts("Fatal error: not enough memory!"); return NTDS_MEM_ERROR; } } else if(retCode==NTDS_MEM_ERROR) { puts("Fatal error: not enough memory!"); return retCode; } }while(JetMove(parser->sesid,tableid,JET_MoveNext,0) == JET_errSuccess); if(!*ldapAccountInfo) success = NTDS_EMPTY_ERROR; /* Parse datatable for ciphered PEK */ jet_err = JetMove(parser->sesid,tableid,JET_MoveFirst,0); do{ if(NTDS_NTLM_ParsePEKRecord(parser,tableid,pek_ciphered)==NTDS_SUCCESS) { success = NTDS_SUCCESS; break; } }while(JetMove(parser->sesid,tableid,JET_MoveNext,0) == JET_errSuccess); jet_err = JetCloseTable(parser->sesid,tableid); if(jet_err!=JET_errSuccess) { NTDS_ErrorPrint(parser,"JetCloseTable",jet_err); return NTDS_API_ERROR; } return success; }