Opening symbolic link in kernel Windows8.1+ - file

i'm trying to open symbolic link as a file using ZwOpenProcess in kernel space. Using code below all is ok on XP-7. But using Windows 8.1 ZwOpenProcess fails with different NTSTATUS codes, like 0xC0000001, 0xC000000D
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE FileHandle;
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
InitializeObjectAttributes ( &ObjectAttributes,
SymLinkOrDeviceName, // <--- \Device\CEDRIVER60
OBJ_KERNEL_HANDLE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
Status = ZwOpenFile ( &FileHandle,
FILE_READ_ACCESS,
&ObjectAttributes,
&IoStatus,
0,
FILE_NON_DIRECTORY_FILE );
if ( !NT_SUCCESS ( Status ) )
{
DbgPrint("TEST: ERROR %p ", Status); // <--- 0xC0000001, 0xC000000D
goto Exit;
}
I checked, using WinObj - symbolic link present in system, code works fine on XP-7.
I also trind to change Access to - FILE_ANY_ACCESS - the same result.

ZwOpenProcess
I suppose you meant ZwOpenFile.
\Device\CEDRIVER60
Did you really looked into the \Device directory? This directory usually contains device objects created by drivers. Symbolic links are usually placed into directories like \DosDevices. You should also include the OBJ_CASE_INSENSITIVE flag.
FILE_READ_ACCESS
This flag determines access right(s) required to successfully deliver an IOCTL message to a target device object. Specify a true file access right constant like FILE_READ_ATTRIBUTES, FILE_READ_DATA (its value is actually the same as FILE_READ_ACCESS) or GENERIC_READ. MSDN page on ZwOpenFile (and ZwCreateFile) is also worth of reading. You may need to include the SYNCHRONIZE flag too.
%p
NTSTATUS is defined as long (32-bit signed integer). %x is more appropriate.

Related

Mingw produces x86 program which only successfully runs as Administrator- x64 and VS(x86 and x64) versions fine

I was taking a look at this Github project: https://github.com/LloydLabs/delete-self-poc
This project uses the SetFileInformationByHandle API (https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle) in a somewhat creative way to allow the deletion from disk of a locked file. I am attempting to implement this as part of a larger program, however I have ran into an issue when compiling for x86. I use mingw-w64 on a debian machine to compile my program and when doing compatibility checks for x86, I found a very strange issue.
#include "main.h"
static
HANDLE
ds_open_handle(
PWCHAR pwPath
)
{
return CreateFileW(pwPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
static
BOOL
ds_rename_handle(
HANDLE hHandle
)
{
FILE_RENAME_INFO fRename;
RtlSecureZeroMemory(&fRename, sizeof(fRename));
// set our FileNameLength and FileName to DS_STREAM_RENAME
LPWSTR lpwStream = DS_STREAM_RENAME;
fRename.FileNameLength = sizeof(lpwStream);
RtlCopyMemory(fRename.FileName, lpwStream, sizeof(lpwStream));
return SetFileInformationByHandle(hHandle, FileRenameInfo, &fRename, sizeof(fRename) + sizeof(lpwStream));
}
static
BOOL
ds_deposite_handle(
HANDLE hHandle
)
{
// set FILE_DISPOSITION_INFO::DeleteFile to TRUE
FILE_DISPOSITION_INFO fDelete;
RtlSecureZeroMemory(&fDelete, sizeof(fDelete));
fDelete.DeleteFile = TRUE;
return SetFileInformationByHandle(hHandle, FileDispositionInfo, &fDelete, sizeof(fDelete));
}
int
main(
int argc,
char** argv
)
{
WCHAR wcPath[MAX_PATH + 1];
RtlSecureZeroMemory(wcPath, sizeof(wcPath));
// get the path to the current running process ctx
if (GetModuleFileNameW(NULL, wcPath, MAX_PATH) == 0)
{
DS_DEBUG_LOG(L"failed to get the current module handle");
return 0;
}
HANDLE hCurrent = ds_open_handle(wcPath);
if (hCurrent == INVALID_HANDLE_VALUE)
{
DS_DEBUG_LOG(L"failed to acquire handle to current running process");
return 0;
}
// rename the associated HANDLE's file name
DS_DEBUG_LOG(L"attempting to rename file name");
if (!ds_rename_handle(hCurrent))
{
DS_DEBUG_LOG(L"failed to rename to stream");
return 0;
}
DS_DEBUG_LOG(L"successfully renamed file primary :$DATA ADS to specified stream, closing initial handle");
CloseHandle(hCurrent);
// open another handle, trigger deletion on close
hCurrent = ds_open_handle(wcPath);
if (hCurrent == INVALID_HANDLE_VALUE)
{
DS_DEBUG_LOG(L"failed to reopen current module");
return 0;
}
if (!ds_deposite_handle(hCurrent))
{
DS_DEBUG_LOG(L"failed to set delete deposition");
return 0;
}
// trigger the deletion deposition on hCurrent
DS_DEBUG_LOG(L"closing handle to trigger deletion deposition");
CloseHandle(hCurrent);
// verify we've been deleted
if (PathFileExistsW(wcPath))
{
DS_DEBUG_LOG(L"failed to delete copy, file still exists");
return 0;
}
DS_DEBUG_LOG(L"successfully deleted self from disk");
return 1;
}
When compiling the base code found in the linked repository (and shown above) as x86, attempting to run the program fails at the SetFileInformationByHandle call in the ds_rename_handle function. Calling GetLastError() returns 123:
ERROR_INVALID_NAME
123 (0x7B)
The filename, directory name, or volume label syntax is incorrect.
The very bizarre part is that the program succeeds when ran from an Administrator prompt. Even stranger, compiling the same code for x64 works both in a normal and an Administrator prompt.
As a sanity check I copied the code verbatim over to VS2019 and compiled there, and the resulting x86 program was able to run without Administrator privileges.
The only changes to the source code made on the debian system were made in the header file:
#pragma once
#pragma comment(lib, "Shlwapi.lib")
#include <Windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <stdlib.h>
#define DS_STREAM_RENAME L":wtfbbq"
#define DS_DEBUG_LOG(msg) wprintf(L"[LOG] - %s\n", msg)
Where <Windows.h> was changed to <windows.h> and the DS_DEBUG_LOG line changed to %ls so that the entire log message would print.
The GCC command used to compile for x86 was:
i686-w64-mingw32-gcc main.c -o delete32.exe -s -DUNICODE -Os -lshlwapi
I have tried removing all switches and compiling and it still fails.
As a note, the shlwapi library is only required for the very last call in main(), PathFileExistsW. I have commented out that portion and removed shlwapi from imports and from the gcc command to no effect.
The x64 gcc command which succeeded was:
x86_64-w64-mingw32-gcc main.c -o delete32.exe -s -DUNICODE -Os -lshlwapi
In the issues tab of the github repo there are some mentions of errors in the code which I have looked at separately. However I would desperately like to know why mingw is causing an issue with the 32 bit version of this program. Unfortunately "just compile with VS" isn't an option, as I use a program to generate and compile the program that this bit of code will be part of on my linux machine.
Thanks for any insight.
RtlCopyMemory(fRename.FileName, lpwStream, sizeof(lpwStream)); writes 4 or 8 bytes into a 2 byte buffer! Who knows where the remaining bytes are going, the program behavior is probably undefined.
The concept of deleting a running exe like this might work but the code indicates that the author does not have a full understanding of Windows and C. You are better off rewriting it from scratch...
Edit:
After playing with your uploaded files I can with confidence say that it is the lack of a application manifest that causes it to fail when it is not elevated. Your vc file has a requestedExecutionLevel element which gives it Vista operating system context. If you remove the manifest resource in the vc exe it stops working. If you add a manifest to the mingw exe it starts working.
the ds_rename_handle function is wrong implemented.
the FILE_RENAME_INFO is variable size structure.
as result declaration
FILE_RENAME_INFO fRename;
almost always wrong (it will ok only if FileName containing 1 or 2 symbols)
really we need first calculate FileNameLength and then allocate PFILE_RENAME_INFO based on this
as example:
ULONG FileNameLength = (ULONG)wcslen(DS_STREAM_RENAME) * sizeof(WCHAR);
ULONG dwBufferSize = FIELD_OFFSET(FILE_RENAME_INFO, FileName) + FileNameLength;
PFILE_RENAME_INFORMATION fRename = (PFILE_RENAME_INFORMATION)alloca(dwBufferSize);
so complete code for ds_rename_handle can be next:
ULONG ds_rename_handle(HANDLE hHandle, PCWSTR DS_STREAM_RENAME)
{
ULONG FileNameLength = (ULONG)wcslen(DS_STREAM_RENAME) * sizeof(WCHAR);
ULONG dwBufferSize = FIELD_OFFSET(FILE_RENAME_INFO, FileName) + FileNameLength;
PFILE_RENAME_INFO fRename = (PFILE_RENAME_INFO)alloca(dwBufferSize);
fRename->ReplaceIfExists = TRUE;
fRename->RootDirectory = 0;
fRename->FileNameLength = FileNameLength;
memcpy(fRename->FileName, DS_STREAM_RENAME, FileNameLength);
return SetFileInformationByHandle(hHandle, FileRenameInfo,
fRename, dwBufferSize) ? NOERROR : GetLastError();
}
but documentation of FILE_RENAME_INFO is very bad. unclear - in what form - full pathname, file name or a relative pathname - must be FileName ?!
from my research - it must be full pathname only (not file name) or begin with a colon : ( The new name for the stream )
much more better use NtSetInformationFile with FileRenameInformation
compare description of FILE_RENAME_INFORMATION structure with FILE_RENAME_INFO !
here exist detailed description - in what form FileName it should be.
so i always use
NTSTATUS ds_rename_handle_nt(HANDLE hHandle, PCWSTR DS_STREAM_RENAME)
{
ULONG FileNameLength = (ULONG)wcslen(DS_STREAM_RENAME) * sizeof(WCHAR);
ULONG dwBufferSize = FIELD_OFFSET(FILE_RENAME_INFO, FileName) + FileNameLength;
PFILE_RENAME_INFORMATION fRename = (PFILE_RENAME_INFORMATION)alloca(dwBufferSize);
fRename->ReplaceIfExists = TRUE;
fRename->RootDirectory = 0;
fRename->FileNameLength = FileNameLength;
memcpy(fRename->FileName, DS_STREAM_RENAME, FileNameLength);
IO_STATUS_BLOCK iosb;
return NtSetInformationFile(
hHandle, &iosb, fRename, dwBufferSize, FileRenameInformation);
}

How do I get the disk drive serial number in filter driver?

I write a driver in windows, and I need disk drive serial number, for user mode I found this ansver.
My question is it possible to translate the above code to kernel mode, and how? Is WMI query available in filter driver? Sample code can greatly help.
EDIT:
I found here this code, but how I rewrite him for get serial number?
void GetSmbios()
{
NTSTATUS status;
GUID smbiosGUID = SMBIOS_DATA_GUID; // defined in wmiguid.h
PVOID wmiObject = NULL;
PWNODE_ALL_DATA dataBuffer;
ULONG bufferSize;
int TAG_SMBIOS = 'smbi';
//
// Get a WMI block handle to the SMBIOS_DATA_GUID
//
status = IoWMIOpenBlock((GUID *)&smbiosGUID, WMIGUID_QUERY,
&wmiObject);
if (!NT_SUCCESS(status))
{
return status;
}
//
// Determine how much space is required for the data
//
status = IoWMIQueryAllData(wmiObject, &bufferSize, NULL);
if (status != STATUS_BUFFER_TOO_SMALL)
{
ObDereferenceObject(wmiObject);
return status;
}
//
// Allocate the necessary storage. This space must come out of NP-pool
//
dataBuffer = ExAllocatePoolWithTag(
NonPagedPool,
bufferSize,
TAG_SMBIOS);
if (dataBuffer == NULL)
{
ObDereferenceObject(wmiObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
After allocating memory, I believe you need to call IoWMIQueryAllData() again, this time passing dataBuffer.
SMBIOS doesn't seem related to disk drives, so you'll want a different GUID to pass to IoWMIOpenBlock(). Perhaps this one ({BF253431-1E4D-4F57-00E7-64B2CACC801E}), since your user-mode example and others query Win32_PhysicalMedia to get SerialNumber.
However, this references a (presumably user-mode) DLL that is the provider for Win32_PhysicalMedia. So this may not be accessible in kernel-mode.
But it also gives a hint how to get the information from kernel-mode: IOCTLs. It mentions IOCTL_SMART_GET_VERSION, which should be just SMART_GET_VERSION, and here's an example:
(in user-mode, but you should be able to do similar from kernel-mode using ZwDeviceIoControlFile()). Note it follows up with another ioctl command, SMART_RCV_DRIVE_DATA, to get the serial number.
Another ioctl that sounds promising (and more general) is IOCTL_STORAGE_QUERY_PROPERTY, with the input STORAGE_PROPERTY_QUERY.PropertyId set to StorageDeviceProperty, so the output will be a STORAGE_DEVICE_DESCRIPTOR structure, which has field SerialNumberOffset:
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.
FILE_FS_VOLUME_INFORMATION contains field VolumeSerialNumber. This data structure might be retrieved with ZwQueryVolumeInformationFile(... FileFsVolumeInformation).
That requires a handle to the volume or a file/directory in the volume. If that's not feasible, but you have a DEVICE_OBJECT, you might try building your own IRP with IRP_MJ_QUERY_VOLUME_INFORMATION and sending it with IoCallDriver(), though I don't know if that's sanctioned -- the docs say such a "request is sent by the I/O Manager."

Using BASS_StreamCreateFile in WPF

BASS_StreamCreateFile(path,offset,length,BassFlags) always returns '0'. I am not understanding how to use this function. Need help on the usage of BassFlags.
PS : Using this with the help of WPF Sound Visualization Library.
Since 0 only informs you that there's an error, you should check what kind of error it is:
int BASS_ErrorGetCode();
This gives you the errorcode for the recent error.
Here's the list of possible error codes (= return values):
BASS_ERROR_INIT // BASS_Init has not been successfully called.
BASS_ERROR_NOTAVAIL // Only decoding channels (BASS_STREAM_DECODE) are allowed when using the "no sound" device. The BASS_STREAM_AUTOFREE // flag is also unavailable to decoding channels.
BASS_ERROR_ILLPARAM // The length must be specified when streaming from memory.
BASS_ERROR_FILEOPEN // The file could not be opened.
BASS_ERROR_FILEFORM // The file's format is not recognised/supported.
BASS_ERROR_CODEC // The file uses a codec that is not available/supported. This can apply to WAV and AIFF files, and also MP3 files when using the "MP3-free" BASS version.
BASS_ERROR_FORMAT // The sample format is not supported by the device/drivers. If the stream is more than stereo or the BASS_SAMPLE_FLOAT flag is used, it could be that they are not supported.
BASS_ERROR_SPEAKER // The specified SPEAKER flags are invalid. The device/drivers do not support them, they are attempting to assign a stereo stream to a mono speaker or 3D functionality is enabled.
BASS_ERROR_MEM // There is insufficient memory.
BASS_ERROR_NO3D // Could not initialize 3D support.
BASS_ERROR_UNKNOWN // Some other mystery problem!
(from bass.h)
Also make shure you have initialised BASS properly - BASS_Init() must get called before you create a stream:
BOOL BASS_Init(
int device, // The device to use... -1 = default device, 0 = no sound, 1 = first real output device
DWORD freq, // Output sample rate
DWORD flags, // A combination of flags
HWND win, // The application's main window... 0 = the current foreground window (use this for console applications)
GUID *clsid // Class identifier of the object to create, that will be used to initialize DirectSound... NULL = use default
);
Example:
int device = -1; // Default device
int freq = 44100; // Sample rate
BASS_Init(device, freq, 0, 0, NULL); // Init BASS

How to add ntdll.dll to project libraries with LoadLibrary() and GetProcAddress() functions?

I want to get the thread's start address with NtQueryInformationThread, but I need to add its library. How can I do that?
I prefer adding ntdll.lib (you can find it in Windows DDK/WDK) to a project. In that case you don't need GetProcAddress stuff.
I used NtQueryInformationThread without any need of loading ntdll (which in my opinion is loaded automatically). I had only to prepare a special header file with such content: http://pastebin.com/ieEqR0eL and include it in my project. After that I was able to do something like this:
NTSTATUS status;
THREAD_BASIC_INFORMATION basicInfo;
typedef NTSTATUS ( WINAPI *NQIT )( HANDLE, LONG, PVOID, ULONG, PULONG );
/* Open thread */
HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadId);
/* Get the address of NtQueryInformationThread function. */
NQIT NtQueryInformationThread = ( NQIT )GetProcAddress( GetModuleHandle(TEXT("ntdll.dll")), "NtQueryInformationThread" );
/* Get basic thread information */
status = NtQueryInformationThread(thread, 0, &basicInfo, sizeof(basicInfo), NULL);
CloseHandle(thread);
/* Get address of the Thread Environment Block, stack start address and last stack address */
tebAddress = (DWORD)basicInfo.TebBaseAddress;
DWORD pebAddress = *((DWORD*)(tebAddress+0x30));
/* For example to get stack base address */
stackBase = *((DWORD*)(tebAddress+4));
stackLimit = *((DWORD*)(tebAddress+8));

Monitor directory for new files only

I'd like to monitor a directory for new files from a C app. However, I'm not interested in modified files, only in new files. Currently I'm using readdir/stat for that purpose:
while ( (ent = readdir(dir)) != NULL ) {
strcpy(path, mon_dir);
strcat(path, "/");
strcat(path, ent->d_name);
if ( stat(path, &statbuf) == -1 ) {
printf( "Can't stat %s\n", ent->d_name );
continue;
}
if ( S_ISREG(statbuf.st_mode) ) {
if ( statbuf.st_mtime > *timestamp ) {
tcomp = localtime( &statbuf.st_mtime );
strftime( s_date, sizeof(s_date), "%Y%m%d %H:%M:%S", tcomp );
printf( "%s %s was added\n", s_date, ent->d_name );
*timestamp = statbuf.st_mtime;
}
}
}
Any idea how I can detect newly created files on Linux AND Solaris 10 without keeping a list of files?
Cheers,
Martin.
gamin provides an abstraction around system dependant file notification apis for many *nixes , and it's included in many linux distros by default.
For linux, you could use the linux specific inotify api.
Win32 has a similar API via FindFirstChangeNotification
The solution is to store the last access time in a global variable and pick the latest files with a filter to scandir():
int cmp_mtime( const struct dirent** lentry, const struct dirent** rentry ):
Stat (*lentry)->d_name (extended by path, but that's a detail only)
ltime = statbuf.st_mtime;
Stat (*rentry)->d_name (extended by path, but that's a detail only)
rtime = statbuf.st_mtime;
if ( ltime < rtime ) return -1;
else if ( ltime > rtime ) return 1;
return 0;
int selector( const struct dirent* entry ):
Stat entry->d_name (extended by path, but that's a detail only)
If not normal file then return 0
If stat.st_mtime > lastseen then return 1 else return 0
Main:
Init global time variable lastseen
scandir( directory, &entries, selector, cmp_mtime );
Process list of entries
lastseen := mtime of last entry in list
There is probably no better way with Solaris 10 outside interfacing with either the dtrace command or libdtrace (not recommended). On SunOS 5.11 based OSes (eg: OpenSolaris, Solaris 11 Express, ...), you can just use the File Event Notification framework.
On MacOS X there is a file monitoring API and the provided sample code shows how to find which files have changed.
You could use FAM - File Alteration Monitor for this.
one trick may be to set the archive bit for handled files.
Edith says:
if nothing else from the other answeres helpes, you may play with chmod() instead.
Was researching the same topic on Solaris and found the example of the watchdir app, to be used from within scripts, as in
watchdir /foo/bar
which will perform a blocking wait until something happens on the watched directory.
Link: https://solarisrants.wordpress.com/2013/07/24/solaris-file-event-notification/
I know you were asking for a solution from C but in fact Java (now) has a cross-platform class for this. You can read more about it here. Also see documentation for the WatchService class which is the central class of Java's file change notification ability.
Perhaps there's some documentation somewhere as to how Java accomplishes this on the various platforms ?
From the little I understand Java uses the OS's native file change notification API on Linux, Solaris and Windows and then in addition also implements a fallback method which is based on polling and which will work on any platform.

Resources