|
Nothing major however the process token handle is leaked after a successful call to OpenProcessToken() in the driver loader. Good job and thanks for the CodeProject example about reading/writing to raw disk sectors. Oh, one last thing, since you're enabling privileges the Win32 way but also using native APIs to load the driver, why not just enable the driver loading privilege with one line instead of many? You can use RtlAdjustPrivilege()
|
|
|
|
|
Hi, Nice aricle.
Is it possible to read multiple sectors in one call to DeviceIoControl
|
|
|
|
|
It is, I do it differently though. Try using the handle to the device, locking it and then SetFilePointerEx + WriteFile (to read and write any sector you want) - it is much easier.
Cheers!
|
|
|
|
|
How can I read and write row sectors in C#?
|
|
|
|
|
I have made a class for this. If anyone wants it, can email me at
[Edit: Email censored*]
* The proper way to share code is to either write an Article on CodeProject or publish it on GitHub, CodePlex et al.
modified 21-Jul-15 2:37am.
|
|
|
|
|
Hi:
Could you post the class code on CodeProject too.
Thanks
Lu
|
|
|
|
|
Hi, could you please share with your C# class ?
Thanks,
Eugene
|
|
|
|
|
Can be the driver included in the main program?
36. When you surround an army, leave an outlet free.
...
Do not press a desperate foe too hard.
SUN-TZU - Art of War
|
|
|
|
|
Anyone happen to get this working in windows 7 x64?
I have The driver compiled and it works for reads, but IoCallDriver with IRP_MJ_WRITE is finishing with C0000010 (STATUS_INVALID_DEVICE_REQUEST)
|
|
|
|
|
Hello
I am using your code, how had the hard disk and the actual numbers are different, I ask you a question, if I want to read and write F: how to write the contents of the disk!
Thank you
|
|
|
|
|
It is a great article and thank you very much.
I want to understand the issue from another angle. Direct access to raw disk is perhaps the most powerful thing an application and of course malware can do. Is there anyway we can stop/detect when an application access the raw disk - I am thinking a trojan malware. Like most people I am always the admin/king/owner of my computer so requiring local admin privilege to run the code is next to no protection at all.
Again thank you for helping us to understand the technology and risk.
|
|
|
|
|
I am looking to write a file filter for disk.sys that needs to attach to the creaton process before the operating system gains access to the disk/partition. However, I only need to read/write one-two bytes in the MBR nothing more and I am not very clear on the ASM/C required to do this.
Hopefully this example will clarify my intentions.
I need to layer the file filter for disk.sys so that when the Disk class driver creates the device objects representing the raw disks (any).
\Device\Harddisk0\DR(0) --> Represents Raw Harddisk 0
it looks in the raw disk MBR at track 0, side 0, sector 1, offset 0x01BC (optionally 0x01BC-0x01BD) and reads the value there and or writes a value to the same location.
dkg0414, your program got me in the right area for conducting this, but I only want to deal with those two bytes not all 512. Any thoughts you can provide would be helpful.
|
|
|
|
|
Hey Brian,
disk.sys is disk class, so filter would be a disk class filter driver. file filter are filters above the filesystems.
I wrote this thing for special cases in which there is some malware sitting and guarding some specific sectors/clusters on volume.
If you want to write to do some operations on MBR or Boot Sector, easy approach would be do a CreateFile on "\\.\\PhysicalDriveX" where X is the disk number (and not drive/partition number).
Then read (ReadFile) out the first sector (which will be MBR) in a buffer, modify the bytes which concern you and do a WriteFile on offset 0.
I am not sure whether this approach will work on vista and later 64bit versions (I think it should work, since you are only modifying MBR or boot sector of partition)
I hope it will help.
Regards
Deepak/dkg0414
|
|
|
|
|
|
1) Look at diskperf sample - See function DiskPerfRegisterDevice which is called from IRP_MN_START_DEVICE handler. This function sends an IOCTL IOCTL_STORAGE_GET_DEVICE_NUMBER down to get the device number (dev number and partition number).
It uses this number to construct the name as below. You can also do the same to construct the name.
RtlStringCbPrintfW(
deviceExtension->PhysicalDeviceNameBuffer,
sizeof(deviceExtension->PhysicalDeviceNameBuffer),
L"\\Device\\Harddisk%d\\Partition%d",
number.DeviceNumber, number.PartitionNumber)
2) It depends on disk.sys, what number it has to assign to storage devices.I think it gets incremented each time a disk is inserted. If same disk is inserted, removed and then inserted again, then it might assign a incremental value (though there are two disks). This is perfectly fine with disk.sys.
We can call it a bug in my driver. It should have mechanism to correctly map these things. I need to improve it further but just I am not getting time these days.
I hope above info helped.
|
|
|
|
|
1) Ok. That method makes sense. I think I overlooked that function because it delt with WMI registration primarily and was not needed for a simple disk filter. Leave it to me to overlook the obvious. So based off of this, ZwCreateFile or ZwOpenFile should have no issues reading as "\\Device\\HarddiskX" where X is the disk #.
Quote:
If you want to write to do some operations on MBR or Boot Sector, easy approach would be do a
CreateFile on "\\.\\PhysicalDriveX" where X is the disk number (and not drive/partition number).
I'm assuming that the '.' in your first createfile example is the indicator for default and thus would be synonmous with 'Device'. Is that correct?
2) Hmm... that is an interesting thought that it is continually incremented. I will have to experiment with that to see if it holds true. I understand about not having enough time to get to finish things. Thanks again.
Oh and this 'bug' if you want to call it that, is what causes the 1167 error that another user posted about last year. Ie, if you try to say go to 'HarddiskX' and not the 'DRX' value you get the 1167 because such a device by how you enumerated it does not exist, but the 'DRX' does. Just in case you get others asking about that error.
Thanks... now off to go see if it works! Fingers crossed
|
|
|
|
|
Ok, I've am doing something wrong but not sure what. I've written this quick and simple 'hello world' driver Driver Entry routine to demonstrate my dillema.
Using the methods you suggested previously, I define the first disk device, then try to open it with ZwOpenFile (I figured ZwCreateFile will make one if it doesn't exist, not something we want to have happen, so I only want to open an existing device.) This is where the driver fails... status from the ZwOpenFile operation is '-1073741788' and file_status->status is '-2141904312', so obviously the operation didn't work. But I don't understand why, and more importantly how to fix it! Likewise it never gets to the ZwReadFile operation so that I can at least in WinDbg look at the MBR in the array to ensure it is correctly read, specifically looking quickly at the '55 AA' at the end.
#include "ntddk.h"
#include "ntddk.h"
#include "ntdddisk.h"
#include "stdarg.h"
#include "stdio.h"
#include <ntddvol.h>
#include <mountdev.h>
#include "ntstrsafe.h"
typedef unsigned char BYTE, *PBYTE, *LPBYTE;
typedef int BOOL, *PBOOL, *LPBOOL;
typedef BYTE BOOLEAN, *PBOOLEAN;
typedef unsigned long DWORD, *PDWORD, *LPDWORD;
typedef unsigned long ULONG, *PULONG;
typedef unsigned short WORD, *PWORD, *LPWORD;
typedef char TCHAR;
typedef void *PVOID;
typedef unsigned short UINT2;
typedef unsigned int UINT4;
VOID OnUnload( IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("OnUnload called!\n");
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )
{
UNICODE_STRING PhysicalDeviceName;
WCHAR PhysicalDeviceNameBuffer[64];
ULONG DiskNumber;
HANDLE MBR;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
IO_STATUS_BLOCK file_status;
OBJECT_ATTRIBUTES obj_attrib;
DWORD disk_MBR[512] = {0x0};
DbgPrint("I loaded!\n");
theDriverObject->DriverUnload = OnUnload;
DiskNumber = 0;
MBR = NULL;
RtlStringCbPrintfW(PhysicalDeviceNameBuffer,
sizeof(PhysicalDeviceNameBuffer),
L"\\Device\\Harddisk%d", DiskNumber);
RtlInitUnicodeString(&PhysicalDeviceName,
&PhysicalDeviceNameBuffer[0]);
DbgPrint("Disk name %ws\n", PhysicalDeviceNameBuffer);
InitializeObjectAttributes(&obj_attrib,
&PhysicalDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenFile(&MBR,
GENERIC_READ | GENERIC_WRITE,
&obj_attrib,
&file_status,
0,
FILE_NON_DIRECTORY_FILE);
if (status != STATUS_SUCCESS){
DbgPrint("Failed to open the disk!\n");
DbgPrint("Disk Status = %x\n", file_status);
}
else {
DbgPrint("Successfully opened the disk.\n");
DbgPrint("Disk Handle = %x\n", MBR);
}
if (MBR != NULL){
status = ZwReadFile(MBR,
NULL,
NULL,
NULL,
&ioStatus,
disk_MBR,
512,
NULL,
NULL);
if (NT_SUCCESS(status)){
DbgPrint("Disk %d MBR read successfully.\n", DiskNumber);
}
else
{
DbgPrint("Disk %d MBR read failed!\n", DiskNumber);
}
}
return STATUS_SUCCESS;
}
Now I decided to see if it was just the "\\Device\Harddisk0" that had caused the failure, so I modified the driver to look at "\\Device\Harddisk0\DR0", and the device was successfully opened.
RtlStringCbPrintfW(PhysicalDeviceNameBuffer,
sizeof(PhysicalDeviceNameBuffer),
L"\\Device\\Harddisk%d\\DR0", DiskNumber);
However, now the ZwReadFile is failing with status '-1073741811', so even if you can open the disk in this manner, you still can't read it. Unless I'm still doing something wrong.
grrr.....
|
|
|
|
|
You are getting error STATUS_INVALID_PARAMETER.
You must be doing some thing wrong.
Is your driver filter in disk stack?
On which system are you trying all this?
Why didn't you use IoCallDriver mechanism.
|
|
|
|
|
I am pretty sure I'm doing something wrong with a parameter... lol. I am sure it has something to do with how I am trying to open it but I am running out of ideas.
>Is your driver filter in disk stack?
Not in this initial scenario. The code previously listed is the entire hello world driver I am experimenting in. Once it works correctly I will implement it in the infamous diskperf-based filter driver to be executed while in the disk stack.
>On which system are you trying all this?
XP SP3 Virtual Machine
>Why didn't you use IoCallDriver mechanism?
Because you had suggested to use this method instead. Besides to send an IRP down to the driver(s) below me (similarly to how you sent it in your sector.sys) would require me to have a pointer to the device I want to manipulate and as of right now, I only have a name.
Furthermore, it seems many say the zwcreatefile should work but I haven't found a working solution yet. http://www.osronline.com/showThread.cfm?link=23865[^] & http://www.pcreview.co.uk/forums/thread-535046.php[^]
Tried various names to identify the raw disk but soo far none have worked out:
L"\\DosDevices\\PhysicalDrive%d"
L"\\??\\PhysicalDrive%d"
L"\\\\.\\PhysicalDrive%d"
L"\\Device\\Harddisk%d"
Of course those name issues could really be a problem with the ZwCreateFile since I've been trying various versions of that as well.
status = ZwCreateFile(&MBR,
GENERIC_READ | GENERIC_WRITE,
&obj_attrib,
&file_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL,
0);
BTW,
How did you convert the -# to the text error code?
Brian
modified on Saturday, March 27, 2010 1:14 AM
|
|
|
|
|
Progress... but not entirely.
From the hello world driver I was able to read and write to a raw disk via:
DiskNumber = 0;
RtlStringCbPrintfW(PhysicalDeviceNameBuffer,
sizeof(PhysicalDeviceNameBuffer),
L"\\DosDevices\\PhysicalDrive%d", DiskNumber);
RtlInitUnicodeString(&PhysicalDeviceName, &PhysicalDeviceNameBuffer[0]);
InitializeObjectAttributes(&obj_attrib,
&PhysicalDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwCreateFile(&MBR,
FILE_READ_DATA | FILE_WRITE_DATA,
&obj_attrib,
&file_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL,
0);
if (status != STATUS_SUCCESS){
DbgPrint("Failed to open the disk!\n");
DbgPrint("Disk Status = %X\n", file_status);
DbgPrint("Operation Status = %u\n", status);
}
else {
DbgPrint("Successfully opened the disk.\n");
DbgPrint("Disk Handle = %x\n", MBR);
}
if (MBR != NULL){
offset.QuadPart = 0;
status = ZwReadFile(MBR,
NULL,
NULL,
NULL,
&ioStatus,
readBuffer,
sizeof(readBuffer),
&offset,
NULL);
After inserting the equivalent of this code (few variable name changes, etc) into the diskperf equivalent of DiskPerfIrpCompletion() so that only on IRP_MN_START_DEVICE does it try to read/write from the disk. However, with same device name as the above example when ZwCreateFile runs it fails with an error of: (-1073741810) or 3221225486 = "A device which does not exist was specified." This occurs for disk0 at boot time, and disk1 (a usb I added after fully in the OS). That doesn't make any sense to me. I could possibly see disk0 failing, but not disk1 when the OS is in the same state as when the hello world driver ran. Thoughts?
|
|
|
|
|
Yeah I had suspicion that in MN_START_DEVICE whether doing IO will be successful or not.
Try doing your operations in it's completion.
If that doesn't succeed, then you can do one thing:--
When a first IO (read/write) comes to the device do your own operation on the device object below (and not o n the your own filter device object). In this case then you should not use Zw calls but you should target your IRPs to lower device object using IoCallDriver. Zw call will send the IRP from top of the stack. And since your device is in the stack which is already holding the IOs will get another IO generated by you. And you will end up in a deadlock.
|
|
|
|
|
Unfortunately I think I am doing it in the completion routine of IRP_MN_START_DEVICE (run in DiskPerfIrpCompletion()). Ok, doing the operation in the first IO op comes sounds reasonable... it will obviously be taking operations at that time... lol. So lets see... an IO flag initialized to zero and then increment it so its only done on the first operation and not every operation... need to have a function take in IRP_MJ_READ & IRP_MJ_WRITE easy enough...
Now building the IRP with IoCallDriver should be pretty easy only catch I'm seeing on that is do I just send it to the next device below me as below or as in your driver you had a pointer to the specific raw disk to read.
myIRP_MJ_READ-WRITE_TRAP(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
MajorFunc = (IoCtl==IOCTL_SECTOR_READ) ? IRP_MJ_READ : IRP_MJ_WRITE;
lDiskOffset.QuadPart = (pDiskObj->ulSectorSize) * (pDiskLoc->ullSectorNum);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
pIrp = IoBuildSynchronousFsdRequest(MajorFunc, pDiskObj->pDiskDevObj, pBuff,
pDiskObj->ulSectorSize, &lDiskOffset,
&Event, &ioStatus);
IF it is the latter, I have proven I can get a working name for the raw disk (L"\\DosDevices\\PhysicalDrive%d"), but how do I convert that to something I can use as a target of my irp to gurantee I get the raw disk (sort of the reverse action of ObQueryNameString) to represent your pDiskDevObj? Can I use the deviceExtension->PhysicalDeviceObject which is set during Add_Device?
The next question that I see then is since I can't use the ZwRead/WriteFile operations because Zw calls will send the IRP from top of the stack. And since my device is in the stack which is already holding the IOs will get another IO generated by me and I will end up in a deadlock.... How does ZwSetSecurityObject get a Handle (an action that takes ZwCreateFile typically)? Or does that not matter now, since I've read/written to the raw disk already by this point and all other irps received are passed through me (other than this first one) or will this too end in deadlock? Is there a way to do ZwSetSecurityObject without causing deadlock prior to releasing the first trapped irp?
|
|
|
|
|
Hi. Thanks. It looks like you're doing everything right here, but oddly, I'm getting far different numbers than what this driver is returning. When I read sector 0, I get the main boot record which starts with 3 numbers and then the ASCII representation for NTFS. This software seems to be pulling data from a sector other than Logical Block Address 0.
|
|
|
|
|
Can you post your command which you are executing to get the data?
|
|
|
|
|
Hi Deepak,
Thanks. Certainly. I'm just calling ReadFile() from a Win32 usermode app on Windows Vista. The subroutine is below. Maybe I'm missing something, but I can't see what could possibly be different. When I read sector 0 using this subroutine, I get the MBR of my drive. When I use your driver to read raw disk sector 0, I get an entirely different set of numbers. But it must be reading a sector somewhere, because I can see the sector fixup in the data that your driver returns. Odd. I'm still working on the problem. There's always a reason.
DWORD ReadSector(LPSTR volume, ULONGLONG sector, unsigned char *q)
{
ULONGLONG ull, ull1;
unsigned long ul, ul1;
unsigned long bytesread = 0;
unsigned long sectorsize = 512;
char sstr[STRINGLENGTH], device[STRINGLENGTH];
HANDLE hDriver;
strcpy_s(device, volume);
_strupr_s(device);
hDriver = CreateFile(device,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
if(hDriver != INVALID_HANDLE_VALUE)
{
ull = sector * sectorsize;
ul = (unsigned long) ull;
ull >>= 32;
ul1 = (unsigned long) ull;
SetFilePointer(hDriver, ul, (PLONG) &ul1, FILE_BEGIN);
if(!ReadFile(hDriver, q, sectorsize, &bytesread, NULL))
{
sprintf_s(sstr, sizeof(sstr), "Error reading sector. Error = %d", GetLastError());
PleaseWait(sstr);
}
CloseHandle(hDriver);
}
else
{
sprintf_s(sstr, sizeof(sstr), "Error opening driver %s", volume);
PleaseWait(sstr);
}
return(bytesread);
}
|
|
|
|
|