Esempio n. 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;
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
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));
    }
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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));
    }
}
Esempio n. 6
0
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
}
Esempio n. 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;
}
Esempio n. 8
0
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);
}
Esempio n. 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;
}