I want to allocate some memory inside a specific module of a process instead of the process in general. The following Windows C++ code can allocate memory inside a process given its process id:
#include "pch.h"
#include <windows.h>
#include <winternl.h>
#include <processthreadsapi.h>
#include <iostream>
#include <conio.h>
#pragma comment(lib, "ntdll.lib")
typedef NTSTATUS (NTAPI *nt_alloc_virtual_memory_func)(HANDLE process_handle, PVOID* base_address, ULONG_PTR zero_bits,
PSIZE_T region_size, ULONG allocation_type, ULONG protect);
typedef NTSTATUS (NTAPI *nt_free_virtual_memory_func)(HANDLE process_handle, PVOID* base_address, PSIZE_T region_size,
ULONG free_type);
void enable_allocating_executable_memory()
{
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY mp;
ZeroMemory(&mp, sizeof mp);
mp.ProhibitDynamicCode = FALSE;
SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &mp, sizeof mp);
}
long allocate_memory(char** arguments, const HANDLE process_handle, PVOID process_memory, SIZE_T& allocation_size)
{
const auto memory_size = arguments[3];
allocation_size = strtoul(memory_size, nullptr, 10);
const auto nt_allocate_virtual_memory = reinterpret_cast<nt_alloc_virtual_memory_func>(GetProcAddress(
GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory"));
const auto allocation_status = nt_allocate_virtual_memory(process_handle, &process_memory, 0, &allocation_size,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (NT_SUCCESS(allocation_status))
{
std::cout << std::hex << process_memory << std::endl;
}
return allocation_status;
}
int free_memory(const int argument_count, char** arguments,
const HANDLE process_handle, SIZE_T& mem_size)
{
const auto address_string = arguments[3];
const auto process_address = strtoull(address_string, nullptr, 16);
auto process_memory_address = reinterpret_cast<PVOID>(process_address);
if (argument_count < 4)
{
return EXIT_FAILURE;
}
const auto memory_size = arguments[4];
mem_size = strtoul(memory_size, nullptr, 10);
const auto nt_free_virtual_memory = reinterpret_cast<nt_free_virtual_memory_func>(GetProcAddress(
GetModuleHandle(L"ntdll.dll"), "NtFreeVirtualMemory"));
const auto status = nt_free_virtual_memory(process_handle, &process_memory_address, &mem_size, MEM_RELEASE);
return status;
}
int main(const int argument_count, char* arguments[])
{
if (argument_count < 4)
{
return EXIT_FAILURE;
}
const auto process_id_string = arguments[1];
const auto process_id = strtoul(process_id_string, nullptr, 10);
enable_allocating_executable_memory();
const auto process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
if (process_handle == nullptr)
{
std::cout << "Cannot open process with process id " << process_id << std::endl;
exit(EXIT_FAILURE);
}
const PVOID process_memory = nullptr;
SIZE_T mem_size;
const auto command = arguments[2];
if (strcmp(command, "--allocate") == 0)
{
allocate_memory(arguments, process_handle, process_memory, mem_size);
}
else if (strcmp(command, "--free") == 0)
{
return free_memory(argument_count, arguments, process_handle, mem_size);
}
return EXIT_SUCCESS;
}
NtAllocateVirtualMemory does not seem to accept an argument for a module. What else can be used?
The reasoning behind this is that I don't want to have jmps going from one module to another after I allocated some memory but rather stay as locally as possible. This also makes jmp instructions shorter in terms of their sizes in memory.
I want to allocate some memory inside a specific module
You cannot do this, when a module is mapped it's memory is allocated. You can't allocate memory inside the module, the module exists inside it's allocated pages and no where else. Any allocated pages will be outside of the module.
Alternatively if you want to use memory which is already allocated but is not used, this called a code cave. It's typically an area of memory inside a module which is filled with zeros. So you can scan for a code cave by finding a certain length of redundant zeros inside the module and then you can write to that memory.
This is done frequently and is especially useful if the page has the execute bit set as you won't have to change any permissions which could be deemed risky.
This is also done frequently in injectors which use "scatter mapping" where it only uses these code caves to inject code.
Related
I'm using the following code to enumerate the contents of the Recyclebin Shell folder and get the file type of each item.
The code gives the expected results but if I call the function in a loop it looks like there's some memory leak when using the IshellItem2 GetString function (see attached screenshot at the end).
Am I cleaning up everything properly?
Am I misinterpreting the results?
void Test1()
{
// Get recyclebin ishellitem
IShellItem* psiRecycleBin;
if (SUCCEEDED(SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT,
NULL, IID_PPV_ARGS(&psiRecycleBin))))
{
// Get ishellitem contents enumerator
IEnumShellItems* pesi;
if (SUCCEEDED(psiRecycleBin->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&pesi))))
{
IShellItem* psi;
while (pesi->Next(1, &psi, NULL) == S_OK)
{
// Get ishellitem2 from ishellitem
IShellItem2* psi2;
if (SUCCEEDED(psi->QueryInterface(IID_PPV_ARGS(&psi2))))
{
// Get the item file type string
LPWSTR fileType = NULL;
if (SUCCEEDED(psi2->GetString(PKEY_ItemTypeText, &fileType)))
{
CoTaskMemFree(fileType);
}
psi2->Release();
}
psi->Release();
}
pesi->Release();
}
psiRecycleBin->Release();
}
}
And I'm calling it in loop like this:
#define STRICT_TYPED_ITEMIDS
#include <shlobj.h>
#include <propkey.h>
#include <iostream>
void Test1();
int main()
{
(void)CoInitialize(NULL);
std::cout << "Enumerating recyclebin items..\n";
for (int ii = 0; ii < 5000; ii++)
{
Test1();
}
CoUninitialize();
return 0;
}
When debugging this console program in VS in the memory diagnostics window this is what I get:
Thanks for the help
yes, here really exist memory leak, related to HIDDENRECYCLEBINDATAV2 structure from shell32.dll
partial definition of it:
struct HIDDENRECYCLEBINDATAV2
{
//... some mebers
FILETIME time;
PWSTR pszLocationBeforeDelete = 0; // !!! not released
PWSTR pszLocationInRecycleBin = 0; // !!! not released
HRESULT Serialize(PBYTE *, PUSHORT);
static HRESULT Deserialize(
_In_reads_bytes_opt_(cbStream) const BYTE *pbStream ,
_In_ USHORT cbStream,
_Out_ HIDDENRECYCLEBINDATAV2 ** pphrbd);
static HRESULT Initialize(HIDDENRECYCLEBINDATAV1 const *, HIDDENRECYCLEBINDATAV2**);
};
this structure hold 2 strings - file path from where it deleted ( pszLocationBeforeDelete - this is my name, i don't know original) and current file path in Recycle Bin ( pszLocationInRecycleBin - again my name)
this names allocated inside Deserialize method, by call IStream_ReadStrLong and must be freed with CoTaskMemFree. but how i found - CoTaskMemFree never called for this two strings.
pseudo code for Deserialize :
static HRESULT HIDDENRECYCLEBINDATAV2::Deserialize(
_In_reads_bytes_opt_(cbInit) const BYTE *pbStream ,
_In_ USHORT cbStream,
_Out_ HIDDENRECYCLEBINDATAV2 ** pphrbd)
{
HRESULT hr = E_FAIL;
if (HIDDENRECYCLEBINDATAV2 *phrbd = new HIDDENRECYCLEBINDATAV2)
{
if (IStream *pStream = SHCreateMemStream(pbStream, cbStream))
{
if (0 <= (hr = IStream_ReadStrLong(pStream, &phrbd->pszLocationBeforeDelete)) &&
0 <= (hr = IStream_ReadStrLong(pStream, &phrbd->pszLocationInRecycleBin)))
{
*pphrbd = phrbd, phrbd = 0;
}
pStream->Release();
}
CoTaskMemFree(phrbd); // !! error, need delete phrbd
}
return hr;
}
and it called from CBitBucket::_ValidateItem :
HRESULT InitDeletedItem(PCWSTR pszLocationBeforeDelete, PCWSTR pszLocationBeforeDelete, DELETEDITEM *);
static HRESULT CBitBucket::_ValidateItem(_ITEMIDLIST_RELATIVE const *, DELETEDITEM ** ppdi)
{
HIDDENRECYCLEBINDATAV2 * phrbd;
if (0 <= HIDDENRECYCLEBINDATAV2::Deserialize(pbStream, cbStream, &phrbd))
{
if (DELETEDITEM * pdi = new DELETEDITEM)
{
if (0 <= InitDeletedItem( phrbd->pszLocationBeforeDelete,
phrbd->pszLocationInRecycleBin, pdi))
{
*ppdi = pdi, pdi = 0;
}
if (pdi) delete pdi;
}
CoTaskMemFree(phrbd); // !! error, need delete phrbd
}
}
in both functions - memory for HIDDENRECYCLEBINDATAV2 simply released with CoTaskMemFree api, but memory for strings inside this structure not released. i think need add
HIDDENRECYCLEBINDATAV2::~HIDDENRECYCLEBINDATAV2()
{
CoTaskMemFree(pszLocationInRecycleBin);
CoTaskMemFree(pszLocationBeforeDelete);
}
to this structure and call delete instead CoTaskMemFree
how possible found this ? i hook RtlAllocateHeap and RtlFreeHeap before second call to Test1() (important do this not on first call, because during first call may be additional libs load, some differed initialization, etc.. - all this can distort the real result)and log all alloc/free calls in current thread. also i replace while (pesi->Next..) to if (pesi->Next..) (usually one iteration is enough ). and i found that count of alloc on 2 more than count of free. so i easy found from where this 2 allocations- inside IStream_ReadStrLong. then i set breakpoint here and easy view from where this called :
CBitBucket::_ValidateItem
HIDDENRECYCLEBINDATAV2::Deserialize
IStream_ReadStrLong
CoTaskMemAlloc
partial demo code for log:
struct AI
{
PVOID BaseAddress;
PVOID From;
ULONG Size;
ULONG Flags;
};
struct TID
{
AI *pi;
ULONG nAlloc, nFree, nCells, MaxAllocDelta;
BOOL bNotLog;
TID()
{
RtlZeroMemory(this, sizeof(*this));
}
};
BOOLEAN NTAPI hook_RtlFreeHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress )
{
TID* p = RTL_FRAME<TID>::get();
if (!p || p->bNotLog)
{
return RtlFreeHeap(HeapHandle, Flags, BaseAddress) != 0;
}
p->bNotLog = TRUE;
if (!RtlFreeHeap(HeapHandle, Flags, BaseAddress))
{
__debugbreak();
}
if (BaseAddress)
{
AI* pi = p->pi;
ULONG n = p->nCells;
do
{
if (pi->BaseAddress == BaseAddress)
{
pi->BaseAddress = 0;
p->nFree++;
break;
}
} while (pi++, --n);
if (!n)
{
__debugbreak();
}
}
p->bNotLog = FALSE;
return TRUE;
}
PVOID NTAPI hook_RtlAllocateHeap( PVOID HeapHandle, ULONG Flags, SIZE_T Size )
{
TID* p = RTL_FRAME<TID>::get();
if (!p || p->bNotLog)
{
return RtlAllocateHeap(HeapHandle, Flags, Size);
}
p->bNotLog = TRUE;
if (PVOID BaseAddress = RtlAllocateHeap(HeapHandle, Flags, Size))
{
AI* pi = p->pi;
ULONG n = p->nCells;
do
{
if (!pi->BaseAddress)
{
pi->BaseAddress = BaseAddress;
pi->From = _ReturnAddress();
pi->Size = (ULONG)Size;
pi->Flags = Flags;
p->nAlloc++;
ULONG k = p->nAlloc - p->nFree;
if (k > p->MaxAllocDelta)
{
p->MaxAllocDelta = k;
}
break;
}
} while (pi++, --n);
if (!n)
{
__debugbreak();
}
p->bNotLog = FALSE;
return BaseAddress;
}
return 0;
}
void TestEx()
{
enum { cell_count = 0x1000 };
if (AI* pi = new AI[cell_count])
{
Test1();// first call
// hook RtlAllocateHeap + RtlFreeHeap
{
RtlZeroMemory(pi, cell_count * sizeof(AI));
RTL_FRAME<TID> f;
f.pi = pi;
f.nCells = cell_count;
Test1();// second call
DbgPrint("%x(%x) %x\n", f.nAlloc, f.nFree, f.MaxAllocDelta);
if (f.nAlloc - f.nFree)
{
ULONG n = cell_count;
AI* qi = pi;
do
{
if (qi->BaseAddress)
{
DbgPrint("%p> %x %x\n", qi->From, qi->Size, qi->Flags);
}
} while (qi++, --n);
}
}
delete [] pi;
}
}
I want to get the entry point to a 64bit process I wrote from a 32bit process, the same way you'd use EnumProcessModule and take the memory addr of the main module.
My end goal is to read a byte from memory in my 64bit process from an offset to it (entry+Offset).
But my NtWow64ReadVirtualMemory64 function keeps failing.
I think it has something to do with my entry memory addr.
#define PROC_BASIC_INFO 0
#define NT_WOW64_QUERY_INFORMATION_PROCESS_64_NAME "NtWow64QueryInformationProcess64"
#define NT_WOW64_READ_VIRTUAL_MEMORY_64_NAME "NtWow64ReadVirtualMemory64"
typedef UINT64 SYM;
typedef SIZE_T SIZE_T64;
HWND WINDOW_HANDLE;
HANDLE PROC_HANDLE;
DWORD PROC_ID;
UINT address;
UINT64 address64;
SIZE_T bytesRead;
SIZE_T64 bytesRead64;
using namespace std;
//initialize variables for importing of essential 64 bit reading functions
//from ntdll
typedef NTSTATUS(NTAPI *FUNC_NtReadVirtualMemory64)
(
IN HANDLE ProcessHandle,
IN PVOID64 BaseAddress,
OUT PVOID Buffer,
IN ULONGLONG BufferLength,
OUT PULONGLONG ReturnLength OPTIONAL
);
typedef NTSTATUS (NTAPI *FUNC_NtWow64QueryInformationProcess64)
(
IN HANDLE ProcessHandle,
IN ULONG ProcessInformationClass,
OUT PVOID ProcessInformation64,
IN ULONG Length,
OUT PULONG ReturnLength OPTIONAL
);
struct PROCESS_BASIC_INFORMATION64 {
SYM Reserved1;
SYM PebBaseAddress;
SYM Reserved2[2];
SYM UniqueProcessId;
SYM Reserved3;
/*
NTSTATUS ExitStatus;
ULONG64 PebBaseAddress;
ULONG64 AffinityMask;
LONG BasePriority;
UINT64 Reserved1;
ULONG64 UniqueProcessId;
ULONG64 InheritedFromUniqueProcessId;
*/
};
HINSTANCE ntdll = LoadLibrary("ntdll.dll");
FUNC_NtWow64QueryInformationProcess64 NtWow64QueryInformationProcess64 = (FUNC_NtWow64QueryInformationProcess64)GetProcAddress(ntdll, NT_WOW64_QUERY_INFORMATION_PROCESS_64_NAME);
FUNC_NtReadVirtualMemory64 NtReadVirtualMemory64 = (FUNC_NtReadVirtualMemory64)GetProcAddress(ntdll, NT_WOW64_READ_VIRTUAL_MEMORY_64_NAME);
int Init32To64MemoryRead(const char* windowClass, const char* caption, SYM addressOffset)
{
DWORD cbNeeded;
DWORD dwdResult;
HMODULE mainModule;
BOOL enumResult;
ULONG read_length=0;
HINSTANCE ntdll;
PROCESS_BASIC_INFORMATION64 procInfo;
ZeroMemory(&procInfo, sizeof(procInfo));
//Get the window handle
WINDOW_HANDLE = FindWindow(windowClass, NULL);
if (WINDOW_HANDLE == NULL)
{
//Window was not foud
return 10;
}
//Get the process ID
dwdResult = GetWindowThreadProcessId(WINDOW_HANDLE, &PROC_ID);
if (dwdResult == 0)
{
//Getting Process ID failed
return 20;
}
//Open the process
PROC_HANDLE = OpenProcess(PROCESS_ALL_ACCESS, false, PROC_ID);
if (PROC_HANDLE == NULL)
{
//Process failed to open
return 30;
}
DWORD result;
//Query Proc Information to get .exe entry point
result = NtWow64QueryInformationProcess64( PROC_HANDLE, 0, &procInfo, sizeof(procInfo), &read_length);
if (result != 0)
{
cerr << "Query Information Process has failed" << endl;
return 40;
}
address64 = (procInfo.PebBaseAddress + addressOffset);
cerr << address64 << endl;
string number;
stringstream stristream;
stristream << address64;
stristream >> number;
byte testByte = 0;
(byte)ReadMemory64<byte>(testByte);
system("PAUSE");
return 1;
}
template <typename _ret_t> _ret_t ReadMemory64(_ret_t& ret)
{
NTSTATUS result = NtReadVirtualMemory64(PROC_HANDLE, (void*)address64, &ret, 8, NULL);
///* Debug # when too lazy for breakpoints
cerr <<"value: " << ret << endl;
cerr << "Error Code: " << GetLastError() << endl;
if (result != 0)
{
cerr << "ReadMemory Failed.\r\nAddress: " << address64 << "\r\nSize: " << sizeof(_ret_t) << "\r\nResult: " << result << endl;
cerr << "NtReadVirtualMemory64 has failed" << endl;
system("PAUSE");
} //*/
return ret;
};
I'd like to know what I am doing wrong.
Edit:
Upon further inspection, I noticed that NtWow64ReadVirtualMemory, does not store a value in the variable "ret" used as the buffer.
I ran a simple test and figured out that the value of my buffer-"ret" was not changed when inserted to the function "NtWow64ReadVirtualMemory64".
The code did compile and run without errors(compile and runtime) except for NtReadMemory64 returning a weird number (there is no documentation available for the ntdll NtWow64 functions, so goolgling it did not yield anything useful).
So i figured I am either providing a faulty buffer or am not reading from a valid memory addr.
since I did initialize the buffer explicitly outside of the function, I figured
that my problem is the latter(not providing a valid memory address).
I was using the following when calling NtReadVirtualMemory
NTSTATUS result = NtReadVirtualMemory64(PROC_HANDLE, (void*)address64, &ret, 8, NULL);
apparently, when calling NtWow64ReadVirtualMemory64, I cast the addr to a 32 bit void pointer (void*)address64 , and since address64 is a UINT64-type , the cast truncated the address, and I was trying to read off of a memory segment I wasn't able to read
I resolved it by changing the cast to (PVOID64)address64
which casts it to a native 64bit pointer.
simpler than I thought, but finding it was hell after days of googling and reviewing the code.
Edit:
this didn't cut it since my address is wrong.
I need to get the ".exe"s' entry point through the location of the process' main module in memory.
looking at the how to now.
any help is appreciated!
I can hook any other function, but not ExitProcess.
Here is the code to demonstrate this:
#include <iostream>
#include <cstdlib>
#include <Windows.h>
#include <Psapi.h>
void __stdcall NewSleep(DWORD milliseconds)
{
std::cout << "Sleep." << std::endl;
std::cin.get();
}
void __stdcall NewExitProcess(UINT exitCode)
{
std::cout << "ExitProcess." << std::endl;
std::cin.get();
}
FARPROC f1 = NULL;
FARPROC f2 = NULL;
int main()
{
HMODULE kernel32Module = GetModuleHandle("KERNEL32.dll");
f1 = GetProcAddress(kernel32Module, "Sleep");
f2 = GetProcAddress(kernel32Module, "ExitProcess");
std::cout << f1 << std::endl;
unsigned char* baseAddress = (unsigned char*)GetModuleHandle(NULL);
IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)baseAddress;
IMAGE_NT_HEADERS* inh = (IMAGE_NT_HEADERS*)(baseAddress + idh->e_lfanew);
IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)(baseAddress + inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (IMAGE_IMPORT_DESCRIPTOR* i = iid; i->Name != 0; ++i)
{
std::string moduleName = (char*)(baseAddress + i->Name);
if (moduleName == "KERNEL32.dll")
{
IMAGE_THUNK_DATA* itd = (IMAGE_THUNK_DATA*)(baseAddress + i->FirstThunk);
for (IMAGE_THUNK_DATA* j = itd; j->u1.Function != 0; ++j)
{
if ((FARPROC)j->u1.Function == f1)
{
DWORD oldProtect = 0;
VirtualProtect(&j->u1.Function, sizeof(DWORD), PAGE_READWRITE, &oldProtect);
j->u1.Function = (DWORD)&NewSleep;
VirtualProtect(&j->u1.Function, sizeof(DWORD), oldProtect, &oldProtect);
}
if ((FARPROC)j->u1.Function == f2)
{
DWORD oldProtect = 0;
VirtualProtect(&j->u1.Function, sizeof(DWORD), PAGE_READWRITE, &oldProtect);
j->u1.Function = (DWORD)&NewExitProcess;
VirtualProtect(&j->u1.Function, sizeof(DWORD), oldProtect, &oldProtect);
}
}
break;
}
}
Sleep(0);
Sleep(0);
ExitProcess(0);
//Crash.
std::cin.sync();
std::cin.get();
return EXIT_SUCCESS;
}
It calls the hooked function, but when NewExitProcess returns I get an access violation. The calls to Sleep are fine, just like any hooked function other than ExitProcess.
EDIT: I get the same issue when hooking ExitThread though.
When looking up the function declaration of ExitProcess you will find something like this:
WINBASEAPI
DECLSPEC_NORETURN
VOID
WINAPI
ExitProcess(
_In_ UINT uExitCode
);
The interesting part is DECLSPEC_NORETURN which is defined as __declspec(noreturn). It's also an attribute used by the ExitThread function that was also causing a crash for you. Looking up on the docs, we find this:
This __declspec attribute tells the compiler that a function does not return. As a consequence, the compiler knows that the code following a call to a __declspec(noreturn) function is unreachable.
According to your findings, it is not only used to disable compiler warnings, but is also used for optimization. This also explains why it would work in Debug mode.
I can't think of a good solution for this, as you are fighting the optimizer. The solution you wrote in a comment did not work for me (VS2013, Release mode, /O2). I came up with something a bit silly, but it seems to do the job for me:
int *ptr = (int*)&ExitProcess;
ptr++;
ptr--;
((VOID (WINAPI*)(UINT))ptr)(0);
In general, hooking ExitProcess of another unknown program should always exit the current thread, because it may be compiled to not have any code to return to.
When unhandled exception occured i want to print a stacktrace instead of just terminating. I've tried to do that using SetUnhandledExceptionFilter:
SetUnhandledExceptionFilter(UnhandledException);
...
LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo)
{
printf("An exception occurred which wasn't handled!\nCode: 0x%08X\nAddress: 0x%08X",
exceptionInfo->ExceptionRecord->ExceptionCode,
exceptionInfo->ExceptionRecord->ExceptionAddress);
return EXCEPTION_EXECUTE_HANDLER;
}
This code, i've found, works fine. However there are no addition information because ExceptionCode and ExceptionAddress are printed in system "Event Viewer" anyway.
If it is possible to print a full stack trace so I can determine the exact point where exception occured?
I've found this code https://code.google.com/p/m0d-s0beit-sa/source/browse/src/main.cpp?r=9ceb4fec21d647b169c72851d7882bef2b9c5a8a It partly solves my problem. Only method where exception occured is printed. But type of exception and line number is not printed.
Here's some stack-walk code for Windows I wrote some years ago. Here's the kind of output it produces:
Walking stack.
0 DebugBreak
1 ThreadFunc2 e:\c\source\stackwalk2a.cpp(72)
2 ThreadFunc1 e:\c\source\stackwalk2a.cpp(79)
3 TargetThread e:\c\source\stackwalk2a.cpp(86)
4 BaseThreadInitThunk
5 RtlUserThreadStart
End of stack walk.
The main thing that's missing is anything about the exception type. If you're talking about a native structured/vectored exception, I'm pretty sure that should be retrievable too. Retrieving types of C++ exceptions might be a little more difficult (but I'm not really sure -- it might be pretty easy).
#include <windows.h>
#include <winnt.h>
#include <string>
#include <vector>
#include <Psapi.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <iterator>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")
// Some versions of imagehlp.dll lack the proper packing directives themselves
// so we need to do it.
#pragma pack( push, before_imagehlp, 8 )
#include <imagehlp.h>
#pragma pack( pop, before_imagehlp )
struct module_data {
std::string image_name;
std::string module_name;
void *base_address;
DWORD load_size;
};
typedef std::vector<module_data> ModuleList;
HANDLE thread_ready;
bool show_stack(std::ostream &, HANDLE hThread, CONTEXT& c);
DWORD __stdcall TargetThread( void *arg );
void ThreadFunc1();
void ThreadFunc2();
DWORD Filter( EXCEPTION_POINTERS *ep );
void *load_modules_symbols( HANDLE hProcess, DWORD pid );
int main( void ) {
DWORD thread_id;
thread_ready = CreateEvent( NULL, false, false, NULL );
HANDLE thread = CreateThread( NULL, 0, TargetThread, NULL, 0, &thread_id );
WaitForSingleObject( thread_ready, INFINITE );
CloseHandle(thread_ready);
return 0;
}
// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack dump at the
// earliest opportunity, to avoid the interesting stack-frames being gone
// by the time you do the dump.
DWORD Filter(EXCEPTION_POINTERS *ep) {
HANDLE thread;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &thread, 0, false, DUPLICATE_SAME_ACCESS);
std::cout << "Walking stack.";
show_stack(std::cout, thread, *(ep->ContextRecord));
std::cout << "\nEnd of stack walk.\n";
CloseHandle(thread);
return EXCEPTION_EXECUTE_HANDLER;
}
void ThreadFunc2() {
__try { DebugBreak(); }
__except (Filter(GetExceptionInformation())) { }
SetEvent(thread_ready);
}
void ThreadFunc1(void (*f)()) {
f();
}
// We'll do a few levels of calls from our thread function so
// there's something on the stack to walk...
//
DWORD __stdcall TargetThread(void *) {
ThreadFunc1(ThreadFunc2);
return 0;
}
class SymHandler {
HANDLE p;
public:
SymHandler(HANDLE process, char const *path=NULL, bool intrude = false) : p(process) {
if (!SymInitialize(p, path, intrude))
throw(std::logic_error("Unable to initialize symbol handler"));
}
~SymHandler() { SymCleanup(p); }
};
#ifdef _M_X64
STACKFRAME64 init_stack_frame(CONTEXT c) {
STACKFRAME64 s;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rbp;
s.AddrFrame.Mode = AddrModeFlat;
return s;
}
#else
STACKFRAME64 init_stack_frame(CONTEXT c) {
STACKFRAME64 s;
s.AddrPC.Offset = c.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Esp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
return s;
}
#endif
void sym_options(DWORD add, DWORD remove=0) {
DWORD symOptions = SymGetOptions();
symOptions |= add;
symOptions &= ~remove;
SymSetOptions(symOptions);
}
class symbol {
typedef IMAGEHLP_SYMBOL64 sym_type;
sym_type *sym;
static const int max_name_len = 1024;
public:
symbol(HANDLE process, DWORD64 address) : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
memset(sym, '\0', sizeof(*sym) + max_name_len);
sym->SizeOfStruct = sizeof(*sym);
sym->MaxNameLength = max_name_len;
DWORD64 displacement;
if (!SymGetSymFromAddr64(process, address, &displacement, sym))
throw(std::logic_error("Bad symbol"));
}
std::string name() { return std::string(sym->Name); }
std::string undecorated_name() {
std::vector<char> und_name(max_name_len);
UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
return std::string(&und_name[0], strlen(&und_name[0]));
}
};
bool show_stack(std::ostream &os, HANDLE hThread, CONTEXT& c) {
HANDLE process = GetCurrentProcess();
int frame_number=0;
DWORD offset_from_symbol=0;
IMAGEHLP_LINE64 line = {0};
SymHandler handler(process);
sym_options(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
void *base = load_modules_symbols(process, GetCurrentProcessId());
STACKFRAME64 s = init_stack_frame(c);
line.SizeOfStruct = sizeof line;
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
DWORD image_type = h->FileHeader.Machine;
do {
if (!StackWalk64(image_type, process, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
return false;
os << std::setw(3) << "\n" << frame_number << "\t";
if ( s.AddrPC.Offset != 0 ) {
std::cout << symbol(process, s.AddrPC.Offset).undecorated_name();
if (SymGetLineFromAddr64( process, s.AddrPC.Offset, &offset_from_symbol, &line ) )
os << "\t" << line.FileName << "(" << line.LineNumber << ")";
}
else
os << "(No Symbols: PC == 0)";
++frame_number;
} while (s.AddrReturn.Offset != 0);
return true;
}
class get_mod_info {
HANDLE process;
static const int buffer_length = 4096;
public:
get_mod_info(HANDLE h) : process(h) {}
module_data operator()(HMODULE module) {
module_data ret;
char temp[buffer_length];
MODULEINFO mi;
GetModuleInformation(process, module, &mi, sizeof(mi));
ret.base_address = mi.lpBaseOfDll;
ret.load_size = mi.SizeOfImage;
GetModuleFileNameEx(process, module, temp, sizeof(temp));
ret.image_name = temp;
GetModuleBaseName(process, module, temp, sizeof(temp));
ret.module_name = temp;
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
return ret;
}
};
void *load_modules_symbols(HANDLE process, DWORD pid) {
ModuleList modules;
DWORD cbNeeded;
std::vector<HMODULE> module_handles(1);
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
module_handles.resize(cbNeeded/sizeof(HMODULE));
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
return modules[0].base_address;
}
I am aware that NT header has all constants defined like SE_TAKE_OWNERSHIP_NAME, and so there are functions available to convert these into human readable form (Take ownership of files or other objects).
My question is how to enumerate these names? With different versions of Windows, not all SE-names would be applicable (i.e. privileges may not be available on particular NT system).
Whilst it is true that Windows7/2008 is the latest and appropriate header for the same would list all of them - and if the application runs on a lower platform, the function taking SE-names would simply fail for given name if given OS doesn't support (like LsaEnumerateAccountsWithUserRight would fail).
But how to make application future compatible that can facilitate listing all privileges for future versions of Windows OS?
Use LsaEnumeratePrivileges (defined in ntlsa.h, which is in the WDK - inc/api):
NTSTATUS
NTAPI
LsaEnumeratePrivileges(
__in LSA_HANDLE PolicyHandle,
__inout PLSA_ENUMERATION_HANDLE EnumerationContext,
__out PVOID *Buffer,
__in ULONG PreferedMaximumLength,
__out PULONG CountReturned
);
The buffer that you get is an array of POLICY_PRIVILEGE_DEFINITION structures:
typedef struct _POLICY_PRIVILEGE_DEFINITION
{
LSA_UNICODE_STRING Name;
LUID LocalValue;
} POLICY_PRIVILEGE_DEFINITION, *PPOLICY_PRIVILEGE_DEFINITION;
For example:
#include <ntlsa.h>
NTSTATUS status;
LSA_HANDLE policyHandle;
LSA_ENUMERATION_HANDLE enumerationContext = 0;
PPOLICY_PRIVILEGE_DEFINITION buffer;
ULONG countReturned;
ULONG i;
LsaOpenPolicy(..., &policyHandle);
while (TRUE)
{
status = LsaEnumeratePrivileges(policyHandle, &enumerationContext, &buffer, 256, &countReturned);
if (status == STATUS_NO_MORE_ENTRIES)
break; // no more privileges
if (!NT_SUCCESS(status))
break; // error
for (i = 0; i < countReturned; i++)
{
// Privilege definition in buffer[i]
}
LsaFreeMemory(buffer);
}
LsaClose(policyHandle);
Working code:
#include <Windows.h>
#include <iostream>
#include <ntstatus.h>
typedef LONG NTSTATUS, *PNTSTATUS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} STRING, *PSTRING;
typedef LARGE_INTEGER OLD_LARGE_INTEGER;
typedef LARGE_INTEGER POLD_LARGE_INTEGER;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#include <ntlsa.h>
LSA_HANDLE GetPolicyHandle() {
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS ntsResult;
LSA_HANDLE lsahPolicyHandle;
// Object attributes are reserved, so initialize to zeros.
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
// Get a handle to the Policy object.
ntsResult = LsaOpenPolicy(NULL, // Name of the target system.
&ObjectAttributes, // Object attributes.
POLICY_ALL_ACCESS, // Desired access permissions.
&lsahPolicyHandle // Receives the policy handle.
);
if (ntsResult != STATUS_SUCCESS) {
// An error occurred. Display it as a win32 error code.
wprintf(L"OpenPolicy returned %lu\n", LsaNtStatusToWinError(ntsResult));
return NULL;
}
return lsahPolicyHandle;
}
void main() {
NTSTATUS status;
LSA_HANDLE policyHandle;
LSA_ENUMERATION_HANDLE enumerationContext = 0;
PPOLICY_PRIVILEGE_DEFINITION buffer;
ULONG countReturned;
ULONG i;
policyHandle = GetPolicyHandle();
while (TRUE) {
status = LsaEnumeratePrivileges(policyHandle, &enumerationContext,
(PVOID *)&buffer, 256, &countReturned);
if (status == STATUS_NO_MORE_ENTRIES)
break; // no more privileges
if (!NT_SUCCESS(status))
break; // error
for (i = 0; i < countReturned; i++) {
// Privilege definition in buffer[i]
std::wcout << L"KEY:" << buffer[i].Name.Buffer << L" HIGH VALUE:"
<< buffer[i].LocalValue.HighPart << L"LOW VALUE:"
<< buffer[i].LocalValue.LowPart << std::endl;
}
LsaFreeMemory(buffer);
}
LsaClose(policyHandle);
}