/* * 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; }
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)); }
// This is the raw NTDS command function. When the remote user // sends a command request for extapi_ntds_parse, this function fires. // It calls the setup routines for our Jet Instance, attaches the isntance // to the NTDS.dit database the user specified, and creates our channel. // The user interacts with the NTDS database through that channel from that point on. DWORD ntds_parse(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD res = ERROR_SUCCESS; struct jetState *ntdsState = calloc(1,sizeof(struct jetState)); PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_NTDS_PATH); // Check if the File exists if (0xffffffff == GetFileAttributes(filePath)) { res = 2; goto out; } strncpy_s(ntdsState->ntdsPath, 255, filePath, 254); // Attempt to get the SysKey from the Registry unsigned char sysKey[17]; if (!get_syskey(sysKey)) { res = GetLastError(); goto out; } JET_ERR startupStatus = engine_startup(ntdsState); if (startupStatus != JET_errSuccess) { res = startupStatus; goto out; } // Start a Session in the Jet Instance JET_ERR sessionStatus = JetBeginSession(ntdsState->jetEngine, &ntdsState->jetSession, NULL, NULL); if (sessionStatus != JET_errSuccess) { JetTerm(ntdsState->jetEngine); res = sessionStatus; goto out; } JET_ERR openStatus = open_database(ntdsState); if (openStatus != JET_errSuccess) { JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL); JetTerm(ntdsState->jetEngine); res = openStatus; goto out; } JET_ERR tableStatus = JetOpenTable(ntdsState->jetSession, ntdsState->jetDatabase, "datatable", NULL, 0, JET_bitTableReadOnly | JET_bitTableSequential, &ntdsState->jetTable); if (tableStatus != JET_errSuccess) { engine_shutdown(ntdsState); res = tableStatus; goto out; } // Create the structure for holding all of the Column Definitions we need struct ntdsColumns *accountColumns = calloc(1, sizeof(struct ntdsColumns)); JET_ERR columnStatus = get_column_info(ntdsState, accountColumns); if (columnStatus != JET_errSuccess) { engine_shutdown(ntdsState); free(accountColumns); res = columnStatus; goto out; } JET_ERR pekStatus; struct encryptedPEK *pekEncrypted = calloc(1,sizeof(struct encryptedPEK)); struct decryptedPEK *pekDecrypted = calloc(1,sizeof(struct decryptedPEK)); // Get and Decrypt the Password Encryption Key (PEK) pekStatus = get_PEK(ntdsState, accountColumns, pekEncrypted); if (pekStatus != JET_errSuccess) { res = pekStatus; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } if (!decrypt_PEK(sysKey, pekEncrypted, pekDecrypted)) { res = GetLastError(); free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } // Set our Cursor on the first User record JET_ERR cursorStatus = find_first(ntdsState); if (cursorStatus != JET_errSuccess) { res = cursorStatus; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } cursorStatus = next_user(ntdsState, accountColumns); if (cursorStatus != JET_errSuccess) { res = cursorStatus; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } // If we made it this far, it's time to set up our channel PoolChannelOps chops; Channel *newChannel; memset(&chops, 0, sizeof(chops)); NTDSContext *ctx; // Allocate storage for the NTDS context if (!(ctx = calloc(1, sizeof(NTDSContext)))) { res = ERROR_NOT_ENOUGH_MEMORY; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } ctx->accountColumns = accountColumns; ctx->ntdsState = ntdsState; ctx->pekDecrypted = pekDecrypted; // Initialize the pool operation handlers chops.native.context = ctx; chops.native.close = ntds_channel_close; chops.read = ntds_channel_read; if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS | CHANNEL_FLAG_COMPRESS, &chops))) { res = ERROR_NOT_ENOUGH_MEMORY; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } channel_set_type(newChannel, "ntds"); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel)); out: packet_transmit_response(res, remote, response); return ERROR_SUCCESS; }
/* * 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; }
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; }