Files
kvc/kvc/KvcXor.cpp
2025-10-04 22:05:44 +02:00

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;
}