diff --git a/Rules/default.json b/Rules/default.json
new file mode 100644
index 0000000..9af559a
--- /dev/null
+++ b/Rules/default.json
@@ -0,0 +1,132 @@
+[
+ {
+ "id": "mimikatz",
+ "description": "Mimikatz can be use to dump credentials, lateral movement and privilege escalation.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)mimikatz\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "lazagne",
+ "description": "LaZagne is an open source application used to retrieve passwords on a local computer.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)lazagne\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "process-hacker",
+ "description": "Process Hacker can be use as a recon tool to identify and kill processes like anti-virus.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)processhacker\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "processxp",
+ "description": "Process Explorer can be use as a recon tool to identify and kill processes like anti-virus.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)processxp(64(a)?)?\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "processdump",
+ "description": "Procdump can be use to dump a process like lsass.exe to extract credentials.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)procdump\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "lsass-dump",
+ "description": "Dumped LSASS.exe process.",
+ "severity": "high",
+ "rule": {
+ "filename": "lsass.dmp|lsass.dump"
+ }
+ },
+ {
+ "id": "cve-2021-34484",
+ "description": "UserProfileSvcEoP is a local privilege escalation tool that exploits cve-2021-34484.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)userprofilesvceop\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "ccleaner",
+ "description": "CCleaner is a disk cleanup tool for temporary junk files, web history, logs and even wiping the disk.",
+ "severity": "high",
+ "rule": {
+ "filename": "(.*)ccleaner\\.exe(-([A-F0-9]{8}).pf)?"
+ }
+ },
+ {
+ "id": "sam-dump",
+ "description": "Dump of SAM database.",
+ "severity": "high",
+ "rule": {
+ "filename": "sam\\.hiv|sam\\.dump|sam\\.dmp"
+ }
+ },
+ {
+ "id": "hash-txt",
+ "description": "Dump of account hashes by Mimikatz.",
+ "severity": "medium",
+ "rule": {
+ "filename": "hash\\.txt"
+ }
+ },
+ {
+ "id": "jaws",
+ "description": "JAWS is PowerShell script designed to identify potential privilege escalation vectors on Windows systems.",
+ "severity": "high",
+ "rule": {
+ "filename": "jaws-enum\\.ps1"
+ }
+ },
+ {
+ "id": "winpeas",
+ "description": "WinPEAS is a script that search for possible paths to escalate privileges on Windows hosts.",
+ "severity": "high",
+ "rule": {
+ "filename": "winpeas(.*)\\.(exe|bat)"
+ }
+ },
+ {
+ "id": "deleted-document",
+ "description": "Documents have been deleted (word, excel, powerpoint, txt, ...)",
+ "severity": "medium",
+ "rule": {
+ "filename": "(.*)(\\.doc|\\.docx|\\.xls|\\.xlsx|\\.ppt|\\.pptx|\\.zip|\\.rar|\\.7z)",
+ "reason": [
+ "+FILE_DELETE"
+ ]
+ }
+ },
+ {
+ "id": "deleted-media",
+ "description": "Media have been deleted (video, audio, images ...)",
+ "severity": "medium",
+ "rule": {
+ "filename": "(.*)(\\.avi|\\.mp4|\\.mkv|\\.m4v|\\.divx|\\.mp3|\\.ogg|\\.flac|\\.jpg|\\.jpeg|\\.gif|\\.png|\\.tiff|\\.psd)",
+ "reason": [
+ "+FILE_DELETE"
+ ]
+ }
+ },
+ {
+ "id": "executable-create",
+ "description": "Excecutable files have been created (exe, dll, sys, bat, ps1 ...)",
+ "severity": "low",
+ "rule": {
+ "filename": "(.*)(\\.exe|\\.dll|\\.sys|\\.vb?|\\.ps1|\\.bat|\\.scr|\\.com)",
+ "reason": [
+ "+FILE_CREATE",
+ "-DATA_EXTEND"
+ ]
+ }
+ }
+]
diff --git a/Sources/Commands/command_bitdecrypt.cpp b/Sources/Commands/command_bitlocker.decrypt.cpp
similarity index 86%
rename from Sources/Commands/command_bitdecrypt.cpp
rename to Sources/Commands/command_bitlocker.decrypt.cpp
index c5ed310..4c06c60 100644
--- a/Sources/Commands/command_bitdecrypt.cpp
+++ b/Sources/Commands/command_bitlocker.decrypt.cpp
@@ -21,8 +21,7 @@
int decrypt_volume(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts)
{
- std::cout << std::setfill('0');
- utils::ui::title("Decrypt Bitlocker Volume from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Decrypt Bitlocker Volume for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
DWORD sector_size = ((PBOOT_SECTOR_FAT32)vol->bootsector())->bytePerSector;
@@ -135,40 +134,43 @@ int decrypt_volume(std::shared_ptr disk, std::shared_ptr vol, std:
namespace commands
{
- namespace bitdecrypt
+ namespace bitlocker
{
- int dispatch(std::shared_ptr opts)
+ namespace decrypt
{
- std::ios_base::fmtflags flag_backup(std::cout.flags());
-
- std::shared_ptr disk = get_disk(opts);
- if (disk != nullptr)
+ int dispatch(std::shared_ptr opts)
{
- std::shared_ptr volume = disk->volumes(opts->volume);
- if (volume != nullptr)
+ std::ios_base::fmtflags flag_backup(std::cout.flags());
+
+ std::shared_ptr disk = get_disk(opts);
+ if (disk != nullptr)
{
- if (opts->output == "")
+ std::shared_ptr volume = disk->volumes(opts->volume);
+ if (volume != nullptr)
{
- invalid_option(opts, "output", opts->output);
+ if (opts->output == "")
+ {
+ invalid_option(opts, "output", opts->output);
+ }
+ if (opts->fvek == "")
+ {
+ invalid_option(opts, "fvek", opts->fvek);
+ }
+ decrypt_volume(disk, volume, opts);
}
- if (opts->fvek == "")
+ else
{
- invalid_option(opts, "fvek", opts->fvek);
+ invalid_option(opts, "volume", opts->volume);
}
- decrypt_volume(disk, volume, opts);
}
else
{
- invalid_option(opts, "volume", opts->volume);
+ invalid_option(opts, "disk", opts->disk);
}
- }
- else
- {
- invalid_option(opts, "disk", opts->disk);
- }
- std::cout.flags(flag_backup);
- return 0;
+ std::cout.flags(flag_backup);
+ return 0;
+ }
}
}
}
\ No newline at end of file
diff --git a/Sources/Commands/command_fve.cpp b/Sources/Commands/command_bitlocker.fve.cpp
similarity index 93%
rename from Sources/Commands/command_fve.cpp
rename to Sources/Commands/command_bitlocker.fve.cpp
index 82ebd3f..358724d 100644
--- a/Sources/Commands/command_fve.cpp
+++ b/Sources/Commands/command_bitlocker.fve.cpp
@@ -198,8 +198,8 @@ std::vector get_fve_entry_values(PFVE_ENTRY entry, const std::strin
return ret;
}
-void print_bitlocker_vbr(std::shared_ptr disk, std::shared_ptr vol, unsigned long block_id) {
- std::cout << std::setfill('0');
+void print_bitlocker_vbr(std::shared_ptr disk, std::shared_ptr vol, unsigned long block_id)
+{
utils::ui::title("FVE Info from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
PBOOT_SECTOR_COMMON pbsc = (PBOOT_SECTOR_COMMON)vol->bootsector();
@@ -243,39 +243,44 @@ void print_bitlocker_vbr(std::shared_ptr disk, std::shared_ptr vol
}
}
-namespace commands {
- namespace fve {
- int dispatch(std::shared_ptr opts)
+namespace commands
+{
+ namespace bitlocker
+ {
+ namespace fve
{
- std::ios_base::fmtflags flag_backup(std::cout.flags());
-
- std::shared_ptr disk = get_disk(opts);
- if (disk != nullptr)
+ int dispatch(std::shared_ptr opts)
{
- std::shared_ptr volume = disk->volumes(opts->volume);
- if (volume != nullptr)
+ std::ios_base::fmtflags flag_backup(std::cout.flags());
+
+ std::shared_ptr disk = get_disk(opts);
+ if (disk != nullptr)
{
- if ((opts->fve_block >= 0) && (opts->fve_block < 3))
+ std::shared_ptr volume = disk->volumes(opts->volume);
+ if (volume != nullptr)
{
- print_bitlocker_vbr(disk, volume, opts->fve_block);
+ if ((opts->fve_block >= 0) && (opts->fve_block < 3))
+ {
+ print_bitlocker_vbr(disk, volume, opts->fve_block);
+ }
+ else
+ {
+ invalid_option(opts, "fve_block", opts->fve_block);
+ }
}
else
{
- invalid_option(opts, "fve_block", opts->fve_block);
+ invalid_option(opts, "volume", opts->volume);
}
}
else
{
- invalid_option(opts, "volume", opts->volume);
+ invalid_option(opts, "disk", opts->disk);
}
- }
- else
- {
- invalid_option(opts, "disk", opts->disk);
- }
- std::cout.flags(flag_backup);
- return 0;
+ std::cout.flags(flag_backup);
+ return 0;
+ }
}
}
}
\ No newline at end of file
diff --git a/Sources/Commands/command_bitlocker.cpp b/Sources/Commands/command_bitlocker.info.cpp
similarity index 98%
rename from Sources/Commands/command_bitlocker.cpp
rename to Sources/Commands/command_bitlocker.info.cpp
index 8cabaeb..7d7fcc6 100644
--- a/Sources/Commands/command_bitlocker.cpp
+++ b/Sources/Commands/command_bitlocker.info.cpp
@@ -23,7 +23,6 @@
void print_test_bitlocker_password(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts)
{
- std::cout << std::setfill('0');
utils::ui::title("Bitlocker Password Test for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
PBOOT_SECTOR_COMMON pbsc = (PBOOT_SECTOR_COMMON)vol->bootsector();
@@ -145,7 +144,6 @@ void print_test_bitlocker_password(std::shared_ptr disk, std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts)
{
- std::cout << std::setfill('0');
utils::ui::title("Bitlocker Recovery Key Test for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
PBOOT_SECTOR_COMMON pbsc = (PBOOT_SECTOR_COMMON)vol->bootsector();
@@ -267,7 +265,6 @@ void print_test_bitlocker_recovery(std::shared_ptr disk, std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts)
{
- std::cout << std::setfill('0');
utils::ui::title("Bitlocker Encryption Key Test for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
PBOOT_SECTOR_COMMON pbsc = (PBOOT_SECTOR_COMMON)vol->bootsector();
@@ -389,8 +386,7 @@ void print_test_bitlocker_bek(std::shared_ptr disk, std::shared_ptr disk, std::shared_ptr vol) {
- std::cout << std::setfill('0');
- utils::ui::title("Bitlocker Info from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Bitlocker Info for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
PBOOT_SECTOR_COMMON pbsc = (PBOOT_SECTOR_COMMON)vol->bootsector();
if (strncmp((char*)pbsc->oemID, "-FVE-FS-", 8) == 0)
@@ -500,7 +496,6 @@ std::string get_guid_from_volume(std::shared_ptr vol)
void list_guid_for_all_disks(std::vector> disks)
{
- std::cout << std::setfill('0');
utils::ui::title("Bitlocker Recovery Key GUIDs");
std::shared_ptr table = std::make_shared();
@@ -584,17 +579,20 @@ namespace commands
{
namespace bitlocker
{
- int dispatch(std::shared_ptr opts)
+ namespace info
{
- if (opts->password != "" || opts->recovery != "" || opts->bek != "")
+ int dispatch(std::shared_ptr opts)
{
- test_password(opts);
- }
- else
- {
- print_bitlocker(opts);
+ if (opts->password != "" || opts->recovery != "" || opts->bek != "")
+ {
+ test_password(opts);
+ }
+ else
+ {
+ print_bitlocker(opts);
+ }
+ return 0;
}
- return 0;
}
}
diff --git a/Sources/Commands/command_efs_backup.cpp b/Sources/Commands/command_efs.backup.cpp
similarity index 98%
rename from Sources/Commands/command_efs_backup.cpp
rename to Sources/Commands/command_efs.backup.cpp
index 86468f8..9a680b2 100644
--- a/Sources/Commands/command_efs_backup.cpp
+++ b/Sources/Commands/command_efs.backup.cpp
@@ -13,8 +13,7 @@ int backup_keys(std::shared_ptr disk, std::shared_ptr vol, std::sh
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Backup certificates and keys from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Backup certificates and keys for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
diff --git a/Sources/Commands/command_efs_certificate.cpp b/Sources/Commands/command_efs.certificate.cpp
similarity index 96%
rename from Sources/Commands/command_efs_certificate.cpp
rename to Sources/Commands/command_efs.certificate.cpp
index d5287b0..35eddbf 100644
--- a/Sources/Commands/command_efs_certificate.cpp
+++ b/Sources/Commands/command_efs.certificate.cpp
@@ -9,8 +9,7 @@ int show_certificate(std::shared_ptr disk, std::shared_ptr vol, st
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Display certificate from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Display certificate for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -137,8 +136,7 @@ int list_certificates(std::shared_ptr disk, std::shared_ptr vol, s
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("List certificates from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("List certificates for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
diff --git a/Sources/Commands/command_efs_decrypt.cpp b/Sources/Commands/command_efs.decrypt.cpp
similarity index 98%
rename from Sources/Commands/command_efs_decrypt.cpp
rename to Sources/Commands/command_efs.decrypt.cpp
index 54ade56..e004b93 100644
--- a/Sources/Commands/command_efs_decrypt.cpp
+++ b/Sources/Commands/command_efs.decrypt.cpp
@@ -101,7 +101,7 @@ int decrypt_file(std::shared_ptr record, std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts)
{
- utils::ui::title("Decrypt EFS file from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Decrypt EFS file for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
diff --git a/Sources/Commands/command_efs_key.cpp b/Sources/Commands/command_efs.key.cpp
similarity index 97%
rename from Sources/Commands/command_efs_key.cpp
rename to Sources/Commands/command_efs.key.cpp
index 27544ad..cc3d2d4 100644
--- a/Sources/Commands/command_efs_key.cpp
+++ b/Sources/Commands/command_efs.key.cpp
@@ -10,8 +10,7 @@ int decrypt_key(std::shared_ptr disk, std::shared_ptr vol, std::sh
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Decrypt key from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Decrypt key for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -210,8 +209,7 @@ int show_key(std::shared_ptr disk, std::shared_ptr vol, std::share
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Display key from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Display key for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -367,8 +365,7 @@ int list_keys(std::shared_ptr disk, std::shared_ptr vol, std::shar
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("List keys from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("List keys for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
diff --git a/Sources/Commands/command_efs_masterkey.cpp b/Sources/Commands/command_efs.masterkey.cpp
similarity index 96%
rename from Sources/Commands/command_efs_masterkey.cpp
rename to Sources/Commands/command_efs.masterkey.cpp
index b9b5700..02a8cc1 100644
--- a/Sources/Commands/command_efs_masterkey.cpp
+++ b/Sources/Commands/command_efs.masterkey.cpp
@@ -9,8 +9,7 @@ int decrypt_masterkey(std::shared_ptr disk, std::shared_ptr vol, s
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Decrypt masterkey from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Decrypt masterkey for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -65,8 +64,7 @@ int show_masterkey(std::shared_ptr disk, std::shared_ptr vol, std:
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Display masterkey from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Display masterkey for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -208,8 +206,7 @@ int list_masterkeys(std::shared_ptr disk, std::shared_ptr vol, std
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("List masterkeys from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("List masterkeys for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
diff --git a/Sources/Commands/command_extract.cpp b/Sources/Commands/command_extract.cpp
index e579427..cd89e4c 100644
--- a/Sources/Commands/command_extract.cpp
+++ b/Sources/Commands/command_extract.cpp
@@ -20,8 +20,7 @@ int extract_file(std::shared_ptr disk, std::shared_ptr vol, std::s
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Extract file from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Extract file for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -31,6 +30,11 @@ int extract_file(std::shared_ptr disk, std::shared_ptr vol, std::s
std::cout << "[-] Record Num : " << record->header()->MFTRecordIndex << " (" << utils::format::hex(record->header()->MFTRecordIndex, true) << ")" << std::endl;
+ if (stream_name != "")
+ {
+ std::cout << "[-] Stream : " << stream_name << std::endl;
+ }
+
std::cout << "[-] Destination : " << opts->output << std::endl;
PMFT_RECORD_ATTRIBUTE_STANDARD_INFORMATION stdinfo = nullptr;
diff --git a/Sources/Commands/command_help.cpp b/Sources/Commands/command_help.cpp
index dc8b217..e27086d 100644
--- a/Sources/Commands/command_help.cpp
+++ b/Sources/Commands/command_help.cpp
@@ -9,7 +9,7 @@
#include
#include
-#define VERSION "1.5"
+#define VERSION "1.6"
void usage(const char* binname)
{
@@ -17,29 +17,31 @@ void usage(const char* binname)
std::cerr << std::endl;
std::cerr << "Version: " << VERSION << " (Build date: " << __DATE__ << " " << __TIME__ << ")" << std::endl << std::endl;
std::cerr << "Commands:" << std::endl;
- std::cerr << " info : list and display physical disks and volumes" << std::endl;
- std::cerr << " mbr : display master boot record" << std::endl;
- std::cerr << " gpt : display GUID partition table" << std::endl;
- std::cerr << " vbr : display volume boot record" << std::endl;
- std::cerr << " mft.record : display master file table" << std::endl;
- std::cerr << " mft.btree : display index btree" << std::endl;
- std::cerr << " extract : extract a file" << std::endl;
- std::cerr << " bitlocker : display bitlocker GUID/status and test password, recovery or BEK file" << std::endl;
- std::cerr << " bitdecrypt : decrypt volume to an image file" << std::endl;
- std::cerr << " fve : display FVE metadata" << std::endl;
- std::cerr << " efs.backup : Export EFS keys from a volume" << std::endl;
- std::cerr << " efs.decrypt : Decrypt EFS encrypted file from backup key" << std::endl;
- std::cerr << " efs.certificate : list, display and export system certificates" << std::endl;
- std::cerr << " efs.key : list, display, decrypt and export private keys" << std::endl;
- std::cerr << " efs.masterkey : list, display and decrypt masterkeys" << std::endl;
- std::cerr << " logfile : dump and parse log file" << std::endl;
- std::cerr << " usn : dump and parse usn journal" << std::endl;
- std::cerr << " shadow : list volume shadow copies" << std::endl;
- std::cerr << " reparse : parse and display reparse points" << std::endl;
- std::cerr << " undelete : find deleted files" << std::endl;
- std::cerr << " shell : start a mini-shell" << std::endl;
- std::cerr << " smart : display SMART data" << std::endl;
- std::cerr << " help : display this message or command help" << std::endl;
+ std::cerr << " info : list and display physical disks and volumes" << std::endl;
+ std::cerr << " mbr : display master boot record" << std::endl;
+ std::cerr << " gpt : display GUID partition table" << std::endl;
+ std::cerr << " vbr : display volume boot record" << std::endl;
+ std::cerr << " mft.btree : display index btree" << std::endl;
+ std::cerr << " mft.dump : dump MFT (raw, csv, json)" << std::endl;
+ std::cerr << " mft.record : display master file table" << std::endl;
+ std::cerr << " extract : extract a file" << std::endl;
+ std::cerr << " bitlocker.info : display bitlocker GUID/status and test password, recovery or BEK file" << std::endl;
+ std::cerr << " bitlocker.decrypt: decrypt volume to an image file" << std::endl;
+ std::cerr << " bitlocker.fve : display FVE metadata" << std::endl;
+ std::cerr << " efs.backup : Export EFS keys from a volume" << std::endl;
+ std::cerr << " efs.decrypt : Decrypt EFS encrypted file from backup key" << std::endl;
+ std::cerr << " efs.certificate : list, display and export system certificates" << std::endl;
+ std::cerr << " efs.key : list, display, decrypt and export private keys" << std::endl;
+ std::cerr << " efs.masterkey : list, display and decrypt masterkeys" << std::endl;
+ std::cerr << " logfile.dump : dump log file (raw, csv, json)" << std::endl;
+ std::cerr << " usn.dump : dump usn journal (raw, csv, json)" << std::endl;
+ std::cerr << " usn.analyze : analyze usn journal with specified rules (csv, json)" << std::endl;
+ std::cerr << " shadow : list volume shadow copies" << std::endl;
+ std::cerr << " reparse : parse and display reparse points" << std::endl;
+ std::cerr << " undelete : find deleted files" << std::endl;
+ std::cerr << " shell : start a mini-shell" << std::endl;
+ std::cerr << " smart : display SMART data" << std::endl;
+ std::cerr << " help : display this message or command help" << std::endl;
std::cerr << std::endl;
std::cerr << "Need help for a command?" << std::endl;
std::cerr << " help [command]" << std::endl;
@@ -122,47 +124,61 @@ void print_help_mft_btree(const char* name)
command_examples(name, "Display Index B-tree for disk 0, volume 2 and from \"c:\\file.bin\"", "mft.btree disk=0 volume=2 from \"c:\\file.bin\"");
}
+void print_help_mft_dump(const char* name)
+{
+ command_header("mft.dump");
+ command_description(name, "mft.dump [disk id] [volume id] [output] (format)", "Dump $MFT for selected disk, volume and inode/path");
+ command_examples(name, "Dump raw $MFT for disk 0, volume 2 to a file", "mft.dump disk=0 volume=2 output=myvolume.mft");
+ command_examples(name, "Parse $MFT for disk 0, volume 2 and output results in a CSV file", "mft.dump disk=0 volume=2 output=my_mft.json format=json");
+}
+
void print_help_bitlocker(const char* name)
{
command_header("bitlocker");
- command_description(name, "bitlocker [disk id] [volume id] (password | recovery | bek)", "Provides Bitlocker information for selected disk, volume");
- command_examples(name, "Display Bitlocker information for disk 2, volume 4", "bitlocker disk=2 volume=4");
- command_examples(name, "Test a password for encrypted for disk 2 and volume 4", "bitlocker disk=0 volume=2 password=123456");
- command_examples(name, "Test a recovery key for encrypted for disk 2 and volume 4", "bitlocker disk=0 volume=2 recovery=123456-234567-345678-456789-567890-678901-789012-890123");
- command_examples(name, "Test a BEK file for encrypted for disk 2 and volume 4", "bitlocker disk=0 volume=2 bek=H:\\3926293F-E661-4417-A36B-B41175B4D862.BEK");
+ command_description(name, "bitlocker.info [disk id] [volume id] (password | recovery | bek)", "Provides Bitlocker information for selected disk, volume");
+ command_examples(name, "Display Bitlocker information for disk 2, volume 4", "bitlocker.info disk=2 volume=4");
+ command_examples(name, "Test a password for encrypted for disk 2 and volume 4", "bitlocker.info disk=0 volume=2 password=123456");
+ command_examples(name, "Test a recovery key for encrypted for disk 2 and volume 4", "bitlocker.info disk=0 volume=2 recovery=123456-234567-345678-456789-567890-678901-789012-890123");
+ command_examples(name, "Test a BEK file for encrypted for disk 2 and volume 4", "bitlocker.info disk=0 volume=2 bek=H:\\3926293F-E661-4417-A36B-B41175B4D862.BEK");
}
void print_help_bitdecrypt(const char* name)
{
- command_header("bitdecrypt");
- command_description(name, "bitdecrypt [disk id] [volume id] [fvek] [output]", "Decrypt Bitlocker encrypted volume to a file using the Full Volume Encryption Key (FVEK)");
- command_examples(name, "Decrypt disk 2, volume 4 to decrypted.img", "bitdecrypt disk=2 volume=4 fvek=21DA18B8434D864D11654FE84AAB1BDDF135DFDE912EBCAD54A6D87CB8EF64AC output=decrypted.img");
+ command_header("bitlocker.decrypt");
+ command_description(name, "bitlocker.decrypt [disk id] [volume id] [fvek] [output]", "Decrypt Bitlocker encrypted volume to a file using the Full Volume Encryption Key (FVEK)");
+ command_examples(name, "Decrypt disk 2, volume 4 to decrypted.img", "bitlocker.decrypt disk=2 volume=4 fvek=21DA18B8434D864D11654FE84AAB1BDDF135DFDE912EBCAD54A6D87CB8EF64AC output=decrypted.img");
}
void print_help_fve(const char* name)
{
- command_header("fve");
- command_description(name, "fve [disk id] [volume id] (block)", "Display FVE metadata information for an Bitlocker encrypted volume");
- command_examples(name, "Display FVE metadata for disk 0, volume 1", "fve disk=0 volume=1");
- command_examples(name, "Display FVE metadata for disk 2, volume 4 and FVE block 2", "fve disk=2 volume=4 fve_block=2");
+ command_header("bitlocker.fve");
+ command_description(name, "bitlocker.fve [disk id] [volume id] (block)", "Display FVE metadata information for an Bitlocker encrypted volume");
+ command_examples(name, "Display FVE metadata for disk 0, volume 1", "bitlocker.fve disk=0 volume=1");
+ command_examples(name, "Display FVE metadata for disk 2, volume 4 and FVE block 2", "bitlocker.fve disk=2 volume=4 fve_block=2");
}
void print_help_logfile(const char* name)
{
- command_header("logfile");
- command_description(name, "logfile [disk id] [volume id] [output] (format)", "Dump or parse the $LogFile of a NTFS volume ");
- command_examples(name, "Dump raw $LogFile for disk 1, volume 2 to log.dat", "logfile disk=1 volume=2 output=log.dat");
- command_examples(name, "Parse logfile for disk 2, volume 4 and output results in csv file", "logfile disk=2 volume=4 output=log.csv format=csv");
+ command_header("logfile.dump");
+ command_description(name, "logfile.dump [disk id] [volume id] [output] (format)", "Dump and parse $LogFile of a NTFS volume (raw, csv, json)");
+ command_examples(name, "Dump raw $LogFile for disk 1, volume 2 to log.dat", "logfile.dump disk=1 volume=2 output=log.dat");
+ command_examples(name, "Parse $LogFile for disk 2, volume 4 and output results in csv file", "logfile.dump disk=2 volume=4 output=log.csv format=csv");
}
-void print_help_usn(const char* name)
+void print_help_usn_dump(const char* name)
{
- command_header("usn");
- command_description(name, "usn [disk id] [volume id] [output] (format)", "Dump or parse the $UsnJrnl of a NTFS volume (raw, csv, json)");
- command_examples(name, "Dump raw $UsnJrnl for disk 1, volume 2 to usn.dat", "usn disk=1 volume=2 output=usn.dat");
- command_examples(name, "Parse usn journal for disk 2, volume 4 and output results in json file", "usn disk=2 volume=4 output=usn.json format=json");
+ command_header("usn.dump");
+ command_description(name, "usn.dump [disk id] [volume id] [output] (format)", "Dump and parse $UsnJrnl of a NTFS volume (raw, csv, json)");
+ command_examples(name, "Dump raw $UsnJrnl for disk 1, volume 2 to usn.dat", "us.dumpn disk=1 volume=2 output=usn.dat");
+ command_examples(name, "Parse $UsnJrnl for disk 2, volume 4 and output results in json file", "usn.dump disk=2 volume=4 output=usn.json format=json");
}
+void print_help_usn_analyze(const char* name)
+{
+ command_header("usn.analyze");
+ command_description(name, "usn.analyze [disk id] [volume id] [rules] [output] (format)", "Parse and filter $UsnJrnl of a NTFS volume with specified rules (csv, json)");
+ command_examples(name, "Parse and filer $UsnJrnl for disk 2, volume 4 and output results in json file", "usn.analyze disk=2 volume=4 rules=myrules.json output=usn.json format=json");
+}
void print_help_efs_masterkey(const char* name)
{
@@ -283,31 +299,33 @@ namespace commands
}
else
{
- if (opts->subcommand == "help") { print_help_help(name.c_str()); return; }
- if (opts->subcommand == "info") { print_help_info(name.c_str()); return; }
- if (opts->subcommand == "mbr") { print_help_mbr(name.c_str()); return; }
- if (opts->subcommand == "gpt") { print_help_gpt(name.c_str()); return; }
- if (opts->subcommand == "vbr") { print_help_vbr(name.c_str()); return; }
- if (opts->subcommand == "mft") { print_help_mft_record(name.c_str()); return; }
- if (opts->subcommand == "btree") { print_help_mft_btree(name.c_str()); return; }
- if (opts->subcommand == "extract") { print_help_extract(name.c_str()); return; }
- if (opts->subcommand == "bitlocker") { print_help_bitlocker(name.c_str()); return; }
if (opts->subcommand == "bitdecrypt") { print_help_bitdecrypt(name.c_str()); return; }
- if (opts->subcommand == "fve") { print_help_fve(name.c_str()); return; }
- if (opts->subcommand == "image") { print_help_image(name.c_str()); return; }
- if (opts->subcommand == "shadow") { print_help_shadow(name.c_str()); return; }
- if (opts->subcommand == "logfile") { print_help_logfile(name.c_str()); return; }
- if (opts->subcommand == "reparse") { print_help_reparse(name.c_str()); return; }
- if (opts->subcommand == "usn") { print_help_usn(name.c_str()); return; }
+ if (opts->subcommand == "bitlocker") { print_help_bitlocker(name.c_str()); return; }
if (opts->subcommand == "efs.backup") { print_help_efs_backup(name.c_str()); return; }
if (opts->subcommand == "efs.certificate") { print_help_efs_certificate(name.c_str()); return; }
if (opts->subcommand == "efs.decrypt") { print_help_efs_decrypt(name.c_str()); return; }
if (opts->subcommand == "efs.key") { print_help_efs_key(name.c_str()); return; }
if (opts->subcommand == "efs.masterkey") { print_help_efs_masterkey(name.c_str()); return; }
- if (opts->subcommand == "undelete") { print_help_undelete(name.c_str()); return; }
+ if (opts->subcommand == "extract") { print_help_extract(name.c_str()); return; }
+ if (opts->subcommand == "fve") { print_help_fve(name.c_str()); return; }
+ if (opts->subcommand == "gpt") { print_help_gpt(name.c_str()); return; }
+ if (opts->subcommand == "help") { print_help_help(name.c_str()); return; }
+ if (opts->subcommand == "image") { print_help_image(name.c_str()); return; }
+ if (opts->subcommand == "info") { print_help_info(name.c_str()); return; }
+ if (opts->subcommand == "logfile.dump") { print_help_logfile(name.c_str()); return; }
+ if (opts->subcommand == "mbr") { print_help_mbr(name.c_str()); return; }
+ if (opts->subcommand == "mft.btree") { print_help_mft_btree(name.c_str()); return; }
+ if (opts->subcommand == "mft.dump") { print_help_mft_dump(name.c_str()); return; }
+ if (opts->subcommand == "mft.record") { print_help_mft_record(name.c_str()); return; }
+ if (opts->subcommand == "reparse") { print_help_reparse(name.c_str()); return; }
+ if (opts->subcommand == "shadow") { print_help_shadow(name.c_str()); return; }
if (opts->subcommand == "shell") { print_help_shell(name.c_str()); return; }
if (opts->subcommand == "smart") { print_help_smart(name.c_str()); return; }
if (opts->subcommand == "streams") { print_help_streams(name.c_str()); return; }
+ if (opts->subcommand == "undelete") { print_help_undelete(name.c_str()); return; }
+ if (opts->subcommand == "usn.analyze") { print_help_usn_analyze(name.c_str()); return; }
+ if (opts->subcommand == "usn.dump") { print_help_usn_dump(name.c_str()); return; }
+ if (opts->subcommand == "vbr") { print_help_vbr(name.c_str()); return; }
usage(name.c_str());
}
diff --git a/Sources/Commands/command_info.cpp b/Sources/Commands/command_info.cpp
index 95316b1..c57db59 100644
--- a/Sources/Commands/command_info.cpp
+++ b/Sources/Commands/command_info.cpp
@@ -106,7 +106,7 @@ void print_hardware_disk(std::shared_ptr disk) {
}
partitions->add_item_line(volume->label());
- partitions->add_item_line(utils::strings::join(volume->mountpoints(), ", "));
+ partitions->add_item_line(utils::strings::join_vec(volume->mountpoints(), ", "));
partitions->add_item_line(volume->filesystem());
partitions->add_item_line(utils::format::hex(volume->offset()));
partitions->add_item_line(utils::format::hex(volume->size()) + " (" + utils::format::size(volume->size()) + ")");
@@ -227,7 +227,7 @@ void print_image_disk(std::shared_ptr disk) {
}
void print_hardware_volume(std::shared_ptr disk, std::shared_ptr vol) {
- utils::ui::title("Info for PhysicalDrive:" + std::to_string(disk->index()) + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Info for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
if (vol->serial_number())
{
@@ -258,7 +258,7 @@ void print_hardware_volume(std::shared_ptr disk, std::shared_ptr v
std::cout << " Mounted : " << (vol->is_mounted() ? "True" : "False");
if (vol->is_mounted())
{
- std::cout << " (" << utils::strings::join(vol->mountpoints(), ", ") << ")";
+ std::cout << " (" << utils::strings::join_vec(vol->mountpoints(), ", ") << ")";
}
std::cout << std::endl;
std::cout << " Bitlocker : " << (vol->bitlocker().bitlocked ? "True" : "False");
diff --git a/Sources/Commands/command_logfile.cpp b/Sources/Commands/command_logfile.dump.cpp
similarity index 66%
rename from Sources/Commands/command_logfile.cpp
rename to Sources/Commands/command_logfile.dump.cpp
index 4f06f32..c7dbec4 100644
--- a/Sources/Commands/command_logfile.cpp
+++ b/Sources/Commands/command_logfile.dump.cpp
@@ -16,6 +16,8 @@
#include
#include
#include
+#include
+#include
void fixup_sequence(PRECORD_PAGE_HEADER prh)
{
@@ -76,48 +78,24 @@ std::vector get_log_clients(PRESTART_AREA ra)
return ret;
}
-void print_record_log(HANDLE outputfile, PRECORD_LOG rl, const std::string& format)
+void _add_record(std::shared_ptr ffile, PRECORD_LOG rl)
{
- std::string to_write;
- if (format == "csv")
- {
- std::ostringstream entry;
- entry << rl->lsn << ",";
- entry << rl->client_previous_lsn << ",";
- entry << rl->client_undo_next_lsn << ",";
- entry << rl->client_id.client_index << ",";
- entry << rl->record_type << ",";
- entry << rl->transaction_id << ",";
- entry << constants::disk::logfile::operation(rl->redo_operation) << ",";
- entry << constants::disk::logfile::operation(rl->undo_operation) << ",";
- entry << rl->mft_cluster_index << ",";
- entry << rl->target_vcn << ",";
- entry << rl->target_lcn;
- entry << std::endl;
- to_write = entry.str();
- }
- if (format == "json")
- {
- nlohmann::json j;
- j["lsn"] = rl->lsn;
- j["previous_lsn"] = rl->client_previous_lsn;
- j["undonext_lsn"] = rl->client_undo_next_lsn;
- j["client_id"] = rl->client_id.client_index;
- j["record_type"] = rl->record_type;
- j["transaction_id"] = rl->transaction_id;
- j["redo_op"] = constants::disk::logfile::operation(rl->redo_operation);
- j["undo_op"] = constants::disk::logfile::operation(rl->undo_operation);
- j["mft_index"] = rl->mft_cluster_index;
- j["target_vcn"] = rl->target_vcn;
- j["target_lcn"] = rl->target_lcn;
- to_write = j.dump() + ",\n";
- }
-
- DWORD written = 0;
- DWORD write_size = 0;
- if (!FAILED(SizeTToDWord(to_write.size(), &write_size))) WriteFile(outputfile, to_write.c_str(), write_size, &written, NULL);
+ ffile->add_item(rl->lsn);
+ ffile->add_item(rl->client_previous_lsn);
+ ffile->add_item(rl->client_undo_next_lsn);
+ ffile->add_item(rl->client_id.client_index);
+ ffile->add_item(rl->record_type);
+ ffile->add_item(rl->transaction_id);
+ ffile->add_item(constants::disk::logfile::operation(rl->redo_operation));
+ ffile->add_item(constants::disk::logfile::operation(rl->undo_operation));
+ ffile->add_item(rl->mft_cluster_index);
+ ffile->add_item(rl->target_vcn);
+ ffile->add_item(rl->target_lcn);
+
+ ffile->new_line();
}
+
int print_logfile_records(std::shared_ptr disk, std::shared_ptr vol, const std::string& format, std::string output)
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
@@ -134,46 +112,41 @@ int print_logfile_records(std::shared_ptr disk, std::shared_ptr vo
ULONG64 total_size = record->datasize();
std::cout << "[+] $LogFile size : " << utils::format::size(total_size) << std::endl;
- HANDLE houtput = CreateFileA(output.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
- if (houtput == INVALID_HANDLE_VALUE)
- {
- std::cout << "[!] Failed to create output file" << std::endl;
- return 1;
- }
+ std::cout << "[+] Creating " << output << std::endl;
if (format == "raw")
{
- ULONG64 processed_count = 0;
- for (auto& block : record->process_data())
- {
- DWORD written = 0;
- WriteFile(houtput, block.first, block.second, &written, NULL);
- std::cout << "\r[+] Processed data size : " << std::to_string(processed_count) << " (" << std::to_string(100 * processed_count / total_size) << "%)";
- processed_count += block.second;
- }
- }
- else if (format == "csv" || format == "json")
- {
- if (format == "csv")
+ HANDLE houtput = CreateFileA(output.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ if (houtput == INVALID_HANDLE_VALUE)
{
- std::string header = "LSN,ClientPreviousLSN,UndoNextLSN,ClientID,RecordType,TransactionID,RedoOperation,UndoOperation,MFTClusterIndex,TargetVCN,TargetLCN\n";
- DWORD written = 0;
- WriteFile(houtput, header.c_str(), static_cast(header.size()), &written, NULL);
+ std::cout << "[!] Failed to create output file" << std::endl;
+ return 1;
}
- if (format == "json")
+
+ ULONG64 processed_size = 0;
+
+ for (auto& block : record->process_data(MFT_ATTRIBUTE_DATA_USN_NAME, 1024 * 1024, true))
{
+ std::cout << "\r[+] Processing data: " << utils::format::size(processed_size) << " ";
+ processed_size += block.second;
+
DWORD written = 0;
- WriteFile(houtput, "[\n", 2, &written, NULL);
+ WriteFile(houtput, block.first, block.second, &written, NULL);
}
+ std::cout << "\r[+] Processing data: " << utils::format::size(processed_size);
- std::shared_ptr> logfile = record->data();
+ CloseHandle(houtput);
- std::cout << "[+] Parsing $LogFile Restart Pages" << std::endl;
+ std::cout << "[+] Closing volume" << std::endl;
+ }
+ else if (format == "json" || format == "csv")
+ {
+ std::shared_ptr> logfile = record->data();
PRESTART_PAGE_HEADER newest_restart_header = find_newest_restart_page(logfile->data());
PRESTART_AREA newest_restart_area = POINTER_ADD(PRESTART_AREA, newest_restart_header, newest_restart_header->restart_area_offset);
- std::cout << "[+] Newest Restart Page LSN : " << std::to_string(newest_restart_area->current_lsn) << std::endl;
+ std::cout << "[-] Newest Restart Page LSN : " << std::to_string(newest_restart_area->current_lsn) << std::endl;
if (newest_restart_area->flags & MFT_LOGFILE_RESTART_AREA_FLAG_VOLUME_CLEANLY_UNMOUNTED)
{
@@ -181,7 +154,7 @@ int print_logfile_records(std::shared_ptr disk, std::shared_ptr vo
}
else
{
- std::cout << "[+] Volume marked as cleanly unmounted" << std::endl;
+ std::cout << "[-] Volume marked as cleanly unmounted" << std::endl;
}
//////////
@@ -189,7 +162,7 @@ int print_logfile_records(std::shared_ptr disk, std::shared_ptr vo
DWORD client_i = 1;
for (auto& client : get_log_clients(newest_restart_area))
{
- std::cout << "[+] Client found : [" << std::to_string(client_i++) << "] " << client << std::endl;
+ std::cout << "[-] Client found : [" << std::to_string(client_i++) << "] " << client << std::endl;
}
//////////
@@ -207,17 +180,44 @@ int print_logfile_records(std::shared_ptr disk, std::shared_ptr vo
record_page_offsets.push_back(prh);
}
- std::cout << "[+] $LogFile Record Page Count : " << std::to_string(record_page_offsets.size()) << std::endl;
+ std::cout << "[-] $LogFile Record Page Count : " << std::to_string(record_page_offsets.size()) << std::endl;
/////////
- std::cout << "[+] Parsing $LogFile Records" << std::endl;
+ std::shared_ptr ffile;
+
+ if (format == "csv")
+ {
+ ffile = std::make_shared(output);
+ }
+ else
+ {
+ ffile = std::make_shared(output);
+ }
+
+ ffile->set_columns(
+ {
+ "LSN",
+ "ClientPreviousLSN",
+ "UndoNextLSN",
+ "ClientID",
+ "RecordType",
+ "TransactionID",
+ "RedoOperation",
+ "UndoOperation",
+ "MFTClusterIndex",
+ "TargetVCN",
+ "TargetLCN"
+ }
+ );
+
+ std::cout << "[-] Parsing $LogFile Records" << std::endl;
Buffer leftover_buffer(8 * 4096);
DWORD leftover_size = 0;
DWORD leftover_missing_size = 0;
-
DWORD processed = 0;
+
for (PRECORD_PAGE_HEADER prh : record_page_offsets)
{
fixup_sequence(prh);
@@ -232,10 +232,10 @@ int print_logfile_records(std::shared_ptr disk, std::shared_ptr vo
if (leftover_missing_size == 0)
{
- PRECORD_LOG prllo = POINTER_ADD(PRECORD_LOG, leftover_buffer.data(), 0);
- print_record_log(houtput, prllo, format);
+ _add_record(ffile, POINTER_ADD(PRECORD_LOG, leftover_buffer.data(), 0));
+
processed++;
- std::cout << "\r[+] $LogFile Record Count : " << std::to_string(processed);
+ std::cout << "\r[-] $LogFile Record Count : " << std::to_string(processed) + " ";
offset += leftover_missing_size;
leftover_size = 0;
@@ -276,32 +276,29 @@ int print_logfile_records(std::shared_ptr disk, std::shared_ptr vo
}
else
{
- print_record_log(houtput, prl, format);
+ _add_record(ffile, prl);
+
processed++;
- std::cout << "\r[+] $LogFile Record Count : " << std::to_string(processed);
+ std::cout << "\r[-] $LogFile Record Count : " << std::to_string(processed) + " ";
}
}
}
- if (format == "json")
- {
- DWORD written = 0;
- WriteFile(houtput, "{}]\n", 2, &written, NULL);
- }
- std::cout << std::endl;
}
else
{
std::cout << "[!] Invalid or missing format" << std::endl;
+ return 2;
}
- CloseHandle(houtput);
- std::cout << "[+] Closing volume" << std::endl;
+ std::cout << std::endl << "[+] Closing volume" << std::endl;
return 0;
}
-namespace commands {
- namespace logfile {
+namespace commands
+{
+ namespace logfile
+ {
int dispatch(std::shared_ptr opts)
{
std::ios_base::fmtflags flag_backup(std::cout.flags());
diff --git a/Sources/Commands/command_mbr.cpp b/Sources/Commands/command_mbr.cpp
index eb0a5f1..2916889 100644
--- a/Sources/Commands/command_mbr.cpp
+++ b/Sources/Commands/command_mbr.cpp
@@ -18,7 +18,7 @@
void print_mbr(std::shared_ptr disk)
{
- utils::ui::title("MBR from " + disk->name());
+ utils::ui::title("MBR for " + disk->name());
PMBR mbr = disk->mbr();
diff --git a/Sources/Commands/command_btree.cpp b/Sources/Commands/command_mft.btree.cpp
similarity index 98%
rename from Sources/Commands/command_btree.cpp
rename to Sources/Commands/command_mft.btree.cpp
index fd5a10d..0d6e9c5 100644
--- a/Sources/Commands/command_btree.cpp
+++ b/Sources/Commands/command_mft.btree.cpp
@@ -31,7 +31,7 @@ int print_btree_info(std::shared_ptr disk, std::shared_ptr vol, st
return 3;
}
- utils::ui::title("B-tree index (inode:" + std::to_string(record->header()->MFTRecordIndex) + ") from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("B-tree index (inode:" + std::to_string(record->header()->MFTRecordIndex) + ") for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::shared_ptr fr_attributes = std::make_shared();
fr_attributes->set_interline(true);
diff --git a/Sources/Commands/command_mft.dump.cpp b/Sources/Commands/command_mft.dump.cpp
new file mode 100644
index 0000000..4bfd79c
--- /dev/null
+++ b/Sources/Commands/command_mft.dump.cpp
@@ -0,0 +1,296 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Commands/commands.h"
+#include "NTFS/ntfs.h"
+#include "NTFS/ntfs_explorer.h"
+#include "Utils/constant_names.h"
+#include "Utils/table.h"
+#include "Utils/utils.h"
+
+#include "options.h"
+#include "Drive/disk.h"
+#include "Drive/volume.h"
+#include
+#include
+#include
+#include
+
+
+int dump_mft(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts, const std::string& format, std::string output)
+{
+ if (!commands::helpers::is_ntfs(disk, vol)) return 1;
+
+ utils::ui::title("MFT Dump (inode:0) for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+
+ std::shared_ptr explorer = std::make_shared(vol);
+
+ std::shared_ptr record = explorer->mft()->record_from_number(0);
+ if (record == nullptr)
+ {
+ std::cout << "[!] Error accessing record 0" << std::endl;
+ return 1;
+ }
+
+ DWORD record_size = explorer->reader()->sizes.record_size;
+ ULONG64 total_size = record->datasize();
+
+ std::cout << "[+] $MFT size : " << utils::format::size(total_size) << std::endl;
+ std::cout << "[-] Record size : " << record_size << std::endl;
+ std::cout << "[-] Record count: " << (total_size / record_size) << std::endl;
+
+ std::cout << "[+] Creating " << output << std::endl;
+
+ if (format == "raw")
+ {
+ HANDLE houtput = CreateFileA(output.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ if (houtput == INVALID_HANDLE_VALUE)
+ {
+ std::cout << "[!] Error creating output file" << std::endl;
+ return 2;
+ }
+
+ ULONG64 processed_size = 0;
+
+ for (auto& block : record->process_data("", 1024 * 1024, true))
+ {
+ std::cout << "\r[+] Processing data: " << utils::format::size(processed_size) << " ";
+ processed_size += block.second;
+
+ DWORD written = 0;
+ WriteFile(houtput, block.first, block.second, &written, NULL);
+ }
+ std::cout << "\r[+] Processing data: " << utils::format::size(processed_size);
+
+ CloseHandle(houtput);
+ }
+ else if (format == "csv" || format == "json")
+ {
+ SYSTEMTIME file_info_creation = { 0 };
+ SYSTEMTIME file_info_access = { 0 };
+ SYSTEMTIME file_info_modification = { 0 };
+ SYSTEMTIME file_info_mft = { 0 };
+ unsigned long long file_info_size = 0;
+ WORD file_info_hardlink_count = 0;
+ std::set file_info_parentids;
+
+ std::shared_ptr ffile;
+
+ if (format == "csv")
+ {
+ ffile = std::make_shared(output);
+ }
+ else
+ {
+ ffile = std::make_shared(output);
+ }
+
+ ffile->set_columns(
+ {
+ "RecordIndex",
+ "InUse",
+ "Type",
+ "Filename",
+ "Ext",
+ "Size",
+ "Parents",
+ "Time_MFT",
+ "Time_Create",
+ "Time_Alter",
+ "Time_Read",
+ "Att_Archive",
+ "Att_Compressed",
+ "Att_Device",
+ "Att_Encrypted",
+ "Att_Hidden",
+ "Att_Normal",
+ "Att_NotIndexed",
+ "Att_Offline",
+ "Att_Readonly",
+ "Att_Reparse",
+ "Att_Sparse",
+ "Att_System",
+ "Att_Temp",
+ "USN",
+ "Hardlinks",
+ "ADS",
+ "ZoneId",
+ "ReferrerUrl",
+ "HostUrl"
+ }
+ );
+
+ std::shared_ptr record = nullptr;
+
+ auto index = 0ULL;
+ for (index = 0ULL; index < (total_size / record_size); index++)
+ {
+ std::cout << "\r[+] Processing data: " << utils::format::size(index * record_size) << " ";
+
+ record = explorer->mft()->record_from_number(index);
+
+ if (record == nullptr || !MFTRecord::is_valid(record->header()))
+ {
+ continue;
+ }
+
+ ffile->add_item(record->header()->MFTRecordIndex);
+ ffile->add_item(record->header()->flag & FILE_RECORD_FLAG_INUSE ? "True" : "False");
+ ffile->add_item(record->header()->flag & FILE_RECORD_FLAG_DIR ? "Directory" : "File");
+
+ auto path = std::filesystem::path(record->filename());
+
+ ffile->add_item(utils::strings::to_utf8(path.filename().generic_wstring()));
+ ffile->add_item(utils::strings::lower(utils::strings::to_utf8(path.extension().generic_wstring())));
+
+ ffile->add_item(record->datasize());
+
+ file_info_parentids.clear();
+ int att_index = 0;
+ PMFT_RECORD_ATTRIBUTE_HEADER pattr = record->attribute_header($FILE_NAME, "", att_index);
+ while (pattr != nullptr)
+ {
+ auto pattr_filename = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_FILENAME, pattr, pattr->Form.Resident.ValueOffset);
+ file_info_parentids.insert(std::to_string(pattr_filename->ParentDirectory.FileRecordNumber));
+ att_index++;
+ if (att_index > 4) break;
+ pattr = record->attribute_header($FILE_NAME, "", att_index);
+ }
+ ffile->add_item(utils::strings::join_set(file_info_parentids, "|"));
+
+ pattr = record->attribute_header($STANDARD_INFORMATION);
+ if (pattr != nullptr)
+ {
+ PMFT_RECORD_ATTRIBUTE_STANDARD_INFORMATION psubattr = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_STANDARD_INFORMATION, pattr, pattr->Form.Resident.ValueOffset);
+ utils::times::ull_to_systemtime(psubattr->MFTTime, &file_info_mft);
+ utils::times::ull_to_systemtime(psubattr->CreateTime, &file_info_creation);
+ utils::times::ull_to_systemtime(psubattr->AlterTime, &file_info_modification);
+ utils::times::ull_to_systemtime(psubattr->ReadTime, &file_info_access);
+
+ ffile->add_item(utils::times::display_systemtime(file_info_mft));
+ ffile->add_item(utils::times::display_systemtime(file_info_creation));
+ ffile->add_item(utils::times::display_systemtime(file_info_modification));
+ ffile->add_item(utils::times::display_systemtime(file_info_access));
+
+ ffile->add_item(psubattr->u.Permission.archive ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.compressed ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.device ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.encrypted ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.hidden ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.normal ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.not_indexed ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.offline ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.readonly ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.reparse ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.sparse ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.system ? "True" : "False");
+ ffile->add_item(psubattr->u.Permission.temp ? "True" : "False");
+
+ ffile->add_item(psubattr->USN);
+ }
+ else
+ {
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+
+ ffile->add_item();
+ }
+
+ ffile->add_item(record->header()->hardLinkCount);
+
+ auto ads = record->ads_names();
+ ffile->add_item(utils::strings::join_vec(ads, "|"));
+
+ if (std::find(ads.begin(), ads.end(), "Zone.Identifier") != ads.end())
+ {
+ std::shared_ptr zi = std::make_shared(record->data("Zone.Identifier"));
+ ffile->add_item(zi->get_value("ZoneId"));
+ ffile->add_item(zi->get_value("ReferrerUrl"));
+ ffile->add_item(zi->get_value("HostUrl"));
+ zi = nullptr;
+ }
+ else
+ {
+ ffile->add_item();
+ ffile->add_item();
+ ffile->add_item();
+ }
+
+ ffile->new_line();
+ }
+ std::cout << "\r[+] Processing data: " << utils::format::size(index * record_size) << " ";
+ }
+ else
+ {
+ std::cout << "[!] Invalid or missing format";
+ }
+
+ std::cout << std::endl << "[+] Closing volume" << std::endl;
+
+ return 0;
+}
+
+namespace commands
+{
+ namespace mft
+ {
+ namespace dump
+ {
+ int dispatch(std::shared_ptr opts)
+ {
+ std::ios_base::fmtflags flag_backup(std::cout.flags());
+
+ std::shared_ptr disk = get_disk(opts);
+ if (disk != nullptr)
+ {
+ std::shared_ptr volume = disk->volumes(opts->volume);
+ if (volume != nullptr)
+ {
+ if (opts->output != "")
+ {
+ if (opts->format == "") opts->format = "raw";
+
+ dump_mft(disk, volume, opts, opts->format, opts->output);
+ }
+ else
+ {
+ invalid_option(opts, "output", opts->output);
+ }
+ }
+ else
+ {
+ invalid_option(opts, "volume", opts->volume);
+ }
+ }
+ else
+ {
+ invalid_option(opts, "disk", opts->disk);
+ }
+
+ std::cout.flags(flag_backup);
+ return 0;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/Commands/command_mft.cpp b/Sources/Commands/command_mft.record.cpp
similarity index 99%
rename from Sources/Commands/command_mft.cpp
rename to Sources/Commands/command_mft.record.cpp
index 7e3369a..4a8b4aa 100644
--- a/Sources/Commands/command_mft.cpp
+++ b/Sources/Commands/command_mft.record.cpp
@@ -850,7 +850,7 @@ int print_mft_info(std::shared_ptr disk, std::shared_ptr vol, std:
std::shared_ptr record = commands::helpers::find_record(explorer, opts);
- utils::ui::title("MFT (inode:" + std::to_string(record->header()->MFTRecordIndex) + ") from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("MFT (inode:" + std::to_string(record->header()->MFTRecordIndex) + ") for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
commands::mft::print_mft_info_details(record, explorer->reader()->sizes.cluster_size);
diff --git a/Sources/Commands/command_reparse.cpp b/Sources/Commands/command_reparse.cpp
index 7656100..1102bd5 100644
--- a/Sources/Commands/command_reparse.cpp
+++ b/Sources/Commands/command_reparse.cpp
@@ -21,7 +21,7 @@ int print_reparse(std::shared_ptr disk, std::shared_ptr vol, const
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- utils::ui::title("Reparse points from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Reparse points for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << vol->name() << std::endl;
diff --git a/Sources/Commands/command_shadow.cpp b/Sources/Commands/command_shadow.cpp
index ffe7823..71a180f 100644
--- a/Sources/Commands/command_shadow.cpp
+++ b/Sources/Commands/command_shadow.cpp
@@ -23,7 +23,7 @@ int print_volumeshadow(std::shared_ptr disk, std::shared_ptr vol)
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
std::cout << std::setfill('0');
- utils::ui::title("Volume Shadow from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Volume Shadow for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << vol->name() << std::endl;
diff --git a/Sources/Commands/command_shell.cpp b/Sources/Commands/command_shell.cpp
index 4f6f810..ad75a69 100644
--- a/Sources/Commands/command_shell.cpp
+++ b/Sources/Commands/command_shell.cpp
@@ -196,7 +196,7 @@ int explorer(std::shared_ptr disk, std::shared_ptr vol)
if ((entry->name_type() == 2) && (win32_named_entries.find(entry->record_number()) != win32_named_entries.end())) continue;
std::shared_ptr entry_rec = explorer->mft()->record_from_number(entry->record_number());
- std::vector ads_names = entry_rec->alternate_data_names();
+ std::vector ads_names = entry_rec->ads_names();
tab->add_item_line(std::to_string(entry_rec->header()->MFTRecordIndex));
if (entry_rec->header()->flag & MFT_RECORD_IS_DIRECTORY)
@@ -257,7 +257,7 @@ int explorer(std::shared_ptr disk, std::shared_ptr vol)
if (stdinfo->u.Permission.system) perms.push_back("Sy");
if (stdinfo->u.Permission.temp) perms.push_back("Tm");
- tab->add_item_line(utils::strings::join(perms, " "));
+ tab->add_item_line(utils::strings::join_vec(perms, " "));
}
else
{
diff --git a/Sources/Commands/command_smart.cpp b/Sources/Commands/command_smart.cpp
index 2a844e4..bc01399 100644
--- a/Sources/Commands/command_smart.cpp
+++ b/Sources/Commands/command_smart.cpp
@@ -130,7 +130,7 @@ std::string read_string(PVOID buffer, DWORD len)
void print_smart_data(std::shared_ptr disk)
{
- utils::ui::title("SMART data from " + disk->name());
+ utils::ui::title("SMART data for " + disk->name());
HANDLE hDisk = CreateFileA(disk->name().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if (hDisk != INVALID_HANDLE_VALUE)
diff --git a/Sources/Commands/command_streams.cpp b/Sources/Commands/command_streams.cpp
index 9b9305f..ea4a6c1 100644
--- a/Sources/Commands/command_streams.cpp
+++ b/Sources/Commands/command_streams.cpp
@@ -20,8 +20,7 @@ int list_streams(std::shared_ptr disk, std::shared_ptr vol, std::s
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Listing streams from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Listing streams for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl;
@@ -30,7 +29,7 @@ int list_streams(std::shared_ptr disk, std::shared_ptr vol, std::s
std::cout << "[-] Record Num : " << record->header()->MFTRecordIndex << " (" << utils::format::hex(record->header()->MFTRecordIndex, true) << ")" << std::endl;
- std::vector ads_names = record->alternate_data_names();
+ std::vector ads_names = record->ads_names();
if (ads_names.size() > 0)
{
diff --git a/Sources/Commands/command_undelete.cpp b/Sources/Commands/command_undelete.cpp
index bc99b90..bf25102 100644
--- a/Sources/Commands/command_undelete.cpp
+++ b/Sources/Commands/command_undelete.cpp
@@ -17,16 +17,6 @@
#include "Utils/table.h"
#include
-bool valid_record(PMFT_RECORD_HEADER ph)
-{
- return (
- (memcmp(ph->signature, "FILE", 4) == 0) &&
- (ph->attributeOffset > 0x30) &&
- (ph->attributeOffset < 0x400) &&
- (POINTER_ADD(PMFT_RECORD_ATTRIBUTE_HEADER, ph, ph->attributeOffset)->TypeCode >= 10) &&
- (POINTER_ADD(PMFT_RECORD_ATTRIBUTE_HEADER, ph, ph->attributeOffset)->TypeCode <= 100)
- );
-}
std::string get_full_path(DWORD index, std::map& index_to_name, std::map& index_to_parent)
{
@@ -101,7 +91,7 @@ int print_deleted_files(std::shared_ptr disk, std::shared_ptr vol,
return 1;
}
- utils::ui::title("Undelete from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Undelete for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << vol->name() << std::endl;
@@ -136,7 +126,7 @@ int print_deleted_files(std::shared_ptr disk, std::shared_ptr vol,
for (offset = 0; offset <= block.second - record_size; offset += record_size)
{
PMFT_RECORD_HEADER pmrh = POINTER_ADD(PMFT_RECORD_HEADER, block.first, offset);
- if (valid_record(pmrh))
+ if (MFTRecord::is_valid(pmrh))
{
std::shared_ptr f = explorer->mft()->record_from_number(pmrh->MFTRecordIndex);
@@ -260,8 +250,7 @@ int extract_deleted_file(std::shared_ptr disk, std::shared_ptr vol
{
if (!commands::helpers::is_ntfs(disk, vol)) return 1;
- std::cout << std::setfill('0');
- utils::ui::title("Extract deleted file from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ utils::ui::title("Extract deleted file for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
std::cout << "[+] Opening " << vol->name() << std::endl;
diff --git a/Sources/Commands/command_usn.analyze.cpp b/Sources/Commands/command_usn.analyze.cpp
new file mode 100644
index 0000000..bcaed14
--- /dev/null
+++ b/Sources/Commands/command_usn.analyze.cpp
@@ -0,0 +1,498 @@
+#include "Drive/disk.h"
+#include "Utils/utils.h"
+#include "options.h"
+#include "Commands/commands.h"
+#include "NTFS/ntfs.h"
+#include "NTFS/ntfs_explorer.h"
+#include "Utils/constant_names.h"
+#include
+#include "Utils/table.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+void process_usn_offline(std::shared_ptr> filebuf, std::string from_file, std::shared_ptr output_file, std::shared_ptr usn_rules, std::map& matches, std::shared_ptr usn_stats, bool full_mode)
+{
+ std::cout << "[-] Mode: " << (full_mode ? "full" : "fast") << std::endl;
+ if (full_mode)
+ {
+ std::cout << "[-] Full mode is disabled for usn dump";
+ full_mode = false;
+ }
+
+ std::cout << "[+] Opening " << from_file << std::endl;
+
+ ULONG64 total_size = filebuf->size();
+ ULONG64 filled_size = 0;
+
+ std::cout << "[-] USN dump filesize: " << utils::format::size(total_size) << std::endl;
+ ULONG64 processed_size = 0;
+ ULONG64 processed_count = 0;
+ ULONG64 matches_count = 0;
+ DWORD cluster_size = 4096;
+ Buffer clusterBuf((DWORD64)2 * cluster_size);
+
+ for (auto& block : filebuf->process_data(cluster_size))
+ {
+ processed_size += block.second;
+
+ std::cout << "\r[+] Processing USN records: " << std::to_string(processed_count) << " (" << utils::format::size(processed_size) << ") - " << matches_count << " matches ";
+
+ if (filled_size)
+ {
+ break;
+ }
+
+ memcpy(clusterBuf.data() + filled_size, block.first, block.second);
+ filled_size += block.second;
+
+ PUSN_RECORD_COMMON_HEADER header = (PUSN_RECORD_COMMON_HEADER)clusterBuf.data();
+ while ((filled_size > 0) && (header->RecordLength <= filled_size))
+ {
+ switch (header->MajorVersion)
+ {
+ case 0:
+ {
+ DWORD i = 0;
+ while ((i < filled_size) && (POINTER_ADD(PWORD, header, i)[0] == 0))
+ {
+ i += 2;
+ }
+ header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, i);
+ filled_size -= i;
+ break;
+ }
+ case 2:
+ {
+ PUSN_RECORD_V2 usn_record = (PUSN_RECORD_V2)header;
+ std::wstring wfilename = std::wstring(usn_record->FileName);
+ wfilename.resize(usn_record->FileNameLength / sizeof(WCHAR));
+ std::string filename = utils::strings::to_utf8(wfilename);
+
+ usn_stats->add_record(filename, usn_record);
+
+ std::vector matched_rules;
+ for (auto& rule : usn_rules->rules())
+ {
+ if (rule->match(filename, usn_record))
+ {
+ matched_rules.push_back(rule->id());
+ matches_count++;
+ if (matches.find(rule->id()) != matches.end())
+ {
+ matches[rule->id()] += 1;
+ }
+ else
+ {
+ matches[rule->id()] = 1;
+ }
+ }
+ }
+ if (!matched_rules.empty())
+ {
+ output_file->add_item(usn_record->MajorVersion);
+ output_file->add_item(usn_record->MinorVersion);
+ output_file->add_item(usn_record->FileReferenceNumber & 0xffffffffffff);
+ output_file->add_item(usn_record->FileReferenceNumber >> 48);
+ output_file->add_item(usn_record->ParentFileReferenceNumber & 0xffffffffffff);
+ output_file->add_item(usn_record->ParentFileReferenceNumber >> 48);
+ output_file->add_item(usn_record->Usn);
+
+ SYSTEMTIME st = { 0 };
+ utils::times::ull_to_local_systemtime(usn_record->TimeStamp.QuadPart, &st);
+ output_file->add_item(utils::times::display_systemtime(st));
+ output_file->add_item(constants::disk::usn::reasons(usn_record->Reason));
+ output_file->add_item((usn_record->SourceInfo));
+ output_file->add_item((usn_record->SecurityId));
+ output_file->add_item(constants::disk::usn::fileattributes(usn_record->FileAttributes));
+
+ output_file->add_item(utils::strings::to_utf8(wfilename));
+
+ output_file->add_item(utils::strings::join_vec(matched_rules, "|"));
+
+ output_file->new_line();
+ }
+
+ processed_count++;
+
+ filled_size -= usn_record->RecordLength;
+ header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, usn_record->RecordLength);
+ break;
+ }
+ default:
+ return;
+ }
+ }
+
+ if (filled_size <= clusterBuf.size())
+ {
+ memcpy(clusterBuf.data(), header, (size_t)filled_size);
+ }
+ }
+ std::cout << "\r[+] Processing USN records: " << std::to_string(processed_count) << " (" << utils::format::size(processed_size) << ") - " << matches_count << " matches ";
+}
+
+void process_usn_live(std::shared_ptr vol, std::shared_ptr ffile, std::shared_ptr usn_rules, std::map& matches, std::shared_ptr usn_stats, bool full_mode)
+{
+ std::cout << "[-] Mode: " << (full_mode ? "full" : "fast") << std::endl;
+ std::shared_ptr path_finder = nullptr;
+ if (full_mode)
+ {
+ path_finder = std::make_shared(vol);
+ std::cout << "[+] " << path_finder->count() << " $MFT records loaded" << std::endl;
+ }
+
+ std::cout << "[+] Opening " << vol->name() << std::endl;
+ std::shared_ptr explorer = std::make_shared(vol);
+
+ std::cout << "[+] Searching for $Extend\\$UsnJrnl" << std::endl;
+
+ std::shared_ptr record = explorer->mft()->record_from_path("\\$Extend\\$UsnJrnl");
+ if (record == nullptr)
+ {
+ std::cout << "[!] Not found" << std::endl;
+ return;
+ }
+
+ std::cout << "[-] Found in file record: " << std::to_string(record->header()->MFTRecordIndex) << std::endl;
+
+
+ ULONG64 total_size = record->datasize(MFT_ATTRIBUTE_DATA_USN_NAME, true);
+ ULONG64 filled_size = 0;
+
+ std::cout << "[-] $J stream size: " << utils::format::size(total_size) << " (could be sparse)" << std::endl;
+ ULONG64 processed_size = 0;
+ ULONG64 processed_count = 0;
+ ULONG64 matches_count = 0;
+ DWORD cluster_size = explorer->reader()->sizes.cluster_size;
+ Buffer clusterBuf((DWORD64)2 * cluster_size);
+
+ for (auto& block : record->process_data(MFT_ATTRIBUTE_DATA_USN_NAME, cluster_size, true))
+ {
+ processed_size += block.second;
+
+ std::cout << "\r[+] Processing USN records: " << std::to_string(processed_count) << " (" << utils::format::size(processed_size) << ") - " << matches_count << " matches ";
+
+ if (filled_size)
+ {
+ break;
+ }
+
+ memcpy(clusterBuf.data() + filled_size, block.first, block.second);
+ filled_size += block.second;
+
+ PUSN_RECORD_COMMON_HEADER header = (PUSN_RECORD_COMMON_HEADER)clusterBuf.data();
+ while ((filled_size > 0) && (header->RecordLength <= filled_size))
+ {
+ switch (header->MajorVersion)
+ {
+ case 0:
+ {
+ DWORD i = 0;
+ while ((i < filled_size) && (POINTER_ADD(PWORD, header, i)[0] == 0))
+ {
+ i += 2;
+ }
+ header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, i);
+ filled_size -= i;
+ break;
+ }
+ case 2:
+ {
+ PUSN_RECORD_V2 usn_record = (PUSN_RECORD_V2)header;
+ std::wstring wfilename = std::wstring(usn_record->FileName);
+ wfilename.resize(usn_record->FileNameLength / sizeof(WCHAR));
+ std::string filename = utils::strings::to_utf8(wfilename);
+
+ usn_stats->add_record(filename, usn_record);
+
+ std::vector matched_rules;
+ for (auto& rule : usn_rules->rules())
+ {
+ if (rule->match(filename, usn_record))
+ {
+ matched_rules.push_back(rule->id());
+ matches_count++;
+ if (matches.find(rule->id()) != matches.end())
+ {
+ matches[rule->id()] += 1;
+ }
+ else
+ {
+ matches[rule->id()] = 1;
+ }
+ }
+ }
+ if (!matched_rules.empty())
+ {
+ ffile->add_item(usn_record->MajorVersion);
+ ffile->add_item(usn_record->MinorVersion);
+ ffile->add_item(usn_record->FileReferenceNumber & 0xffffffffffff);
+ ffile->add_item(usn_record->FileReferenceNumber >> 48);
+ ffile->add_item(usn_record->ParentFileReferenceNumber & 0xffffffffffff);
+ ffile->add_item(usn_record->ParentFileReferenceNumber >> 48);
+ ffile->add_item(usn_record->Usn);
+
+ SYSTEMTIME st = { 0 };
+ utils::times::ull_to_local_systemtime(usn_record->TimeStamp.QuadPart, &st);
+ ffile->add_item(utils::times::display_systemtime(st));
+ ffile->add_item(constants::disk::usn::reasons(usn_record->Reason));
+ ffile->add_item((usn_record->SourceInfo));
+ ffile->add_item((usn_record->SecurityId));
+ ffile->add_item(constants::disk::usn::fileattributes(usn_record->FileAttributes));
+
+ if (full_mode)
+ {
+ ffile->add_item(path_finder->get_file_path(utils::strings::to_utf8(wfilename), usn_record->ParentFileReferenceNumber));
+ }
+ else
+ {
+ ffile->add_item(utils::strings::to_utf8(wfilename));
+ }
+
+ ffile->add_item(utils::strings::join_vec(matched_rules, "|"));
+
+ ffile->new_line();
+ }
+
+ processed_count++;
+
+ filled_size -= usn_record->RecordLength;
+ header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, usn_record->RecordLength);
+ break;
+ }
+ default:
+ return;
+ }
+ }
+
+ if (filled_size <= clusterBuf.size())
+ {
+ memcpy(clusterBuf.data(), header, (size_t)filled_size);
+ }
+ }
+ std::cout << "\r[+] Processing USN records: " << std::to_string(processed_count) << " (" << utils::format::size(processed_size) << ") - " << matches_count << " matches ";
+}
+
+std::shared_ptr load_rules(std::shared_ptr opts)
+{
+ std::cout << "[+] Loading rules from: " << opts->rules << std::endl;
+ return std::make_shared(opts->rules);
+}
+
+int analyze_usn_journal(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr> filebuf, std::shared_ptr opts)
+{
+ if (disk == nullptr && vol == nullptr)
+ {
+ utils::ui::title("Analyze USN journal for " + opts->from);
+ }
+ else
+ {
+ if (!commands::helpers::is_ntfs(disk, vol)) return 1;
+ utils::ui::title("Analyze USN journal for " + disk->name() + " > Volume:" + std::to_string(vol->index()));
+ }
+
+ std::shared_ptr usn_rules = load_rules(opts);
+ if (usn_rules && usn_rules->size() > 0)
+ {
+ std::cout << "[-] " << usn_rules->size() << " rules loaded" << std::endl;
+ }
+ else
+ {
+ std::cout << "[!] No rule loaded. Exiting." << std::endl;
+ return 1;
+ }
+
+ std::cout << "[+] Creating " << opts->output << std::endl;
+
+ std::shared_ptr output_file = std::make_shared(opts->output);
+
+ output_file->set_columns(
+ {
+ "MajorVersion",
+ "MinorVersion",
+ "FileReferenceNumber",
+ "FileReferenceSequenceNumber",
+ "ParentFileReferenceNumber",
+ "ParentFileReferenceSequenceNumber",
+ "Usn",
+ "Timestamp",
+ "Reason",
+ "SourceInfo",
+ "SecurityId",
+ "FileAttributes",
+ "Filename",
+ "Rules"
+ }
+ );
+
+ std::map matches;
+ std::shared_ptr usn_stats = std::make_shared();
+
+ if (filebuf != nullptr)
+ {
+ process_usn_offline(filebuf, opts->from, output_file, usn_rules, matches, usn_stats, opts->mode == "full");
+ }
+ else
+ {
+ process_usn_live(vol, output_file, usn_rules, matches, usn_stats, opts->mode == "full");
+ }
+
+ std::cout << std::endl << "[+] Closing volume" << std::endl;
+
+ std::cout << "[+] Summary:" << std::endl;
+
+ std::shared_ptr summary = std::make_shared();
+ summary->set_margin_left(4);
+
+ summary->add_header_line("Index");
+ summary->add_header_line("Category");
+ summary->add_header_line("Value", utils::ui::TableAlign::RIGHT);
+ summary->add_header_line("%", utils::ui::TableAlign::RIGHT);
+
+ auto count = usn_stats->get_stat("records count");
+
+ int index = 0;
+ std::stringstream ss;
+ for (auto& element : usn_stats->get_stats())
+ {
+ summary->add_item_line(std::to_string(index++));
+ summary->add_item_line(element.first);
+
+ if (usn_stats->is_date(element))
+ {
+ SYSTEMTIME st = { 0 };
+ utils::times::ull_to_local_systemtime(element.second, &st);
+ ss.str(std::string());
+ ss << utils::times::display_systemtime(st);
+ summary->add_item_line(ss.str());
+ }
+ else
+ {
+ summary->add_item_line(std::to_string(element.second));
+ }
+
+ ss.str(std::string());
+ if (usn_stats->is_count(element))
+ {
+ ss << std::fixed << std::setprecision(2) << (100.0 * element.second / count);
+ }
+ summary->add_item_line(ss.str());
+
+ summary->new_line();
+ }
+ summary->render(std::cout);
+
+ if (!matches.empty())
+ {
+ std::cout << "[+] Rules results:" << std::endl;
+
+ std::shared_ptr results = std::make_shared();
+ results->set_margin_left(4);
+
+ results->add_header_line("Index");
+ results->add_header_line("Rule ID");
+ results->add_header_line("Count", utils::ui::TableAlign::RIGHT);
+ results->add_header_line("%", utils::ui::TableAlign::RIGHT);
+
+ int index = 0;
+ std::stringstream ss;
+ for (std::pair element : matches)
+ {
+ results->add_item_line(std::to_string(index++));
+ results->add_item_line(element.first);
+ results->add_item_line(std::to_string(element.second));
+
+ ss.str(std::string());
+ ss << std::fixed << std::setprecision(2) << (100.0 * element.second / count);
+ results->add_item_line(ss.str());
+
+ results->new_line();
+ }
+ results->render(std::cout);
+ }
+ else
+ {
+ std::cout << "[+] No match" << std::endl;
+ }
+
+ return 0;
+}
+
+namespace commands
+{
+ namespace usn
+ {
+ namespace analyze
+ {
+ int dispatch(std::shared_ptr opts)
+ {
+ std::ios_base::fmtflags flag_backup(std::cout.flags());
+
+ if (opts->output != "")
+ {
+ if (opts->rules != "")
+ {
+ if (opts->mode != "full" && opts->mode != "fast")
+ {
+ opts->mode = "fast";
+ }
+ }
+ else
+ {
+ invalid_option(opts, "rules", opts->rules);
+ }
+ }
+ else
+ {
+ invalid_option(opts, "output", opts->output);
+ }
+
+ if (opts->from != "")
+ {
+ std::shared_ptr> filebuf = Buffer::from_file(utils::strings::from_string(opts->from));
+ if (filebuf != nullptr)
+ {
+ analyze_usn_journal(nullptr, nullptr, filebuf, opts);
+ }
+ else
+ {
+ invalid_option(opts, "from", opts->from);
+ }
+ }
+ else
+ {
+ std::shared_ptr disk = get_disk(opts);
+ if (disk != nullptr)
+ {
+ std::shared_ptr volume = disk->volumes(opts->volume);
+ if (volume != nullptr)
+ {
+ analyze_usn_journal(disk, volume, nullptr, opts);
+ }
+ else
+ {
+ invalid_option(opts, "volume", opts->volume);
+ }
+ }
+ else
+ {
+ invalid_option(opts, "disk", opts->disk);
+ }
+ }
+ std::cout.flags(flag_backup);
+ return 0;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/Commands/command_usn.cpp b/Sources/Commands/command_usn.cpp
deleted file mode 100644
index 578c8c3..0000000
--- a/Sources/Commands/command_usn.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-#include "Drive/disk.h"
-#include "Utils/utils.h"
-#include "options.h"
-#include "Commands/commands.h"
-#include "NTFS/ntfs.h"
-#include "NTFS/ntfs_explorer.h"
-#include "Utils/constant_names.h"
-#include "Utils/table.h"
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-int print_usn_journal(std::shared_ptr disk, std::shared_ptr vol, const std::string& format, std::string output)
-{
- if (!commands::helpers::is_ntfs(disk, vol)) return 1;
-
- std::cout << std::setfill('0');
- utils::ui::title("USN Journals from " + disk->name() + " > Volume:" + std::to_string(vol->index()));
-
- DWORD cluster_size = ((PBOOT_SECTOR_NTFS)vol->bootsector())->bytePerSector * ((PBOOT_SECTOR_NTFS)vol->bootsector())->sectorPerCluster;
-
- std::cout << "[+] Opening " << vol->name() << std::endl;
-
- std::shared_ptr explorer = std::make_shared(vol);
-
- std::cout << "[+] Finding $Extend\\$UsnJrnl record" << std::endl;
-
- std::shared_ptr record = explorer->mft()->record_from_path("\\$Extend\\$UsnJrnl");
-
- if (record == nullptr)
- {
- std::cout << "[!] Not found" << std::endl;
- return 2;
- }
-
- std::cout << "[+] Found in file record : " << std::to_string(record->header()->MFTRecordIndex) << std::endl;
-
- PMFT_RECORD_HEADER record_header = record->header();
-
- Buffer clusterBuf((DWORD64)2 * 1024 * 1024);
- ULONG64 total_size = record->datasize(MFT_ATTRIBUTE_DATA_USN_NAME, true);
-
- std::cout << "[+] Virtual $J stream size : " << utils::format::size(total_size) << " (sparse, ~32MiBs on disk by default)" << std::endl;
-
- ULONG64 processed_size = 0;
- ULONG64 processed_count = 0;
- ULONG64 filled_size = 0;
-
- std::cout << "[+] Creating " << output << std::endl;
-
- HANDLE houtput = CreateFileA(output.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
- if (houtput == INVALID_HANDLE_VALUE)
- {
- std::cout << "[!] Error creating output file" << std::endl;
- return 1;
- }
-
- if (format == "raw")
- {
- for (auto& block : record->process_data(MFT_ATTRIBUTE_DATA_USN_NAME, 1024 * 1024, true))
- {
- std::cout << "\r[+] Processing data: " << utils::format::size(processed_count) << " ";
- processed_count += block.second;
-
- DWORD written = 0;
- WriteFile(houtput, block.first, block.second, &written, NULL);
- }
- }
- else if (format == "csv")
- {
- std::string csv_header = "MajorVersion,MinorVersion,FileReferenceNumber,FileReferenceSequenceNumber,ParentFileReferenceNumber,ParentFileReferenceSequenceNumber,Usn,Timestamp,Reason,SourceInfo,SecurityId,FileAttributes,Filename\n";
- DWORD written = 0;
- DWORD read = 0;
- DWORD header_size = 0;
- if (!FAILED(SizeTToDWord(csv_header.size(), &header_size))) WriteFile(houtput, csv_header.c_str(), header_size, &written, NULL);
-
- for (auto& block : record->process_data(MFT_ATTRIBUTE_DATA_USN_NAME, cluster_size, true))
- {
- read += block.second;
-
- memcpy(clusterBuf.data() + filled_size, block.first, block.second);
- filled_size += block.second;
-
- PUSN_RECORD_COMMON_HEADER header = (PUSN_RECORD_COMMON_HEADER)clusterBuf.data();
- while ((filled_size > 0) && (header->RecordLength <= filled_size))
- {
- switch (header->MajorVersion)
- {
- case 0:
- {
- DWORD i = 0;
- while ((i < filled_size) && (((PBYTE)header)[i] == 0)) i++;
- processed_size += i;
- header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, i);
- filled_size -= i;
- break;
- }
- case 2:
- {
- PUSN_RECORD_V2 usn_record = (PUSN_RECORD_V2)header;
- processed_size += usn_record->RecordLength;
- std::wstring a = std::wstring(usn_record->FileName);
- a.resize(usn_record->FileNameLength / sizeof(WCHAR));
-
- std::cout << "\r[+] Processing entry: " << std::to_string(++processed_count) << " (" << utils::format::size(read) << ") ";
-
- std::ostringstream entry;
- entry << usn_record->MajorVersion << ",";
- entry << usn_record->MinorVersion << ",";
- entry << (usn_record->FileReferenceNumber & 0xffffffffffff) << ",";
- entry << (usn_record->FileReferenceNumber >> 48) << ",";
- entry << (usn_record->ParentFileReferenceNumber & 0xffffffffffff) << ",";
- entry << (usn_record->ParentFileReferenceNumber >> 48) << ",";
- entry << usn_record->Usn << ",";
- SYSTEMTIME st = { 0 };
- utils::times::ull_to_local_systemtime(usn_record->TimeStamp.QuadPart, &st);
- entry << utils::times::display_systemtime(st) << ",";
- entry << constants::disk::usn::reasons(usn_record->Reason) << ",";
- entry << std::to_string(usn_record->SourceInfo) << ",";
- entry << std::to_string(usn_record->SecurityId) << ",";
- entry << constants::disk::usn::fileattributes(usn_record->FileAttributes) << ",";
- entry << utils::strings::to_utf8(a) << std::endl;
-
- std::string line = entry.str();
- DWORD write_size = 0;
- if (!FAILED(SizeTToDWord(line.size(), &write_size))) WriteFile(houtput, line.c_str(), write_size, &written, NULL);
-
- filled_size -= usn_record->RecordLength;
- header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, usn_record->RecordLength);
- break;
- }
- default:
- std::cout << std::endl << "[!] Unknown USN record version (" << std::to_string(header->MajorVersion) << ")" << std::endl;
- return 1;
- }
- }
-
- memcpy(clusterBuf.data(), header, (size_t)filled_size);
- }
- }
- else if (format == "json")
- {
- DWORD written = 0;
- DWORD read = 0;
- WriteFile(houtput, "[\n", 2, &written, NULL);
-
- for (auto& block : record->process_data(MFT_ATTRIBUTE_DATA_USN_NAME, cluster_size, true))
- {
- read += block.second;
-
- PUSN_RECORD_COMMON_HEADER header = (PUSN_RECORD_COMMON_HEADER)clusterBuf.data();
-
- memcpy(clusterBuf.data() + filled_size, block.first, block.second);
- filled_size += block.second;
-
- while ((filled_size > 0) && (header->RecordLength <= filled_size))
- {
- switch (header->MajorVersion)
- {
- case 0:
- {
- DWORD i = 0;
- while ((i < filled_size) && (((PBYTE)header)[i] == 0)) i++;
- processed_size += i;
- header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, i);
- filled_size -= i;
- break;
- }
- case 2:
- {
- PUSN_RECORD_V2 usn_record = (PUSN_RECORD_V2)header;
- processed_size += usn_record->RecordLength;
- std::wstring a = std::wstring(usn_record->FileName);
- a.resize(usn_record->FileNameLength / sizeof(WCHAR));
-
- std::cout << "\r[+] Processing entry: " << std::to_string(++processed_count) << " (" << utils::format::size(read) << ") ";
-
- nlohmann::json entry;
- entry["MajorVersion"] = usn_record->MajorVersion;
- entry["MinorVersion"] = usn_record->MinorVersion;
- entry["FileReferenceNumber"] = (usn_record->FileReferenceNumber & 0xffffffffffff);
- entry["FileReferenceSequenceNumber"] = (usn_record->FileReferenceNumber >> 48);
- entry["ParentFileReferenceNumber"] = (usn_record->ParentFileReferenceNumber & 0xffffffffffff);
- entry["ParentFileReferenceSequenceNumber"] = (usn_record->ParentFileReferenceNumber >> 48);
- entry["Usn"] = usn_record->Usn;
- SYSTEMTIME st = { 0 };
- utils::times::ull_to_local_systemtime(usn_record->TimeStamp.QuadPart, &st);
- entry["Timestamp"] = utils::times::display_systemtime(st);
- entry["Reasons"] = constants::disk::usn::reasons(usn_record->Reason);
- entry["SourceInfo"] = usn_record->SourceInfo;
- entry["SecurityId"] = usn_record->SecurityId;
- entry["FileAttributes"] = constants::disk::usn::fileattributes(usn_record->FileAttributes);
- entry["FileName"] = utils::strings::to_utf8(a);
-
- std::string line = entry.dump() + ",\n";
- DWORD line_size = 0;
-
- if (!FAILED(SizeTToDWord(line.size(), &line_size))) WriteFile(houtput, line.c_str(), line_size, &written, NULL);
-
- filled_size -= usn_record->RecordLength;
- header = POINTER_ADD(PUSN_RECORD_COMMON_HEADER, header, usn_record->RecordLength);
- break;
- }
- default:
- std::cout << std::endl << "[!] Unknown USN record version (" << std::to_string(header->MajorVersion) << ")" << std::endl;
- return 1;
- }
- }
-
- memcpy(clusterBuf.data(), header, filled_size);
- }
-
- WriteFile(houtput, "{}]\n", 2, &written, NULL);
- }
- else
- {
- std::cout << "[!] Invalid or missing format" << std::endl;
- }
-
- CloseHandle(houtput);
- std::cout << std::endl << "[+] Closing volume" << std::endl;
-
- return 0;
-}
-
-namespace commands
-{
- namespace usn
- {
- int dispatch(std::shared_ptr opts)
- {
- std::ios_base::fmtflags flag_backup(std::cout.flags());
-
- std::shared_ptr disk = get_disk(opts);
- if (disk != nullptr)
- {
- std::shared_ptr volume = disk->volumes(opts->volume);
- if (volume != nullptr)
- {
- if (opts->output != "")
- {
- if (opts->format == "") opts->format = "raw";
-
- print_usn_journal(disk, volume, opts->format, opts->output);
- }
- else
- {
- invalid_option(opts, "output", opts->output);
- }
- }
- else
- {
- invalid_option(opts, "volume", opts->volume);
- }
- }
- else
- {
- invalid_option(opts, "disk", opts->disk);
- }
-
- std::cout.flags(flag_backup);
- return 0;
- }
- }
-}
\ No newline at end of file
diff --git a/Sources/Commands/command_usn.dump.cpp b/Sources/Commands/command_usn.dump.cpp
new file mode 100644
index 0000000..b46c3f5
--- /dev/null
+++ b/Sources/Commands/command_usn.dump.cpp
@@ -0,0 +1,256 @@
+#include "Drive/disk.h"
+#include "Utils/utils.h"
+#include "options.h"
+#include "Commands/commands.h"
+#include "NTFS/ntfs.h"
+#include "NTFS/ntfs_explorer.h"
+#include "Utils/constant_names.h"
+#include "Utils/table.h"
+#include "Utils/path_finder.h"
+
+#include
+
+#include |