Page MenuHomePhabricator

No OneTemporary

Size
125 KB
Referenced Files
None
Subscribers
None
diff --git a/trunk/dlgaddkey.cpp b/trunk/dlgaddkey.cpp
index 5f00cf6..81253ad 100644
--- a/trunk/dlgaddkey.cpp
+++ b/trunk/dlgaddkey.cpp
@@ -1,381 +1,382 @@
/*******************************************************************************
* 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 <QString>
#include <QStringList>
#include <QMessageBox>
#include <QRegExp>
#include <stdlib.h>
#include <QDebug>
#include "dlgaddkey.h"
#include "ui_dlgaddkey.h"
#include "registryhive.h"
#define MACROS_ENDIANNESS
#include "macros.h"
DlgAddKey::DlgAddKey(QWidget *p_parent,
QString key_name,
QString key_value_type,
QByteArray key_value) :
QDialog(p_parent),
ui(new Ui::DlgAddKey)
{
this->p_current_widget=NULL;
ui->setupUi(this);
// Create widgets
this->CreateValueWidgets();
// Set dialog title
if(key_name.isEmpty() && key_value_type.isEmpty() && key_value.isEmpty()) {
// If no values were passed, we consider this the ddd key dialog
this->setWindowTitle(tr("Add key"));
} else {
// If a value was passed, this is considered the edit key dialog
this->setWindowTitle(tr("Edit key"));
this->ui->EdtKeyName->setEnabled(false);
this->ui->CmbKeyType->setEnabled(false);
}
// Preload key value type values
QStringList value_types=RegistryHive::GetKeyValueTypes();
this->ui->CmbKeyType->addItems(value_types);
// Load values
if(!key_name.isEmpty()) this->ui->EdtKeyName->setText(key_name);
if(!key_value_type.isEmpty())
this->ui->CmbKeyType->setCurrentIndex(value_types.indexOf(key_value_type));
if(!key_value.isEmpty()) this->SetValueWidgetData(key_value,key_value_type);
}
DlgAddKey::~DlgAddKey() {
this->DestroyValueWidgets();
delete ui;
}
QString DlgAddKey::KeyName() {
return this->ui->EdtKeyName->text();
}
QString DlgAddKey::KeyType() {
return this->ui->CmbKeyType->currentText();
}
QByteArray DlgAddKey::KeyValue() {
return this->GetValueWidgetData();
}
void DlgAddKey::on_BtnCancel_clicked() {
this->reject();
}
void DlgAddKey::on_BtnOk_clicked() {
QString key_value_type=this->KeyType();
// Check entered data for correctness
if(key_value_type=="REG_MULTI_SZ") {
// REG_MULTI_SZ's can't contain empty sub-strings
QString cur_data=this->p_text_widget_text_edit->toPlainText();
QString new_data=cur_data;
// TODO: Do we need to check for \r\n on Windows??
new_data.replace(QRegExp("\n\n*"),"\n");
if(new_data.startsWith("\n")) new_data.remove(0,1);
if(new_data.endsWith("\n")) new_data.chop(1);
if(cur_data!=new_data) {
if(QMessageBox::information(this,
tr("Invalid data"),
tr("A REG_MULTI_SZ can not contain empty sub-strings! If you continue, they will be removed."),
QMessageBox::Yes,
QMessageBox::No)==QMessageBox::Yes)
{
this->p_text_widget_text_edit->setPlainText(new_data);
} else {
return;
}
}
} else if(key_value_type=="REG_DWORD" ||
key_value_type=="REG_DWORD_BIG_ENDIAN")
{
bool ok=false;
if(this->p_number_widget_rb_decimal->isChecked()) {
this->p_number_widget_line_edit->text().toInt(&ok);
} else {
// TODO: There seems to be a problem with 0xFFFFFFFF
this->p_number_widget_line_edit->text().toInt(&ok,16);
}
if(!ok) {
QMessageBox::information(this,
tr("Invalid data"),
tr("The value you entered could not be converted to a %1! Please correct it.").arg(key_value_type),
QMessageBox::Ok);
return;
}
} else if(key_value_type=="REG_QWORD") {
bool ok=false;
if(this->p_number_widget_rb_decimal->isChecked()) {
this->p_number_widget_line_edit->text().toLongLong(&ok);
} else {
this->p_number_widget_line_edit->text().toLongLong(&ok,16);
}
if(!ok) {
QMessageBox::information(this,
tr("Invalid data"),
tr("The value you entered could not be converted to a %1! Please correct it.").arg(key_value_type),
QMessageBox::Ok);
return;
}
}
this->accept();
}
void DlgAddKey::on_CmbKeyType_currentIndexChanged(const QString &arg1) {
// Remove current widget from grid layout
if(this->p_current_widget!=NULL) {
this->ui->gridLayout->removeWidget(this->p_current_widget);
this->p_current_widget->setVisible(false);
this->p_current_widget=NULL;
}
// Add new widget for selected value type
if(arg1=="REG_SZ" || arg1=="REG_EXPAND_SZ") {
// Line edit widget for REG_SZ and REG_EXPAND_SZ
this->ui->gridLayout->addWidget(this->p_line_widget,2,1);
this->p_current_widget=this->p_line_widget;
} else if(arg1=="REG_MULTI_SZ") {
// Text edit widget for REG_MULTI_SZ
this->ui->gridLayout->addWidget(this->p_text_widget,2,1);
this->p_current_widget=this->p_text_widget;
} else if(arg1=="REG_DWORD" ||
arg1=="REG_DWORD_BIG_ENDIAN" ||
arg1=="REG_QWORD")
{
// Number widget for REG_DWORD, REG_DWORD_BIG_ENDIAN and REG_QWORD
this->ui->gridLayout->addWidget(this->p_number_widget,2,1);
this->p_current_widget=this->p_number_widget;
} else if(arg1=="REG_BINARY" ||
arg1=="REG_LINK" ||
arg1=="REG_RESOURCE_LIST" ||
arg1=="REG_FULL_RESOURCE_DESC" ||
arg1=="REG_RESOURCE_REQ_LIST")
{
// Binary widget for all other types
this->ui->gridLayout->addWidget(this->p_binary_widget,2,1);
this->p_current_widget=this->p_binary_widget;
}
if(arg1!="REG_NONE") {
this->p_current_widget->setVisible(true);
this->ui->LblKeyValue->setVisible(true);
} else {
this->ui->LblKeyValue->setVisible(false);
}
}
void DlgAddKey::CreateValueWidgets() {
this->p_line_widget=new QWidget();
this->p_line_widget_layout=new QHBoxLayout(this->p_line_widget);
this->p_line_widget_line_edit=new QLineEdit();
this->p_line_widget->setContentsMargins(0,0,0,0);
this->p_line_widget_layout->setContentsMargins(0,0,0,0);
this->p_line_widget_layout->addWidget(this->p_line_widget_line_edit);
this->p_text_widget=new QWidget();
this->p_text_widget_layout=new QHBoxLayout(this->p_text_widget);
this->p_text_widget_text_edit=new QPlainTextEdit();
this->p_text_widget->setContentsMargins(0,0,0,0);
this->p_text_widget_layout->setContentsMargins(0,0,0,0);
this->p_text_widget_layout->addWidget(this->p_text_widget_text_edit);
this->p_number_widget=new QWidget();
this->p_number_widget_layout=new QHBoxLayout(this->p_number_widget);
this->p_number_widget_line_edit=new QLineEdit();
this->p_number_widget_rb_decimal=new QRadioButton(tr("Dec base"));
this->p_number_widget_rb_decimal->setChecked(true);
this->p_number_widget_rb_hex=new QRadioButton(tr("Hex base"));
this->p_number_widget->setContentsMargins(0,0,0,0);
this->p_number_widget_layout->setContentsMargins(0,0,0,0);
this->p_number_widget_layout->addWidget(this->p_number_widget_line_edit);
this->p_number_widget_layout->addWidget(this->p_number_widget_rb_decimal);
this->p_number_widget_layout->addWidget(this->p_number_widget_rb_hex);
this->p_binary_widget=new QWidget();
this->p_binary_widget_layout=new QHBoxLayout(this->p_binary_widget);
this->p_binary_widget_hex_edit=new HexEditWidget(this,false,false);
this->p_binary_widget->setContentsMargins(0,0,0,0);
this->p_binary_widget_layout->setContentsMargins(0,0,0,0);
this->p_binary_widget_layout->addWidget(this->p_binary_widget_hex_edit);
}
void DlgAddKey::DestroyValueWidgets() {
delete this->p_line_widget_line_edit;
delete this->p_line_widget_layout;
delete this->p_line_widget;
delete this->p_text_widget_text_edit;
delete this->p_text_widget_layout;
delete this->p_text_widget;
delete this->p_number_widget_rb_hex;
delete this->p_number_widget_rb_decimal;
delete this->p_number_widget_line_edit;
delete this->p_number_widget_layout;
delete this->p_number_widget;
delete this->p_binary_widget_hex_edit;
delete this->p_binary_widget_layout;
delete this->p_binary_widget;
}
void DlgAddKey::SetValueWidgetData(QByteArray &key_value,
QString &key_value_type)
{
if(key_value_type=="REG_SZ" || key_value_type=="REG_EXPAND_SZ") {
this->p_line_widget_line_edit->setText(
RegistryHive::KeyValueToString(key_value,
RegistryHive::StringToKeyValueType(
key_value_type)));
} else if(key_value_type=="REG_MULTI_SZ") {
+ // TODO: Switch to RegistryHive::KeyValueStringList
// TODO: Identify if this is UTF16 or UTF8 and remember it
- QStringList strings=RegistryHive::KeyValueToStringList(key_value,
- key_value_type);
+ QStringList strings=RegistryHive::KeyValueToStringList(key_value);
this->p_text_widget_text_edit->setPlainText(strings.join("\n"));
} else if(key_value_type=="REG_DWORD") {
this->p_number_widget_line_edit->setText(
RegistryHive::KeyValueToString(key_value,"int32"));
} else if(key_value_type=="REG_DWORD_BIG_ENDIAN") {
this->p_number_widget_line_edit->setText(
RegistryHive::KeyValueToString(key_value,"int32",0,0,false));
} else if(key_value_type=="REG_QWORD") {
this->p_number_widget_line_edit->setText(
RegistryHive::KeyValueToString(key_value,"int64",0,0,false));
} else if(key_value_type=="REG_BINARY" ||
key_value_type=="REG_LINK" ||
key_value_type=="REG_RESOURCE_LIST" ||
key_value_type=="REG_FULL_RESOURCE_DESC" ||
key_value_type=="REG_RESOURCE_REQ_LIST")
{
this->p_binary_widget_hex_edit->SetData(key_value);
}
}
QByteArray DlgAddKey::GetValueWidgetData() {
QString key_value_type=this->KeyType();
if(key_value_type=="REG_SZ" || key_value_type=="REG_EXPAND_SZ") {
// TODO: Wouldn't it be wise to let the user choose the encoding?
// Get data
QString data=this->p_line_widget_line_edit->text();
// Convert data to UTF16LE buffer
uint16_t *p_buf=NULL;
int buf_len=this->ToUtf16LeBuf(&p_buf,data.utf16(),data.size());
if(p_buf==NULL || buf_len==0) {
// TODO: Inform user there was an error???
return QByteArray("\x00\x00",2);
}
// Construct ByteArray, free buffer and return
QByteArray ret=QByteArray((char*)p_buf,buf_len);
free(p_buf);
return ret;
} else if(key_value_type=="REG_MULTI_SZ") {
+ // TODO: Switch to RegistryHive::StringListToKeyValue
// TODO: Wouldn't it be wise to let the user choose the encoding?
// TODO: When editing, use same encoding as original data
QString data=this->p_text_widget_text_edit->toPlainText();
// Convert data to UTF16LE buffer
uint16_t *p_buf=NULL;
int buf_len=this->ToUtf16LeBuf(&p_buf,data.utf16(),data.size());
if(p_buf==NULL || buf_len==0) {
// TODO: Inform user there was an error???
return QByteArray("\x00\x00\x00\x00",4);
}
// Replace \n in buffer with \0 which actually converts it to a
// semi REG_MULTI_SZ :-)
// TODO: Do we need to check for \r\n on Windows??
for(int i=0;i<buf_len;i++) {
if(LE32TOH(p_buf[i])==10) p_buf[i]=0;
}
// Construct ByteArray
QByteArray ret=QByteArray((char*)p_buf,buf_len);
// Append \0\0 to the end to make a real REG_MULTI_SZ
ret.append("\x00\x00",2);
// Free buffer and return
free(p_buf);
return ret;
} else if(key_value_type=="REG_DWORD" ||
key_value_type=="REG_DWORD_BIG_ENDIAN")
{
int32_t val;
if(this->p_number_widget_rb_decimal->isChecked()) {
val=this->p_number_widget_line_edit->text().toInt();
} else {
val=this->p_number_widget_line_edit->text().toInt(0,16);
}
if(key_value_type=="REG_DWORD") val=HTOLE32(val);
else val=HTOBE32(val);
return QByteArray((char*)&val,4);
} else if(key_value_type=="REG_QWORD") {
int64_t val;
if(this->p_number_widget_rb_decimal->isChecked()) {
val=this->p_number_widget_line_edit->text().toLongLong();
} else {
val=this->p_number_widget_line_edit->text().toLongLong(0,16);
}
val=HTOLE64(val);
return QByteArray((char*)&val,8);
} else if(key_value_type=="REG_BINARY" ||
key_value_type=="REG_LINK" ||
key_value_type=="REG_RESOURCE_LIST" ||
key_value_type=="REG_FULL_RESOURCE_DESC" ||
key_value_type=="REG_RESOURCE_REQ_LIST")
{
return this->p_binary_widget_hex_edit->GetData();
}
return QByteArray();
}
int DlgAddKey::ToUtf16LeBuf(uint16_t **pp_buf,
const uint16_t *p_data,
int ascii_len)
{
// Calculate utf16 buffer size
// TODO: This fails if there are chars that need more than 16bit!!
int buf_len=(ascii_len*2)+2;
// Alloc buffer and set to 0x00h
*pp_buf=(uint16_t*)malloc(buf_len);
if(*pp_buf==NULL) return 0;
memset(*pp_buf,0,buf_len);
if(ascii_len==0) {
// Empty string, we're done (buffer contains \0\0)
return buf_len;
}
// Fill buffer with UTF16 string (ignoring \0\0 at the end)
memcpy(*pp_buf,p_data,buf_len-2);
// Make sure endianness is LE
for(int i=0;i<ascii_len;i++) {
(*pp_buf)[i]=HTOLE16((*pp_buf)[i]);
}
return buf_len;
}
diff --git a/trunk/mainwindow.cpp b/trunk/mainwindow.cpp
index 942856d..06cdc6a 100644
--- a/trunk/mainwindow.cpp
+++ b/trunk/mainwindow.cpp
@@ -1,1078 +1,1078 @@
/*******************************************************************************
* 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 "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!"));
+ tr("Unable to switch write-support: %1")
+ .arg(this->p_hive->GetErrorMsg()));
return;
}
// Switch internal state
this->is_hive_writable=!this->is_hive_writable;
this->UpdateEnableWriteSupportMenu();
this->p_node_tree->SetWritable(this->is_hive_writable);
this->p_key_table->SetWritable(this->is_hive_writable);
this->UpdateWindowTitle(this->p_hive->Filename());
}
/*
* on_ActionPreferences_triggered
*/
void MainWindow::on_ActionPreferences_triggered() {
DlgPreferences dlg_preferences(this->p_settings,this);
dlg_preferences.exec();
// Update vars, objects and GUI elements which might be affected by the new
// settings
this->UpdateRecentlyOpenedMenu();
this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly();
this->UpdateEnableWriteSupportMenu();
this->p_reports->LoadReportTemplates();
}
/*
* on_ActionGenerateReport_triggered
*/
void MainWindow::on_ActionGenerateReport_triggered() {
DlgReportChooser dlg_repchooser(this->p_reports,
this->p_hive->HiveTypeToString(
this->p_hive->HiveType()),
this->p_settings,
this);
if(dlg_repchooser.exec()==QDialog::Accepted) {
QList<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;
}
}
}
/*
* on_ActionReloadReportTemplates_triggered
*/
void MainWindow::on_ActionReloadReportTemplates_triggered() {
this->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 \<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::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));
+ tr("Unable to create node '%1\\%2': %3!")
+ .arg(node_path,
+ node_name,
+ this->p_hive->GetErrorMsg()));
} else {
// Add node to model. We have to pass node_name as Ascii as utf8 names are
// not supported inside hives!
QModelIndex new_node_index=
this->p_reg_node_tree_model->AddNode(this->p_hive,
index,
new_node_id,
node_name.toAscii());
// Now that node has been added, expand parent and select new node
this->p_node_tree->expand(
this->p_reg_node_tree_model_proxy->mapFromSource(index));
new_node_index=
this->p_reg_node_tree_model_proxy->mapFromSource(new_node_index);
this->p_node_tree->scrollTo(new_node_index,
QAbstractItemView::PositionAtCenter);
this->p_node_tree->selectionModel()->clear();
this->p_node_tree->selectionModel()->
select(new_node_index,
QItemSelectionModel::ClearAndSelect |
QItemSelectionModel::Rows |
QItemSelectionModel::Current);
// And finally update key table
this->SlotNodeTreeClicked(new_node_index);
}
}
}
/*
* SlotDeleteNode
*/
void MainWindow::SlotDeleteNode(QModelIndex index) {
QString node_path;
if(!index.isValid()) return;
// Map proxy index to tree model index and get node path
index=this->p_reg_node_tree_model_proxy->mapToSource(index);
node_path=this->p_reg_node_tree_model->GetNodePath(index);
if(QMessageBox::warning(this,
tr("Delete node"),
tr("Are you sure you want to remove the node '%1' and all of its child nodes?").arg(node_path),
QMessageBox::Yes,
QMessageBox::No)==QMessageBox::Yes)
{
// Remove node from hive
if(!this->p_hive->DeleteNode(node_path)) {
- // TODO: Get error message and display it
QMessageBox::critical(this,
tr("Error"),
- tr("Unable to delete node '%1'!")
- .arg(node_path));
+ tr("Unable to delete node '%1': %2!")
+ .arg(node_path,this->p_hive->GetErrorMsg()));
return;
}
// Remove node from tree model and select nearest node
QModelIndex next_node_index=this->p_reg_node_tree_model->RemoveNode(index);
if(next_node_index.isValid()) {
next_node_index=
this->p_reg_node_tree_model_proxy->mapFromSource(next_node_index);
this->p_node_tree->selectionModel()->clear();
this->p_node_tree->selectionModel()->
select(next_node_index,
QItemSelectionModel::ClearAndSelect |
QItemSelectionModel::Rows |
QItemSelectionModel::Current);
}
// And finally update key table
this->SlotNodeTreeClicked(next_node_index);
}
}
/*
* SlotAddKey
*/
void MainWindow::SlotAddKey() {
DlgAddKey dlg_add_key(this);
if(dlg_add_key.exec()==QDialog::Accepted) {
// Get selected parent node
QModelIndex parent_node=this->p_node_tree->currentIndex();
parent_node=this->p_reg_node_tree_model_proxy->mapToSource(parent_node);
QString parent_node_path=this->p_reg_node_tree_model->GetNodePath(parent_node);
// Add key
int new_key=this->p_hive->AddKey(parent_node_path,
dlg_add_key.KeyName(),
dlg_add_key.KeyType(),
dlg_add_key.KeyValue());
if(new_key==0) {
- // TODO: Get error
QMessageBox::critical(this,
tr("Error"),
- tr("Unable to add key!"));
+ tr("Unable to add key: %1")
+ .arg(this->p_hive->GetErrorMsg()));
return;
}
// Add new key to the key table model
QModelIndex new_key_index=
this->p_reg_key_table_model->AddKey(this->p_hive,new_key);
if(new_key_index.isValid()) {
this->p_key_table->clearSelection();
this->p_key_table->scrollTo(new_key_index,
QAbstractItemView::PositionAtCenter);
this->p_key_table->selectRow(new_key_index.row());
}
this->SlotKeyTableClicked(new_key_index);
}
}
/*
* SlotEditKey
*/
void MainWindow::SlotEditKey(QModelIndex index) {
if(!index.isValid()) return;
// Get current values
QString key_name=
this->p_reg_key_table_model->data(this->p_reg_key_table_model->
index(index.row(),
RegistryKeyTableModel::
ColumnContent_KeyName),
Qt::DisplayRole).toString();
QString key_value_type=
this->p_reg_key_table_model->data(this->p_reg_key_table_model->
index(index.row(),
RegistryKeyTableModel::
ColumnContent_KeyType),
Qt::DisplayRole).toString();
QByteArray key_value=
this->p_reg_key_table_model->data(this->p_reg_key_table_model->
index(index.row(),
RegistryKeyTableModel::
ColumnContent_KeyValue),
RegistryKeyTableModel::
AdditionalRoles_GetRawData).toByteArray();
// Exec update dialog
DlgAddKey dlg_update_key(this,key_name,key_value_type,key_value);
if(dlg_update_key.exec()==QDialog::Accepted) {
// Get selected parent node
QModelIndex parent_node=this->p_node_tree->currentIndex();
parent_node=this->p_reg_node_tree_model_proxy->mapToSource(parent_node);
QString parent_node_path=this->p_reg_node_tree_model->GetNodePath(parent_node);
// Update key
int new_key=this->p_hive->UpdateKey(parent_node_path,
dlg_update_key.KeyName(),
dlg_update_key.KeyType(),
dlg_update_key.KeyValue());
if(new_key==0) {
- // TODO: Get error
QMessageBox::critical(this,
tr("Error"),
- tr("Unable to update key!"));
+ tr("Unable to update key: %1")
+ .arg(this->p_hive->GetErrorMsg()));
return;
}
// Update key in key table model
QModelIndex new_key_index=
this->p_reg_key_table_model->UpdateKey(this->p_hive,new_key);
this->p_key_table->clearSelection();
if(new_key_index.isValid()) {
this->p_key_table->scrollTo(new_key_index,
QAbstractItemView::PositionAtCenter);
this->p_key_table->selectRow(new_key_index.row());
// TODO: Update geometry in case data has been added and is now expanding
// behind the right border
// Update HexEditWidget
}
this->SlotKeyTableClicked(new_key_index);
}
}
/*
* SlotDeleteKey
*/
void MainWindow::SlotDeleteKey(QModelIndex index) {
if(!index.isValid()) return;
// Get selected key name
QString key_name=
this->p_reg_key_table_model->data(this->p_reg_key_table_model->
index(index.row(),
RegistryKeyTableModel::
ColumnContent_KeyName),
Qt::DisplayRole).toString();
// Get selected parent node
QModelIndex parent_node=this->p_node_tree->currentIndex();
parent_node=this->p_reg_node_tree_model_proxy->mapToSource(parent_node);
QString parent_node_path=this->p_reg_node_tree_model->GetNodePath(parent_node);
if(QMessageBox::warning(this,
tr("Delete key"),
tr("Are you sure you want to remove the key '%1\\%2'?")
.arg(parent_node_path,key_name),
QMessageBox::Yes,
QMessageBox::No)==QMessageBox::Yes)
{
// Remove key from hive
if(!this->p_hive->DeleteKey(parent_node_path,key_name)) {
- // 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));
+ tr("Unable to delete key '%1\\%2': %3")
+ .arg(parent_node_path,
+ key_name,
+ this->p_hive->GetErrorMsg()));
return;
}
// Remove key from table model and update selection
QModelIndex new_key_index=this->p_reg_key_table_model->RemoveKey(index);
this->p_key_table->clearSelection();
if(new_key_index.isValid()) {
this->p_key_table->scrollTo(new_key_index,
QAbstractItemView::PositionAtCenter);
this->p_key_table->selectRow(new_key_index.row());
}
}
}
/*******************************************************************************
* Private
******************************************************************************/
/*
* OpenHive
*/
void MainWindow::OpenHive(QString hive_file) {
// Update last open location
this->p_settings->SetLastOpenLocation(
hive_file.left(hive_file.lastIndexOf(QDir::separator())));
// If another hive is currently open, close it
if(this->is_hive_open) this->on_action_Close_hive_triggered();
// Try to open hive
if(!this->p_hive->Open(hive_file,!this->is_hive_writable)) {
QMessageBox::critical(this,
tr("Error opening hive file"),
tr("Unable to open file '%1'").arg(hive_file));
return;
}
// Create tree model & proxy
this->p_reg_node_tree_model=new RegistryNodeTreeModel(this->p_hive);
this->p_reg_node_tree_model_proxy=new RegistryNodeTreeModelProxy(this);
//this->p_reg_node_tree_model_proxy->setDynamicSortFilter(true);
this->p_reg_node_tree_model_proxy->
setSourceModel(this->p_reg_node_tree_model);
this->p_node_tree->setModel(this->p_reg_node_tree_model_proxy,
this->is_hive_writable);
this->is_hive_open=true;
// Enable data interpreter
this->p_hex_edit_widget->setEnabled(true);
// Update window title
this->UpdateWindowTitle(hive_file);
// Update menu states
this->UpdateMenuStates();
// Add file to recent list and update recently opened menu
this->p_settings->AddRecentFile(hive_file);
this->UpdateRecentlyOpenedMenu();
}
/*
* UpdateWindowTitle
*/
void MainWindow::UpdateWindowTitle(QString filename) {
if(filename=="") {
this->setWindowTitle(QString("%1 v%2").arg(APP_TITLE,APP_VERSION));
} else {
this->setWindowTitle(QString("%1 v%2 - %3").arg(APP_TITLE,
APP_VERSION,
filename.toLocal8Bit()
.constData()));
if(!this->is_hive_writable) {
this->setWindowTitle(this->windowTitle().append(QString(" (%1)")
.arg(tr("read-only"))));
}
}
}
/*
* UpdateMenuStates
*/
void MainWindow::UpdateMenuStates() {
if(this->is_hive_open) {
this->ui->action_Close_hive->setEnabled(true);
this->ui->ActionEnableWriteSupport->setEnabled(true);
this->ui->ActionSearch->setEnabled(true);
this->ui->ActionEnableWriteSupport->setEnabled(true);
this->ui->ActionGenerateReport->setEnabled(true);
this->ui->ActionReloadReportTemplates->setEnabled(true);
} 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<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;
}
}
/*
* 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<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);
}
}
/*
* 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!"));
+ tr("Unable to save changes: %1")
+ .arg(this->p_hive->GetErrorMsg()));
return false;
}
break;
}
case QMessageBox::No: {
// TODO: Discard any changes if we are changing to read-only!
break;
}
default: {
return false;
}
}
return true;
}
diff --git a/trunk/registryhive.cpp b/trunk/registryhive.cpp
index f866bd0..f3bacf4 100644
--- a/trunk/registryhive.cpp
+++ b/trunk/registryhive.cpp
@@ -1,1228 +1,1343 @@
/*******************************************************************************
* 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 <stdlib.h>
+#include <stdio.h>
#include <QDebug>
-#include <stdlib.h>
-#include <stdio.h>
+#include "registryhive.h"
+
+#define MACROS_ENDIANNESS
+#include "macros.h"
// TODO: __WORDSIZE is not defined under mingw and I currently have no idea how
// to identify a 64bit windows
#ifndef __WORDSIZE
#define __WORDSIZE 32
#endif
#if __WORDSIZE == 64
#define EPOCH_DIFF 0x19DB1DED53E8000
#else
#define EPOCH_DIFF 0x19DB1DED53E8000LL
#endif
/*******************************************************************************
* Public
******************************************************************************/
/*
* RegistryHive
*/
RegistryHive::RegistryHive(QObject *p_parent) : QObject(p_parent) {
this->erro_msg="";
this->is_error=false;
this->hive_file="";
this->p_hive=NULL;
this->is_hive_open=false;
this->is_hive_writable=false;
this->has_changes_to_commit=false;
}
/*
* ~RegistryHive
*/
RegistryHive::~RegistryHive() {
if(this->is_hive_open) this->Close();
}
/*
* Error
*/
bool RegistryHive::Error() {
return this->is_error;
}
/*
* GetErrorMsg
*/
QString RegistryHive::GetErrorMsg() {
QString msg=this->erro_msg;
this->erro_msg="";
this->is_error=false;
return msg;
}
/*
* Open
*/
bool RegistryHive::Open(QString file, bool read_only) {
if(this->is_hive_open) return false;
// Open hive file
this->p_hive=hivex_open(file.toAscii().constData(),
read_only ? 0 : HIVEX_OPEN_WRITE);
if(this->p_hive==NULL) return false;
// Set local vars
this->hive_file=file;
this->is_hive_open=true;
this->is_hive_writable=!read_only;
return true;
}
/*
* Reopen
*/
bool RegistryHive::Reopen(bool read_only) {
if(!this->is_hive_open) return false;
// Close hive first
if(hivex_close(this->p_hive)!=0) {
// According to the docs, even if hivex_close fails, it frees all handles.
// So we consider this fatal and final!
this->hive_file="";
this->is_hive_open=false;
this->is_hive_writable=false;
this->has_changes_to_commit=false;
return false;
}
// Reopen same hive
this->p_hive=hivex_open(this->hive_file.toAscii().constData(),
read_only ? 0 : HIVEX_OPEN_WRITE);
if(this->p_hive==NULL) {
this->hive_file="";
this->is_hive_open=false;
this->is_hive_writable=false;
this->has_changes_to_commit=false;
return false;
}
// Update local vars
this->is_hive_writable=!read_only;
this->has_changes_to_commit=false;
return true;
}
/*
* CommitChanges
*/
bool RegistryHive::CommitChanges() {
if(!this->is_hive_open || !this->is_hive_writable) return false;
if(!this->has_changes_to_commit) return true;
// TODO: Maybe it would be more secure to commit changes to a new file and
// then move it over the original one.
if(hivex_commit(this->p_hive,NULL,0)!=0) {
return false;
}
return true;
}
/*
* Close
*/
bool RegistryHive::Close() {
if(this->is_hive_open) {
// As hivex_close will _ALWAYS_ free the handle, we don't need the following
// values anymore
this->hive_file="";
this->is_hive_open=false;
this->is_hive_writable=false;
this->has_changes_to_commit=false;
// Close hive
if(hivex_close(this->p_hive)!=0) return false;
}
return true;
}
/*
* Filename
*/
QString RegistryHive::Filename() {
if(this->is_hive_open) return this->hive_file;
return QString();
}
/*
* HiveType
*/
RegistryHive::teHiveType RegistryHive::HiveType() {
// Check for SYSTEM hive
if(this->PathExists("\\Select") && this->PathExists("\\MountedDevices"))
return RegistryHive::eHiveType_SYSTEM;
// Check for SOFTWARE hive
if(this->PathExists("\\Microsoft\\Windows\\CurrentVersion") &&
this->PathExists("\\Microsoft\\Windows NT\\CurrentVersion"))
return RegistryHive::eHiveType_SOFTWARE;
// Check for SAM
if(this->PathExists("SAM\\Domains\\Account\\Users"))
return RegistryHive::eHiveType_SAM;
// Check for SECURITY
if(this->PathExists("\\Policy\\Accounts") &&
this->PathExists("\\Policy\\PolAdtEv"))
return RegistryHive::eHiveType_SECURITY;
// Check for NTUSER.DAT
if(this->PathExists("\\Software\\Microsoft\\Windows\\CurrentVersion"))
return RegistryHive::eHiveType_NTUSER;
// Unknown hive
return RegistryHive::eHiveType_UNKNOWN;
}
/*
* HiveTypeToString
*/
QString RegistryHive::HiveTypeToString(teHiveType hive_type) {
switch(hive_type) {
case RegistryHive::eHiveType_SYSTEM:
return "SYSTEM";
break;
case RegistryHive::eHiveType_SOFTWARE:
return "SOFTWARE";
break;
case RegistryHive::eHiveType_SAM:
return "SAM";
break;
case RegistryHive::eHiveType_SECURITY:
return "SECURITY";
break;
case RegistryHive::eHiveType_NTUSER:
return "NTUSER";
break;
default:
return "UNKNOWN";
}
}
/*
* HasChangesToCommit
*/
bool RegistryHive::HasChangesToCommit() {
return this->has_changes_to_commit;
}
/*
* GetNodes
*/
QMap<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);
}
/*
* GetKeyName
*/
bool RegistryHive::GetKeyName(int hive_key, QString &key_name) {
char *buf;
if(!this->is_hive_open) {
this->SetError(tr("Need to operate on an open hive!"));
return false;
}
buf=hivex_value_key(this->p_hive,(hive_value_h)hive_key);
if(buf==NULL) {
this->SetError(tr("Unable to get key name for key '%1'").arg(hive_key));
return false;
}
key_name=QString(buf);
free(buf);
return true;
}
/*
* GetKeyValue
*/
QByteArray RegistryHive::GetKeyValue(QString path,
QString key,
int *p_value_type,
size_t *p_value_len)
{
hive_node_h parent_node;
hive_value_h hive_key;
// Get handle to last node in path
if(!this->GetNodeHandle(path,&parent_node)) return QByteArray();
// Get key handle
hive_key=hivex_node_get_value(this->p_hive,
parent_node,key.toAscii().constData());
if(hive_key==0) {
this->SetError(tr("Unable to get key handle!"));
*p_value_len=-1;
return QByteArray();
}
// Get and return key value
return this->GetKeyValueHelper(hive_key,p_value_type,p_value_len);
}
/*
* GetKeyValue
*/
QByteArray RegistryHive::GetKeyValue(int hive_key,
int *p_value_type,
size_t *p_value_len)
{
if(hive_key==0) {
this->SetError(tr("Invalid key handle specified!"));
*p_value_type=-1;
return QByteArray();
}
// Get and return key value
return this->GetKeyValueHelper(hive_key,p_value_type,p_value_len);
}
/*
* GetKeyModTime
*/
int64_t RegistryHive::GetNodeModTime(QString path) {
hive_node_h node;
// Get handle to last node in path
if(!this->GetNodeHandle(path,&node)) {
this->SetError(tr("Unable to get node handle!"));
return 0;
}
// Get and return node's last modification timestamp
return this->GetNodeModTime(node);
}
/*
* GetKeyModTime
*/
int64_t RegistryHive::GetNodeModTime(int node) {
if(node==0) {
this->SetError(tr("Invalid node handle specified!"));
return 0;
}
// Get and return key's last modification timestamp
return hivex_node_timestamp(this->p_hive,node);
}
/*
* KeyValueToString
*/
QString RegistryHive::KeyValueToString(QByteArray value, int value_type) {
QString ret="";
int i=0;
#define ToHexStr() { \
for(i=0;i<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)
- // TODO: Same as above, what happens if....
+ // A Windows string (encoding is unknown, but often UTF16-LE) that may
+ // contain %env% (environment variable expansion) elements
+ // TODO: What happens if encoding is not UTF16-LE ??? Thx Billy!!!
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
- // TODO: What about LE32TOH ?
- ret=QString().sprintf("0x%08X",*(uint32_t*)value.constData());
+ ret=QString().sprintf("0x%08X",LE32TOH(*(uint32_t*)value.constData()));
break;
case hive_t_REG_DWORD_BIG_ENDIAN:
// DWORD (32 bit integer), big endian
- // TODO: What about BE32TOH ?
- ret=QString().sprintf("0x%08X",*(uint32_t*)value.constData());
+ ret=QString().sprintf("0x%08X",BE32TOH(*(uint32_t*)value.constData()));
break;
case hive_t_REG_LINK:
// Symbolic link to another part of the registry tree
ToHexStr();
break;
case hive_t_REG_MULTI_SZ:
// Multiple Windows strings.
- // See http://blogs.msdn.com/oldnewthing/archive/2009/10/08/9904646.aspx
- // TODO: Shouldn't this be decoded here? If so, how to know if it is ASCII
- // or UTF16XX??? Some magic is needed!
- ToHexStr();
+ // I suppose this is always LE encoded! M$ devs really suck!
+ ret=RegistryHive::KeyValueToStringList(value).join("\n");
break;
case hive_t_REG_RESOURCE_LIST:
// Resource list
ToHexStr();
break;
case hive_t_REG_FULL_RESOURCE_DESCRIPTOR:
// Resource descriptor
ToHexStr();
break;
case hive_t_REG_RESOURCE_REQUIREMENTS_LIST:
// Resouce requirements list
ToHexStr();
break;
case hive_t_REG_QWORD:
// QWORD (64 bit integer). Usually little endian.
- // TODO: What about LE64TOH? And what if not LE?
ret=
- QString("0x%1").arg((quint64)(*(uint64_t*)value.constData()),
+ QString("0x%1").arg((quint64)LE64TOH(*(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!
- // Use/add functions from/to macros.h!!!!
- // TODO: In addition, converting to host endianness is missing!
-#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
+ // Convert value
if(format=="int8" && remaining_data_len>=1) {
ret=QString().sprintf("%d",*(int8_t*)p_data);
} else if(format=="uint8" && remaining_data_len>=1) {
ret=QString().sprintf("%u",*(uint8_t*)p_data);
} else if(format=="int16" && remaining_data_len>=2) {
- int16_t val=*(int16_t*)p_data;
- if(little_endian) ret=QString().sprintf("%d",val);
- else ret=QString().sprintf("%d",bswap_16(val));
+ int16_t val;
+ if(little_endian) val=LE16TOH(*(int16_t*)p_data);
+ else val=BE16TOH(*(int16_t*)p_data);
+ ret=QString().sprintf("%d",val);
} else if(format=="uint16" && remaining_data_len>=2) {
- uint16_t val=*(uint16_t*)p_data;
- if(little_endian) ret=QString().sprintf("%u",val);
- else ret=QString().sprintf("%u",bswap_16(val));
+ uint16_t val;
+ if(little_endian) val=LE16TOH(*(uint16_t*)p_data);
+ else val=BE16TOH(*(uint16_t*)p_data);
+ ret=QString().sprintf("%u",val);
} else if(format=="int32" && remaining_data_len>=4) {
- int32_t val=*(int32_t*)p_data;
- if(little_endian) ret=QString().sprintf("%d",val);
- else ret=QString().sprintf("%d",bswap_32(val));
+ int32_t val;
+ if(little_endian) val=LE32TOH(*(int32_t*)p_data);
+ else val=BE32TOH(*(int32_t*)p_data);
+ ret=QString().sprintf("%d",val);
} else if(format=="uint32" && remaining_data_len>=4) {
- uint32_t val=*(uint32_t*)p_data;
- if(little_endian) ret=QString().sprintf("%u",val);
- else ret=QString().sprintf("%u",bswap_32(val));
+ uint32_t val;
+ if(little_endian) val=LE32TOH(*(uint32_t*)p_data);
+ else val=BE32TOH(*(uint32_t*)p_data);
+ ret=QString().sprintf("%u",val);
} else if(format=="unixtime" && remaining_data_len>=4) {
- uint32_t val=*(uint32_t*)p_data;
- if(!little_endian) val=bswap_32(val);
+ uint32_t val;
+ if(little_endian) val=LE32TOH(*(uint32_t*)p_data);
+ else val=BE32TOH(*(uint32_t*)p_data);
if(val==0) {
ret="n/a";
} else {
QDateTime date_time;
date_time.setTimeSpec(Qt::UTC);
date_time.setTime_t(val);
ret=date_time.toString("yyyy/MM/dd hh:mm:ss");
}
} else if(format=="int64" && remaining_data_len>=8) {
- int64_t val=*(int64_t*)p_data;
- if(little_endian) ret=QString("%1").arg(val);
- else ret=QString("%1").arg((int64_t)bswap_64(val));
+ int64_t val;
+ if(little_endian) val=LE64TOH(*(int64_t*)p_data);
+ else val=BE64TOH(*(int64_t*)p_data);
+ ret=QString("%1").arg(val);
} else if(format=="uint64" && remaining_data_len>=8) {
- uint64_t val=*(uint64_t*)p_data;
- if(little_endian) ret=QString("%1").arg(val);
- else ret=QString("%1").arg(bswap_64(val));
+ uint64_t val;
+ if(little_endian) val=LE64TOH(*(uint64_t*)p_data);
+ else val=BE64TOH(*(uint64_t*)p_data);
+ ret=QString("%1").arg(val);
/*
// TODO: Check how one could implement this
} else if(format=="unixtime64" && remaining_data_len>=8) {
if(*(uint64_t*)p_data==0) {
ret="n/a";
} else {
uint64_t secs=*(uint64_t*)p_data;
QDateTime date_time;
date_time.setTimeSpec(Qt::UTC);
// Set 32bit part of date/time
date_time.setTime_t(secs&0xFFFFFFFF);
// Now add high 32bit part of date/time
date_time.addSecs(secs>>32);
ret=date_time.toString("yyyy/MM/dd hh:mm:ss");
}
*/
} else if(format=="filetime" && remaining_data_len>=8) {
- uint64_t val=*(uint64_t*)p_data;
- if(!little_endian) val=bswap_64(val);
+ uint64_t val;
+ if(little_endian) val=LE64TOH(*(uint64_t*)p_data);
+ else val=BE64TOH(*(uint64_t*)p_data);
if(val==0) {
ret="n/a";
} else {
// TODO: Warn if >32bit
QDateTime date_time;
date_time.setTimeSpec(Qt::UTC);
date_time.setTime_t(RegistryHive::FiletimeToUnixtime(val));
ret=date_time.toString("yyyy/MM/dd hh:mm:ss");
}
} else if(format=="ascii") {
- // 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);
+ if(length!=-1) {
+ // User specified how many bytes to convert
+ ret=QString().fromAscii((char*)p_data,length);
+ } else {
+ // User did not specify how many bytes to convert, make sure data is 0
+ // terminated
+ if(key_value.indexOf("\x00",offset)!=-1) {
+ // Data is 0 terminated
+ ret=QString().fromAscii((char*)p_data);
+ } else {
+ // Data is not 0 terminated, convert all remaining_data_len bytes
+ ret=QString().fromAscii((char*)p_data,remaining_data_len);
+ }
+ }
} else if(format=="utf16" && remaining_data_len>=2) {
- ret=QString().fromUtf16((ushort*)p_data,length);
+ if(length!=-1) {
+ // User specified how many bytes to convert
+ ret=QString().fromUtf16((ushort*)p_data,length);
+ } else {
+ // User did not specify how many bytes to convert, make sure data is
+ // double 0 terminated
+ if(key_value.indexOf(QByteArray("\x00\x00",2),offset)!=-1) {
+ // Data is double 0 terminated
+ ret=QString().fromUtf16((ushort*)p_data);
+ } else {
+ // Data is not double 0 terminated, convert all remaining_data_len bytes
+ ret=QString().fromUtf16((ushort*)p_data,remaining_data_len);
+ }
+ }
} else {
// Unknown variant type or another error
return QString();
}
return ret;
}
/*
* KeyValueToStringList
+ *
+ * Should only be used for REG_MULTI_SZ values
*/
QStringList RegistryHive::KeyValueToStringList(QByteArray value,
- int value_type)
+ bool little_endian,
+ bool *p_ansi_encoded)
{
- // TODO: Fails bad if string is ASCCI and not UTF16XX!!!
- 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();
+ // Try to find value encoding (ANSI vs UNICODE)
+ bool is_ansi;
+ if(value.size()<=2) {
+ // http://blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx
+ // Ansi version of a REG_MULTI_SZ needs to be terminated by 2 \0 chars.
+ // So as long as the byte array has less or equal to 2 chars, it must be
+ // empty.
+ return QStringList();
+ } else if(value.size()==3) {
+ // Only 3 chars, this can only be an ansi string consisting of 1 char and 2
+ // \0 to terminate it
+ return QStringList()
+ <<QString(QChar((char)*((uint8_t*)(value.constData()))));
+ } else if(value.size()==4) {
+ if((uint32_t)*((uint32_t*)(value.constData()))==0) {
+ // http://blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx
+ // Unicode version of a REG_MULTI_SZ needs to be terminated by 4 \0 chars.
+ // So as long as the byte array has less or equal to 4 chars, and they are
+ // all 0 it must be empty.
+ return QStringList();
+ } else {
+ // Must be the ansi version of REG_MULTI_SZ
+ is_ansi=true;
+ }
+ } else if((uint32_t)*((uint32_t*)(value.right(4).constData()))==0) {
+ // Value end with 4 \0 chars, it must be unicode
+ is_ansi=false;
+ } else if((uint32_t)*((uint32_t*)(value.right(3).constData()))==0) {
+ // Value end with 3 \0 chars. Not possible according to the specs, but
+ // already seen in values M$ is storing! Those were unicode.
+ is_ansi=false;
+ } else if((uint16_t)*((uint16_t*)(value.right(2).constData()))==0) {
+ // Value only end with 2 \0 chars, it must be ansi
+ is_ansi=true;
+ } else {
+ // Value has more than 4 chars but does not end in 2 or 4 \0 chars. This
+ // is not according to specs!
+ 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()));
+ // Convert value to string list
+ QStringList result=QStringList();
+ QByteArray buf;
+ int last_pos=0,cur_pos=0;
+ if(!is_ansi) {
+ // Extract unicode strings
+ while(last_pos<value.size() &&
+ (cur_pos=value.indexOf(QByteArray("\x00\x00",2),last_pos))!=-1)
+ {
+ if(cur_pos==last_pos) break;
+ buf=value.mid(last_pos,(cur_pos-last_pos)+3);
+ if(little_endian) {
+ // Convert from LE to host
+ for(int i=0;i<buf.size();i+=2) {
+ *((uint16_t*)(buf.data()+i))=LE16TOH(*((uint16_t*)(buf.data()+i)));
+ }
+ } else {
+ // Convert from BE to host
+ for(int i=0;i<buf.size();i+=2) {
+ *((uint16_t*)(buf.data()+i))=BE16TOH(*((uint16_t*)(buf.data()+i)));
+ }
+ }
+ result.append(QString().fromUtf16((ushort*)buf.constData()));
+ last_pos=cur_pos+3;
+ }
+ } else {
+ // Extract ansi strings
+ while(last_pos<value.count() &&
+ (cur_pos=value.indexOf(QByteArray("\x00",1),last_pos))!=-1)
+ {
+ if(cur_pos==last_pos) break;
+ result.append(QString().fromLocal8Bit(value.mid(last_pos,
+ (cur_pos-last_pos)+1)
+ .constData()));
+ last_pos=cur_pos+1;
}
- last_pos=cur_pos+3;
}
+ if(p_ansi_encoded!=NULL) *p_ansi_encoded=is_ansi;
return result;
}
/*
- * KeyValueToStringList
+ * StringListToKeyValue
*/
-QStringList RegistryHive::KeyValueToStringList(QByteArray value,
- QString value_type)
+QByteArray RegistryHive::StringListToKeyValue(QStringList strings,
+ bool little_endian,
+ bool ansi_encoded)
{
- return RegistryHive::KeyValueToStringList(value,
- RegistryHive::StringToKeyValueType(
- value_type));
+ // Return empty key value if there are no strings
+ if(strings.count()==0) {
+ if(ansi_encoded) return QByteArray("\x00\x00",2);
+ else return QByteArray("\x00\x00\x00\x00",4);
+ }
+
+ // Convert string list
+ QByteArray result=QByteArray();
+ QString cur_string;
+ QByteArray buf;
+ QListIterator<QString> strings_it(strings);
+ while(strings_it.hasNext()) {
+ cur_string=strings_it.next();
+ if(ansi_encoded) {
+ // Ansi encoding, simply append char string and terminating \0
+ result.append(cur_string.toAscii().constData(),cur_string.size());
+ result.append("\x00",1);
+ } else {
+ // Unicode encoding
+ // First, convert value to utf16
+ // TODO: May fail if there is a char that needs more than 16 bit
+ buf=QByteArray((char*)(cur_string.utf16()),cur_string.size()*2);
+ // Then convert to correct endianness
+ if(little_endian) {
+ for(int i=0;i<buf.size();i+=2) {
+ *((uint16_t*)(buf.data()+i))=HTOLE16(*((uint16_t*)(buf.data()+i)));
+ }
+ } else {
+ for(int i=0;i<buf.size();i+=2) {
+ *((uint16_t*)(buf.data()+i))=HTOBE16(*((uint16_t*)(buf.data()+i)));
+ }
+ }
+ // And finally append converted value and terminating \0\0 to result
+ result.append(buf);
+ result.append("\x00\x00",2);
+ }
+ }
+
+ // Append terminating \0 chars and return
+ if(ansi_encoded) result.append("\x00",1);
+ else result.append("\x00\x00",2);
+ return result;
}
/*
* GetKeyValueTypes
*/
QStringList RegistryHive::GetKeyValueTypes() {
return QStringList()<<"REG_NONE"
<<"REG_SZ"
<<"REG_EXPAND_SZ"
<<"REG_BINARY"
<<"REG_DWORD"
<<"REG_DWORD_BIG_ENDIAN"
<<"REG_LINK"
<<"REG_MULTI_SZ"
<<"REG_RESOURCE_LIST"
<<"REG_FULL_RESOURCE_DESC"
<<"REG_RESOURCE_REQ_LIST"
<<"REG_QWORD";
}
/*
* KeyTypeToString
*/
QString RegistryHive::KeyValueTypeToString(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;
}
/*
* StringToKeyValueType
*/
int RegistryHive::StringToKeyValueType(QString value_type) {
if(value_type=="REG_NONE") return hive_t_REG_NONE;
if(value_type=="REG_SZ") return hive_t_REG_SZ;
if(value_type=="REG_EXPAND_SZ") return hive_t_REG_EXPAND_SZ;
if(value_type=="REG_BINARY") return hive_t_REG_BINARY;
if(value_type=="REG_DWORD") return hive_t_REG_DWORD;
if(value_type=="REG_DWORD_BIG_ENDIAN") return hive_t_REG_DWORD_BIG_ENDIAN;
if(value_type=="REG_LINK") return hive_t_REG_LINK;
if(value_type=="REG_MULTI_SZ") return hive_t_REG_MULTI_SZ;
if(value_type=="REG_RESOURCE_LIST") return hive_t_REG_RESOURCE_LIST;
if(value_type=="REG_FULL_RESOURCE_DESC")
return hive_t_REG_FULL_RESOURCE_DESCRIPTOR;
if(value_type=="REG_RESOURCE_REQ_LIST")
return hive_t_REG_RESOURCE_REQUIREMENTS_LIST;
if(value_type=="REG_QWORD") return hive_t_REG_QWORD;
// I think this might be a good default :-)
return hive_t_REG_BINARY;
}
/*
* FiletimeToUnixtime
*/
uint64_t RegistryHive::FiletimeToUnixtime(int64_t filetime) {
return (unsigned)((filetime-EPOCH_DIFF)/10000000);
}
/*
* AddNode
*/
int RegistryHive::AddNode(QString parent_node_path, QString node_name) {
if(!this->is_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<QString,int> child_nodes=this->GetNodes(parent_node);
if(child_nodes.contains(node_name.toAscii())) {
this->SetError(tr("The node '%1\\%2' already exists!")
.arg(parent_node_path,node_name));
return 0;
}
// Add new node
hive_node_h new_node=hivex_node_add_child(this->p_hive,
parent_node,
node_name.toAscii().constData());
if(new_node==0) {
this->SetError(tr("Unable to create new node '%1\\%2'!")
.arg(parent_node_path,node_name));
return 0;
}
this->has_changes_to_commit=true;
return new_node;
}
/*
* DeleteNode
*/
bool RegistryHive::DeleteNode(QString node_path) {
if(!this->is_hive_writable) return false;
// Get node handle to the node that should be deleted
hive_node_h node;
if(!this->GetNodeHandle(node_path,&node)) {
this->SetError(tr("Unable to get node handle for '%1'!")
.arg(node_path));
return false;
}
// Delete node
if(hivex_node_delete_child(this->p_hive,node)==-1) {
this->SetError(tr("Unable to delete node '%1'!")
.arg(node_path));
return false;
}
this->has_changes_to_commit=true;
return true;
}
/*
* AddKey
*/
int RegistryHive::AddKey(QString parent_node_path,
QString key_name,
QString key_value_type,
QByteArray key_value)
{
if(!this->is_hive_open || !this->is_hive_writable) {
- // TODO: Set error
+ this->SetError(tr("Hive has not been opened or opened read-only!"));
return false;
}
return this->SetKey(parent_node_path,
key_name,
key_value_type,
key_value,
true);
}
/*
* UpdateKey
*/
int RegistryHive::UpdateKey(QString parent_node_path,
QString key_name,
QString key_value_type,
QByteArray key_value)
{
if(!this->is_hive_open || !this->is_hive_writable) {
- // TODO: Set error
+ this->SetError(tr("Hive has not been opened or opened read-only!"));
return false;
}
return this->SetKey(parent_node_path,
key_name,
key_value_type,
key_value,
false);
}
/*
* DeleteKey
*/
bool RegistryHive::DeleteKey(QString parent_node_path, QString key_name) {
if(!this->is_hive_open || !this->is_hive_writable) {
- // TODO: Set error
+ this->SetError(tr("Hive has not been opened or opened read-only!"));
return false;
}
// libhivex offers no possibility to delete a single key :-(
// As a work around, this function temporarly stores all keys of the specified
// node, then deletes them all an re-creates all but the one that should be
// deleted.
// Get handle to parent node
hive_node_h parent_node;
if(!this->GetNodeHandle(parent_node_path,&parent_node)) {
- // TODO: Set error
return false;
}
// Get all child keys
hive_value_h *p_keys=hivex_node_values(this->p_hive,parent_node);
if(p_keys==NULL) {
this->SetError(tr("Unable to enumerate child keys for parent '%1'!")
.arg(parent_node_path));
return false;
}
// Get all child key values except the one that should be deleted
int i=0;
char *p_name;
int node_keys_count=0;
hive_set_value *node_keys=NULL;
#define FREE_NODE_KEYS() { \
for(int x=0;x<node_keys_count;x++) { \
free(node_keys[x].key); \
free(node_keys[x].value); \
} \
free(node_keys); \
}
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 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;i<nodes.count();i++) {
*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;
}
/*
* 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
+ this->SetError(tr("Unable to get handle to key '%1\\%2'!")
+ .arg(parent_node_path,key_name));
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;
}
/*
* 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
+ this->SetError(tr("Inexisting key '%1\\%2' can't be updated!")
+ .arg(parent_node_path,key_name));
return 0;
}
}
// Create and populate hive_set_value structure
- // 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
+ this->SetError(tr("Unable to alloc memory for hive_set_value struct!"));
return 0;
}
strcpy(key_val.key,key_name.toAscii().constData());
key_val.t=(hive_type)this->StringToKeyValueType(key_value_type);
key_val.len=key_value.size();
memcpy(key_val.value,key_value.constData(),key_value.size());
// Create/Update key
if(hivex_node_set_value(this->p_hive,parent_node,&key_val,0)!=0) {
- // TODO: Set error
+ this->SetError(tr("Unable to update key '%1\\%2'!")
+ .arg(parent_node_path,key_name));
return 0;
}
// Free the hive_set_value structure
free(key_val.key);
free(key_val.value);
// To make sure everything worked, a hadle to the new key is now requeried
// from hive and then returned
hive_value_h key;
if(!this->GetKeyHandle(parent_node_path,key_name,&key)) {
- // TODO: Set error
return 0;
}
this->has_changes_to_commit=true;
return key;
}
diff --git a/trunk/registryhive.h b/trunk/registryhive.h
index 86320d2..b9a319b 100644
--- a/trunk/registryhive.h
+++ b/trunk/registryhive.h
@@ -1,128 +1,131 @@
/*******************************************************************************
* 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 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);
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,
+ int length=-1,
bool little_endian=true);
- static QStringList KeyValueToStringList(QByteArray value, int value_type);
static QStringList KeyValueToStringList(QByteArray value,
- QString value_type);
+ bool little_endian=true,
+ bool *p_ansi_encoded=NULL);
+ static QByteArray StringListToKeyValue(QStringList strings,
+ bool little_endian=true,
+ bool ansi_encoded=false);
static QStringList GetKeyValueTypes();
static QString KeyValueTypeToString(int value_type);
static int StringToKeyValueType(QString value_type);
static uint64_t FiletimeToUnixtime(int64_t filetime);
int AddNode(QString parent_node_path, QString node_name);
bool DeleteNode(QString node_path);
int AddKey(QString parent_node_path,
QString key_name,
QString key_value_type,
QByteArray key_value);
int UpdateKey(QString parent_node_path,
QString key_name,
QString key_value_type,
QByteArray key_value);
bool DeleteKey(QString parent_node_path, QString key_name);
private:
QString erro_msg;
bool is_error;
QString hive_file;
hive_h *p_hive;
bool is_hive_open;
bool is_hive_writable;
bool has_changes_to_commit;
QString HivexError2String(int error);
void SetError(QString msg);
bool GetNodeHandle(QString &path, hive_node_h *p_node);
bool GetKeyHandle(QString &parent_node_path,
QString &key_name,
hive_value_h *p_key);
QMap<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);
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/report_templates/SYSTEM_BackupRestore.qs b/trunk/report_templates/SYSTEM_BackupRestore.qs
index 81f3f3c..3992726 100644
--- a/trunk/report_templates/SYSTEM_BackupRestore.qs
+++ b/trunk/report_templates/SYSTEM_BackupRestore.qs
@@ -1,72 +1,72 @@
function fred_report_info() {
var info={report_cat : "SYSTEM",
report_name : "Backup / Restore settings",
report_author : "Gillen Daniel",
report_desc : "Dump files / directories not to snapshot / backup and registry keys not to restore",
fred_api : 2,
hive : "SYSTEM"
};
return info;
}
function IsValid(val) {
if(typeof val !== 'undefined') return true;
else return false;
}
function print_table_row(cell01,cell02) {
println(" <tr><td>",cell01,"</td><td>",cell02,"</td></tr>");
}
function ListValues(root_key) {
var values=GetRegistryKeys(root_key);
if(IsValid(values)) {
println(" <p style=\"font-size:12; white-space:nowrap\">");
println(" <table style=\"margin-left:20px; font-size:12; white-space:nowrap\">");
for(var i=0;i<values.length;i++) {
var val=GetRegistryKeyValue(root_key,values[i]);
if(IsValid(val)) {
println(" <tr>");
println(" <td>",values[i],"</td>");
println(" <td>");
- var strings=RegistryKeyValueToStringList(val.value,val.type);
+ var strings=RegistryKeyValueToStringList(val.value);
for(var ii=0;ii<strings.length;ii++) {
println(" ",strings[ii],"<br />");
}
println(" </td>");
println(" </tr>");
}
}
println(" </table>");
println(" </p>");
} else {
println(" None");
}
}
function fred_report_html() {
var val;
println(" <h2>Backup / Restore settings</h2>");
// Get current controlset
var cur_controlset=GetRegistryKeyValue("\\Select","Current");
if(IsValid(cur_controlset)) {
cur_controlset=RegistryKeyValueToString(cur_controlset.value,cur_controlset.type);
// Current holds a DWORD value, thus we get a string like 0x00000000, but
// control sets are referenced only with the last 3 digits.
cur_controlset="ControlSet"+String(cur_controlset).substr(7,3);
println(" <u>Directories / files not to back up in Volume Shadow Copies</u>");
ListValues(cur_controlset+"\\Control\\BackupRestore\\FilesNotToSnapshot");
println(" <u>Directories / files not to back up or restore by backup apps</u>");
ListValues(cur_controlset+"\\Control\\BackupRestore\\FilesNotToBackup");
println(" <u>Registry nodes or values not to restore by backup apps</u>");
ListValues(cur_controlset+"\\Control\\BackupRestore\\KeysNotToRestore");
} else {
println(" <p><font color='red'>");
println(" Unable to determine current control set!<br />");
println(" Are you sure you are running this report against the correct registry hive?");
println(" </font></p>");
}
}
diff --git a/trunk/reportengine.cpp b/trunk/reportengine.cpp
index b7a9405..659afec 100644
--- a/trunk/reportengine.cpp
+++ b/trunk/reportengine.cpp
@@ -1,556 +1,559 @@
/*******************************************************************************
* 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 "reportengine.h"
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QStringList>
#include <QtDebug>
/*******************************************************************************
* Public
******************************************************************************/
ReportEngine::ReportEngine(RegistryHive *p_hive) : QScriptEngine() {
// Init vars
this->p_registry_hive=p_hive;
this->report_content="";
// Add our constants
this->globalObject().setProperty("ENGINE_API_VERSION",
FRED_REPORTENGINE_API_VERSION,
QScriptValue::ReadOnly|
QScriptValue::Undeletable);
/*
this->globalObject().setProperty("HIVE_FILE",
this->p_registry_hive->Filename(),
QScriptValue::ReadOnly|
QScriptValue::Undeletable);
*/
// Add our types to engine
qScriptRegisterMetaType<s_RegistryKeyValue>(this,
this->RegistryKeyValueToScript,
this->RegistryKeyValueFromScript);
this->p_type_byte_array=new ByteArray(this);
this->globalObject().setProperty("ByteArray",
this->p_type_byte_array->constructor());
// TODO: Is it really necessary to explicitly export these functions
// here ??????????
// Add our functions
// print
QScriptValue func_print=this->newFunction(this->Print);
this->globalObject().setProperty("print",func_print);
// println
QScriptValue func_println=this->newFunction(this->PrintLn);
this->globalObject().setProperty("println",func_println);
// RegistryKeyValueToString
QScriptValue func_value_to_string=
this->newFunction(this->RegistryKeyValueToString,2);
this->globalObject().setProperty("RegistryKeyValueToString",
func_value_to_string);
// RegistryKeyValueToVariant
QScriptValue func_value_to_variant=
this->newFunction(this->RegistryKeyValueToVariant);
this->globalObject().setProperty("RegistryKeyValueToVariant",
func_value_to_variant);
// RegistryKeyValueToStringList
QScriptValue func_value_to_string_list=
this->newFunction(this->RegistryKeyValueToStringList);
this->globalObject().setProperty("RegistryKeyValueToStringList",
func_value_to_string_list);
// RegistryKeyTypeToString
QScriptValue func_type_to_string=
this->newFunction(this->RegistryKeyTypeToString,1);
this->globalObject().setProperty("RegistryKeyTypeToString",
func_type_to_string);
}
ReportEngine::~ReportEngine() {
delete this->p_type_byte_array;
}
/*
* GetReportTemplateInfo
*/
QMap<QString,QVariant> ReportEngine::GetReportTemplateInfo(QString file) {
// Read template file
QString report_code;
if(!this->GetReportTemplateFileContents(file,report_code)) {
QMap<QString,QVariant> error_msg;
error_msg["error"]=report_code;
return error_msg;
}
// Evaluate report template script
QScriptValue report_result=this->evaluate(report_code,file);
if (report_result.isError() || this->hasUncaughtException()) {
QMap<QString,QVariant> error_msg;
error_msg["error"]=QString("File: %1\n Line: %2\nError: %3")
.arg(file)
.arg(report_result.property("lineNumber").toInt32())
.arg(report_result.toString());
return error_msg;
}
// Try to call the fred_report_info script function and return result
QScriptValue fred_report_info_func=
this->globalObject().property("fred_report_info");
if(!fred_report_info_func.isFunction()) {
QMap<QString,QVariant> error_msg;
error_msg["error"]=
QString("Report template '%1' does not have a fred_report_info function!")
.arg(file)
.arg(report_result.property("lineNumber").toInt32())
.arg(report_result.toString());
return error_msg;
}
QScriptValue fred_report_info_res=fred_report_info_func.call();
// TODO: Maybe do more checking on return value
return fred_report_info_res.toVariant().toMap();
}
/*
* GenerateReport
*/
bool ReportEngine::GenerateReport(RegistryHive *p_hive,
QString report_file,
QString &report_result,
bool console_mode)
{
// Clear internal buffer
this->report_content.clear();
// Update exported functions
this->UpdateExportedFunctions(p_hive);
// Read template file
QString report_code;
if(!this->GetReportTemplateFileContents(report_file,report_code)) {
report_result=report_code;
return false;
}
// Evaluate report template script
QScriptValue script_result=this->evaluate(report_code,report_file);
if (script_result.isError() || this->hasUncaughtException()) {
script_result=QString("File: %1\n Line: %2\nError: %3")
.arg(report_file)
.arg(script_result.property("lineNumber").toInt32())
.arg(script_result.toString());
return false;
}
// Try to call the fred_report_html script function and return result
QScriptValue fred_report_html_func=
this->globalObject().property("fred_report_html");
if(!fred_report_html_func.isFunction()) {
report_result=
QString("Report template '%1' does not have a fred_report_info function!")
.arg(report_file)
.arg(script_result.property("lineNumber").toInt32())
.arg(script_result.toString());
return false;
}
QScriptValue fred_report_html_res=fred_report_html_func.call();
// TODO: Maybe do more checking on return value
report_result=this->report_content;
return true;
}
/*******************************************************************************
* Public Slots
******************************************************************************/
/*******************************************************************************
* Private
******************************************************************************/
/*
* Print
*/
QScriptValue ReportEngine::Print(QScriptContext *context,
QScriptEngine *engine)
{
int i;
QString content;
// Append all arguments to content
for(i=0;i<context->argumentCount();++i) {
//if(i>0) content.append(" ");
content.append(context->argument(i).toString());
}
qobject_cast<ReportEngine*>(engine)->report_content.append(content);
return engine->undefinedValue();
}
/*
* PrintLn
*/
QScriptValue ReportEngine::PrintLn(QScriptContext *context,
QScriptEngine *engine)
{
int i;
QString content;
// Append all arguments to content
for(i=0;i<context->argumentCount();++i) {
//if(i>0) content.append(" ");
content.append(context->argument(i).toString());
}
qobject_cast<ReportEngine*>(engine)->
report_content.append(content).append("\n");
return engine->undefinedValue();
}
/*
* GetRegistryNodes
*/
QScriptValue ReportEngine::GetRegistryNodes(QScriptContext *context,
QScriptEngine *engine)
{
QScriptValue calleeData;
RegistryHive *p_hive;
QMap<QString,int> nodes;
QScriptValue ret_nodes;
int ii=0;
// This function needs one argument, parent node path
if(context->argumentCount()!=1) return engine->undefinedValue();
// Get calle data (Pointer to RegistryHive class)
calleeData=context->callee().data();
p_hive=qobject_cast<RegistryHive*>(calleeData.toQObject());
// Get nodes
nodes=p_hive->GetNodes(context->argument(0).toString());
if(p_hive->Error()) {
// Clear error state
p_hive->GetErrorMsg();
return engine->undefinedValue();
}
// Build script array
ret_nodes=engine->newArray(nodes.count());
QMapIterator<QString,int> i(nodes);
while(i.hasNext()) {
i.next();
ret_nodes.setProperty(ii++,QScriptValue(i.key()));
}
return ret_nodes;
}
/*
* GetRegistryKeys
*/
QScriptValue ReportEngine::GetRegistryKeys(QScriptContext *context,
QScriptEngine *engine)
{
QScriptValue calleeData;
RegistryHive *p_hive;
QMap<QString,int> keys;
QScriptValue ret_keys;
int ii=0;
// This function needs one argument, parent node path
if(context->argumentCount()!=1) return engine->undefinedValue();
// Get calle data (Pointer to RegistryHive class)
calleeData=context->callee().data();
p_hive=qobject_cast<RegistryHive*>(calleeData.toQObject());
// Get keys
keys=p_hive->GetKeys(context->argument(0).toString());
if(p_hive->Error()) {
// Clear error state
p_hive->GetErrorMsg();
return engine->undefinedValue();
}
// Build script array
ret_keys=engine->newArray(keys.count());
QMapIterator<QString,int> i(keys);
while(i.hasNext()) {
i.next();
ret_keys.setProperty(ii++,QScriptValue(i.key()));
}
return ret_keys;
}
/*
* RegistryKeyValueToScript
*/
QScriptValue ReportEngine::RegistryKeyValueToScript(QScriptEngine *engine,
const s_RegistryKeyValue &s)
{
QScriptValue obj=engine->newObject();
obj.setProperty("type",s.type);
obj.setProperty("length",s.length);
ByteArray *p_byte_array=new ByteArray(engine);
obj.setProperty("value",p_byte_array->newInstance(s.value));
return obj;
}
/*
* RegistryKeyValueFromScriptValue
*/
void ReportEngine::RegistryKeyValueFromScript(const QScriptValue &obj,
s_RegistryKeyValue &s)
{
s.type=obj.property("type").toInt32();
s.length=obj.property("length").toInt32();
- // TODO: Don't know if this works, but it probably does ;)
s.value=qvariant_cast<QByteArray>(obj.property("value").data().toVariant());
}
/*
* GetRegistryKeyValue
*/
QScriptValue ReportEngine::GetRegistryKeyValue(QScriptContext *context,
QScriptEngine *engine)
{
QScriptValue calleeData;
RegistryHive *p_hive;
QByteArray key_value;
int key_type=0;
size_t key_length=0;
s_RegistryKeyValue script_key_value;
// This function needs two arguments, key path and key name
if(context->argumentCount()!=2) return engine->undefinedValue();
// Get calle data (Pointer to RegistryHive class)
calleeData=context->callee().data();
p_hive=qobject_cast<RegistryHive*>(calleeData.toQObject());
// Get key value
key_value=p_hive->GetKeyValue(context->argument(0).toString(),
context->argument(1).toString(),
&key_type,
&key_length);
if(p_hive->Error()) {
// Get error message to clear error state
p_hive->GetErrorMsg();
// printf("\nError: %s\n",p_hive->GetErrorMsg().toAscii().constData());
return engine->undefinedValue();
}
// Save key value to s_RegistryKeyValue struct
script_key_value.type=key_type;
script_key_value.length=key_length;
script_key_value.value=key_value;
return ReportEngine::RegistryKeyValueToScript(engine,script_key_value);
}
/*
* RegistryKeyValueToString
*/
QScriptValue ReportEngine::RegistryKeyValueToString(QScriptContext *context,
QScriptEngine *engine)
{
QByteArray key_value;
QString ret="";
// This function needs two arguments, key value and value type
if(context->argumentCount()!=2) return engine->undefinedValue();
// Cast ByteArray argument to QByteArray and convert
key_value=qvariant_cast<QByteArray>(context->argument(0).data().toVariant());
ret=RegistryHive::KeyValueToString(key_value,
context->argument(1).toInt32());
return engine->newVariant(ret);
}
/*
* RegistryKeyValueToVariant
*/
QScriptValue ReportEngine::RegistryKeyValueToVariant(QScriptContext *context,
QScriptEngine *engine)
{
int offset=0;
int length=-1;
bool little_endian=true;
QByteArray key_value;
QString format="";
QString ret="";
// This function needs at least two arguments, key value and variant type,
// and may have three optional arguments, offset, length and little_endian
if(context->argumentCount()<2 || context->argumentCount()>5) {
return engine->undefinedValue();
}
if(context->argumentCount()==3) {
offset=context->argument(2).toInt32();
}
if(context->argumentCount()==4) {
offset=context->argument(2).toInt32();
length=context->argument(3).toInt32();
}
if(context->argumentCount()==5) {
offset=context->argument(2).toInt32();
length=context->argument(3).toInt32();
little_endian=(context->argument(4).toInt32()==1);
}
// Cast ByteArray argument to QByteArray
key_value=qvariant_cast<QByteArray>(context->argument(0).data().toVariant());
format=context->argument(1).toString();
ret=RegistryHive::KeyValueToString(key_value,format,offset,length,little_endian);
return engine->newVariant(ret);
}
/*
* RegistryKeyValueToStringList
*/
QScriptValue ReportEngine::RegistryKeyValueToStringList(QScriptContext *context,
QScriptEngine *engine)
{
QByteArray value;
QStringList strings;
QScriptValue ret;
int i=0;
+ bool little_endian=true;
- // This function needs two arguments, key value and key type
- if(context->argumentCount()!=2) return engine->undefinedValue();
+ // This function needs one arguments, key value, and may have a second
+ // specifying endianness
+ if(context->argumentCount()==0 || context->argumentCount()>2)
+ return engine->undefinedValue();
+ if(context->argumentCount()==2) {
+ little_endian=context->argument(2).toBool();
+ }
// Cast ByteArray argument to QByteArray and convert
value=qvariant_cast<QByteArray>(context->argument(0).data().toVariant());
- strings=RegistryHive::KeyValueToStringList(value,
- context->argument(1)
- .toInt32());
+ strings=RegistryHive::KeyValueToStringList(value,little_endian);
// Build script array
ret=engine->newArray(strings.count());
QListIterator<QString> str_it(strings);
while(str_it.hasNext()) {
ret.setProperty(i++,QScriptValue(str_it.next()));
}
return ret;
}
/*
* RegistryKeyTypeToString
*/
QScriptValue ReportEngine::RegistryKeyTypeToString(QScriptContext *context,
QScriptEngine *engine)
{
QString ret="";
// This function needs one argument, key type
if(context->argumentCount()!=1) return engine->undefinedValue();
ret=RegistryHive::KeyValueTypeToString(context->argument(0).toInt32());
return engine->newVariant(ret);
}
/*
* GetRegistryNodeModTime
*/
QScriptValue ReportEngine::GetRegistryNodeModTime(QScriptContext *context,
QScriptEngine *engine)
{
QScriptValue calleeData;
RegistryHive *p_hive;
int64_t mod_time=0;
// This function needs one argument, node path
if(context->argumentCount()!=1) return engine->undefinedValue();
// Get calle data (Pointer to RegistryHive class)
calleeData=context->callee().data();
p_hive=qobject_cast<RegistryHive*>(calleeData.toQObject());
mod_time=p_hive->GetNodeModTime(context->argument(0).toString());
if(p_hive->Error()) {
// Get error message to clear error state
p_hive->GetErrorMsg();
return engine->undefinedValue();
}
QDateTime date_time;
date_time.setTimeSpec(Qt::UTC);
date_time.setTime_t(RegistryHive::FiletimeToUnixtime(mod_time));
return engine->newVariant(date_time.toString("yyyy/MM/dd hh:mm:ss"));
}
/*
* GetReportTemplateFileContents
*/
bool ReportEngine::GetReportTemplateFileContents(QString file,
QString &contents)
{
// Open report template file
QFile template_file(file);
if(!template_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
contents=QString("Couldn't open report template file '%1'!").arg(file);
return false;
}
// Read template file and close it
contents.clear();
QTextStream in(&template_file);
while(!in.atEnd()) contents.append(in.readLine()).append("\n");
template_file.close();
return true;
}
/*
* UpdateExportedFunctions
*/
void ReportEngine::UpdateExportedFunctions(RegistryHive *p_hive) {
this->p_registry_hive=p_hive;
// GetRegistryNodes
QScriptValue func_get_nodes=this->newFunction(this->GetRegistryNodes,1);
func_get_nodes.setData(this->newQObject(this->p_registry_hive));
this->globalObject().setProperty("GetRegistryNodes",func_get_nodes);
// GetRegistryKeys
QScriptValue func_get_keys=this->newFunction(this->GetRegistryKeys,1);
func_get_keys.setData(this->newQObject(this->p_registry_hive));
this->globalObject().setProperty("GetRegistryKeys",func_get_keys);
// GetRegistryKeyValue
QScriptValue func_get_key_value=this->newFunction(this->GetRegistryKeyValue,
2);
func_get_key_value.setData(this->newQObject(this->p_registry_hive));
this->globalObject().setProperty("GetRegistryKeyValue",func_get_key_value);
// GetRegistryNodeModTime
QScriptValue func_get_node_modt=
this->newFunction(this->GetRegistryNodeModTime,1);
func_get_node_modt.setData(this->newQObject(this->p_registry_hive));
this->globalObject().setProperty("GetRegistryNodeModTime",func_get_node_modt);
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 11:36 AM (6 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1176850
Default Alt Text
(125 KB)

Event Timeline