diff --git a/trunk/autogen.sh b/trunk/autogen.sh index fd0d9a6..12ae69a 100755 --- a/trunk/autogen.sh +++ b/trunk/autogen.sh @@ -1,57 +1,253 @@ #!/bin/bash -LINUX_QMAKE="qmake" +# ----------------------------------------------------------------------------- +# Default command line options. +# ----------------------------------------------------------------------------- -WIN32_HOST="i686-w64-mingw32" -WIN32_QMAKE="/opt/qt-4.8.4-mingw/bin/qmake" +OPT_JOBS=1 +OPT_ONLY_BOOTSTRAP=0 +OPT_PLATFORM="linux" +OPT_STATIC_HIVEX=0 -HIVEX_CONFIGURE_OPTIONS="--disable-ocaml --disable-perl --disable-python --disable-ruby --disable-shared" +OPT_LINUX_QMAKE=`which qmake` +OPT_WIN32_COMPILER_SUFFIX="i686-w64-mingw32" +OPT_WIN32_QMAKE="/opt/qt-4.8.4-mingw/bin/qmake" + +# ----------------------------------------------------------------------------- +# ------------------ DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------- +# ----------------------------------------------------------------------------- + +# Try to make somehow sure we are running in bash and not some other shell +if [ -z "$BASH_VERSION" ]; then + echo "ERROR: This script must be run in a bash shell! Try using \"bash $0\"" + exit 1 +fi + +# ----------------------------------------------------------------------------- +# Function declarations +# ----------------------------------------------------------------------------- + +# Print usage and exit PrintUsage() { - echo "Usage: $0 " + echo + echo "Usage:" + echo " $0 [options]" echo echo "Options:" - echo " platform: Specify the platform fred should be build for (linux or win32)." + echo " --help: Print this help message." + echo " --jobs= (Def.: 1) : Specify how many make jobs should be run simultaneously." + echo " --linux-qmake= (Def.: qmake) : Specify the linux qmake binary to use." + echo " --only-bootstrap[=0..1] (Def.: 0) : Only bootstrap, but do not compile fred." + echo " --platform= (Def.: linux) : Specify the platform fred should be build for. Available platforms are 'linux' and 'win32'." + echo " --static-hivex[=0..1] (Def.: 0): Build and link in-tree hivex statically." + echo " --win32-compiler-suffix= (Def.: i686-w64-mingw32) : Specify the win32 crosscompiler suffix to use." + echo " --win32-qmake= (Def.: /opt/qt-4.8.4-mingw/bin/qmake) : Specify the win32 qmake binary to use." echo exit 1 } -PLATFORM="" -[ -z "$1" ] && PrintUsage -[ "$1" = "linux" ] && PLATFORM="linux" -[ "$1" = "win32" ] && PLATFORM="win32" -[ -z "$PLATFORM" ] && PrintUsage +# Extract argument value +get_arg_val() { + local TMP=`echo "$1" | cut -d= -f2` + if [ "$1" = "$TMP" ]; then + # No arg specified for option, assume 1 + echo 1 + else + if [[ -z "$TMP" || $(echo -n "$TMP" | sed 's/[0-9]//g' | wc -c) -ne 0 ]]; then + echo "ERROR: Non-integer arg for option '$1' specified!" 1>&2 + exit 1 + fi + echo $TMP + fi +} -echo "Bootstrapping fred..." -git submodule init -git submodule update +# ----------------------------------------------------------------------------- +# Parse command line args +# ----------------------------------------------------------------------------- -echo "Bootstrapping hivex..." -( - cd hivex - if [ "$PLATFORM" = "linux" ]; then - ./autogen.sh $HIVEX_CONFIGURE_OPTIONS +shopt extglob &>/dev/null +EXTGLOB=$? +shopt -s extglob &>/dev/null +while :; do + case "$1" in + --help) + PrintUsage + ;; + --jobs=*) + OPT_JOBS=$(get_arg_val "$1") || PrintUsage + shift + ;; + --linux-qmake=*) + TMP=`echo "$1" | cut -d= -f2` + if [[ -z "$TMP" || "$1" = "$TMP" ]]; then + echo "ERROR: No option arg for '$1' specified!" + PrintUsage + fi + if [ ! -x "$TMP" ]; then + echo "ERROR: The specified linux qmake binary '$TMP' does not exist or is not executable!" + exit 1 + fi + OPT_LINUX_QMAKE="$TMP" + shift + ;; + --only-bootstrap?(=[01])) + OPT_ONLY_BOOTSTRAP=$(get_arg_val "$1") || PrintUsage + shift + ;; + --platform=*) + TMP=`echo "$1" | cut -d= -f2` + if [[ -z "$TMP" || "$1" = "$TMP" ]]; then + echo "ERROR: No option arg for '$1' specified!" + PrintUsage + fi + TMP=`echo "$TMP" | tr "[A-Z]" "[a-z]"` + if [[ "$TMP" != "linux" && "$TMP" != "win32" ]]; then + echo "ERROR: Unsupported platform '$TMP' specified!" + PrintUsage + fi + OPT_PLATFORM="$TMP" + shift + ;; + --static-hivex?(=[01])) + OPT_STATIC_HIVEX=$(get_arg_val "$1") || PrintUsage + shift + ;; + --win32-compiler-suffix=*) + TMP=`echo "$1" | cut -d= -f2` + if [[ -z "$TMP" || "$1" = "$TMP" ]]; then + echo "ERROR: No option arg for '$1' specified!" + PrintUsage + fi + if [[ ! -x "$(which \"${TMP}-gcc\")" || ! -x "$(which \"${TMP}-g++\")" ]]; then + echo "ERROR: Couldn't find '${TMP}-gcc' or '${TMP}-g++'!" + echo "ERROR: The specified win32 compiler suffix does not seem to be correct!" + exit 1 + fi + OPT_WIN32_COMPILER_SUFFIX="$TMP" + shift + ;; + --win32-qmake=*) + TMP=`echo "$1" | cut -d= -f2` + if [[ -z "$TMP" || "$1" = "$TMP" ]]; then + echo "ERROR: No option arg for '$1' specified!" + PrintUsage + fi + if [ ! -x "$TMP" ]; then + echo "ERROR: The specified win32 qmake binary '$TMP' does not exist or is not executable!" + exit 1 + fi + OPT_WIN32_QMAKE="$TMP" + shift + ;; + --*) + echo "ERROR: Unknown option / Wrong option arg '$1' specified!" 1>&2 + PrintUsage + ;; + *) + break + ;; + esac +done +if [ $EXTGLOB -ne 0 ]; then + shopt -u extglob &>/dev/null +fi + +# ----------------------------------------------------------------------------- +# Check command line args +# ----------------------------------------------------------------------------- + +if [ "$OPT_PLATFORM" = "linux" ]; then + if [ ! -x "$OPT_LINUX_QMAKE" ]; then + echo "ERROR: Couldn't find qmake! Consider specifying it with --linux-qmake." + exit 1 fi - if [ "$PLATFORM" = "win32" ]; then - ./autogen.sh --host=$WIN32_HOST $HIVEX_CONFIGURE_OPTIONS +fi + +if [ "$OPT_PLATFORM" = "win32" ]; then + if [ ! -x "$OPT_WIN32_QMAKE" ]; then + echo "ERROR: Couldn't find qmake! Consider specifying it with --win32-qmake." + exit 1 fi -) +fi -echo "Building hivex..." +# ----------------------------------------------------------------------------- +# Build +# ----------------------------------------------------------------------------- + +# Get script directory and cd to it +SCRIPT_DIR=`dirname "$0"` ( - cd hivex - make + cd "$SCRIPT_DIR" + + # When requested to build static, init, bootstrap, configure and make hivex + if [ $OPT_STATIC_HIVEX -eq 1 ]; then + echo "-----------------------------------------------------------------------------" + echo "Bootstrapping fred" + echo "-----------------------------------------------------------------------------" + ( + git submodule init + git submodule update + ) + + echo "-----------------------------------------------------------------------------" + echo "Bootstrapping hivex" + echo "-----------------------------------------------------------------------------" + ( + cd hivex + if [ "$OPT_PLATFORM" = "linux" ]; then + ./autogen.sh --disable-ocaml --disable-perl --disable-python --disable-ruby --disable-shared || exit 1 + fi + if [ "$OPT_PLATFORM" = "win32" ]; then + ./autogen.sh --host=$OPT_WIN32_COMPILER_SUFFIX --disable-ocaml --disable-perl --disable-python --disable-ruby --disable-shared || exit 1 + fi + ) + [ $? -ne 0 ] && exit 1 + + echo "-----------------------------------------------------------------------------" + echo "Building hivex" + echo "-----------------------------------------------------------------------------" + ( + cd hivex + make clean &>/dev/null + make -j$OPT_JOBS || exit 1 + ) + [ $? -ne 0 ] && [ "$OPT_PLATFORM" != "win32" ] && exit 1 + fi + + if [ $OPT_ONLY_BOOTSTRAP -eq 0 ]; then + echo "-----------------------------------------------------------------------------" + echo "Building fred" + echo "-----------------------------------------------------------------------------" + make distclean &>/dev/null + if [ "$OPT_PLATFORM" = "linux" ]; then + if [ $OPT_STATIC_HIVEX -eq 0 ]; then + $OPT_LINUX_QMAKE || exit 1 + else + $OPT_LINUX_QMAKE HIVEX_STATIC=1 || exit 1 + fi + make clean &>/dev/null + make -j$OPT_JOBS || exit 1 + fi + if [ "$OPT_PLATFORM" = "win32" ]; then + if [ $OPT_STATIC_HIVEX -eq 0 ]; then + $OPT_WIN32_QMAKE || exit 1 + else + $OPT_WIN32_QMAKE HIVEX_STATIC=1 || exit 1 + fi + make clean &>/dev/null + make -j$OPT_JOBS || exit 1 + fi + fi ) -echo "Building fred..." -if [ "$PLATFORM" = "linux" ]; then - $LINUX_QMAKE - make -fi -if [ "$PLATFORM" = "win32" ]; then - $WIN32_QMAKE - make +if [ $? -eq 0 ]; then + echo "-----------------------------------------------------------------------------" + echo "All done." + echo "-----------------------------------------------------------------------------" +else + echo "-----------------------------------------------------------------------------" + echo "An error occured while building! See output above for details." + echo "-----------------------------------------------------------------------------" fi -echo "All done." - diff --git a/trunk/fred.pro b/trunk/fred.pro index 9096c57..0f29a78 100644 --- a/trunk/fred.pro +++ b/trunk/fred.pro @@ -1,125 +1,132 @@ #******************************************************************************* # fred Copyright (c) 2011-2013 by Gillen Daniel * # * # Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * # with special feautures useful during forensic analysis. * # * # This program is free software: you can redistribute it and/or modify it * # under the terms of the GNU General Public License as published by the Free * # Software Foundation, either version 3 of the License, or (at your option) * # any later version. * # * # This program is distributed in the hope that it will be useful, but WITHOUT * # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * # more details. * # * # You should have received a copy of the GNU General Public License along with * # this program. If not, see . * #******************************************************************************/ # Generate compileinfo.h system(bash compileinfo.sh > compileinfo.h) #compileinfo.target = compileinfo.h #compileinfo.commands = $$PWD/compileinfo.sh > compileinfo.h #QMAKE_EXTRA_TARGETS += compileinfo #PRE_TARGETDEPS += compileinfo.h -# Build fred +# Check command line args +!isEmpty(HIVEX_STATIC) { + DEFINES += "HIVEX_STATIC" +} +# Configure fred QMAKE_CXXFLAGS += -Wall -QT += core \ - gui \ - script \ - webkit +QT += core \ + gui \ + script \ + webkit -CONFIG += console +CONFIG += console -TARGET = fred +TARGET = fred -TEMPLATE = app +TEMPLATE = app -SOURCES += main.cpp\ - mainwindow.cpp \ - registrynode.cpp \ - registrynodetreemodel.cpp \ - registrykey.cpp \ - registrykeytablemodel.cpp \ - dlgabout.cpp \ - qhexedit/qhexedit_p.cpp \ - qhexedit/qhexedit.cpp \ - reporttemplate.cpp \ - registryhive.cpp \ - qtscript_types/bytearray.cpp \ - qtscript_types/bytearrayprototype.cpp \ - qtscript_types/bytearrayiterator.cpp \ - dlgreportviewer.cpp \ - registrykeytable.cpp \ - registrynodetree.cpp \ - dlgsearch.cpp \ - threadsearch.cpp \ - searchresultwidget.cpp \ - tabwidget.cpp \ - argparser.cpp \ - datainterpretertable.cpp \ - datainterpreterwidget.cpp \ - hexeditwidget.cpp \ - settings.cpp \ - searchresulttabledelegate.cpp \ - registrynodetreemodelproxy.cpp \ - reports.cpp \ - reportengine.cpp \ - dlgreportchooser.cpp \ - dlgpreferences.cpp \ - dlgaddkey.cpp +SOURCES += main.cpp\ + mainwindow.cpp \ + registrynode.cpp \ + registrynodetreemodel.cpp \ + registrykey.cpp \ + registrykeytablemodel.cpp \ + dlgabout.cpp \ + qhexedit/qhexedit_p.cpp \ + qhexedit/qhexedit.cpp \ + reporttemplate.cpp \ + registryhive.cpp \ + qtscript_types/bytearray.cpp \ + qtscript_types/bytearrayprototype.cpp \ + qtscript_types/bytearrayiterator.cpp \ + dlgreportviewer.cpp \ + registrykeytable.cpp \ + registrynodetree.cpp \ + dlgsearch.cpp \ + threadsearch.cpp \ + searchresultwidget.cpp \ + tabwidget.cpp \ + argparser.cpp \ + datainterpretertable.cpp \ + datainterpreterwidget.cpp \ + hexeditwidget.cpp \ + settings.cpp \ + searchresulttabledelegate.cpp \ + registrynodetreemodelproxy.cpp \ + reports.cpp \ + reportengine.cpp \ + dlgreportchooser.cpp \ + dlgpreferences.cpp \ + dlgaddkey.cpp -HEADERS += mainwindow.h \ - registrynode.h \ - registrynodetreemodel.h \ - registrykey.h \ - registrykeytablemodel.h \ - dlgabout.h \ - qhexedit/qhexedit_p.h \ - qhexedit/qhexedit.h \ - reporttemplate.h \ - registryhive.h \ - qtscript_types/bytearray.h \ - qtscript_types/bytearrayprototype.h \ - qtscript_types/bytearrayiterator.h \ - dlgreportviewer.h \ - registrykeytable.h \ - registrynodetree.h \ - dlgsearch.h \ - threadsearch.h \ - searchresultwidget.h \ - tabwidget.h \ - argparser.h \ - datainterpretertable.h \ - datainterpreterwidget.h \ - hexeditwidget.h \ - settings.h \ - searchresulttabledelegate.h \ - registrynodetreemodelproxy.h \ - reports.h \ - reportengine.h \ - dlgreportchooser.h \ - dlgpreferences.h \ - dlgaddkey.h +HEADERS += mainwindow.h \ + registrynode.h \ + registrynodetreemodel.h \ + registrykey.h \ + registrykeytablemodel.h \ + dlgabout.h \ + qhexedit/qhexedit_p.h \ + qhexedit/qhexedit.h \ + reporttemplate.h \ + registryhive.h \ + qtscript_types/bytearray.h \ + qtscript_types/bytearrayprototype.h \ + qtscript_types/bytearrayiterator.h \ + dlgreportviewer.h \ + registrykeytable.h \ + registrynodetree.h \ + dlgsearch.h \ + threadsearch.h \ + searchresultwidget.h \ + tabwidget.h \ + argparser.h \ + datainterpretertable.h \ + datainterpreterwidget.h \ + hexeditwidget.h \ + settings.h \ + searchresulttabledelegate.h \ + registrynodetreemodelproxy.h \ + reports.h \ + reportengine.h \ + dlgreportchooser.h \ + dlgpreferences.h \ + dlgaddkey.h -FORMS += mainwindow.ui \ - dlgabout.ui \ - dlgreportviewer.ui \ - dlgsearch.ui \ - dlgreportchooser.ui \ - dlgpreferences.ui \ - dlgaddkey.ui +FORMS += mainwindow.ui \ + dlgabout.ui \ + dlgreportviewer.ui \ + dlgsearch.ui \ + dlgreportchooser.ui \ + dlgpreferences.ui \ + dlgaddkey.ui -#LIBS += -lhivex -LIBS += $$PWD/hivex/lib/.libs/libhivex.a +!isEmpty(HIVEX_STATIC) { + LIBS += $$PWD/hivex/lib/.libs/libhivex.a +} else { + LIBS += -lhivex +} -#DEFINES += __STDC_FORMAT_MACROS +win32:LIBS += -liconv -RESOURCES += fred.qrc -RC_FILE = fred.rc -ICON = resources/fred.icns +RESOURCES += fred.qrc +RC_FILE = fred.rc +ICON = resources/fred.icns diff --git a/trunk/registryhive.cpp b/trunk/registryhive.cpp index 241a07e..de31dff 100644 --- a/trunk/registryhive.cpp +++ b/trunk/registryhive.cpp @@ -1,1388 +1,1400 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include #include #include #include #include #include #include "registryhive.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 +// Macros to ease UTF16 endianness conversions #undef UTF16LETOH #define UTF16LETOH(buf,buf_len) { \ for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ *((quint16*)((buf)+buf_off))=qFromLittleEndian(*((quint16*)((buf)+buf_off))); \ } \ } #undef UTF16BETOH #define UTF16BETOH(buf,buf_len) { \ for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ *((quint16*)((buf)+buf_off))=qFromBigEndian(*((quint16*)((buf)+buf_off))); \ } \ } #undef HTOUTF16LE #define HTOUTF16LE(buf,buf_len) { \ for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ *((quint16*)((buf)+buf_off))=qToLittleEndian(*((quint16*)((buf)+buf_off))); \ } \ } #undef HTOUTF16BE #define HTOUTF16BE(buf,buf_len) { \ for(int buf_off=0;buf_off<((buf_len)-1);buf_off+=2) { \ *((quint16*)((buf)+buf_off))=qToBigEndian(*((quint16*)((buf)+buf_off))); \ } \ } +// Some errno numbers that hivex uses are not defined under Windows +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + // These are the same values as defined by MSVC 10, for interoperability. + #ifndef ENOTSUP + #define ENOTSUP 129 + #endif + #ifndef ELOOP + #define ELOOP 114 + #endif +#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; } this->has_changes_to_commit=false; return true; } /* * Close */ bool RegistryHive::Close() { if(this->is_hive_open) { // As hivex_close will _ALWAYS_ free the handle, we don't need the following // values anymore this->hive_file=""; this->is_hive_open=false; this->is_hive_writable=false; this->has_changes_to_commit=false; // Close hive if(hivex_close(this->p_hive)!=0) return false; } return true; } /* * Filename */ QString RegistryHive::Filename() { if(this->is_hive_open) return this->hive_file; return QString(); } /* * HiveType */ RegistryHive::teHiveType RegistryHive::HiveType() { // Check for SYSTEM hive if(this->PathExists("\\Select") && this->PathExists("\\MountedDevices")) return RegistryHive::eHiveType_SYSTEM; // Check for SOFTWARE hive if(this->PathExists("\\Microsoft\\Windows\\CurrentVersion") && this->PathExists("\\Microsoft\\Windows NT\\CurrentVersion")) return RegistryHive::eHiveType_SOFTWARE; // Check for SAM if(this->PathExists("SAM\\Domains\\Account\\Users")) return RegistryHive::eHiveType_SAM; // Check for SECURITY if(this->PathExists("\\Policy\\Accounts") && this->PathExists("\\Policy\\PolAdtEv")) return RegistryHive::eHiveType_SECURITY; // Check for NTUSER.DAT if(this->PathExists("\\Software\\Microsoft\\Windows\\CurrentVersion")) return RegistryHive::eHiveType_NTUSER; // Unknown hive return RegistryHive::eHiveType_UNKNOWN; } /* * HiveTypeToString */ QString RegistryHive::HiveTypeToString(teHiveType hive_type) { switch(hive_type) { case RegistryHive::eHiveType_SYSTEM: return "SYSTEM"; break; case RegistryHive::eHiveType_SOFTWARE: return "SOFTWARE"; break; case RegistryHive::eHiveType_SAM: return "SAM"; break; case RegistryHive::eHiveType_SECURITY: return "SECURITY"; break; case RegistryHive::eHiveType_NTUSER: return "NTUSER"; break; default: return "UNKNOWN"; } } /* * HasChangesToCommit */ bool RegistryHive::HasChangesToCommit() { return this->has_changes_to_commit; } /* * GetNodes */ QMap RegistryHive::GetNodes(QString path) { hive_node_h parent_node; // Get handle to last node in path if(!this->GetNodeHandle(path,&parent_node)) return QMap(); // Get and return nodes return this->GetNodesHelper(parent_node); } /* * GetNodes */ QMap RegistryHive::GetNodes(int parent_node) { if(parent_node==0) { this->SetError(tr("Invalid parent node handle specified!")); return QMap(); } // Get and return nodes return this->GetNodesHelper(parent_node); } /* * GetKeys */ QMap RegistryHive::GetKeys(QString path) { hive_node_h parent_node; // Get handle to last node in path if(!this->GetNodeHandle(path,&parent_node)) return QMap(); // Get and return keys return this->GetKeysHelper(parent_node); } /* * GetKeys */ QMap RegistryHive::GetKeys(int parent_node) { if(parent_node==0) { this->SetError(tr("Invalid parent node handle specified!")); return QMap(); } // Get and return keys return this->GetKeysHelper(parent_node); } /* * GetKeyName */ bool RegistryHive::GetKeyName(int hive_key, QString &key_name) { char *buf; if(!this->is_hive_open) { this->SetError(tr("Need to operate on an open hive!")); return false; } buf=hivex_value_key(this->p_hive,(hive_value_h)hive_key); if(buf==NULL) { this->SetError(tr("Unable to get key name for key '%1'").arg(hive_key)); return false; } key_name=QString(buf); free(buf); return true; } /* * GetKeyValue */ QByteArray RegistryHive::GetKeyValue(QString path, QString key, int *p_value_type, size_t *p_value_len) { hive_node_h parent_node; hive_value_h hive_key; // Get handle to last node in path if(!this->GetNodeHandle(path,&parent_node)) return QByteArray(); // Get key handle hive_key=hivex_node_get_value(this->p_hive, parent_node,key.toAscii().constData()); if(hive_key==0) { this->SetError(tr("Unable to get key handle!")); *p_value_len=-1; return QByteArray(); } // Get and return key value return this->GetKeyValueHelper(hive_key,p_value_type,p_value_len); } /* * GetKeyValue */ QByteArray RegistryHive::GetKeyValue(int hive_key, int *p_value_type, size_t *p_value_len) { if(hive_key==0) { this->SetError(tr("Invalid key handle specified!")); *p_value_type=-1; return QByteArray(); } // Get and return key value return this->GetKeyValueHelper(hive_key,p_value_type,p_value_len); } /* * GetKeyModTime */ qint64 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 */ qint64 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=""; #define ToHexStr() { \ for(int i=0;i=2 && value.endsWith(QByteArray("\x00\x00",2))) { // Seems to be a unicode string, convert to host endianness and return // TODO: What if it is UTF16-BE?? Thx Billy! QByteArray buf=value; UTF16LETOH(buf.data(),buf.size()); ret=QString().fromUtf16((ushort*)(buf.constData())); } else if(value.endsWith(QByteArray("\x00",1))) { // Seems to be an ansi string ret=QString().fromAscii((char*)value.constData()); } else { // If we can't detect encoding, return string as hex ToHexStr(); } break; case hive_t_REG_MULTI_SZ: // Multiple Windows strings. // I suppose this is always LE encoded! M$ devs really suck! ret=RegistryHive::KeyValueToStringList(value).join("\n"); break; case hive_t_REG_DWORD: // DWORD (32 bit integer), little endian ret=QString("0x%1") .arg(qFromLittleEndian(*(quint32*)value.constData()), 8,16,QChar('0')); break; case hive_t_REG_DWORD_BIG_ENDIAN: // DWORD (32 bit integer), big endian ret=QString("0x%1") .arg(qFromBigEndian(*(quint32*)value.constData()), 8,16,QChar('0')); break; case hive_t_REG_QWORD: // QWORD (64 bit integer). Usually little endian (grrrr). ret=QString("0x%1") .arg(qFromLittleEndian(*(quint64*)value.constData()), 16,16,QChar('0')); break; case hive_t_REG_NONE: case hive_t_REG_BINARY: case hive_t_REG_LINK: case hive_t_REG_RESOURCE_LIST: case hive_t_REG_FULL_RESOURCE_DESCRIPTOR: case hive_t_REG_RESOURCE_REQUIREMENTS_LIST: default: // A key without a value (REG_NONE), a blob of binary (REG_BINARY), a // symbolic link to another part of the registry tree (REG_LINK), a // resource list (REG_RESOURCE_LIST), a resource descriptor // (FULL_RESOURCE_DESCRIPTOR), a resource requirements list // (REG_RESOURCE_REQUIREMENTS_LIST) or something unknown. // All these are converted to hex. ToHexStr(); } #undef ToHexStr return ret; } /* * KeyValueToString */ QString RegistryHive::KeyValueToString(QByteArray key_value, QString format, int offset, int length, bool little_endian) { int remaining_data_len; const char *p_data; QString ret=""; // Calculate how many bytes are remainig after specified offset remaining_data_len=key_value.size()-offset; if(!remaining_data_len>0) { // Nothing to show return QString(); } // Get pointer to data at specified offset p_data=key_value.constData(); p_data+=offset; // Convert value if(format=="int8" && remaining_data_len>=1) { ret=QString("%1").arg(*(qint8*)p_data); } else if(format=="uint8" && remaining_data_len>=1) { ret=QString("%1").arg(*(quint8*)p_data); } else if(format=="int16" && remaining_data_len>=2) { qint16 val; if(little_endian) val=qFromLittleEndian(*(qint16*)p_data); else val=qFromBigEndian(*(qint16*)p_data); ret=QString("%1").arg(val); } else if(format=="uint16" && remaining_data_len>=2) { quint16 val; if(little_endian) val=qFromLittleEndian(*(quint16*)p_data); else val=qFromBigEndian(*(quint16*)p_data); ret=QString("%1").arg(val); } else if(format=="int32" && remaining_data_len>=4) { qint32 val; if(little_endian) val=qFromLittleEndian(*(qint32*)p_data); else val=qFromBigEndian(*(qint32*)p_data); ret=QString("%1").arg(val); } else if(format=="uint32" && remaining_data_len>=4) { quint32 val; if(little_endian) val=qFromLittleEndian(*(quint32*)p_data); else val=qFromBigEndian(*(quint32*)p_data); ret=QString("%1").arg(val); } else if(format=="unixtime" && remaining_data_len>=4) { quint32 val; if(little_endian) val=qFromLittleEndian(*(quint32*)p_data); else val=qFromBigEndian(*(quint32*)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) { qint64 val; if(little_endian) val=qFromLittleEndian(*(qint64*)p_data); else val=qFromBigEndian(*(qint64*)p_data); ret=QString("%1").arg(val); } else if(format=="uint64" && remaining_data_len>=8) { quint64 val; if(little_endian) val=qFromLittleEndian(*(quint64*)p_data); else val=qFromBigEndian(*(quint64*)p_data); ret=QString("%1").arg(val); /* // TODO: Check how one could implement this } else if(format=="unixtime64" && remaining_data_len>=8) { if(*(quint64*)p_data==0) { ret="n/a"; } else { quint64 secs=*(quint64*)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) { quint64 val; if(little_endian) val=qFromLittleEndian(*(quint64*)p_data); else val=qFromBigEndian(*(quint64*)p_data); if(val==0) { ret="n/a"; } else { // TODO: Warn if >32bit QDateTime date_time; date_time.setTimeSpec(Qt::UTC); date_time.setTime_t(RegistryHive::FiletimeToUnixtime(val)); ret=date_time.toString("yyyy/MM/dd hh:mm:ss"); } } else if(format=="ascii") { if(length!=-1) { // User specified how many bytes to convert ret=QString().fromAscii((char*)p_data,length); } else { // User did not specify how many bytes to convert, make sure data is 0 // terminated if(key_value.indexOf("\x00",offset)!=-1) { // Data is 0 terminated ret=QString().fromAscii((char*)p_data); } else { // Data is not 0 terminated, convert all remaining_data_len bytes ret=QString().fromAscii((char*)p_data,remaining_data_len); } } } else if(format=="utf16" && remaining_data_len>=2) { QByteArray buf; if(length!=-1) { // User specified how many bytes to convert buf=key_value.mid(offset,(length%2)==0 ? length : length-1); buf.append("\x00\x00",2); } else { // User did not specify how many bytes to convert, make sure data is // double 0 terminated int null_offset=RegistryHive::FindUnicodeStringEnd(key_value.mid(offset)); if(null_offset!=-1) { // Data is double 0 terminated buf=key_value.mid(offset,null_offset+2); } else { // Data is not double 0 terminated, convert all remaining_data_len bytes buf=key_value.mid(offset, (remaining_data_len%2)==0 ? remaining_data_len : remaining_data_len-1); buf.append("\x00\x00",2); } } // Convert from requested endianness to host if(little_endian) { UTF16LETOH(buf.data(),buf.size()); } else { UTF16BETOH(buf.data(),buf.size()); } ret=QString().fromUtf16((ushort*)buf.constData()); } else { // Unknown variant type or another error // TODO: Maybe return an error return QString(); } return ret; } /* * KeyValueToStringList * * Should only be used for REG_MULTI_SZ values */ QStringList RegistryHive::KeyValueToStringList(QByteArray value, bool little_endian, bool *p_ansi_encoded) { // Try to find value encoding (ANSI vs UNICODE) bool is_ansi; if(value.size()<=2) { // http://blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx // Ansi version of a REG_MULTI_SZ needs to be terminated by 2 \0 chars. // So as long as the byte array has less or equal to 2 chars, it must be // empty. return QStringList(); } else if(value.size()==3) { // Only 3 chars, this can only be an ansi string consisting of 1 char and 2 // \0 to terminate it return QStringList() < strings_it(strings); while(strings_it.hasNext()) { cur_string=strings_it.next(); if(ansi_encoded) { // Ansi encoding, simply append char string and terminating \0 result.append(cur_string.toAscii().constData(),cur_string.size()); result.append("\x00",1); } else { // Unicode encoding // First, convert value to utf16 // TODO: May fail if there is a char that needs more than 16 bit buf=QByteArray((char*)(cur_string.utf16()),cur_string.size()*2); // Then convert to correct endianness if(little_endian) { HTOUTF16LE(buf.data(),buf.size()); } else { HTOUTF16BE(buf.data(),buf.size()); } // 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("0x%1").arg((quint32)value_type,8,16,QChar('0')); } 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 */ quint64 RegistryHive::FiletimeToUnixtime(qint64 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 child_nodes=this->GetNodes(parent_node); if(child_nodes.contains(node_name.toAscii())) { this->SetError(tr("The node '%1\\%2' already exists!") .arg(parent_node_path,node_name)); return 0; } // Add new node hive_node_h new_node=hivex_node_add_child(this->p_hive, parent_node, node_name.toAscii().constData()); if(new_node==0) { this->SetError(tr("Unable to create new node '%1\\%2'!") .arg(parent_node_path,node_name)); return 0; } this->has_changes_to_commit=true; return new_node; } /* * DeleteNode */ bool RegistryHive::DeleteNode(QString node_path) { if(!this->is_hive_writable) return false; // Get node handle to the node that should be deleted hive_node_h node; if(!this->GetNodeHandle(node_path,&node)) { this->SetError(tr("Unable to get node handle for '%1'!") .arg(node_path)); return false; } // Delete node if(hivex_node_delete_child(this->p_hive,node)==-1) { this->SetError(tr("Unable to delete node '%1'!") .arg(node_path)); return false; } this->has_changes_to_commit=true; return true; } /* * AddKey */ int RegistryHive::AddKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value) { if(!this->is_hive_open || !this->is_hive_writable) { this->SetError(tr("Hive has not been opened or opened read-only!")); return false; } return this->SetKey(parent_node_path, key_name, key_value_type, key_value, true); } /* * UpdateKey */ int RegistryHive::UpdateKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value) { if(!this->is_hive_open || !this->is_hive_writable) { this->SetError(tr("Hive has not been opened or opened read-only!")); return false; } return this->SetKey(parent_node_path, key_name, key_value_type, key_value, false); } /* * DeleteKey */ bool RegistryHive::DeleteKey(QString parent_node_path, QString key_name) { if(!this->is_hive_open || !this->is_hive_writable) { this->SetError(tr("Hive has not been opened or opened read-only!")); return false; } // libhivex offers no possibility to delete a single key :-( // As a work around, this function temporarly stores all keys of the specified // node, then deletes them all an re-creates all but the one that should be // deleted. // Get handle to parent node hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { return false; } // Get all child keys hive_value_h *p_keys=hivex_node_values(this->p_hive,parent_node); if(p_keys==NULL) { this->SetError(tr("Unable to enumerate child keys for parent '%1'!") .arg(parent_node_path)); return false; } // Get all child key values except the one that should be deleted int i=0; char *p_name; int node_keys_count=0; hive_set_value *node_keys=NULL; #define FREE_NODE_KEYS() { \ for(int x=0;xp_hive,p_keys[i]); if(p_name==NULL) { this->SetError(tr("Unable to get key name for a child of '%1'!") .arg(parent_node_path)); return false; } if(QString(p_name)!=key_name) { // Current key is not the one that should be deleted, save it // Alloc mem for new hive_set_value struct in node_keys array node_keys=(hive_set_value*)realloc(node_keys, sizeof(hive_set_value)* (node_keys_count+1)); if(node_keys==NULL) { this->SetError(tr("Unable to alloc enough memory for all child keys!")); return false; } // Save key name in hive_set_value struct node_keys[node_keys_count].key=p_name; // Get key value, key value type and key value len and save to // hive_set_value struct node_keys[node_keys_count].value= hivex_value_value(this->p_hive, p_keys[i], &(node_keys[node_keys_count].t), &(node_keys[node_keys_count].len)); if(node_keys[node_keys_count].value==NULL) { this->SetError(tr("Unable to get value for key '%1'!").arg(p_name)); free(p_name); // Free all temporary stored keys FREE_NODE_KEYS(); return false; } node_keys_count++; } else { // Current key is to be deleted, ignore it free(p_name); } i++; } // Save all stored keys to hive, which will discard the one that should be // deleted if(hivex_node_set_values(this->p_hive, parent_node, node_keys_count, node_keys, 0)!=0) { this->SetError(tr("Unable to re-save all child keys! Please discard any " "changes you made and start over. No doing so might end " "in data loss!")); // Free all temporary stored keys FREE_NODE_KEYS(); return false; } // Free all temporary stored keys and return FREE_NODE_KEYS(); #undef FREE_NODE_KEYS this->has_changes_to_commit=true; return true; } /******************************************************************************* * Private ******************************************************************************/ /* * HivexError2String */ QString RegistryHive::HivexError2String(int error) { switch(error) { case ENOTSUP: return QString("Corrupt or unsupported Registry file format."); break; case HIVEX_NO_KEY: return QString("Missing root key."); break; case EINVAL: return QString("Passed an invalid argument to the function."); break; case EFAULT: return QString("Followed a Registry pointer which goes outside the " "registry or outside a registry block."); break; case ELOOP: return QString("Registry contains cycles."); break; case ERANGE: return QString("Field in the registry out of range."); break; case EEXIST: return QString("Registry key already exists."); break; case EROFS: return QString("Tried to write to a registry which is not opened for " "writing."); break; default: return QString("Unknown error."); } } /* * SetError */ void RegistryHive::SetError(QString msg) { this->erro_msg=msg; this->is_error=true; } /* * GetNodeHandle */ bool RegistryHive::GetNodeHandle(QString &path, hive_node_h *p_node) { QStringList nodes; int i=0; // Get root node handle *p_node=hivex_root(this->p_hive); if(*p_node==0) { this->SetError(tr("Unable to get root node!")); return false; } if(path!="\\") { // If we aren't listing the root node, we have to get a handle to the // last node in the path. Split path into nodes nodes=path.split('\\',QString::SkipEmptyParts); // Iterate to the correct parent node for(i=0;ip_hive, *p_node, nodes.value(i).toAscii().constData()); if(*p_node==0) { this->SetError(tr("Unable to find node '%1'!").arg(nodes.value(i))); return false; } } } return true; } /* * GetKeyHandle */ bool RegistryHive::GetKeyHandle(QString &parent_node_path, QString &key_name, hive_value_h *p_key) { // Get handle to parent node hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { return false; } // Get handle to key *p_key=hivex_node_get_value(this->p_hive, parent_node, key_name.toAscii().constData()); if(*p_key==0) { this->SetError(tr("Unable to get handle to key '%1\\%2'!") .arg(parent_node_path,key_name)); return false; } return true; } /* * GetNodesHelper */ QMap RegistryHive::GetNodesHelper(hive_node_h parent_node) { QMap keys; char *p_name; int i=0; // Get child nodes hive_node_h *child_nodes=hivex_node_children(this->p_hive,parent_node); if(child_nodes==NULL) { this->SetError( tr("Unable to enumerate child nodes!")); return QMap(); } // Build result keys.clear(); i=0; while(child_nodes[i]) { p_name=hivex_node_name(this->p_hive,child_nodes[i]); if(p_name==NULL) { this->SetError(tr("Unable to get node name!")); free(child_nodes); return QMap(); } keys.insert(QString(p_name),(int)child_nodes[i]); free(p_name); i++; } free(child_nodes); return keys; } /* * GetKeysHelper */ QMap RegistryHive::GetKeysHelper(hive_node_h parent_node) { QMap keys; char *p_name; int i=0; // Get child keys hive_value_h *p_keys=hivex_node_values(this->p_hive,parent_node); if(p_keys==NULL) { this->SetError( tr("Unable to enumerate child keys!")); return QMap(); } // Build result list keys.clear(); i=0; while(p_keys[i]) { p_name=hivex_value_key(this->p_hive,p_keys[i]); if(p_name==NULL) { this->SetError(tr("Unable to get key name!")); return QMap(); } keys.insert(QString(p_name),p_keys[i]); free(p_name); i++; } free(p_keys); return keys; } /* * GetKeyValueHelper */ QByteArray RegistryHive::GetKeyValueHelper(hive_value_h hive_key, int *p_value_type, size_t *p_value_len) { QByteArray key_value; char *p_key_value; p_key_value=hivex_value_value(this->p_hive, hive_key, (hive_type*)p_value_type, p_value_len); if(p_key_value==NULL) { this->SetError(tr("Unable to get key value!")); *p_value_type=-1; return QByteArray(); } // Feed QByteArray and free p_key_value key_value=QByteArray(p_key_value,*p_value_len); free(p_key_value); return key_value; } /* * PathExists */ bool RegistryHive::PathExists(QString path) { bool ret; hive_node_h node; ret=this->GetNodeHandle(path,&node); if(!ret || this->Error()) { // Clear error and return false this->GetErrorMsg(); return false; } return true; } /* * SetKey */ int RegistryHive::SetKey(QString &parent_node_path, QString &key_name, QString &key_value_type, QByteArray &key_value, bool create_key) { // Get node handle to the node that holds the key to create/update hive_node_h parent_node; if(!this->GetNodeHandle(parent_node_path,&parent_node)) { return 0; } // Make sure key exists if we should update it if(!create_key) { hive_value_h temp_key=hivex_node_get_value(this->p_hive, parent_node, key_name.toAscii().constData()); if(temp_key==0) { this->SetError(tr("Inexisting key '%1\\%2' can't be updated!") .arg(parent_node_path,key_name)); return 0; } } // Create and populate hive_set_value structure hive_set_value key_val; key_val.key=(char*)malloc((sizeof(char)*key_name.toAscii().count())+1); key_val.value=(char*)malloc(sizeof(char)*key_value.size()); if(key_val.key==NULL || key_val.value==NULL) { this->SetError(tr("Unable to alloc memory for hive_set_value struct!")); return 0; } strcpy(key_val.key,key_name.toAscii().constData()); key_val.t=(hive_type)this->StringToKeyValueType(key_value_type); key_val.len=key_value.size(); memcpy(key_val.value,key_value.constData(),key_value.size()); // Create/Update key if(hivex_node_set_value(this->p_hive,parent_node,&key_val,0)!=0) { this->SetError(tr("Unable to update key '%1\\%2'!") .arg(parent_node_path,key_name)); return 0; } // Free the hive_set_value structure free(key_val.key); free(key_val.value); // To make sure everything worked, a hadle to the new key is now requeried // from hive and then returned hive_value_h key; if(!this->GetKeyHandle(parent_node_path,key_name,&key)) { return 0; } this->has_changes_to_commit=true; return key; } /* * FindUnicodeStringEnd */ int RegistryHive::FindUnicodeStringEnd(QByteArray data, int offset) { int end_pos; for(end_pos=offset;end_pos<(data.size()-1);end_pos+=2) { if(*((quint16*)(data.constData()+end_pos))==0) break; } return end_pos<(data.size()-1) ? end_pos : -1; } diff --git a/trunk/registryhive.h b/trunk/registryhive.h index d93117e..5faaa4b 100644 --- a/trunk/registryhive.h +++ b/trunk/registryhive.h @@ -1,132 +1,136 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef REGISTRYHIVE_H #define REGISTRYHIVE_H #include #include -#include +#ifndef HIVEX_STATIC + #include +#else + #include "hivex/lib/hivex.h" +#endif class RegistryHive : public QObject { Q_OBJECT public: typedef enum eHiveType { eHiveType_UNKNOWN=0, eHiveType_SYSTEM, eHiveType_SOFTWARE, eHiveType_SAM, eHiveType_SECURITY, eHiveType_NTUSER } teHiveType; explicit RegistryHive(QObject *p_parent=0); ~RegistryHive(); bool Error(); QString GetErrorMsg(); bool Open(QString file, bool read_only=true); bool Reopen(bool read_only=true); bool CommitChanges(); bool Close(); QString Filename(); teHiveType HiveType(); QString HiveTypeToString(teHiveType hive_type); bool HasChangesToCommit(); QMap GetNodes(QString path="\\"); QMap GetNodes(int parent_node=0); QMap GetKeys(QString path="\\"); QMap GetKeys(int parent_node=0); bool GetKeyName(int hive_key, QString &key_name); QByteArray GetKeyValue(QString path, QString key, int *p_value_type, size_t *p_value_len); QByteArray GetKeyValue(int hive_key, int *p_value_type, size_t *p_value_len); qint64 GetNodeModTime(QString path); qint64 GetNodeModTime(int node); static QString KeyValueToString(QByteArray value, int value_type); static QString KeyValueToString(QByteArray value, QString format, int offset=0, int length=-1, bool little_endian=true); static QStringList KeyValueToStringList(QByteArray value, bool little_endian=true, bool *p_ansi_encoded=NULL); static QByteArray StringListToKeyValue(QStringList strings, bool little_endian=true, bool ansi_encoded=false); static QStringList GetKeyValueTypes(); static QString KeyValueTypeToString(int value_type); static int StringToKeyValueType(QString value_type); static quint64 FiletimeToUnixtime(qint64 filetime); int AddNode(QString parent_node_path, QString node_name); bool DeleteNode(QString node_path); int AddKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value); int UpdateKey(QString parent_node_path, QString key_name, QString key_value_type, QByteArray key_value); bool DeleteKey(QString parent_node_path, QString key_name); private: QString erro_msg; bool is_error; QString hive_file; hive_h *p_hive; bool is_hive_open; bool is_hive_writable; bool has_changes_to_commit; QString HivexError2String(int error); void SetError(QString msg); bool GetNodeHandle(QString &path, hive_node_h *p_node); bool GetKeyHandle(QString &parent_node_path, QString &key_name, hive_value_h *p_key); QMap GetNodesHelper(hive_node_h parent_node); QMap GetKeysHelper(hive_node_h parent_node); QByteArray GetKeyValueHelper(hive_value_h hive_key, int *p_value_type, size_t *p_value_len); bool PathExists(QString path); int SetKey(QString &parent_node_path, QString &key_name, QString &key_value_type, QByteArray &key_value, bool create_key); static int FindUnicodeStringEnd(QByteArray data, int offset=0); }; #endif // REGISTRYHIVE_H diff --git a/trunk/threadsearch.h b/trunk/threadsearch.h index 4afb4d6..6e1c0ff 100644 --- a/trunk/threadsearch.h +++ b/trunk/threadsearch.h @@ -1,73 +1,77 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef THREADSEARCH_H #define THREADSEARCH_H #include #include #include #include #include -#include +#ifndef HIVEX_STATIC + #include +#else + #include "hivex/lib/hivex.h" +#endif class ThreadSearch : public QThread { Q_OBJECT public: enum eMatchType { eMatchType_NodeName=0, eMatchType_KeyName, eMatchType_KeyValue }; ThreadSearch(QObject *p_parent=0); bool Search(QString registry_hive, QList search_keywords, bool search_node_names, bool search_key_names, bool search_key_values, QString search_path="\\"); signals: void SignalFoundMatch(ThreadSearch::eMatchType match_type, QString path, QString key, QString value); protected: void run(); private: QString hive_file; hive_h *h_hive; QList keywords; bool search_nodes; bool search_keys; bool search_values; QString root_path; hive_node_h root_node; void Match(QString path="", hive_node_h node=0); }; #endif // THREADSEARCH_H