Pentest Mini Framework


Lightweight penetration testing framework

Pentesting often means developing and deploying native C++ binaries. Repeating tasks include file & registry operations, process injection, acquiring system information, and so on... Since WinAPI is an API and not framework like .NET, duplicate and boilerplate code accumulates. I named my accumulation "Pentest Mini Framework".

Pentest Mini Framework is a collection of most commonly used API's wrapped up in useful classes.


Just a quick inspiration of what I mean by "wrapping". Simple tasks like below are trivial in frameworks like .NET, but a hassle with C++ / WinAPI.

// Getting a string from the registry
wstring Registry::GetValueString(RegistryKey key, wstring path, wstring name, wstring defaultValue)
	HKEY hKey;
	wchar_t value[512];
	DWORD valueSize = sizeof(value);

	if (RegOpenKeyExW(GetHkey(key), path.c_str(), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS && hKey != NULL)
		LSTATUS status = RegQueryValueExW(hKey, name.c_str(), 0, NULL, (LPBYTE)value, &valueSize);
		if (status == ERROR_SUCCESS) return wstring(value);

	return defaultValue;

// Getting CommandLine arguments in a proper vector<wstring>
vector<wstring> Application::GetCommandLineArgs()
	int argc;
	LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
	vector<wstring> args = vector<wstring>();

	if (argv != NULL)
		for (int i = 0; i < argc; i++)

	return args;

// Accessing common strings that usually require out parameters and conversion to wstring
wstring File::GetExecutablePath()
	wchar_t path[MAX_PATH];
	GetModuleFileNameW(NULL, path, MAX_PATH);
	return wstring(path);

So, nothing magical. But it shouldn't be your primary occupation.

What it is and what it's not

And to clear up leftover misunderstandings of what Pentest Mini Framework is and what it's not:

A framework Frameworks, like .NET, are complete wrap-ups of any underlaying API available. Pentest Mini Framework just covers the most basic classes that are almost always required to spare you from repeated boilerplate work.
Fail safe There is not much focus on handling every possible error event with descriptive exceptions. This is not an out of the box toolset or framework. Using it means your job is probably to debug literally everything and that you know exactly what you do, better than any user of a framework.
Well documented If you are one to use it, you likely comprehend decompiled code that has no comments or meaningful symbol exports in the first place. Most likely, you already have some sort of wrap-up functions anyway. Here I'm just sharing mine.
A DLL You probably realized quickly, that dependency injection can reduce reliability of payload execution. So no compromise, just include the header file and go!
A wrapper As seen in the example above, this is just a wrapper that collapses dozens of lines of WinAPI code into a single line. Core tasks are covered and provided in strongly typed classes.
Some of these tasks are particularly exhausting, like finding a process by its name rather than ID, which is a single method call here.
A necessity If you develop more than just one binary now and then, you need some sort of framework. No one copy and pastes code blocks like these one below the other when writing code.
Saving time Developers familiar with both a high-level language and C++ usually appreciate frameworks and even minimalistic wrappers more than anyone else.
Dependency-less When compiled with /MT, you will have no dependencies other than system DLL's. Especially DLL's injected into remote processes must not load .NET Framework or otherwise things will get irreliable and even more complicated.

Header files - what's in it

Let's have a look to evaluate the contents of this wrapper. It's still a beta state, because I constantly add new methods. The naming will seem familiar to .NET developers.

class Application
	static wstring GetCommandLineString();
	static vector<wstring> GetCommandLineArgs();

class Convert
	static wstring ToString(string str);
	static wstring ToString(int value);
	static wstring ToString(int value, int base);
	static wstring ToString(unsigned int value);
	static wstring ToString(unsigned int value, int base);
	static wstring ToString(long value);
	static wstring ToString(long value, int base);
	static wstring ToString(unsigned long value);
	static wstring ToString(unsigned long value, int base);
	static wstring ToString(long long value);
	static wstring ToString(long long value, int base);
	static wstring ToString(unsigned long long value);
	static wstring ToString(unsigned long long value, int base);
	static wstring ToStringFormatted(DWORD value);
	static wstring ToStringFormatted(HWND value);
	static long ToInt32(wstring str);
	static long long ToInt64(wstring str);
	static float ToFloat(wstring str);
	static double ToDouble(wstring str);

class DebugLog
	DebugLog(wstring path);
	DebugLog(wstring path, bool clear);

	wstring getPath();

	void Lock();
	void Unlock();
	void Clear();
	void Write(wstring str);
	void WriteLine();
	void WriteLine(wstring str);

class File
	static wstring GetExecutablePath();
	static wstring GetModulePath(HMODULE module);
	static wstring GetStartupPath();
	static wstring GetTempFolderPath();
	static wstring GetFolderPath(SpecialFolder folder);
	static wstring CombinePaths(wstring path1, wstring path2);
	static bool FileExists(wstring path);

class FileOperation
	static void Copy(wstring srcPath, wstring destDirectory, wstring newFileName);
	static void Move(wstring srcPath, wstring destDirectory, wstring newFileName);
	static void Delete(wstring path);

class Message
	static void Information(wstring message);
	static void Information(wstring title, wstring message);
	static void Warning(wstring message);
	static void Warning(wstring title, wstring message);
	static void Error(wstring message);
	static void Error(wstring title, wstring message);
	static void Error(wstring message, bool exitProcess);
	static void Error(wstring title, wstring message, bool exitProcess);
	static bool Confirmation(wstring message);
	static bool Confirmation(wstring title, wstring message);
	static bool Confirmation(wstring message, bool warning);
	static bool Confirmation(wstring title, wstring message, bool warning);

class Process
	Process(HANDLE handle);

	static bool EnableDebugPrivilege();
	static Process GetCurrent();
	static Process GetProcessById(int id);
	static Process GetProcessById(int id, DWORD desiredAccess);
	static Process GetProcessByName(wstring name);
	static Process GetProcessByName(wstring name, DWORD desiredAccess);
	static Process CreateWithIntegrityLevel(wstring commandLine, DWORD integrityLevel);

	int getId();
	HANDLE getHandle();
	void getIntegrity(DWORD &integrityLevel, wstring &integrityLevelName);
	bool getParentProcess(int &processID, wstring &processName);

	void Kill();
	void Kill(unsigned int exitCode);
	bool Inject(wstring dllPath);

class Registry
	static void CreateKey(RegistryKey key, wstring path, wstring name);
	static vector<wstring> GetKeyNames(RegistryKey key, wstring path);
	static vector<wstring> GetValueNames(RegistryKey key, wstring path);
	static void DeleteKey(RegistryKey key, wstring path, wstring name);
	static wstring GetValueString(RegistryKey key, wstring path, wstring name, wstring defaultValue);
	static DWORD GetValueDword(RegistryKey key, wstring path, wstring name, DWORD defaultValue);
	static void SetValue(RegistryKey key, wstring path, wstring name, wstring value);
	static void SetValue(RegistryKey key, wstring path, wstring name, wstring value, bool expand);
	static void SetValue(RegistryKey key, wstring path, wstring name, DWORD value);
	static void DeleteValue(RegistryKey key, wstring path, wstring name);

class Service
	Service(SC_HANDLE handle);

	static Service GetServiceByName(wstring name);

	ServiceState GetState();
	bool Start();
	bool Start(ServiceState expectedState);
	bool Start(ServiceState expectedState, int timeoutMilliseconds);
	bool Control(ServiceControl control, ServiceState expectedState);
	bool Control(ServiceControl control, ServiceState expectedState, int timeoutMilliseconds);

class System
	static long long GetMilliseconds();
	static long long GetMicroseconds();
	static wstring GetCurrentUser();
	static bool GetWindowsVersion(DWORD &major, DWORD &minor);

Precompiled binaries

A couple of binaries are included along the line, compiled for both x86 and x64.


This executable can be used to test an *.exe payload and displays basic information.


This is basically the same, but it writes the result to the registry. Very useful when executing under the SYSTEM user or a context without UI.




Usually, write attempt to HKEY_CURRENT_USER is always possible, while the value written to HKEY_LOCAL_MACHINE indicates elevated privileges.


Simple injector. Arguments are Process ID and path to DLL.

Inject.exe 1234 "C:\MyPayload.dll"


Creates a sandboxed process with Low Integrity Level. With no arguments specified, cmd.exe is started. Specifying -u, will start the process with Untrusted Integrity Level instead of low IL. Examples:

Please note, that many applications don't function under Low IL. With Untrusted IL, most applications, including cmd.exe, will not properly start up.

REM starts cmd.exe with Low IL

REM starts cmd.exe with Untrusted IL
CreateProcessWithIntegrity.exe -u

REM starts file in path with Low IL
CreateProcessWithIntegrity.exe C:\Windows\System32\notepad.exe

REM starts file in path with Untrusted IL
CreateProcessWithIntegrity.exe C:\Windows\System32\notepad.exe -u