GetRegionData() not working for stack allocated buffer - winapi

I am using the win32 function GetRegionData(...) to extract the exact rectangles which make up the invalidated paint region in response to a WM_PAINT message.
The following code works correctly and the second call to GetRegionData succeeds.
DWORD uRegionSize = GetRegionData(hRgn, sizeof(RGNDATA), NULL); // Send NULL request to get the storage size
RGNDATA* pData = (RGNDATA*)(new char[uRegionSize]); // Allocate space for the region data
pData->rdh.dwSize = uRegionSize;
DWORD uSizeCheck = GetRegionData(hRgn, uRegionSize, pData);
if (uSizeCheck != uRegionSize) {
// FAIL!
delete[] pData;
return;
}
...
do stuff with rectangles
...
But when I tried to move the data buffer to a member variable allocated on the stack, GetRegionData fails every time returning 0.
In my header:
char UpdateRegionData[LOTS_MORE_BYTES_THAN_NEEDED];
In my cpp:
DWORD uRegionSize = GetRegionData(hRgn, sizeof(RGNDATA), NULL); // Send NULL request to get the storage size
RGNDATA* pData2 = (RGNDATA*)UpdateRegionData;
pData2->rdh.dwSize = uRegionSize;
DWORD uSizeCheck = GetRegionData(hRgn, uRegionSize, pData2);
if (uSizeCheck != uRegionSize) {
// FAIL!
return;
}
The only thing different between the 2 versions is the memory allocation, but the second one fails. GetLastError() returns code 183 which is ERROR_ALREADY_EXISTS which doesn't seem to make much sense.

Thanks to Raymond for pointing out the size error - that was indeed an error, but it was not cause of the issue. The actual cause was byte alignment. The project I am working on has its byte alignment set to a default of 1. When I specified 4 byte alignment for the buffer using __declspec(align(4)) then the problem was solved.

Related

Whic is correct way to use pair GlobalLock() \ GlobalUnlock()?

Documentation about GlobalLock says:
Return value
If the function succeeds, the return value is a pointer to the first byte of the memory block.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
Remarks
Each successful call that a process makes to GlobalLock for an object must be matched by a corresponding call to GlobalUnlock.
....
If the specified memory block has been discarded or if the memory block has a zero-byte size, this function returns NULL.
So, as we see, GlobalLock() could return NULL if error or memory block size has zero-byte size.
On the other hand, GlobalUnlock() should be called ONLY if GlobalLock() was successful. So, how correctly define case when GlobalUnlock() should be called? What approach is correct from following variants and why?
Variant 0:
HGLOBAL hMem = /*some handle on global memory block*/;
// lock block
auto pMem = static_cast<LPBYTE>(::GlobalLock(hMem));
if (pMem!=nullptr)
{
// ... work with pMem
}
// call unlock in any case
::GlobalUnlock(hMem);
Variant 1:
HGLOBAL hMem = /*some handle on global memory block*/;
// lock block
auto pMem = static_cast<LPBYTE>(::GlobalLock(hMem));
if (pMem!=nullptr)
{
// ... work with pMem
// unlock block
::GlobalUnlock(hMem);
}
Variant 2:
HGLOBAL hMem = /*some handle on global memory block*/;
// lock block
auto pMem = static_cast<LPBYTE>(::GlobalLock(hMem));
auto isMemLocked = (pMem!=nullptr);
if (isMemLocked)
{
// ... work with pMem
}
else
{
// is it real error?
isMemLocked = ::GetLastError()==NOERROR;
}
if (isMemLocked)
{
// unlock block
::GlobalUnlock(hMem);
}
Update:
We assume that hMem is valid (handle is not NULL).
P.S.: Great thanks for your answers.
from GlobalLock documentation
Each successful call that a process makes to GlobalLock for an
object must be matched by a corresponding call to GlobalUnlock.
and
If the function succeeds, the return value is a pointer to the first
byte of the memory block.
If the function fails, the return value is NULL
so we need call GlobalUnlock only if previous call to GlobalLock return not NULL
pattern is next:
if (PVOID pv = GlobalLock(hg))
{
//...
GlobalUnlock(hg);
}
in case we try do GlobalLock on memory block which has a zero-byte size - we always got 0 and ERROR_DISCARDED. we not need call GlobalUnlock in this case - it simply return ERROR_NOT_LOCKED in this case.
if look from c++ perspective GlobalAlloc with GMEM_MOVEABLE flag return ~ weak_ptr - so HGLOBAL by fact point to object like weak_ptr in this case. the GlobalLock(hg) is analog of weak_ptr::lock which return shared_ptr (direct pointer to actual memory block). and GlobalLock is analog of release this shared_ptr. after call GlobalDiscard on HGLOBAL hg - shared_ptr (real memory block) will be destroyed. but HGLOBAL hg (weak_ptr) still will be valid, simply every GlobalLock(hg) (weak_ptr::lock) call on it fail with error ERROR_DISCARDED. finally GlobalFree delete this weak_ptr. demo code:
if (HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, 8))
{
if (PVOID pv = GlobalLock(hg))
{
ASSERT(!GlobalDiscard(hg));
GlobalUnlock(hg);
}
ASSERT(GlobalDiscard(hg));
ASSERT(!GlobalLock(hg));
ASSERT(GetLastError() == ERROR_DISCARDED);
ASSERT(!GlobalUnlock(hg));
ASSERT(GetLastError() == ERROR_NOT_LOCKED);
GlobalFree(hg);
}
if (HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, 0))
{
ASSERT(!GlobalLock(hg));
ASSERT(GetLastError() == ERROR_DISCARDED);
ASSERT(!GlobalUnlock(hg));
ASSERT(GetLastError() == ERROR_NOT_LOCKED);
GlobalFree(hg);
}
if (PVOID p = GlobalLock(hGlob))
{
...
GlobalUnlock(hGlob);
}
is the correct pattern and answered by RbMm but variant 0 is also accepted by Windows because GlobalUnlock(NULL) returns TRUE without doing anything else. This is of course a undocumented implementation detail and I only verified this on Windows NT 4 and Windows 8 but I assume everything in between acts the same.
This happens because Windows uses certain tag bits and alignment to tell if the HGLOBAL is fixed or moveable memory and NULL obviously has no tag bits set so GlobalUnlock just returns.
There is no reason to use this alternative pattern because:
You would be relying on implementation details.
You cannot omit the GlobalLock return value check unless you know that the HGLOBAL is fixed memory and in that case you can omit all the locking/unlocking because it is pointless overhead if you are only using fixed memory.

ReadFile on volume fails with ERROR_INVALID_PARAMETER after FSCTL_ALLOW_EXTENDED_DASD_IO

I'm reading a volume (logical drive) with ReadFile. I'm using DeviceIoControl with FSCTL_ALLOW_EXTENDED_DASD_IO code, because I want to have access to all (including the last) bytes and had an issue trying to read last 512 bytes (ReadFile successed, but reported 0 bytes read) and saw advice to use it. Unfortunately, ReadFile fails being called after that DeviceIoControl called.
In code it looks like this (all success checks are omitted for the brevity):
HANDLE fd;
DWORD junk;
int lenToBeRead = 0x1000;
DWORD nread;
char* alignedBuf = new char[lenToBeRead];
fd = CreateFile("path to volume", FILE_READ_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL)) //success
DeviceIoControl(fd, FSCTL_ALLOW_EXTENDED_DASD_IO,
NULL, 0, NULL, 0, &junk, (LPOVERLAPPED) NULL) //success
ReadFile(fd, alignedBuf, (DWORD) lenToBeRead, &nread, NULL)
// fails with 0x57 code, ERROR_INVALID_PARAMETER
All work with fd handle is synchronous.
EDIT. I solved the problem. I was trying to read last bytes. So my volume was of length L = 0x...200 and I had my handle on position pos = L - 0x200. What I had done before I did the FSCTL_ALLOW_EXTENDED_DASD_IO thing - I cut lenToBeRead to fit in remaining space (so, if it was 0x1000, it would change to 0x200), because I had found that ReadFile did not guarantee read all the bytes to the EOF in case of lenToBeRead is greater than amount of bytes remained from current handle position. This did not help, ReadFilewas still returning with success and 0 bytes read. I deleted that fix and then used FSCTL_ALLOW_EXTENDED_DASD_IO, which deliver me then ReadFile failing with ERROR_INVALID_PARAMETER on lenToBeRead = 0x1000. I totally forgot about the first fix and remembered now and now it works.
I found the solution and add it to the question body.
What one has to keep in mind when working with ReadFile is to control arguments (length) to not cross the boundary of the file.
I had tried it as a fix before doing the FSCTL_ALLOW_EXTENDED_DASD_IO thing and it did not help. But combination of the FSCTL_ALLOW_EXTENDED_DASD_IO thing and the boundary check gave me wanted result - I could read that last bytes.

Error while reading other process memory

I'm using ReadProcessMemory to read a single byte out of a process i've created.
Since i'm attaching as a debugger, i'm reading addresses that are being executed now (or in the near past).
but i get a 299 error for ReadProcessMemory via GetLastError() on some addresses only (some works fine..)
On the cases i get an error, i call VirtualQueryEx, and the memInfo protect is 0x1, while the type & baseAddress are 0x0 (but the region size is some normal number), also VirtualQueryEx isn't failing..
if i call VirtualProtectEx for those cases, i get error 487 (Attempt to access invalid address).
i thought maybe the address i'm trying to read is paged out, thus all the errors, but it doesn't seem right since, as i've already mentioned, its an address that was executed recently.
ideas anyone?
You want to loop through all the memory, calling VirtualQueryEx() to make sure the memory is valid before calling ReadProcessMemory()
You need to make sure that MEMORY_BASIC_INFORMATION.State is MEM_COMMIT in most cases.
This whole operation can be easy to screw up, because you didn't supply any code I will provide a working solution that should work in 95% of situations. It's ok for bytesRead to be different than regionSize, you just need to handle that situation correctly. You don't need to take permissions in most cases using VirtualProtect because all valid memory should have read access.
int main()
{
DWORD procid = GetProcId("notepad.exe");
unsigned char* addr = 0;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
{
if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS)
{
char* buffer = new char[mbi.RegionSize]{ 0 };
SIZE_T bytesRead = 0;
if (ReadProcessMemory(hProc, addr, buffer, mbi.RegionSize, &bytesRead))
{
if (bytesRead)
{
//scan from buffer[0] to buffer[bytesRead]
}
else
{
//scan from buffer[0] to buffer[mbi.RegionSize]
}
}
delete[] buffer;
}
addr += mbi.RegionSize;
}
CloseHandle(hProc);
}
Reminder: This is just a PoC to teach you the concept.

why it was failed SetFilePointer()?

BOOL SetDeviceID(HANDLE device,char *id){//
char data[2];
data[0]=0x02;
data[1]=0x27;
DWORD dwPtr=SetFilePointer(device,0x33,//distance
NULL,//
FILE_BEGIN);
if(dwPtr==INVALID_SET_FILE_POINTER) cout<<GetLastError()<<endl;
BOOL result=WriteFile(device,data,2,NULL,NULL);
//cout<<GetLastError()<<endl;
if(result==false)cout<<"Fail WRITE "<<endl;
return TRUE;
}
HANDLE GetDeviceHandle(char *path){
HANDLE handle= CreateFile(LPCSTR(path),
GENERIC_ALL,//
0,
NULL,
OPEN_EXISTING,
NULL,
NULL);
if(handle==INVALID_HANDLE_VALUE){
cout<<"fail to createfile()"<<endl;
exit(1);
}
else return handle;
}
this is some code of my works.
I am going to read/write directly device(usb)
on ReadFile() case, It was successful.
But, I have tried to call SetFilePointer
But GetLastError returned 87. it means invalid input
What is the problem? on my code
shortly, CreateFile,ReadFile is ok but SetFilePointer and WriteFile failed
When you are directly accessing a disk device you cannot seek to positions in the middle of a sector. The position must always be a multiple of the sector length. And 0x33 is not a mutiple of your sector length.
What you will need to do is read an entire sector. Modify the bytes that need to be modified. And finally write back the entire sector.

Windows RegQueryValueEx odd return results

I am getting odd results when using RegQueryValueEx and I cannot figure out why.
This is what I had set up before making the RegQueryValueEx
DWORD dataSize;
TCHAR data[256];
The first time I call
LONG ret = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
ret is equal to 234 (ERROR_MORE_DATA)
But when I call the same thing on the next line
LONG ret2 = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
ret2 is equal to 0 (ERROR_SUCCESS)
Why would this function return ERROR_MORE_DATA the first time I call it, then return ERROR_SUCESS on the same call on the very next line?
I attempted to change TCHAR data[1024] but I got the exact same results. Any ideas?
Complete code:
for( int i=0; i<NUM_HISTORY; i++){
CString dataKey = getDataKey(i);
DWORD dataSize = 1024;
TCHAR data[1024];
LONG ret = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
LONG ret2 = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
// Breakpoint to see what ret and ret2 are equal to
int j = 0;
}
This is by design. The first call failed because you specified as size that was too small. But what you didn't count on is that it also updated your dataSize variable. To tell you how much memory to allocate so the call can succeed.
So the second call succeeded since you now specify a size that's exactly correct. But without doing the other thing you needed to do, actually make the buffer bigger. Nothing much good can happen when the call then causes a buffer overflow and corrupt your stack frame, be sure to use the /RTC compile option so you'll get a runtime error from that.
You avoided this problem by increasing the buffer size from 256 to 1024. But your code is still incorrect, your program will fail miserably if the registry value ever gets larger than 1024 bytes. Don't use a local array, use the new operator or malloc() to allocate the buffer so it can never fail like this. Or simply fail the call and declare "bad data".
Also note another bug, the dataSize is in bytes but the buffer is TCHAR, not a byte. Which is probably why you didn't corrupt the stack frame, the buffer was big enough by accident. You don't want to rely on accidents like this. Consider a helper class like CRegKey to avoid these kind of mistakes.

Resources