DeviceIoControl returning ERROR_ACCESS_DENIED - c

I'm trying to interface with a driver for creating TUN interfaces (WinTun), but in order to send and receive data from them I need to register a ring buffer. The code I'm using looks something like this (I omitted the part where I create the device with SetupApi, as that seems to be working):
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#define REGISTER_RINGS_IOCTL CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
#define BUF_CAPACITY 0x20000
#define BUF_TRAILING 0x10000
typedef struct {
volatile ULONG Head;
volatile ULONG Tail;
volatile LONG Alertable;
UCHAR Data[BUF_CAPACITY + BUF_TRAILING];
} TUN_RING;
typedef struct {
ULONG Size;
UCHAR Data;
} TUN_PACKET;
typedef struct {
struct {
ULONG RingSize;
TUN_RING *Ring;
HANDLE TailMoved;
} Send, Receive;
} TUN_REGISTER_RINGS;
int main() {
HANDLE device = CreateFileW(
L"\\\\?\\ROOT#NET#0006#{cac88484-7515-4c03-82e6-71a87abac361}",
// ^^ This comes from the omitted code ^^
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if(device == INVALID_HANDLE_VALUE) {
printf("The device does not exist\n");
return -1;
}
TUN_RING Send, Receive = {0};
TUN_REGISTER_RINGS params = {
.Send.RingSize = sizeof(TUN_RING),
.Send.Ring = &Send,
.Send.TailMoved = CreateEventW(NULL, FALSE, FALSE, NULL),
.Receive.RingSize = sizeof(TUN_RING),
.Receive.Ring = &Receive,
.Receive.TailMoved = CreateEventW(NULL, FALSE, FALSE, NULL),
};
DWORD bytes;
BOOL ret = DeviceIoControl(
device,
REGISTER_RINGS_IOCTL,
&params,
sizeof(TUN_REGISTER_RINGS),
NULL,
0,
&bytes,
NULL
);
if(ret == 0) {
printf("Err: %d\n", GetLastError());
return -2;
}
return 0;
}
My problem is that this code fails at DeviceIoControl with error 5, which corresponds to ERROR_ACCESS_DENIED.
I have no idea why that is happening as the program is already running with admin privileges and the device handle has been opened with the recommended attributes (as you can see here). Sorry for the lack of extra information but I don't have much experience when it comes to windows drivers and don't know how to debug this any further.
I think the problem might have to do with this check in the source code of the driver, as it appears to be stopping before checking the input buffer (it should rerurn INVALID_PARAMETER when I put garbage in the input buffer, but that doesn't happen).
Again, sorry if I misunderstood something or missed something critical but I am learning all of this as I go, thanks in advance!

Found the solution. As #RbMm described, the code that creates the security descriptor only allows access to LocalSystem. That means that it is the only account allowed to talk to the driver.

Related

C Windows DLL Injection Notepad crashes

i have tryed a DLL Injection on the Programm Notepad.exe
But if i start my Injector, notepad Crashes.
Here is my Injector Code:
#include <windows.h>
#include <stdio.h>
char const Path[]="C:\\Users\\IEUser\\Desktop\\Mydll.dll";
int main(void) {
HANDLE hWnd, hProcess, AllocAdresse, hRemoteThread;
DWORD PID;
hWnd = FindWindow(0,"Untitled - Notepad");
GetWindowThreadProcessId((HWND)hWnd, &PID);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
AllocAdresse = VirtualAllocEx(hProcess, 0, sizeof(Path), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, (void*)AllocAdresse, (void*)Path, sizeof(Path), 0);
hRemoteThread=CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA"), AllocAdresse, 0, 0);
WaitForSingleObject(hRemoteThread, INFINITE);
VirtualFreeEx(hProcess, AllocAdresse, sizeof(Path), MEM_DECOMMIT);
CloseHandle(hProcess);
return 0;
}
And this is my Code for my DLL File:
#include <windows.h>
#include <stdio.h>
void InjNachricht() {
MessageBox(0, "It Works", "My DLL File", 0);
}
int WINAPI DllMain(HINSTANCE hInst,DWORD reason,LPVOID reserved) {
if(reason==DLL_PROCESS_ATTACH)
CreateThread(0, 0, (LPTHREAD_START_ROUTINE) InjNachricht, 0, 0, 0);
return 0;
}
I compile this Code in my Linux machine with MinGW:
(Injector) i686-w64-mingw32-gcc -o Injector.exe injector.c
(DLL-File) i686-w64-mingw32-gcc -o Mydll.dll mydll.c
I also written a Function for Setting Debug Privileges:
void SetDebugPrivilege() {
HANDLE hProcess=GetCurrentProcess(), hToken;
TOKEN_PRIVILEGES priv;
LUID luid;
OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
LookupPrivilegeValue(0, "seDebugPrivilege", &luid);
priv.PrivilegeCount = 1;
priv.Privileges[0].Luid = luid;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &priv, 0, 0, 0);
CloseHandle(hToken);
}
And if i Run my Program in my Virtual Machine:
Notepad.exe Crash
Why is notepad crashing?
If i inject my dll File with the Program it works:
enter image description here
And please do not come with me now "Then I use the program instead of writing a separate injector" !! That does not help me any further !!
Its been a while since I did these things, so I may be off, but:
You try to map LoadLibraryA method when you run remote thread. Most of modern apps use LoadLibraryW or just LoadLibrary that will use the default value based on compiler mode.
You use gcc, why not use Microsoft compiler? There might be mapping issues between the compilers, that prevent you from linking the gcc generated code with (possibly) vcc compiler....
Hop it helps
Firstly, you can use strlen/wcslen (first is for Ascii encoding, latter for Unicode encoding) instead for calculating the length of a buffer. It's more appropriate in my opinion.
Here's a properly working variant of DLL injection via remote threads which I've written as a demonstration example for you. It is a quick example so don't expect too much, extremely simplistic. You can improve it by using shell-code injection and then utilise a manual map loader or LdrLoadDll.
BOOLEAN InjectDll(
HANDLE ProcessHandle,
CHAR *DllPath
)
{
BOOLEAN BlStatus = FALSE;
HANDLE ThreadHandle = 0;
PVOID LoadLibraryAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
PVOID DllMemory = 0;
SIZE_T DllLength = strlen(DllPath);
if (!ProcessHandle ||
!DllPath ||
!LoadLibraryAddress)
{
return FALSE;
}
DllMemory = VirtualAllocEx(ProcessHandle,
NULL,
DllLength,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!DllMemory)
{
return FALSE;
}
BlStatus = WriteProcessMemory(ProcessHandle,
DllMemory,
DllPath,
DllLength,
NULL);
if (!BlStatus)
{
goto cleanup;
}
ThreadHandle = CreateRemoteThread(ProcessHandle,
NULL,
0,
(LPTHREAD_START_ROUTINE)LoadLibraryAddress,
DllMemory,
0,
0);
cleanup:
if (!ThreadHandle)
{
if (DllMemory)
{
VirtualFree(DllMemory,
NULL,
MEM_RELEASE);
}
BlStatus = FALSE;
}
else
{
BlStatus = TRUE;
}
return BlStatus;
}
On that note, you may be interested in NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory, RtlCreateUserThread/NtCreateThreadEx and NtAdjustPrivilegesToken. As for CreateRemoteThread, it won't work with processes on other user accounts, whereas RtlCreateUserThread/NtCreateThreadEx both will (as long as you have debugging rights - SeDebugPrivilege).
As a last pointer, make sure you compile with /MT so the run-time is statically linked (especially for the DLL you're injecting). If my example code does not help you, and you still cannot fix the issue, try using a debugger to diagnose the issue. You should already have tried doing this, debuggers are there for a reason!

IIO device buffer always null

I am using an IMU sensor called LSM6DSL with the iio drivers. They work fine if I display the raw values with the command:
cat /sys/bus/iio/devices/iio:device0/in_accel_x_raw
Then I decided to use the libiio so I can read all these values from a C program :
struct iio_context *context = iio_create_local_context();
struct iio_device *device = iio_context_get_device(context, 1);
struct iio_channel *chan = iio_device_get_channel(device, 0);
iio_channel_enable(chan);
if (iio_channel_is_scan_element(chan) == true)
printf("OK\n");
struct iio_channel *chan2 = iio_device_get_channel(device, 1);
iio_channel_enable(chan2);
struct iio_buffer *buff = iio_device_create_buffer(device, 1, true);
if (buff == NULL)
{
printf("Error: %s\n", strerror(errno));
return (1);
}
And this is the result :
OK
Error: Device or resource busy
Am I missing something? Let me know if you need more informations.
I guess I found the answer, and I didn't pay attention to the effects of the ncurses library (sorry for not mentioning that I was using it).
I moved these functions before the initialization of ncurses and now the buffer is created successful.

Creating a file and writing something into it does not work how I wanted it

I write the following code:
#include <windows.h>
#include "stdbool.h"
#include <winuser.h>
#include <WinDef.h>
#include <Winerror.h>
#include <stdio.h>
#include <Strsafe.h>
#define MAX_SIZE 100
void DisplayError(LPTSTR lpszFunction)
// Routine Description:
// Retrieve and output the system error message for the last-error code
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL );
lpDisplayBuf =
(LPVOID)LocalAlloc( LMEM_ZEROINIT,
( lstrlen((LPCTSTR)lpMsgBuf)
+ lstrlen((LPCTSTR)lpszFunction)
+ 40) // account for format string
* sizeof(TCHAR) );
if (FAILED( StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error code %d as follows:\n%s"),
lpszFunction,
dw,
lpMsgBuf)))
{
printf("FATAL ERROR: Unable to output error code.\n");
}
printf(TEXT("ERROR: %s\n"), (LPCTSTR)lpDisplayBuf);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
int main(){
//parameters of CreateFile()
HANDLE hFile;
LPCTSTR lpFileName;
DWORD dwDesiredAccess;
DWORD dwShareMode;
LPSECURITY_ATTRIBUTES lpSecurityAttributes;
DWORD dwCreationDisposition;
DWORD dwFlagsAndAttributes;
HANDLE hTemplateFile;
//parameters of WriteFile()
DWORD nNumberOfBytesToWrite;
DWORD numberOfBytesWritten;
LPOVERLAPPED lpOverlapped;
char DataBuffer[MAX_SIZE];
//others
BOOL bErrorFlag;
//initialize args of CreateFile()
lpFileName = "C:\\file.txt";
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
dwShareMode = 0;
lpSecurityAttributes = NULL;
dwCreationDisposition = CREATE_NEW;
dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
hTemplateFile = NULL;
//initialize args of WriteFile()
strcpy(DataBuffer, "This is the test file");
nNumberOfBytesToWrite = (DWORD)strlen(DataBuffer);
numberOfBytesWritten = 0;
lpOverlapped = NULL;
hFile = CreateFile(lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
if (hFile == INVALID_HANDLE_VALUE)
{
DisplayError(TEXT("CreateFile"));
printf(TEXT("Terminal failure: Unable to open file \"%s\" for write.\n"), lpFileName);
return;
}
printf(TEXT("Writing %d bytes to %s.\n"), nNumberOfBytesToWrite, lpFileName);
bErrorFlag = WriteFile(hFile, DataBuffer, nNumberOfBytesToWrite,
&numberOfBytesWritten, lpOverlapped);
if (FALSE == bErrorFlag)
{
DisplayError(TEXT("WriteFile"));
printf("Terminal failure: Unable to write to file.\n");
}
else
{
if (numberOfBytesWritten != nNumberOfBytesToWrite)
{
// This is an error because a synchronous write that results in
// success (WriteFile returns TRUE) should write all data as
// requested. This would not necessarily be the case for
// asynchronous writes.
printf("Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
printf(TEXT("Wrote %d bytes to %s successfully.\n"), numberOfBytesWritten, lpFileName);
}
}
CloseHandle(hFile);
}
So, as you can see. A program that should create a file named file.txt to the desktop and write a little text into it.
I use Microsoft Visual C++ Express, it compiles without errors..but when i let run it by clicking the green play-button, then I see not such a file created on my desktop.
By searching my possible faults, I have also read on https://msdn.microsoft.com/en-us/library/windows/desktop/bb540534%28v=vs.85%29.aspx that they use nearly the same code. Except that I do not include the displaying error parts.
So, my question: What could be the reason why it does not work?
Do I (the program) need some extra permissions to do that?
For example, I wrote the same in Ubuntu with open() & write() except that I use "/tmp/file.txt" as destionation directory. And it works without additional permissions.
best regards,
The code is using the wrong path to your Desktop directory.
For Windows 7 (and most versions of windows) the path would be:
be sure to use two backslashs at each directory level
C:\Users\yourUsername\Desktop
This is the wrong way to write the filename
"C:\Desktop\file.txt";
you need to escape the '\', the \f is actually a escape sequence
"C:\\Desktop\\file.txt";
and also, the file will not be created on your desktop apparently, try this instead
"C:\\file.txt";
and check your C: drive to see the file.

asynchronous serial port communication in windows in c

I am getting an error when I try to run a c file which does some basic writes to a serial port. I am trying to run it asynchronously because the writes sometimes take a long time to transfer. My original version had it running synchronously with WriteFile() commands which worked fine. I am new to using OVERLAPPED and would appreciate and input concerning it.
The error I am getting is:
Debug Assertion Failed!
<path to dbgheap.c>
Line: 1317
Expression: _CrtIsValidHeapPointer(pUserData)
when the second write function is called.
In main:
{
//initialized port (with overlapped), DBC, and timeouts
result = write_port(outPortHandle, 128);
result = write_port(outPortHandle, 131);
}
static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
//write completed. check for errors? if so throw an exception maybe?
printf("write completed--and made it to callback function\n");
}
int write_port(HANDLE hComm,BYTE* lpBuf) {
OVERLAPPED osWrite = {0};
// Create this write operation's OVERLAPPED structure's hEvent.
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
// error creating overlapped event handle
return 0;
// Issue write.
if (!WriteFileEx(hComm, &lpBuf, 1, &osWrite, &write_compl )) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
printf("last error: %ld",GetLastError());
return 0; //failed, return false;
}
else {
// Write is pending.
WaitForSingleObjectEx(osWrite.hEvent, 50, TRUE); //50 ms timeout
return -1; //pending
}
}
else {
return 1; //finished
}
}
That was not the full code, sorry. I was using an array of BYTEs as well, not constants. But system("pause")'s were causing my debug assertion failed errors, and after carefully looking through my code, when the WriteFileEx() was successful, it was never setting an alert/timeout on the event in the overlapped structure, so the callback function would never get called. I fixed these problems though.
I just need help with the handling/accessing a single BYTE in a structure which is allocated when a ReadFileEx() function is called (for storing the BYTE that is read so it can be handled). I need to know how to access that BYTE storage using an offset and make the overlapped structure null. Would making the overlapped structure null be as simple as setting the handle in it to INVALID_HANDLE_VALUE?
I think you have a couple of issues:
You are passing an integer as a pointer (your compiler should warn against this or preferably refuse to compile the code):
result = write_port(outPortHandle, 128);
Compare this to the definition of write_port:
int write_port(HANDLE hComm,BYTE* lpBuf) {
The above statements doesn't match. Later on you then pass a pointer to the lpBuf pointer to the WriteFileEx function by taking the address of the BYTE* -> "&lpBuf". This will not result in what you think it will do.
Even if you fix this, you will still have potential lifetime issues whenever the write is successfully queued but won't complete within the 50 ms timeout.
When using overlapped I/O, you need to make sure that the read/write buffer and the overlapped structure remain valid until the I/O is completed, cancelled or the associated device is closed. In your code above you use a pointer to an OVERLAPPED struct that lives on the stack in your call to WriteFileEx. If WriteFileEx does not complete within 50 ms, the pending I/O will have a reference to a non-existing OVERLAPPED struct and you will (hopefully) have an access violation (or worse, silently corrupted stack data somewhere in your app).
The canonical way of handling these lifetime issues (if performance is not a big issue), is to use a custom struct that includes an OVERLAPPED struct and some storage for the data to be read/written. Allocate the struct when posting the write and deallocate the struct from the I/O completion routine. Pass the address of the included OVERLAPPED struct to WriteFileEx, and use e.g. offsetof to get the address to the custom struct from the OVERLAPPED address in the completion routine.
Also note that WriteFileEx does not actually use the hEvent member, IIRC.
EDIT: Added code sample, please note:
I haven't actually tried to compile the code, there might be typos or other problems with the code.
It's not the most efficient way of sending data (allocating/deallocating a memory block for each byte that is sent). It should be easy to improve, though.
#include <stddef.h>
#include <assert.h>
#include <windows.h>
// ...
typedef struct _MYOVERLAPPED
{
OVERLAPPED ol;
BYTE buffer;
} MYOVERLAPPED, *LPMYOVERLAPPED;
// ...
static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
if (NULL == lpOverlapped)
{
assert(!"Should never happen");
return;
}
LPBYTE pOlAsBytes = (LPBYTE)lpOverlapped;
LPBYTE pMyOlAsBytes = pOlAsBytes - offsetof(MYOVERLAPPED, ol);
LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)pOlAsBytes;
if ((ERROR_SUCCESS == dwErrorCode) &&
(sizeof(BYTE) == dwNumberOfBytesTransfered))
{
printf("written %uc\n", pMyOl->buffer);
}
else
{
// handle error
}
free(pMyOl);
}
int write_port(HANDLE hComm, BYTE byte) {
LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)malloc(sizeof(MYOVERLAPPED));
ZeroMemory(pMyOl, sizeof(MYOVERLAPPED));
pMyOl->buffer = byte;
// Issue write.
if (!WriteFileEx(hComm, &pMyOl->buffer, sizeof(BYTE), pMyOl, &write_compl )) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
free(pMyOl);
printf("last error: %ld",GetLastError());
return 0; //failed, return false;
}
else {
return -1; //pending
}
}
else {
free(pMyOl);
return 1; //finished
}
}
result = write_port(outPortHandle, 128);
result = write_port(outPortHandle, 131);
The lpBuf argument have to be pointers to buffers, not constants.
e.g.
char buffer;
buffer = 128;
result = write_port(outPortHandle, &buffer);
buffer = 131;
result = write_port(outPortHandle, &buffer);
What you really want to do is also pass a buffer length.
e.g.
char buffer[] = { 128, 131 };
result = write_port(outPortHandle, &buffer, sizeof(buffer));
int write_port(HANDLE hComm,BYTE* lpBuf, size_t length) {
...
// Issue write.
if (!WriteFileEx(hComm, &lpBuf, length, &osWrite, &write_compl )) {
...

How to get name associated with open HANDLE

What's the easiest way to get the filename associated with an open HANDLE in Win32?
I tried the code posted by Mehrdad here. It works, but with limitations:
It should not be used for network shares because the MountPointManager may hang for a very long time.
It uses undocumented API (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) I don't like that very much
It does not support USB devices that create virtual COM ports (I need that in my project)
I also studied other approaches like GetFileInformationByHandleEx() and GetFinalPathNameByHandle(), but these are useless as they return only Path + Filename but without drive. Additionally GetFinalPathNameByHandle() also has the hanging bug.
The GetMappedFileName() approach in the MSDN (posted by Max here) is also very limited:
It works only with real files
The file size must not be zero bytes
Directories, Network and COM ports are not supported
The code is clumsy
So I wrote my own code. I tested it on Win XP and on Win 7, 8, and 10. It works perfectly.
NOTE: You do NOT need any additional LIB file to compile this code!
CPP FILE:
t_NtQueryObject NtQueryObject()
{
static t_NtQueryObject f_NtQueryObject = NULL;
if (!f_NtQueryObject)
{
HMODULE h_NtDll = GetModuleHandle(L"Ntdll.dll"); // Ntdll is loaded into EVERY process!
f_NtQueryObject = (t_NtQueryObject)GetProcAddress(h_NtDll, "NtQueryObject");
}
return f_NtQueryObject;
}
// returns
// "\Device\HarddiskVolume3" (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp" (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat" (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" (DVD drive)
// "\Device\Serial1" (real COM port)
// "\Device\USBSER000" (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini" (network drive share, Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" (network drive share, Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd" (internet socket)
// "\Device\Console000F" (unique name for any Console handle)
// "\Device\NamedPipe\Pipename" (named pipe)
// "\BaseNamedObjects\Objectname" (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt" (HKEY_CLASSES_ROOT\.txt)
DWORD GetNtPathFromHandle(HANDLE h_File, CString* ps_NTPath)
{
if (h_File == 0 || h_File == INVALID_HANDLE_VALUE)
return ERROR_INVALID_HANDLE;
// NtQueryObject() returns STATUS_INVALID_HANDLE for Console handles
if (IsConsoleHandle(h_File))
{
ps_NTPath->Format(L"\\Device\\Console%04X", (DWORD)(DWORD_PTR)h_File);
return 0;
}
BYTE u8_Buffer[2000];
DWORD u32_ReqLength = 0;
UNICODE_STRING* pk_Info = &((OBJECT_NAME_INFORMATION*)u8_Buffer)->Name;
pk_Info->Buffer = 0;
pk_Info->Length = 0;
// IMPORTANT: The return value from NtQueryObject is bullshit! (driver bug?)
// - The function may return STATUS_NOT_SUPPORTED although it has successfully written to the buffer.
// - The function returns STATUS_SUCCESS although h_File == 0xFFFFFFFF
NtQueryObject()(h_File, ObjectNameInformation, u8_Buffer, sizeof(u8_Buffer), &u32_ReqLength);
// On error pk_Info->Buffer is NULL
if (!pk_Info->Buffer || !pk_Info->Length)
return ERROR_FILE_NOT_FOUND;
pk_Info->Buffer[pk_Info->Length /2] = 0; // Length in Bytes!
*ps_NTPath = pk_Info->Buffer;
return 0;
}
// converts
// "\Device\HarddiskVolume3" -> "E:"
// "\Device\HarddiskVolume3\Temp" -> "E:\Temp"
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" -> "E:\Temp\transparent.jpeg"
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" -> "I:\foto.jpg"
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" -> "P:\Data\Passwords.txt"
// "\Device\Floppy0\Autoexec.bat" -> "A:\Autoexec.bat"
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" -> "H:\VIDEO_TS\VTS_01_0.VOB"
// "\Device\Serial1" -> "COM1"
// "\Device\USBSER000" -> "COM4"
// "\Device\Mup\ComputerName\C$\Boot.ini" -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" -> "\\ComputerName\Shares\Dance.m3u"
// returns an error for any other device type
DWORD GetDosPathFromNtPath(const WCHAR* u16_NTPath, CString* ps_DosPath)
{
DWORD u32_Error;
if (wcsnicmp(u16_NTPath, L"\\Device\\Serial", 14) == 0 || // e.g. "Serial1"
wcsnicmp(u16_NTPath, L"\\Device\\UsbSer", 14) == 0) // e.g. "USBSER000"
{
HKEY h_Key;
if (u32_Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\DeviceMap\\SerialComm", 0, KEY_QUERY_VALUE, &h_Key))
return u32_Error;
WCHAR u16_ComPort[50];
DWORD u32_Type;
DWORD u32_Size = sizeof(u16_ComPort);
if (u32_Error = RegQueryValueEx(h_Key, u16_NTPath, 0, &u32_Type, (BYTE*)u16_ComPort, &u32_Size))
{
RegCloseKey(h_Key);
return ERROR_UNKNOWN_PORT;
}
*ps_DosPath = u16_ComPort;
RegCloseKey(h_Key);
return 0;
}
if (wcsnicmp(u16_NTPath, L"\\Device\\LanmanRedirector\\", 25) == 0) // Win XP
{
*ps_DosPath = L"\\\\";
*ps_DosPath += (u16_NTPath + 25);
return 0;
}
if (wcsnicmp(u16_NTPath, L"\\Device\\Mup\\", 12) == 0) // Win 7
{
*ps_DosPath = L"\\\\";
*ps_DosPath += (u16_NTPath + 12);
return 0;
}
WCHAR u16_Drives[300];
if (!GetLogicalDriveStrings(300, u16_Drives))
return GetLastError();
WCHAR* u16_Drv = u16_Drives;
while (u16_Drv[0])
{
WCHAR* u16_Next = u16_Drv +wcslen(u16_Drv) +1;
u16_Drv[2] = 0; // the backslash is not allowed for QueryDosDevice()
WCHAR u16_NtVolume[1000];
u16_NtVolume[0] = 0;
// may return multiple strings!
// returns very weird strings for network shares
if (!QueryDosDevice(u16_Drv, u16_NtVolume, sizeof(u16_NtVolume) /2))
return GetLastError();
int s32_Len = (int)wcslen(u16_NtVolume);
if (s32_Len > 0 && wcsnicmp(u16_NTPath, u16_NtVolume, s32_Len) == 0)
{
*ps_DosPath = u16_Drv;
*ps_DosPath += (u16_NTPath + s32_Len);
return 0;
}
u16_Drv = u16_Next;
}
return ERROR_BAD_PATHNAME;
}
HEADER FILE:
#pragma warning(disable: 4996) // wcsnicmp deprecated
#include <winternl.h>
// This makro assures that INVALID_HANDLE_VALUE (0xFFFFFFFF) returns FALSE
#define IsConsoleHandle(h) (((((ULONG_PTR)h) & 0x10000003) == 0x3) ? TRUE : FALSE)
enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
};
struct OBJECT_NAME_INFORMATION
{
UNICODE_STRING Name; // defined in winternl.h
WCHAR NameBuffer;
};
typedef NTSTATUS (NTAPI* t_NtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength);
There is a correct (although undocumented) way to do this on Windows XP which also works with directories -- the same method GetFinalPathNameByHandle uses on Windows Vista and later.
Here are the eneded declarations. Some of these are already in WInternl.h and MountMgr.h but I just put them here anyway:
#include "stdafx.h"
#include <Windows.h>
#include <assert.h>
enum OBJECT_INFORMATION_CLASS { ObjectNameInformation = 1 };
enum FILE_INFORMATION_CLASS { FileNameInformation = 9 };
struct FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; };
struct IO_STATUS_BLOCK { PVOID Dummy; ULONG_PTR Information; };
struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; };
struct MOUNTMGR_TARGET_NAME { USHORT DeviceNameLength; WCHAR DeviceName[1]; };
struct MOUNTMGR_VOLUME_PATHS { ULONG MultiSzLength; WCHAR MultiSz[1]; };
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryObject(IN HANDLE Handle OPTIONAL,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation OPTIONAL, IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL);
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryInformationFile(IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation,
IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass);
#define MOUNTMGRCONTROLTYPE ((ULONG) 'm')
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH \
CTL_CODE(MOUNTMGRCONTROLTYPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
union ANY_BUFFER {
MOUNTMGR_TARGET_NAME TargetName;
MOUNTMGR_VOLUME_PATHS TargetPaths;
FILE_NAME_INFORMATION NameInfo;
UNICODE_STRING UnicodeString;
WCHAR Buffer[USHRT_MAX];
};
Here's the core function:
LPWSTR GetFilePath(HANDLE hFile)
{
static ANY_BUFFER nameFull, nameRel, nameMnt;
ULONG returnedLength; IO_STATUS_BLOCK iosb; NTSTATUS status;
status = NtQueryObject(hFile, ObjectNameInformation,
nameFull.Buffer, sizeof(nameFull.Buffer), &returnedLength);
assert(status == 0);
status = NtQueryInformationFile(hFile, &iosb, nameRel.Buffer,
sizeof(nameRel.Buffer), FileNameInformation);
assert(status == 0);
//I'm not sure how this works with network paths...
assert(nameFull.UnicodeString.Length >= nameRel.NameInfo.FileNameLength);
nameMnt.TargetName.DeviceNameLength = (USHORT)(
nameFull.UnicodeString.Length - nameRel.NameInfo.FileNameLength);
wcsncpy(nameMnt.TargetName.DeviceName, nameFull.UnicodeString.Buffer,
nameMnt.TargetName.DeviceNameLength / sizeof(WCHAR));
HANDLE hMountPointMgr = CreateFile(_T("\\\\.\\MountPointManager"),
0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, NULL);
__try
{
DWORD bytesReturned;
BOOL success = DeviceIoControl(hMountPointMgr,
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &nameMnt,
sizeof(nameMnt), &nameMnt, sizeof(nameMnt),
&bytesReturned, NULL);
assert(success && nameMnt.TargetPaths.MultiSzLength > 0);
wcsncat(nameMnt.TargetPaths.MultiSz, nameRel.NameInfo.FileName,
nameRel.NameInfo.FileNameLength / sizeof(WCHAR));
return nameMnt.TargetPaths.MultiSz;
}
__finally { CloseHandle(hMountPointMgr); }
}
and here's an example usage:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFile(_T("\\\\.\\C:\\Windows\\Notepad.exe"),
0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
assert(hFile != NULL && hFile != INVALID_HANDLE_VALUE);
__try
{
wprintf(L"%s\n", GetFilePath(hFile));
// Prints:
// C:\Windows\notepad.exe
}
__finally { CloseHandle(hFile); }
return 0;
}
edit Thanks for the comments about this being Vista or Server 2008 only. I missed that in the page. Guess I should have read the whole article ;)
It looks like you can use GetFileInformationByHandleEx() to get this information.
You'll likely want to do something like:
GetFileInformationByHandleEx( fileHandle, FILE_NAME_INFO, lpFileInformation, sizeof(FILE_NAME_INFO));
Double check the MSDN page to make sure I haven't misled you too badly :)
Cheers,
Taylor
FWIW, here's the same solution from the MSDN article suggested by Prakash in Python using the wonderful ctypes:
from ctypes import *
# get handle to c:\boot.ini to test
handle = windll.kernel32.CreateFileA("c:\\boot.ini", 0x80000000, 3, 0, 3, 0x80, 0)
hfilemap = windll.kernel32.CreateFileMappingA(handle, 0, 2, 0, 1, 0)
pmem = windll.kernel32.MapViewOfFile(hfilemap, 4, 0, 0, 1)
name = create_string_buffer(1024)
windll.psapi.GetMappedFileNameA(windll.kernel32.GetCurrentProcess(), pmem, name, 1024)
print "The name for the handle 0x%08x is %s" % (handle, name.value)
# convert device name to drive letter
buf = create_string_buffer(512)
size = windll.kernel32.GetLogicalDriveStringsA(511, buf)
names = buf.raw[0:size-1].split("\0")
for drive in names:
windll.kernel32.QueryDosDeviceA(drive[0:2], buf, 512)
if name.value.startswith(buf.value):
print "%s%s" % (drive[0:2], name.value[len(buf.value):])
break
For Windows Vista and later I prefer to use
GetFinalPathNameByHandle()
char buf[MAX_PATH];
GetFinalPathNameByHandleA(fileHandle, buf, sizeof(buf), VOLUME_NAME_DOS)
For Windows XP I prefer the solution by Mehrdad.
So I load GetFinalPathNameByHandle() dynamically via GetProcAddress() and if this fails (because it's Windows XP) I go for Mehrdad's solution with NtQueryObject()
If you need to do this on Win32 pre-Vista or Server 2008, look at the GetMappedFileName(...) function, which is one of the best kept secrets in Win32. WIth a little C/C++-fu, you can memory map a small portion of the file in question, and then pass that handle to this function.
Also, on Win32, you cannot really delete a file that is open (the open/unlink issue mentioned on another answer) - you can mark it for deletion on close, but it will still hang around until its last open handle is closed. Dunno if mapping (via mmap(...)) the file in this case would help, because it has to point back to a physical file...
-=- James.
On unixes there is no real way of reliably doing this. In unix with the traditional unix filesystem, you can open a file and then unlink it (remove its entry from the directory) and use it, at which point the name isn't stored anywhere. In addition, because a file may have multiple hardlinks into the filesystem, each of the names are equivalent, so once you've got just the open handle it wouldn't be clear which filename you should map back towards.
So, you may be able to do this on Win32 using the other answers, but should you ever need to port the application to a unix enviornment, you'll be out of luck. My advice to you is to refactor your program, if possible, so that you don't need the OS to be able to maintain an open resource to filename connection.

Resources