/* * 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; }
BOOL Find( IN JET_TABLEID TableId, IN PWCHAR Name) { JET_ERR JetError; JetError = JetMove( SesId, TableId, JET_MoveFirst, 0); if ( JetError != JET_errSuccess) { RplAssert( TRUE, ("JetMove failed err=%d", JetError)); return( FALSE); } JetError = JetMakeKey( SesId, TableId, Name, ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey); if ( JetError != JET_errSuccess) { RplAssert( TRUE, ("MakeKey failed err=%d", JetError)); return( FALSE); } JetError = JetSeek( SesId, TableId, JET_bitSeekEQ); if ( JetError != JET_errSuccess) { if ( JetError == JET_errRecordNotFound) { // // This is an expected error => no break for this. // RplDbgPrint(("JetSeek for %ws failed with error = %d.\n", Name, JetError)); } else { RplAssert( TRUE, ("JetSeek failed err=%d", JetError)); } return( FALSE); } return( TRUE); }
VOID Enum( IN JET_TABLEID TableId, IN JET_COLUMNID ColumnId, IN PCHAR IndexName) { WCHAR Name[ 20]; DWORD NameSize; JET_ERR ForJetError; JET_ERR JetError; Call( JetSetCurrentIndex( SesId, TableId, IndexName)); for ( ForJetError = JetMove( SesId, TableId, JET_MoveFirst, 0); ForJetError == JET_errSuccess; ForJetError = JetMove( SesId, TableId, JET_MoveNext, 0)) { JetError = JetRetrieveColumn( SesId, TableId, ColumnId, Name, sizeof( Name), &NameSize, 0, NULL); if ( JetError != JET_errSuccess) { RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError)); continue; } RplDbgPrint(( "%ws\n", Name)); } }
HRESULT Table::MoveCursor(const int32_t offset) { const JET_ERR jetErr = JetMove(sesId_, tableId_, offset, 0); if (jetErr != JET_errSuccess) { SetLastErrorDesc(Error("JetMove", jetErr, __FUNCTION__)); if (JET_errNoCurrentRecord == jetErr) { return E_RECORD_NOT_FOUND; } return E_FAIL; } return S_OK; }
VOID ResumeList( IN PRPL_SESSION pSession, IN PCHAR String ) { WCHAR ResumeValue[ 20]; DWORD NameSize; JET_ERR ForJetError; DWORD ResumeHandle; DWORD ServerHandle; Call( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle)); // // We could skip JetMove, but if we do so, we need to modify error // testing for the first JetRetrieveColumn() call (it would return // JET_errNoCurrentRecord for the empty table). // for ( ForJetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveFirst, 0); ForJetError == JET_errSuccess; ForJetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveNext, 0)) { Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId, ResumeTable[ RESUME_ResumeHandle].ColumnId, &ResumeHandle, sizeof( ResumeHandle), &NameSize, 0, NULL)); Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId, ResumeTable[ RESUME_ResumeValue].ColumnId, ResumeValue, sizeof( ResumeValue), &NameSize, 0, NULL)); Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId, ResumeTable[ RESUME_ServerHandle].ColumnId, &ServerHandle, sizeof( ServerHandle), &NameSize, 0, NULL)); RplDump( RG_DebugLevel & RPL_DEBUG_RESUME, ( "%s ResumeH=0x%x,Value=%ws,ServerH=0x%x", String, ResumeHandle, ResumeValue, ServerHandle)); } }
VOID ResumePrune( IN PRPL_SESSION pSession, IN DWORD ServerHandle ) /*++ This function returns no errors, since failure to delete old resume handles is not considered fatal (and there is very little we can do about this). --*/ { JET_ERR JetError; #ifdef RPL_DEBUG if ( RG_DebugLevel == (DWORD)(-1)) { ResumeList( pSession, "++ResumePrune"); } #endif // RPL_DEBUG CallR( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ServerHandle)); CallR( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ServerHandle, sizeof( ServerHandle), JET_bitNewKey)); #ifdef NOT_YET JetError = JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ); #else JetError = JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ | JET_bitSetIndexRange); #endif if ( JetError == JET_errSuccess) { #ifdef NOT_YET CallR( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ServerHandle, sizeof( ServerHandle), JET_bitNewKey)); CallR( JetSetIndexRange( pSession->SesId, pSession->ResumeTableId, JET_bitRangeInclusive | JET_bitRangeUpperLimit)); #endif do { Call( JetDelete( pSession->SesId, pSession->ResumeTableId)); JetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveNext, 0); } while ( JetError == JET_errSuccess); } #ifdef RPL_DEBUG if ( RG_DebugLevel == (DWORD)(-1)) { ResumeList( pSession, "--ResumePrune"); } #endif // RPL_DEBUG }
/* * 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; }
BOOL ResumeKeySet( IN PRPL_SESSION pSession, IN DWORD ServerHandle, IN PVOID ResumeValue, IN DWORD ResumeSize, OUT PDWORD pResumeHandle ) { DWORD DataSize; *pResumeHandle = 0; // just in case we fail below #ifdef RPL_DEBUG if ( RG_DebugLevel == (DWORD)(-1)) { ResumeList( pSession, "++ResumeKeySet"); } #endif // RPL_DEBUG CallB( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle)); #if 0 // // We do NOT call JetMove() here, because in the case of an empty table // it returns error JET_errNoCurrentRecord. Also, if JetMove() is used // here, then ordering of elements in the table is such that ResumePrune() // function deletes only the OLDEST record for a given server handle. // CallB( JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveLast, 0)); #endif CallB( JetPrepareUpdate( pSession->SesId, pSession->ResumeTableId, JET_prepInsert)); CallB( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId, ResumeTable[ RESUME_ResumeHandle].ColumnId, pResumeHandle, sizeof( *pResumeHandle), &DataSize, JET_bitRetrieveCopy, NULL)); if ( DataSize != sizeof( *pResumeHandle) || *pResumeHandle == 0) { RplDump( ++RG_Assert, ( "DataSize=%d", DataSize)); return( FALSE); } CallB( JetSetColumn( pSession->SesId, pSession->ResumeTableId, ResumeTable[ RESUME_ResumeValue].ColumnId, ResumeValue, ResumeSize, 0, NULL)); CallB( JetSetColumn( pSession->SesId, pSession->ResumeTableId, ResumeTable[ RESUME_ServerHandle].ColumnId, &ServerHandle, sizeof( ServerHandle), 0, NULL)); CallB( JetUpdate( pSession->SesId, pSession->ResumeTableId, NULL, 0, NULL)); #ifdef RPL_DEBUG if ( RG_DebugLevel & RPL_DEBUG_RESUME) { WCHAR ValueBuffer[ 32]; DWORD Length; PWCHAR Value; Length = wcslen( (PWCHAR)ResumeValue); if ( (Length + 1) * sizeof(WCHAR) < ResumeSize) { wcscpy( ValueBuffer, (PWCHAR)ResumeValue); wcscat( ValueBuffer, L"!"); wcscat( ValueBuffer, (PWCHAR)ResumeValue + Length + 1); Value = ValueBuffer; } else { Value = (PWCHAR)ResumeValue; } RplDump( RG_DebugLevel & RPL_DEBUG_RESUME, ( "ResumeKeySet: Handle=0x%x, Value=%.20ws", *pResumeHandle, Value)); } #endif // RPL_DEBUG return( TRUE); }
int main(int argc, char * argv[]) { //Linked list to contain the datatable columns metadata typedef struct _COLUMNLIST { int type; int id; char name[NAME_SIZE]; struct _COLUMNLIST * next; }COLUMNLIST; COLUMNLIST *columnList; COLUMNLIST *listHead = (COLUMNLIST *)malloc(sizeof(COLUMNLIST)); JET_ERR err; JET_INSTANCE instance = JET_instanceNil; JET_SESID sesid; JET_DBID dbid; JET_TABLEID tableid ; /* JET_COLUMNDEF *columndefid = malloc(sizeof(JET_COLUMNDEF)); JET_COLUMNDEF *columndeftype = malloc(sizeof(JET_COLUMNDEF)); JET_COLUMNDEF *columndeftypecol = malloc(sizeof(JET_COLUMNDEF)); JET_COLUMNDEF *columndefname = malloc(sizeof(JET_COLUMNDEF)); JET_COLUMNDEF *columndefobjid = malloc(sizeof(JET_COLUMNDEF)); */ JET_COLUMNDEF _columndefid; JET_COLUMNDEF _columndeftype; JET_COLUMNDEF _columndeftypecol; JET_COLUMNDEF _columndefname; JET_COLUMNDEF _columndefobjid; JET_COLUMNDEF *columndefid = &_columndefid; JET_COLUMNDEF *columndeftype = &_columndeftype; JET_COLUMNDEF *columndeftypecol = &_columndeftypecol; JET_COLUMNDEF *columndefname = &_columndefname; JET_COLUMNDEF *columndefobjid = &_columndefobjid; unsigned long a,b,c,d,e; long bufferid[16]; char buffertype[256]; char buffertypecol[8]; char buffername[NAME_SIZE]; long bufferobjid[8]; //Actually max buffer size should depend on the page size but it doesn't. Further investigation required. unsigned char jetBuffer[JET_BUFFER_SIZE]; unsigned long jetSize; char *baseName = argv[2]; char *targetTable; char *tableName; unsigned int datatableId = 0xffffffff; unsigned int i; FILE *dump; char dumpFileName[64]; //SYSTEMTIME lt; RPC_WSTR Guid = NULL; LPWSTR stringSid = NULL; long long sd_id = 0; listHead->next = NULL; columnList = listHead; if( argc < 3) PrintUsage(); if(!strcmp(argv[1],"ad") || !strcmp(argv[1],"sid") || !strcmp(argv[1],"att") || !strcmp(argv[1],"cat") || !strcmp(argv[1],"users")) targetTable = "datatable"; else if(!strcmp(argv[1],"ace")) targetTable = "sd_table"; else PrintUsage(); if(!strcmp(argv[1],"sid")) { printf("To dump Exchange Mailbox security descriptors, \nenter the ATT value for your specific Exchange Schema:\n(msDS-IntId value for msExchMailboxSecurityDescriptor, \nfound in 'esent_dump att' results)\n"); printf("Otherwise just input anything and press enter\n"); scanf_s("%s",&exchangeMailboxSDCol[4], 28); } //Our result file, don't modify if you want to use auto import scripts from dbbrowser //GetLocalTime(<); //sprintf_s(dumpFileName, 64, "%s_ntds_%02d-%02d-%04d_%02dh%02d.csv",argv[1], lt.wDay, lt.wMonth, lt.wYear, lt.wHour, lt.wMinute); sprintf_s(dumpFileName, 64, "%s-ntds.dit-dump.csv", argv[1]); fopen_s(&dump, dumpFileName, "w"); if (dump == 0) { printf("Could not open csv file for writing\n"); return(-1); } if(!strcmp(argv[1],"ace")) fprintf(dump, "sd_id\tPrimaryOwner\tPrimaryGroup\tACEType\tACEFlags\tAccessMask\tFlags\tObjectType\tInheritedObjectType\tTrusteeSID\n"); // Initialize ESENT. // See http://msdn.microsoft.com/en-us/library/windows/desktop/gg269297(v=exchg.10).aspx for error codes err = JetSetSystemParameter(0, JET_sesidNil, JET_paramDatabasePageSize, 8192, NULL); err = JetCreateInstance(&instance, "blabla"); err = JetInit(&instance); err = JetBeginSession(instance, &sesid, 0, 0); err = JetAttachDatabase(sesid, baseName, JET_bitDbReadOnly); if (err != 0) { printf("JetAttachDatabase : %i\n", err); return(-1); } err = JetOpenDatabase(sesid, baseName, 0, &dbid, 0); if (err != 0) { printf("JetOpenDatabase : %i\n", err); return(-1); } //Let's enumerate the metadata about datatable (AD table) tableName = "MSysObjects"; err = JetOpenTable(sesid, dbid, tableName, 0, 0, JET_bitTableReadOnly, &tableid); printf("[*]Opened table: %s\n", tableName); //Obtain structures with necessary information to retrieve column values err = JetGetColumnInfo(sesid, dbid, tableName, "Id", columndefid, sizeof(JET_COLUMNDEF), JET_ColInfo); err = JetGetColumnInfo(sesid, dbid, tableName, "Type", columndeftype, sizeof(JET_COLUMNDEF), JET_ColInfo); err = JetGetColumnInfo(sesid, dbid, tableName, "ColtypOrPgnoFDP", columndeftypecol, sizeof(JET_COLUMNDEF), JET_ColInfo); err = JetGetColumnInfo(sesid, dbid, tableName, "Name", columndefname, sizeof(JET_COLUMNDEF), JET_ColInfo); err = JetGetColumnInfo(sesid, dbid, tableName, "ObjIdTable", columndefobjid, sizeof(JET_COLUMNDEF), JET_ColInfo); //printf("Type de colonne :%d, de longueur : %d", columndef->coltyp, columndef->cbMax); //Position the cursor at the first record JetMove(sesid, tableid, JET_MoveFirst, 0); //Retrieve columns metadata do { JetRetrieveColumn(sesid, tableid, columndefid->columnid, 0, 0, &a, 0, 0); JetRetrieveColumn(sesid, tableid, columndefid->columnid, bufferid, a, 0, 0, 0); JetRetrieveColumn(sesid, tableid, columndeftype->columnid, 0, 0, &b, 0, 0); JetRetrieveColumn(sesid, tableid, columndeftype->columnid, buffertype, b, 0, 0, 0); JetRetrieveColumn(sesid, tableid, columndeftypecol->columnid, 0, 0, &e, 0, 0); JetRetrieveColumn(sesid, tableid, columndeftypecol->columnid, buffertypecol, e, 0, 0, 0); JetRetrieveColumn(sesid, tableid, columndefname->columnid, 0, 0, &c, 0, 0); JetRetrieveColumn(sesid, tableid, columndefname->columnid, buffername, c, 0, 0, 0); buffername[c]='\0'; if(datatableId == 0xffffffff && !strcmp(buffername, targetTable)) { //We found the target table in the metadata, pickup its id and make another pass datatableId = bufferid[0]; printf("[*]%s tableID found : %d\n", buffername, datatableId); JetMove(sesid, tableid, JET_MoveFirst, 0); continue; } JetRetrieveColumn(sesid, tableid, columndefobjid->columnid, 0, 0, &d, 0, 0); JetRetrieveColumn(sesid, tableid, columndefobjid->columnid, bufferobjid, d, 0, 0, 0); //We got the correct type and table id, let's dump the column name and add it to the column list if(buffertype[0] == 2 && bufferobjid[0] == datatableId) { unsigned int j; columnList->next = (COLUMNLIST *)malloc(sizeof(COLUMNLIST)); if(!columnList->next) { printf("Memory allocation failed during metadata dump\n"); return(-1); } columnList = columnList->next; columnList->next = NULL; for(j=0;j<c;j++) columnList->name[j] = buffername[j]; columnList->name[c] = '\0'; columnList->type = buffertypecol[0]; columnList->id = bufferid[0]; } }while(JetMove(sesid, tableid, JET_MoveNext, 0) == JET_errSuccess); JetCloseTable(sesid, tableid); //Let's use our metadata to dump the whole AD schema tableName = targetTable; err = JetOpenTable(sesid, dbid, tableName, 0, 0, JET_bitTableReadOnly, &tableid); printf("[*]Opened table: %s\n", tableName); printf("Dumping %s column names...\n", tableName); columnList = listHead; while(columnList->next) { columnList = columnList->next; if(!strcmp("ad",argv[1])) fprintf(dump,"%d:%s\t",columnList->type,columnList->name); else if(ValidateColumn(argv[1], columnList->name)) fprintf(dump, "%s\t", translateATT(columnList->name)); }; fprintf(dump,"\n"); printf("Dumping content...\n"); JetMove(sesid, tableid, JET_MoveFirst, 0); do { columnList = listHead; while(columnList->next) { columnList = columnList->next; if(ValidateColumn(argv[1], columnList->name)) { //NOTE that this approach implies post processing multi valued columns if you re-use this code... err = JetRetrieveColumn(sesid, tableid, columnList->id, 0, 0, &jetSize, 0, 0); #ifdef _DEBUG //positive are warnings, -1047 is invalid buffer size which is expected here if (err < 0 && err != -1047) { printf("JetRetrieveColumn error : %i, jetSize : %d\n", err, jetSize); return(-2); } if (jetSize > JET_BUFFER_SIZE) { printf("Jet Buffer incorrect size preset: %d bytes are needed\n",jetSize); return(-2); } #endif memset(jetBuffer,0,JET_BUFFER_SIZE); switch(columnList->type) { //signed int types case 4: JetRetrieveColumn(sesid, tableid, columnList->id, jetBuffer, jetSize, 0, 0, 0); //Specific useraccountcontrol code, currently dead code if(!strcmp("users",argv[1]) && !strcmp("ATTj589832",columnList->name)) { if(jetBuffer[0] & ADS_UF_ACCOUNTDISABLE) fprintf(dump,"disabled "); if(jetBuffer[0] & ADS_UF_DONT_EXPIRE_PASSWD) fprintf(dump,"dontexpire "); if(jetBuffer[0] & ADS_UF_LOCKOUT) fprintf(dump,"lockedout "); if(jetBuffer[0] & ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED) fprintf(dump,"reversiblepwd "); } else fprintf(dump,"%d",*(int *)jetBuffer); /* fprintf(dump,"%u_",*(unsigned int *)jetBuffer); for(unsigned int i=0;i<jetSize;i++) fprintf(dump,"%.2X",jetBuffer[i]); */ break; //signed long long type case 5: JetRetrieveColumn(sesid, tableid, columnList->id, jetBuffer, jetSize, 0, 0, 0); if(!strcmp("sd_id",columnList->name)) sd_id = *(long long *)jetBuffer; else fprintf(dump,"%lld",*(long long *)jetBuffer); break; //Raw binary types case 9: JetRetrieveColumn(sesid, tableid, columnList->id, jetBuffer, jetSize, 0, 0, 0); for(i=0;i<jetSize;i++) fprintf(dump,"%.2X",jetBuffer[i]); break; case 11: /* We check matches on security descriptor, then SID * to display accordingly, otherwise hex dump */ JetRetrieveColumn(sesid, tableid, columnList->id, jetBuffer, jetSize, 0, 0, 0); if(!strcmp("sd_value",columnList->name) && IsValidSecurityDescriptor(jetBuffer)) { //Correct sd_id because sd_id column is before sd_value column in sd_table DumpACE(sd_id, jetBuffer, dump); } else if(!strcmp("ATTr589970",columnList->name) && IsValidSid(jetBuffer)) { //AD SID storage swaps endianness in RID bytes (yeah !) unsigned char temp; temp = jetBuffer[24]; jetBuffer[24] = jetBuffer[27]; jetBuffer[27] = temp; temp = jetBuffer[25]; jetBuffer[25] = jetBuffer[26]; jetBuffer[26] = temp; ConvertSidToStringSid((PSID)jetBuffer, &stringSid); fwprintf(dump, L"%s", stringSid); LocalFree(stringSid); stringSid = NULL; } //NT Security Descriptor index to lookup in sd_table else if(!strcmp("sid",argv[1]) && ( !strcmp("ATTp131353",columnList->name) || !strcmp(exchangeMailboxSDCol,columnList->name) )) { fprintf(dump,"%d",*(int *)jetBuffer); } //Schema-Id-Guid else if(!strcmp("sid",argv[1]) && !strcmp("ATTk589972",columnList->name) ) { UuidToString((UUID *)jetBuffer, &Guid); fwprintf(dump,L"%s",Guid); RpcStringFree(&Guid); Guid = NULL; } else //hex dump for(i=0;i<jetSize;i++) fprintf(dump,"%.2X",jetBuffer[i]); /* dumping type 11 as int (?) else { fprintf(dump,"%d",*(int *)jetBuffer); printf("dump type 11 as int : %d\n", *(int *)jetBuffer); } */ break; //widechar text types case 10: case 12: JetRetrieveColumn(sesid, tableid, columnList->id, jetBuffer, jetSize, 0, 0, 0); for(i=0;i<jetSize/2;i++) if((wchar_t)jetBuffer[2*i] != '\t' || (wchar_t)jetBuffer[2*i] != '\n' || (wchar_t)jetBuffer[2*i] != '\r') fwprintf(dump,L"%c",(wchar_t)jetBuffer[2*i]); break; }; if(strcmp("ace",argv[1])) fprintf(dump,"\t"); } } if(strcmp("ace",argv[1])) fprintf(dump,"\n"); }while(JetMove(sesid, tableid, JET_MoveNext, 0) == JET_errSuccess); // cleanup printf("cleaning...\n"); JetCloseTable(sesid, tableid); JetEndSession(sesid, 0); JetTerm(instance); fclose(dump); return 0; }