diff --git a/trunk/mainwindow.cpp b/trunk/mainwindow.cpp index 76782af..aa16f48 100644 --- a/trunk/mainwindow.cpp +++ b/trunk/mainwindow.cpp @@ -1,1033 +1,1074 @@ /******************************************************************************* * 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)) { // TODO: get error message from RegistryHive QMessageBox::critical(this, tr("Error"), tr("Unable to switch write-support!")); 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); // TODO: This does not work!! 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) { // TODO: Get error message and display it QMessageBox::critical(this, tr("Error"), tr("Unable to create node '%1\\%2'!") .arg(node_path,node_name)); } 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)) { // TODO: Get error message and display it QMessageBox::critical(this, tr("Error"), tr("Unable to delete node '%1'!") .arg(node_path)); return; } // Remove node from tree model and select nearest node QModelIndex next_node_index=this->p_reg_node_tree_model->RemoveNode(index); 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) { // TODO: Get error QMessageBox::critical(this, tr("Error"), tr("Unable to add key!")); 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); 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) { // TODO: Get error QMessageBox::critical(this, tr("Error"), tr("Unable to update key!")); 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(); - 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 + 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) { - // TODO + 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)) { + // TODO: Get error message and display it + QMessageBox::critical(this, + tr("Error"), + tr("Unable to delete key '%1\\%2'!") + .arg(parent_node_path,key_name)); + 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); } 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->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()) { // TODO: Get error message QMessageBox::critical(this, tr("Saving changes"), tr("Unable to save changes!")); 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 ac30c7a..1a98323 100644 --- a/trunk/registryhive.cpp +++ b/trunk/registryhive.cpp @@ -1,1187 +1,1216 @@ /******************************************************************************* * 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 "registryhive.h" #include #include #include #include #include // 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;i0) { // Nothing to show return QString(); } // Get pointer to data at specified offset p_data=key_value.constData(); p_data+=offset; // TODO: This will fail on platforms with different endianness! #define bswap_16(value) ((((value) & 0xff) << 8) | ((value) >> 8)) #define bswap_32(value) \ (((uint32_t)bswap_16((uint16_t)((value) & 0xffff)) << 16) | \ (uint32_t)bswap_16((uint16_t)((value) >> 16))) #define bswap_64(value) \ (((uint64_t)bswap_32((uint32_t)((value) & 0xffffffff)) << 32) | \ (uint64_t)bswap_32((uint32_t)((value) >> 32))) \ // Convert full name 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=*(int16_t*)p_data; if(little_endian) ret=QString().sprintf("%d",val); else ret=QString().sprintf("%d",bswap_16(val)); } else if(format=="uint16" && remaining_data_len>=2) { uint16_t val=*(uint16_t*)p_data; if(little_endian) ret=QString().sprintf("%u",val); else ret=QString().sprintf("%u",bswap_16(val)); } else if(format=="int32" && remaining_data_len>=4) { int32_t val=*(int32_t*)p_data; if(little_endian) ret=QString().sprintf("%d",val); else ret=QString().sprintf("%d",bswap_32(val)); } else if(format=="uint32" && remaining_data_len>=4) { uint32_t val=*(uint32_t*)p_data; if(little_endian) ret=QString().sprintf("%u",val); else ret=QString().sprintf("%u",bswap_32(val)); } else if(format=="unixtime" && remaining_data_len>=4) { uint32_t val=*(uint32_t*)p_data; if(!little_endian) val=bswap_32(val); 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=*(int64_t*)p_data; if(little_endian) ret=QString("%1").arg(val); else ret=QString("%1").arg((int64_t)bswap_64(val)); } else if(format=="uint64" && remaining_data_len>=8) { uint64_t val=*(uint64_t*)p_data; if(little_endian) ret=QString("%1").arg(val); else ret=QString("%1").arg(bswap_64(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=*(uint64_t*)p_data; if(!little_endian) val=bswap_64(val); 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") { // TODO: This fails bad if the string is not null terminated!! It might be // wise checking for a null char here ret=QString().fromAscii((char*)p_data,length); } else if(format=="utf16" && remaining_data_len>=2) { ret=QString().fromUtf16((ushort*)p_data,length); } else { // Unknown variant type or another error return QString(); } return ret; } /* * KeyValueToStringList */ QStringList RegistryHive::KeyValueToStringList(QByteArray value, int value_type) { QStringList result; const char str_sep[2]={0x00,0x00}; int last_pos=0,cur_pos=0; // Only supported on REG_MULTI_SZ values!! if(value_type!=hive_t_REG_MULTI_SZ) return QStringList(); while(last_posis_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) + QString key_name, + QString key_value_type, + QByteArray key_value) { if(!this->is_hive_open || !this->is_hive_writable) { // TODO: Set error 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) { // TODO: Set error 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) { // TODO: Set error 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)) { // TODO: Set error return false; } - // Get all keys - QMap node_keys; - node_keys=this->GetKeys(parent_node); - if(node_keys.empty()) { - // TODO: Set error + // 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 key values - QList node_key_values; - QMapIterator node_keys_it(node_keys); - while(node_keys_it.hasNext()) { - node_keys_it.next(); - // TODO - // node_key_values.append(this->GetKeyValue()); + // 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)) { // TODO: Set error 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) { // TODO: Set error 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; } -/* - * GetKey - */ -bool RegistryHive::GetKey(QString &parent_node_path, - QString &key_name, - ptsRegistryKey *key_value) -{ - // Get handle to parent node - hive_node_h parent_node; - if(!this->GetNodeHandle(parent_node_path,&parent_node)) { - // TODO: Set error - return false; - } - - // Get handle to key - hive_value_h key=hivex_node_get_value(this->p_hive, - parent_node, - key_name.toAscii().constData()); - if(key==0) { - // TODO: Set error - return false; - } - - // Alloc memory for the ptsRegistryKey struct - *key_value=(ptsRegistryKey)malloc(sizeof(tsRegistryKey)); - if(*key_value==NULL) { - // TODO: Set error - return false; - } - - // Get key name from hive (We could also simply use the specified name, but - // this allows to us to get the actual upper/lowercase name from hive. - char *buf; - buf=hivex_value_key(this->p_hive,key); - if(buf==NULL) { - // TODO: Set error - return false; - } - // Store name in ptsRegistryKey struct - (*key_value)->name=QString().fromLocal8Bit(buf); - free(buf); - - // Get key value - buf=hivex_value_value(this->p_hive, - key, - (hive_type*)&((*key_value)->type), - &((*key_value)->value_len)); - if(buf==NULL) { - // TODO: Set error - return false; - } - // Store value in ptsRegistryKey struct - (*key_value)->value=QByteArray(buf,(*key_value)->value_len); - free(buf); - - return true; -} - -/* - * GetKeys - */ -bool RegistryHive::GetKeys(QString &parent_node_path, - QList *p_key_values) -{ - // Get handle to parent node - hive_node_h parent_node; - if(!this->GetNodeHandle(parent_node_path,&parent_node)) { - // TODO: Set error - return false; - } - - // TODO: Check if this is really needed - // hive_value_h *hivex_node_values (hive_h *h, hive_node_h node); - return false; -} - /* * 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)) { // TODO: Set error 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) { // TODO: Set error return 0; } } // Create and populate hive_set_value structure // TODO: From the hivex docs // Note that the value field is just treated as a list of bytes, and is stored // directly in the hive. The caller has to ensure correct encoding and // endianness, for example converting dwords to little endian. 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) { // TODO: Set error 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) { // TODO: Set error 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)) { // TODO: Set error return 0; } this->has_changes_to_commit=true; return key; } diff --git a/trunk/registryhive.h b/trunk/registryhive.h index a64fa2e..86320d2 100644 --- a/trunk/registryhive.h +++ b/trunk/registryhive.h @@ -1,139 +1,128 @@ /******************************************************************************* * 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 struct sRegistryKey { - QString name; - int type; - size_t value_len; - QByteArray value; - } tsRegistryKey, *ptsRegistryKey; - 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=0, bool little_endian=true); static QStringList KeyValueToStringList(QByteArray value, int value_type); static QStringList KeyValueToStringList(QByteArray value, QString value_type); 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); - bool GetKey(QString &parent_node_path, - QString &key_name, - ptsRegistryKey *key_value); - bool GetKeys(QString &parent_node_path, - QList *p_key_values); int SetKey(QString &parent_node_path, QString &key_name, QString &key_value_type, QByteArray &key_value, bool create_key); }; #endif // REGISTRYHIVE_H diff --git a/trunk/registrykey.cpp b/trunk/registrykey.cpp index 7554d0b..6d4f772 100644 --- a/trunk/registrykey.cpp +++ b/trunk/registrykey.cpp @@ -1,58 +1,63 @@ /******************************************************************************* * 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 "registrykey.h" RegistryKey::RegistryKey(const QList &data) { this->key_data=data; } RegistryKey::~RegistryKey() { qDeleteAll(this->keys); } void RegistryKey::Append(RegistryKey *p_key) { this->keys.append(p_key); } void RegistryKey::SetData(const QList &data) { this->key_data=data; } +void RegistryKey::Remove(uint64_t row) { + RegistryKey *p_key=this->keys.takeAt(row); + delete p_key; +} + RegistryKey* RegistryKey::Key(uint64_t row) { return this->keys.value(row); } uint64_t RegistryKey::RowCount() { return this->keys.count(); } QVariant RegistryKey::Data(int column) const { if(column>=0 && column<3) { return this->key_data.value(column); } else { return QVariant(); } } uint64_t RegistryKey::Row() const { return this->keys.indexOf(const_cast(this)); } diff --git a/trunk/registrykey.h b/trunk/registrykey.h index d32e196..24c1d1b 100644 --- a/trunk/registrykey.h +++ b/trunk/registrykey.h @@ -1,46 +1,47 @@ /******************************************************************************* * 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 REGISTRYKEY_H #define REGISTRYKEY_H #include #include #include class RegistryKey { public: RegistryKey(const QList &data); ~RegistryKey(); void Append(RegistryKey *p_key); void SetData(const QList &data); + void Remove(uint64_t row); RegistryKey *Key(uint64_t row); uint64_t RowCount(); QVariant Data(int column) const; uint64_t Row() const; private: QList keys; QList key_data; }; #endif // REGISTRYKEY_H diff --git a/trunk/registrykeytablemodel.cpp b/trunk/registrykeytablemodel.cpp index 1e56e7f..2e742e3 100644 --- a/trunk/registrykeytablemodel.cpp +++ b/trunk/registrykeytablemodel.cpp @@ -1,274 +1,286 @@ /******************************************************************************* * 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 "registrykeytablemodel.h" /******************************************************************************* * Public ******************************************************************************/ RegistryKeyTableModel::RegistryKeyTableModel(RegistryHive *p_hive, QString node_path, QObject *p_parent) : QAbstractTableModel(p_parent) { // Create the "root" key. It's values will be used as header values. this->p_keys=new RegistryKey(QList()<< tr("Key")<< tr("Type")<< tr("Value")); // Build key list this->SetupModelData(p_hive,node_path); } RegistryKeyTableModel::~RegistryKeyTableModel() { delete this->p_keys; } QVariant RegistryKeyTableModel::data(const QModelIndex &index, int role) const { bool ok; if(!index.isValid()) return QVariant(); RegistryKey *p_key=static_cast(index.internalPointer()); switch(role) { case Qt::DisplayRole: { switch(index.column()) { case RegistryKeyTableModel::ColumnContent_KeyName: { return p_key->Data(index.column()); break; } case RegistryKeyTableModel::ColumnContent_KeyType: { int value_type=p_key->Data(index.column()).toInt(&ok); if(!ok) return QVariant(); return RegistryHive::KeyValueTypeToString(value_type); break; } case RegistryKeyTableModel::ColumnContent_KeyValue: { // Get index to value type QModelIndex type_index=this->index(index.row(), RegistryKeyTableModel:: ColumnContent_KeyType); // Get value type int value_type=this->data(type_index, RegistryKeyTableModel:: AdditionalRoles_GetRawData).toInt(&ok); if(!ok) return QVariant(); // Return value converted to human readeable string QByteArray value_array=p_key->Data(index.column()).toByteArray(); return RegistryHive::KeyValueToString(value_array,value_type); break; } default: return QVariant(); } break; } case RegistryKeyTableModel::AdditionalRoles_GetRawData: { return p_key->Data(index.column()); break; } default: return QVariant(); } return QVariant(); } Qt::ItemFlags RegistryKeyTableModel::flags(const QModelIndex &index) const { if(!index.isValid()) return 0; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant RegistryKeyTableModel::headerData(int section, Qt::Orientation orientation, int role) const { // Only horizontal header is supported if(orientation!=Qt::Horizontal) return QVariant(); switch(role) { case Qt::TextAlignmentRole: // Handle text alignment if(section==2) return Qt::AlignLeft; else return Qt::AlignCenter; break; case Qt::DisplayRole: // Header text return this->p_keys->Data(section); break; default: return QVariant(); } } QModelIndex RegistryKeyTableModel::index(int row, int column, const QModelIndex &parent) const { if(!this->hasIndex(row,column,parent)) return QModelIndex(); RegistryKey *p_key=this->p_keys->Key(row); return this->createIndex(row,column,p_key); } int RegistryKeyTableModel::rowCount(const QModelIndex &parent) const { // According to Qt doc, when parent in TableModel is valid, we should return 0 if(parent.isValid()) return 0; // Get and return row count from the keys list return this->p_keys->RowCount(); } int RegistryKeyTableModel::columnCount(const QModelIndex &parent) const { // According to Qt doc, when parent in TableModel is valid, we should return 0 if(parent.isValid()) return 0; // There are always 3 columns return 3; } int RegistryKeyTableModel::GetKeyRow(QString key_name) const { int i; for(i=0;ip_keys->RowCount();i++) { if(this->p_keys->Key(i)->Data(0)==key_name) { return i; } } // When key isn't found, return the first row return 0; } QModelIndex RegistryKeyTableModel::AddKey(RegistryHive *p_hive, int new_key_id) { RegistryKey *p_key; QString key_name; QByteArray key_value; int key_value_type; size_t key_value_len; // Tell users of this model that we are going to add a row emit(RegistryKeyTableModel::beginInsertRows(QModelIndex(), this->p_keys->RowCount(), this->p_keys->RowCount())); // Get key name if(!p_hive->GetKeyName(new_key_id,key_name)) { return QModelIndex(); } // Get key value, value type and value length key_value=p_hive->GetKeyValue(new_key_id,&key_value_type,&key_value_len); if(p_hive->GetErrorMsg()!="") { return QModelIndex(); } // Create new RegistryKey object and add it to our internal list p_key=new RegistryKey(QList()<< QString(key_name.length() ? key_name : "(default)")<< QVariant(key_value_type)<< key_value); this->p_keys->Append(p_key); // Tell users of this model we have finished adding a row emit(RegistryKeyTableModel::endInsertRows()); // Return an index to the new row return this->index(this->p_keys->RowCount()-1,0); } QModelIndex RegistryKeyTableModel::UpdateKey(RegistryHive *p_hive, int new_key_id) { QString key_name; QByteArray key_value; int key_value_type; size_t key_value_len; int key_row=-1; // Get key name if(!p_hive->GetKeyName(new_key_id,key_name)) { return QModelIndex(); } // Get key value, value type and value length key_value=p_hive->GetKeyValue(new_key_id,&key_value_type,&key_value_len); if(p_hive->GetErrorMsg()!="") { return QModelIndex(); } // Find row containig the key to update for(int i=0;ip_keys->RowCount();i++) { if(this->p_keys->Key(i)->Data(0).toString().toLower()==key_name.toLower()) { key_row=i; } } if(key_row==-1) return QModelIndex(); - // TODO: Update values + // Update values this->p_keys->Key(key_row)->SetData(QList()<< QString(key_name.length() ? key_name : "(default)")<< QVariant(key_value_type)<< key_value); // Tell users of this model that data has changed emit(RegistryKeyTableModel::dataChanged(this->index(key_row,0), this->index(key_row,0))); return this->index(key_row,0); } QModelIndex RegistryKeyTableModel::RemoveKey(const QModelIndex &index) { - // TODO + // Tell users of this model that a row is going to be removed + emit(RegistryKeyTableModel::beginRemoveRows(QModelIndex(), + index.row(), + index.row())); + // Remove row + this->p_keys->Remove(index.row()); + // Tell users of this model that a row has been removed + emit(RegistryKeyTableModel::endRemoveRows()); + // Return a valid index to be selected + if(this->rowCount()==0) return QModelIndex(); + else if(index.row()==0) return this->index(0,0); + else if(index.row()rowCount()) return this->index(index.row(),0); + else return this->index(index.row()-1,0); } /******************************************************************************* * Private ******************************************************************************/ void RegistryKeyTableModel::SetupModelData(RegistryHive *p_hive, QString &node_path) { QMap node_keys; RegistryKey *p_key; QByteArray key_value; int key_value_type; size_t key_value_len; // Get all keys for current node node_keys=p_hive->GetKeys(node_path); if(node_keys.isEmpty()) return; // Add all keys to list QMapIterator i(node_keys); while(i.hasNext()) { i.next(); key_value=p_hive->GetKeyValue(i.value(), &key_value_type, &key_value_len); if(p_hive->GetErrorMsg()!="") continue; p_key=new RegistryKey(QList()<< QString(i.key().length() ? i.key() : tr("(default)"))<< QVariant(key_value_type)<< key_value); this->p_keys->Append(p_key); } }