diff --git a/NTFS.vcxproj b/NTFS.vcxproj index 5440712..087cef6 100644 --- a/NTFS.vcxproj +++ b/NTFS.vcxproj @@ -35,8 +35,10 @@ + + @@ -68,6 +70,7 @@ + @@ -93,7 +96,7 @@ - + diff --git a/NTFS.vcxproj.filters b/NTFS.vcxproj.filters index 3248052..87a69ba 100644 --- a/NTFS.vcxproj.filters +++ b/NTFS.vcxproj.filters @@ -168,6 +168,12 @@ Header Files\EFS + + Header Files\EFS + + + Header Files\EFS + @@ -320,15 +326,18 @@ Source Files\Commands - - Header Files\EFS - Source Files\Commands Source Files\Commands + + Source Files\Commands + + + Source Files\EFS + diff --git a/README.md b/README.md index 8ceaa09..cb2dbfb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Language: C++](https://img.shields.io/badge/Language-C%2B%2B-brightgreen.svg?tyle=flat-square)](#) [![x64](https://img.shields.io/badge/Windows-64_bit-0078d7.svg)](#) [![x86](https://img.shields.io/badge/Windows-32_bit-0078d7.svg)](#) -[![v1.3](https://img.shields.io/badge/Version-1.3-ff5733.svg)](#) +[![v1.4](https://img.shields.io/badge/Version-1.4-ff5733.svg)](#) [![Build](https://ci.appveyor.com/api/projects/status/a3cn5dpdv146tdji?svg=true)](https://ci.appveyor.com/project/thewhiteninja/ntfstool) @@ -79,6 +79,7 @@ Options can be entered as decimal or hex number with "0x" prefix (ex: inode). | [bitlocker](#bitlocker) | Display detailed information and hash ($bitlocker$) for all VMK. It is possible to test a password or recovery key. If it is correct, the decrypted VMK and FVEK is displayed. | | [bitdecrypt](#bitdecrypt) | Decrypt a volume to a file using password, recovery key or bek. | | [efs.backup](#efs-backup) | Export EFS keys in PKCS12 (pfx) format. | +| [efs.decrypt](#efs-decrypt) | Decrypt EFS encrypted file using keys in PKCS12 (pfx) format. | | [efs.certificate](#efs-certificate) | List, display and export system certificates (SystemCertificates/My/Certificates). | | [efs.key](#efs-key) | List, display, decrypt and export private keys (Crypto/RSA). | | [efs.masterkey](#efs-masterkey) | List, display and decrypt masterkeys (Protect). | @@ -707,6 +708,34 @@ Current third-party libs: + +### EFS-decrypt + + + +
efs.decrypt efs.decrypt disk=0 volume=4 from=c:\cat.png pfx=z:\my_backup.pfx password=backup output=c:\socute.png
+ + Decrypt EFS file from \\.\PhysicalDrive0 > Volume:4 + --------------------------------------------------- + + [+] Loading PKCS12 input file + [-] KeyID : 86598de9ed5dbdd00aa2ff467ed71f1f28acf61b + [-] Reading record: 13525 + [+] Parsing $EFS streams + [-] 1 data decryption field(s) found + [+] Decrypting FEK + [-] FEK + +----------------------------------------------------------------------------------+ + | Property | Value | + +----------------------------------------------------------------------------------+ + | Entropy | 32 | + | Algorithm | CALG_AES_256 | + | Key (256bits) | 5BBBB8A7F9DD9B9FFFDE9E62370254979F32A9CFFDDB74212A0C1AEECCD75B4A | + +----------------------------------------------------------------------------------+ + [+] Decrypting file + [-] Decrypted file written to c:\socute.png (1.94 MiB) +
+ ### EFS-certificate @@ -810,7 +839,7 @@ Current third-party libs: | | | ff:0c | +----------------------------------------------------------------------------------------------------------------------------+ - + - +
efs.certificate disk=0 volume=4
efs.certificate disk=0 volume=4 inode=0xb5a4 output=mycert format=pem
efs.certificate disk=0 volume=4 inode=0xb5a4 output=mycert
Display certificate from \\.\PhysicalDrive0 > Volume:4 @@ -997,7 +1026,7 @@ Current third-party libs: | | | F39FC063F1F20323E3220229E29FA42D | +----------------------------------------------------------+
efs.key disk=0 volume=4 inode=742107 masterkey=34...eb output=mykey format=pem
efs.key disk=0 volume=4 inode=742107 masterkey=34...eb output=mykey
Decrypt key from \\.\PhysicalDrive0 > Volume:4 diff --git a/Sources/Commands/command_btree.cpp b/Sources/Commands/command_btree.cpp index 578b955..fd5a10d 100644 --- a/Sources/Commands/command_btree.cpp +++ b/Sources/Commands/command_btree.cpp @@ -19,11 +19,7 @@ int print_btree_info(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + if (!commands::helpers::is_ntfs(disk, vol)) return 1; std::shared_ptr explorer = std::make_shared(vol); std::shared_ptr record = commands::helpers::find_record(explorer, opts); diff --git a/Sources/Commands/command_efs_backup.cpp b/Sources/Commands/command_efs_backup.cpp index deca96b..86468f8 100644 --- a/Sources/Commands/command_efs_backup.cpp +++ b/Sources/Commands/command_efs_backup.cpp @@ -6,16 +6,12 @@ #include #include #include -#include +#include int backup_keys(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); @@ -214,7 +210,7 @@ int backup_keys(std::shared_ptr disk, std::shared_ptr vol, std::sh auto decrypted_private_key = keyfile->private_key()->decrypt_with_masterkey(masterkey); if (decrypted_private_key != nullptr) { - std::shared_ptr pkcs12 = std::make_shared(cert, decrypted_private_key); + std::shared_ptr pkcs12 = std::make_shared(cert, decrypted_private_key); if (opts->output == "") { opts->output = cert->hash(); diff --git a/Sources/Commands/command_efs_certificate.cpp b/Sources/Commands/command_efs_certificate.cpp index db8696e..d5287b0 100644 --- a/Sources/Commands/command_efs_certificate.cpp +++ b/Sources/Commands/command_efs_certificate.cpp @@ -5,15 +5,9 @@ #include "EFS/certificate_file.h" -const std::vector format = { "pem" }; - int show_certificate(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); @@ -126,29 +120,13 @@ int show_certificate(std::shared_ptr disk, std::shared_ptr vol, st } else { - if (opts->format == "") - { - opts->format = format[0]; - } - else - { - opts->format = utils::strings::lower(opts->format); - } - - if (std::find(format.begin(), format.end(), opts->format) == format.end()) + if (certificate_file->export_to_PEM(opts->output) == 0) { - std::cerr << "[!] Invalid output format (" << opts->format << ")" << std::endl; + std::cout << "[+] Certificate exported to " << opts->output << ".pem" << std::endl; } else { - if (certificate_file->export_to_PEM(opts->output) == 0) - { - std::cout << "[+] Certificate exported to " << opts->output << ".pem" << std::endl; - } - else - { - std::cerr << "[!] Unable to export the certificate" << std::endl; - } + std::cerr << "[!] Unable to export the certificate" << std::endl; } } @@ -157,11 +135,7 @@ int show_certificate(std::shared_ptr disk, std::shared_ptr vol, st int list_certificates(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); diff --git a/Sources/Commands/command_efs_decrypt.cpp b/Sources/Commands/command_efs_decrypt.cpp new file mode 100644 index 0000000..f6da2a2 --- /dev/null +++ b/Sources/Commands/command_efs_decrypt.cpp @@ -0,0 +1,274 @@ +#include "Commands/commands.h" +#include "Utils/table.h" +#include "Utils/constant_names.h" +#include +#include +#include +#include + +static unsigned char EFS_IV[16] = { 0x12, 0x13, 0x16, 0xe9, 0x7b, 0x65, 0x16, 0x58, 0x61, 0x89, 0x91, 0x44, 0xbe, 0xad, 0x89, 0x19 }; + +std::shared_ptr> decrypt_fek(RSA* private_key, std::shared_ptr> encrypted_fek) +{ + if (encrypted_fek && private_key) + { + std::shared_ptr> decrypted_fek = std::make_shared>(encrypted_fek->size()); + int ret = RSA_private_decrypt(encrypted_fek->size(), encrypted_fek->data(), decrypted_fek->address(), private_key, RSA_PKCS1_PADDING); + if (ret == -1) + { + return nullptr; + } + else + { + decrypted_fek->shrink(ret); + return decrypted_fek; + } + } + return nullptr; +} + +void decrypt_block(std::pair block, std::shared_ptr> fek, DWORD64 index) +{ + EVP_CIPHER_CTX* pctx = EVP_CIPHER_CTX_new(); + unsigned char iv[16]; + + memcpy_s(iv, 16, EFS_IV, 16); + + ((DWORD64*)iv)[0] += (index * 512); + ((DWORD64*)iv)[1] += (index * 512); + + int outl = block.second; + EVP_DecryptInit(pctx, utils::crypto::cryptoapi::encryption_to_evp(fek->data()->Algorithm), fek->data()->Key, iv); + EVP_DecryptUpdate(pctx, block.first, &outl, block.first, block.second); + block.second = outl; + EVP_CIPHER_CTX_free(pctx); +} + +int decrypt_file(std::shared_ptr record, std::shared_ptr> fek, std::shared_ptr opts) +{ + int ret = 0; + if (opts->output == "") + { + opts->output = utils::strings::to_utf8(record->filename() + L".decrypted"); + } + + BIO* output = BIO_new_file(opts->output.c_str(), "wb"); + if (output) + { + DWORD64 written_bytes = 0ULL; + DWORD res_write = 0; + DWORD index_block = 0; + DWORD64 clear_size = record->datasize(); + for (auto data_block : record->process_data("", 512, true)) + { + if (data_block.second == 512) + { + decrypt_block(data_block, fek, index_block); + + int need_to_write = static_cast(min(clear_size - written_bytes, data_block.second)); + if (need_to_write) + { + res_write = BIO_write(output, data_block.first, need_to_write); + if (res_write == 0 || res_write == -1) + { + std::cout << "[!] Failed to write decrypted file" << std::endl; + ret = 3; + } + else + { + written_bytes += res_write; + } + } + } + else + { + std::cerr << "[!] Wrong block size during decryption (" << data_block.second << ")" << std::endl; + ret = 4; + break; + } + index_block++; + } + BIO_free(output); + } + else + { + std::cout << "[!] Failed to create decrypted file" << std::endl; + ret = 1; + } + + return ret; +} + +int load_key_and_decrypt_file(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())); + + if (!commands::helpers::is_ntfs(disk, vol)) return 1; + + std::string keyid; + + std::cout << "[+] Loading PKCS12 input file" << std::endl; + std::shared_ptr pkcs12 = std::make_shared(opts->pfx, opts->password); + if (pkcs12->certificate() && pkcs12->key()) + { + keyid = pkcs12->certificate_hash(); + std::cout << "[-] KeyID : " << keyid << std::endl; + + } + else + { + std::cout << "[!] Failed to load PKCS12 file" << std::endl; + return 2; + } + + std::shared_ptr explorer = std::make_shared(vol); + + std::shared_ptr record = commands::helpers::find_record(explorer, opts); + + PMFT_RECORD_ATTRIBUTE_STANDARD_INFORMATION stdinfo = nullptr; + PMFT_RECORD_ATTRIBUTE_HEADER stdinfo_att = record->attribute_header($STANDARD_INFORMATION); + if (stdinfo_att) + { + stdinfo = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_STANDARD_INFORMATION, stdinfo_att, stdinfo_att->Form.Resident.ValueOffset); + } + if (stdinfo) + { + if (stdinfo->u.Permission.encrypted == 0) + { + std::cout << "[!] File is not encrypted" << std::endl; + return 1; + } + } + + std::cout << "[+] Parsing $EFS streams" << std::endl; + PMFT_RECORD_ATTRIBUTE_HEADER pLogged_utility_attr_header = record->attribute_header($LOGGED_UTILITY_STREAM, "$EFS"); + if (pLogged_utility_attr_header) + { + auto efs_header = record->attribute_data(pLogged_utility_attr_header); + if (efs_header) + { + PMFT_RECORD_ATTRIBUTE_EFS_ARRAY_HEADER efs_arr_header = nullptr; + if (efs_header->data()->OffsetToDDF != 0) + { + efs_arr_header = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_EFS_ARRAY_HEADER, efs_header->data(), efs_header->data()->OffsetToDDF); + std::cout << "[-] " << efs_arr_header->Count << " data decryption field(s) found" << std::endl; + + uint32_t i = 0; + PMFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY_HEADER entry_header = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY_HEADER, efs_arr_header, 4); + while (i < efs_arr_header->Count) + { + auto fek_enc = std::make_shared>(POINTER_ADD(PBYTE, entry_header, entry_header->FEKOffset), entry_header->FEKSize); + fek_enc->reverse_bytes(); + + PMFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY entry = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY, entry_header, entry_header->CredentialHeaderOffset); + PMFT_RECORD_ATTRIBUTE_EFS_DF_CERTIFICATE_THUMBPRINT_HEADER thumprint_header = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_EFS_DF_CERTIFICATE_THUMBPRINT_HEADER, entry, entry->cert_thumbprint_header_offset); + + auto cert_fingerprint = std::make_shared>(POINTER_ADD(PBYTE, thumprint_header, thumprint_header->thumbprint_offset), thumprint_header->thumbprint_size); + std::string ddf_id = utils::strings::to_utf8(cert_fingerprint->to_hex()); + + if (ddf_id == keyid) + { + std::cout << "[+] Decrypting FEK" << std::endl; + std::shared_ptr> decrypted_fek = decrypt_fek(EVP_PKEY_get0_RSA(pkcs12->key()), fek_enc); + if (decrypted_fek) + { + std::cout << "[-] FEK" << std::endl; + std::shared_ptr table = std::make_shared(); + table->set_margin_left(4); + table->add_header_line("Property"); + table->add_header_line("Value"); + + table->add_item_line("Entropy"); + table->add_item_line(std::to_string(decrypted_fek->data()->Entropy)); + table->new_line(); + + table->add_item_line("Algorithm"); + table->add_item_line(constants::efs::enc_algorithm(decrypted_fek->data()->Algorithm)); + table->new_line(); + + table->add_item_line("Key (" + std::to_string((decrypted_fek->size() - 16) * 8) + "bits)"); + table->add_item_line(utils::convert::to_hex(decrypted_fek->data()->Key, decrypted_fek->size() - 16)); + table->new_line(); + + table->render(std::cout); + table = nullptr; + + std::cout << "[+] Decrypting file" << std::endl; + if (!decrypt_file(record, decrypted_fek, opts)) + { + std::cout << "[-] Decrypted file written to " << opts->output << " (" << utils::format::size(record->datasize()) << ")" << std::endl; + } + else + { + std::cerr << "[!] Failed to decrypt the file using FEK" << std::endl; + return 6; + } + } + else + { + std::cerr << "[!] Failed to decrypt encrypted FEK" << std::endl; + return 5; + } + } + else + { + std::cout << "[-] Skipping field: " << i + 1 << " (cert/key not match)" << std::endl; + } + + entry_header = POINTER_ADD(PMFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY_HEADER, entry_header, entry_header->Length); + i++; + } + } + else + { + std::cout << "[!] Empty data decryption field" << std::endl; + return 4; + } + } + } + else + { + std::cout << "[!] Unable to find $EFS stream in file" << std::endl; + return 3; + } + return 0; +} + + +namespace commands +{ + namespace efs + { + namespace decrypt + { + 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->pfx == "") invalid_option(opts, "pfx", opts->pfx); + if (opts->password == "") invalid_option(opts, "password", opts->password); + + load_key_and_decrypt_file(disk, volume, 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_efs_key.cpp b/Sources/Commands/command_efs_key.cpp index 015f628..cf18950 100644 --- a/Sources/Commands/command_efs_key.cpp +++ b/Sources/Commands/command_efs_key.cpp @@ -6,15 +6,9 @@ #include #include -const std::vector format = { "pem" }; - int decrypt_key(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); @@ -22,181 +16,144 @@ int decrypt_key(std::shared_ptr disk, std::shared_ptr vol, std::sh std::cout << "[+] Opening " << (vol->name().empty() ? reinterpret_cast(vol->parent())->name() : vol->name()) << std::endl; std::shared_ptr explorer = std::make_shared(vol); + std::shared_ptr key_file_record = commands::helpers::find_record(explorer, opts); - std::shared_ptr key_file_record = nullptr; - - std::cout << "[+] Reading key file record: "; - if (opts->from != "") - { - std::cout << opts->from << std::endl; - key_file_record = explorer->mft()->record_from_path(opts->from); - } - else + auto data = key_file_record->data(); + std::shared_ptr key_file = std::make_shared(data->data(), data->size()); + if (!key_file->is_loaded()) { - std::cout << opts->inode << std::endl; - key_file_record = explorer->mft()->record_from_number(opts->inode); + std::cerr << "[!] Failed to parse key file from record: " << opts->inode << std::endl; + return 3; } - if (key_file_record == nullptr) - { - std::cerr << "[!] Err: Failed to read record: " << opts->inode << std::endl; - return 2; - } - else + auto key = key_file->private_key(); + if (key) { - auto data = key_file_record->data(); - std::shared_ptr key_file = std::make_shared(data->data(), data->size()); - if (!key_file->is_loaded()) + std::cout << "[-] Key" << std::endl; + std::cout << " Encryption Algorithm : " << constants::efs::enc_algorithm(key->header()->EncryptionAlgorithm) << std::endl; + std::cout << " Hash Algorithm : " << constants::efs::hash_algorithm(key->header()->HashAlgorithm) << std::endl; + std::cout << " Salt : " << utils::convert::to_hex(key->salt()->data(), key->salt()->size()) << std::endl; + + std::cout << "[+] Decrypting key" << std::endl; + std::shared_ptr res = key->decrypt_with_masterkey(opts->masterkey); + if (res == nullptr) { - std::cerr << "[!] Err: Failed to parse key file from record: " << opts->inode << std::endl; - return 3; + std::cout << "[!] Failed to decrypt. Check masterkey." << std::endl; } - - auto key = key_file->private_key(); - if (key) + else { - std::cout << "[-] Key" << std::endl; - std::cout << " Encryption Algorithm : " << constants::efs::enc_algorithm(key->header()->EncryptionAlgorithm) << std::endl; - std::cout << " Hash Algorithm : " << constants::efs::hash_algorithm(key->header()->HashAlgorithm) << std::endl; - std::cout << " Salt : " << utils::convert::to_hex(key->salt()->data(), key->salt()->size()) << std::endl; - - std::cout << "[+] Decrypting key" << std::endl; - std::shared_ptr res = key->decrypt_with_masterkey(opts->masterkey); - if (res == nullptr) - { - std::cout << "[!] Failed to decrypt. Check masterkey." << std::endl; - } - else + if (opts->output != "") { - if (opts->output != "") + if (res->export_public_to_PEM(opts->output) == 0) { - if (opts->format == "") - { - opts->format = format[0]; - } - else - { - opts->format = utils::strings::lower(opts->format); - } - - if (std::find(format.begin(), format.end(), opts->format) == format.end()) - { - std::cerr << "[!] Err: Invalid output format. (" << opts->format << ")" << std::endl; - } - else - { - if (res->export_public_to_PEM(opts->output) == 0) - { - std::cout << "[+] Public key exported to " << opts->output << ".pub.pem" << "." << std::endl; - } - else - { - std::cerr << "[!] Err: Unable to export the public key." << std::endl; - } - if (res->export_private_to_PEM(opts->output) == 0) - { - std::cout << "[+] Private key exported to " << opts->output << ".priv.pem" << "." << std::endl; - } - else - { - std::cerr << "[!] Err: Unable to export the private key." << std::endl; - } - } + std::cout << "[+] Public key exported to " << opts->output << ".pub.pem" << "." << std::endl; } else { + std::cerr << "[!] Unable to export the public key." << std::endl; + } + if (res->export_private_to_PEM(opts->output) == 0) + { + std::cout << "[+] Private key exported to " << opts->output << ".priv.pem" << "." << std::endl; + } + else + { + std::cerr << "[!] Unable to export the private key." << std::endl; + } + } + else + { + std::cout << "[+] Clear key (" << res->header()->Bitsize << "bits):" << std::endl; - std::cout << "[+] Clear key (" << res->header()->Bitsize << "bits):" << std::endl; - - std::shared_ptr tab = std::make_shared(); - tab->set_margin_left(4); - tab->set_interline(true); - tab->add_header_line("Id", utils::ui::TableAlign::RIGHT); - tab->add_header_line("Property"); - tab->add_header_line("Value"); + std::shared_ptr tab = std::make_shared(); + tab->set_margin_left(4); + tab->set_interline(true); + tab->add_header_line("Id", utils::ui::TableAlign::RIGHT); + tab->add_header_line("Property"); + tab->add_header_line("Value"); - int i = 0; - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Magic"); - std::string magic_str = { ((char*)(&res->header()->Magic))[0],((char*)(&res->header()->Magic))[1],((char*)(&res->header()->Magic))[2],((char*)(&res->header()->Magic))[3] }; - tab->add_item_line(magic_str); + int i = 0; + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Magic"); + std::string magic_str = { ((char*)(&res->header()->Magic))[0],((char*)(&res->header()->Magic))[1],((char*)(&res->header()->Magic))[2],((char*)(&res->header()->Magic))[3] }; + tab->add_item_line(magic_str); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Bitsize"); - tab->add_item_line(std::to_string(res->header()->Bitsize)); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Bitsize"); + tab->add_item_line(std::to_string(res->header()->Bitsize)); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Permissions"); - tab->add_item_multiline(constants::efs::permissions(res->header()->Permissions)); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Permissions"); + tab->add_item_multiline(constants::efs::permissions(res->header()->Permissions)); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Exponent"); - tab->add_item_line(std::to_string(res->header()->Exponent)); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Exponent"); + tab->add_item_line(std::to_string(res->header()->Exponent)); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Modulus"); - tab->add_item_line(utils::convert::to_hex(res->modulus()->data(), res->modulus()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Modulus"); + tab->add_item_line(utils::convert::to_hex(res->modulus()->data(), res->modulus()->size())); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Prime1"); - tab->add_item_line(utils::convert::to_hex(res->prime1()->data(), res->prime1()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Prime1"); + tab->add_item_line(utils::convert::to_hex(res->prime1()->data(), res->prime1()->size())); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Prime2"); - tab->add_item_line(utils::convert::to_hex(res->prime2()->data(), res->prime2()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Prime2"); + tab->add_item_line(utils::convert::to_hex(res->prime2()->data(), res->prime2()->size())); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Exponent1"); - tab->add_item_line(utils::convert::to_hex(res->exponent1()->data(), res->exponent1()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Exponent1"); + tab->add_item_line(utils::convert::to_hex(res->exponent1()->data(), res->exponent1()->size())); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Exponent2"); - tab->add_item_line(utils::convert::to_hex(res->exponent2()->data(), res->exponent2()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Exponent2"); + tab->add_item_line(utils::convert::to_hex(res->exponent2()->data(), res->exponent2()->size())); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Coefficient"); - tab->add_item_line(utils::convert::to_hex(res->coefficient()->data(), res->coefficient()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Coefficient"); + tab->add_item_line(utils::convert::to_hex(res->coefficient()->data(), res->coefficient()->size())); - tab->new_line(); + tab->new_line(); - tab->add_item_line(std::to_string(i++)); - tab->add_item_line("Private Exponent"); - tab->add_item_line(utils::convert::to_hex(res->private_exponent()->data(), res->private_exponent()->size())); + tab->add_item_line(std::to_string(i++)); + tab->add_item_line("Private Exponent"); + tab->add_item_line(utils::convert::to_hex(res->private_exponent()->data(), res->private_exponent()->size())); - tab->new_line(); + tab->new_line(); - tab->render(std::cout); - } + tab->render(std::cout); } } - else - { - std::cerr << "[!] Err: No key in specified file." << std::endl; - return 3; - } - - return 0; } + else + { + std::cerr << "[!] No key in specified file." << std::endl; + return 3; + } + + return 0; } + std::vector print_encrypted_private_key(std::shared_ptr private_key) { std::vector cell = @@ -237,11 +194,7 @@ std::vector print_encrypted_private_key(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); @@ -255,7 +208,7 @@ int show_key(std::shared_ptr disk, std::shared_ptr vol, std::share std::shared_ptr key_file = std::make_shared(data->data(), data->size()); if (!key_file->is_loaded()) { - std::cerr << "[!] Err: Failed to parse key file from record: " << opts->inode << std::endl; + std::cerr << "[!] Failed to parse key file from record: " << opts->inode << std::endl; return 3; } @@ -396,11 +349,7 @@ int show_key(std::shared_ptr disk, std::shared_ptr vol, std::share int list_keys(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); diff --git a/Sources/Commands/command_efs_masterkey.cpp b/Sources/Commands/command_efs_masterkey.cpp index 2c5a400..b9b5700 100644 --- a/Sources/Commands/command_efs_masterkey.cpp +++ b/Sources/Commands/command_efs_masterkey.cpp @@ -7,11 +7,7 @@ int decrypt_masterkey(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); @@ -25,7 +21,7 @@ int decrypt_masterkey(std::shared_ptr disk, std::shared_ptr vol, s std::shared_ptr masterkey_file = std::make_shared(data->data(), data->size()); if (!masterkey_file->is_loaded()) { - std::cerr << "[!] Err: Failed to parse masterkey file from record: " << opts->inode << std::endl; + std::cerr << "[!] Failed to parse masterkey file from record: " << opts->inode << std::endl; return 3; } @@ -57,7 +53,7 @@ int decrypt_masterkey(std::shared_ptr disk, std::shared_ptr vol, s } else { - std::cerr << "[!] Err: No masterkey in specified file." << std::endl; + std::cerr << "[!] No masterkey in specified file." << std::endl; return 3; } @@ -67,11 +63,7 @@ int decrypt_masterkey(std::shared_ptr disk, std::shared_ptr vol, s int show_masterkey(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); @@ -85,7 +77,7 @@ int show_masterkey(std::shared_ptr disk, std::shared_ptr vol, std: std::shared_ptr masterkey_file = std::make_shared(data->data(), data->size()); if (!masterkey_file->is_loaded()) { - std::cerr << "[!] Err: Failed to parse masterkey file from record: " << opts->inode << std::endl; + std::cerr << "[!] Failed to parse masterkey file from record: " << opts->inode << std::endl; return 3; } @@ -214,11 +206,7 @@ int show_masterkey(std::shared_ptr disk, std::shared_ptr vol, std: int list_masterkeys(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); diff --git a/Sources/Commands/command_extract.cpp b/Sources/Commands/command_extract.cpp index 116358b..f8ca2fb 100644 --- a/Sources/Commands/command_extract.cpp +++ b/Sources/Commands/command_extract.cpp @@ -18,11 +18,7 @@ int extract_file(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); diff --git a/Sources/Commands/command_help.cpp b/Sources/Commands/command_help.cpp index 69e199c..3db13ea 100644 --- a/Sources/Commands/command_help.cpp +++ b/Sources/Commands/command_help.cpp @@ -9,7 +9,7 @@ #include #include -#define VERSION "1.35" +#define VERSION "1.4" void usage(const char* binname) { @@ -28,6 +28,7 @@ void usage(const char* binname) 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; @@ -181,6 +182,14 @@ void print_help_efs_backup(const char* name) command_examples(name, "Export EFS keys for disk 1, volume 2 using password:123456", "efs.backup disk=1 volume=2 password=123456"); } +void print_help_efs_decrypt(const char* name) +{ + command_header("efs.decrypt"); + command_description(name, "efs.decrypt [disk id] [volume id] [inode|from] [pfx] [password] (output)", "Decrypt file from inode or path using pfx archive (protected by password) to output"); + command_examples(name, "Decrypt EFS file inode:1234 for disk 1 and volume 2 using backup.pfx protected by password 123456 to mydecryptedfile", + "efs.decrypt disk=1 volume=2 inode=1234 pfx=backup.pfx password=123456 output=mydecryptedfile"); +} + void print_help_efs_key(const char* name) { command_header("efs.key"); @@ -188,7 +197,7 @@ void print_help_efs_key(const char* name) command_examples(name, "List keys for disk 1, volume 2", "efs.key disk=1 volume=2"); command_examples(name, "Display a key for disk 1, volume 2 and inode 0x1337", "efs.key disk=1 volume=2 inode=0x1337"); command_examples(name, "Decrypt a key for inode 0x1337 with masterkey", "efs.key disk=1 volume=2 inode=0x1337 masterkey=DEADBEEF123...321"); - command_examples(name, "Export a key to mykey.pem (format in [pem])", "efs.key disk=1 volume=2 inode=0x1337 masterkey=DEADBEEF123...321 output=mykey format=pem"); + command_examples(name, "Export a key to mykey.pem", "efs.key disk=1 volume=2 inode=0x1337 masterkey=DEADBEEF123...321 output=mykey"); } void print_help_efs_certificate(const char* name) @@ -197,7 +206,7 @@ void print_help_efs_certificate(const char* name) command_description(name, "efs.certificate [disk id] [volume id] [inode|from] (output) (format)", "List and display certificate on a volume"); command_examples(name, "List certificates for disk 1, volume 2", "efs.certificate disk=1 volume=2"); command_examples(name, "Display a certificate for disk 1, volume 2 and inode 0x1337", "efs.certificate disk=1 volume=2 inode=0x1337"); - command_examples(name, "Export a certificate to mycert.pem (format in [pem])", "efs.certificate disk=1 volume=2 inode=0x1337 output=mycert format=pem"); + command_examples(name, "Export a certificate to mycert.pem", "efs.certificate disk=1 volume=2 inode=0x1337 output=mycert"); } void print_help_extract(const char* name) @@ -291,10 +300,11 @@ namespace commands 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 == "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 == "efs.backup") { print_help_efs_backup(name.c_str()); return; } if (opts->subcommand == "undelete") { print_help_undelete(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; } diff --git a/Sources/Commands/command_logfile.cpp b/Sources/Commands/command_logfile.cpp index 127d069..4f06f32 100644 --- a/Sources/Commands/command_logfile.cpp +++ b/Sources/Commands/command_logfile.cpp @@ -118,12 +118,9 @@ void print_record_log(HANDLE outputfile, PRECORD_LOG rl, const std::string& form if (!FAILED(SizeTToDWord(to_write.size(), &write_size))) WriteFile(outputfile, to_write.c_str(), write_size, &written, NULL); } -int print_logfile_records(std::shared_ptr disk, std::shared_ptr vol, const std::string& format, std::string output) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } +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; utils::ui::title("LogFile from " + disk->name() + " > Volume:" + std::to_string(vol->index())); diff --git a/Sources/Commands/command_mft.cpp b/Sources/Commands/command_mft.cpp index a26b8a8..b1def50 100644 --- a/Sources/Commands/command_mft.cpp +++ b/Sources/Commands/command_mft.cpp @@ -263,7 +263,7 @@ void print_efs_entry(std::vector& ret, PMFT_RECORD_ATTRIBUTE_EFS_AR ret.push_back(""); if (entry->Type == MFT_ATTRIBUTE_EFS_CONTAINER) { - + ret.push_back("EFS Container not implemented"); } else if (entry->Type == MFT_ATTRIBUTE_EFS_CERTIFICATE) { @@ -745,11 +745,7 @@ int commands::mft::print_mft_info_details(std::shared_ptr record, ULO int print_mft_info(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + if (!commands::helpers::is_ntfs(disk, vol)) return 1; std::shared_ptr explorer = std::make_shared(vol); diff --git a/Sources/Commands/command_reparse.cpp b/Sources/Commands/command_reparse.cpp index a637496..7656100 100644 --- a/Sources/Commands/command_reparse.cpp +++ b/Sources/Commands/command_reparse.cpp @@ -19,11 +19,7 @@ int print_reparse(std::shared_ptr disk, std::shared_ptr vol, const std::string& format, std::string output) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + if (!commands::helpers::is_ntfs(disk, vol)) return 1; utils::ui::title("Reparse points from " + disk->name() + " > Volume:" + std::to_string(vol->index())); diff --git a/Sources/Commands/command_shadow.cpp b/Sources/Commands/command_shadow.cpp index f4f080b..ffe7823 100644 --- a/Sources/Commands/command_shadow.cpp +++ b/Sources/Commands/command_shadow.cpp @@ -18,12 +18,9 @@ #include -int print_volumeshadow(std::shared_ptr disk, std::shared_ptr vol) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } +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())); diff --git a/Sources/Commands/command_shell.cpp b/Sources/Commands/command_shell.cpp index d6967e1..aa1bac9 100644 --- a/Sources/Commands/command_shell.cpp +++ b/Sources/Commands/command_shell.cpp @@ -79,11 +79,8 @@ std::string clean_disk_name(std::shared_ptr disk) int explorer(std::shared_ptr disk, std::shared_ptr vol) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + if (!commands::helpers::is_ntfs(disk, vol)) return 1; + std::shared_ptr explorer = std::make_shared(vol); std::shared_ptr current_dir_record = explorer->mft()->record_from_number(ROOT_FILE_NAME_INDEX_NUMBER); std::string current_dir = "\\"; diff --git a/Sources/Commands/command_streams.cpp b/Sources/Commands/command_streams.cpp index b1d0b00..9b9305f 100644 --- a/Sources/Commands/command_streams.cpp +++ b/Sources/Commands/command_streams.cpp @@ -18,11 +18,7 @@ int list_streams(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); diff --git a/Sources/Commands/command_undelete.cpp b/Sources/Commands/command_undelete.cpp index 4790311..c710200 100644 --- a/Sources/Commands/command_undelete.cpp +++ b/Sources/Commands/command_undelete.cpp @@ -15,6 +15,7 @@ #include "Utils/utils.h" #include "Utils/constant_names.h" #include "Utils/table.h" +#include bool valid_record(PMFT_RECORD_HEADER ph) { @@ -257,11 +258,7 @@ int print_deleted_files(std::shared_ptr disk, std::shared_ptr vol, int extract_deleted_file(std::shared_ptr disk, std::shared_ptr vol, std::shared_ptr opts) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } + 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())); diff --git a/Sources/Commands/command_usn.cpp b/Sources/Commands/command_usn.cpp index 0fa88b6..a4a0c6e 100644 --- a/Sources/Commands/command_usn.cpp +++ b/Sources/Commands/command_usn.cpp @@ -16,12 +16,9 @@ #include #include -int print_usn_journal(std::shared_ptr disk, std::shared_ptr vol, const std::string& format, std::string output) { - if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) - { - std::cerr << "[!] NTFS volume required" << std::endl; - return 1; - } +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())); diff --git a/Sources/Commands/commands.cpp b/Sources/Commands/commands.cpp index 18544a7..db59840 100644 --- a/Sources/Commands/commands.cpp +++ b/Sources/Commands/commands.cpp @@ -2,6 +2,16 @@ #include "Utils/utils.h" +int commands::helpers::is_ntfs(std::shared_ptr disk, std::shared_ptr vol) +{ + if ((vol->filesystem() != "NTFS") && (vol->filesystem() != "Bitlocker")) + { + std::cerr << "[!] NTFS volume required" << std::endl; + return 0; + } + return 1; +} + std::shared_ptr commands::helpers::find_record(std::shared_ptr ex, std::shared_ptr opts) { std::shared_ptr rec = nullptr; @@ -25,5 +35,7 @@ std::shared_ptr commands::helpers::find_record(std::shared_ptrheader()->MFTRecordIndex << std::endl; + return rec; } \ No newline at end of file diff --git a/Sources/Commands/commands.h b/Sources/Commands/commands.h index 5b4f068..281c180 100644 --- a/Sources/Commands/commands.h +++ b/Sources/Commands/commands.h @@ -13,6 +13,8 @@ namespace commands { namespace helpers { + int is_ntfs(std::shared_ptr disk, std::shared_ptr vol); + std::shared_ptr find_record(std::shared_ptr ex, std::shared_ptr opts); } @@ -39,6 +41,10 @@ namespace commands { { int dispatch(std::shared_ptr opts); } + namespace decrypt + { + int dispatch(std::shared_ptr opts); + } } namespace mbr diff --git a/Sources/EFS/certificate_file.cpp b/Sources/EFS/certificate_file.cpp index 3ed061a..2b572eb 100644 --- a/Sources/EFS/certificate_file.cpp +++ b/Sources/EFS/certificate_file.cpp @@ -54,6 +54,7 @@ CertificateFile::CertificateFile(PBYTE data, DWORD size) int CertificateFile::export_to_PEM(std::string name) { + int ret = 1; for (auto element : _fields) { DWORD prop_id = std::get<0>(element); @@ -64,25 +65,28 @@ int CertificateFile::export_to_PEM(std::string name) X509* x = d2i_X509(NULL, &bufder, std::get<1>(element)->size()); if (x) { - FILE* fp = nullptr; - fopen_s(&fp, (name + ".pem").c_str(), "wb"); - if (!fp) + BIO* out = BIO_new_file((name + ".pem").c_str(), "wb"); + if (out) { - return 1; + if (!PEM_write_bio_X509(out, x)) + { + ret = 3; + } + else + { + ret = 0; + } + BIO_free(out); } else { - if (!PEM_write_X509(fp, x)) - { - return 2; - } - fclose(fp); + ret = 2; } X509_free(x); } } } - return 0; + return ret; } X509* CertificateFile::export_to_X509() diff --git a/Sources/EFS/fek.h b/Sources/EFS/fek.h new file mode 100644 index 0000000..605b9ff --- /dev/null +++ b/Sources/EFS/fek.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#pragma pack(push, 1) + +typedef struct { + DWORD KeyLength; + DWORD Entropy; + ALG_ID Algorithm; + DWORD Reserved; + BYTE Key[1]; +} EFS_FEK, * PEFS_FEK; + +#pragma pack(pop) diff --git a/Sources/EFS/pkcs12_archive.cpp b/Sources/EFS/pkcs12_archive.cpp new file mode 100644 index 0000000..d06827c --- /dev/null +++ b/Sources/EFS/pkcs12_archive.cpp @@ -0,0 +1,295 @@ +#include "EFS/pkcs12_archive.h" + +int parse_bag(PKCS12_SAFEBAG* bag, const char* pass, int passlen, + EVP_PKEY** pkey, STACK_OF(X509)* ocerts); + +int parse_bags(const STACK_OF(PKCS12_SAFEBAG)* bags, const char* pass, + int passlen, EVP_PKEY** pkey, STACK_OF(X509)* ocerts); + +int parse_pk12(PKCS12* p12, const char* pass, int passlen, + EVP_PKEY** pkey, STACK_OF(X509)* ocerts); + +PKCS12Archive::PKCS12Archive(std::shared_ptr cert, std::shared_ptr private_key) +{ + _cert = _build_x509_cert(cert); + _key = _build_evp_key(private_key); +} + +int parse_bag(PKCS12_SAFEBAG* bag, const char* pass, int passlen, + EVP_PKEY** pkey, STACK_OF(X509)* ocerts) +{ + PKCS8_PRIV_KEY_INFO* p8; + X509* x509; + const ASN1_TYPE* attrib; + ASN1_BMPSTRING* fname = NULL; + ASN1_OCTET_STRING* lkid = NULL; + + if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) + fname = attrib->value.bmpstring; + + if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID))) + lkid = attrib->value.octet_string; + + switch (PKCS12_SAFEBAG_get_nid(bag)) { + case NID_keyBag: + if (pkey == NULL || *pkey != NULL) + return 1; + *pkey = EVP_PKCS82PKEY(PKCS12_SAFEBAG_get0_p8inf(bag)); + if (*pkey == NULL) + return 0; + break; + + case NID_pkcs8ShroudedKeyBag: + if (pkey == NULL || *pkey != NULL) + return 1; + if ((p8 = PKCS12_decrypt_skey(bag, pass, passlen)) == NULL) + return 0; + *pkey = EVP_PKCS82PKEY(p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (!(*pkey)) + return 0; + break; + + case NID_certBag: + if (ocerts == NULL + || PKCS12_SAFEBAG_get_bag_nid(bag) != NID_x509Certificate) + return 1; + if ((x509 = PKCS12_SAFEBAG_get1_cert(bag)) == NULL) + return 0; + if (lkid && !X509_keyid_set1(x509, lkid->data, lkid->length)) { + X509_free(x509); + return 0; + } + if (fname) { + int len, r; + unsigned char* data; + + len = ASN1_STRING_to_UTF8(&data, fname); + if (len >= 0) { + r = X509_alias_set1(x509, data, len); + OPENSSL_free(data); + if (!r) { + X509_free(x509); + return 0; + } + } + } + + if (!sk_X509_push(ocerts, x509)) { + X509_free(x509); + return 0; + } + + break; + + case NID_safeContentsBag: + return parse_bags(PKCS12_SAFEBAG_get0_safes(bag), pass, passlen, pkey, + ocerts); + + default: + return 1; + } + return 1; +} + +int parse_bags(const STACK_OF(PKCS12_SAFEBAG)* bags, const char* pass, + int passlen, EVP_PKEY** pkey, STACK_OF(X509)* ocerts) +{ + int i; + for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) { + if (!parse_bag(sk_PKCS12_SAFEBAG_value(bags, i), + pass, passlen, pkey, ocerts)) + return 0; + } + return 1; +} + +int parse_pk12(PKCS12* p12, const char* pass, int passlen, + EVP_PKEY** pkey, STACK_OF(X509)* ocerts) +{ + STACK_OF(PKCS7)* asafes; + STACK_OF(PKCS12_SAFEBAG)* bags; + int i, bagnid; + PKCS7* p7; + + if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL) + return 0; + for (i = 0; i < sk_PKCS7_num(asafes); i++) { + p7 = sk_PKCS7_value(asafes, i); + bagnid = OBJ_obj2nid(p7->type); + if (bagnid == NID_pkcs7_data) { + bags = PKCS12_unpack_p7data(p7); + } + else if (bagnid == NID_pkcs7_encrypted) { + bags = PKCS12_unpack_p7encdata(p7, pass, passlen); + } + else + continue; + if (!bags) { + sk_PKCS7_pop_free(asafes, PKCS7_free); + return 0; + } + if (!parse_bags(bags, pass, passlen, pkey, ocerts)) { + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + sk_PKCS7_pop_free(asafes, PKCS7_free); + return 0; + } + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + } + sk_PKCS7_pop_free(asafes, PKCS7_free); + return 1; +} + +EVP_PKEY* PKCS12Archive::_build_evp_key(std::shared_ptr privatekey_file) +{ + if (privatekey_file) + { + EVP_PKEY* key = EVP_PKEY_new(); + RSA* rsa_key = privatekey_file->export_private_to_RSA(); + if (rsa_key) + { + if (EVP_PKEY_assign_RSA(key, rsa_key)) + { + EVP_PKEY_add1_attr_by_NID(key, NID_ms_csp_name, MBSTRING_ASC, (const unsigned char*)"Microsoft Enhanced Cryptographic Provider v1.0", -1); + return key; + } + } + if (key) + { + EVP_PKEY_free(key); + } + } + return nullptr; +} + +X509* PKCS12Archive::_build_x509_cert(std::shared_ptr certfile) +{ + if (certfile) + { + X509* cert = certfile->export_to_X509(); + if (cert) + { + return cert; + } + } + return nullptr; +} + +PKCS12Archive::PKCS12Archive(std::string filename, std::string password) +{ + bool err = true; + + PKCS12* p12 = NULL; + BIO* in = BIO_new_file(filename.c_str(), "rb"); + if (in) + { + p12 = d2i_PKCS12_bio(in, &p12); + if (p12) + { + STACK_OF(X509)* _ca = sk_X509_new_null(); + STACK_OF(X509)* ocerts = sk_X509_new_null(); + if (parse_pk12(p12, password.c_str(), static_cast(password.size()), &_key, ocerts)) + { + X509* x = NULL; + while ((x = sk_X509_shift(ocerts)) != NULL) + { + if (_key != NULL && _cert == NULL) + { + if (X509_check_private_key(x, _key)) + { + _cert = x; + continue; + } + } + } + if (_ca) + { + sk_X509_push(_ca, x); + } + sk_X509_free(ocerts); + + err = false; + } + PKCS12_free(p12); + } + + BIO_free(in); + } + + if (err) + { + if (_key) + { + EVP_PKEY_free(_key); + _key = nullptr; + } + if (_cert) + { + X509_free(_cert); + _cert = nullptr; + } + if (_ca_chain) + { + sk_X509_free(_ca_chain); + _ca_chain = nullptr; + } + } +} + +int PKCS12Archive::export_to_pfx(std::string filename, std::string password) +{ + int err = 0; + + if (_cert && _key) + { + if (X509_check_private_key(_cert, _key)) + { + X509_keyid_set1(_cert, NULL, 0); + X509_alias_set1(_cert, NULL, 0); + + PKCS12* p12 = PKCS12_create(password.c_str(), NULL, _key, _cert, sk_X509_new_null(), NID_undef, NID_undef, 0, -1, KEY_EX); + if (p12) + { + BIO* out = BIO_new_file(filename.c_str(), "wb"); + if (out) + { + i2d_PKCS12_bio(out, p12); + BIO_free(out); + } + else + { + err = 4; + } + PKCS12_free(p12); + } + else + { + err = 3; + } + } + else + { + err = 2; + } + } + else + { + err = 1; + } + + return err; +} + +std::string PKCS12Archive::certificate_hash() +{ + if (_cert) + { + unsigned int fprint_size; + auto fprint_type = EVP_sha1(); + unsigned char fprint[EVP_MAX_MD_SIZE]; + + X509_digest(_cert, fprint_type, fprint, &fprint_size); + return utils::strings::lower(utils::convert::to_hex(fprint, fprint_size)); + } + return ""; +} diff --git a/Sources/EFS/pkcs12_archive.h b/Sources/EFS/pkcs12_archive.h new file mode 100644 index 0000000..f71ae53 --- /dev/null +++ b/Sources/EFS/pkcs12_archive.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +#include + + +class PKCS12Archive +{ +private: + EVP_PKEY* _key = nullptr; + X509* _cert = nullptr; + STACK_OF(X509)* _ca_chain = nullptr; + + EVP_PKEY* _build_evp_key(std::shared_ptr privatekey_file); + + X509* _build_x509_cert(std::shared_ptr certfile); + +public: + explicit PKCS12Archive(std::shared_ptr cert, std::shared_ptr private_key); + + PKCS12Archive(std::string filename, std::string password); + + int export_to_pfx(std::string filename, std::string password); + + std::string certificate_hash(); + + X509* certificate() { return _cert; } + + EVP_PKEY* key() { return _key; } + + STACK_OF(X509)* certs_chain() { return _ca_chain; } +}; \ No newline at end of file diff --git a/Sources/EFS/pkcs12_backup.h b/Sources/EFS/pkcs12_backup.h deleted file mode 100644 index 3a5e88d..0000000 --- a/Sources/EFS/pkcs12_backup.h +++ /dev/null @@ -1,111 +0,0 @@ - -#include -#include - - -#include -#include - -class PKCS12Backup -{ -private: - std::shared_ptr _cert = nullptr; - std::shared_ptr _private_key = nullptr; - - EVP_PKEY* _build_evp_key() - { - if (_private_key) - { - EVP_PKEY* key = EVP_PKEY_new(); - RSA* rsa_key = _private_key->export_private_to_RSA(); - if (rsa_key) - { - if (EVP_PKEY_assign_RSA(key, rsa_key)) - { - EVP_PKEY_add1_attr_by_NID(key, NID_ms_csp_name, MBSTRING_ASC, (const unsigned char*)"Microsoft Enhanced Cryptographic Provider v1.0", -1); - return key; - } - } - if (key) - { - EVP_PKEY_free(key); - } - } - return nullptr; - } - - X509* _build_x509_cert() - { - if (_cert) - { - X509* cert = _cert->export_to_X509(); - if (cert) - { - return cert; - } - } - return nullptr; - } - -public: - PKCS12Backup(std::shared_ptr cert, std::shared_ptr private_key) - { - _cert = cert; - _private_key = private_key; - } - - int export_to_pfx(std::string filename, std::string password) - { - int err = 0; - - if (_cert && _private_key) - { - X509* cert = _build_x509_cert(); - if (cert) - { - EVP_PKEY* key = _build_evp_key(); - if (key) - { - if (X509_check_private_key(cert, key)) - { - X509_keyid_set1(cert, NULL, 0); - X509_alias_set1(cert, NULL, 0); - - PKCS12* p12 = PKCS12_create(password.c_str(), NULL, key, cert, sk_X509_new_null(), NID_undef, NID_undef, 0, -1, KEY_EX); - if (p12) - { - BIO* out = BIO_new_file(filename.c_str(), "wb"); - if (out) - { - i2d_PKCS12_bio(out, p12); - BIO_free(out); - } - else - { - err = 4; - } - PKCS12_free(p12); - } - else - { - err = 3; - } - } - else - { - err = 2; - } - - EVP_PKEY_free(key); - } - X509_free(cert); - } - } - else - { - err = 1; - } - - return err; - } -}; \ No newline at end of file diff --git a/Sources/EFS/private_key.cpp b/Sources/EFS/private_key.cpp index 2554fa9..72f8dff 100644 --- a/Sources/EFS/private_key.cpp +++ b/Sources/EFS/private_key.cpp @@ -38,8 +38,9 @@ PrivateKey::PrivateKey(PBYTE data, DWORD size) int PrivateKey::export_private_to_PEM(std::string filename) { - RSA* rsa = RSA_new(); + int ret = 1; + RSA* rsa = RSA_new(); if (rsa != nullptr) { BIGNUM* p, * q, * n, * e, * d, * dmp1, * dmq1, * iqmp; @@ -67,33 +68,33 @@ int PrivateKey::export_private_to_PEM(std::string filename) RSA_set0_key(rsa, n, e, d); RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp); - FILE* fp = nullptr; - fopen_s(&fp, (filename + ".priv.pem").c_str(), "wb"); - if (!fp) + BIO* out = BIO_new_file((filename + ".priv.pem").c_str(), "wb"); + if (out) { - return 1; + if (!PEM_write_bio_RSAPrivateKey(out, rsa, nullptr, nullptr, 0, nullptr, nullptr)) + { + ret = 3; + } + else + { + ret = 0; + } + BIO_free(out); } else { - if (!PEM_write_RSAPrivateKey(fp, rsa, nullptr, nullptr, 0, nullptr, nullptr)) - { - return 2; - } - fclose(fp); + ret = 2; } RSA_free(rsa); } - else - { - return 3; - } - return 0; + return ret; } int PrivateKey::export_public_to_PEM(std::string filename) { - RSA* rsa = RSA_new(); + int ret = 1; + RSA* rsa = RSA_new(); if (rsa != nullptr) { BIGNUM* n, * e; @@ -107,27 +108,27 @@ int PrivateKey::export_public_to_PEM(std::string filename) RSA_set0_key(rsa, n, e, nullptr); - FILE* fp = nullptr; - fopen_s(&fp, (filename + ".pub.pem").c_str(), "wb"); - if (!fp) + BIO* out = BIO_new_file((filename + ".pub.pem").c_str(), "wb"); + if (out) { - return 1; + if (!PEM_write_bio_RSA_PUBKEY(out, rsa)) + { + ret = 3; + } + else + { + ret = 0; + } + BIO_free(out); } else { - if (!PEM_write_RSA_PUBKEY(fp, rsa)) - { - return 2; - } - fclose(fp); + ret = 2; } RSA_free(rsa); } - else - { - return 3; - } - return 0; + + return ret; } RSA* PrivateKey::export_private_to_RSA() diff --git a/Sources/EFS/public_key.cpp b/Sources/EFS/public_key.cpp index 74ee7c7..78e090f 100644 --- a/Sources/EFS/public_key.cpp +++ b/Sources/EFS/public_key.cpp @@ -13,6 +13,8 @@ PublicKey::PublicKey(PBYTE data, DWORD size) int PublicKey::export_to_PEM(std::string filename) { + int ret = 1; + RSA* rsa = RSA_new(); BIGNUM* n, * e; @@ -27,25 +29,25 @@ int PublicKey::export_to_PEM(std::string filename) RSA_set0_key(rsa, n, e, 0); - FILE* fp = nullptr; - fopen_s(&fp, (filename + ".pub.pem").c_str(), "wb"); - if (!fp) + BIO* out = BIO_new_file((filename + ".pub.pem").c_str(), "wb"); + if (out) { - return 1; + if (!PEM_write_bio_RSA_PUBKEY(out, rsa)) + { + ret = 3; + } + else + { + ret = 0; + } + BIO_free(out); } else { - if (!PEM_write_RSA_PUBKEY(fp, rsa)) - { - return 2; - } - fclose(fp); + ret = 2; } RSA_free(rsa); } - else - { - return 3; - } - return 0; + + return ret; } diff --git a/Sources/NTFS/ntfs.h b/Sources/NTFS/ntfs.h index 968020a..7803238 100644 --- a/Sources/NTFS/ntfs.h +++ b/Sources/NTFS/ntfs.h @@ -541,7 +541,6 @@ typedef struct { struct { DWORD cert_thumbprint_header_size; DWORD cert_thumbprint_header_offset; - }; }; } MFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY, * PMFT_RECORD_ATTRIBUTE_EFS_DATA_DECRYPTION_ENTRY; diff --git a/Sources/NTFS/ntfs_mft.cpp b/Sources/NTFS/ntfs_mft.cpp index 94ae697..edb3145 100644 --- a/Sources/NTFS/ntfs_mft.cpp +++ b/Sources/NTFS/ntfs_mft.cpp @@ -111,14 +111,14 @@ std::shared_ptr MFT::record_from_number(ULONG64 record_number) } if (offset == -1LL) { - wprintf(L"Err: Unable to find record offset for inode 0x%08llx", record_number); + wprintf(L"Failed to find record offset for inode 0x%08llx", record_number); return nullptr; } _reader->seek(offset); if (!_reader->read(buffer->address() + sector * _reader->boot_record()->bytePerSector, _reader->boot_record()->bytePerSector)) { - wprintf(L"Err: Reading record at offset 0x%08llx failed", offset); + wprintf(L"Failed to read record at offset 0x%08llx", offset); return nullptr; } } diff --git a/Sources/NTFS/ntfs_mft_record.cpp b/Sources/NTFS/ntfs_mft_record.cpp index 351bd84..ae78e5b 100644 --- a/Sources/NTFS/ntfs_mft_record.cpp +++ b/Sources/NTFS/ntfs_mft_record.cpp @@ -51,7 +51,7 @@ uint64_t MFTRecord::raw_address(PMFT_RECORD_ATTRIBUTE_HEADER pAttr, uint64_t off return 0; } -ULONG64 MFTRecord::datasize(std::string stream_name) +ULONG64 MFTRecord::datasize(std::string stream_name, bool real_size) { if (_record->data()->flag & FILE_RECORD_FLAG_DIR) { @@ -67,7 +67,8 @@ ULONG64 MFTRecord::datasize(std::string stream_name) } else { - return pAttribute->Form.Nonresident.FileSize; + if (real_size) return pAttribute->Form.Nonresident.FileSize; + else return pAttribute->Form.Nonresident.AllocatedLength; } } else @@ -93,7 +94,7 @@ ULONG64 MFTRecord::datasize(std::string stream_name) std::shared_ptr extRecordHeader = _mft->record_from_number(pAttr->recordNumber & 0xffffffffffff); if (extRecordHeader != nullptr) { - return extRecordHeader->datasize(); + return extRecordHeader->datasize(stream_name, real_size); } else { @@ -283,68 +284,6 @@ std::vector> MFTRecord::index() return ret; } -template -std::shared_ptr> MFTRecord::attribute_data(PMFT_RECORD_ATTRIBUTE_HEADER pAttributeData) -{ - std::shared_ptr> ret = nullptr; - - if (pAttributeData->FormCode == RESIDENT_FORM) - { - ret = std::make_shared>(pAttributeData->Form.Resident.ValueLength); - memcpy_s(ret->data(), ret->size(), POINTER_ADD(LPBYTE, pAttributeData, pAttributeData->Form.Resident.ValueOffset), pAttributeData->Form.Resident.ValueLength); - } - else if (pAttributeData->FormCode == NON_RESIDENT_FORM) - { - ULONGLONG readSize = 0; - ULONGLONG filesize = pAttributeData->Form.Nonresident.FileSize; - - ret = std::make_shared>(pAttributeData->Form.Nonresident.AllocatedLength); - - bool err = false; - - std::vector runList = read_dataruns(pAttributeData); - for (const MFT_DATARUN& run : runList) - { - if (err) break; //-V547 - - if (run.offset == 0) - { - for (ULONGLONG i = 0; i < run.length; i++) - { - readSize += min(filesize - readSize, _reader->sizes.cluster_size); - } - } - else - { - _reader->seek(run.offset * _reader->sizes.cluster_size); - - if (!_reader->read(POINTER_ADD(PBYTE, ret->data(), DWORD(readSize)), static_cast(run.length) * _reader->sizes.cluster_size)) - { - std::cout << "[!] ReadFile failed" << std::endl; - err = true; - break; - } - else - { - readSize += min(filesize - readSize, static_cast(run.length) * _reader->sizes.cluster_size); - } - } - } - if (readSize != filesize) - { - - std::cout << "[!] Invalid read file size" << std::endl; - ret = nullptr; - } - else - { - ret->shrink(static_cast(filesize)); - } - } - - return ret; -} - std::vector MFTRecord::read_dataruns(PMFT_RECORD_ATTRIBUTE_HEADER pAttribute) { std::vector result; @@ -429,14 +368,14 @@ PMFT_RECORD_ATTRIBUTE_HEADER MFTRecord::attribute_header(DWORD type, std::string return nullptr; } -ULONG64 MFTRecord::data_to_file(std::wstring dest_filename, std::string stream_name) +ULONG64 MFTRecord::data_to_file(std::wstring dest_filename, std::string stream_name, bool real_size) { ULONG64 written_bytes = 0ULL; HANDLE output = CreateFileW(dest_filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); if (output != INVALID_HANDLE_VALUE) { - for (auto data_block : process_data(stream_name)) + for (auto data_block : process_data(stream_name, real_size)) { DWORD written_block; if (!WriteFile(output, data_block.first, data_block.second, &written_block, NULL)) @@ -458,7 +397,7 @@ ULONG64 MFTRecord::data_to_file(std::wstring dest_filename, std::string stream_n return written_bytes; } -cppcoro::generator> MFTRecord::process_data(std::string stream_name, DWORD block_size) +cppcoro::generator> MFTRecord::process_data(std::string stream_name, DWORD block_size, bool real_size) { PMFT_RECORD_ATTRIBUTE_HEADER pAttributeData = attribute_header($DATA, stream_name); if (pAttributeData != NULL) @@ -471,7 +410,10 @@ cppcoro::generator> MFTRecord::process_data(std::string if (pAttributeData->Form.Resident.ValueOffset + pAttributeData->Form.Resident.ValueLength <= pAttributeData->RecordLength) { PBYTE data = POINTER_ADD(PBYTE, pAttributeData, pAttributeData->Form.Resident.ValueOffset); - co_yield std::pair(data, pAttributeData->Form.Resident.ValueLength); + for (DWORD offset = 0; offset < pAttributeData->Form.Resident.ValueLength; offset += block_size) + { + co_yield std::pair(data + offset, min(block_size, pAttributeData->Form.Resident.ValueLength - offset)); + } } else { @@ -567,7 +509,7 @@ cppcoro::generator> MFTRecord::process_data(std::string } } } - else if (pAttributeData->FormCode == NON_RESIDENT_FORM) + else { Buffer buffer(block_size); @@ -599,7 +541,7 @@ cppcoro::generator> MFTRecord::process_data(std::string break; } fixed_blocksize = DWORD(min(pAttributeData->Form.Nonresident.FileSize - writeSize, block_size)); - co_yield std::pair(buffer.data(), fixed_blocksize); + co_yield std::pair(buffer.data(), !real_size ? fixed_blocksize : block_size); writeSize += fixed_blocksize; } } @@ -643,7 +585,7 @@ cppcoro::generator> MFTRecord::process_data(std::string for (std::pair b : extRecordHeader->process_data(stream_name, block_size)) { - if (filesize_left < b.second) + if (filesize_left < b.second && !real_size) { b.second = static_cast(filesize_left); } @@ -669,21 +611,21 @@ cppcoro::generator> MFTRecord::process_data(std::string } } -std::shared_ptr> MFTRecord::data(std::string stream_name) +std::shared_ptr> MFTRecord::data(std::string stream_name, bool real_size) { std::shared_ptr> ret = nullptr; PMFT_RECORD_ATTRIBUTE_HEADER pAttributeData = attribute_header($DATA, stream_name); if (pAttributeData != NULL) { - return attribute_data(pAttributeData); + return attribute_data(pAttributeData, real_size); } else { PMFT_RECORD_ATTRIBUTE_HEADER pAttributeList = attribute_header($ATTRIBUTE_LIST); if (pAttributeList != NULL) { - std::shared_ptr> attribute_list_data = attribute_data(pAttributeList); + std::shared_ptr> attribute_list_data = attribute_data(pAttributeList, real_size); if (attribute_list_data != nullptr) { DWORD offset = 0; diff --git a/Sources/NTFS/ntfs_mft_record.h b/Sources/NTFS/ntfs_mft_record.h index 01ded7c..c915206 100644 --- a/Sources/NTFS/ntfs_mft_record.h +++ b/Sources/NTFS/ntfs_mft_record.h @@ -14,6 +14,7 @@ #include "ntfs_reader.h" #include "Utils/buffer.h" +#include "Utils/utils.h" #include @@ -50,17 +51,76 @@ class MFTRecord PMFT_RECORD_ATTRIBUTE_HEADER attribute_header(DWORD type, std::string name = "", int index = 0); template - std::shared_ptr> attribute_data(PMFT_RECORD_ATTRIBUTE_HEADER attr); + std::shared_ptr> attribute_data(PMFT_RECORD_ATTRIBUTE_HEADER pAttributeData, bool real_size = true) + { + std::shared_ptr> ret = nullptr; + + if (pAttributeData->FormCode == RESIDENT_FORM) + { + ret = std::make_shared>(pAttributeData->Form.Resident.ValueLength); + memcpy_s(ret->data(), ret->size(), POINTER_ADD(LPBYTE, pAttributeData, pAttributeData->Form.Resident.ValueOffset), pAttributeData->Form.Resident.ValueLength); + } + else if (pAttributeData->FormCode == NON_RESIDENT_FORM) + { + ULONGLONG readSize = 0; + ULONGLONG filesize = pAttributeData->Form.Nonresident.FileSize; + + ret = std::make_shared>(pAttributeData->Form.Nonresident.AllocatedLength); + + bool err = false; + + std::vector runList = read_dataruns(pAttributeData); + for (const MFT_DATARUN& run : runList) + { + if (err) break; //-V547 + + if (run.offset == 0) + { + for (ULONGLONG i = 0; i < run.length; i++) + { + readSize += min(filesize - readSize, _reader->sizes.cluster_size); + } + } + else + { + _reader->seek(run.offset * _reader->sizes.cluster_size); + + if (!_reader->read(POINTER_ADD(PBYTE, ret->data(), DWORD(readSize)), static_cast(run.length) * _reader->sizes.cluster_size)) + { + std::cout << "[!] ReadFile failed" << std::endl; + err = true; + break; + } + else + { + readSize += min(filesize - readSize, static_cast(run.length) * _reader->sizes.cluster_size); + } + } + } + if (readSize != filesize) + { + + std::cout << "[!] Invalid read file size" << std::endl; + ret = nullptr; + } + if (real_size) + { + ret->shrink(static_cast(filesize)); + } + } + + return ret; + } std::wstring filename(); - ULONG64 datasize(std::string stream_name = ""); + ULONG64 datasize(std::string stream_name = "", bool real_size = true); - std::shared_ptr> data(std::string stream_name = ""); + std::shared_ptr> data(std::string stream_name = "", bool real_size = true); - ULONG64 data_to_file(std::wstring dest_filename, std::string stream_name = ""); + ULONG64 data_to_file(std::wstring dest_filename, std::string stream_name = "", bool real_size = true); - cppcoro::generator> process_data(std::string stream_name = "", DWORD blocksize = 1024 * 1024); + cppcoro::generator> process_data(std::string stream_name = "", DWORD blocksize = 1024 * 1024, bool real_size = true); std::vector alternate_data_names(); diff --git a/Sources/main.cpp b/Sources/main.cpp index ef0cbcf..f772fd3 100644 --- a/Sources/main.cpp +++ b/Sources/main.cpp @@ -44,6 +44,7 @@ int main(int argc, char** argv) { else if (opts->command == "streams") commands::streams::dispatch(opts); else if (opts->command == "efs.backup") commands::efs::backup::dispatch(opts); else if (opts->command == "efs.certificate") commands::efs::certificate::dispatch(opts); + else if (opts->command == "efs.decrypt") commands::efs::decrypt::dispatch(opts); else if (opts->command == "efs.masterkey") commands::efs::masterkey::dispatch(opts); else if (opts->command == "efs.key") commands::efs::key::dispatch(opts); else if (opts->command == "bitdecrypt") commands::bitdecrypt::dispatch(opts); @@ -59,13 +60,15 @@ int main(int argc, char** argv) { } else { - throw std::logic_error("unknown command '" + opts->command + "'"); + throw std::logic_error("Unknown command '" + opts->command + "'"); } } } catch (const std::exception& e) { - std::cerr << "Err: " << e.what() << std::endl << std::endl; + std::cerr << e.what() << std::endl << std::endl; + opts->subcommand = ""; + commands::help::dispatch(opts); } } diff --git a/Sources/options.cpp b/Sources/options.cpp index 7c86340..0586541 100644 --- a/Sources/options.cpp +++ b/Sources/options.cpp @@ -120,6 +120,7 @@ std::shared_ptr parse_options(int argc, char** argv) { if (is_option(argv[i], "password")) { read_option_string(argv[i], ret->password); continue; } if (is_option(argv[i], "sid")) { read_option_string(argv[i], ret->sid); continue; } if (is_option(argv[i], "masterkey")) { read_option_hexbuffer(argv[i], &ret->masterkey); continue; } + if (is_option(argv[i], "pfx")) { read_option_string(argv[i], ret->pfx); continue; } if (is_option(argv[i], "recovery")) { read_option_string(argv[i], ret->recovery); continue; } if (is_option(argv[i], "image")) { read_option_string(argv[i], ret->image); continue; } if (is_option(argv[i], "bek")) { read_option_string(argv[i], ret->bek); continue; } diff --git a/Sources/options.h b/Sources/options.h index a4c71ae..1987c5c 100644 --- a/Sources/options.h +++ b/Sources/options.h @@ -21,6 +21,7 @@ class Options { std::string bek; std::string fvek; std::string image; + std::string pfx; std::shared_ptr> masterkey = nullptr;