Page MenuHomePhabricator

No OneTemporary

Size
95 KB
Referenced Files
None
Subscribers
None
diff --git a/trunk/mainwindow.cpp b/trunk/mainwindow.cpp
index 4b16561..c71d74a 100644
--- a/trunk/mainwindow.cpp
+++ b/trunk/mainwindow.cpp
@@ -1,706 +1,764 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#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 <QFileDialog>
#include <QMessageBox>
#include <QStringList>
#include <QDesktopWidget>
#include <QDir>
#include <QSplitter>
#include <QListIterator>
+#include <QInputDialog>
#include <QDebug>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dlgabout.h"
#include "dlgkeydetails.h"
#include "dlgreportchooser.h"
#include "dlgreportviewer.h"
#include "dlgsearch.h"
#include "dlgpreferences.h"
#include "compileinfo.h"
/*******************************************************************************
* Public
******************************************************************************/
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();
// 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)));
// 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"));
}
}
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
******************************************************************************/
void MainWindow::closeEvent(QCloseEvent *p_event) {
Q_UNUSED(p_event)
// Save window position and size on exit
this->p_settings->SetWindowGeometry("MainWindow",this->saveGeometry());
QMainWindow::closeEvent(p_event);
}
/*******************************************************************************
* Private slots
******************************************************************************/
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);
}
void MainWindow::on_action_Close_hive_triggered() {
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();
}
}
void MainWindow::on_action_Quit_triggered() {
qApp->exit();
}
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());
}
}
void MainWindow::on_ActionEnableWriteSupport_triggered() {
+ if(this->is_hive_writable && this->p_hive->HasChangesToCommit()) {
+ // There are unsaved changes, ask user if we should commit them
+ if(QMessageBox::warning(this,
+ tr("Save changes"),
+ tr("You have unsaved changes! Do you want to save them before switching to read-only mode?"),
+ QMessageBox::Yes,
+ QMessageBox::No)==QMessageBox::Yes)
+ {
+ if(!this->p_hive->CommitChanges()) {
+ QMessageBox::critical(this,
+ tr("Saving changes"),
+ tr("Unable to save changes!"));
+ }
+ }
+ }
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());
}
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();
}
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<ReportTemplate*> 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: "<<report_result;
}
}
}
void MainWindow::on_ActionReloadReportTemplates_triggered() {
this->p_reports->LoadReportTemplates();
}
+void MainWindow::on_actionAbout_Qt_triggered() {
+ QMessageBox::aboutQt(this,tr("About Qt"));
+}
+
+void MainWindow::on_actionAbout_fred_triggered() {
+ DlgAbout dlg_about(this);
+ dlg_about.exec();
+}
+
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();
}
-void MainWindow::on_actionAbout_Qt_triggered() {
- QMessageBox::aboutQt(this,tr("About Qt"));
-}
-
-void MainWindow::on_actionAbout_fred_triggered() {
- DlgAbout dlg_about(this);
- dlg_about.exec();
-}
-
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();
}
void MainWindow::SlotKeyTableDoubleClicked(QModelIndex index) {
Q_UNUSED(index);
/*
QModelIndex key_index;
QModelIndex node_index;
QStringList nodes;
QString key_name;
QString key_type;
QByteArray key_value;
if(!index.isValid()) return;
// Get key name, type and value
key_index=this->p_reg_key_table_model->index(index.row(),0);
key_name=this->p_reg_key_table_model->data(key_index,Qt::DisplayRole)
.toString();
key_index=this->p_reg_key_table_model->index(index.row(),1);
key_type=this->p_reg_key_table_model->data(key_index,Qt::DisplayRole)
.toString();ThreadSearch
key_index=this->p_reg_key_table_model->index(index.row(),2);
key_value=this->p_reg_key_table_model->data(key_index,
RegistryKeyTableModel::
AdditionalRoles_GetRawData)
.toByteArray();
// Get current node
node_index=this->p_node_tree->currentIndex();
//Built node path
nodes.clear();
nodes.append(this->p_reg_node_tree_model->
data(node_index,Qt::DisplayRole).toString());
while(this->p_reg_node_tree_model->parent(node_index)!=QModelIndex()) {
// Prepend all parent nodes
node_index=this->p_reg_node_tree_model->parent(node_index);
nodes.prepend(this->p_reg_node_tree_model->
data(node_index,Qt::DisplayRole).toString());
}
DlgKeyDetails dlg_key_details(this);
dlg_key_details.SetValues(nodes,key_name,key_type,key_value);
dlg_key_details.exec();
*/
}
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);
}
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 \<key name> from path
path.chop(key.length()+1);
}
// Expand treeview to correct node
QList<QModelIndex> indexes=
this->p_reg_node_tree_model->GetIndexListOf(path);
for(i=0;i<indexes.count();i++) {
indexes.replace(i,this->p_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::Select);
// 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));
}
}
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);
}
void MainWindow::SlotRecentlyOpenedFileClicked(bool checked) {
Q_UNUSED(checked)
QAction *p_sender=(QAction*)QObject::sender();
this->OpenHive(p_sender->text());
}
+void MainWindow::SlotAddNode(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);
+
+ // 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(!this->p_hive->AddNode(node_path,node_name)) {
+ QMessageBox::critical(this,
+ tr("Error"),
+ tr("Unable to create node '%1\\%2'!")
+ .arg(node_path,node_name));
+ } else {
+ this->p_reg_node_tree_model->ReloadModelData();
+ // TODO: Reload / add new node in node tree
+ }
+ }
+}
+
+void MainWindow::SlotDeleteNode(QModelIndex index) {
+ // TODO
+}
+
/*******************************************************************************
* Private
******************************************************************************/
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)) {
+ 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();
}
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"))));
}
}
}
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);
}
}
void MainWindow::ClearRecentlyOpenedMenu() {
QAction *p_action;
// Remove existing menu entries
QList<QAction*> menu_entries=this->p_recently_opened_menu->actions();
QListIterator<QAction*> it_me(menu_entries);
while(it_me.hasNext()) {
p_action=it_me.next();
this->p_recently_opened_menu->removeAction(p_action);
delete p_action;
}
}
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<QString> 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);
}
}
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);
}
}
diff --git a/trunk/mainwindow.h b/trunk/mainwindow.h
index 18845be..dd4d832 100644
--- a/trunk/mainwindow.h
+++ b/trunk/mainwindow.h
@@ -1,121 +1,123 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>
#include <QLabel>
#include <QTabWidget>
#include <QVBoxLayout>
#include <QSplitter>
#include <QString>
#include <QByteArray>
#include <hivex.h>
#include "argparser.h"
#include "registryhive.h"
#include "registrynodetree.h"
#include "registrynodetreemodel.h"
#include "registrynodetreemodelproxy.h"
#include "registrykeytable.h"
#include "registrykeytablemodel.h"
#include "hexeditwidget.h"
#include "reports.h"
#include "threadsearch.h"
#include "searchresultwidget.h"
#include "tabwidget.h"
#include "settings.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(ArgParser *p_arg_parser);
~MainWindow();
protected:
void closeEvent(QCloseEvent *p_event);
private slots:
void on_action_Open_hive_triggered();
void on_action_Close_hive_triggered();
void on_action_Quit_triggered();
void on_ActionSearch_triggered();
void on_ActionEnableWriteSupport_triggered();
void on_ActionPreferences_triggered();
void on_ActionGenerateReport_triggered();
void on_ActionReloadReportTemplates_triggered();
void on_actionAbout_Qt_triggered();
void on_actionAbout_fred_triggered();
void SlotNodeTreeClicked(QModelIndex index);
void SlotKeyTableClicked(QModelIndex index);
void SlotKeyTableDoubleClicked(QModelIndex index);
void SlotSearchFinished();
void SlotSearchResultWidgetDoubleClicked(QModelIndex index);
void SlotTabCloseButtonClicked(int index);
void SlotRecentlyOpenedFileClicked(bool checked);
+ void SlotAddNode(QModelIndex index);
+ void SlotDeleteNode(QModelIndex index);
private:
Ui::MainWindow *ui;
ArgParser *p_args;
RegistryHive *p_hive;
bool is_hive_open;
bool is_hive_writable;
QByteArray selected_key_value;
QList<SearchResultWidget*> search_result_widgets;
Settings *p_settings;
// Models
RegistryNodeTreeModel *p_reg_node_tree_model;
RegistryNodeTreeModelProxy *p_reg_node_tree_model_proxy;
RegistryKeyTableModel *p_reg_key_table_model;
// Widgets etc...
RegistryNodeTree *p_node_tree;
RegistryKeyTable *p_key_table;
TabWidget *p_tab_widget;
HexEditWidget *p_hex_edit_widget;
QSplitter *p_horizontal_splitter;
QSplitter *p_vertical_splitter;
Reports *p_reports;
QMenu *p_recently_opened_menu;
// Threads
ThreadSearch *p_search_thread;
// Functions
void OpenHive(QString hive_file);
void UpdateWindowTitle(QString filename="");
void UpdateMenuStates();
void ClearRecentlyOpenedMenu();
void UpdateRecentlyOpenedMenu();
void UpdateEnableWriteSupportMenu();
};
#endif // MAINWINDOW_H
diff --git a/trunk/registryhive.cpp b/trunk/registryhive.cpp
index fe21954..6950b7d 100644
--- a/trunk/registryhive.cpp
+++ b/trunk/registryhive.cpp
@@ -1,738 +1,849 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#include "registryhive.h"
#include <QStringList>
#include <QDateTime>
#include <QDebug>
#include <stdlib.h>
#include <stdio.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(bool commit_changes) {
+bool RegistryHive::Close() {
if(this->is_hive_open) {
- if(commit_changes) {
- // Commit changes before closing hive.
- // TODO: Maybe it would be more secure to commit changes to a new file and
- // then move it over the original one.
- hivex_commit(this->p_hive,NULL,0);
- }
-
// 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<QString,int> RegistryHive::GetNodes(QString path) {
hive_node_h parent_node;
// Get handle to last node in path
if(!this->GetNodeHandle(path,&parent_node)) return QMap<QString,int>();
// Get and return nodes
return this->GetNodesHelper(parent_node);
}
/*
* GetNodes
*/
QMap<QString,int> RegistryHive::GetNodes(int parent_node) {
if(parent_node==0) {
this->SetError(tr("Invalid parent node handle specified!"));
return QMap<QString,int>();
}
// Get and return nodes
return this->GetNodesHelper(parent_node);
}
/*
* GetKeys
*/
QMap<QString,int> RegistryHive::GetKeys(QString path) {
hive_node_h parent_node;
// Get handle to last node in path
if(!this->GetNodeHandle(path,&parent_node)) return QMap<QString,int>();
// Get and return keys
return this->GetKeysHelper(parent_node);
}
/*
* GetKeys
*/
QMap<QString,int> RegistryHive::GetKeys(int parent_node) {
if(parent_node==0) {
this->SetError(tr("Invalid parent node handle specified!"));
return QMap<QString,int>();
}
// Get and return keys
return this->GetKeysHelper(parent_node);
}
/*
* 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<value.size();i++) { \
ret.append(QString().sprintf("%02X ",(uint8_t)(value.constData()[i]))); \
} \
ret.chop(1); \
}
switch(value_type) {
case hive_t_REG_NONE:
// Just a key without a value, but to be certain...
ToHexStr();
break;
case hive_t_REG_SZ:
// A Windows string (encoding is unknown, but often UTF16-LE)
// TODO: What happens if encoding is not UTF16-LE ??? Thx Billy!!!
ret=value.size() ? QString().fromUtf16((ushort*)(value.constData())) : "";
break;
case hive_t_REG_EXPAND_SZ:
// A Windows string that contains %env% (environment variable expansion)
ret=value.size() ? QString().fromUtf16((ushort*)(value.constData())) : "";
break;
case hive_t_REG_BINARY:
// A blob of binary
ToHexStr();
break;
case hive_t_REG_DWORD:
// DWORD (32 bit integer), little endian
ret=QString().sprintf("0x%08X",*(uint32_t*)value.constData());
break;
case hive_t_REG_DWORD_BIG_ENDIAN:
// DWORD (32 bit integer), big endian
ret=QString().sprintf("0x%08X",*(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.
// See http://blogs.msdn.com/oldnewthing/archive/2009/10/08/9904646.aspx
ToHexStr();
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.
ret=
QString("0x%1").arg((quint64)(*(uint64_t*)value.constData()),16,16,QChar('0'));
break;
default:
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;
// 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_pos<value.count() &&
(cur_pos=value.indexOf(QByteArray().fromRawData(str_sep,2),last_pos))!=-1)
{
if(cur_pos!=last_pos) {
// TODO: What happens if encoding is not UTF16-LE ??? Thx Billy!!!
result.append(QString().fromUtf16((ushort*)value
.mid(last_pos,cur_pos-last_pos)
.append(QByteArray().fromRawData(str_sep,2))
.constData()));
}
last_pos=cur_pos+3;
}
return result;
}
/*
* KeyTypeToString
*/
QString RegistryHive::KeyTypeToString(int value_type) {
QString ret="";
switch(value_type) {
case hive_t_REG_NONE:
ret="REG_NONE";
break;
case hive_t_REG_SZ:
ret="REG_SZ";
break;
case hive_t_REG_EXPAND_SZ:
ret="REG_EXPAND_SZ";
break;
case hive_t_REG_BINARY:
ret="REG_BINARY";
break;
case hive_t_REG_DWORD:
ret="REG_DWORD";
break;
case hive_t_REG_DWORD_BIG_ENDIAN:
ret="REG_DWORD_BIG_ENDIAN";
break;
case hive_t_REG_LINK:
ret="REG_LINK";
break;
case hive_t_REG_MULTI_SZ:
ret="REG_MULTI_SZ";
break;
case hive_t_REG_RESOURCE_LIST:
ret="REG_RESOURCE_LIST";
break;
case hive_t_REG_FULL_RESOURCE_DESCRIPTOR:
ret="REG_FULL_RESOURCE_DESC";
break;
case hive_t_REG_RESOURCE_REQUIREMENTS_LIST:
ret="REG_RESOURCE_REQ_LIST";
break;
case hive_t_REG_QWORD:
ret="REG_QWORD";
break;
default:
ret=QString().sprintf("0x%08X",(uint32_t)value_type);
}
return ret;
}
/*
* FiletimeToUnixtime
*/
uint64_t RegistryHive::FiletimeToUnixtime(int64_t filetime) {
return (unsigned)((filetime-EPOCH_DIFF)/10000000);
}
+/*
+ * AddNode
+ */
+bool RegistryHive::AddNode(QString parent_node_path, QString node_name) {
+ if(!this->is_hive_writable) return false;
+
+ // 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)) {
+ // TODO: Set error
+ return false;
+ }
+
+ // Make sure there is no other node with same name
+ QMap<QString,int> child_nodes=this->GetNodes(parent_node);
+ if(child_nodes.contains(node_name.toAscii())) {
+ // TODO: Set error
+ return false;
+ }
+
+ // 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) {
+ // TODO: Set error
+ return false;
+ }
+
+ this->has_changes_to_commit=true;
+ return true;
+}
+
+/*
+ * 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)) {
+ // TODO: Set error
+ return false;
+ }
+
+ // Delete node
+ if(hivex_node_delete_child(this->p_hive,node)==-1) {
+ // TODO: Set error
+ return false;
+ }
+
+ this->has_changes_to_commit=true;
+ return true;
+}
+
/*******************************************************************************
* Private
******************************************************************************/
/*
* 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;i<nodes.count();i++) {
// printf("Spotting node '%s'\n",nodes.value(i).toAscii().constData());
*p_node=hivex_node_get_child(this->p_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;
}
/*
* GetNodesHelper
*/
QMap<QString,int> RegistryHive::GetNodesHelper(hive_node_h parent_node) {
QMap<QString,int> 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<QString,int>();
}
// 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<QString,int>();
}
keys.insert(QString(p_name),(int)child_nodes[i]);
free(p_name);
i++;
}
free(child_nodes);
return keys;
}
/*
* GetKeysHelper
*/
QMap<QString,int> RegistryHive::GetKeysHelper(hive_node_h parent_node) {
QMap<QString,int> 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<QString,int>();
}
// 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<QString,int>();
}
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;
}
diff --git a/trunk/registryhive.h b/trunk/registryhive.h
index 1924384..d0ab759 100644
--- a/trunk/registryhive.h
+++ b/trunk/registryhive.h
@@ -1,96 +1,104 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#ifndef REGISTRYHIVE_H
#define REGISTRYHIVE_H
#include <QObject>
#include <QMap>
#include <hivex.h>
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 Close(bool commit_changes=false);
+ bool Reopen(bool read_only=true);
+ bool CommitChanges();
+ bool Close();
QString Filename();
teHiveType HiveType();
QString HiveTypeToString(teHiveType hive_type);
+ bool HasChangesToCommit();
QMap<QString,int> GetNodes(QString path="\\");
QMap<QString,int> GetNodes(int parent_node=0);
QMap<QString,int> GetKeys(QString path="\\");
QMap<QString,int> GetKeys(int parent_node=0);
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 QString KeyTypeToString(int value_type);
static uint64_t FiletimeToUnixtime(int64_t filetime);
+ bool AddNode(QString parent_node_path, QString node_name);
+ bool DeleteNode(QString node_path);
+
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;
void SetError(QString msg);
bool GetNodeHandle(QString &path, hive_node_h *p_node);
QMap<QString,int> GetNodesHelper(hive_node_h parent_node);
QMap<QString,int> 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);
};
#endif // REGISTRYHIVE_H
diff --git a/trunk/registrykeytable.cpp b/trunk/registrykeytable.cpp
index 6583f68..124cce7 100644
--- a/trunk/registrykeytable.cpp
+++ b/trunk/registrykeytable.cpp
@@ -1,167 +1,204 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#include "registrykeytable.h"
#include <QHeaderView>
#include <QApplication>
#include <QClipboard>
+/*******************************************************************************
+ * 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
+ // Create context menu item
this->p_action_add_key=new QAction(tr("Add key"),this);
this->p_action_edit_key=new QAction(tr("Edit key"),this);
this->p_action_delete_key=new QAction(tr("Delete key"),this);
this->p_menu_copy=new QMenu(tr("Copy"),this);
- this->p_action_copy_key_name=new QAction(tr("Key name"),
- this->p_menu_copy);
+ this->p_action_copy_key_name=new QAction(tr("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("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->p_action_copy_key_value=new QAction(tr("Key value"),
- this->p_menu_copy);
- this->p_menu_copy->addAction(this->p_action_copy_key_value);
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;i<this->model()->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 when a row is selected
if(this->selectedIndexes().count()!=3) return;
// Only show context menu when user clicked on selected row
if(!(this->indexAt(p_event->pos())==this->selectedIndexes().at(0) ||
this->indexAt(p_event->pos())==this->selectedIndexes().at(1) ||
this->indexAt(p_event->pos())==this->selectedIndexes().at(2)))
{
return;
}
// Emit a click signal
emit(this->clicked(this->indexAt(p_event->pos())));
// Create context menu and add actions
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 &current,
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() {
+ // TODO: We need to emit the ModelIndex of the parent node!!
+ //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/registrykeytable.h b/trunk/registrykeytable.h
index f2833b7..f576563 100644
--- a/trunk/registrykeytable.h
+++ b/trunk/registrykeytable.h
@@ -1,63 +1,69 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#ifndef REGISTRYKEYTABLE_H
#define REGISTRYKEYTABLE_H
#include <QTableView>
#include <QContextMenuEvent>
#include <QMenu>
#include <QAction>
class RegistryKeyTable : public QTableView {
Q_OBJECT
public:
RegistryKeyTable(QWidget *p_parent=0);
~RegistryKeyTable();
void setModel(QAbstractItemModel *p_model, bool writable=false);
void SetWritable(bool writable);
//void selectRow(QString key_name);
Q_SIGNALS:
void CurrentItemChanged(QModelIndex current);
+ void SignalAddKey(QModelIndex root_node);
+ void SignalEditKey(QModelIndex node);
+ void SignalDeleteKey(QModelIndex node);
protected:
int sizeHintForColumn(int column) const;
void contextMenuEvent(QContextMenuEvent *p_event);
+ void currentChanged(const QModelIndex &current,
+ const QModelIndex &previous);
private:
bool is_writable;
QAction *p_action_add_key;
QAction *p_action_edit_key;
QAction *p_action_delete_key;
QMenu *p_menu_copy;
QAction *p_action_copy_key_name;
QAction *p_action_copy_key_value;
- void currentChanged(const QModelIndex &current,
- const QModelIndex &previous);
private slots:
+ void SlotAddKey();
+ void SlotEditKey();
+ void SlotDeleteKey();
void SlotCopyKeyName();
void SlotCopyKeyValue();
};
#endif // REGISTRYKEYTABLE_H
diff --git a/trunk/registrynodetree.cpp b/trunk/registrynodetree.cpp
index afe81a8..f7cf24d 100644
--- a/trunk/registrynodetree.cpp
+++ b/trunk/registrynodetree.cpp
@@ -1,157 +1,211 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#include "registrynodetree.h"
#include "registrynodetreemodel.h"
#include <QHeaderView>
#include <QApplication>
#include <QClipboard>
+/*******************************************************************************
+ * Public
+ ******************************************************************************/
+
RegistryNodeTree::RegistryNodeTree(QWidget *p_parent) : QTreeView(p_parent) {
this->is_writable=false;
// Configure widget
this->setTextElideMode(Qt::ElideNone);
this->setSelectionMode(QAbstractItemView::SingleSelection);
this->setSelectionBehavior(QAbstractItemView::SelectRows);
this->sortByColumn(0,Qt::AscendingOrder);
this->setSortingEnabled(true);
- // Create context menu
+ // Create context menu items
this->p_action_add_node=new QAction(tr("Add node"),this);
this->p_action_rename_node=new QAction(tr("Rename node"),this);
this->p_action_delete_node=new QAction(tr("Delete node"),this);
this->p_menu_copy=new QMenu(tr("Copy"),this);
- this->p_action_copy_node_name=new QAction(tr("Node name"),
- this->p_menu_copy);
+ this->p_action_copy_node_name=new QAction(tr("Node name"),this->p_menu_copy);
this->p_menu_copy->addAction(this->p_action_copy_node_name);
+ this->p_action_copy_node_path=new QAction(tr("Node path"),this->p_menu_copy);
+ this->p_menu_copy->addAction(this->p_action_copy_node_path);
+
+ // Connect context menu signals
+ this->connect(this->p_action_add_node,
+ SIGNAL(triggered()),
+ this,
+ SLOT(SlotAddNode()));
+ this->connect(this->p_action_rename_node,
+ SIGNAL(triggered()),
+ this,
+ SLOT(SlotRenameNode()));
+ this->connect(this->p_action_delete_node,
+ SIGNAL(triggered()),
+ this,
+ SLOT(SlotDeleteNode()));
this->connect(this->p_action_copy_node_name,
SIGNAL(triggered()),
this,
SLOT(SlotCopyNodeName()));
- this->p_action_copy_node_path=new QAction(tr("Node path"),
- this->p_menu_copy);
- this->p_menu_copy->addAction(this->p_action_copy_node_path);
this->connect(this->p_action_copy_node_path,
SIGNAL(triggered()),
this,
SLOT(SlotCopyNodePath()));
}
RegistryNodeTree::~RegistryNodeTree() {
// Delete context menu
delete this->p_action_copy_node_name;
delete this->p_action_copy_node_path;
delete this->p_menu_copy;
delete this->p_action_delete_node;
delete this->p_action_rename_node;
delete this->p_action_add_node;
}
void RegistryNodeTree::setModel(QAbstractItemModel *p_model, bool writable) {
// Assign model to view
QTreeView::setModel(p_model);
this->header()->setResizeMode(0,QHeaderView::ResizeToContents);
this->header()->setStretchLastSection(true);
if(p_model!=NULL && p_model->index(0,0).isValid()) {
// Select first tree item
this->setCurrentIndex(p_model->index(0,0));
}
// Set writable status
this->SetWritable(writable);
}
void RegistryNodeTree::SetWritable(bool writable) {
this->is_writable=writable;
this->p_action_add_node->setEnabled(this->is_writable);
this->p_action_rename_node->setEnabled(this->is_writable);
this->p_action_delete_node->setEnabled(this->is_writable);
}
+/*******************************************************************************
+ * Protected
+ ******************************************************************************/
+
void RegistryNodeTree::contextMenuEvent(QContextMenuEvent *p_event) {
// Only show context menu when a node is selected
if(this->selectedIndexes().count()!=2) return;
// Only show context menu when user clicked on selected row
// TODO: Does not work when clicking on column 2
if(this->indexAt(p_event->pos())!=this->selectedIndexes().at(0)) return;
// Emit a click signal
emit(this->clicked(this->indexAt(p_event->pos())));
// Create context menu and add actions
QMenu context_menu(this);
context_menu.addAction(this->p_action_add_node);
context_menu.addAction(this->p_action_rename_node);
context_menu.addAction(this->p_action_delete_node);
context_menu.addSeparator();
context_menu.addMenu(this->p_menu_copy);
context_menu.exec(p_event->globalPos());
}
void RegistryNodeTree::keyPressEvent(QKeyEvent *p_event) {
// Only react if a node is selected and user pressed Key_Left
if(this->selectedIndexes().count()==2 &&
p_event->key()==Qt::Key_Left)
{
QModelIndex cur_index=this->selectedIndexes().at(0);
if(this->model()->hasChildren(cur_index) && this->isExpanded(cur_index)) {
// Current node is expanded. Only collapse this one
this->collapse(cur_index);
return;
}
if(!cur_index.parent().isValid()) {
// Do no try to collapse anything above root node
return;
}
this->collapse(cur_index.parent());
this->setCurrentIndex(cur_index.parent());
return;
}
// If we didn't handle the key event, let our parent handle it
QTreeView::keyPressEvent(p_event);
}
void RegistryNodeTree::currentChanged(const QModelIndex &current,
const QModelIndex &previous)
{
// Call parent class's currentChanged first
QTreeView::currentChanged(current,previous);
// Now emit our signal
QModelIndex current_item=QModelIndex(current);
emit(RegistryNodeTree::CurrentItemChanged(current_item));
}
+/*******************************************************************************
+ * Private slots
+ ******************************************************************************/
+
void RegistryNodeTree::SlotCopyNodeName() {
QApplication::clipboard()->
setText(this->selectedIndexes().at(0).data().toString(),
QClipboard::Clipboard);
}
void RegistryNodeTree::SlotCopyNodePath() {
QString path=((RegistryNodeTreeModel*)(this->model()))->
GetNodePath(this->selectedIndexes().at(0));
QApplication::clipboard()->setText(path,QClipboard::Clipboard);
}
+
+void RegistryNodeTree::SlotAddNode() {
+ emit(RegistryNodeTree::SignalAddNode(this->selectedIndexes().at(0)));
+}
+
+void RegistryNodeTree::SlotRenameNode() {
+ emit(RegistryNodeTree::SignalRenameNode(this->selectedIndexes().at(0)));
+/*
+ // Get current node name and path
+ QString node_name=this->selectedIndexes().at(0).data().toString();
+ QString node_path=((RegistryNodeTreeModel*)(this->model()))->
+ GetNodePath(this->selectedIndexes().at(0));
+
+ // Query new name
+ bool ok=false;
+ QString new_node_name=QInputDialog::getText(this,
+ tr("Rename node"),
+ tr("Please specify a new name for the node"),
+ QLineEdit::Normal,
+ node_name,
+ &ok);
+ if(ok) {
+
+ }
+*/
+}
+
+void RegistryNodeTree::SlotDeleteNode() {
+ emit(RegistryNodeTree::SignalDeleteNode(this->selectedIndexes().at(0)));
+}
diff --git a/trunk/registrynodetree.h b/trunk/registrynodetree.h
index edf26e1..a8a92c6 100644
--- a/trunk/registrynodetree.h
+++ b/trunk/registrynodetree.h
@@ -1,64 +1,70 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#ifndef REGISTRYNODETREE_H
#define REGISTRYNODETREE_H
#include <QTreeView>
#include <QAbstractItemModel>
#include <QContextMenuEvent>
#include <QMenu>
#include <QAction>
class RegistryNodeTree : public QTreeView {
Q_OBJECT
public:
RegistryNodeTree(QWidget *p_parent=0);
~RegistryNodeTree();
void setModel(QAbstractItemModel *p_model, bool writable=false);
void SetWritable(bool writable);
Q_SIGNALS:
void CurrentItemChanged(QModelIndex current);
+ void SignalAddNode(QModelIndex root_node);
+ void SignalRenameNode(QModelIndex node);
+ void SignalDeleteNode(QModelIndex node);
protected:
// int sizeHintForColumn(int column) const;
void contextMenuEvent(QContextMenuEvent *p_event);
void keyPressEvent(QKeyEvent *p_event);
+ void currentChanged(const QModelIndex &current,
+ const QModelIndex &previous);
private:
bool is_writable;
QAction *p_action_add_node;
QAction *p_action_rename_node;
QAction *p_action_delete_node;
QMenu *p_menu_copy;
QAction *p_action_copy_node_name;
QAction *p_action_copy_node_path;
- void currentChanged(const QModelIndex &current,
- const QModelIndex &previous);
private slots:
+ void SlotAddNode();
+ void SlotRenameNode();
+ void SlotDeleteNode();
void SlotCopyNodeName();
void SlotCopyNodePath();
};
#endif // REGISTRYNODETREE_H
diff --git a/trunk/registrynodetreemodel.cpp b/trunk/registrynodetreemodel.cpp
index 6f429d1..954774c 100644
--- a/trunk/registrynodetreemodel.cpp
+++ b/trunk/registrynodetreemodel.cpp
@@ -1,229 +1,253 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#include "registrynodetreemodel.h"
#include <QList>
#include <QStringList>
#include <QVariant>
#include <QDateTime>
#include <inttypes.h>
-RegistryNodeTreeModel::RegistryNodeTreeModel(RegistryHive *p_hive,
+/*******************************************************************************
+ * Public
+ ******************************************************************************/
+
+RegistryNodeTreeModel::RegistryNodeTreeModel(RegistryHive *p_reghive,
QObject *p_parent)
: QAbstractItemModel(p_parent)
{
- // Create root node. It's values will be used as header values.
- this->p_root_node=new RegistryNode(QList<QVariant>()<<tr("Node")
- <<tr("Last mod. time"));
+ // Init private vars
+ this->p_root_node=NULL;
+ this->p_hive=p_reghive;
+
// Build node list
- this->SetupModelData(p_hive,this->p_root_node);
+ this->ReloadModelData();
}
RegistryNodeTreeModel::~RegistryNodeTreeModel() {
delete this->p_root_node;
}
QVariant RegistryNodeTreeModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid()) return QVariant();
if(role!=Qt::DisplayRole) return QVariant();
RegistryNode *p_node=static_cast<RegistryNode*>(index.internalPointer());
switch(role) {
case Qt::DisplayRole: {
switch(index.column()) {
case RegistryNodeTreeModel::ColumnContent_NodeName: {
return p_node->Data(index.column());
break;
}
case RegistryNodeTreeModel::ColumnContent_NodeModTime: {
QDateTime date_time;
bool ok=false;
date_time.setTimeSpec(Qt::UTC);
date_time.setTime_t(RegistryHive::FiletimeToUnixtime(
p_node->Data(index.column()).toLongLong(&ok)));
if(ok) return date_time.toString("yyyy/MM/dd hh:mm:ss");
else return tr("Unknown");
break;
}
default: {
return QVariant();
}
}
break;
}
default: {
return QVariant();
}
}
// Control will never reach this, but in order to stop g++ complaining...
return QVariant();
}
Qt::ItemFlags RegistryNodeTreeModel::flags(const QModelIndex &index) const {
if(!index.isValid()) return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant RegistryNodeTreeModel::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
return Qt::AlignCenter;
break;
case Qt::DisplayRole:
// Header text
return this->p_root_node->Data(section);
break;
default:
return QVariant();
}
}
QModelIndex RegistryNodeTreeModel::index(int row,
int column,
const QModelIndex &parent) const
{
if(!this->hasIndex(row,column,parent)) return QModelIndex();
RegistryNode *p_parent_node;
if(!parent.isValid()) {
p_parent_node=this->p_root_node;
} else {
p_parent_node=static_cast<RegistryNode*>(parent.internalPointer());
}
RegistryNode *p_child_node=p_parent_node->Child(row);
if(p_child_node) {
return this->createIndex(row,column,p_child_node);
} else {
return QModelIndex();
}
}
QModelIndex RegistryNodeTreeModel::parent(const QModelIndex &index) const {
if(!index.isValid()) return QModelIndex();
RegistryNode *p_child_node=
static_cast<RegistryNode*>(index.internalPointer());
RegistryNode *p_parent_node=p_child_node->Parent();
if(p_parent_node==this->p_root_node) {
return QModelIndex();
} else {
return this->createIndex(p_parent_node->Row(),0,p_parent_node);
}
}
int RegistryNodeTreeModel::rowCount(const QModelIndex &parent) const {
if(parent.column()>0) return 0;
RegistryNode *p_parent_node;
if(!parent.isValid()) {
p_parent_node=this->p_root_node;
} else {
p_parent_node=static_cast<RegistryNode*>(parent.internalPointer());
}
return p_parent_node->ChildCount();
}
int RegistryNodeTreeModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return 2;
}
QList<QModelIndex> RegistryNodeTreeModel::GetIndexListOf(QString path) const {
RegistryNode *p_parent_node=this->p_root_node;
QList<QModelIndex> ret;
QStringList nodes=path.split("\\",QString::SkipEmptyParts);
bool found=false;
// Create a list of all index's that form the supplied path
ret.clear();
for(int i=0;i<nodes.count();i++) {
found=false;
for(uint64_t ii=0;ii<p_parent_node->ChildCount();ii++) {
if(p_parent_node->Child(ii)->Data(0)==nodes.at(i)) {
ret.append(this->createIndex(ii,0,p_parent_node->Child(ii)));
p_parent_node=p_parent_node->Child(ii);
found=true;
break;
}
}
// Break when last child node was found
if(found && i==nodes.count()-1) break;
// Return an empty list when a child node wasn't found
else if(!found) return QList<QModelIndex>();
}
return ret;
}
QString RegistryNodeTreeModel::GetNodePath(QModelIndex child_index) const
{
QString path;
// Get current node name
path=this->data(child_index,Qt::DisplayRole).toString().prepend("\\");
// Build node path by prepending all parent nodes names
while(this->parent(child_index)!=QModelIndex()) {
child_index=this->parent(child_index);
path.prepend(this->data(child_index,
Qt::DisplayRole).toString().prepend("\\"));
}
return path;
}
+void RegistryNodeTreeModel::ReloadModelData() {
+ // Delete current values if data was already loaded once
+ if(this->p_root_node!=NULL) delete this->p_root_node;
+
+ // Create root node. It's values will be used as header values.
+ this->p_root_node=new RegistryNode(QList<QVariant>()<<tr("Node")
+ <<tr("Last mod. time"));
+
+ // Load data
+ this->SetupModelData(this->p_hive,this->p_root_node);
+
+ // TODO: Maybe emit signal that data has changed
+ //emit(RegistryNodeTreeModel::dataChanged())
+}
+
+/*******************************************************************************
+ * Private
+ ******************************************************************************/
+
void RegistryNodeTreeModel::SetupModelData(RegistryHive *p_hive,
RegistryNode *p_parent,
int hive_node)
{
QMap<QString,int> hive_children;
RegistryNode *p_node;
int64_t key_mod_time;
// Get all sub nodes of current hive node
if(hive_node) hive_children=p_hive->GetNodes(hive_node);
else hive_children=p_hive->GetNodes("\\");
if(hive_children.isEmpty()) return;
// Recursivly iterate over all sub nodes
QMapIterator<QString, int> i(hive_children);
while(i.hasNext()) {
i.next();
key_mod_time=p_hive->GetNodeModTime(i.value());
// TODO: Maybe we have to call GetErrorMsg in case an error occured
p_node=new RegistryNode(QList<QVariant>()<<i.key()<<
QVariant((qlonglong)key_mod_time),p_parent);
p_parent->AppendChild(p_node);
this->SetupModelData(p_hive,p_node,i.value());
}
}
diff --git a/trunk/registrynodetreemodel.h b/trunk/registrynodetreemodel.h
index d10ccd2..ececa60 100644
--- a/trunk/registrynodetreemodel.h
+++ b/trunk/registrynodetreemodel.h
@@ -1,66 +1,68 @@
/*******************************************************************************
* fred Copyright (c) 2011-2013 by Gillen Daniel <gillen.dan@pinguin.lu> *
* *
* 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 <http://www.gnu.org/licenses/>. *
*******************************************************************************/
#ifndef REGISTRYNODETREEMODEL_H
#define REGISTRYNODETREEMODEL_H
#include <QAbstractItemModel>
#include <QList>
#include <QString>
#include "registrynode.h"
#include "registryhive.h"
class RegistryNodeTreeModel : public QAbstractItemModel {
Q_OBJECT
public:
- RegistryNodeTreeModel(RegistryHive *p_hive, QObject *p_parent=0);
+ RegistryNodeTreeModel(RegistryHive *p_reghive, QObject *p_parent=0);
~RegistryNodeTreeModel();
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section,
Qt::Orientation orientation,
int role=Qt::DisplayRole) const;
QModelIndex index(int row,
int column,
const QModelIndex &parent=QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent=QModelIndex()) const;
int columnCount(const QModelIndex &parent=QModelIndex()) const;
QList<QModelIndex> GetIndexListOf(QString path) const;
QString GetNodePath(QModelIndex child_index) const;
+ void ReloadModelData();
private:
enum ColumnContent {
ColumnContent_NodeName=0,
ColumnContent_NodeModTime
};
+ RegistryHive *p_hive;
RegistryNode *p_root_node;
void SetupModelData(RegistryHive *p_hive,
RegistryNode *p_parent,
int hive_node=0);
};
#endif // REGISTRYNODETREEMODEL_H

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 5:21 AM (1 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1176831
Default Alt Text
(95 KB)

Event Timeline