Check for environment variable in another process? - c++

In Windows, is there a way to check for the existence of an environment variable for another process? Just need to check existence, not necessarily get value.
I need to do this from code.

If you know the virtual address at which the environment is stored, you can use OpenProcess and ReadProcessMemory to read the environment out of the other process. However, to find the virtual address, you'll need to poke around in the Thread Information Block of one of the process' threads.
To get that, you'll need to call GetThreadContext() after calling SuspendThread(). But in order to call those, you need a thread handle, which you can get by calling CreateToolhelp32Snapshot with the TH32CS_SNAPTHREAD flag to create a snapshot of the process, Thread32First to get the thread ID of the first thread in the process, and OpenThread to get a handle to the thread.

Here is a working example which the printed output can be used to check existence as well as read the value, (build it as the same architecture as the executable's process identifier you must target):
getenv.cpp
#include <string>
#include <vector>
#include <cwchar>
#include <windows.h>
#include <winternl.h>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
// define process_t type
typedef DWORD process_t;
// #define instead of typedef to override
#define RTL_DRIVE_LETTER_CURDIR struct {\
WORD Flags;\
WORD Length;\
ULONG TimeStamp;\
STRING DosPath;\
}\
// #define instead of typedef to override
#define RTL_USER_PROCESS_PARAMETERS struct {\
ULONG MaximumLength;\
ULONG Length;\
ULONG Flags;\
ULONG DebugFlags;\
PVOID ConsoleHandle;\
ULONG ConsoleFlags;\
PVOID StdInputHandle;\
PVOID StdOutputHandle;\
PVOID StdErrorHandle;\
UNICODE_STRING CurrentDirectoryPath;\
PVOID CurrentDirectoryHandle;\
UNICODE_STRING DllPath;\
UNICODE_STRING ImagePathName;\
UNICODE_STRING CommandLine;\
PVOID Environment;\
ULONG StartingPositionLeft;\
ULONG StartingPositionTop;\
ULONG Width;\
ULONG Height;\
ULONG CharWidth;\
ULONG CharHeight;\
ULONG ConsoleTextAttributes;\
ULONG WindowFlags;\
ULONG ShowWindowFlags;\
UNICODE_STRING WindowTitle;\
UNICODE_STRING DesktopName;\
UNICODE_STRING ShellInfo;\
UNICODE_STRING RuntimeData;\
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[32];\
ULONG EnvironmentSize;\
}\
// shortens a wide string to a narrow string
static inline string shorten(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string { buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
// replace all occurrences of substring found in string with specified new string
static inline string string_replace_all(string str, string substr, string nstr) {
size_t pos = 0;
while ((pos = str.find(substr, pos)) != string::npos) {
str.replace(pos, substr.length(), nstr);
pos += nstr.length();
}
return str;
}
// func that splits string by first occurrence of equals sign
vector<string> string_split_by_first_equalssign(string str) {
size_t pos = 0;
vector<string> vec;
if ((pos = str.find_first_of("=")) != string::npos) {
vec.push_back(str.substr(0, pos));
vec.push_back(str.substr(pos + 1));
}
return vec;
}
// checks whether process handle is 32-bit or not
static inline bool IsX86Process(HANDLE process) {
BOOL isWow = true;
SYSTEM_INFO systemInfo = { 0 };
GetNativeSystemInfo(&systemInfo);
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
return isWow;
IsWow64Process(process, &isWow);
return isWow;
}
// helper to open processes based on pid with full debug privileges
static inline HANDLE OpenProcessWithDebugPrivilege(process_t pid) {
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);
CloseHandle(hToken);
return OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
}
// get wide character string of pids environ based on handle
static inline wchar_t *GetEnvironmentStringsW(HANDLE proc) {
PEB peb;
SIZE_T nRead;
ULONG res_len = 0;
PROCESS_BASIC_INFORMATION pbi;
RTL_USER_PROCESS_PARAMETERS upp;
HMODULE p_ntdll = GetModuleHandleW(L"ntdll.dll");
typedef NTSTATUS (__stdcall *tfn_qip)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
tfn_qip pfn_qip = tfn_qip(GetProcAddress(p_ntdll, "NtQueryInformationProcess"));
NTSTATUS status = pfn_qip(proc, ProcessBasicInformation, &pbi, sizeof(pbi), &res_len);
if (status) { return NULL; }
ReadProcessMemory(proc, pbi.PebBaseAddress, &peb, sizeof(peb), &nRead);
if (!nRead) { return NULL; }
ReadProcessMemory(proc, peb.ProcessParameters, &upp, sizeof(upp), &nRead);
if (!nRead) { return NULL; }
PVOID buffer = upp.Environment;
ULONG length = upp.EnvironmentSize;
wchar_t *res = new wchar_t[length / 2 + 1];
ReadProcessMemory(proc, buffer, res, length, &nRead);
if (!nRead) { return NULL; }
res[length / 2] = 0;
return res;
}
// get env of pid as a narrow string
string env_from_pid(process_t pid) {
string envs;
HANDLE proc = OpenProcessWithDebugPrivilege(pid);
wchar_t *wenvs = NULL;
if (IsX86Process(GetCurrentProcess())) {
if (IsX86Process(proc)) {
wenvs = GetEnvironmentStringsW(proc);
}
} else {
if (!IsX86Process(proc)) {
wenvs = GetEnvironmentStringsW(proc);
}
}
string arg;
if (wenvs == NULL) {
return "";
} else {
arg = shorten(wenvs);
}
size_t i = 0;
do {
size_t j = 0;
vector<string> envVec = string_split_by_first_equalssign(arg);
for (const string &env : envVec) {
if (j == 0) {
if (env.find_first_of("%<>^&|:") != string::npos) { continue; }
if (env.empty()) { continue; }
envs += env;
} else { envs += "=\"" + string_replace_all(env, "\"", "\\\"") + "\"\n"; }
j++;
}
i += wcslen(wenvs + i) + 1;
arg = shorten(wenvs + i);
} while (wenvs[i] != L'\0');
if (envs.back() == '\n') { envs.pop_back(); }
if (wenvs != NULL) { delete[] wenvs; }
CloseHandle(proc);
return envs;
}
// test function (can be omitted)
int main(int argc, char **argv) {
if (argc == 2) {
printf("%s", env_from_pid(stoul(string(argv[1]), nullptr, 10)).c_str());
printf("%s", "\r\n");
} else {
printf("%s", env_from_pid(GetCurrentProcessId()).c_str());
printf("%s", "\r\n");
}
return 0;
}
buildx86.sh
g++ getenv.cpp -o getenv.exe -std=c++17 -static-libgcc -static-libstdc++ -static -m32
buildx64.sh
g++ getenv.cpp -o getenv.exe -std=c++17 -static-libgcc -static-libstdc++ -static -m64
Quotes are added around the printed value for clarity, and escaping is applied to inner quotes.

With a utility:
You can use Process Explorer.
Right click on the process, go to Properties... and there is an Environment tab which lists the environment variables for that process.
With code:
There doesn't appear to be a Win32 API call to do this directly, but apparently you get fiddle with the results of GetProcessStrings to get access to this information. This CodeProject article has some code to get you started.

Related

SetProcessValidCallTargets trows error 87

Im trying to use the control flow guard api to set a area of memory to be execution valid.
I used these examples for reverence material:
https://github.com/BreakingMalwareResearch/CFGExceptions/blob/master/CFGExceptions/main.cpp
https://github.com/trailofbits/cfg-showcase/blob/master/cfg_valid_targets.cpp#L111
Here is my code:
#include <stdio.h>
#include <Windows.h>
#include <psapi.h>
#include "cfgTest.h"
BOOL GetMemoryAllocationBaseAndRegionSize(PVOID, PVOID*, PSIZE_T);
INT main() {
HANDLE hProcess;
NTSTATUS ntStatus = ERROR_SUCCESS;
STARTUPINFOA startupInfo;
PROCESS_INFORMATION processInformation = { 0 };
CFG_CALL_TARGET_INFO cfgCallTargetInfoList[1];
DWORD dwPid = 0;
SIZE_T stRegionSize = NULL;
PVOID pvAllocationBase = NULL;
// Function pointers
SETPROCESSVALIDCALLTARGETS pSetProcessValidCallTargets = NULL;
PVOID pvAddressToAddCfgExceptionTo = NULL;
//
// Get address of SetProcessValidCallTargets
//
CONST HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
CONST HMODULE hKernelbase = LoadLibraryW(L"Kernelbase.dll");
if (hKernelbase && hNtdll) {
pSetProcessValidCallTargets = (SETPROCESSVALIDCALLTARGETS)GetProcAddress(hKernelbase, "SetProcessValidCallTargets");
pvAddressToAddCfgExceptionTo = GetProcAddress(hNtdll, "NtSetContextThread");
}
else {
return ERROR_MOD_NOT_FOUND;
}
FreeLibrary(hNtdll);
FreeLibrary(hKernelbase);
// Get memory allocation base and region size by calling VirtualProtect.
if (GetMemoryAllocationBaseAndRegionSize(pvAddressToAddCfgExceptionTo, &pvAllocationBase, &stRegionSize) == FALSE) {
ntStatus = ERROR_UNHANDLED_ERROR;
goto lblCleanup;
}
//
// Add cfg exception
//
cfgCallTargetInfoList[0].Flags = CFG_CALL_TARGET_VALID;
cfgCallTargetInfoList[0].Offset = (ULONG_PTR)pvAddressToAddCfgExceptionTo - (ULONG_PTR)pvAllocationBase;;
if (pSetProcessValidCallTargets(GetCurrentProcess(), pvAllocationBase, stRegionSize, 1, cfgCallTargetInfoList) == FALSE) {
printf("%d", GetLastError());
ntStatus = ERROR_UNHANDLED_ERROR;
goto lblCleanup;
}
lblCleanup:
if (processInformation.hProcess) {
CloseHandle(processInformation.hProcess);
}
if (processInformation.hThread) {
CloseHandle(processInformation.hThread);
}
return ntStatus;
}
BOOL GetMemoryAllocationBaseAndRegionSize(PVOID pvAddress, PVOID* ppvAllocationBase, PSIZE_T pstRegionSize) {
SIZE_T stErr = 0;
MEMORY_BASIC_INFORMATION tMemoryBasicInformation = { 0 };
stErr = VirtualQuery(pvAddress, &tMemoryBasicInformation, sizeof(tMemoryBasicInformation));
if (0 == stErr) {
return FALSE;
}
*ppvAllocationBase = tMemoryBasicInformation.AllocationBase;
*pstRegionSize = tMemoryBasicInformation.RegionSize;
return TRUE;
}
#pragma once
typedef enum _VIRTUAL_MEMORY_INFORMATION_CLASS
{
VmPrefetchInformation,
VmPagePriorityInformation,
VmCfgCallTargetInformation
} VIRTUAL_MEMORY_INFORMATION_CLASS;
typedef struct _MEMORY_RANGE_ENTRY
{
PVOID VirtualAddress;
SIZE_T NumberOfBytes;
} MEMORY_RANGE_ENTRY, * PMEMORY_RANGE_ENTRY;
typedef struct _VM_INFORMATION
{
DWORD dwNumberOfOffsets;
DWORD dwMustBeZero;
PDWORD pdwOutput;
PCFG_CALL_TARGET_INFO ptOffsets;
} VM_INFORMATION, * PVM_INFORMATION;
typedef NTSTATUS(NTAPI* NTSETINFORMATIONVIRTUALMEMORY)(
HANDLE hProcess,
VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass,
ULONG_PTR NumberOfEntries,
PMEMORY_RANGE_ENTRY VirtualAddresses,
PVOID VmInformation,
ULONG VmInformationLength
);
typedef BOOL(WINAPI* SETPROCESSVALIDCALLTARGETS)(
HANDLE hProcess,
PVOID VirtualAddress,
SIZE_T RegionSize,
ULONG NumberOfOffsets,
PCFG_CALL_TARGET_INFO OffsetInformation
);
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
My problem is SetProcessValidCallTargets returns FALSE and GetLastError() tells me its 87 which means an invalid parameter has been passed. But i have no idea what is going wrong. Could anyone see whats going on?

Memory leak when using IShellItem2.GetString()

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;
}
}

Why this code runs perfectly in visual studio with multibyte characher set but not with unicode char set?

when I run this code on g++ , it runs smoothly, but when I run this code on visual studio wiith unicode char set option, it doesn't print product id.
can you explain me how to fix this problem and why it happens?
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
wchar_t* GetRegistryKeyValue(const char* RegKey, const char* pPIDName)
{
HKEY Registry;
long ReturnStatus;
DWORD regType = 0;
DWORD regSize = 0;
char* pPID = 0;
ReturnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKey, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &Registry);
if (ReturnStatus == ERROR_SUCCESS)
{
ReturnStatus = RegQueryValueEx(Registry, pPIDName, 0, &regType, 0, &regSize);
pPID = new char[regSize];
/* Get Value. */
ReturnStatus = RegQueryValueEx(Registry, pPIDName, 0, &regType, (LPBYTE)pPID, &regSize);
RegCloseKey(Registry);
if (pPID[regSize] > 127 || pPID[regSize] < 32)
{
pPID[regSize] = '\0';
}
if (regSize > 1)
{
int s = 0;
int i=0;
while (pPID[i] != NULL)
{
s++;
i++;
}
const size_t cSize = s ;
wchar_t* wc = new wchar_t[cSize];
mbstowcs(wc, pPID, cSize);
return wc;
}
else
{
printf("Size not > 1 (%d)\n", regSize);
return NULL;
}
}
else
{
RegCloseKey(Registry);
return NULL;
}
}
int main()
{
wchar_t * resultData=NULL;
resultData = GetRegistryKeyValue("SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", "ProductId");
wcout << resultData;
cout << endl;
delete resultData;
system("PAUSE");
return 0;
}
You are using TCHAR-based APIs that rely on char* data when compiling for MBCS and wchar_t* data when compiling for Unicode. You are not taking that difference into account correctly. Your code should not even compile when set for Unicode, because you would be passing ANSI parameters to Unicode functions. Since you want to return a Unicode string, you should be using the Unicode API functions to begin with.
There are other logic errors in your code, such as leaking memory, not allocating the right number of bytes for the wc buffer, insufficient error handling, etc.
Try something more like this instead:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
wchar_t* GetRegistryKeyValue(const wchar_t* RegKey, const wchar_t* pValueName)
{
long ReturnStatus;
DWORD regType = 0;
DWORD regSize = 0;
DWORD dwFlags = RRF_RT_REG_SZ | RRF_RT_REG_MULTI_SZ | RRF_RT_REG_EXPAND_SZ | RRF_SUBKEY_WOW6464KEY;
wchar_t* ws = 0;
ReturnStatus = RegGetValueW(HKEY_LOCAL_MACHINE, RegKey, pValueName, dwFlags, &regType, 0, &regSize);
if (ReturnStatus == ERROR_SUCCESS)
{
ws = new wchar_t[regSize / sizeof(WCHAR)];
ReturnStatus = RegGetValueW(HKEY_LOCAL_MACHINE, RegKey, pValueName, dwFlags, &regType, ws, &regSize);
if (ReturnStatus != ERROR_SUCCESS)
{
delete[] ws;
ws = NULL;
}
}
return ws;
}
int main()
{
wchar_t* resultData = GetRegistryKeyValue(L"SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", L"ProductId");
wcout << resultData << endl;
delete[] resultData;
system("PAUSE");
return 0;
}
Alternatively:
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
wstring GetRegistryKeyValue(const wchar_t* RegKey, const wchar_t* pValueName)
{
long ReturnStatus;
DWORD regType = 0;
DWORD regSize = 0;
DWORD dwFlags = RRF_RT_REG_SZ | RRF_RT_REG_MULTI_SZ | RRF_RT_REG_EXPAND_SZ | RRF_SUBKEY_WOW6464KEY;
ReturnStatus = RegGetValueW(HKEY_LOCAL_MACHINE, RegKey, pValueName, dwFlags, &regType, 0, &regSize);
if (ReturnStatus == ERROR_SUCCESS)
{
vector<BYTE> buf;
buf.resize(regSize);
ReturnStatus = RegGetValueW(HKEY_LOCAL_MACHINE, RegKey, pValueName, dwFlags, &regType, &buf[0], &regSize);
if (ReturnStatus == ERROR_SUCCESS)
return wstring((wchar_t*)&buf[0], (regSize / sizeof(WCHAR)) - 1);
}
return wstring();
}
int main()
{
wcout << GetRegistryKeyValue(L"SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", L"ProductId") << endl;
system("PAUSE");
return 0;
}
According to this documentation:
http://www.cplusplus.com/reference/cstdlib/mbstowcs/
If max characters are successfully translated, the resulting string stored in dest is not null-terminated.
The tricky way you're trying to calculate string length (which, btw, could be replaced by strlen(pPID) ) will skip the '\0' in the first string and thus the result might not have '\0' on the end. Why don't you use regSize there?

Enumerating Privileges (Local Security Policy)

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);
}

Windows CD Burning API

We need to programatically burn files to CD in a C\C++ Windows XP/Vista application we are developing using Borlands Turbo C++.
What is the simplest and best way to do this? We would prefer a native windows API (that doesnt rely on MFC) so as not to rely on any third party software/drivers if one is available.
We used the following:
Store files in the directory returned by GetBurnPath, then write using Burn. GetCDRecordableInfo is used to check when the CD is ready.
#include <stdio.h>
#include <imapi.h>
#include <windows.h>
struct MEDIAINFO {
BYTE nSessions;
BYTE nLastTrack;
ULONG nStartAddress;
ULONG nNextWritable;
ULONG nFreeBlocks;
};
//==============================================================================
// Description: CD burning on Windows XP
//==============================================================================
#define CSIDL_CDBURN_AREA 0x003b
SHSTDAPI_(BOOL) SHGetSpecialFolderPathA(HWND hwnd, LPSTR pszPath, int csidl, BOOL fCreate);
SHSTDAPI_(BOOL) SHGetSpecialFolderPathW(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate);
#ifdef UNICODE
#define SHGetSpecialFolderPath SHGetSpecialFolderPathW
#else
#define SHGetSpecialFolderPath SHGetSpecialFolderPathA
#endif
//==============================================================================
// Interface IDiscMaster
const IID IID_IDiscMaster = {0x520CCA62,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
const CLSID CLSID_MSDiscMasterObj = {0x520CCA63,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
typedef interface ICDBurn ICDBurn;
// Interface ICDBurn
const IID IID_ICDBurn = {0x3d73a659,0xe5d0,0x4d42,{0xaf,0xc0,0x51,0x21,0xba,0x42,0x5c,0x8d}};
const CLSID CLSID_CDBurn = {0xfbeb8a05,0xbeee,0x4442,{0x80,0x4e,0x40,0x9d,0x6c,0x45,0x15,0xe9}};
MIDL_INTERFACE("3d73a659-e5d0-4d42-afc0-5121ba425c8d")
ICDBurn : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetRecorderDriveLetter(
/* [size_is][out] */ LPWSTR pszDrive,
/* [in] */ UINT cch) = 0;
virtual HRESULT STDMETHODCALLTYPE Burn(
/* [in] */ HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE HasRecordableDrive(
/* [out] */ BOOL *pfHasRecorder) = 0;
};
//==============================================================================
// Description: Get burn pathname
// Parameters: pathname - must be at least MAX_PATH in size
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int GetBurnPath(char *path)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
BOOL flag;
if (pICDBurn->HasRecordableDrive(&flag) == S_OK) {
if (SHGetSpecialFolderPath(0, path, CSIDL_CDBURN_AREA, 0)) {
strcat(path, "\\");
}
else {
ret = 1;
}
}
else {
ret = 2;
}
pICDBurn->Release();
}
else {
ret = 3;
}
return ret;
}
//==============================================================================
// Description: Get CD pathname
// Parameters: pathname - must be at least 5 bytes in size
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int GetCDPath(char *path)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
BOOL flag;
WCHAR drive[5];
if (pICDBurn->GetRecorderDriveLetter(drive, 4) == S_OK) {
sprintf(path, "%S", drive);
}
else {
ret = 1;
}
pICDBurn->Release();
}
else {
ret = 3;
}
return ret;
}
//==============================================================================
// Description: Burn CD
// Parameters: None
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int Burn(void)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
if (pICDBurn->Burn(NULL) != S_OK) {
ret = 1;
}
pICDBurn->Release();
}
else {
ret = 2;
}
return ret;
}
//==============================================================================
bool GetCDRecordableInfo(long *FreeSpaceSize)
{
bool Result = false;
IDiscMaster *idm = NULL;
IDiscRecorder *idr = NULL;
IEnumDiscRecorders *pEnumDiscRecorders = NULL;
ULONG cnt;
long type;
long mtype;
long mflags;
MEDIAINFO mi;
try {
CoCreateInstance(CLSID_MSDiscMasterObj, 0, CLSCTX_ALL, IID_IDiscMaster, (void**)&idm);
idm->Open();
idm->EnumDiscRecorders(&pEnumDiscRecorders);
pEnumDiscRecorders->Next(1, &idr, &cnt);
pEnumDiscRecorders->Release();
idr->OpenExclusive();
idr->GetRecorderType(&type);
idr->QueryMediaType(&mtype, &mflags);
idr->QueryMediaInfo(&mi.nSessions, &mi.nLastTrack, &mi.nStartAddress, &mi.nNextWritable, &mi.nFreeBlocks);
idr->Release();
idm->Close();
idm->Release();
Result = true;
}
catch (...) {
Result = false;
}
if (Result == true) {
Result = false;
if (mtype == 0) {
// No Media inserted
Result = false;
}
else {
if ((mflags & 0x04) == 0x04) {
// Writable Media
Result = true;
}
else {
Result = false;
}
if (Result == true) {
*FreeSpaceSize = (mi.nFreeBlocks * 2048);
}
else {
*FreeSpaceSize = 0;
}
}
}
return Result;
}
To complement the accepted answer, we added this helper function to programatically change the burn directory on the fly as this was a requirement of ours.
typedef HMODULE (WINAPI * SHSETFOLDERPATHA)( int , HANDLE , DWORD , LPCTSTR );
int SetBurnPath( char * cpPath )
{
SHSETFOLDERPATHA pSHSetFolderPath;
HANDLE hShell = LoadLibraryA( "shell32.dll" );
if( hShell == NULL )
return -2;
DWORD dwOrdinal = 0x00000000 + 231;
pSHSetFolderPath = (SHSETFOLDERPATHA)GetProcAddress( hShell, (LPCSTR)dwOrdinal );
if( pSHSetFolderPath == NULL )
return -3;
if( pSHSetFolderPath( CSIDL_CDBURN_AREA, NULL, 0, cpPath ) == S_OK )
return 0;
return -1;
}
This is the information for IMAPI in MSDN site http://msdn.microsoft.com/en-us/library/aa939967.aspx
You should be able to use the shell's ICDBurn interface. Back in the XP day MFC didn't even have any classes for cd burning. I'll see if I can find some examples for you, but it's been a while since I looked at this.