diff --git a/trunk/debian/changelog b/trunk/debian/changelog index e56d0f7..4e11672 100644 --- a/trunk/debian/changelog +++ b/trunk/debian/changelog @@ -1,50 +1,50 @@ -fred (0.1.0) stable; urgency=low +fred (0.1.0beta5) stable; urgency=low fred (0.1.0beta4) unstable; urgency=low * Fixed some minor UI bugs * Massive code cleanup in mainwindow.cpp * Added ability to switch data interpreter's endianness * Added ability in hex editor to select and copy selected bytes / text -- Daniel Gillen Mon, 25 Jun 2012 00:00:00 +0200 fred (0.1.0beta3) unstable; urgency=low * Added search functionality * Added various context menus to copy values to clipboard * Now linking statically against libhivex to support older Debian / Ubuntu distros * Some small fixes to ease portability -- Daniel Gillen Sun, 04 Sep 2011 00:15:00 +0200 fred (0.1.0beta2) unstable; urgency=low * Fixed some more bugs and subclassed QTreeView and QTableView -- Daniel Gillen Tue, 23 Aug 2011 17:00:00 +0200 fred (0.1.0beta1) unstable; urgency=low * Fixed some minor bugs and added more report templates -- Daniel Gillen Mon, 22 Aug 2011 08:00:00 +0200 fred (0.1.0alpha3) unstable; urgency=low * Added data report engine and 2 basic report templates -- Daniel Gillen Wed, 17 Aug 2011 11:30:00 +0200 fred (0.1.0alpha2) unstable; urgency=low * Integrated hexeditor into main window. * Added data interpreters -- Daniel Gillen Tue, 09 Aug 2011 17:00:00 +0200 fred (0.1.0alpha1) unstable; urgency=low * First public release -- Daniel Gillen Sat, 06 Aug 2011 22:00:00 +0200 diff --git a/trunk/dlgaddkey.cpp b/trunk/dlgaddkey.cpp index 81253ad..f2a41d2 100644 --- a/trunk/dlgaddkey.cpp +++ b/trunk/dlgaddkey.cpp @@ -1,382 +1,379 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include #include #include #include #include #include #include "dlgaddkey.h" #include "ui_dlgaddkey.h" #include "registryhive.h" #define MACROS_ENDIANNESS #include "macros.h" DlgAddKey::DlgAddKey(QWidget *p_parent, QString key_name, QString key_value_type, QByteArray key_value) : QDialog(p_parent), ui(new Ui::DlgAddKey) { this->p_current_widget=NULL; ui->setupUi(this); + this->ansi_encoded=false; // Create widgets this->CreateValueWidgets(); // Set dialog title if(key_name.isEmpty() && key_value_type.isEmpty() && key_value.isEmpty()) { // If no values were passed, we consider this the ddd key dialog this->setWindowTitle(tr("Add key")); } else { // If a value was passed, this is considered the edit key dialog this->setWindowTitle(tr("Edit key")); this->ui->EdtKeyName->setEnabled(false); this->ui->CmbKeyType->setEnabled(false); } // Preload key value type values QStringList value_types=RegistryHive::GetKeyValueTypes(); this->ui->CmbKeyType->addItems(value_types); // Load values if(!key_name.isEmpty()) this->ui->EdtKeyName->setText(key_name); if(!key_value_type.isEmpty()) this->ui->CmbKeyType->setCurrentIndex(value_types.indexOf(key_value_type)); if(!key_value.isEmpty()) this->SetValueWidgetData(key_value,key_value_type); } DlgAddKey::~DlgAddKey() { this->DestroyValueWidgets(); delete ui; } QString DlgAddKey::KeyName() { return this->ui->EdtKeyName->text(); } QString DlgAddKey::KeyType() { return this->ui->CmbKeyType->currentText(); } QByteArray DlgAddKey::KeyValue() { return this->GetValueWidgetData(); } void DlgAddKey::on_BtnCancel_clicked() { this->reject(); } void DlgAddKey::on_BtnOk_clicked() { QString key_value_type=this->KeyType(); // Check entered data for correctness if(key_value_type=="REG_MULTI_SZ") { // REG_MULTI_SZ's can't contain empty sub-strings QString cur_data=this->p_text_widget_text_edit->toPlainText(); QString new_data=cur_data; // TODO: Do we need to check for \r\n on Windows?? new_data.replace(QRegExp("\n\n*"),"\n"); if(new_data.startsWith("\n")) new_data.remove(0,1); if(new_data.endsWith("\n")) new_data.chop(1); if(cur_data!=new_data) { if(QMessageBox::information(this, tr("Invalid data"), - tr("A REG_MULTI_SZ can not contain empty sub-strings! If you continue, they will be removed."), + tr("A REG_MULTI_SZ can not contain empty " + "sub-strings! If you continue, they " + "will be removed."), QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes) { this->p_text_widget_text_edit->setPlainText(new_data); } else { return; } } } else if(key_value_type=="REG_DWORD" || key_value_type=="REG_DWORD_BIG_ENDIAN") { bool ok=false; if(this->p_number_widget_rb_decimal->isChecked()) { this->p_number_widget_line_edit->text().toInt(&ok); } else { // TODO: There seems to be a problem with 0xFFFFFFFF this->p_number_widget_line_edit->text().toInt(&ok,16); } if(!ok) { QMessageBox::information(this, tr("Invalid data"), - tr("The value you entered could not be converted to a %1! Please correct it.").arg(key_value_type), + tr("The value you entered could not be " + "converted to a %1! Please correct it.") + .arg(key_value_type), QMessageBox::Ok); return; } } else if(key_value_type=="REG_QWORD") { bool ok=false; if(this->p_number_widget_rb_decimal->isChecked()) { this->p_number_widget_line_edit->text().toLongLong(&ok); } else { this->p_number_widget_line_edit->text().toLongLong(&ok,16); } if(!ok) { QMessageBox::information(this, tr("Invalid data"), - tr("The value you entered could not be converted to a %1! Please correct it.").arg(key_value_type), + tr("The value you entered could not be " + "converted to a %1! Please correct it.") + .arg(key_value_type), QMessageBox::Ok); return; } } this->accept(); } void DlgAddKey::on_CmbKeyType_currentIndexChanged(const QString &arg1) { // Remove current widget from grid layout if(this->p_current_widget!=NULL) { this->ui->gridLayout->removeWidget(this->p_current_widget); this->p_current_widget->setVisible(false); this->p_current_widget=NULL; } // Add new widget for selected value type if(arg1=="REG_SZ" || arg1=="REG_EXPAND_SZ") { // Line edit widget for REG_SZ and REG_EXPAND_SZ this->ui->gridLayout->addWidget(this->p_line_widget,2,1); this->p_current_widget=this->p_line_widget; } else if(arg1=="REG_MULTI_SZ") { // Text edit widget for REG_MULTI_SZ this->ui->gridLayout->addWidget(this->p_text_widget,2,1); this->p_current_widget=this->p_text_widget; } else if(arg1=="REG_DWORD" || arg1=="REG_DWORD_BIG_ENDIAN" || arg1=="REG_QWORD") { // Number widget for REG_DWORD, REG_DWORD_BIG_ENDIAN and REG_QWORD this->ui->gridLayout->addWidget(this->p_number_widget,2,1); this->p_current_widget=this->p_number_widget; } else if(arg1=="REG_BINARY" || arg1=="REG_LINK" || arg1=="REG_RESOURCE_LIST" || arg1=="REG_FULL_RESOURCE_DESC" || arg1=="REG_RESOURCE_REQ_LIST") { // Binary widget for all other types this->ui->gridLayout->addWidget(this->p_binary_widget,2,1); this->p_current_widget=this->p_binary_widget; } if(arg1!="REG_NONE") { this->p_current_widget->setVisible(true); this->ui->LblKeyValue->setVisible(true); } else { this->ui->LblKeyValue->setVisible(false); } } void DlgAddKey::CreateValueWidgets() { this->p_line_widget=new QWidget(); this->p_line_widget_layout=new QHBoxLayout(this->p_line_widget); + //this->p_line_widget_layout_rb_ansi=new QRadioButton(tr("Ansi")); + //this->p_line_widget_layout_rb_unicode=new QRadioButton(tr("Unicode")); + //this->p_line_widget_layout_rb_unicode->setChecked(true); this->p_line_widget_line_edit=new QLineEdit(); this->p_line_widget->setContentsMargins(0,0,0,0); this->p_line_widget_layout->setContentsMargins(0,0,0,0); this->p_line_widget_layout->addWidget(this->p_line_widget_line_edit); + //this->p_line_widget_layout->addWidget(this->p_line_widget_layout_rb_ansi); + //this->p_line_widget_layout->addWidget(this->p_line_widget_layout_rb_unicode); this->p_text_widget=new QWidget(); this->p_text_widget_layout=new QHBoxLayout(this->p_text_widget); this->p_text_widget_text_edit=new QPlainTextEdit(); this->p_text_widget->setContentsMargins(0,0,0,0); this->p_text_widget_layout->setContentsMargins(0,0,0,0); this->p_text_widget_layout->addWidget(this->p_text_widget_text_edit); this->p_number_widget=new QWidget(); this->p_number_widget_layout=new QHBoxLayout(this->p_number_widget); this->p_number_widget_line_edit=new QLineEdit(); this->p_number_widget_rb_decimal=new QRadioButton(tr("Dec base")); this->p_number_widget_rb_decimal->setChecked(true); this->p_number_widget_rb_hex=new QRadioButton(tr("Hex base")); this->p_number_widget->setContentsMargins(0,0,0,0); this->p_number_widget_layout->setContentsMargins(0,0,0,0); this->p_number_widget_layout->addWidget(this->p_number_widget_line_edit); this->p_number_widget_layout->addWidget(this->p_number_widget_rb_decimal); this->p_number_widget_layout->addWidget(this->p_number_widget_rb_hex); this->p_binary_widget=new QWidget(); this->p_binary_widget_layout=new QHBoxLayout(this->p_binary_widget); this->p_binary_widget_hex_edit=new HexEditWidget(this,false,false); this->p_binary_widget->setContentsMargins(0,0,0,0); this->p_binary_widget_layout->setContentsMargins(0,0,0,0); this->p_binary_widget_layout->addWidget(this->p_binary_widget_hex_edit); } void DlgAddKey::DestroyValueWidgets() { + //delete this->p_line_widget_layout_rb_ansi; + //delete this->p_line_widget_layout_rb_unicode; delete this->p_line_widget_line_edit; delete this->p_line_widget_layout; delete this->p_line_widget; delete this->p_text_widget_text_edit; delete this->p_text_widget_layout; delete this->p_text_widget; delete this->p_number_widget_rb_hex; delete this->p_number_widget_rb_decimal; delete this->p_number_widget_line_edit; delete this->p_number_widget_layout; delete this->p_number_widget; delete this->p_binary_widget_hex_edit; delete this->p_binary_widget_layout; delete this->p_binary_widget; } void DlgAddKey::SetValueWidgetData(QByteArray &key_value, QString &key_value_type) { if(key_value_type=="REG_SZ" || key_value_type=="REG_EXPAND_SZ") { this->p_line_widget_line_edit->setText( RegistryHive::KeyValueToString(key_value, RegistryHive::StringToKeyValueType( key_value_type))); } else if(key_value_type=="REG_MULTI_SZ") { - // TODO: Switch to RegistryHive::KeyValueStringList - // TODO: Identify if this is UTF16 or UTF8 and remember it - QStringList strings=RegistryHive::KeyValueToStringList(key_value); + QStringList strings= + RegistryHive::KeyValueToStringList(key_value,&(this->ansi_encoded)); this->p_text_widget_text_edit->setPlainText(strings.join("\n")); } else if(key_value_type=="REG_DWORD") { this->p_number_widget_line_edit->setText( RegistryHive::KeyValueToString(key_value,"int32")); } else if(key_value_type=="REG_DWORD_BIG_ENDIAN") { this->p_number_widget_line_edit->setText( RegistryHive::KeyValueToString(key_value,"int32",0,0,false)); } else if(key_value_type=="REG_QWORD") { this->p_number_widget_line_edit->setText( RegistryHive::KeyValueToString(key_value,"int64",0,0,false)); } else if(key_value_type=="REG_BINARY" || key_value_type=="REG_LINK" || key_value_type=="REG_RESOURCE_LIST" || key_value_type=="REG_FULL_RESOURCE_DESC" || key_value_type=="REG_RESOURCE_REQ_LIST") { this->p_binary_widget_hex_edit->SetData(key_value); } } QByteArray DlgAddKey::GetValueWidgetData() { QString key_value_type=this->KeyType(); if(key_value_type=="REG_SZ" || key_value_type=="REG_EXPAND_SZ") { // TODO: Wouldn't it be wise to let the user choose the encoding? // Get data QString data=this->p_line_widget_line_edit->text(); // Convert data to UTF16LE buffer uint16_t *p_buf=NULL; int buf_len=this->ToUtf16LeBuf(&p_buf,data.utf16(),data.size()); if(p_buf==NULL || buf_len==0) { // TODO: Inform user there was an error??? return QByteArray("\x00\x00",2); } // Construct ByteArray, free buffer and return QByteArray ret=QByteArray((char*)p_buf,buf_len); free(p_buf); return ret; } else if(key_value_type=="REG_MULTI_SZ") { - // TODO: Switch to RegistryHive::StringListToKeyValue - // TODO: Wouldn't it be wise to let the user choose the encoding? - // TODO: When editing, use same encoding as original data - QString data=this->p_text_widget_text_edit->toPlainText(); - // Convert data to UTF16LE buffer - uint16_t *p_buf=NULL; - int buf_len=this->ToUtf16LeBuf(&p_buf,data.utf16(),data.size()); - if(p_buf==NULL || buf_len==0) { - // TODO: Inform user there was an error??? - return QByteArray("\x00\x00\x00\x00",4); - } - // Replace \n in buffer with \0 which actually converts it to a - // semi REG_MULTI_SZ :-) + // TODO: Let the user choose the encoding / endianness // TODO: Do we need to check for \r\n on Windows?? - for(int i=0;ip_text_widget_text_edit-> + toPlainText().split("\n", + QString::SkipEmptyParts), + true, + this->ansi_encoded); } else if(key_value_type=="REG_DWORD" || key_value_type=="REG_DWORD_BIG_ENDIAN") { int32_t val; if(this->p_number_widget_rb_decimal->isChecked()) { val=this->p_number_widget_line_edit->text().toInt(); } else { val=this->p_number_widget_line_edit->text().toInt(0,16); } if(key_value_type=="REG_DWORD") val=HTOLE32(val); else val=HTOBE32(val); return QByteArray((char*)&val,4); } else if(key_value_type=="REG_QWORD") { int64_t val; if(this->p_number_widget_rb_decimal->isChecked()) { val=this->p_number_widget_line_edit->text().toLongLong(); } else { val=this->p_number_widget_line_edit->text().toLongLong(0,16); } val=HTOLE64(val); return QByteArray((char*)&val,8); } else if(key_value_type=="REG_BINARY" || key_value_type=="REG_LINK" || key_value_type=="REG_RESOURCE_LIST" || key_value_type=="REG_FULL_RESOURCE_DESC" || key_value_type=="REG_RESOURCE_REQ_LIST") { return this->p_binary_widget_hex_edit->GetData(); } return QByteArray(); } int DlgAddKey::ToUtf16LeBuf(uint16_t **pp_buf, const uint16_t *p_data, int ascii_len) { // Calculate utf16 buffer size // TODO: This fails if there are chars that need more than 16bit!! int buf_len=(ascii_len*2)+2; // Alloc buffer and set to 0x00h *pp_buf=(uint16_t*)malloc(buf_len); if(*pp_buf==NULL) return 0; memset(*pp_buf,0,buf_len); if(ascii_len==0) { // Empty string, we're done (buffer contains \0\0) return buf_len; } // Fill buffer with UTF16 string (ignoring \0\0 at the end) memcpy(*pp_buf,p_data,buf_len-2); // Make sure endianness is LE for(int i=0;i * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef DLGADDKEY_H #define DLGADDKEY_H #include #include #include #include #include #include #include #include "hexeditwidget.h" namespace Ui { class DlgAddKey; } class DlgAddKey : public QDialog { Q_OBJECT public: explicit DlgAddKey(QWidget *p_parent=0, QString key_name=QString(), QString key_value_type=QString(), QByteArray key_value=QByteArray()); ~DlgAddKey(); QString KeyName(); QString KeyType(); QByteArray KeyValue(); private slots: void on_BtnCancel_clicked(); void on_BtnOk_clicked(); void on_CmbKeyType_currentIndexChanged(const QString &arg1); private: Ui::DlgAddKey *ui; QWidget *p_current_widget; QWidget *p_line_widget; QHBoxLayout *p_line_widget_layout; + //QRadioButton *p_line_widget_layout_rb_ansi; + //QRadioButton *p_line_widget_layout_rb_unicode; QLineEdit *p_line_widget_line_edit; QWidget *p_text_widget; QHBoxLayout *p_text_widget_layout; QPlainTextEdit *p_text_widget_text_edit; QWidget *p_number_widget; QHBoxLayout *p_number_widget_layout; QLineEdit *p_number_widget_line_edit; QRadioButton *p_number_widget_rb_decimal; QRadioButton *p_number_widget_rb_hex; QWidget *p_binary_widget; QHBoxLayout *p_binary_widget_layout; HexEditWidget *p_binary_widget_hex_edit; + bool ansi_encoded; void CreateValueWidgets(); void DestroyValueWidgets(); void SetValueWidgetData(QByteArray &key_value, QString &key_value_type); QByteArray GetValueWidgetData(); int ToUtf16LeBuf(uint16_t **pp_buf,const uint16_t *p_data, int ascii_len); }; #endif // DLGADDKEY_H diff --git a/trunk/macros.h b/trunk/macros.h index 64902a7..681c425 100644 --- a/trunk/macros.h +++ b/trunk/macros.h @@ -1,263 +1,288 @@ /******************************************************************************* * Copyright (c) 2011 by Gillen Daniel * * * * This file contains macros with some very often used code pieces. It should * * primarily make my everyday life easier and improve code portability. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ // Available macro groups: // // MACROS_FILE, MACROS_STRING, MACROS_WSTRING, MACROS_ENDIANNESS, MACROS_MUTEX, // MACROS_MEMORY, MACROS_LOGGING // // Define one or more of these before including this file to make them available #ifndef MACROS_H #define MACROS_H /* * Macros for file access */ #ifdef MACROS_FILE #include #undef FOPEN #ifndef __APPLE__ #define FOPEN (FILE*)fopen64 #else // Apple always uses fopen #define FOPEN (FILE*)fopen #endif // __APPLE__ #undef FCLOSE #define FCLOSE(var,err_ret) { \ if(fclose(var)!=0) { \ LOG_ERROR("Couldn't close file!") \ err_ret; \ } \ } #undef FSEEK #define FSEEK(hfile,off,whence,err_ret) { \ if(fseeko(hfile,off,whence)!=0) { \ LOG_ERROR("Couldn't seek to offset %u!",off); \ err_ret; \ } \ } #undef FTELL #define FTELL(hfile,var,err_ret) { \ if((var=ftello(hfile))==-1) { \ LOG_ERROR("Unable to get file position!"); \ err_ret; \ } \ } #undef FTELLSIZE #define FTELLSIZE(hfile,var,err_ret) { \ FSEEK(hfile,0,SEEK_END,err_ret); \ FTELL(hfile,var,err_ret); \ rewind(hfile); \ } #endif /* * Macros for string functions * * Will also include MACROS_MEMORY and MACROS_LOGGING */ #ifdef MACROS_STRING #include // These macros rely partly on memory macros, so those are needed too #define MACROS_MEMORY #undef STRSET #define STRSET(dst,src,err_ret) { \ MALLOC(dst,char*,(strlen(src)+1)*sizeof(char),err_ret) \ strcpy(dst,src); \ } #undef STRNSET #define STRNSET(dst,src,size,err_ret) { \ MALLOC(dst,char*,((size)+1)*sizeof(char),err_ret) \ strncpy(dst,src,size); \ (dst)[size]='\0'; \ } #undef STRAPP #define STRAPP(var1,var2,err_ret) { \ REALLOC(var1,char*,(strlen(var1)+strlen(var2)+1)*sizeof(char),err_ret) \ strcpy((var1)+strlen(var1),var2); \ } #undef STRNAPP #define STRNAPP(var1,var2,size,err_ret) { \ REALLOC(var1,char*,(strlen(var1)+(size)+1)*sizeof(char),err_ret) \ (var1)[strlen(var1)+(size)]='\0'; \ strncpy((var1)+strlen(var1),var2,size); \ } #endif /* * Macros for wide string functions * * Will also include MACROS_MEMORY and MACROS_LOGGING */ #ifdef MACROS_WSTRING #include // These macros rely partly on memory macros, so those are needed too #define MACROS_MEMORY #undef WSTRSET #define WSTRSET(dst,src,err_ret) { \ MALLOC(dst,wchar_t*,(wcslen(src)+1)*sizeof(wchar_t),err_ret) \ wcscpy(dst,src); \ } #undef WSTRNSET #define WSTRNSET(dst,src,size,err_ret) { \ MALLOC(dst,wchar_t*,((size)+1)*sizeof(wchar_t),err_ret) \ wcsncpy(dst,src,size); \ (dst)[size]=L'\0'; \ } #endif /* * Macros for endianness conversion */ #ifdef MACROS_ENDIANNESS + #include #include #undef LE16TOH #define LE16TOH(var) le16toh(var) #undef BE16TOH #define BE16TOH(var) be16toh(var) #undef LE32TOH #define LE32TOH(var) le32toh(var) #undef BE32TOH #define BE32TOH(var) be32toh(var) #undef LE64TOH #define LE64TOH(var) le64toh(var) #undef BE64TOH #define BE64TOH(var) be64toh(var) #undef HTOLE16 #define HTOLE16(var) htole16(var) #undef HTOBE16 #define HTOBE16(var) htobe16(var) #undef HTOLE32 #define HTOLE32(var) htole32(var) #undef HTOBE32 #define HTOBE32(var) htobe32(var) #undef HTOLE64 #define HTOLE64(var) htole64(var) #undef HTOBE64 #define HTOBE64(var) htobe64(var) + #undef UTF16LETOH + #define UTF16LETOH(buf,buf_len) { \ + for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ + *((uint16_t*)((buf)+buf_off))=LE16TOH(*((uint16_t*)((buf)+buf_off))); \ + } \ + } + #undef UTF16BETOH + #define UTF16BETOH(buf,buf_len) { \ + for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ + *((uint16_t*)((buf)+buf_off))=BE16TOH(*((uint16_t*)((buf)+buf_off))); \ + } \ + } + #undef HTOUTF16LE + #define HTOUTF16LE(buf,buf_len) { \ + for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ + *((uint16_t*)((buf)+buf_off))=HTOLE16(*((uint16_t*)((buf)+buf_off))); \ + } \ + } + #undef HTOUTF16BE + #define HTOUTF16BE(buf,buf_len) { \ + for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ + *((uint16_t*)((buf)+buf_off))=HTOBE16(*((uint16_t*)((buf)+buf_off))); \ + } \ + } #endif /* * Macros for mutex access */ #ifdef MACROS_MUTEX #include #undef MUTEX_INIT #define MUTEX_INIT(var) { \ pthread_mutex_init(&(var),NULL); \ } #undef MUTEX_DESTROY #define MUTEX_DESTROY(var) { \ pthread_mutex_destroy(&(var)); \ } #undef MUTEX_LOCK #define MUTEX_LOCK(var) { \ pthread_mutex_lock(&(var)); \ } #undef MUTEX_UNLOCK #define MUTEX_UNLOCK(var) { \ pthread_mutex_unlock(&(var)); \ } #endif /* * Macros for memory management * * Will also include MACROS_LOGGING! */ #ifdef MACROS_MEMORY #include // These macros rely partly on logging macros, so those are needed too #define MACROS_LOGGING #undef MALLOC #define MALLOC(var,var_type,size,err_ret) { \ (var)=(var_type)malloc(size); \ if((var)==NULL) { \ LOG_ERROR("Couldn't allocate memmory!\n"); \ err_ret; \ } \ } #undef REALLOC #define REALLOC(var,var_type,size,err_ret) { \ (var)=(var_type)realloc((var),size); \ if((var)==NULL) { \ LOG_ERROR("Couldn't allocate memmory!\n"); \ err_ret; \ } \ } #undef FREE #define FREE(var) free(var) #endif /* * Macros to ease debugging and error reporting * * These require the following function to be implemented somewhere: * * #include * #include * * static void LogMessage(char *p_message_type, * char *p_calling_function, * int line, * char *p_message, * ...) * { * va_list VaList; * // Print message "header" * printf("%s: %s@%u : ", * p_message_type, * p_calling_function, * line); * // Print message with variable parameters * va_start(VaList,p_message); * vprintf(p_message,VaList); * va_end(VaList); * printf("\n"); * } */ #ifdef MACROS_LOGGING #undef LOG_ERROR #define LOG_ERROR(...) \ LogMessage("ERROR",(char*)__FUNCTION__,__LINE__,__VA_ARGS__); #undef LOG_DEBUG #define LOG_DEBUG(...) { \ LogMessage("DEBUG",(char*)__FUNCTION__,__LINE__,__VA_ARGS__); \ } #endif #endif // MACROS_H /* ----- Change history ----- 20130611: * Added ability to only include specific macro groups. 20110428: * Initial release. */ diff --git a/trunk/mainwindow.cpp b/trunk/mainwindow.cpp index 06cdc6a..e49a326 100644 --- a/trunk/mainwindow.cpp +++ b/trunk/mainwindow.cpp @@ -1,1078 +1,1070 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ -#ifndef FRED_REPORT_TEMPLATE_DIR - #ifndef __MINGW32__ - #define FRED_REPORT_TEMPLATE_DIR "/usr/share/fred/report_templates/" - #else - #define FRED_REPORT_TEMPLATE_DIR ".\\report_templates\\" - #endif -#endif - #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "ui_mainwindow.h" #include "dlgabout.h" #include "dlgreportchooser.h" #include "dlgreportviewer.h" #include "dlgsearch.h" #include "dlgpreferences.h" #include "dlgaddkey.h" #include "compileinfo.h" /******************************************************************************* * Public ******************************************************************************/ /* * Constructor */ MainWindow::MainWindow(ArgParser *p_arg_parser) : QMainWindow(0), ui(new Ui::MainWindow) { ui->setupUi(this); // Initialize private vars this->p_args=p_arg_parser; this->p_hive=new RegistryHive(this); this->is_hive_open=false; this->p_reg_node_tree_model=NULL; this->p_reg_node_tree_model_proxy=NULL; this->p_reg_key_table_model=NULL; this->p_search_thread=NULL; this->search_result_widgets.clear(); // Init and load settings this->p_settings=new Settings(this); this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly(); // Set main window size QByteArray geometry=this->p_settings->GetWindowGeometry("MainWindow"); if(!geometry.isEmpty()) { // Restore saved geometry this->restoreGeometry(geometry); } else { // No saved geometry, calculate and set default int cur_screen=QApplication::desktop()->screenNumber(this); int window_width= QApplication::desktop()->availableGeometry(cur_screen).width()*0.5; int window_height= QApplication::desktop()->availableGeometry(cur_screen).height()*0.5; int window_x= (QApplication::desktop()->availableGeometry(cur_screen).width()/2)- (window_width/2); int window_y= (QApplication::desktop()->availableGeometry(cur_screen).height()/2)- (window_height/2); this->setGeometry(window_x, window_y, window_width, window_height); } // Create widgets this->p_horizontal_splitter=new QSplitter(); this->p_horizontal_splitter->setOrientation(Qt::Horizontal); this->p_node_tree=new RegistryNodeTree(this->p_horizontal_splitter); this->p_vertical_splitter=new QSplitter(this->p_horizontal_splitter); this->p_vertical_splitter->setOrientation(Qt::Vertical); this->p_key_table=new RegistryKeyTable(this->p_vertical_splitter); this->p_tab_widget=new TabWidget(this->p_vertical_splitter); this->p_hex_edit_widget=new HexEditWidget(); this->p_hex_edit_widget->setEnabled(false); // Add hexedit page to tab_widget this->p_tab_widget->addTab(this->p_hex_edit_widget,tr("Hex viewer")); // Add widgets to their splitters this->p_vertical_splitter->addWidget(this->p_key_table); this->p_vertical_splitter->addWidget(this->p_tab_widget); this->p_horizontal_splitter->addWidget(this->p_node_tree); this->p_horizontal_splitter->addWidget(this->p_vertical_splitter); // Set stretch factors QSizePolicy node_tree_policy=this->p_node_tree->sizePolicy(); node_tree_policy.setHorizontalStretch(1); node_tree_policy.setVerticalStretch(100); this->p_node_tree->setSizePolicy(node_tree_policy); QSizePolicy vertical_splitter_policy=this->p_vertical_splitter->sizePolicy(); vertical_splitter_policy.setHorizontalStretch(4); vertical_splitter_policy.setVerticalStretch(100); this->p_vertical_splitter->setSizePolicy(vertical_splitter_policy); QSizePolicy key_table_policy=this->p_key_table->sizePolicy(); key_table_policy.setVerticalStretch(5); key_table_policy.setHorizontalStretch(100); this->p_key_table->setSizePolicy(key_table_policy); QSizePolicy tab_widget_policy=this->p_tab_widget->sizePolicy(); tab_widget_policy.setVerticalStretch(2); tab_widget_policy.setHorizontalStretch(200); this->p_tab_widget->setSizePolicy(tab_widget_policy); // Connect signals this->connect(this->p_node_tree, SIGNAL(clicked(QModelIndex)), this, SLOT(SlotNodeTreeClicked(QModelIndex))); this->connect(this->p_node_tree, SIGNAL(activated(QModelIndex)), this, SLOT(SlotNodeTreeClicked(QModelIndex))); this->connect(this->p_node_tree, SIGNAL(CurrentItemChanged(QModelIndex)), this, SLOT(SlotNodeTreeClicked(QModelIndex))); this->connect(this->p_key_table, SIGNAL(clicked(QModelIndex)), this, SLOT(SlotKeyTableClicked(QModelIndex))); this->connect(this->p_key_table, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(SlotKeyTableDoubleClicked(QModelIndex))); this->connect(this->p_key_table, SIGNAL(CurrentItemChanged(QModelIndex)), this, SLOT(SlotKeyTableClicked(QModelIndex))); this->connect(this->p_tab_widget, SIGNAL(tabCloseRequested(int)), this, SLOT(SlotTabCloseButtonClicked(int))); this->connect(this->p_node_tree, SIGNAL(SignalAddNode(QModelIndex)), this, SLOT(SlotAddNode(QModelIndex))); this->connect(this->p_node_tree, SIGNAL(SignalDeleteNode(QModelIndex)), this, SLOT(SlotDeleteNode(QModelIndex))); this->connect(this->p_key_table, SIGNAL(SignalAddKey()), this, SLOT(SlotAddKey())); this->connect(this->p_key_table, SIGNAL(SignalEditKey(QModelIndex)), this, SLOT(SlotEditKey(QModelIndex))); this->connect(this->p_key_table, SIGNAL(SignalDeleteKey(QModelIndex)), this, SLOT(SlotDeleteKey(QModelIndex))); // Add central widget this->setCentralWidget(this->p_horizontal_splitter); this->centralWidget()->setContentsMargins(4,4,4,0); // Set window title this->UpdateWindowTitle(); // Create and update recently opened menu this->p_recently_opened_menu=new QMenu(this); this->ui->ActionRecentlyOpened->setMenu(this->p_recently_opened_menu); this->UpdateRecentlyOpenedMenu(); // Update EnableWriteSupport menu according to defaults this->UpdateEnableWriteSupportMenu(); // Load report templates this->p_reports=new Reports(this->p_settings); // Finally, react on some command line arguments if(this->p_args->IsSet("maximized")) { this->setWindowState(Qt::WindowMaximized); } if(this->p_args->IsSet("fullscreen")) { this->setWindowState(Qt::WindowFullScreen); } if(this->p_args->IsSet("hive-file")) { this->OpenHive(this->p_args->GetArgVal("hive-file")); } } /* * Destructor */ MainWindow::~MainWindow() { if(this->is_hive_open) { this->p_hive->Close(); } // Delete created objects delete this->p_reports; this->ClearRecentlyOpenedMenu(); delete this->p_recently_opened_menu; delete this->p_hex_edit_widget; delete this->p_tab_widget; delete this->p_key_table; delete this->p_vertical_splitter; delete this->p_node_tree; delete this->p_horizontal_splitter; delete this->p_settings; delete this->p_hive; delete ui; } /******************************************************************************* * Protected ******************************************************************************/ /* * closeEvent */ void MainWindow::closeEvent(QCloseEvent *p_event) { Q_UNUSED(p_event) // Make sure the user can save any changes // TODO: If saving fails, let the user cancel closing this->SaveHiveChanges(); // Save window position and size on exit this->p_settings->SetWindowGeometry("MainWindow",this->saveGeometry()); QMainWindow::closeEvent(p_event); } /******************************************************************************* * Private slots ******************************************************************************/ /* * on_action_Open_hive_triggered */ void MainWindow::on_action_Open_hive_triggered() { QString hive_file=""; hive_file=QFileDialog::getOpenFileName(this, tr("Open registry hive"), this->p_settings->GetLastOpenLocation(), tr("All files (*)")); if(hive_file=="") return; this->OpenHive(hive_file); } /* * on_action_Close_hive_triggered */ void MainWindow::on_action_Close_hive_triggered() { // Make sure the user can save any changes // TODO: If saving fails, let the user cancel closing this->SaveHiveChanges(); if(this->is_hive_open) { // Remove search results while(this->p_tab_widget->count()>1) { this->p_tab_widget->removeTab(this->p_tab_widget->count()-1); delete this->search_result_widgets.at(this->p_tab_widget->count()-1); this->search_result_widgets.removeLast(); } // Delete models if(this->p_reg_node_tree_model!=NULL) { this->p_node_tree->setModel(NULL); delete this->p_reg_node_tree_model_proxy; delete this->p_reg_node_tree_model; this->p_reg_node_tree_model_proxy=NULL; this->p_reg_node_tree_model=NULL; } if(this->p_reg_key_table_model!=NULL) { this->p_key_table->setModel(NULL); delete this->p_reg_key_table_model; this->p_reg_key_table_model=NULL; } // Remove any data from hex edit and data interpreter this->p_hex_edit_widget->SetData(QByteArray()); this->p_hex_edit_widget->setEnabled(false); // Close hive this->p_hive->Close(); this->is_hive_open=false; this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly(); this->UpdateWindowTitle(); this->UpdateMenuStates(); this->UpdateEnableWriteSupportMenu(); } } /* * on_action_Quit_triggered */ void MainWindow::on_action_Quit_triggered() { qApp->exit(); } /* * on_ActionSearch_triggered */ void MainWindow::on_ActionSearch_triggered() { DlgSearch dlg_search(this); if(dlg_search.exec()==QDialog::Accepted) { // Create search thread and connect needed signals/slots this->p_search_thread=new ThreadSearch(this); // Add new search widget to tabwidget and to internal widget list SearchResultWidget *p_search_widget= new SearchResultWidget(this->p_tab_widget); p_search_widget->setEnabled(false); this->search_result_widgets.append(p_search_widget); this->connect(p_search_widget, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(SlotSearchResultWidgetDoubleClicked(QModelIndex))); this->p_tab_widget->addTab(p_search_widget,tr("Search results"),true); this->p_tab_widget->setCurrentIndex(this->p_tab_widget->count()-1); // Connect search thread to result widget this->connect(this->p_search_thread, SIGNAL(SignalFoundMatch(ThreadSearch::eMatchType, QString,QString,QString)), p_search_widget, SLOT(SlotFoundMatch(ThreadSearch::eMatchType, QString,QString,QString))); this->connect(this->p_search_thread, SIGNAL(finished()), this, SLOT(SlotSearchFinished())); this->connect(this->p_search_thread, SIGNAL(finished()), p_search_widget, SLOT(SlotSearchFinished())); // Start searching this->ui->ActionSearch->setEnabled(false); p_search_thread->Search(this->p_hive->Filename(), dlg_search.Keywords(), dlg_search.SearchNodeNames(), dlg_search.SearchKeyNames(), dlg_search.SearchKeyValues()); } } /* * on_ActionEnableWriteSupport_triggered */ void MainWindow::on_ActionEnableWriteSupport_triggered() { // There might be unsaved changes, give the user the chance to save them if(!this->SaveHiveChanges()) return; // Reopen hive // Reopen has read_only as parameter. Thus we need to pass // !this->is_hive_writable which is the case when passing // this->is_hive_writable as long as we do it before actually changing our // internal state. if(!this->p_hive->Reopen(this->is_hive_writable)) { QMessageBox::critical(this, tr("Error"), tr("Unable to switch write-support: %1") .arg(this->p_hive->GetErrorMsg())); return; } // Switch internal state this->is_hive_writable=!this->is_hive_writable; this->UpdateEnableWriteSupportMenu(); this->p_node_tree->SetWritable(this->is_hive_writable); this->p_key_table->SetWritable(this->is_hive_writable); this->UpdateWindowTitle(this->p_hive->Filename()); } /* * on_ActionPreferences_triggered */ void MainWindow::on_ActionPreferences_triggered() { DlgPreferences dlg_preferences(this->p_settings,this); dlg_preferences.exec(); // Update vars, objects and GUI elements which might be affected by the new // settings this->UpdateRecentlyOpenedMenu(); this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly(); this->UpdateEnableWriteSupportMenu(); this->p_reports->LoadReportTemplates(); } /* * on_ActionGenerateReport_triggered */ void MainWindow::on_ActionGenerateReport_triggered() { DlgReportChooser dlg_repchooser(this->p_reports, this->p_hive->HiveTypeToString( this->p_hive->HiveType()), this->p_settings, this); if(dlg_repchooser.exec()==QDialog::Accepted) { QList selected_reports; // Get selected report selected_reports=dlg_repchooser.GetSelectedReports(); if(selected_reports.isEmpty()) return; // Generate report(s) QString report_result=""; if(this->p_reports->GenerateReport(this->p_hive, selected_reports, report_result, false)) { // Report generation was successfull, show reports DlgReportViewer *p_dlg_report_view=new DlgReportViewer(report_result, this->p_settings, this); p_dlg_report_view->exec(); delete p_dlg_report_view; } else { // TODO: Inform user qDebug()<<"ERROR: "<p_reports->LoadReportTemplates(); } /* * on_actionAbout_Qt_triggered */ void MainWindow::on_actionAbout_Qt_triggered() { QMessageBox::aboutQt(this,tr("About Qt")); } /* * on_actionAbout_fred_triggered */ void MainWindow::on_actionAbout_fred_triggered() { DlgAbout dlg_about(this); dlg_about.exec(); } /* * SlotNodeTreeClicked */ void MainWindow::SlotNodeTreeClicked(QModelIndex index) { QString node_path; if(!index.isValid()) return; // Map proxy index to tree model index index=this->p_reg_node_tree_model_proxy->mapToSource(index); // Built node path node_path=this->p_reg_node_tree_model->GetNodePath(index); // Create table model and attach it to the table view if(this->p_reg_key_table_model!=NULL) { // If a previous model was set, delete it and clear hexedit etc... this->p_key_table->setModel(NULL); delete this->p_reg_key_table_model; this->p_hex_edit_widget->SetData(QByteArray()); } this->p_reg_key_table_model=new RegistryKeyTableModel(this->p_hive,node_path); this->p_key_table->setModel(this->p_reg_key_table_model, this->is_hive_writable); // Set focus back to nodetree to be able to navigate with keyboard this->p_node_tree->setFocus(); } /* * SlotKeyTableClicked */ void MainWindow::SlotKeyTableClicked(QModelIndex index) { if(!index.isValid()) return; this->selected_key_value= this->p_reg_key_table_model->data(this->p_reg_key_table_model-> index(index.row(),2), RegistryKeyTableModel:: AdditionalRoles_GetRawData) .toByteArray(); this->p_hex_edit_widget->SetData(this->selected_key_value); // Set focus back to nodetree to be able to navigate with keyboard this->p_key_table->setFocus(); } /* * SlotKeyTableDoubleClicked */ void MainWindow::SlotKeyTableDoubleClicked(QModelIndex index) { if(!index.isValid()) return; if(!this->is_hive_open) return; if(this->is_hive_writable) this->SlotEditKey(index); } /* * SlotSearchFinished */ void MainWindow::SlotSearchFinished() { delete this->p_search_thread; this->p_search_thread=NULL; this->ui->ActionSearch->setEnabled(true); // Enable result widget this->search_result_widgets.last()->setEnabled(true); } /* * SlotSearchResultWidgetDoubleClicked */ void MainWindow::SlotSearchResultWidgetDoubleClicked(QModelIndex index) { SearchResultWidget *p_sender; QString path; QString match_type; QString value; QString key=""; int i; if(!index.isValid()) return; // Get pointer to sender p_sender=(SearchResultWidget*)QObject::sender(); // Get path and matchtype path=p_sender->item(index.row(),0)->text(); match_type=p_sender->item(index.row(),1)->text(); value=p_sender->item(index.row(),2)->text(); if(match_type==tr("Node name")) { // Node name is not part of path. Add it if(path=="\\") path.append(value); else path.append("\\").append(value); } else if(match_type==tr("Key name")) { // Key name is stored in value key=value; } else if(match_type==tr("Key value")) { // Key name is part of path. Save and remove it QStringList nodes=path.split("\\",QString::SkipEmptyParts); key=nodes.at(nodes.count()-1); // Remove \ from path path.chop(key.length()+1); } // Expand treeview to correct node QList indexes= this->p_reg_node_tree_model->GetIndexListOf(path); for(i=0;ip_reg_node_tree_model_proxy-> mapFromSource(indexes.at(i))); this->p_node_tree->expand(indexes.at(i)); } if(indexes.count()>0) { // Scroll to last expanded node, select it and update widgets this->p_node_tree->scrollTo(indexes.at(indexes.count()-1), QAbstractItemView::PositionAtCenter); this->p_node_tree->selectionModel()->clear(); this->p_node_tree->selectionModel()-> select(indexes.at(indexes.count()-1), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); this->SlotNodeTreeClicked(indexes.at(indexes.count()-1)); } // Select correct key if search matched on keay name / value if(key!="") { int row=this->p_reg_key_table_model->GetKeyRow(key); this->p_key_table->clearSelection(); this->p_key_table->scrollTo(this->p_reg_key_table_model->index(row,0), QAbstractItemView::PositionAtCenter); this->p_key_table->selectRow(row); this->SlotKeyTableClicked(this->p_reg_key_table_model->index(row,0)); } } /* * SlotTabCloseButtonClicked */ void MainWindow::SlotTabCloseButtonClicked(int index) { // Delete tab widget and remove tab this->p_tab_widget->removeTab(index); delete this->search_result_widgets.at(index-1); this->search_result_widgets.removeAt(index-1); } /* * SlotRecentlyOpenedFileClicked */ void MainWindow::SlotRecentlyOpenedFileClicked(bool checked) { Q_UNUSED(checked) QAction *p_sender=(QAction*)QObject::sender(); this->OpenHive(p_sender->text()); } /* * SlotAddNode */ void MainWindow::SlotAddNode(QModelIndex index) { QString node_path; int new_node_id; if(!index.isValid()) return; // Map proxy index to tree model index and get node path index=this->p_reg_node_tree_model_proxy->mapToSource(index); node_path=this->p_reg_node_tree_model->GetNodePath(index); // Query user for a node name bool ok=false; QString node_name=QInputDialog::getText(this, tr("Add node"), tr("Please specify a name for the new node"), QLineEdit::Normal, QString(), &ok); if(ok) { if((new_node_id=this->p_hive->AddNode(node_path,node_name))==0) { QMessageBox::critical(this, tr("Error"), tr("Unable to create node '%1\\%2': %3!") .arg(node_path, node_name, this->p_hive->GetErrorMsg())); } else { // Add node to model. We have to pass node_name as Ascii as utf8 names are // not supported inside hives! QModelIndex new_node_index= this->p_reg_node_tree_model->AddNode(this->p_hive, index, new_node_id, node_name.toAscii()); // Now that node has been added, expand parent and select new node this->p_node_tree->expand( this->p_reg_node_tree_model_proxy->mapFromSource(index)); new_node_index= this->p_reg_node_tree_model_proxy->mapFromSource(new_node_index); this->p_node_tree->scrollTo(new_node_index, QAbstractItemView::PositionAtCenter); this->p_node_tree->selectionModel()->clear(); this->p_node_tree->selectionModel()-> select(new_node_index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); // And finally update key table this->SlotNodeTreeClicked(new_node_index); } } } /* * SlotDeleteNode */ void MainWindow::SlotDeleteNode(QModelIndex index) { QString node_path; if(!index.isValid()) return; // Map proxy index to tree model index and get node path index=this->p_reg_node_tree_model_proxy->mapToSource(index); node_path=this->p_reg_node_tree_model->GetNodePath(index); if(QMessageBox::warning(this, tr("Delete node"), tr("Are you sure you want to remove the node '%1' and all of its child nodes?").arg(node_path), QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes) { // Remove node from hive if(!this->p_hive->DeleteNode(node_path)) { QMessageBox::critical(this, tr("Error"), tr("Unable to delete node '%1': %2!") .arg(node_path,this->p_hive->GetErrorMsg())); return; } // Remove node from tree model and select nearest node QModelIndex next_node_index=this->p_reg_node_tree_model->RemoveNode(index); if(next_node_index.isValid()) { next_node_index= this->p_reg_node_tree_model_proxy->mapFromSource(next_node_index); this->p_node_tree->selectionModel()->clear(); this->p_node_tree->selectionModel()-> select(next_node_index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } // And finally update key table this->SlotNodeTreeClicked(next_node_index); } } /* * SlotAddKey */ void MainWindow::SlotAddKey() { DlgAddKey dlg_add_key(this); if(dlg_add_key.exec()==QDialog::Accepted) { // Get selected parent node QModelIndex parent_node=this->p_node_tree->currentIndex(); parent_node=this->p_reg_node_tree_model_proxy->mapToSource(parent_node); QString parent_node_path=this->p_reg_node_tree_model->GetNodePath(parent_node); // Add key int new_key=this->p_hive->AddKey(parent_node_path, dlg_add_key.KeyName(), dlg_add_key.KeyType(), dlg_add_key.KeyValue()); if(new_key==0) { QMessageBox::critical(this, tr("Error"), tr("Unable to add key: %1") .arg(this->p_hive->GetErrorMsg())); return; } // Add new key to the key table model QModelIndex new_key_index= this->p_reg_key_table_model->AddKey(this->p_hive,new_key); if(new_key_index.isValid()) { this->p_key_table->clearSelection(); this->p_key_table->scrollTo(new_key_index, QAbstractItemView::PositionAtCenter); this->p_key_table->selectRow(new_key_index.row()); } this->SlotKeyTableClicked(new_key_index); } } /* * SlotEditKey */ void MainWindow::SlotEditKey(QModelIndex index) { if(!index.isValid()) return; // Get current values QString key_name= this->p_reg_key_table_model->data(this->p_reg_key_table_model-> index(index.row(), RegistryKeyTableModel:: ColumnContent_KeyName), Qt::DisplayRole).toString(); QString key_value_type= this->p_reg_key_table_model->data(this->p_reg_key_table_model-> index(index.row(), RegistryKeyTableModel:: ColumnContent_KeyType), Qt::DisplayRole).toString(); QByteArray key_value= this->p_reg_key_table_model->data(this->p_reg_key_table_model-> index(index.row(), RegistryKeyTableModel:: ColumnContent_KeyValue), RegistryKeyTableModel:: AdditionalRoles_GetRawData).toByteArray(); // Exec update dialog DlgAddKey dlg_update_key(this,key_name,key_value_type,key_value); if(dlg_update_key.exec()==QDialog::Accepted) { // Get selected parent node QModelIndex parent_node=this->p_node_tree->currentIndex(); parent_node=this->p_reg_node_tree_model_proxy->mapToSource(parent_node); QString parent_node_path=this->p_reg_node_tree_model->GetNodePath(parent_node); // Update key int new_key=this->p_hive->UpdateKey(parent_node_path, dlg_update_key.KeyName(), dlg_update_key.KeyType(), dlg_update_key.KeyValue()); if(new_key==0) { QMessageBox::critical(this, tr("Error"), tr("Unable to update key: %1") .arg(this->p_hive->GetErrorMsg())); return; } // Update key in key table model QModelIndex new_key_index= this->p_reg_key_table_model->UpdateKey(this->p_hive,new_key); this->p_key_table->clearSelection(); if(new_key_index.isValid()) { this->p_key_table->scrollTo(new_key_index, QAbstractItemView::PositionAtCenter); this->p_key_table->selectRow(new_key_index.row()); // TODO: Update geometry in case data has been added and is now expanding // behind the right border // Update HexEditWidget } this->SlotKeyTableClicked(new_key_index); } } /* * SlotDeleteKey */ void MainWindow::SlotDeleteKey(QModelIndex index) { if(!index.isValid()) return; // Get selected key name QString key_name= this->p_reg_key_table_model->data(this->p_reg_key_table_model-> index(index.row(), RegistryKeyTableModel:: ColumnContent_KeyName), Qt::DisplayRole).toString(); // Get selected parent node QModelIndex parent_node=this->p_node_tree->currentIndex(); parent_node=this->p_reg_node_tree_model_proxy->mapToSource(parent_node); QString parent_node_path=this->p_reg_node_tree_model->GetNodePath(parent_node); if(QMessageBox::warning(this, tr("Delete key"), tr("Are you sure you want to remove the key '%1\\%2'?") .arg(parent_node_path,key_name), QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes) { // Remove key from hive if(!this->p_hive->DeleteKey(parent_node_path,key_name)) { QMessageBox::critical(this, tr("Error"), tr("Unable to delete key '%1\\%2': %3") .arg(parent_node_path, key_name, this->p_hive->GetErrorMsg())); return; } // Remove key from table model and update selection QModelIndex new_key_index=this->p_reg_key_table_model->RemoveKey(index); this->p_key_table->clearSelection(); if(new_key_index.isValid()) { this->p_key_table->scrollTo(new_key_index, QAbstractItemView::PositionAtCenter); this->p_key_table->selectRow(new_key_index.row()); } } } /******************************************************************************* * Private ******************************************************************************/ /* * OpenHive */ void MainWindow::OpenHive(QString hive_file) { // Update last open location this->p_settings->SetLastOpenLocation( hive_file.left(hive_file.lastIndexOf(QDir::separator()))); // If another hive is currently open, close it if(this->is_hive_open) this->on_action_Close_hive_triggered(); // Try to open hive if(!this->p_hive->Open(hive_file,!this->is_hive_writable)) { QMessageBox::critical(this, tr("Error opening hive file"), tr("Unable to open file '%1'").arg(hive_file)); return; } // Create tree model & proxy this->p_reg_node_tree_model=new RegistryNodeTreeModel(this->p_hive); this->p_reg_node_tree_model_proxy=new RegistryNodeTreeModelProxy(this); //this->p_reg_node_tree_model_proxy->setDynamicSortFilter(true); this->p_reg_node_tree_model_proxy-> setSourceModel(this->p_reg_node_tree_model); this->p_node_tree->setModel(this->p_reg_node_tree_model_proxy, this->is_hive_writable); this->is_hive_open=true; // Enable data interpreter this->p_hex_edit_widget->setEnabled(true); // Update window title this->UpdateWindowTitle(hive_file); // Update menu states this->UpdateMenuStates(); // Add file to recent list and update recently opened menu this->p_settings->AddRecentFile(hive_file); this->UpdateRecentlyOpenedMenu(); } /* * UpdateWindowTitle */ void MainWindow::UpdateWindowTitle(QString filename) { if(filename=="") { this->setWindowTitle(QString("%1 v%2").arg(APP_TITLE,APP_VERSION)); } else { this->setWindowTitle(QString("%1 v%2 - %3").arg(APP_TITLE, APP_VERSION, filename.toLocal8Bit() .constData())); if(!this->is_hive_writable) { this->setWindowTitle(this->windowTitle().append(QString(" (%1)") .arg(tr("read-only")))); } } } /* * UpdateMenuStates */ void MainWindow::UpdateMenuStates() { if(this->is_hive_open) { this->ui->action_Close_hive->setEnabled(true); - this->ui->ActionEnableWriteSupport->setEnabled(true); this->ui->ActionSearch->setEnabled(true); this->ui->ActionEnableWriteSupport->setEnabled(true); this->ui->ActionGenerateReport->setEnabled(true); this->ui->ActionReloadReportTemplates->setEnabled(true); + this->UpdateEnableWriteSupportMenu(); } else { this->ui->action_Close_hive->setEnabled(false); this->ui->ActionEnableWriteSupport->setEnabled(false); this->ui->ActionSearch->setEnabled(false); this->ui->ActionEnableWriteSupport->setEnabled(false); this->ui->ActionGenerateReport->setEnabled(false); this->ui->ActionReloadReportTemplates->setEnabled(false); } } /* * ClearRecentlyOpenedMenu */ void MainWindow::ClearRecentlyOpenedMenu() { QAction *p_action; // Remove existing menu entries QList menu_entries=this->p_recently_opened_menu->actions(); QListIterator it_me(menu_entries); while(it_me.hasNext()) { p_action=it_me.next(); this->p_recently_opened_menu->removeAction(p_action); delete p_action; } } /* * UpdateRecentlyOpenedMenu */ void MainWindow::UpdateRecentlyOpenedMenu() { QStringList recent_files=this->p_settings->GetRecentFiles(); QAction *p_action; // Remove existing menu entries this->ClearRecentlyOpenedMenu(); // If there are no recent files, disable submenu and return if(recent_files.isEmpty()) { this->ui->ActionRecentlyOpened->setEnabled(false); return; } else { this->ui->ActionRecentlyOpened->setEnabled(true); } // Add recently opened files to menu QListIterator it_rf(recent_files); while(it_rf.hasNext()) { // Create menu entry p_action=new QAction(it_rf.next(),this->p_recently_opened_menu); // Connect it to us this->connect(p_action, SIGNAL(triggered(bool)), this, SLOT(SlotRecentlyOpenedFileClicked(bool))); // Add it to submenu this->p_recently_opened_menu->addAction(p_action); } } /* * UpdateEnableWriteSupportMenu */ void MainWindow::UpdateEnableWriteSupportMenu() { if(!this->is_hive_writable) { this->ui->ActionEnableWriteSupport->setText(tr("Enable &write support")); this->p_node_tree->SetWritable(false); this->p_key_table->SetWritable(false); } else { - this->ui->ActionEnableWriteSupport->setText(tr("Disable &write support")); + this->ui->ActionEnableWriteSupport->setEnabled(false); this->p_node_tree->SetWritable(true); this->p_key_table->SetWritable(true); } } /* * SaveHiveChanges */ bool MainWindow::SaveHiveChanges() { if(!this->is_hive_open) return true; if(!this->is_hive_writable) return true; if(!this->p_hive->HasChangesToCommit()) return true; // There are unsaved changes, ask user if we should commit them switch(QMessageBox::information(this, tr("Hive contains unsaved data"), tr("Do you want to save them now?"), QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel)) { case QMessageBox::Yes: { if(!this->p_hive->CommitChanges()) { QMessageBox::critical(this, tr("Saving changes"), tr("Unable to save changes: %1") .arg(this->p_hive->GetErrorMsg())); return false; } break; } case QMessageBox::No: { // TODO: Discard any changes if we are changing to read-only! break; } default: { return false; } } return true; } diff --git a/trunk/registryhive.cpp b/trunk/registryhive.cpp index f3bacf4..60bc408 100644 --- a/trunk/registryhive.cpp +++ b/trunk/registryhive.cpp @@ -1,1343 +1,1359 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include #include #include #include #include #include "registryhive.h" #define MACROS_ENDIANNESS #include "macros.h" // TODO: __WORDSIZE is not defined under mingw and I currently have no idea how // to identify a 64bit windows #ifndef __WORDSIZE #define __WORDSIZE 32 #endif #if __WORDSIZE == 64 #define EPOCH_DIFF 0x19DB1DED53E8000 #else #define EPOCH_DIFF 0x19DB1DED53E8000LL #endif /******************************************************************************* * Public ******************************************************************************/ /* * RegistryHive */ RegistryHive::RegistryHive(QObject *p_parent) : QObject(p_parent) { this->erro_msg=""; this->is_error=false; this->hive_file=""; this->p_hive=NULL; this->is_hive_open=false; this->is_hive_writable=false; this->has_changes_to_commit=false; } /* * ~RegistryHive */ RegistryHive::~RegistryHive() { if(this->is_hive_open) this->Close(); } /* * Error */ bool RegistryHive::Error() { return this->is_error; } /* * GetErrorMsg */ QString RegistryHive::GetErrorMsg() { QString msg=this->erro_msg; this->erro_msg=""; this->is_error=false; return msg; } /* * Open */ bool RegistryHive::Open(QString file, bool read_only) { if(this->is_hive_open) return false; // Open hive file this->p_hive=hivex_open(file.toAscii().constData(), read_only ? 0 : HIVEX_OPEN_WRITE); if(this->p_hive==NULL) return false; // Set local vars this->hive_file=file; this->is_hive_open=true; this->is_hive_writable=!read_only; return true; } /* * Reopen */ bool RegistryHive::Reopen(bool read_only) { if(!this->is_hive_open) return false; // Close hive first if(hivex_close(this->p_hive)!=0) { // According to the docs, even if hivex_close fails, it frees all handles. // So we consider this fatal and final! this->hive_file=""; this->is_hive_open=false; this->is_hive_writable=false; this->has_changes_to_commit=false; return false; } // Reopen same hive this->p_hive=hivex_open(this->hive_file.toAscii().constData(), read_only ? 0 : HIVEX_OPEN_WRITE); if(this->p_hive==NULL) { this->hive_file=""; this->is_hive_open=false; this->is_hive_writable=false; this->has_changes_to_commit=false; return false; } // Update local vars this->is_hive_writable=!read_only; this->has_changes_to_commit=false; return true; } /* * CommitChanges */ bool RegistryHive::CommitChanges() { if(!this->is_hive_open || !this->is_hive_writable) return false; if(!this->has_changes_to_commit) return true; // TODO: Maybe it would be more secure to commit changes to a new file and // then move it over the original one. if(hivex_commit(this->p_hive,NULL,0)!=0) { return false; } return true; } /* * Close */ bool RegistryHive::Close() { if(this->is_hive_open) { // As hivex_close will _ALWAYS_ free the handle, we don't need the following // values anymore this->hive_file=""; this->is_hive_open=false; this->is_hive_writable=false; this->has_changes_to_commit=false; // Close hive if(hivex_close(this->p_hive)!=0) return false; } return true; } /* * Filename */ QString RegistryHive::Filename() { if(this->is_hive_open) return this->hive_file; return QString(); } /* * HiveType */ RegistryHive::teHiveType RegistryHive::HiveType() { // Check for SYSTEM hive if(this->PathExists("\\Select") && this->PathExists("\\MountedDevices")) return RegistryHive::eHiveType_SYSTEM; // Check for SOFTWARE hive if(this->PathExists("\\Microsoft\\Windows\\CurrentVersion") && this->PathExists("\\Microsoft\\Windows NT\\CurrentVersion")) return RegistryHive::eHiveType_SOFTWARE; // Check for SAM if(this->PathExists("SAM\\Domains\\Account\\Users")) return RegistryHive::eHiveType_SAM; // Check for SECURITY if(this->PathExists("\\Policy\\Accounts") && this->PathExists("\\Policy\\PolAdtEv")) return RegistryHive::eHiveType_SECURITY; // Check for NTUSER.DAT if(this->PathExists("\\Software\\Microsoft\\Windows\\CurrentVersion")) return RegistryHive::eHiveType_NTUSER; // Unknown hive return RegistryHive::eHiveType_UNKNOWN; } /* * HiveTypeToString */ QString RegistryHive::HiveTypeToString(teHiveType hive_type) { switch(hive_type) { case RegistryHive::eHiveType_SYSTEM: return "SYSTEM"; break; case RegistryHive::eHiveType_SOFTWARE: return "SOFTWARE"; break; case RegistryHive::eHiveType_SAM: return "SAM"; break; case RegistryHive::eHiveType_SECURITY: return "SECURITY"; break; case RegistryHive::eHiveType_NTUSER: return "NTUSER"; break; default: return "UNKNOWN"; } } /* * HasChangesToCommit */ bool RegistryHive::HasChangesToCommit() { return this->has_changes_to_commit; } /* * GetNodes */ QMap RegistryHive::GetNodes(QString path) { hive_node_h parent_node; // Get handle to last node in path if(!this->GetNodeHandle(path,&parent_node)) return QMap(); // Get and return nodes return this->GetNodesHelper(parent_node); } /* * GetNodes */ QMap RegistryHive::GetNodes(int parent_node) { if(parent_node==0) { this->SetError(tr("Invalid parent node handle specified!")); return QMap(); } // Get and return nodes return this->GetNodesHelper(parent_node); } /* * GetKeys */ QMap RegistryHive::GetKeys(QString path) { hive_node_h parent_node; // Get handle to last node in path if(!this->GetNodeHandle(path,&parent_node)) return QMap(); // Get and return keys return this->GetKeysHelper(parent_node); } /* * GetKeys */ QMap RegistryHive::GetKeys(int parent_node) { if(parent_node==0) { this->SetError(tr("Invalid parent node handle specified!")); return QMap(); } // Get and return keys return this->GetKeysHelper(parent_node); } /* * GetKeyName */ bool RegistryHive::GetKeyName(int hive_key, QString &key_name) { char *buf; if(!this->is_hive_open) { this->SetError(tr("Need to operate on an open hive!")); return false; } buf=hivex_value_key(this->p_hive,(hive_value_h)hive_key); if(buf==NULL) { this->SetError(tr("Unable to get key name for key '%1'").arg(hive_key)); return false; } key_name=QString(buf); free(buf); return true; } /* * GetKeyValue */ QByteArray RegistryHive::GetKeyValue(QString path, QString key, int *p_value_type, size_t *p_value_len) { hive_node_h parent_node; hive_value_h hive_key; // Get handle to last node in path if(!this->GetNodeHandle(path,&parent_node)) return QByteArray(); // Get key handle hive_key=hivex_node_get_value(this->p_hive, parent_node,key.toAscii().constData()); if(hive_key==0) { this->SetError(tr("Unable to get key handle!")); *p_value_len=-1; return QByteArray(); } // Get and return key value return this->GetKeyValueHelper(hive_key,p_value_type,p_value_len); } /* * GetKeyValue */ QByteArray RegistryHive::GetKeyValue(int hive_key, int *p_value_type, size_t *p_value_len) { if(hive_key==0) { this->SetError(tr("Invalid key handle specified!")); *p_value_type=-1; return QByteArray(); } // Get and return key value return this->GetKeyValueHelper(hive_key,p_value_type,p_value_len); } /* * GetKeyModTime */ int64_t RegistryHive::GetNodeModTime(QString path) { hive_node_h node; // Get handle to last node in path if(!this->GetNodeHandle(path,&node)) { this->SetError(tr("Unable to get node handle!")); return 0; } // Get and return node's last modification timestamp return this->GetNodeModTime(node); } /* * GetKeyModTime */ int64_t RegistryHive::GetNodeModTime(int node) { if(node==0) { this->SetError(tr("Invalid node handle specified!")); return 0; } // Get and return key's last modification timestamp return hivex_node_timestamp(this->p_hive,node); } /* * KeyValueToString */ QString RegistryHive::KeyValueToString(QByteArray value, int value_type) { QString ret=""; - int i=0; - #define ToHexStr() { \ - for(i=0;i=2 && value.endsWith(QByteArray("\x00\x00",2))) { + // Seems to be a unicode string, convert to host endianness and return + // TODO: What if it is UTF16-BE?? Thx Billy! + QByteArray buf=value; + UTF16LETOH(buf.data(),buf.size()); + ret=QString().fromUtf16((ushort*)(buf.constData())); + } else if(value.endsWith(QByteArray("\x00",1))) { + // Seems to be an ansi string + ret=QString().fromAscii((char*)value.constData()); + } else { + // If we can't detect encoding, return string as hex + ToHexStr(); + } break; - case hive_t_REG_BINARY: - // A blob of binary - ToHexStr(); + case hive_t_REG_MULTI_SZ: + // Multiple Windows strings. + // I suppose this is always LE encoded! M$ devs really suck! + ret=RegistryHive::KeyValueToStringList(value).join("\n"); break; case hive_t_REG_DWORD: // DWORD (32 bit integer), little endian ret=QString().sprintf("0x%08X",LE32TOH(*(uint32_t*)value.constData())); break; case hive_t_REG_DWORD_BIG_ENDIAN: // DWORD (32 bit integer), big endian ret=QString().sprintf("0x%08X",BE32TOH(*(uint32_t*)value.constData())); break; - case hive_t_REG_LINK: - // Symbolic link to another part of the registry tree - ToHexStr(); - break; - case hive_t_REG_MULTI_SZ: - // Multiple Windows strings. - // I suppose this is always LE encoded! M$ devs really suck! - ret=RegistryHive::KeyValueToStringList(value).join("\n"); - break; - case hive_t_REG_RESOURCE_LIST: - // Resource list - ToHexStr(); - break; - case hive_t_REG_FULL_RESOURCE_DESCRIPTOR: - // Resource descriptor - ToHexStr(); - break; - case hive_t_REG_RESOURCE_REQUIREMENTS_LIST: - // Resouce requirements list - ToHexStr(); - break; case hive_t_REG_QWORD: - // QWORD (64 bit integer). Usually little endian. + // QWORD (64 bit integer). Usually little endian (grrrr). ret= QString("0x%1").arg((quint64)LE64TOH(*(uint64_t*)value.constData()), 16, 16, QChar('0')); break; + case hive_t_REG_NONE: + case hive_t_REG_BINARY: + case hive_t_REG_LINK: + case hive_t_REG_RESOURCE_LIST: + case hive_t_REG_FULL_RESOURCE_DESCRIPTOR: + case hive_t_REG_RESOURCE_REQUIREMENTS_LIST: default: + // A key without a value (REG_NONE), a blob of binary (REG_BINARY), a + // symbolic link to another part of the registry tree (REG_LINK), a + // resource list (REG_RESOURCE_LIST), a resource descriptor + // (FULL_RESOURCE_DESCRIPTOR), a resource requirements list + // (REG_RESOURCE_REQUIREMENTS_LIST) or something unknown. + // All these are converted to hex. ToHexStr(); } #undef ToHexStr return ret; } /* * KeyValueToString */ QString RegistryHive::KeyValueToString(QByteArray key_value, QString format, int offset, int length, bool little_endian) { int remaining_data_len; const char *p_data; QString ret=""; // Calculate how many bytes are remainig after specified offset remaining_data_len=key_value.size()-offset; if(!remaining_data_len>0) { // Nothing to show return QString(); } // Get pointer to data at specified offset p_data=key_value.constData(); p_data+=offset; // Convert value if(format=="int8" && remaining_data_len>=1) { ret=QString().sprintf("%d",*(int8_t*)p_data); } else if(format=="uint8" && remaining_data_len>=1) { ret=QString().sprintf("%u",*(uint8_t*)p_data); } else if(format=="int16" && remaining_data_len>=2) { int16_t val; if(little_endian) val=LE16TOH(*(int16_t*)p_data); else val=BE16TOH(*(int16_t*)p_data); ret=QString().sprintf("%d",val); } else if(format=="uint16" && remaining_data_len>=2) { uint16_t val; if(little_endian) val=LE16TOH(*(uint16_t*)p_data); else val=BE16TOH(*(uint16_t*)p_data); ret=QString().sprintf("%u",val); } else if(format=="int32" && remaining_data_len>=4) { int32_t val; if(little_endian) val=LE32TOH(*(int32_t*)p_data); else val=BE32TOH(*(int32_t*)p_data); ret=QString().sprintf("%d",val); } else if(format=="uint32" && remaining_data_len>=4) { uint32_t val; if(little_endian) val=LE32TOH(*(uint32_t*)p_data); else val=BE32TOH(*(uint32_t*)p_data); ret=QString().sprintf("%u",val); } else if(format=="unixtime" && remaining_data_len>=4) { uint32_t val; if(little_endian) val=LE32TOH(*(uint32_t*)p_data); else val=BE32TOH(*(uint32_t*)p_data); if(val==0) { ret="n/a"; } else { QDateTime date_time; date_time.setTimeSpec(Qt::UTC); date_time.setTime_t(val); ret=date_time.toString("yyyy/MM/dd hh:mm:ss"); } } else if(format=="int64" && remaining_data_len>=8) { int64_t val; if(little_endian) val=LE64TOH(*(int64_t*)p_data); else val=BE64TOH(*(int64_t*)p_data); ret=QString("%1").arg(val); } else if(format=="uint64" && remaining_data_len>=8) { uint64_t val; if(little_endian) val=LE64TOH(*(uint64_t*)p_data); else val=BE64TOH(*(uint64_t*)p_data); ret=QString("%1").arg(val); /* // TODO: Check how one could implement this } else if(format=="unixtime64" && remaining_data_len>=8) { if(*(uint64_t*)p_data==0) { ret="n/a"; } else { uint64_t secs=*(uint64_t*)p_data; QDateTime date_time; date_time.setTimeSpec(Qt::UTC); // Set 32bit part of date/time date_time.setTime_t(secs&0xFFFFFFFF); // Now add high 32bit part of date/time date_time.addSecs(secs>>32); ret=date_time.toString("yyyy/MM/dd hh:mm:ss"); } */ } else if(format=="filetime" && remaining_data_len>=8) { uint64_t val; if(little_endian) val=LE64TOH(*(uint64_t*)p_data); else val=BE64TOH(*(uint64_t*)p_data); if(val==0) { ret="n/a"; } else { // TODO: Warn if >32bit QDateTime date_time; date_time.setTimeSpec(Qt::UTC); date_time.setTime_t(RegistryHive::FiletimeToUnixtime(val)); ret=date_time.toString("yyyy/MM/dd hh:mm:ss"); } } else if(format=="ascii") { if(length!=-1) { // User specified how many bytes to convert ret=QString().fromAscii((char*)p_data,length); } else { // User did not specify how many bytes to convert, make sure data is 0 // terminated if(key_value.indexOf("\x00",offset)!=-1) { // Data is 0 terminated ret=QString().fromAscii((char*)p_data); } else { // Data is not 0 terminated, convert all remaining_data_len bytes ret=QString().fromAscii((char*)p_data,remaining_data_len); } } } else if(format=="utf16" && remaining_data_len>=2) { + QByteArray buf; if(length!=-1) { // User specified how many bytes to convert - ret=QString().fromUtf16((ushort*)p_data,length); + buf=key_value.mid(offset,(length%2)==0 ? length : length-1); + buf.append("\x00\x00",2); } else { // User did not specify how many bytes to convert, make sure data is // double 0 terminated - if(key_value.indexOf(QByteArray("\x00\x00",2),offset)!=-1) { + int null_offset=RegistryHive::FindUnicodeStringEnd(key_value.mid(offset)); + if(null_offset!=-1) { // Data is double 0 terminated - ret=QString().fromUtf16((ushort*)p_data); + buf=key_value.mid(offset,null_offset+2); } else { // Data is not double 0 terminated, convert all remaining_data_len bytes - ret=QString().fromUtf16((ushort*)p_data,remaining_data_len); + buf=key_value.mid(offset, + (remaining_data_len%2)==0 ? + remaining_data_len : remaining_data_len-1); + buf.append("\x00\x00",2); } } + // Convert from requested endianness to host + if(little_endian) { + UTF16LETOH(buf.data(),buf.size()); + } else { + UTF16BETOH(buf.data(),buf.size()); + } + ret=QString().fromUtf16((ushort*)buf.constData()); } else { // Unknown variant type or another error + // TODO: Maybe return an error return QString(); } return ret; } /* * KeyValueToStringList * * Should only be used for REG_MULTI_SZ values */ QStringList RegistryHive::KeyValueToStringList(QByteArray value, bool little_endian, bool *p_ansi_encoded) { // Try to find value encoding (ANSI vs UNICODE) bool is_ansi; if(value.size()<=2) { // http://blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx // Ansi version of a REG_MULTI_SZ needs to be terminated by 2 \0 chars. // So as long as the byte array has less or equal to 2 chars, it must be // empty. return QStringList(); } else if(value.size()==3) { // Only 3 chars, this can only be an ansi string consisting of 1 char and 2 // \0 to terminate it return QStringList() < strings_it(strings); while(strings_it.hasNext()) { cur_string=strings_it.next(); if(ansi_encoded) { // Ansi encoding, simply append char string and terminating \0 result.append(cur_string.toAscii().constData(),cur_string.size()); result.append("\x00",1); } else { // Unicode encoding // First, convert value to utf16 // TODO: May fail if there is a char that needs more than 16 bit buf=QByteArray((char*)(cur_string.utf16()),cur_string.size()*2); // Then convert to correct endianness if(little_endian) { - for(int i=0;iis_hive_writable) return 0; // Make sure name does not contain a backslash char if(node_name.contains('\\')) { this->SetError(tr("Unable to add node with name '%1'. " "Names can not include a backslash character.") .arg(node_name)); return 0; } // Get node handle to the parent where the new node should be created hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { this->SetError(tr("Unable to get node handle for '%1'!") .arg(parent_node_path)); return 0; } // Make sure there is no other node with same name QMap child_nodes=this->GetNodes(parent_node); if(child_nodes.contains(node_name.toAscii())) { this->SetError(tr("The node '%1\\%2' already exists!") .arg(parent_node_path,node_name)); return 0; } // Add new node hive_node_h new_node=hivex_node_add_child(this->p_hive, parent_node, node_name.toAscii().constData()); if(new_node==0) { this->SetError(tr("Unable to create new node '%1\\%2'!") .arg(parent_node_path,node_name)); return 0; } this->has_changes_to_commit=true; return new_node; } /* * DeleteNode */ bool RegistryHive::DeleteNode(QString node_path) { if(!this->is_hive_writable) return false; // Get node handle to the node that should be deleted hive_node_h node; if(!this->GetNodeHandle(node_path,&node)) { this->SetError(tr("Unable to get node handle for '%1'!") .arg(node_path)); return false; } // Delete node if(hivex_node_delete_child(this->p_hive,node)==-1) { this->SetError(tr("Unable to delete node '%1'!") .arg(node_path)); return false; } this->has_changes_to_commit=true; return true; } /* * AddKey */ int RegistryHive::AddKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value) { if(!this->is_hive_open || !this->is_hive_writable) { this->SetError(tr("Hive has not been opened or opened read-only!")); return false; } return this->SetKey(parent_node_path, key_name, key_value_type, key_value, true); } /* * UpdateKey */ int RegistryHive::UpdateKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value) { if(!this->is_hive_open || !this->is_hive_writable) { this->SetError(tr("Hive has not been opened or opened read-only!")); return false; } return this->SetKey(parent_node_path, key_name, key_value_type, key_value, false); } /* * DeleteKey */ bool RegistryHive::DeleteKey(QString parent_node_path, QString key_name) { if(!this->is_hive_open || !this->is_hive_writable) { this->SetError(tr("Hive has not been opened or opened read-only!")); return false; } // libhivex offers no possibility to delete a single key :-( // As a work around, this function temporarly stores all keys of the specified // node, then deletes them all an re-creates all but the one that should be // deleted. // Get handle to parent node hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { return false; } // Get all child keys hive_value_h *p_keys=hivex_node_values(this->p_hive,parent_node); if(p_keys==NULL) { this->SetError(tr("Unable to enumerate child keys for parent '%1'!") .arg(parent_node_path)); return false; } // Get all child key values except the one that should be deleted int i=0; char *p_name; int node_keys_count=0; hive_set_value *node_keys=NULL; #define FREE_NODE_KEYS() { \ for(int x=0;xp_hive,p_keys[i]); if(p_name==NULL) { this->SetError(tr("Unable to get key name for a child of '%1'!") .arg(parent_node_path)); return false; } if(QString(p_name)!=key_name) { // Current key is not the one that should be deleted, save it // Alloc mem for new hive_set_value struct in node_keys array node_keys=(hive_set_value*)realloc(node_keys, sizeof(hive_set_value)* (node_keys_count+1)); if(node_keys==NULL) { this->SetError(tr("Unable to alloc enough memory for all child keys!")); return false; } // Save key name in hive_set_value struct node_keys[node_keys_count].key=p_name; // Get key value, key value type and key value len and save to // hive_set_value struct node_keys[node_keys_count].value= hivex_value_value(this->p_hive, p_keys[i], &(node_keys[node_keys_count].t), &(node_keys[node_keys_count].len)); if(node_keys[node_keys_count].value==NULL) { this->SetError(tr("Unable to get value for key '%1'!").arg(p_name)); free(p_name); // Free all temporary stored keys FREE_NODE_KEYS(); return false; } node_keys_count++; } else { // Current key is to be deleted, ignore it free(p_name); } i++; } // Save all stored keys to hive, which will discard the one that should be // deleted if(hivex_node_set_values(this->p_hive, parent_node, node_keys_count, node_keys, 0)!=0) { this->SetError(tr("Unable to re-save all child keys! Please discard any " "changes you made and start over. No doing so might end " "in data loss!")); // Free all temporary stored keys FREE_NODE_KEYS(); return false; } // Free all temporary stored keys and return FREE_NODE_KEYS(); #undef FREE_NODE_KEYS this->has_changes_to_commit=true; return true; } /******************************************************************************* * Private ******************************************************************************/ /* * HivexError2String */ QString RegistryHive::HivexError2String(int error) { switch(error) { case ENOTSUP: return QString("Corrupt or unsupported Registry file format."); break; case HIVEX_NO_KEY: return QString("Missing root key."); break; case EINVAL: return QString("Passed an invalid argument to the function."); break; case EFAULT: return QString("Followed a Registry pointer which goes outside the " "registry or outside a registry block."); break; case ELOOP: return QString("Registry contains cycles."); break; case ERANGE: return QString("Field in the registry out of range."); break; case EEXIST: return QString("Registry key already exists."); break; case EROFS: return QString("Tried to write to a registry which is not opened for " "writing."); break; default: return QString("Unknown error."); } } /* * SetError */ void RegistryHive::SetError(QString msg) { this->erro_msg=msg; this->is_error=true; } /* * GetNodeHandle */ bool RegistryHive::GetNodeHandle(QString &path, hive_node_h *p_node) { QStringList nodes; int i=0; // Get root node handle *p_node=hivex_root(this->p_hive); if(*p_node==0) { this->SetError(tr("Unable to get root node!")); return false; } if(path!="\\") { // If we aren't listing the root node, we have to get a handle to the // last node in the path. Split path into nodes nodes=path.split('\\',QString::SkipEmptyParts); // Iterate to the correct parent node for(i=0;ip_hive, *p_node, nodes.value(i).toAscii().constData()); if(*p_node==0) { this->SetError(tr("Unable to find node '%1'!").arg(nodes.value(i))); return false; } } } return true; } /* * GetKeyHandle */ bool RegistryHive::GetKeyHandle(QString &parent_node_path, QString &key_name, hive_value_h *p_key) { // Get handle to parent node hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { return false; } // Get handle to key *p_key=hivex_node_get_value(this->p_hive, parent_node, key_name.toAscii().constData()); if(*p_key==0) { this->SetError(tr("Unable to get handle to key '%1\\%2'!") .arg(parent_node_path,key_name)); return false; } return true; } /* * GetNodesHelper */ QMap RegistryHive::GetNodesHelper(hive_node_h parent_node) { QMap keys; char *p_name; int i=0; // Get child nodes hive_node_h *child_nodes=hivex_node_children(this->p_hive,parent_node); if(child_nodes==NULL) { this->SetError( tr("Unable to enumerate child nodes!")); return QMap(); } // Build result keys.clear(); i=0; while(child_nodes[i]) { p_name=hivex_node_name(this->p_hive,child_nodes[i]); if(p_name==NULL) { this->SetError(tr("Unable to get node name!")); free(child_nodes); return QMap(); } keys.insert(QString(p_name),(int)child_nodes[i]); free(p_name); i++; } free(child_nodes); return keys; } /* * GetKeysHelper */ QMap RegistryHive::GetKeysHelper(hive_node_h parent_node) { QMap keys; char *p_name; int i=0; // Get child keys hive_value_h *p_keys=hivex_node_values(this->p_hive,parent_node); if(p_keys==NULL) { this->SetError( tr("Unable to enumerate child keys!")); return QMap(); } // Build result list keys.clear(); i=0; while(p_keys[i]) { p_name=hivex_value_key(this->p_hive,p_keys[i]); if(p_name==NULL) { this->SetError(tr("Unable to get key name!")); return QMap(); } keys.insert(QString(p_name),p_keys[i]); free(p_name); i++; } free(p_keys); return keys; } /* * GetKeyValueHelper */ QByteArray RegistryHive::GetKeyValueHelper(hive_value_h hive_key, int *p_value_type, size_t *p_value_len) { QByteArray key_value; char *p_key_value; p_key_value=hivex_value_value(this->p_hive, hive_key, (hive_type*)p_value_type, p_value_len); if(p_key_value==NULL) { this->SetError(tr("Unable to get key value!")); *p_value_type=-1; return QByteArray(); } // Feed QByteArray and free p_key_value key_value=QByteArray(p_key_value,*p_value_len); free(p_key_value); return key_value; } /* * PathExists */ bool RegistryHive::PathExists(QString path) { bool ret; hive_node_h node; ret=this->GetNodeHandle(path,&node); if(!ret || this->Error()) { // Clear error and return false this->GetErrorMsg(); return false; } return true; } /* * SetKey */ int RegistryHive::SetKey(QString &parent_node_path, QString &key_name, QString &key_value_type, QByteArray &key_value, bool create_key) { // Get node handle to the node that holds the key to create/update hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { return 0; } // Make sure key exists if we should update it if(!create_key) { hive_value_h temp_key=hivex_node_get_value(this->p_hive, parent_node, key_name.toAscii().constData()); if(temp_key==0) { this->SetError(tr("Inexisting key '%1\\%2' can't be updated!") .arg(parent_node_path,key_name)); return 0; } } // Create and populate hive_set_value structure hive_set_value key_val; key_val.key=(char*)malloc((sizeof(char)*key_name.toAscii().count())+1); key_val.value=(char*)malloc(sizeof(char)*key_value.size()); if(key_val.key==NULL || key_val.value==NULL) { this->SetError(tr("Unable to alloc memory for hive_set_value struct!")); return 0; } strcpy(key_val.key,key_name.toAscii().constData()); key_val.t=(hive_type)this->StringToKeyValueType(key_value_type); key_val.len=key_value.size(); memcpy(key_val.value,key_value.constData(),key_value.size()); // Create/Update key if(hivex_node_set_value(this->p_hive,parent_node,&key_val,0)!=0) { this->SetError(tr("Unable to update key '%1\\%2'!") .arg(parent_node_path,key_name)); return 0; } // Free the hive_set_value structure free(key_val.key); free(key_val.value); // To make sure everything worked, a hadle to the new key is now requeried // from hive and then returned hive_value_h key; if(!this->GetKeyHandle(parent_node_path,key_name,&key)) { return 0; } this->has_changes_to_commit=true; return key; } + +/* + * FindUnicodeStringEnd + */ +int RegistryHive::FindUnicodeStringEnd(QByteArray data, int offset) { + int end_pos; + for(end_pos=offset;end_pos<(data.size()-1);end_pos+=2) { + if(*((uint16_t*)(data.constData()+end_pos))==0) break; + } + return end_pos<(data.size()-1) ? end_pos : -1; +} diff --git a/trunk/registryhive.h b/trunk/registryhive.h index b9a319b..b782a03 100644 --- a/trunk/registryhive.h +++ b/trunk/registryhive.h @@ -1,131 +1,132 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef REGISTRYHIVE_H #define REGISTRYHIVE_H #include #include #include class RegistryHive : public QObject { Q_OBJECT public: typedef enum eHiveType { eHiveType_UNKNOWN=0, eHiveType_SYSTEM, eHiveType_SOFTWARE, eHiveType_SAM, eHiveType_SECURITY, eHiveType_NTUSER } teHiveType; explicit RegistryHive(QObject *p_parent=0); ~RegistryHive(); bool Error(); QString GetErrorMsg(); bool Open(QString file, bool read_only=true); bool Reopen(bool read_only=true); bool CommitChanges(); bool Close(); QString Filename(); teHiveType HiveType(); QString HiveTypeToString(teHiveType hive_type); bool HasChangesToCommit(); QMap GetNodes(QString path="\\"); QMap GetNodes(int parent_node=0); QMap GetKeys(QString path="\\"); QMap GetKeys(int parent_node=0); bool GetKeyName(int hive_key, QString &key_name); QByteArray GetKeyValue(QString path, QString key, int *p_value_type, size_t *p_value_len); QByteArray GetKeyValue(int hive_key, int *p_value_type, size_t *p_value_len); int64_t GetNodeModTime(QString path); int64_t GetNodeModTime(int node); static QString KeyValueToString(QByteArray value, int value_type); static QString KeyValueToString(QByteArray value, QString format, int offset=0, int length=-1, bool little_endian=true); static QStringList KeyValueToStringList(QByteArray value, bool little_endian=true, bool *p_ansi_encoded=NULL); static QByteArray StringListToKeyValue(QStringList strings, bool little_endian=true, bool ansi_encoded=false); static QStringList GetKeyValueTypes(); static QString KeyValueTypeToString(int value_type); static int StringToKeyValueType(QString value_type); static uint64_t FiletimeToUnixtime(int64_t filetime); int AddNode(QString parent_node_path, QString node_name); bool DeleteNode(QString node_path); int AddKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value); int UpdateKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value); bool DeleteKey(QString parent_node_path, QString key_name); private: QString erro_msg; bool is_error; QString hive_file; hive_h *p_hive; bool is_hive_open; bool is_hive_writable; bool has_changes_to_commit; QString HivexError2String(int error); void SetError(QString msg); bool GetNodeHandle(QString &path, hive_node_h *p_node); bool GetKeyHandle(QString &parent_node_path, QString &key_name, hive_value_h *p_key); QMap GetNodesHelper(hive_node_h parent_node); QMap GetKeysHelper(hive_node_h parent_node); QByteArray GetKeyValueHelper(hive_value_h hive_key, int *p_value_type, size_t *p_value_len); bool PathExists(QString path); int SetKey(QString &parent_node_path, QString &key_name, QString &key_value_type, QByteArray &key_value, bool create_key); + static int FindUnicodeStringEnd(QByteArray data, int offset=0); }; #endif // REGISTRYHIVE_H diff --git a/trunk/registrykeytable.cpp b/trunk/registrykeytable.cpp index d01cfe5..a15e7b8 100644 --- a/trunk/registrykeytable.cpp +++ b/trunk/registrykeytable.cpp @@ -1,211 +1,213 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include "registrykeytable.h" #include #include #include /******************************************************************************* * Public ******************************************************************************/ RegistryKeyTable::RegistryKeyTable(QWidget *p_parent) : QTableView(p_parent) { this->is_writable=false; // Configure widget this->setSelectionMode(QAbstractItemView::SingleSelection); this->setSelectionBehavior(QAbstractItemView::SelectRows); this->setAutoScroll(false); this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); this->verticalHeader()->setHidden(true); this->setTextElideMode(Qt::ElideNone); // Create context menu item this->p_action_add_key=new QAction(tr("Add new key"),this); this->p_action_edit_key=new QAction(tr("Edit selected key"),this); this->p_action_delete_key=new QAction(tr("Delete selected key"),this); this->p_menu_copy=new QMenu(tr("Copy"),this); this->p_action_copy_key_name= new QAction(tr("Selected key name"),this->p_menu_copy); this->p_menu_copy->addAction(this->p_action_copy_key_name); this->p_action_copy_key_value= new QAction(tr("Selected key value"),this->p_menu_copy); this->p_menu_copy->addAction(this->p_action_copy_key_value); // Connect context menu signals this->connect(this->p_action_add_key, SIGNAL(triggered()), this, SLOT(SlotAddKey())); this->connect(this->p_action_edit_key, SIGNAL(triggered()), this, SLOT(SlotEditKey())); this->connect(this->p_action_delete_key, SIGNAL(triggered()), this, SLOT(SlotDeleteKey())); this->connect(this->p_action_copy_key_name, SIGNAL(triggered()), this, SLOT(SlotCopyKeyName())); this->connect(this->p_action_copy_key_value, SIGNAL(triggered()), this, SLOT(SlotCopyKeyValue())); } RegistryKeyTable::~RegistryKeyTable() { // Delete context menu delete this->p_action_copy_key_name; delete this->p_action_copy_key_value; delete this->p_menu_copy; delete this->p_action_delete_key; delete this->p_action_edit_key; delete this->p_action_add_key; } void RegistryKeyTable::setModel(QAbstractItemModel *p_model, bool writable) { QTableView::setModel(p_model); // Resize table rows / columns to fit data this->resizeColumnsToContents(); this->resizeRowsToContents(); this->horizontalHeader()->stretchLastSection(); if(p_model!=NULL && p_model->rowCount()>0) { // Select first table item this->selectRow(0); } // Set writable status this->SetWritable(writable); } void RegistryKeyTable::SetWritable(bool writable) { this->is_writable=writable; this->p_action_add_key->setEnabled(this->is_writable); this->p_action_edit_key->setEnabled(this->is_writable); this->p_action_delete_key->setEnabled(this->is_writable); } /* void RegistryKeyTable::selectRow(QString key_name) { int i; this->clearSelection(); for(i=0;imodel()->rowCount();i++) { if(this->model()) } } */ /******************************************************************************* * Protected ******************************************************************************/ int RegistryKeyTable::sizeHintForColumn(int column) const { int size_hint=-1; int i=0; int item_width=0; QFontMetrics fm(this->fontMetrics()); QModelIndex idx; if(this->model()==NULL) return -1; // Find string that needs the most amount of space idx=this->model()->index(i,column); while(idx.isValid()) { item_width=fm.width(this->model()->data(idx).toString())+10; if(item_width>size_hint) size_hint=item_width; idx=this->model()->index(++i,column); } return size_hint; } void RegistryKeyTable::contextMenuEvent(QContextMenuEvent *p_event) { // Only show context menu if a hive is open (a model was set) if(this->model()==NULL) return; // Decide what menus should be enabled if(this->selectedIndexes().count()==3) { // A row is selected, enable full context menu - this->p_action_edit_key->setEnabled(true); - this->p_action_delete_key->setEnabled(true); + this->p_action_add_key->setEnabled(this->is_writable); + this->p_action_edit_key->setEnabled(this->is_writable); + this->p_action_delete_key->setEnabled(this->is_writable); this->p_menu_copy->setEnabled(true); } else { // No row is selected, disable all menu items except AddKey + this->p_action_add_key->setEnabled(this->is_writable); this->p_action_edit_key->setEnabled(false); this->p_action_delete_key->setEnabled(false); this->p_menu_copy->setEnabled(false); } // Emit clicked signal (makes sure item under cursor is selected if it wasn't) emit(this->clicked(this->indexAt(p_event->pos()))); // Create context menu, add actions and show it QMenu context_menu(this); context_menu.addAction(this->p_action_add_key); context_menu.addAction(this->p_action_edit_key); context_menu.addAction(this->p_action_delete_key); context_menu.addSeparator(); context_menu.addMenu(this->p_menu_copy); context_menu.exec(p_event->globalPos()); } void RegistryKeyTable::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { // Call parent class's currentChanged first QTableView::currentChanged(current,previous); // Now emit our signal QModelIndex current_item=QModelIndex(current); emit(RegistryKeyTable::CurrentItemChanged(current_item)); } /******************************************************************************* * Private slots ******************************************************************************/ void RegistryKeyTable::SlotAddKey() { emit(this->SignalAddKey()); } void RegistryKeyTable::SlotEditKey() { emit(this->SignalEditKey(this->selectedIndexes().at(0))); } void RegistryKeyTable::SlotDeleteKey() { emit(this->SignalDeleteKey(this->selectedIndexes().at(0))); } void RegistryKeyTable::SlotCopyKeyName() { QApplication::clipboard()-> setText(this->selectedIndexes().at(0).data().toString(), QClipboard::Clipboard); } void RegistryKeyTable::SlotCopyKeyValue() { QApplication::clipboard()-> setText(this->selectedIndexes().at(2).data().toString(), QClipboard::Clipboard); } diff --git a/trunk/report_templates/NTUSER_Autoruns.qs b/trunk/report_templates/NTUSER_Autoruns.qs index f0a40a0..e67797c 100644 --- a/trunk/report_templates/NTUSER_Autoruns.qs +++ b/trunk/report_templates/NTUSER_Autoruns.qs @@ -1,61 +1,56 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Autoruns", report_author : "Gillen Daniel", report_desc : "Dump autorun keys", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function ListAutoruns(autorun_path,autorun_key) { println("

"); println(" "+autorun_key+"
"); var run_keys=GetRegistryKeys(autorun_path+autorun_key); if(IsValid(run_keys) && run_keys.length>0) { println(" "); print_table_row("Name","Executable"); for(var i=0;i"); } else { println("         None"); } println("

"); } function fred_report_html() { var val; -// println(""); -// println(" User Autoruns"); -// println(" "); println("

User Autoruns

"); // Run ListAutoruns("\\Microsoft\\Windows\\CurrentVersion\\","Run"); // RunOnce ListAutoruns("\\Microsoft\\Windows\\CurrentVersion\\","RunOnce"); // RunOnceEx ListAutoruns("\\Microsoft\\Windows\\CurrentVersion\\","RunOnceEx"); // TODO: There might be a Run under WindowsNT\CurrentVersion\Run too! - -// println(""); } diff --git a/trunk/report_templates/NTUSER_LaunchedApplications.qs b/trunk/report_templates/NTUSER_LaunchedApplications.qs index 1e4bb61..346296f 100644 --- a/trunk/report_templates/NTUSER_LaunchedApplications.qs +++ b/trunk/report_templates/NTUSER_LaunchedApplications.qs @@ -1,111 +1,108 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Launched applications", report_author : "Gillen Daniel", report_desc : "Dump IE launched applications", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function PrintTableRow(cell01,cell02,cell03) { println("
"); } function Rot13Decode(val) { var ret=""; for(var i=0;i64 && decoded<91) || (decoded>96 && decoded<123)) { if((decoded-13)<65 || (decoded>96 && (decoded-13)<97)) { decoded=(decoded-13)+26; } else { if(decoded>96 && (decoded-13)<97) { decoded+=13; } else { decoded-=13; } } ret+=String.fromCharCode(decoded); } else { ret+=val[i]; } } return ret; } function PrintUserAssistEntry(key,val,os) { var run_count; var last_run; switch(os) { case "winxp": run_count=RegistryKeyValueToVariant(val.value,"uint32",4); break; case "win7": run_count=RegistryKeyValueToVariant(val.value,"uint32",4,0,1); last_run=RegistryKeyValueToVariant(val.value,"filetime",60); break; } PrintTableRow(key,run_count,last_run); } function fred_report_html() { -// println(""); -// println(" Launched Applications"); -// println(" "); println("

Launched applications

"); // First, we need to find the correct GUID for the current Windows version var path; var apps; var os; // Windows XP os="winxp"; path="\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{5E6AB780-7743-11CF-A12B-00AA004AE837}\\Count"; apps=GetRegistryKeys(path); // TODO: Determine GUIDs for Vista / Win8 if(!IsValid(apps)) { // Windows 7 os="win7"; path="\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count"; apps=GetRegistryKeys(path); } if(IsValid(apps)) { if(apps.length!=0) { println("

"); println("

",cell01,"",cell02,"",cell03,"
"); println(" "); for(var i=0;i"); println("

"); } else { println("

"); println(" The list of launched applications is empty."); println("

"); } } else { println("

"); println(" This registry hive does not contain a list of launched applications!"); println("

"); } } diff --git a/trunk/report_templates/NTUSER_RecentDocs.qs b/trunk/report_templates/NTUSER_RecentDocs.qs index 32228fe..bd8b2fa 100644 --- a/trunk/report_templates/NTUSER_RecentDocs.qs +++ b/trunk/report_templates/NTUSER_RecentDocs.qs @@ -1,54 +1,49 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Recent documents", report_author : "Gillen Daniel", report_desc : "Dump recent docs", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function fred_report_html() { -// println(""); -// println(" Recent Documents"); -// println(" "); println("

Recent documents

"); // Get list of recent docs var recent_docs=GetRegistryKeyValue("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs","MRUListEx"); if(IsValid(recent_docs)) { // Iterate over all recent docs var i=0; var runlist=RegistryKeyValueToVariant(recent_docs.value,"uint32",i); if(Number(runlist)!=0xffffffff) { println("

"); println("

ApplicationRun countLast run
"); while(Number(runlist)!=0xffffffff) { var entry=GetRegistryKeyValue("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs",runlist.toString(10)); println(" "); i+=4; runlist=RegistryKeyValueToVariant(recent_docs.value,"uint32",i); } println("
",RegistryKeyValueToVariant(entry.value,"utf16",0),"
"); println("

"); } else { println("

"); println(" The list of recent documents is empty."); println("

"); } } else { println("

"); println(" This registry hive does not contain a list of recent documents!"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/NTUSER_TypedUrls.qs b/trunk/report_templates/NTUSER_TypedUrls.qs index f5c4e1c..d8465c7 100644 --- a/trunk/report_templates/NTUSER_TypedUrls.qs +++ b/trunk/report_templates/NTUSER_TypedUrls.qs @@ -1,49 +1,44 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Typed URLs", report_author : "Gillen Daniel", report_desc : "Dump typed URLs", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function fred_report_html() { -// println(""); -// println(" Typed Urls"); -// println(" "); println("

Typed urls

"); // Iterate over all typed urls var typed_urls=GetRegistryKeys("\\Software\\Microsoft\\Internet Explorer\\TypedURLs"); if(IsValid(typed_urls)) { if(typed_urls.length!=0) { println("

"); println(" "); for(var i=0;i"); } println("
",RegistryKeyValueToString(val.value,val.type),"
"); println("

"); } else { println("

"); println(" The list of typed urls is empty."); println("

"); } } else { println("

"); println(" This registry hive does not contain a list of typed urls!"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/NTUSER_Windows7_SearchKeywords.qs b/trunk/report_templates/NTUSER_Windows7_SearchKeywords.qs index fe97c28..b4afed4 100644 --- a/trunk/report_templates/NTUSER_Windows7_SearchKeywords.qs +++ b/trunk/report_templates/NTUSER_Windows7_SearchKeywords.qs @@ -1,54 +1,49 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Windows 7 search keywords", report_author : "Gillen Daniel", report_desc : "Dump Windows 7 search keywords", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function fred_report_html() { -// println(""); -// println(" Document And Folder Search Keywords"); -// println(" "); println("

Document and folder search keywords

"); // Get list of search keys var mrulist=GetRegistryKeyValue("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\WordWheelQuery","MRUListEx"); if(IsValid(mrulist)) { // Iterate over all items var i=0; var runlist=RegistryKeyValueToVariant(mrulist.value,"uint32",i); if(Number(runlist)!=0xffffffff) { println("

"); println(" "); while(Number(runlist)!=0xffffffff) { var entry=GetRegistryKeyValue("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\WordWheelQuery",runlist.toString(10)); println(" "); i+=4; runlist=RegistryKeyValueToVariant(mrulist.value,"uint32",i); } println("
",RegistryKeyValueToVariant(entry.value,"utf16",0),"
"); println("

"); } else { println("

"); println(" The list of document and search keywords is empty."); println("

"); } } else { println("

"); println(" This registry hive does not contain a list of document and folder search keywords!"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/NTUSER_Windows7_TypedPaths.qs b/trunk/report_templates/NTUSER_Windows7_TypedPaths.qs index 947a9c6..96ccb20 100644 --- a/trunk/report_templates/NTUSER_Windows7_TypedPaths.qs +++ b/trunk/report_templates/NTUSER_Windows7_TypedPaths.qs @@ -1,49 +1,44 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Windows 7 typed paths", report_author : "Gillen Daniel", report_desc : "Dump Windows 7 typed paths", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function fred_report_html() { -// println(""); -// println(" Typed Paths"); -// println(" "); println("

Typed paths

"); // Iterate over all typed paths var urls=GetRegistryKeys("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\TypedPaths"); if(IsValid(urls)) { if(urls.length!=0) { println("

"); println(" "); for(var i=0;i"); } println("
",RegistryKeyValueToString(val.value,val.type),"
"); println("

"); } else { println("

"); println(" The list of typed paths is empty."); println("

"); } } else { println("

"); println(" This registry hive does not contain a list of typed paths!"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/NTUSER_WindowsLiveAccounts.qs b/trunk/report_templates/NTUSER_WindowsLiveAccounts.qs index c52547b..5c48bd1 100644 --- a/trunk/report_templates/NTUSER_WindowsLiveAccounts.qs +++ b/trunk/report_templates/NTUSER_WindowsLiveAccounts.qs @@ -1,48 +1,43 @@ function fred_report_info() { var info={report_cat : "NTUSER", report_name : "Windows Live accounts", report_author : "Gillen Daniel", report_desc : "Dump Windows Live accounts", fred_api : 2, hive : "NTUSER" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function fred_report_html() { -// println(""); -// println(" Windows Live Accounts"); -// println(" "); println("

Windows live accounts

"); // Iterate over all contacts var accounts=GetRegistryKeys("\\Software\\Microsoft\\Windows Live Contacts\\Database"); if(IsValid(accounts)) { println("

"); println(" "); for(var i=0;i"); } accounts=GetRegistryKeys("\\Software\\Microsoft\\Windows Live Contacts\\Me"); for(var i=0;i"); } println("
",accounts[i],"",RegistryKeyValueToString(val.value,val.type),"
",accounts[i],"",RegistryKeyValueToString(val.value,val.type),"
"); println("

"); } else { println("

"); println(" This registry hive does not contain a list of Windows Live Accounts!"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SAM_UserAccounts.qs b/trunk/report_templates/SAM_UserAccounts.qs index f3ee71c..6eb2770 100644 --- a/trunk/report_templates/SAM_UserAccounts.qs +++ b/trunk/report_templates/SAM_UserAccounts.qs @@ -1,109 +1,104 @@ function fred_report_info() { var info={report_cat : "SAM", report_name : "User accounts", report_author : "Gillen Daniel", report_desc : "Dump Windows user accounts", fred_api : 2, hive : "SAM" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function print_v_info(v_key_value,info_name,str_off) { var offset=Number(RegistryKeyValueToVariant(v_key_value,"uint16",str_off))+0x0cc; var len=Number(RegistryKeyValueToVariant(v_key_value,"uint16",str_off+4))/2; if(len>0) print_table_row(info_name,RegistryKeyValueToVariant(v_key_value,"utf16",offset,len)); } function fred_report_html() { // See http://windowsir.blogspot.com/2006/08/getting-user-info-from-image.html -// println(""); -// println(" User Accounts"); -// println(" "); println("

User accounts

"); // Iterate over all user names var user_names=GetRegistryNodes("\\SAM\\Domains\\Account\\Users\\Names"); if(IsValid(user_names)) { for(var i=0;i"); // Print user name println(" ",user_names[i],"
"); println(" "); // Get user rid stored in "default" key var user_rid=GetRegistryKeyValue(String().concat("\\SAM\\Domains\\Account\\Users\\Names\\",user_names[i]),""); user_rid=RegistryKeyTypeToString(user_rid.type); println(" "); // RegistryKeyTypeToString returns the rid prepended with "0x". We have to remove that for further processing user_rid=String(user_rid).substr(2); // Get user's V key and print various infos var v_key=GetRegistryKeyValue(String().concat("\\SAM\\Domains\\Account\\Users\\",user_rid),"V"); print_v_info(v_key.value,"Full name:",0x18); print_v_info(v_key.value,"Comment:",0x24); print_v_info(v_key.value,"Home directory:",0x48); print_v_info(v_key.value,"Home directory drive:",0x54); print_v_info(v_key.value,"Logon script path:",0x60); print_v_info(v_key.value,"Profile path:",0x6c); // Get user's F key and print various infos var f_key=GetRegistryKeyValue(String().concat("\\SAM\\Domains\\Account\\Users\\",user_rid),"F"); print_table_row("Last login time:",RegistryKeyValueToVariant(f_key.value,"filetime",8)); print_table_row("Last pw change:",RegistryKeyValueToVariant(f_key.value,"filetime",24)); print_table_row("Last failed login:",RegistryKeyValueToVariant(f_key.value,"filetime",40)); print_table_row("Account expires:",RegistryKeyValueToVariant(f_key.value,"filetime",32)); print_table_row("Total logins:",RegistryKeyValueToVariant(f_key.value,"uint16",66)); print_table_row("Failed logins:",RegistryKeyValueToVariant(f_key.value,"uint16",64)); var acc_flags=Number(RegistryKeyValueToVariant(f_key.value,"uint16",56)); print(" "); // Get password hint if available var hint=GetRegistryKeyValue(String().concat("\\SAM\\Domains\\Account\\Users\\",user_rid),"UserPasswordHint"); if(typeof hint !== 'undefined') { // Append missing trailing utf16 zero byte hint.value.appendByte(0); hint.value.appendByte(0); print_table_row("Password hint:",RegistryKeyValueToVariant(hint.value,"utf16")); } // TODO: User group membership println("
RID:",Number(user_rid).toString(10)," (",user_rid,")","
Account flags:"); if(acc_flags&0x0001) print("Disabled "); if(acc_flags&0x0002) print("HomeDirReq "); if(acc_flags&0x0004) print("PwNotReq "); if(acc_flags&0x0008) print("TempDupAcc "); // I don't think this would be useful to show //if(acc_flags&0x0010) print("NormUserAcc "); if(acc_flags&0x0020) print("MnsAcc "); if(acc_flags&0x0040) print("DomTrustAcc "); if(acc_flags&0x0080) print("WksTrustAcc "); if(acc_flags&0x0100) print("SrvTrustAcc "); if(acc_flags&0x0200) print("NoPwExpiry "); if(acc_flags&0x0400) print("AccAutoLock "); print(" (",acc_flags,")"); println("
"); println("

"); } } else { println("

"); println(" Unable to enumerate users!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SOFTWARE_Autoruns.qs b/trunk/report_templates/SOFTWARE_Autoruns.qs index d335d70..053e63d 100644 --- a/trunk/report_templates/SOFTWARE_Autoruns.qs +++ b/trunk/report_templates/SOFTWARE_Autoruns.qs @@ -1,61 +1,56 @@ function fred_report_info() { var info={report_cat : "SOFTWARE", report_name : "Autoruns", report_author : "Gillen Daniel", report_desc : "Dump autoruns", fred_api : 2, hive : "SOFTWARE" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function ListAutoruns(autorun_path,autorun_key) { println("

"); println(" "+autorun_key+"
"); var run_keys=GetRegistryKeys(autorun_path+autorun_key); if(IsValid(run_keys) && run_keys.length>0) { println(" "); print_table_row("Name","Executable"); for(var i=0;i"); } else { println("         None"); } println("

"); } function fred_report_html() { var val; -// println(""); -// println(" System Autoruns"); -// println(" "); println("

System Autoruns

"); // Run ListAutoruns("\\Microsoft\\Windows\\CurrentVersion\\","Run"); // RunOnce ListAutoruns("\\Microsoft\\Windows\\CurrentVersion\\","RunOnce"); // RunOnceEx ListAutoruns("\\Microsoft\\Windows\\CurrentVersion\\","RunOnceEx"); // TODO: There might be a Run under WindowsNT\CurrentVersion\Run too! - -// println(""); } diff --git a/trunk/report_templates/SOFTWARE_ProfileList.qs b/trunk/report_templates/SOFTWARE_ProfileList.qs index 435e7e0..3f00709 100644 --- a/trunk/report_templates/SOFTWARE_ProfileList.qs +++ b/trunk/report_templates/SOFTWARE_ProfileList.qs @@ -1,56 +1,51 @@ function fred_report_info() { var info={report_cat : "SOFTWARE", report_name : "Profile list", report_author : "Gillen Daniel", report_desc : "Dump profile list", fred_api : 2, hive : "SOFTWARE" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println("
"); } function fred_report_html() { var val; -// println(""); -// println(" Profile List"); -// println(" "); println("

Profile List

"); var profile_list=GetRegistryNodes("\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"); if(IsValid(profile_list) && profile_list.length>0) { for(var i=0;i"); println(" "+profile_list[i]+"
"); println("
",cell01,"",cell02,"
"); // Get profile image path val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\"+profile_list[i],"ProfileImagePath"); print_table_row("Profile image path:",IsValid(val) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); // Get last load time (Saved as 2 dwords. Another "good" idea of M$ ;-)) var loadtime_low=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\"+profile_list[i],"ProfileLoadTimeLow"); var loadtime_high=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\"+profile_list[i],"ProfileLoadTimeHigh"); print_table_row("Profile load time:",(IsValid(loadtime_low) && IsValid(loadtime_high)) ? RegistryKeyValueToVariant(loadtime_low.value.append(loadtime_high.value),"filetime",0) : "n/a"); // TODO: There is more to decode under \\Microsoft\\Windows NT\\CurrentVersion\\ProfileList println("
"); println("

"); } println(" "); } else { println("         None"); } - -// println(""); } diff --git a/trunk/report_templates/SOFTWARE_WindowsVersion.qs b/trunk/report_templates/SOFTWARE_WindowsVersion.qs index 0a06fb3..b2151f4 100644 --- a/trunk/report_templates/SOFTWARE_WindowsVersion.qs +++ b/trunk/report_templates/SOFTWARE_WindowsVersion.qs @@ -1,109 +1,104 @@ function fred_report_info() { var info={report_cat : "SOFTWARE", report_name : "Windows version", report_author : "Gillen Daniel", report_desc : "Dump Windows version info", fred_api : 2, hive : "SOFTWARE" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function DecodeProductKey(arr) { //ProductKey is base24 encoded var keychars=new Array("B","C","D","F","G","H","J","K","M","P","Q","R","T","V","W","X","Y","2","3","4","6","7","8","9"); var key=new Array(30); var ret=""; var ncur; if(arr.length<66) return ret; arr=arr.mid(52,15); for(var ilbyte=24;ilbyte>=0;ilbyte--) { ncur=0; for(var ilkeybyte=14;ilkeybyte>=0;ilkeybyte--) { ncur=ncur*256^arr[ilkeybyte]; arr[ilkeybyte]=ncur/24; ncur%=24; } ret=keychars[ncur]+ret; if(ilbyte%5==0 && ilbyte!=0) ret="-"+ret; } return ret; } function fred_report_html() { -// println(""); -// println(" Windows version info"); -// println(" "); println("

Windows version info

"); // Windows version sp and build info var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","ProductName"); if(IsValid(val)) { println("

"); println(" "); print(" "); // Build string var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","BuildLab"); print_table_row("Build string:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); // Extended build string var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","BuildLabEx"); print_table_row("Extended build string:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); // Install date var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","InstallDate"); print_table_row("Install date:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"unixtime") : "n/a"); // Owner and Organization info var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","RegisteredOwner"); print_table_row("Registered owner:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","RegisteredOrganization"); print_table_row("Registered organization:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); // Windows ID / Key var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","ProductId"); print_table_row("Product ID:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","DigitalProductId"); if(IsValid(val)) { var key=DecodeProductKey(val.value); if(key!="BBBBB-BBBBB-BBBBB-BBBBB-BBBBB") print_table_row("Product Key:",key); else print_table_row("Product Key:","n/a (Probably a volume license key was used)"); } else print_table_row("Product Key:","n/a"); // Install directory / Source directory var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","PathName"); print_table_row("Install path:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","SourcePath"); print_table_row("Source path:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); println("
Windows version:",RegistryKeyValueToString(val.value,val.type)); var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","CSDVersion"); if(IsValid(val)) { print(" ",RegistryKeyValueToString(val.value,val.type)); } var val=GetRegistryKeyValue("\\Microsoft\\Windows NT\\CurrentVersion","CurrentBuildNumber"); if(IsValid(val)) { print(" build ",RegistryKeyValueToString(val.value,val.type)); } println("
"); println("

"); } else { println("

"); println(" Unable to get product name!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SYSTEM_CurrentNetworkSettings.qs b/trunk/report_templates/SYSTEM_CurrentNetworkSettings.qs index 7a6dac4..6a0a940 100644 --- a/trunk/report_templates/SYSTEM_CurrentNetworkSettings.qs +++ b/trunk/report_templates/SYSTEM_CurrentNetworkSettings.qs @@ -1,141 +1,136 @@ function fred_report_info() { var info={report_cat : "SYSTEM", report_name : "Current network settings", report_author : "Gillen Daniel", report_desc : "Dump current network settings", fred_api : 2, hive : "SYSTEM" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function ZeroPad(number,padlen) { var ret=number.toString(10); if(!padlen || ret.length>=padlen) return ret; return Math.pow(10,padlen-ret.length).toString().slice(1)+ret; } function fred_report_html() { // See Appendix A: TCP/IP Configuration Parameters: // http://technet.microsoft.com/de-de/library/cc739819%28v=WS.10%29.aspx var val; -// println(""); -// println(" Current Network Settings (Tcp/Ip)"); -// println(" "); println("

Current network settings (Tcp/Ip)

"); // Get current controlset var cur_controlset=GetRegistryKeyValue("\\Select","Current"); if(IsValid(cur_controlset)) { cur_controlset=RegistryKeyValueToString(cur_controlset.value,cur_controlset.type); // Current holds a DWORD value, thus we get a string like 0x00000000, but // control sets are referenced by its decimal representation. cur_controlset="ControlSet"+ZeroPad(parseInt(String(cur_controlset).substr(2,8),16),3) println("

"); println(" "); print_table_row("Active control set:",cur_controlset); // Computer name val=GetRegistryKeyValue(cur_controlset+"\\Control\\ComputerName\\ComputerName","ComputerName"); print_table_row("Computer name:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); println("
"); println("
"); // Iterate over all available network adapters var adapters=GetRegistryNodes(cur_controlset+"\\Services\\Tcpip\\Parameters\\Adapters"); for(var i=0;i",RegistryKeyValueToString(val.value,val.type),""); } else { println(" ",adapters[i],""); } // Get settings node var adapter_settings_node=GetRegistryKeyValue(cur_controlset+"\\Services\\Tcpip\\Parameters\\Adapters\\"+adapters[i],"IpConfig"); adapter_settings_node=RegistryKeyValueToVariant(adapter_settings_node.value,"utf16",0); println(" "); //print_table_row("Adapter id:",adapters[i]); // Get configuration mode val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"EnableDHCP"); val=Number(RegistryKeyValueToString(val.value,val.type)); if(val) { // DHCP enabled print_table_row("Configuration mode:","DHCP"); // DHCP server val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DhcpServer"); print_table_row("Last used DHCP server:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); // IP address val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DhcpIPAddress"); print_table_row("IP address:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); // Subnet mask val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DhcpSubnetMask"); print_table_row("Subnet mask:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); // Nameserver(s) val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DhcpNameServer"); print_table_row("Nameserver(s):",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); // Domain val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DhcpDomain"); print_table_row("Domain:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); // Default gw val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DhcpDefaultGateway"); print_table_row("Default gateway:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"utf16",0) : ""); // Lease obtained val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"LeaseObtainedTime"); print_table_row("Lease obtained:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"unixtime",0) : ""); // Lease valid until val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"LeaseTerminatesTime"); print_table_row("Lease terminates:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"unixtime",0) : ""); } else { print_table_row("Configuration mode:","Manual"); // IP address val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"IPAddress"); print_table_row("IP address:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"utf16",0) : ""); // Subnet mask val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"SubnetMask"); print_table_row("Subnet mask:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"utf16",0) : ""); // Nameserver val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"NameServer"); print_table_row("Nameserver:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"utf16",0) : ""); // Domain val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"Domain"); print_table_row("Domain:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); // Default gw val=GetRegistryKeyValue(cur_controlset+"\\Services\\"+adapter_settings_node,"DefaultGateway"); print_table_row("Default gateway:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"utf16",0) : ""); } // TODO: Check for EnableSecurityFilters, TCPAllowedPorts and UDPAllowedPorts to get firewall status. println("
"); println("
"); // TODO: Get persistent routes from \ControlSet001\Services\Tcpip\Parameters\PersistentRoutes } println("

"); } else { println("

"); println(" Unable to determine current control set!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SYSTEM_Services.qs b/trunk/report_templates/SYSTEM_Services.qs index 4344e66..64c2748 100644 --- a/trunk/report_templates/SYSTEM_Services.qs +++ b/trunk/report_templates/SYSTEM_Services.qs @@ -1,111 +1,106 @@ function fred_report_info() { var info={report_cat : "SYSTEM", report_name : "Services", report_author : "Gillen Daniel", report_desc : "Dump services", fred_api : 2, hive : "SYSTEM" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function ZeroPad(number,padlen) { var ret=number.toString(10); if(!padlen || ret.length>=padlen) return ret; return Math.pow(10,padlen-ret.length).toString().slice(1)+ret; } function PrintTableRow(cell01,cell02,cell03,cell04,cell05) { println(" ",cell01,"",cell02,"",cell03,"",cell04,"",cell05,""); } function ListService(service_node) { // Service name var name=GetRegistryKeyValue(service_node,"DisplayName"); name=(IsValid(name)) ? RegistryKeyValueToString(name.value,name.type) : "Unknwon"; // Service group var group=GetRegistryKeyValue(service_node,"Group"); group=(IsValid(group)) ? RegistryKeyValueToString(group.value,group.type) : ""; // Service exe var image=GetRegistryKeyValue(service_node,"ImagePath"); image=(IsValid(image)) ? RegistryKeyValueToString(image.value,image.type) : "Unknwon"; // Start var start=GetRegistryKeyValue(service_node,"Start"); start=(IsValid(start)) ? RegistryKeyValueToString(start.value,start.type) : -1; switch(Number(start)) { case 0: start="Boot"; break; case 1: start="System"; break; case 2: start="Automatic"; break; case 3: start="Manual"; break; case 4: start="Disabled"; break; default: start="Unknown"; } // Description var desc=GetRegistryKeyValue(service_node,"Description"); desc=(IsValid(desc)) ? RegistryKeyValueToString(desc.value,desc.type) : ""; PrintTableRow(name,group,start,image,desc) } function fred_report_html() { var val; -// println(""); -// println(" Services"); -// println(" "); println("

Services

"); // Get current controlset var cur_controlset=GetRegistryKeyValue("\\Select","Current"); if(IsValid(cur_controlset)) { cur_controlset=RegistryKeyValueToString(cur_controlset.value,cur_controlset.type); // Current holds a DWORD value, thus we get a string like 0x00000000, but // control sets are referenced by its decimal representation. cur_controlset="ControlSet"+ZeroPad(parseInt(String(cur_controlset).substr(2,8),16),3) // Get list of possible services var services=GetRegistryNodes(cur_controlset+"\\Services"); if(IsValid(services)) { println("

"); println(" "); println(" "); for(var i=0;i"); println("

"); } else { println("

"); println(" This registry hive does not contain any services!
"); println("

"); } } else { println("

"); println(" Unable to determine current control set!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SYSTEM_ShutdownTime.qs b/trunk/report_templates/SYSTEM_ShutdownTime.qs index 443552b..1e5ce77 100644 --- a/trunk/report_templates/SYSTEM_ShutdownTime.qs +++ b/trunk/report_templates/SYSTEM_ShutdownTime.qs @@ -1,57 +1,52 @@ function fred_report_info() { var info={report_cat : "SYSTEM", report_name : "Shutdown time", report_author : "Gillen Daniel", report_desc : "Dump last known shutdown time", fred_api : 2, hive : "SYSTEM" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println("
"); } function fred_report_html() { var val; -// println(""); -// println(" Last known shutdown time"); -// println(" "); println("

Last known shutdown time

"); // Get current controlset var cur_controlset=GetRegistryKeyValue("\\Select","Current"); if(IsValid(cur_controlset)) { cur_controlset=RegistryKeyValueToString(cur_controlset.value,cur_controlset.type); // Current holds a DWORD value, thus we get a string like 0x00000000, but // control sets are referenced only with the last 3 digits. cur_controlset="ControlSet"+String(cur_controlset).substr(7,3); println("

"); println("

NameGroupStartupImage pathDescription
",cell01,"",cell02,"
"); print_table_row("Active control set:",cur_controlset); // Shutdown time val=GetRegistryKeyValue(cur_controlset+"\\Control\\Windows","ShutdownTime"); print_table_row("Shutdown time:",(IsValid(val)) ? RegistryKeyValueToVariant(val.value,"filetime") : "Unknown"); println("
"); println("
"); println("

"); } else { println("

"); println(" Unable to determine current control set!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SYSTEM_SystemTimeInfo.qs b/trunk/report_templates/SYSTEM_SystemTimeInfo.qs index 588a14a..0f7ae70 100644 --- a/trunk/report_templates/SYSTEM_SystemTimeInfo.qs +++ b/trunk/report_templates/SYSTEM_SystemTimeInfo.qs @@ -1,122 +1,117 @@ function fred_report_info() { var info={report_cat : "SYSTEM", report_name : "System time info", report_author : "Gillen Daniel", report_desc : "Dump system time info", fred_api : 2, hive : "SYSTEM" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function ToUTC(num) { var retnum=new Number(num); if(retnum&0x80000000) { retnum=((0xFFFFFFFF-retnum)+1)/60; return "UTC+"+Number(retnum).toString(10); } else { retnum=retnum/60; if(retnum!=0) return "UTC-"+Number(retnum).toString(10); else return "UTC+"+Number(retnum).toString(10); } } function ZeroPad(number,padlen) { var ret=number.toString(10); if(!padlen || ret.length>=padlen) return ret; return Math.pow(10,padlen-ret.length).toString().slice(1)+ret; } function fred_report_html() { var val; -// println(""); -// println(" System Time Info"); -// println(" "); println("

System time info

"); // Get current controlset var cur_controlset=GetRegistryKeyValue("\\Select","Current"); if(IsValid(cur_controlset)) { cur_controlset=RegistryKeyValueToString(cur_controlset.value,cur_controlset.type); // Current holds a DWORD value, thus we get a string like 0x00000000, but // control sets are referenced by its decimal representation. cur_controlset="ControlSet"+ZeroPad(parseInt(String(cur_controlset).substr(2,8),16),3) println("

"); println(" Time zone info"); println(" "); // Active time bias val=GetRegistryKeyValue(cur_controlset+"\\Control\\TimeZoneInformation","ActiveTimeBias"); print_table_row("Active time bias:",(IsValid(val)) ? ToUTC(RegistryKeyValueToString(val.value,val.type)) : "n/a"); // Std. tz name and bias val=GetRegistryKeyValue(cur_controlset+"\\Control\\TimeZoneInformation","StandardName"); print_table_row("Std. time zone name:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); val=GetRegistryKeyValue(cur_controlset+"\\Control\\TimeZoneInformation","StandardBias"); print_table_row("Std. time bias:",(IsValid(val)) ? ToUTC(RegistryKeyValueToString(val.value,val.type)) : "n/a"); // Daylight tz name and bias val=GetRegistryKeyValue(cur_controlset+"\\Control\\TimeZoneInformation","DaylightName"); print_table_row("Daylight time zone name:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); val=GetRegistryKeyValue(cur_controlset+"\\Control\\TimeZoneInformation","DaylightBias"); print_table_row("Daylight time bias:",(IsValid(val)) ? ToUTC(RegistryKeyValueToString(val.value,val.type)) : "n/a"); println("
"); println("
"); println(" W32Time service info"); println(" "); // Get W32Time service settings val=GetRegistryKeyValue(cur_controlset+"\\Services\\W32Time","Start"); if(IsValid(val)) { print(" "); // If service is enabled, get ntp server if(Number(val)<4) { val=GetRegistryKeyValue(cur_controlset+"\\Services\\W32Time\\Parameters","NtpServer"); print_table_row("NTP server(s):",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : "n/a"); } } else print_table_row("Startup method:","n/a"); println("
Startup method:"); val=RegistryKeyValueToString(val.value,val.type); switch(Number(val)) { case 0: print("Boot"); break; case 1: print("System"); break; case 2: print("Automatic"); break; case 3: print("Manual"); break; case 4: print("Disabled"); break; default: print("Unknown"); } println("
"); println("

"); } else { println("

"); println(" Unable to determine current control set!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); } diff --git a/trunk/report_templates/SYSTEM_UsbStorageDevices.qs b/trunk/report_templates/SYSTEM_UsbStorageDevices.qs index e7cded4..20667d0 100644 --- a/trunk/report_templates/SYSTEM_UsbStorageDevices.qs +++ b/trunk/report_templates/SYSTEM_UsbStorageDevices.qs @@ -1,150 +1,145 @@ function fred_report_info() { var info={report_cat : "SYSTEM", report_name : "USB storage devices", report_author : "Gillen Daniel", report_desc : "Dump USB storage devices", fred_api : 2, hive : "SYSTEM" }; return info; } function IsValid(val) { if(typeof val !== 'undefined') return true; else return false; } function print_table_row(cell01,cell02) { println(" ",cell01,"",cell02,""); } function ZeroPad(number,padlen) { var ret=number.toString(10); if(!padlen || ret.length>=padlen) return ret; return Math.pow(10,padlen-ret.length).toString().slice(1)+ret; } function fred_report_html() { // TODO: There is more here. Check http://www.forensicswiki.org/wiki/USB_History_Viewing var val; -// println(""); -// println(" USB Storage Devices"); -// println(" "); println("

USB storage devices

"); // Preload MountedDevices to possibly identify mount points of USB storage devices var mnt_keys=GetRegistryKeys("\\MountedDevices"); var mnt_values=new Array(); if(IsValid(mnt_keys)) { for(var i=0;i"); println(" Settings
"); println(" "); // Are USB storage devices enabled? // http://www.forensicmag.com/article/windows-7-registry-forensics-part-5 // Is this true for WinXP etc.. ??? var val=GetRegistryKeyValue(cur_controlset+"\\services\\USBSTOR","Start"); if(IsValid(val)) { val=RegistryKeyValueToString(val.value,val.type); val=parseInt(String(val).substr(2,8),10); switch(val) { case 3: print_table_row("Storage driver enabled:","Yes"); break; case 4: print_table_row("Storage driver enabled:","No"); break; default: print_table_row("Storage driver enabled:","Unknown"); } } else { print_table_row("Storage driver enabled:","Unknown"); } println("
"); println("

"); println("

"); println(" Devices
"); var storage_roots=GetRegistryNodes(cur_controlset+"\\Enum\\USBSTOR"); if(IsValid(storage_roots)) { for(var i=0;i",storage_roots[i],"
"); var storage_subroots=GetRegistryNodes(cur_controlset+"\\Enum\\USBSTOR\\"+storage_roots[i]); for(ii=0;ii"); // If the second character of the unique instance ID is a '&', then the ID was // generated by the system, as the device did not have a serial number. if(String(storage_subroots[ii]).charAt(1)=="&") print_table_row("Unique ID:",storage_subroots[ii]+" (Generated by system)"); else print_table_row("Unique ID:",storage_subroots[ii]); val=GetRegistryKeyValue(cur_controlset+"\\Enum\\USBSTOR\\"+storage_roots[i]+"\\"+storage_subroots[ii],"Class"); print_table_row("Class:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); val=GetRegistryKeyValue(cur_controlset+"\\Enum\\USBSTOR\\"+storage_roots[i]+"\\"+storage_subroots[ii],"DeviceDesc"); print_table_row("Device description:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); val=GetRegistryKeyValue(cur_controlset+"\\Enum\\USBSTOR\\"+storage_roots[i]+"\\"+storage_subroots[ii],"FriendlyName"); print_table_row("Friendly name:",(IsValid(val)) ? RegistryKeyValueToString(val.value,val.type) : ""); val=GetRegistryKeyValue(cur_controlset+"\\Enum\\USBSTOR\\"+storage_roots[i]+"\\"+storage_subroots[ii],"ParentIdPrefix"); if(IsValid(val)) { // Windows XP uses the ParentId to link to MountedDevices var parent_id=RegistryKeyValueToString(val.value,val.type); print_table_row("Parent ID prefix:",parent_id); // Find mount point(s) print(" Mount point(s):"); var br=0; for(var iii=0;iii"); else br=1; print(mnt_keys[iii]); } } if(br==0) print("n/a"); println(""); } else { // Since Vista, Unique IDs are used // Find mount point(s) print(" Mount point(s):"); var br=0; for(var iii=0;iii"); else br=1; print(mnt_keys[iii]); } } if(br==0) print("n/a"); println(""); } println(" "); println("
"); } } } else { println(" This registry hive does not contain a list of attached USB storage devices!"); } println("

"); } else { println("

"); println(" Unable to determine current control set!
"); println(" Are you sure you are running this report against the correct registry hive?"); println("

"); } - -// println(""); }