660 lines
21 KiB
C++
660 lines
21 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <array>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <span>
|
|
#include <ranges>
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <optional>
|
|
#include <variant>
|
|
#include <cstdint>
|
|
|
|
#ifdef _WIN32
|
|
#define NOMINMAX
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
namespace fs = std::filesystem;
|
|
namespace rng = std::ranges;
|
|
|
|
// XOR key
|
|
constexpr std::array<uint8_t, 7> XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C };
|
|
|
|
// File paths
|
|
constexpr std::string_view KVC_PASS_EXE = "kvc_pass.exe";
|
|
constexpr std::string_view KVC_CRYPT_DLL = "kvc_crypt.dll";
|
|
constexpr std::string_view KVC_RAW = "kvc.raw";
|
|
constexpr std::string_view KVC_DAT = "kvc.dat";
|
|
constexpr std::string_view KVC_EXE = "kvc.exe";
|
|
constexpr std::string_view KVC_ENC = "kvc.enc";
|
|
|
|
// Helper for string concatenation (replaces std::format)
|
|
inline std::string concat(std::string_view a) {
|
|
return std::string(a);
|
|
}
|
|
|
|
inline std::string concat(std::string_view a, std::string_view b) {
|
|
std::string result;
|
|
result.reserve(a.size() + b.size());
|
|
result.append(a);
|
|
result.append(b);
|
|
return result;
|
|
}
|
|
|
|
inline std::string concat(std::string_view a, std::string_view b, std::string_view c) {
|
|
std::string result;
|
|
result.reserve(a.size() + b.size() + c.size());
|
|
result.append(a);
|
|
result.append(b);
|
|
result.append(c);
|
|
return result;
|
|
}
|
|
|
|
inline std::string concat(std::string_view a, std::string_view b, std::string_view c, std::string_view d) {
|
|
std::string result;
|
|
result.reserve(a.size() + b.size() + c.size() + d.size());
|
|
result.append(a);
|
|
result.append(b);
|
|
result.append(c);
|
|
result.append(d);
|
|
return result;
|
|
}
|
|
|
|
inline std::string concat(std::string_view a, std::string_view b, std::string_view c,
|
|
std::string_view d, std::string_view e) {
|
|
std::string result;
|
|
result.reserve(a.size() + b.size() + c.size() + d.size() + e.size());
|
|
result.append(a);
|
|
result.append(b);
|
|
result.append(c);
|
|
result.append(d);
|
|
result.append(e);
|
|
return result;
|
|
}
|
|
|
|
inline std::string concat(std::string_view a, std::string_view b, std::string_view c,
|
|
std::string_view d, std::string_view e, std::string_view f) {
|
|
std::string result;
|
|
result.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size());
|
|
result.append(a);
|
|
result.append(b);
|
|
result.append(c);
|
|
result.append(d);
|
|
result.append(e);
|
|
result.append(f);
|
|
return result;
|
|
}
|
|
|
|
inline std::string concat(std::string_view a, std::string_view b, std::string_view c,
|
|
std::string_view d, std::string_view e, std::string_view f,
|
|
std::string_view g) {
|
|
std::string result;
|
|
result.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() + g.size());
|
|
result.append(a);
|
|
result.append(b);
|
|
result.append(c);
|
|
result.append(d);
|
|
result.append(e);
|
|
result.append(f);
|
|
result.append(g);
|
|
return result;
|
|
}
|
|
|
|
// Simple Result type (replacement for std::expected which MSVC doesn't fully support yet)
|
|
template<typename T>
|
|
class Result {
|
|
std::variant<T, std::string> data;
|
|
|
|
public:
|
|
Result(T value) : data(std::move(value)) {}
|
|
Result(std::string error) : data(std::move(error)) {}
|
|
|
|
bool has_value() const { return std::holds_alternative<T>(data); }
|
|
explicit operator bool() const { return has_value(); }
|
|
|
|
T& value() { return std::get<T>(data); }
|
|
const T& value() const { return std::get<T>(data); }
|
|
|
|
const std::string& error() const { return std::get<std::string>(data); }
|
|
|
|
T* operator->() { return &std::get<T>(data); }
|
|
const T* operator->() const { return &std::get<T>(data); }
|
|
};
|
|
|
|
// Specialization for void
|
|
template<>
|
|
class Result<void> {
|
|
std::optional<std::string> error_msg;
|
|
|
|
public:
|
|
Result() : error_msg(std::nullopt) {}
|
|
Result(std::string error) : error_msg(std::move(error)) {}
|
|
|
|
bool has_value() const { return !error_msg.has_value(); }
|
|
explicit operator bool() const { return has_value(); }
|
|
|
|
const std::string& error() const { return *error_msg; }
|
|
};
|
|
|
|
// Console colors
|
|
enum class Color : int {
|
|
Default = 7,
|
|
Green = 10,
|
|
Red = 12,
|
|
Yellow = 14
|
|
};
|
|
|
|
void set_color(Color color) {
|
|
#ifdef _WIN32
|
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), static_cast<int>(color));
|
|
#else
|
|
switch (color) {
|
|
case Color::Green: std::cout << "\033[32m"; break;
|
|
case Color::Red: std::cout << "\033[31m"; break;
|
|
case Color::Yellow: std::cout << "\033[33m"; break;
|
|
case Color::Default: std::cout << "\033[0m"; break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void reset_color() {
|
|
set_color(Color::Default);
|
|
}
|
|
|
|
// RAII color guard
|
|
class ColorGuard {
|
|
public:
|
|
explicit ColorGuard(Color new_color) {
|
|
set_color(new_color);
|
|
}
|
|
~ColorGuard() {
|
|
reset_color();
|
|
}
|
|
ColorGuard(const ColorGuard&) = delete;
|
|
ColorGuard& operator=(const ColorGuard&) = delete;
|
|
};
|
|
|
|
// XOR operation
|
|
void xor_data(std::span<uint8_t> data, std::span<const uint8_t> key) noexcept {
|
|
for (size_t i = 0; i < data.size(); ++i) {
|
|
data[i] ^= key[i % key.size()];
|
|
}
|
|
}
|
|
|
|
// Read entire file into vector
|
|
Result<std::vector<uint8_t>> read_file(const fs::path& path) {
|
|
if (!fs::exists(path)) {
|
|
return Result<std::vector<uint8_t>>(
|
|
concat("File '", path.string(), "' does not exist")
|
|
);
|
|
}
|
|
|
|
std::ifstream file(path, std::ios::binary);
|
|
if (!file) {
|
|
return Result<std::vector<uint8_t>>(
|
|
concat("Cannot open file '", path.string(), "'")
|
|
);
|
|
}
|
|
|
|
std::vector<uint8_t> data(
|
|
(std::istreambuf_iterator<char>(file)),
|
|
std::istreambuf_iterator<char>()
|
|
);
|
|
|
|
return data;
|
|
}
|
|
|
|
// Write data to file
|
|
Result<void> write_file(const fs::path& path, std::span<const uint8_t> data) {
|
|
std::ofstream file(path, std::ios::binary);
|
|
if (!file) {
|
|
return Result<void>(
|
|
concat("Cannot create file '", path.string(), "'")
|
|
);
|
|
}
|
|
|
|
file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
|
|
|
if (!file) {
|
|
return Result<void>(
|
|
concat("Error writing to file '", path.string(), "'")
|
|
);
|
|
}
|
|
|
|
return Result<void>();
|
|
}
|
|
|
|
// Helper to read uint16_t from buffer
|
|
constexpr uint16_t read_uint16(std::span<const uint8_t> data, size_t offset) {
|
|
return static_cast<uint16_t>(data[offset]) |
|
|
(static_cast<uint16_t>(data[offset + 1]) << 8);
|
|
}
|
|
|
|
// Helper to read uint32_t from buffer
|
|
constexpr uint32_t read_uint32(std::span<const uint8_t> data, size_t offset) {
|
|
return static_cast<uint32_t>(data[offset]) |
|
|
(static_cast<uint32_t>(data[offset + 1]) << 8) |
|
|
(static_cast<uint32_t>(data[offset + 2]) << 16) |
|
|
(static_cast<uint32_t>(data[offset + 3]) << 24);
|
|
}
|
|
|
|
// Determine PE file length from buffer
|
|
std::optional<size_t> get_pe_file_length(std::span<const uint8_t> data, size_t offset = 0) noexcept {
|
|
try {
|
|
// Check if we have enough data for DOS header
|
|
if (data.size() < offset + 0x40) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Check for MZ signature
|
|
if (data[offset] != 'M' || data[offset + 1] != 'Z') {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Get e_lfanew from offset 0x3C
|
|
const uint32_t e_lfanew = read_uint32(data, offset + 0x3C);
|
|
const size_t pe_header_offset = offset + e_lfanew;
|
|
|
|
// Check if we have enough data for PE header
|
|
if (pe_header_offset + 6 > data.size()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Check for PE signature
|
|
if (data[pe_header_offset] != 'P' || data[pe_header_offset + 1] != 'E' ||
|
|
data[pe_header_offset + 2] != 0 || data[pe_header_offset + 3] != 0) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Get number of sections and size of optional header
|
|
const uint16_t number_of_sections = read_uint16(data, pe_header_offset + 6);
|
|
const uint16_t size_of_optional_header = read_uint16(data, pe_header_offset + 20);
|
|
|
|
// Calculate section table offset
|
|
const size_t section_table_offset = pe_header_offset + 24 + size_of_optional_header;
|
|
|
|
// Check if we have enough data for section table
|
|
if (section_table_offset + number_of_sections * 40 > data.size()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Find the maximum end of section raw data
|
|
size_t max_end = 0;
|
|
for (uint16_t i = 0; i < number_of_sections; ++i) {
|
|
const size_t sh_offset = section_table_offset + i * 40;
|
|
|
|
const uint32_t size_of_raw = read_uint32(data, sh_offset + 16);
|
|
const uint32_t pointer_to_raw = read_uint32(data, sh_offset + 20);
|
|
|
|
if (pointer_to_raw == 0) continue;
|
|
|
|
const size_t end = pointer_to_raw + size_of_raw;
|
|
max_end = std::max(max_end, end);
|
|
}
|
|
|
|
// If we found section data, use it
|
|
if (max_end > 0) {
|
|
const size_t header_end = section_table_offset + number_of_sections * 40;
|
|
return std::max(max_end, header_end);
|
|
}
|
|
|
|
// Fallback: Use SizeOfHeaders from optional header
|
|
const size_t optional_header_offset = pe_header_offset + 24;
|
|
if (optional_header_offset + 64 <= data.size()) {
|
|
const uint32_t size_of_headers = read_uint32(data, optional_header_offset + 60);
|
|
if (size_of_headers > 0) {
|
|
return size_of_headers;
|
|
}
|
|
}
|
|
}
|
|
catch (...) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Find next MZ header in buffer
|
|
std::optional<size_t> find_next_mz_header(std::span<const uint8_t> data, size_t start_offset) {
|
|
constexpr std::array<uint8_t, 2> pattern = { 'M', 'Z' };
|
|
|
|
auto search_range = rng::subrange(
|
|
data.begin() + start_offset,
|
|
data.end()
|
|
);
|
|
|
|
auto result = rng::search(search_range, pattern);
|
|
|
|
if (result.empty()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return std::distance(data.begin(), result.begin());
|
|
}
|
|
|
|
// Ask user Y/N question
|
|
bool ask_yes_no(std::string_view question) {
|
|
std::cout << question << " (Y/N): ";
|
|
std::string answer;
|
|
std::getline(std::cin, answer);
|
|
|
|
return !answer.empty() && (answer[0] == 'Y' || answer[0] == 'y');
|
|
}
|
|
|
|
// Encode files: kvc_pass.exe + kvc_crypt.dll -> kvc.raw + kvc.dat
|
|
Result<void> encode_files() {
|
|
std::cout << "Step 1: Encoding " << KVC_PASS_EXE << " + " << KVC_CRYPT_DLL << "...\n";
|
|
|
|
// Read both files
|
|
auto exe_result = read_file(KVC_PASS_EXE);
|
|
if (!exe_result) {
|
|
return Result<void>(exe_result.error());
|
|
}
|
|
|
|
auto dll_result = read_file(KVC_CRYPT_DLL);
|
|
if (!dll_result) {
|
|
return Result<void>(dll_result.error());
|
|
}
|
|
|
|
// Combine files
|
|
std::vector<uint8_t> combined_data;
|
|
combined_data.reserve(exe_result->size() + dll_result->size());
|
|
combined_data.insert(combined_data.end(), exe_result->begin(), exe_result->end());
|
|
combined_data.insert(combined_data.end(), dll_result->begin(), dll_result->end());
|
|
|
|
// Write raw file
|
|
if (auto result = write_file(KVC_RAW, combined_data); !result) {
|
|
return result;
|
|
}
|
|
|
|
// XOR encode the data
|
|
xor_data(combined_data, XOR_KEY);
|
|
|
|
// Write encoded file
|
|
if (auto result = write_file(KVC_DAT, combined_data); !result) {
|
|
return result;
|
|
}
|
|
|
|
std::cout << " -> Files combined -> " << KVC_RAW << "\n";
|
|
std::cout << " -> Combined file XOR-encoded -> " << KVC_DAT << "\n";
|
|
|
|
return Result<void>();
|
|
}
|
|
|
|
// Decode files: kvc.dat -> kvc.raw + kvc_pass.exe + kvc_crypt.dll
|
|
Result<void> decode_files() {
|
|
std::cout << "Decoding " << KVC_DAT << "...\n";
|
|
|
|
auto enc_result = read_file(KVC_DAT);
|
|
if (!enc_result) {
|
|
return Result<void>(enc_result.error());
|
|
}
|
|
|
|
// XOR decode the data
|
|
std::vector<uint8_t> dec_data = std::move(enc_result.value());
|
|
xor_data(dec_data, XOR_KEY);
|
|
|
|
// Write decoded raw file
|
|
if (auto result = write_file(KVC_RAW, dec_data); !result) {
|
|
return result;
|
|
}
|
|
|
|
// Try to determine the exact size of the first PE file
|
|
auto first_size = get_pe_file_length(dec_data, 0);
|
|
|
|
// Fallback if PE parsing failed
|
|
if (!first_size || *first_size >= dec_data.size()) {
|
|
std::cout << " -> PE parsing failed, using fallback search for MZ header...\n";
|
|
|
|
const size_t search_start = std::min<size_t>(0x200, dec_data.size() - 1);
|
|
first_size = find_next_mz_header(dec_data, search_start);
|
|
|
|
if (!first_size) {
|
|
// Ultimate fallback: don't split
|
|
first_size = dec_data.size();
|
|
}
|
|
}
|
|
|
|
// Split the files
|
|
if (auto result = write_file(KVC_PASS_EXE, std::span(dec_data.data(), *first_size)); !result) {
|
|
return result;
|
|
}
|
|
|
|
if (auto result = write_file(KVC_CRYPT_DLL, std::span(dec_data.data() + *first_size, dec_data.size() - *first_size)); !result) {
|
|
return result;
|
|
}
|
|
|
|
std::cout << " -> Decoded -> " << KVC_RAW << "\n";
|
|
std::cout << " -> Split into " << KVC_PASS_EXE << " and " << KVC_CRYPT_DLL << "\n";
|
|
|
|
return Result<void>();
|
|
}
|
|
|
|
// Build distribution package: kvc.exe + kvc.dat -> kvc.enc
|
|
Result<void> build_distribution() {
|
|
std::cout << "Building distribution package...\n";
|
|
|
|
// Check if kvc.dat exists
|
|
if (!fs::exists(KVC_DAT)) {
|
|
std::cout << " -> " << KVC_DAT << " not found.\n";
|
|
|
|
// Check if source files exist
|
|
if (!fs::exists(KVC_PASS_EXE) || !fs::exists(KVC_CRYPT_DLL)) {
|
|
return Result<void>(
|
|
concat("Cannot create ", KVC_DAT, ": missing ", KVC_PASS_EXE, " or ", KVC_CRYPT_DLL)
|
|
);
|
|
}
|
|
|
|
// Ask if we should create it
|
|
if (ask_yes_no(concat("Create ", KVC_DAT, " from ", KVC_PASS_EXE, " and ", KVC_CRYPT_DLL, "?"))) {
|
|
if (auto result = encode_files(); !result) {
|
|
return result;
|
|
}
|
|
} else {
|
|
return Result<void>("Operation cancelled by user");
|
|
}
|
|
}
|
|
|
|
// Read both files
|
|
auto exe_result = read_file(KVC_EXE);
|
|
if (!exe_result) {
|
|
return Result<void>(exe_result.error());
|
|
}
|
|
|
|
auto dat_result = read_file(KVC_DAT);
|
|
if (!dat_result) {
|
|
return Result<void>(dat_result.error());
|
|
}
|
|
|
|
// Combine files
|
|
std::vector<uint8_t> combined_data;
|
|
combined_data.reserve(exe_result->size() + dat_result->size());
|
|
combined_data.insert(combined_data.end(), exe_result->begin(), exe_result->end());
|
|
combined_data.insert(combined_data.end(), dat_result->begin(), dat_result->end());
|
|
|
|
// XOR encode the combined data
|
|
xor_data(combined_data, XOR_KEY);
|
|
|
|
// Write encoded distribution file
|
|
if (auto result = write_file(KVC_ENC, combined_data); !result) {
|
|
return result;
|
|
}
|
|
|
|
std::cout << " -> Distribution package created -> " << KVC_ENC << "\n";
|
|
std::cout << " -> Ready for remote deployment!\n";
|
|
|
|
return Result<void>();
|
|
}
|
|
|
|
// Decode distribution package: kvc.enc -> kvc.exe + kvc.dat
|
|
Result<void> decode_distribution() {
|
|
std::cout << "Decoding distribution package...\n";
|
|
|
|
auto enc_result = read_file(KVC_ENC);
|
|
if (!enc_result) {
|
|
return Result<void>(enc_result.error());
|
|
}
|
|
|
|
// XOR decode the data
|
|
std::vector<uint8_t> dec_data = std::move(enc_result.value());
|
|
xor_data(dec_data, XOR_KEY);
|
|
|
|
// Try to determine the exact size of kvc.exe
|
|
auto exe_size = get_pe_file_length(dec_data, 0);
|
|
|
|
// Fallback if PE parsing failed
|
|
if (!exe_size || *exe_size >= dec_data.size()) {
|
|
std::cout << " -> PE parsing failed, using fallback search for MZ header...\n";
|
|
|
|
const size_t search_start = std::min<size_t>(0x200, dec_data.size() - 1);
|
|
exe_size = find_next_mz_header(dec_data, search_start);
|
|
|
|
if (!exe_size) {
|
|
// Ultimate fallback: use half
|
|
exe_size = dec_data.size() / 2;
|
|
}
|
|
}
|
|
|
|
// Split the files
|
|
if (auto result = write_file(KVC_EXE, std::span(dec_data.data(), *exe_size)); !result) {
|
|
return result;
|
|
}
|
|
|
|
if (auto result = write_file(KVC_DAT, std::span(dec_data.data() + *exe_size, dec_data.size() - *exe_size)); !result) {
|
|
return result;
|
|
}
|
|
|
|
std::cout << " -> Distribution package decoded -> " << KVC_EXE << " + " << KVC_DAT << "\n";
|
|
|
|
return Result<void>();
|
|
}
|
|
|
|
// Decode everything: kvc.enc -> kvc.exe + kvc_pass.exe + kvc_crypt.dll
|
|
Result<void> decode_everything() {
|
|
std::cout << "Complete decoding of distribution package...\n";
|
|
|
|
// Check if kvc.enc exists
|
|
if (!fs::exists(KVC_ENC)) {
|
|
std::cout << " -> " << KVC_ENC << " not found.\n";
|
|
|
|
// Check if we can create it from existing files
|
|
if (fs::exists(KVC_EXE) && fs::exists(KVC_DAT)) {
|
|
if (ask_yes_no(concat("Create ", KVC_ENC, " from ", KVC_EXE, " and ", KVC_DAT, "?"))) {
|
|
if (auto result = build_distribution(); !result) {
|
|
return result;
|
|
}
|
|
} else {
|
|
return Result<void>("Operation cancelled by user");
|
|
}
|
|
} else {
|
|
return Result<void>(concat("File '", KVC_ENC, "' does not exist"));
|
|
}
|
|
}
|
|
|
|
auto enc_result = read_file(KVC_ENC);
|
|
if (!enc_result) {
|
|
return Result<void>(enc_result.error());
|
|
}
|
|
|
|
// XOR decode the data
|
|
std::vector<uint8_t> dec_data = std::move(enc_result.value());
|
|
xor_data(dec_data, XOR_KEY);
|
|
|
|
// Find first PE file (kvc.exe)
|
|
auto first_pe_size = get_pe_file_length(dec_data, 0);
|
|
|
|
if (!first_pe_size || *first_pe_size >= dec_data.size()) {
|
|
return Result<void>("Cannot determine first PE file size");
|
|
}
|
|
|
|
// Extract kvc.exe
|
|
std::vector<uint8_t> kvc_exe_data(dec_data.begin(), dec_data.begin() + *first_pe_size);
|
|
|
|
// The remaining data should be kvc.dat
|
|
std::vector<uint8_t> kvc_dat_data(dec_data.begin() + *first_pe_size, dec_data.end());
|
|
|
|
// Decode kvc.dat to get kvc_pass.exe and kvc_crypt.dll
|
|
xor_data(kvc_dat_data, XOR_KEY);
|
|
|
|
// Find the PE file in kvc.dat (kvc_pass.exe)
|
|
auto second_pe_size = get_pe_file_length(kvc_dat_data, 0);
|
|
|
|
if (!second_pe_size || *second_pe_size >= kvc_dat_data.size()) {
|
|
return Result<void>("Cannot determine second PE file size in kvc.dat");
|
|
}
|
|
|
|
// Write all files
|
|
if (auto result = write_file(KVC_EXE, kvc_exe_data); !result) {
|
|
return result;
|
|
}
|
|
|
|
if (auto result = write_file(KVC_PASS_EXE, std::span(kvc_dat_data.data(), *second_pe_size)); !result) {
|
|
return result;
|
|
}
|
|
|
|
if (auto result = write_file(KVC_CRYPT_DLL, std::span(kvc_dat_data.data() + *second_pe_size, kvc_dat_data.size() - *second_pe_size)); !result) {
|
|
return result;
|
|
}
|
|
|
|
std::cout << " -> Complete decoding successful!\n";
|
|
std::cout << " -> Extracted: " << KVC_EXE << ", " << KVC_PASS_EXE << ", " << KVC_CRYPT_DLL << "\n";
|
|
|
|
return Result<void>();
|
|
}
|
|
|
|
// Display menu
|
|
void display_menu() {
|
|
std::cout << "==================================================\n";
|
|
std::cout << "| FILE ENCODER/DECODER TOOL |\n";
|
|
std::cout << "==================================================\n";
|
|
std::cout << "| 1. ENCODE: kvc_pass.exe + kvc_crypt.dll |\n";
|
|
std::cout << "| -> kvc.raw + kvc.dat |\n";
|
|
std::cout << "| 2. DECODE: kvc.dat -> kvc.raw + |\n";
|
|
std::cout << "| kvc_pass.exe + kvc_crypt.dll |\n";
|
|
std::cout << "| 3. BUILD DISTRIBUTION: kvc.exe + kvc.dat |\n";
|
|
std::cout << "| -> kvc.enc |\n";
|
|
std::cout << "| 4. DECODE DISTRIBUTION: kvc.enc -> |\n";
|
|
std::cout << "| kvc.exe + kvc.dat |\n";
|
|
std::cout << "| 5. DECODE EVERYTHING: kvc.enc -> |\n";
|
|
std::cout << "| kvc.exe + kvc_pass.exe + |\n";
|
|
std::cout << "| kvc_crypt.dll |\n";
|
|
std::cout << "==================================================\n\n";
|
|
std::cout << "kvc.enc is used for remote installation via command:\n";
|
|
|
|
ColorGuard green(Color::Green);
|
|
std::cout << "irm https://kvc.pl/run | iex\n\n";
|
|
}
|
|
|
|
int main() {
|
|
display_menu();
|
|
std::cout << "Select operation (1-5): ";
|
|
|
|
int choice;
|
|
std::cin >> choice;
|
|
std::cin.ignore(); // Clear newline from buffer
|
|
|
|
Result<void> result = Result<void>("Invalid choice");
|
|
|
|
switch (choice) {
|
|
case 1: result = encode_files(); break;
|
|
case 2: result = decode_files(); break;
|
|
case 3: result = build_distribution(); break;
|
|
case 4: result = decode_distribution(); break;
|
|
case 5: result = decode_everything(); break;
|
|
default:
|
|
ColorGuard red(Color::Red);
|
|
std::cerr << "Invalid choice. Please select 1-5.\n";
|
|
return 1;
|
|
}
|
|
|
|
if (!result) {
|
|
ColorGuard red(Color::Red);
|
|
std::cerr << "Error: " << result.error() << "\n";
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
} |