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?
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;
}
}
https://learn.microsoft.com/ko-kr/windows/win32/api/virtdisk/nf-virtdisk-querychangesvirtualdisk?redirectedfrom=MSDN
DWORD QueryChangesVirtualDisk(
HANDLE VirtualDiskHandle,
PCWSTR ChangeTrackingId,
ULONG64 ByteOffset,
ULONG64 ByteLength,
QUERY_CHANGES_VIRTUAL_DISK_FLAG Flags,
PQUERY_CHANGES_VIRTUAL_DISK_RANGE Ranges,
PULONG RangeCount,
PULONG64 ProcessedLength
);
I am trying to get difference between two virtual disk or two Resillent Change Tracking ID (RCT)
but RangeCount always return as 0
I opened virtual disk file (vhdx) with two enum parameter VIRTUAL_DISK_ACCESS_GET_INFO, OPEN_VIRTUAL_DISK_FLAG_NONE
opStatus = OpenVirtualDisk(
&storageType,
VirtualDiskPath,
VIRTUAL_DISK_ACCESS_GET_INFO,
OPEN_VIRTUAL_DISK_FLAG_NONE,
NULL,
&vhdHandle ); // output handle
then use QueryChangesVirtualDisk()
opStatus = QueryChangesVirtualDisk(
vhdHandle,
ChangeTrackingId,
0,
32212254720,
QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
pRctRanges,
&rctRangeCnt,
&processedLength
);
OpenVirtualDisk() QueryChangesVirtualDisk()
Both function did not return error but in every case RangeCount always has a value of 0
There is definitely a difference between virtual disk file and RCT ID
please give me some advice
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <initguid.h>
#include <strsafe.h>
#include <virtdisk.h>
#include "Storage.h"
DWORD QueryChangesVirtualDisk(_In_ LPCWSTR VirtualDiskPath, _In_ LPCWSTR ChangeTrackingId)
{
VIRTUAL_STORAGE_TYPE storageType;
PGET_VIRTUAL_DISK_INFO diskInfo;
ULONG diskInfoSize;
DWORD opStatus;
HANDLE vhdHandle;
QUERY_CHANGES_VIRTUAL_DISK_RANGE *pRctRanges;
ULONG rctRangeCnt;
ULONG64 processedLength;
UINT i;
vhdHandle = INVALID_HANDLE_VALUE;
diskInfo = NULL;
diskInfoSize = sizeof(GET_VIRTUAL_DISK_INFO);
rctRangeCnt = 0L;
processedLength = 0L;
pRctRanges = NULL;
i = 0;
diskInfo = (PGET_VIRTUAL_DISK_INFO)malloc(diskInfoSize);
if (diskInfo == NULL)
{
opStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;
//////////////////////////////////////////////////////////////////
opStatus = OpenVirtualDisk(
&storageType,
VirtualDiskPath,
VIRTUAL_DISK_ACCESS_GET_INFO,
OPEN_VIRTUAL_DISK_FLAG_NONE,
NULL,
&vhdHandle);
if (opStatus != ERROR_SUCCESS)
{
wprintf(L"OpenVirtualDisk fail\n");
goto Cleanup;
}
opStatus = QueryChangesVirtualDisk(
vhdHandle,
ChangeTrackingId,
0,
32212254720,
QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
pRctRanges,
&rctRangeCnt,
&processedLength
);
wprintf(L"rctRangeCnt : %lu\n", rctRangeCnt);
wprintf(L"processedLength : %llu\n", processedLength);
for (i = 0; i < rctRangeCnt; i++)
{
wprintf(L"ByteOffset : %lld ByteLength : %lld\n", pRctRanges[i].ByteOffset, pRctRanges[i].ByteLength);
}
if (opStatus != ERROR_SUCCESS)
{
wprintf(L"QueryChangesVirtualDisk fail\n");
goto Cleanup;
}
Cleanup:
if (opStatus == ERROR_SUCCESS)
{
wprintf(L"success\n");
}
else
{
wprintf(L"error = %u\n", opStatus);
}
if (vhdHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(vhdHandle);
}
if (diskInfo != NULL)
{
free(diskInfo);
}
if (pRctRanges != NULL)
{
for (i = 0; i < rctRangeCnt; i++)
{
free(&pRctRanges[i]);
}
}
return opStatus;
}
The problem is that you pass a null-pointer for the Ranges argument and zero for RangeCount.
For Ranges you're supposed to pass a pointer to the first element or an array of QUERY_CHANGES_VIRTUAL_DISK_RANGE elements, and the RangeCount should be initialized to the number of elements in that array.
When the QueryChangesVirtualDisk function returns it will have modified RangeCount to be the number of initialized elements in the Ranges array.
From the documentation you linked to, about the RangeCount argument:
On input, the value indicates the number of QUERY_CHANGES_VIRTUAL_DISK_RANGE structures that the array that the Ranges parameter points to can hold. On output, the value contains the number of QUERY_CHANGES_VIRTUAL_DISK_RANGE structures that the method placed in the array.
In short: The function doesn't create this array for you, you must do it before you call the function.
I'm just beginning with directx/directinput development and I'm running some tests with some code samples I've found online. Anyway, I want to hook an application that uses dinput8 to send my own custom input to the forewindow and I'm working with this base to do it:
// dllmain.cpp : Defines the entry point for the DLL application.
#define _CRT_SECURE_NO_WARNINGS // ignore some warnings...
#define _CRT_NON_CONFORMING_SWPRINTFS // ...
#include "stdio.h"
#include <windows.h>
#include "detours.h"
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <time.h>
#include "dinput.h"
#pragma comment(lib, "detours.lib")
#pragma comment(lib, "user32.lib")
typedef HRESULT(__stdcall* GetDeviceState_t)(LPDIRECTINPUTDEVICE, DWORD, LPVOID *);
HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE pDevice, DWORD cbData, LPVOID *lpvData);
DWORD Base = 0;
DWORD GetDeviceStateOffset = 0x7670; // This is the offset of GetDeviceState from DInput8.dll
// Open IDA and Import the DInput8.dll, then look in the Functions Table for DirectInput8Create
// There is an Address (1000XXXX or 0CXXXXX) - copy it and save it for later
// Then take a look for CDIDev_GetDeviceState and copy that address too
// Now substract the Address from CDIDev_GetDeviceState from DIrectInput8Create and u'll get your offset
HANDLE tmpHandle = NULL;
HMODULE hModDInput8 = NULL;
DWORD dwGetDeviceState = NULL;
FARPROC dwDirectInput8Create = NULL;
struct MyKeys
{
BYTE Key;
DWORD StartTime;
DWORD TTL;
BOOLEAN isDown;
};
MyKeys KeyBuffer[256];
DWORD WINAPI HookThread();
void add_log(char* format, ...);
void SendKeyDInput(byte DIK_, DWORD time);
GetDeviceState_t pGetDeviceState;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
add_log("==========LOG START==========");
add_log("DLL Attached");
add_log("Creating Thread...");
tmpHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&HookThread, 0, 0, 0);
if (!tmpHandle)
{
add_log("ThreadCreation Failed!");
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DWORD WINAPI HookThread()
{
Base = (DWORD)GetModuleHandleA("test.exe");
add_log("Thread Created");
add_log("game.exe Base: %x", Base);
while (!hModDInput8)
{
add_log("Searching dinput8.dll...");
hModDInput8 = GetModuleHandle(L"dinput8.dll");
Sleep(100);
}
add_log("Found dinput8.dll: %x !", hModDInput8);
while (!dwDirectInput8Create)
{
add_log("Searching GetDeviceState...");
dwDirectInput8Create = GetProcAddress(hModDInput8, "DirectInput8Create");
Sleep(100);
}
add_log("Found DirectInput8Create: %x !", dwDirectInput8Create);
dwGetDeviceState = (DWORD)((DWORD)dwDirectInput8Create - GetDeviceStateOffset);
add_log("GetDevicestate is here (DirectInput8Create - %x): %x", GetDeviceStateOffset, dwGetDeviceState);
add_log("Hooking GetDeviceState...");
pGetDeviceState = (GetDeviceState_t)DetourAttach(&(PVOID&)dwGetDeviceState, (PBYTE)hkGetDeviceState);
add_log("Initiate Keyboard Buffer...");
//initiate buffer
for (int i = 0; i < 256; i++)
{
KeyBuffer[i].isDown = false;
KeyBuffer[i].Key = 0;
KeyBuffer[i].StartTime = 0;
KeyBuffer[i].TTL = 0;
}
add_log("Going into Main Loop...");
while (true)
{
if (GetAsyncKeyState(VK_F5) & 1 << 15)
{
// We check the Most Sigificant Bit from VK_F5 (F5) whilst we shifted it with 15 bits to left 1
// and then a small delay so we have enaught time to release the key
add_log("F5 pushed attempting to sendkey");
// Sleep a short time so we have time to release the F5 Key
Sleep(500);
// Now we send a A Key with 1 sec time to our Game
SendKeyDInput(DIK_A, 1000);
}
}
return 0;
}
void SendKeyDInput(byte DIK, DWORD time)
{
KeyBuffer[DIK].Key = DIK;
KeyBuffer[DIK].TTL = time;
KeyBuffer[DIK].StartTime = GetTickCount();
}
HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE lpDevice, DWORD cbData, LPVOID *lpvData)
{
HRESULT hResult = DI_OK;
static BYTE buffer[256];
int key_count = 0;
for (int i = 0; i<256; i++)
{
if (KeyBuffer[i].Key != 0 && KeyBuffer[i].TTL>0 && KeyBuffer[i].StartTime != 0)
{
if (GetTickCount() > KeyBuffer[i].StartTime + KeyBuffer[i].TTL && KeyBuffer[i].isDown)
{
KeyBuffer[i].Key = 0;
KeyBuffer[i].StartTime = 0;
KeyBuffer[i].TTL = 0;
KeyBuffer[i].isDown = false;
buffer[KeyBuffer[i].Key] = 0;
}
else {
KeyBuffer[i].isDown = true;
buffer[KeyBuffer[i].Key] = 0x80;
key_count += 1;
add_log("Sending Key %x for %i milliseconds count: %i", KeyBuffer[i].Key, KeyBuffer[i].TTL, key_count);
}
}
}
if (key_count != 0)
{
cbData = 256;
memcpy(lpvData, buffer, cbData);
}
else {
hResult = pGetDeviceState(lpDevice, cbData, lpvData);
}
return hResult;
}
//Creates a Logfile in the Game Directory
void add_log(char* format, ...)
{
HANDLE filehandle;
DWORD dwReadBytes;
char buffer[2048];
char writebuffer[2048];
va_list args;
va_start(args, format);
vsprintf_s(buffer, format, args);
filehandle = CreateFile(L"Log.txt", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0);
SetFilePointer(filehandle, 0, 0, FILE_END);
sprintf_s(writebuffer, 2048, "Log Added: %s\r\n", buffer);
WriteFile(filehandle, writebuffer, strlen(writebuffer), &dwReadBytes, 0);
CloseHandle(filehandle);
}
The only issue in this code is when I attempt to send input, it doesn't go through. I've gotten some help and narrowed down a solution to this, which was: "Try GetDeviceState hk just memset(buffer, 0, size) or SendDeviceData". I've searched around a bit and I've been unable to find more on how to implement this solution and I'm stumped.
Could one of you kind people show me how I could use this information to fix this base? I'd be extremely grateful, thanks.
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.
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.