I am currently writing a program that utilizes ReadProcessMemory to save a block of memory to a file as text. Within the program, the user will designate the base address and size of the memory block. Input validations are done before the call to ReadProcessMemory to ensure that there are no parameter-related errors. Memory bounds are also restricted to pre-allocated regions to ensure that the user does not attempt to read non-allocated memory. Due to the fact that all allocated memory is readable, I believe that there should not be a need to perform general post-call error checking for ReadProcessMemory. Am I correct in stating this? Assuming all parameters are valid, of course. I have used ReadProcessMemory many times before in the past with proper error checking and found it to be unnecessary because the function always succeeded.
Examples would also be appreciated.
Thanks in advance!
I assume that all parameters are valid:
The handle have PROCESS_VM_READ access to the process;
lpBaseAddress is valid;
nSize <= the size of lpBuffer.
...
I think the only factor that will cause ReadProcessMemory failure is Memory Protection attributes.
As #IInspectable: "memory is allocated doesn't imply that you can read it".
After testing, I found that the following situations will get failed.
PAGE_NOACCESS
https://learn.microsoft.com/en-us/windows/win32/memory/reserving-and-committing-memory
lpvBase = VirtualAlloc(
NULL, // System selects address
PAGELIMIT*dwPageSize, // Size of allocation
MEM_RESERVE, // Allocate reserved pages
PAGE_NOACCESS); // Protection = no access
PAGE_GUARD | ...
https://learn.microsoft.com/en-us/windows/win32/memory/creating-guard-pages
lpvAddr = VirtualAlloc(NULL, dwPageSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READONLY | PAGE_GUARD);
Or modified by VirtualProtectEx:
VirtualProtectEx(hProcess, lpAddress, dwSize, PAGE_NOACCESS, &old);
and
VirtualProtectEx(hProcess, lpAddress, dwSize, PAGE_GUARD | PAGE_READONLY, &old);
GetLastError will return 299(ERROR_PARTIAL_COPY)
Use VirtualQueryEx to retrieve the The access protection of the pages in the specified region
Related
How do i write to a physical drive in Windows 7?
I am trying to write to a physical disk (e.g. \\.\PhysicalDrive0) in Windows 7.
This question has been asked to death, but has never been answered. It is something that used to work in Windows XP, but Microsoft intentionally broke in Windows Vista. Microsoft provides hints about how to do it, but nobody has even been able to figure it out.
It used to work
In the olden days, writing to a physical disk was allowed (as long as you were an administrator). The method to do it was even documented in a Knowledge Base article:
INFO: Direct Drive Access Under Win32
To open a physical hard drive for direct disk access (raw I/O) in a Win32-based application, use a device name of the form
\\.\PhysicalDriveN
where N is 0, 1, 2, and so forth, representing each of the physical drives in the system.
You can open a physical or logical drive using the CreateFile() application programming interface (API) with these device names provided that you have the appropriate access rights to the drive (that is, you must be an administrator). You must use both the CreateFile() FILE_SHARE_READ and FILE_SHARE_WRITE flags to gain access to the drive.
All that changed in Windows Vista, when addition security restrictions were put in place.
How do you write to a physical disk?
Many people, and many answers, on many stackoverflow questions confuse:
writing to a physical disk (e.g. \\.\PhysicalDrive0), and
writing to a logical volume (e.g. \\.\C$)
Microsoft notes the restrictions placed on both kinds of operations:
Blocking Direct Write Operations to Volumes and Disks
Write operations on a DASD (Direct access storage device) volume handle will succeed if:
the file system is not mounted, or if
The sectors being written to are the boot sectors.
The sectors being written to reside outside file system space.
The file system has been locked implicitly by requesting exclusive write access.
The file system has been locked explicitly by sending down a lock/dismount request.
The write request has been flagged by a kernel-mode driver that indicates that this check should be bypassed. The flag is called SL_FORCE_DIRECT_WRITE and it is in the IrpSp->flags field. This flag is checked by both the file system and storage drivers.
In my case i am asking about writing to a Physical, not a Logical one. Microsoft notes the new set of restrictions on writing to a physical disk handle:
Write operations on a disk handle will succeed if:
The sectors being written to do not fall within a file system.
The sectors being written to fall within a mounted file system that is locked explicitly.
The sectors being written to fall within a file system that is not mounted or the volume has no file system.
My sectors being written do fall within a file system --> fail
My sectors being written do fall within mounted, unlocked, file system --> fail
My sectors being written do fall within a file system that is mounted, and in inside a logical volume that has a file system.
The hints on how to make it work revolve around:
unmounting a file system
locking a file system
But the question is how do you unmount a file system? How do you lock a file system?
What are you doing now?
I am able to read all physical sectors of a disk; that is no problem. The problem is when i want to write to a physical sector of the disk.
The current code i have is, in pseudo-code:
void ZeroSector(Int64 PhysicalSectorNumber)
{
String diskName := '\\.\PhysicalDrive0';
DWORD desiredAccess := GENERIC_READ or GENERIC_WRITE;
//INFO: Direct Drive Access Under Win32
//https://support.microsoft.com/en-us/kb/100027
//says you nedd both
DWORD shareMode := FILE_SHARE_READ or FILE_SHARE_WRITE;
//Open the physical disk
hDisk := CreateFile(diskName, desiredAccess, shareMode,
nil, OPEN_EXISTING, 0, 0);
if hDisk = INVALID_HANDLE_VALUE
RaiseLastWin32Error();
try
{
Int32 bytesPerPhysicalSector := 4096; //Determined elsewhere using IOCTL_STORAGE_QUERY_PROPERTY+STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
//Setup buffer for what we will be writing
Byte[] buffer = new Byte[bytesPerPhysicalSector];
//Calculate the byte offset of where the sector is
Int64 byteOffset = PhysicalSectorNumber * bytesPerPhysicalSector;
//Seek to that byte offset
SetFilePointer(hDisk, byteOffset.Lo, byteOffset.Hi, FILE_BEGIN);
//Write the buffer
DWORD numberOfBytesWritten;
if (!WriteFile(hDisk, buffer, bytesPerPhysicalSector, out numberOfBytesWritten, nil))
RaiseLastWin32Error();
}
finally
{
CloseHandle(hDisk);
}
}
Surprisingly:
i can open the physical disk for GENERIC_READ + GENERIC_WRITE access
it doesn't fail until the actual WriteFile, which fails with:
ERROR_ACCESS_DENIED
How to do what Microsoft says
Microsoft said that my write would fail, and they were right. They said that i need to explicitly lock the file system:
Write operations on a disk handle will succeed if:
The sectors being written to fall within a mounted file system that is locked explicitly.
Except i don't know how to do that.
I know i probably have to use DeviceIoControl and one of the IOCTLS to "lock" a volume. But that presents three challenges:
figuring out which volume(s) are on the physical disk selected
figuring out which IOCTL to use
figuring out how to unlock the locked volumes
Ignoring those problems, i blindly tried the LockFile API. Just before calling WriteFile:
//Try to lock the physical sector we will be writing
if (!LockFile(DiskHandle, byteOffset.Lo, byteOffset.Hi, bytesPerPhysicalSector, 0)
RaiseLastWin32Error();
That fails with:
ERROR_INVALID_FUNCTION (1)
Check out FSCTL_LOCK_VOLUME, FSCTL_DISMOUNT_VOLUME control codes. I believe you would have to enum all volumes you have on your disk, and then dismount-and-lock them. After lock succeeded, the disk is all yours.
You probably won't be able to do this on a system drive though. I'd also guess that there will be caveats with volumes that contain page files.
after I have done this, I can write to the corresponding \\.\PhysicalDrive3. I could not before:
HANDLE hVol = CreateFileA(
"\\\\.\\X:",
FILE_READ_DATA | FILE_WRITE_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD unused;
BOOL b = DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
if (!b) {
printf("%u", GetLastError());
abort();
}
...
HANDLE h = CreateFileA(
argv[1], // that's my \\physicaldrive3
FILE_READ_DATA | FILE_WRITE_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
...
bResult = WriteFile(h, buf, cb, &dwWritten, &foo);
if (!bResult) {
// used to fail without messing with vol handle
printf("Failed writing data. Error = %d.\n", GetLastError());
return 0;
}
Memory in the Linux kernel is usually unswappable (Do Kernel pages get swapped out?). However, sometimes it is useful to allow memory to be swapped out. Is it possible to explicitly allocate swappable memory inside the Linux kernel? One method I thought of was to create a user space process and use its memory. Is there anything better?
You can create a file in the internal shm shared memory filesystem.
const char *name = "example";
loff_t size = PAGE_SIZE;
unsigned long flags = 0;
struct file *filp = shmem_file_setup(name, size, flags);
/* assert(!IS_ERR(filp)); */
The file isn't actually linked, so the name isn't visible. The flags may include VM_NORESERVE to skip accounting up-front, instead accounting as pages are allocated. Now you have a shmem file. You can map a page like so:
struct address_space *mapping = filp->f_mapping;
pgoff_t index = 0;
struct page *p = shmem_read_mapping_page(mapping, index);
/* assert(!IS_ERR(filp)); */
void *data = page_to_virt(p);
memset(data, 0, PAGE_SIZE);
There is also shmem_read_mapping_page_gfp(..., gfp_t) to specify how the page is allocated. Don't forget to put the page back when you're done with it.
put_page(p);
Ditto with the file.
fput(filp);
Answer to your question is a simple No, or Yes with a complex modification to kernel source.
First, to enable swapping out, you have to ask yourself what is happening when kswapd is swapping out. Essentially it will walk through all the processes and make a decision whether its memory can be swapped out or not. And all these memory have the hardware mode of ring 3. So SMAP essentially forbid it from being read as data or executed as program in the kernel (ring 0):
https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention
And check your distros "CONFIG_X86_SMAP", for mine Ubuntu it is default to "y" which is the case for past few years.
But if you keep your memory as a kernel address (ring 0), then you may need to consider changing the kswapd operation to trigger swapout of kernel addresses. Whick kernel addresses to walk first? And what if the address is part of the kswapd's kernel operation? The complexities involved is huge.
And next is to consider the swap in operation: When the memory read is attempted and it's "not present" bit is enabled, then hardware exception will trigger linux kernel memory fault handler (which is __do_page_fault()).
And looking into __do_page_fault:
https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/fault.c#L1477
and there after how it handler the kernel addresses (do_kern_address_fault()):
https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/fault.c#L1174
which essentially is just reporting as error for possible scenario. If you want to enable kernel address pagefaulting, then this path has to be modified.
And note too that the SMAP check (inside smap_violation) is done in the user address pagefaulting (do_usr_addr_fault()).
Essentially I am looking for a function that could do for kernel mode what VirtualProtect does for user mode.
I am allocating memory using a logic exemplified by the following simplified code.
PMDL mdl = MmAllocatePagesForMdl
(
LowAddress,
HighAddress,
SkipAddress,
size
);
ULONG flags = NormalPagePriority | MdlMappingNoExecute | MdlMappingNoWrite;
PVOID ptr = MmGetSystemAddressForMdlSafe
(
mdl,
flags
);
The MdlMappingNoExecute and MdlMappingNoWrite flags will have effect only on Win8+.
Moreover, using only MmGetSystemAddressForMdlSafe I cannot assign for example NoAccess protection for the memory region.
Are there any additional or alternative API-s I could use so that I can modify the page protection of the allocated memory?
A hack would do too since currently this functionality would not be in use in production code.
C:\Windows\System32>dumpbin /exports ntdll.dll | find "Protect"
391 17E 0004C030 NtProtectVirtualMemory
1077 42C 000CE8F0 RtlProtectHeap
1638 65D 0004C030 ZwProtectVirtualMemory
I think you can call Zw functions from kernel mode, and the args are generally the same as for the corresponding Nt functions. And while ZwProtectVirtualMemory is undocumented, there is a documented ZwAllocateVirtualMemory that accepts protection flags.
Another approach might be to allocate and protect virtual memory in user-mode, pass the buffer down to your driver, then create the corresponding MDL there.
The code I currently ended up using is below.
All used APIs are official.
Here I create another mdl for subrange of the allocated memory and change protection of that subrange.
If You trip over memory protected with this method below then:
at IRQL < DISPATCH_LEVEL You will get PAGE_FAULT_IN_NONPAGED_AREA fault (Invalid system memory was referenced. This cannot be protected by try-except,
it must be protected by a Probe. Typically the address is just plain bad or it
is pointing at freed memory.)
at IRQL == DISPATCH_LEVEL You will get
DRIVER_IRQL_NOT_LESS_OR_EQUAL fault (An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is usually
caused by drivers using improper addresses.)
Note that changing the protection might fail if the subrange is part of large page allocation. Then the status will be likely STATUS_NOT_SUPPORTED.
Large page allocations can happen if the originally allocated memory region's size and alignment (which depends on SkipAddress variable in the question) are suitable and some additional preconditions are fulfilled with which I am not familiar with (perhaps starting from certain OS version).
PMDL guard_mdl = IoAllocateMdl
(
NULL,
PAGE_SIZE * guardPageCount,
FALSE,
FALSE,
NULL
);
if (guard_mdl)
{
IoBuildPartialMdl
(
mdl,
guard_mdl,
(PVOID)(0), // **offset** from the beginning of allocated memory ptr
PAGE_SIZE * guardPageCount
);
status = MmProtectMdlSystemAddress
(
guard_mdl,
PAGE_NOACCESS
);
}
I use Lazarus 1.2.2 and Freepascal 2.6.4.
I have a program called QuickHash that hashes files and, when I run it on Linux, it can be used to hash physcial disks too (/dev/sdXX). However, I'd like to add the ability to the Windows version.
I gather that to access physcial devices, like disks, one has to use CreateFile. Specifically, CreateFileW.
So, the user clicks a button which scans the computer for disks and lists them in a List Box. The one the user double clicks is then parsed (ListBox.GetSelectedText) for the string '\.\PhyscialDiskX' and that is assigned to a string variable,
strDiskID := getDiskID(Listbox.GetSelectedText);
That works fine.
I then try to create a handle to that disk :
hSelectedDisk := Windows.CreateFileW(PWideChar(strDiskID), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
Based on this article, specifcally "You must use both the CreateFile() FILE_SHARE_READ and FILE_SHARE_WRITE flags to gain access to the drive" I have also tried the other two combinations below :
hSelectedDisk := CreateFileW(PWideChar(strDiskID), GENERIC_READ, FILE_SHARE_READ AND FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
hSelectedDisk := CreateFileW(PWideChar(strDiskID), GENERIC_READ, FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
All three successfully assign a handle. But, the top syntax and the bottom syntax eventually generate an error (explained below). The middle option immediately returns the default initialisation hash for a zero byte file, i.e. DA39... for SHA1.
My problem is I am unable to pass that handle (which is an integer) to the SHA1File and MD5FILE functions of the Freepascal md5 and SHA1 units. They expect a filename, which has to be a string.
So, if I pass it the strDiskID ('\.\PhyscialDiskX') (which defeats the objects of assigning a handle at all) I do get disk activity and the program appears to be working.
strDiskHashValue := SHA1Print(SHA1File(strDiskID));
ShowMessage(strDiskHashValue);
CloseHandle(hSelectedDisk);
But even when run on really small disks like a 500Mb USB drive, it takes many minutes and eventually returns "Run Error 1117" which according to this means
"ERROR_IO_DEVICE
1117 (0x45D)
The request could not be performed because of an I/O device error."
However, I have tried it on several working disks and the error continues.
So, my question, ultimately, is how on earth do I pass that successfully assigned THandle to the hashing functions? (UI have also asked the question at the Lazarus forums but sometimes I get answers here from members who don't see the threads there)
You aren't going to be able to pass volume handles to functions that aren't expecting volume handles. These are very special handles with quite stringent requirements on their use. Not the least of which is that you must read blocks that are sector aligned, and whose sizes are multiples of the sector size.
So the solution is for you to take charge of reading the data. Read it into a buffer, and then pass that buffer to the hashing library. This means that you'll need a hashing library that can be called repeatedly to process new data. All comprehensive hashing libraries will offer such functionality.
The share mode flags are combined with bitwise or:
FILE_SHARE_READ or FILE_SHARE_WRITE
I would create the handle like this:
hSelectedDisk := CreateFileW(PWideChar(strDiskID), FILE_READ_DATA,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
First of all I would concentrate on reading the content of the volume. Once you can do that, hashing will be routine.
It seems from the comments that you are having some trouble writing the hashing code. First of all you need to allocate a buffer that is a multiple of the sector size:
var
Buffer: Pointer;
....
GetMem(Buffer, BufferSize);
Use IOCTL_DISK_GET_DRIVE_GEOMETRY to find out the sector size. And take note of this text from the documentation:
To read or write to the last few sectors of the volume, you must call DeviceIoControl and specify FSCTL_ALLOW_EXTENDED_DASD_IO. This signals the file system driver not to perform any I/O boundary checks on partition read or write calls. Instead, boundary checks are performed by the device driver.
Now that you have a buffer, you can read and hash the content.
var
ctx: TSHA1Context;
Digest: TSHA1Digest;
BytesRead: DWORD;
....
SHA1Init(ctx);
repeat
if not ReadFile(hSelectedDisk, Buffer^, BufferSize, BytesRead, nil) then
// handle error, raise exception
SHA1Update(ctx, Buffer^, BytesRead);
until BytesRead < BufferSize;
SHA1Final(ctx, Digest);
I've not attempted to compile or test this code. It's not meant to be complete or comprehensive. It is just intended to show you how to tackle the problem.
I am trying to learn how to access other process's memory. I'm using ReadProcessMemory but it fails, supposedly because i'm reading from an unaccessible memory. I think I should use VirtualQueryEx first to find out if the memory I'm trying to read is readable, but I don't know how to parse the results of VirtualQueryEx.
VirtualQueryEx returns a structure described here http://msdn.microsoft.com/en-us/library/windows/desktop/aa366775%28v=vs.85%29.aspx
Is there any good simple way to know whether the values in the MEMORY_BASIC_INFORMATION structure tell that the page is readable by ReadProcessMemory or not?
This structure has several parameters that, from what I understand, define whether or not memory is accessible:
DWORD AllocationProtect;
DWORD State;
DWORD Protect;
DWORD Type;
Its not even clear on what combination of parameters actually allows reading the memory (for example, should I read Protection flag from AllocationProtect or Protect, does State other than Commit mean that memory is unreadable, etc) and making a huge if (this is that and that is that or this is that....) doesn't seem like a clean solution.
There's got to be a better way, but I don't seem to be able to find it...
Actually the clue is in the documentation you linked:
AllocationProtect
The memory protection option when the region was initially allocated. This member can be one
of the memory protection constants or 0 if the caller does not have access.
VirtualProtect or VirtualProtectEx could have been used meanwhile to change the protection (per-page, while the initial protection was for the whole allocated range). So you'd have to check the Protect member of the structure against the Memory Protection Constants in the MSDN documentation that state that read is allowed. Checking for a single bit doesn't seem to be enough.