Skip to content

Instantly share code, notes, and snippets.

@hasherezade
Created July 14, 2023 14:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hasherezade/3a9417377cacd893c580bdffb85292c1 to your computer and use it in GitHub Desktop.
Save hasherezade/3a9417377cacd893c580bdffb85292c1 to your computer and use it in GitHub Desktop.
#include <windows.h>
#include <iostream>
#include <detours.h> // include MS Detours header
#include <peconv.h> // include libPeConv header
size_t g_PESize = 0;
BYTE *g_PEBuf = NULL;
NTSTATUS WINAPI ZwQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ DWORD ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
decltype(&ZwQueryInformationProcess) pZwQueryInformationProcess = nullptr;
NTSTATUS WINAPI my_ZwQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ DWORD ProcessInformationClass,
_Out_ DWORD* ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
)
{
NTSTATUS result = pZwQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength);
if (result == 0 && ProcessInformationClass == 0x22) {
(*ProcessInformation) |= 0x20;
}
return result;
}
bool hook_apis()
{
HMODULE ntdll = LoadLibraryA("ntdll");
if (!ntdll) return false;
auto proc = GetProcAddress(ntdll, "ZwQueryInformationProcess");
if (!proc) return false;
pZwQueryInformationProcess = reinterpret_cast<decltype(&ZwQueryInformationProcess)>(proc);
//initialize hooking:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
// This patch allows for the exceptions to be handled:
DetourAttach(&(PVOID&)pZwQueryInformationProcess, my_ZwQueryInformationProcess);
//finalize hooking:
DetourTransactionCommit();
return true;
}
BYTE* load_pe(const LPCSTR pe_path)
{
// manually load the PE file using libPeConv:
size_t v_size = 0;
#ifdef LOAD_FROM_PATH
//if the PE is dropped on the disk, you can load it from the file:
BYTE* my_pe = peconv::load_pe_executable(pe_path, v_size);
#else
size_t bufsize = 0;
BYTE *buffer = peconv::load_file(pe_path, bufsize);
// if the file is NOT dropped on the disk, you can load it directly from a memory buffer:
BYTE* my_pe = peconv::load_pe_executable(buffer, bufsize, v_size);
#endif
if (!my_pe) {
return NULL;
}
// set the loaded PE in the global variables:
g_PESize = v_size;
g_PEBuf = my_pe;
// if the loaded PE needs to access resources, you may need to connect it to the PEB:
peconv::set_main_module_in_peb((HMODULE)my_pe);
return g_PEBuf;
}
int run_pe_entrypoint(BYTE *my_pe)
{
//calculate the Entry Point of the manually loaded module
DWORD ep_rva = peconv::get_entry_point_rva(my_pe);
if (!ep_rva) {
return -2;
}
ULONG_PTR ep_va = ep_rva + (ULONG_PTR)my_pe;
//assuming that the payload is an EXE file (not DLL) this will be the simplest prototype of the main:
int(*new_main)() = (int(*)())ep_va;
//call the Entry Point of the manually loaded PE:
return new_main();
}
int main(int argc, char *argv[])
{
if (argc < 2) {
std::cout << "Args: <path to the exe>" << std::endl;
return 0;
}
const LPCSTR pe_path = argv[1];
// manually load an EXE with libPEConv:
if (!load_pe(pe_path)) {
std::cout << "[-] Loading the PE: "<< pe_path << " failed!\n";
return -1;
}
if (!hook_apis()) {
std::cerr << "Hooking failed!\n";
return (-1);
}
// run the manually loaded EXE:
int res = run_pe_entrypoint(g_PEBuf);
system("pause");
return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment