Example #1
0
/*
 * 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;
}
Example #2
0
DWORD ResumeDeleteTable(
    IN      PRPL_SESSION        pSession
    )
{
    if ( pSession->ResumeTableId != 0) {
        CallM( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
        CallM( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
    }
    return( NO_ERROR);
}
Example #3
0
HRESULT Table::Close()
{
    const JET_ERR jetErr = JetCloseTable(sesId_, tableId_);
    if (jetErr != JET_errSuccess)
    {
        SetLastErrorDesc(Error("JetCloseTable", jetErr, __FUNCTION__));
        return E_FAIL;
    }
    sesId_ = JET_sesidNil;
    tableId_ = JET_tableidNil;
    return S_OK;
}
Example #4
0
Table::~Table()
{
    if (tableId_ != JET_tableidNil)
    {
        const JET_ERR jetErr = JetCloseTable(sesId_, tableId_);
        JET_ASSERT(jetErr, "JetCloseTable");
        sesId_ = JET_sesidNil;
        tableId_ = JET_tableidNil;
        dbId_ = JET_dbidNil;
        tableName_.clear();
        colNames_.clear();
    }
}
Example #5
0
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));
}
Example #6
0
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;
}
Example #7
0
/*
 * 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;
}
Example #8
0
DWORD ResumeCreateTable( OUT PRPL_SESSION pSession)
/*++
    Table gets created, then closed.  The reason we do not keep the table
    open is that creator of a table holds exclusive access to that table
    until he/she closes the table.  This would prevent other threads from
    using the table.
--*/
{
    JET_COLUMNDEF           ColumnDef;
    JET_ERR                 JetError;
    DWORD                   index;
    DWORD                   Offset;
    CHAR                    IndexKey[ 255];

    JetError = JetCreateTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME,
            RESUME_TABLE_PAGE_COUNT, RESUME_TABLE_DENSITY, &pSession->ResumeTableId);
    if ( JetError == JET_errTableDuplicate) {
        //
        //  This would happend only if last time we did not shutdown properly
        //  so ResumeTable never got deleted.
        //
        CallM( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
        JetError = JetCreateTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME,
                RESUME_TABLE_PAGE_COUNT, RESUME_TABLE_DENSITY, &pSession->ResumeTableId);
    }
    if ( JetError != JET_errSuccess) {
        return( MapJetError( JetError));
    }

    //
    //  Create columns.  First initalize fields that do not change between
    //  addition of columns.
    //
    ColumnDef.cbStruct  = sizeof(ColumnDef);
    ColumnDef.columnid  = 0;
    ColumnDef.wCountry  = 1;
    ColumnDef.langid    = 0x0409;       //  USA english
    ColumnDef.cp        = 1200;         //  UNICODE codepage
    ColumnDef.wCollate  = 0;
    ColumnDef.cbMax     = 0;

    for ( index = 0;  index < RESUME_TABLE_LENGTH;  index++) {

        ColumnDef.coltyp   = ResumeTable[ index].ColumnType;
        //
        //  All columns are variable length, but resume handle is autoincrement
        //  as well.
        //
        ColumnDef.grbit = (index==RESUME_ResumeHandle) ?
                JET_bitColumnAutoincrement : 0;

        CallM( JetAddColumn( pSession->SesId, pSession->ResumeTableId,
                    ResumeTable[ index].ColumnName, &ColumnDef,
                    NULL, 0, &ResumeTable[ index].ColumnId));
    }

    //
    //  We need resume handle as an index, so that at Enum() time we quickly
    //  find resume value for a given resume handle.  Beging primary index,
    //  this index is also unique.
    //
    Offset = AddKey( IndexKey, '+', ResumeTable[ RESUME_ResumeHandle].ColumnName);
    IndexKey[ Offset++] = '\0';
    CallM( JetCreateIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle,
            JET_bitIndexPrimary, IndexKey, Offset, 50));

    //
    //  We need server handle as an index, so that at Close() time we quickly
    //  enumerate all resume keys for a given server handle, then delete them.
    //  This index is NOT unique since we can have a number of resume handles
    //  per client.
    //
    Offset = AddKey( IndexKey, '+', ResumeTable[ RESUME_ServerHandle].ColumnName);
    IndexKey[ Offset++] = '\0';
    CallM( JetCreateIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ServerHandle,
            0, IndexKey, Offset, 50));

#ifdef RPL_DEBUG
    if ( RG_DebugLevel == (DWORD)(-1)) {
        ResumeList( pSession, "ResumeCreateTable");
    }
#endif // RPL_DEBUG

    CallM( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
    return( ERROR_SUCCESS);
}
Example #9
0
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(&lt);
	//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;
}