How to properly use ReadProcessMemory without raising warnings - c++

What is the correct way to use ReadProcessMemory?
I am currently using it like this:
DWORD read_mem(DWORD addr)
{
DWORD buffer = 0x0;
if (!ReadProcessMemory(phandle, LPCVOID(addr), &buffer, sizeof(DWORD), nullptr))
{
return false;
}
return buffer;
}
This causes a warning due to addr being wrongly casted.
warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
Other example code I've seen such as this uses the same approach.
So what is the proper way to use this function without getting a warning?

"cast to pointer from integer of different size" - this means that DWORD and void* are different byte sizes, which can only happen if you are compiling your code for 64-bit, as they are the same byte size in a 32-bit compilation.
From the MSDN documentation, Windows Data Types:
DWORD
A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
A DWORD is simply not large enough to hold a 64-bit memory address (the other code you mention will similarly only work correctly in 32-bit).
Change Addr (and whatever code you are using to determine the value of Addr) to use DWORD_PTR instead:
DWORD_PTR
An unsigned long type for pointer precision. Use when casting a pointer to a long type to perform pointer arithmetic. (Also commonly used for general 32-bit parameters that have been extended to 64 bits in 64-bit Windows.)
Then Addr will be the correct byte size whether you compile for 32-bit or 64-bit.

Related

Type cast warning from DWORD to 64 bit pointer

An old 32 bit C++ application (MS Visual Studio) has code lines like this:
m_value = (PUCHAR)someDWORD;
Where PUCHAR is a pointer to an unsigned char.
Now I have changed to 64 bit and I get a (valid) warning about conversion from DWORD to the 64 bit pointer. My unqualified solution to this is to write like this:
m_value = (PUCHAR)(DWARD_PTR)someDWORD;
Is this the correct way to fix this warning (and potential runtime error)?
That savage cast to DWORD_PTR will only pad someDWORD with zeros, it won't bring back the upper half of the pointer value that was lost.
You need someDWORD to be a DWORD_PTR (or, in fact, a std::uintptr_t) from the beginning.

Converting from hex to LPCVOID cuts off half of the address

I'm trying to read (and eventually write) memory of another process. I have the address (found using cheat engine) and I want to read it's value from my program, but I don't get the expected value. The address is 274A88A1630, but when I convert it to LPCVOID (which is required by ReadProcessMemory) I only get A88A1630 (which doesn't point to the memory I want)
I've tried converting using (LPCVOID) and (void*), both give the same result
int val = 0;
ReadProcessMemory(handle, (void*)0x274A88A1630, &val, sizeof(val), 0);
cout << val <<endl;
That happens because you have compiled your app as an x86 (32 bit/Win32) binary. Pointers (void* in this case) are 32 bits on x86. Pointers on x64 (64 bit/Win64) are 64 bits. 0x274A88A1630 is a 64 bit value, so you will therefore not encounter this problem if you compile your app for x64 (64 bit/Win64).
TLDR; A pointer value over 32 bits gets truncated to 32 bits when compiled for x86.

Invalid Handle with NtWow64AllocateVirtualMemory64

Test code:
typedef NTSTATUS(NTAPI *ntalloc64t)(HANDLE, PULONG64, ULONG64, PULONG64, ULONG, ULONG);
#define NtCurrentProcess() ( (HANDLE)(PULONG64) -1 ) ;
int _tmain(int argc, _TCHAR* argv[])
{
ULONG64 dwSize = 0x1000;
ntalloc64t ntalloc64f = (ntalloc64t)(GetProcAddress(GetModuleHandleA("ntdll"), "NtWow64AllocateVirtualMemory64"));
PVOID pvBaseAddress;
pvBaseAddress = (PVOID)NULL;
long kk = ntalloc64f((HANDLE)GetCurrentProcess(), (PULONG64)&pvBaseAddress, 0, (PULONG64)&dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}
I am running under WOW64. This returns 0xc0000008 which means the handle is invalid. Also does not work when passing -1 as the handle, which should indicate to WinAPI to use the current process.
NtWow64AllocateVirtualMemory64 is undocumented but you can assume that its parameters are almost the same as NtAllocateVirtualMemory and MSDN says this about the base address parameter:
A pointer to a variable that will receive the base address of the allocated region of pages. If the initial value of this parameter is non-NULL, the region is allocated starting at the specified virtual address rounded down to the next host page size address boundary. If the initial value of this parameter is NULL, the operating system will determine where to allocate the region.
You are hiding a bug with your casts; (PULONG64)&pvBaseAddress points to 32 zero bits from pvBaseAddress = (PVOID)NULL and 32 undefined bits from somewhere on your stack and if these bits are not all zero then you are asking for a specific base address that is probably not available!
Remove as many casts as possible and it should start working:
typedef NTSTATUS(NTAPI *ntalloc64t)(HANDLE, PULONG64, ULONG64, PULONG64, ULONG, ULONG);
ntalloc64t ntalloc64f = (ntalloc64t) GetProcAddress(GetModuleHandleA("ntdll"), "NtWow64AllocateVirtualMemory64");
// TODO: if (!ntalloc64f) not wow64, handle error...
HANDLE hTargetProcess = OpenProcess(...);
ULONG64 base = 0, size = 0x1000;
long nts = ntalloc64f(hTargetProcess, &base, 0, &size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
printf("status=%d base=%I64x size=%I64x\n", nts, base, size);
when we call NtWow64AllocateVirtualMemory64 from 32-bit ntdll.dll (it exist only in wow64 ntdll.dll) the whNtWow64AllocateVirtualMemory64 (64bit function) called inside wow64.dll. my reconstruction from win10 assembler code:
struct Wow64AllocateVirtualMemory64_Stack {
ULONG ProcessHandle;// !!! unsigned !!
ULONG BaseAddress;
ULONG64 ZeroBits;
ULONG RegionSize;
ULONG AllocationType;
ULONG Protection;
};
NTSTATUS
NTAPI
whNtWow64AllocateVirtualMemory64(Wow64AllocateVirtualMemory64_Stack* p)
{
return NtAllocateVirtualMemory(
(HANDLE)(ULONG_PTR)p->ProcessHandle,
(void**)(ULONG_PTR)p->BaseAddress,
p->ZeroBits,
(PSIZE_T)(ULONG_PTR)p->RegionSize,
p->AllocationType,
p->Protection);
}
key point here that HANDLE is 32-bit size in 32 bit code and 64-bit size in 64-bit code. as result 32-bit handle value must be extended to 64-bit handle in 64bit code. but it can be zero or sign extended. of course when we extend positive 32bit value (real process handle) - no different, result will be the same. but when we extend negative value -1 - result of zero extend will be 0xFFFFFFFF (this is invalid handle). result of sign extend - will be 0xFFFFFFFFFFFFFFFF - correct pseudo handle to current process. windows 10 use zero extend handle:
as result we can not use -1 (GetCurrentProcess()) here
win8 use sign-extend handle:
however no any sense use this api for allocate memory in wow64 process. really - if we accept any memory base address, or < 4GB - we can use NtAllocateVirtualMemory or VirtualAlloc[Ex]. so this function only have sense use in case we want allocate memory at base address >= 4Gb. but this is impossible in wow64 process. - system reserve all memory space higher than >= 4G. typical memory map for wow64bit process (with /LARGEADDRESSAWARE option)
so visible only 64bit ntdll.dll here, and all other memory is reserved.
without /LARGEADDRESSAWARE option reserved range begin from 7FFF0000. also this reserved memory can not be released - on call NtFreeVirtualMemory (from 64bit process) i got STATUS_INVALID_PAGE_PROTECTION error.
so no sense use this api for allocate inside self (and any another wow64 process). only if we want allocate memory in 64bit process and not simply allocate, but at range higher than 4GB. i even dont know for which target this can be need - why <4GB memory base, which can be allocated with usual NtAllocateVirtualMemory or VirtualAlloc[Ex] not ok. and funny that no related NtWow64FreeVirtualMemory64 api - so impossible free alocated memory. of course possible write base-independed (and as result no import) 64bit code, embedded in 32bit process, call it via 64 call gate, this code can call functions from 64bit ntdll (and only from it) and return. this is possible, but already another story

getting an warning cast to pointer from integer of different size

I am currently working on porting from 32 bit to 64 bit. And I am getting a warning:
cast to pointer from integer of different size
In the following code
AcLogFileWrite(
(FILE *) pOut->pTrace->logFileHandle, /* warning in this line */
outRecord,
outRecordLen);
and prototype of AcLogFileWrite() is
int AcLogFileWrite(
FILE * handle,
char * data,
int bytes);
Here the parameter pOut->pTrace->logFileHandle is of type int.
How can I fix this warning?
You can’t store 64 bits in a 32-bit int. If logFileHandle has to store a pointer, the only integer types that can safely do that are uintptr_t or intptr_t from <stdint.h>. Or you can typedef void*. Since you’re porting to a new ABI anyway, binary compatibility is not an issue and this is a good time to fix it.
If you absolutely cannot change the definition of the type from int, then you must compile under the ILP64 model, in which int, long and void* are all 64 bits wide.
If logFileHandle in fact is a file descriptor getting initialised using e.g. open() you want to use fdopen on it and pass the function's result to AcLogFileWrite().

printing the address of a pointer variable

I have learnt that while printing the address of a variable in c, we use unsigned int (%u in format string).
Its range is from 0 to 65535. Now an integer takes 4 bytes of memory, which means a maximum of 16384 (65536/4) integers can be stored. What will happen if we try to declare an array int a[20000] and get addresses of each of its element?
#include<stdio.h>
int main(void)
{
int a[20000];
for(i=0; i<19999; i++])
printf("%u", &a[i]);
}
In early times of C, a pointer and a int where similar types, and you could safely cast from one to the other and back again. In that early time, pointers and int were both 16 bits long. It is still true on 32 bits systems where int is now 32 bits. But it is false on 64 bits systems, because pointers are 64 bits long and int only 32 bits.
So I do not know where and how you learnt that while printing the address of a variable in c , we use unsigned int(%u), but forget it ASAP because in the general case it is wrong. The only foolproof way to print an adress is %p because system automatically adapt the size to 16, 32 or 64 bits.
And no longer convert pointers to int or opposite side, because it is highly non portable. From that other post, A more portable way (on the C99 standard variant) is to #include <stdint.h> and then cast pointers to intptr_t (and back). This integer type is guaranteed to be the size of a pointer.
And, I almost forgot 65536 = 0x10000 = 216, and 232 = 4294967296. So you were not that far from reality, and it is true that in older 16 bits system you could not have int array[40000] because int were 16 bits long and the array would exhaust all the available memory.
But on 32 bits systems, you can address 4 Gb of memory, so int array[20000] is harmless.
A pointer is a memory address where you can find some data. We can find the size the of a pointer variable using sizeof( ) operator. So its size doesn't depend on what it points at .It however depends on many bytes a memory address takes up on your system, which is 4 for a 32 bit compilers and 8 for 64-bit compiler.
If we have declared a pointer, double j, type of j is double, i.e. “a pointer to double".
%p is the correct format specifier for printing a pointer. %p outputs addresses in the hexadecimal notation.
Sometimes people use %u and %x (unsigned int in hexadecimal form) specifiers to print a pointer variable. It is however an undefined behavior to pass a pointer for a %x or %u argument.
However it works with 32 bit compilers like code blocks .This is because the size of unsigned int and the pointer is same here. (Both 4 bytes)
(It is false to assume that int and pointers have the same width . For both GCC 64 bit and MSVC 64 bit running on x64, sizeof(void *) == 8, while sizeof(unsigned int) == 4. It just so happens that on some architectures pointers and ints are the same size, e.g. the PDP-11/34a, or most 32 bit architectures nowadays. But it is extremely unwise to ever write code that relies on it.
You can do add extra 2 lines as below and verify:
printf("size of unsigned int is %lu\n", sizeof(unsigned int));
printf("size of pointer is %lu\n", sizeof(int *));
On a 64-bit GCC machine with a 64-bit operating system, this should give you 4 and 8 respectively )
On a GCC 64-bit machine-%x casts your pointer to an unsigned integer (32-bit length). The size of pointer is of 8-byte (64 bit) length. Printing with %p prints the whole pointer, in its complete size – 64 bits. But when you are printing with %x, only the lower 32 bits are printed. Hence it is always safe to print a pointer with %p.
Actually unsigned integers are having range of 0 to 65536 on older 16bit compilers like turbo c. Now a days all systems are having 32 or 64 bit architecture on which unsigned integers range is 0 to 4G (giga). So this code should work fine in latest compilers like gcc (under linux) or visual studio (under windows). Try switching to these compilers. They are very good and are widely used now a days. 16bit compilers are obsolete. Avoid using such compilers. If you are using windows then code blocks or dev c++ are some good programming IDEs for learning c.
P.S. avoid using %u for printing addresses. Use %p instead.