Understanding the "Why" of a file access code snippet - go

I have been trying to make sense of a code snippet for past some days. You can find the gist here
Overview
The code reads MFT of a Windows drive, creates a struct of maps of files in the MFT. Then it goes on reading the USN Journal to detect what has changed of those files.
Problem
There are some logical operations happening in the script. I can understand what the code part is doing but why is it doing so is what has been haunting me for past couple of days. I stumbled upon various Windows docs like this but even then, it did not make much sense to me.
For example -
switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
case O_RDONLY:
access = GENERIC_READ
case O_WRONLY:
access = GENERIC_WRITE
case O_RDWR:
access = GENERIC_READ | GENERIC_WRITE
}
if mode&O_CREAT != 0 {
access |= GENERIC_WRITE
}
if mode&O_APPEND != 0 {
access &^= GENERIC_WRITE
access |= FILE_APPEND_DATA
}
Why are we doing these logical operations? There are other instances of such parts in the code also. If anyone can point me to the direction or help me why these operations are done, it'd be really helpful. Thanks

It is a conversion from the Linux (POSIX) API open (man 2 open; http://man7.org/linux/man-pages/man2/open.2.html) to the Windows API CreateFile (https://learn.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilew).
For the original code, see src/syscall/syscall_windows.go (https://go.googlesource.com/go):
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
if len(path) == 0 {
return InvalidHandle, ERROR_FILE_NOT_FOUND
}
pathp, err := UTF16PtrFromString(path)
if err != nil {
return InvalidHandle, err
}
var access uint32
switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
case O_RDONLY:
access = GENERIC_READ
case O_WRONLY:
access = GENERIC_WRITE
case O_RDWR:
access = GENERIC_READ | GENERIC_WRITE
}
if mode&O_CREAT != 0 {
access |= GENERIC_WRITE
}
if mode&O_APPEND != 0 {
access &^= GENERIC_WRITE
access |= FILE_APPEND_DATA
}
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)
var sa *SecurityAttributes
if mode&O_CLOEXEC == 0 {
sa = makeInheritSa()
}
var createmode uint32
switch {
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
createmode = CREATE_NEW
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
createmode = CREATE_ALWAYS
case mode&O_CREAT == O_CREAT:
createmode = OPEN_ALWAYS
case mode&O_TRUNC == O_TRUNC:
createmode = TRUNCATE_EXISTING
default:
createmode = OPEN_EXISTING
}
h, e := CreateFile(pathp, access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
return h, e
}

Related

Windows NDIS FilterDriver object IO access for non-admin processes

I have Windows NDIS FilterDriver and it has name \\Device\\MyFilter.
User application performs some DeviceIoControl operation with the FilterDriver and calls following code to open the device handle:
LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
DWORD CreationDistribution = OPEN_EXISTING;
DWORD FlagsAndAttributes = FILE_FLAG_OVERLAPPED;
DWORD DesiredAccess = GENERIC_READ | GENERIC_WRITE;
DWORD ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD lastErr = 0;
m_hFilter = CreateFileA(MY_FILTER_NAME, /* "\\\\.\\\\MyFilter" */
DesiredAccess, ShareMode, lpSecurityAttributes, CreationDistribution, FlagsAndAttributes, NULL);
if (m_hFilter == INVALID_HANDLE_VALUE)
{
lastErr = GetLastError();
return false;
}
It works great if user application was run "As Administrator", otherwise (if run as regular User) CreateFileA returns INVALID_HANDLE_VALUE, and lastErr = 5 (Access Denied)
Reasons why it returns "Access Denied" are clear.
How to make user's application to open the Filter Driver object?
The idea of creating an interface with IoRegisterDeviceInterface() looks promising, but it requires the pointer to PDO, which I do not know where to obtain for NDIS Filter.
DefaultSDDLString field of the NDIS_DEVICE_OBJECT_ATTRIBUTES argument should be properly set for call of NdisRegisterDeviceEx()
//...
DeviceAttribute.DefaultSDDLString = &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX;
// Register the filter driver
Status = NdisRegisterDeviceEx(g_FHandle, &DeviceAttribute, &g_Object, &g_Handle);
Thanks, Scott_Noone_(OSR)!

How to get a serial number of a Windows disk?

I'm trying to get a serial number of a disk, using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
struct {
USHORT Reserved;
USHORT SerialNumberLength;
UCHAR SerialNumber[252];
} dsn;
DWORD nr;
memset(&dsn, '\0', sizeof dsn);
if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
printf("Serial number: %s\n", dsn.SerialNumber);
} else {
printf("No serial number, error %d.\n", (int)GetLastError());
}
}
However, GetLastError() returns ERROR_INVALID_FUNCTION.
The disk does exist, and it has a serial number, see this registry entry:
How can I retrieve the serial number from C code without using the registry?
we can use IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty (Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR)
and use SerialNumberOffset member of STORAGE_DEVICE_DESCRIPTOR
Specifies the byte offset from the beginning of the structure to a
NULL-terminated ASCII string that contains the device's serial number.
If the device has no serial number, this member is zero.
code can look like this:
ULONG GetSerial(HANDLE hFile)
{
static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
ULONG dwError;
do
{
dwError = ERROR_NO_SYSTEM_RESOURCES;
if (buf = LocalAlloc(0, size))
{
ULONG BytesReturned;
if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
{
if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
dwError = ERROR_MORE_DATA;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
dwError = NOERROR;
}
else
{
dwError = ERROR_NO_DATA;
}
}
}
else
{
dwError = ERROR_GEN_FAILURE;
}
}
else
{
dwError = GetLastError();
}
LocalFree(buf);
}
} while (dwError == ERROR_MORE_DATA);
return dwError;
}
also for open device we can use CreateFileW (L"\\\\.\\PhysicalDrive0", 0, 0, 0, OPEN_EXISTING, 0, 0); - in place dwDesiredAccess we can use 0 because IOCTL_STORAGE_QUERY_PROPERTY defined as
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - so FILE_ANY_ACCESS - accept any file access and FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING have sense only for file system devices (more general which use cache) - for disk devices - this is irrelevant
I have tried different approaches and figured out that sending IOCTL_STORAGE_QUERY_PROPERTY doesn't work as expected for different USB devices in both User and Kernel mode code. For some USB mass storages it doesn't return serial number. I'd assume that there are 2 correct ways to do that:
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER (a few AV products do that?)
create and send URB_CONTROL_DESCRIPTOR_REQUEST
Update 1.
I saw using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER in the one file system mini-filter driver that was used like the following:
FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,
...
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,....
I haven't tested this yet in KernelMode code, unfortunately, but trying to make it works in user mode code shows that this IOCTL mostly
is not supported by different devices, maybe this IOCTL is reserved for the future as a standard way to get the serial number and will be
required by USB standards later?
Also, "wmic diskdrive get name, serialnumber" returns in my case for USB Mass Storage incorrect serial number = "E" the same result as we would use IOCTL_STORAGE_QUERY_PROPERTY.
So, the correct way to get the serial number of USB mass storage is creating a USB request block in KernelMode code and using DeviceIoControl to the hub driver in the UserMode code.
USBVIEW (UserMode code) gets serial number by sending IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ioctl to the HUB driver which
returns USB_NODE_CONNECTION_INFORMATION_EX that contains USB_DEVICE_DESCRIPTOR. iSerialNumber member of USB_DEVICE_DESCRIPTOR is used later in the
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl request to the hub driver which finally gets serial number.
Another approach I see is maybe using some 3-rd party libraries like libusb to simplicate all these actions...
Update 2.
I took a look at USBSTOR disassembled code. USBSTOR_DeviceControl routine has the following code for the IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
So, it passes the IRP down the stack to the usbhub driver as was expected. So maybe this functionlaty is expected to be realized in
the usbhub driver sometime ? That would be great as for me...

How to enumerate the volumes on a phyiscal drive using Windows API?

I'm trying to create a list of partitions and their volumes of all the (fixed) disks in the system (Something like: PhyiscalDrive0, Partition 1, C:\; PhyiscalDrive0, Partition 2, D:\; ...) . I already got the list of installed disks via SetupApi and IOCTL_STORAGE_GET_DEVICE_NUMBER, as well as the number of partition.
The problem I'm having is how to find out the drive letters that are mounted to a partiticular partition (in meaning of partition 1 of Physical drive is C:\, for example)?
Thanks in before for any help,
Willi K.
The API you probably want to use is the Storage Management API. This provides a way to query just about anything you want. In particular, the drive letters are part of the MSFT_Volume class. The API isn't the friendliest for C++, which is why I'm not providing a sample query, but there's a sample here that shows how to write queries.
Once you have obtained the drives through the setup api you need to obtain information about partitions (through IOCTL_DISK_GET_DRIVE_LAYOUT_EX) and especially their starting offset and length.
Here is the code I use inside my library libwindevblk (libwindevblk):
BOOL FindVolume(int diskno, PSMI_DEVBLK_ENTRY pDevBlkEntry)
{
HANDLE vol;
BOOL success;
TCHAR szNextVolName[MAX_PATH+1];
TCHAR szNextVolNameNoBSlash[MAX_PATH+1];
vol = FindFirstVolume(szNextVolName, MAX_PATH);
success = (vol != INVALID_HANDLE_VALUE);
while (success)
{
//We are now enumerating volumes. In order for this function to work, we need to get partitions that compose this volume
HANDLE volH;
PVOLUME_DISK_EXTENTS vde;
DWORD bret;
//For this CreateFile, volume must be without trailing backslash
StringCchCopy(szNextVolNameNoBSlash, MAX_PATH, szNextVolName);
szNextVolNameNoBSlash[_tcslen(szNextVolNameNoBSlash) - 1] = _T('\0');
volH = CreateFile(szNextVolNameNoBSlash,
FILE_READ_ATTRIBUTES | SYNCHRONIZE | FILE_TRAVERSE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0);
if (volH != INVALID_HANDLE_VALUE)
{
bret = sizeof(VOLUME_DISK_EXTENTS) + 256 * sizeof(DISK_EXTENT);
vde = (PVOLUME_DISK_EXTENTS)malloc(bret);
if (DeviceIoControl(volH, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (void *)vde, bret, &bret, NULL))
{
for (unsigned i = 0; i < vde->NumberOfDiskExtents; i++)
{
if (vde->Extents[i].DiskNumber == diskno &&
vde->Extents[i].StartingOffset.QuadPart == pDevBlkEntry->StartingOffset.QuadPart &&
vde->Extents[i].ExtentLength.QuadPart == pDevBlkEntry->PartitionLength.QuadPart)
{
CHAR szVolumeName[MAX_PATH + 1] = { 0 };
CHAR szFileSystemName[MAX_PATH + 1] = { 0 };
DWORD dwSerialNumber = 0;
DWORD dwMaxFileNameLength = 0;
DWORD dwFileSystemFlags = 0;
if (GetVolumeInformation(szNextVolName,
szVolumeName, _countof(szVolumeName),
&dwSerialNumber,
&dwMaxFileNameLength,
&dwFileSystemFlags,
szFileSystemName,
_countof(szFileSystemName)))
{
_tcsncpy(pDevBlkEntry->szRootPathName, szNextVolName, MAX_PATH);
_tcsncpy(pDevBlkEntry->szVolumeName, szVolumeName, MAX_PATH);
_tcsncpy(pDevBlkEntry->szFileSystemName, szFileSystemName, MAX_PATH);
pDevBlkEntry->dwSerialNumber = dwSerialNumber;
pDevBlkEntry->dwFileSystemFlags = dwFileSystemFlags;
}
else
{
DWORD dwErr = GetLastError();
printf("%lu", dwErr);
}
DWORD length = 0;
TCHAR pathnames[MAX_PATH + 1] = { 0 };
if (GetVolumePathNamesForVolumeName(szNextVolName, (LPTSTR)pathnames, MAX_PATH, &length))
{
_tcsncpy(pDevBlkEntry->szVolumePathName, pathnames, MAX_PATH);
}
free(vde);
CloseHandle(volH);
FindVolumeClose(vol);
return TRUE;
}
}//for
}
free(vde);
CloseHandle(volH);
}
success = FindNextVolume(vol, szNextVolName, MAX_PATH) != 0;
}
FindVolumeClose(vol);
return FALSE;
}

Use CreateFile on locked file to get file handle

I deleted my other question. Thanks all for helping me realize how to post.
I have a file on my desktop named rawr.txt. The file is locked. I would like to open it for the sole reason of getting a filehandle to it. (i will search a list of enumerated filed handles to identify what process id's are locking this file).
This is my code:
Cu.import("resource://gre/modules/ctypes.jsm");
var lib_kernel32 = ctypes.open("kernel32.dll");
//var INVALID_HANDLE_VALUE = ctypes.voidptr_t(-1);
var GENERIC_READ = 0x80000000;
var GENERIC_WRITE = 0x40000000;
var OPEN_EXISTING = 3;
var FILE_ATTRIBUTE_NORMAL = 0x80;
var FILE_FLAG_OVERLAPPED = 0x40000000;
var OPEN_ALWAYS = 4;
var INVALID_HANDLE_VALUE = new ctypes.Int64(-1);
var FSCTL_SET_SPARSE = 0x900c4;
var FSCTL_SET_ZERO_DATA = 0x980c8;
var FILE_BEGIN = 0;
let CreateFile = lib_kernel32.declare(
"CreateFileW",
ctypes.winapi_abi,
ctypes.voidptr_t, // return type: handle to the file
ctypes.jschar.ptr, // in: lpFileName
ctypes.uint32_t, // in: dwDesiredAccess
ctypes.uint32_t, // in: dwShareMode
ctypes.voidptr_t, // in, optional: lpSecurityAttributes (note that
// we're cheating here by not declaring a
// SECURITY_ATTRIBUTES structure -- that's because
// we're going to pass in null anyway)
ctypes.uint32_t, // in: dwCreationDisposition
ctypes.uint32_t, // in: dwFlagsAndAttributes
ctypes.voidptr_t // in, optional: hTemplateFile
);
let CloseHandle = lib_kernel32.declare(
"CloseHandle",
ctypes.winapi_abi,
ctypes.int32_t, //bool // return type: 1 indicates success, 0 failure
ctypes.voidptr_t // in: hObject
);
var aFile = FileUtils.getFile('Desk', ['rawr.txt']);
let filePath = aFile.path;
let hFile = CreateFile(filePath, GENERIC_READ, 0, null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, null);
let hFileInt = ctypes.cast(hFile, ctypes.intptr_t);
if (ctypes.Int64.compare(hFileInt.value, INVALID_HANDLE_VALUE) == 0) {
throw new Error("CreateFile failed for " + filePath + ", error " +
ctypes.winLastError);
}
CloseHandle(hFile);
lib_kernel32.close();
Problem with this is I always get some exception on the throw new Error line. I get error 32 most commonly and sometimes 87 when experimenting with flags.
Thanks
Since all you want is a handle to the file, you shouldn't be using GENERIC_READ. That requires that the other process opened the file with FILE_SHARE_READ.
Also, you need to allow other processes to have the file open, by specifying FILE_SHARE_READ, FILE_SHARE_WRITE, and FILE_SHARE_DELETE:
CreateFile(filepath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
...)
... and after all this, you've got a handle to the file with no access, which is pretty much useless. (The handle you get has no relationship to the handles of other processes, so having a handle yourself does not help you search a list of existing handles in any way I can see.)
let hFile = CreateFile(filePath, GENERIC_READ, 0, ...)
Passing 0 for the dwShareMode argument is not going to get you anywhere. That asks for exclusive access to the file, you cannot get that because another process already obtained read or write access. Usually GENERIC_WRITE access in the case of a log file. You'll need FILE_SHARE_READ | FILE_SHARE_WRITE to ask humbly.
If that still doesn't work then the other process was adamant about you not messing with the file. It intentionally omitted FILE_SHARE_READ. Not very common for a text file, but done when the programmer deems it impossible to read the file correctly because he is constantly changing it. You cannot override that decision, other than by picking up the phone and calling the programmer.

C CreateFileMapping error 5 Access Denied ALWAYS

I would like to ask for help with WINAPI function CreateFileMapping (), which returns constantly NULL. After GetLastError() I get 5 - "ERROR_ACCESS_DENIED 5 (0x5) Access is denied". The file has been created after CreateFile with no problem, but following CreateFileMapping never has bee succesful.
int MapDestFile(LPCWSTR fPath)
{
hDestFile = CreateFile(
fPath,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hSourceFile == NULL)
{
printf("%d\n", GetLastError());
}
hDestMapFile = CreateFileMapping(
hDestFile,
NULL,
PAGE_READWRITE,
0,
10,
NULL
);
if (hDestMapFile == NULL)
{
// here always tell error number 5
printf("%d\n", GetLastError());
}
lpMapAddressDestFile = MapViewOfFile(
hDestMapFile,
FILE_MAP_WRITE,
0,
0,
0);
if (lpMapAddressDestFile == NULL)
{
printf("%d\n", GetLastError());
}
return 1;
}
I would appreciate any suggestions.
Thanks
You need to create the file with GENERIC_WRITE | GENERIC_READ to match PAGE_READWRITE.
That seems self-evident when you think about it. How can you have memory that you can read from backed by a file that you cannot read from? The documentation does call this out explicitly in any case:
PAGE_READWRITE
The file handle that the hFile parameter specifies must be created with the GENERIC_READ and GENERIC_WRITE access rights.
On top of that your error checking on the call to CreateFile is wrong. Take another look at the documentations. Error is indicated by a return value of INVALID_FILE_HANDLE.

Resources