A forum for reverse engineering, OS internals and malware analysis 

 #31394  by Li Yong
 Sun Apr 01, 2018 1:53 pm
I have this following code, but i'm with trouble of AV in NtOpenFile() (of HandleFile(POBJECT_ATTRIBUTES poa) ) returning 0xC0000005 STATUS_ACCESS_VIOLATION.

Follows: DbgPrint + complete code (tested on Win 7 x86 inside a VirtualBox, listing folders/files of Windows Media Player (off) ).

PS: usermode application sends path name to driver.

How solve?

Image
Code: Select all
#include <ntifs.h>
#include <WinDef.h>

#define ALLOCSIZE PAGE_SIZE

//#define _REAL_DELETE_

#ifdef _REAL_DELETE_
#define USE_DELETE_ON_CLOSE FILE_DELETE_ON_CLOSE
#define FILE_ACCESS FILE_GENERIC_READ|DELETE
#else
#define USE_DELETE_ON_CLOSE FILE_DIRECTORY_FILE
#define FILE_ACCESS FILE_GENERIC_READ
#endif

#define echo(x) x 
#define label(x) echo(x)__LINE__ 
#define RTL_CONSTANT_STRINGW(s) { sizeof( s ) - sizeof( (s)[0] ), sizeof( s ),(PWSTR)(s) } 
#define STATIC_UNICODE_STRING(name, str) static const WCHAR label(__)[] = L##str; static const UNICODE_STRING name = RTL_CONSTANT_STRINGW(label(__))
#define STATIC_OBJECT_ATTRIBUTES(oa, name) STATIC_UNICODE_STRING(label(m), name); static OBJECT_ATTRIBUTES oa = { sizeof oa, 0, (PUNICODE_STRING)&label(m), OBJ_CASE_INSENSITIVE }

NTKERNELAPI NTSTATUS ZwQueryDirectoryFile(IN HANDLE FileHandle, 
										  IN HANDLE Event OPTIONAL, 
										  IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, 
										  IN PVOID ApcContext OPTIONAL, 
										  OUT PIO_STATUS_BLOCK IoStatusBlock, 
										  OUT PVOID FileInformation, 
										  IN ULONG Length, 
										  IN FILE_INFORMATION_CLASS FileInformationClass, 
										  IN BOOLEAN ReturnSingleEntry, 
										  IN PUNICODE_STRING FileName  OPTIONAL, 
										  IN BOOLEAN RestartScan);

NTSYSCALLAPI NTSTATUS NTAPI NtClose(HANDLE);

TSTATUS HandleFile(POBJECT_ATTRIBUTES poa, UNICODE_STRING us)
{
	IO_STATUS_BLOCK iosb;
	HANDLE hFile;
	NTSTATUS status;

	DbgPrint("FileName....: %wZ \n", &us);

	if (0 <= (status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
	{
		NtClose(hFile);
	}

	DbgPrint("NtOpenFile(): 0x%X \n", status);

	return status;
}

void ntTraverse(POBJECT_ATTRIBUTES poa, ULONG FileAttributes, int nLevel, PSTR prefix)
{

	if (IoGetRemainingStackSize() < PAGE_SIZE)
	{
		DbgPrint("no stack!\n");
		return;
	}

	if (!nLevel)
	{
		DbgPrint("!nLevel\n");
		return;
	}

	NTSTATUS status;
	IO_STATUS_BLOCK iosb;
	UNICODE_STRING ObjectName;
	OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };

	DbgPrint("DIRECTORY...: %s[<%wZ>]\n", prefix, poa->ObjectName);

#ifdef _REAL_DELETE_
	if (FileAttributes & FILE_ATTRIBUTE_READONLY)
	{
		if (0 <= ZwOpenFile(&oa.RootDirectory, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT))
		{
			FILE_BASIC_INFORMATION fbi = { 0 };
			fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;

			ZwSetInformationFile(oa.RootDirectory, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
			NtClose(oa.RootDirectory);
		}
	}
#endif

	if (0 <= (status = ZwOpenFile(&oa.RootDirectory, FILE_ACCESS, poa, &iosb, FILE_SHARE_VALID_FLAGS,
		FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT | USE_DELETE_ON_CLOSE)))
	{
		if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			PVOID buffer = ExAllocatePoolWithTag(PagedPool, ALLOCSIZE, 'tset');

			if (buffer)
			{
				union Data {
					PVOID pv;
					PBYTE pb;
					PFILE_DIRECTORY_INFORMATION DirInfo;
				};

				union Data data;

				while (0 <= (status = ZwQueryDirectoryFile(oa.RootDirectory, NULL, NULL, NULL, &iosb,
					data.pv = buffer, ALLOCSIZE, FileDirectoryInformation, 0, NULL, FALSE)))
				{

					ULONG NextEntryOffset = 0;

					do
					{
						data.pb += NextEntryOffset;

						ObjectName.Buffer = data.DirInfo->FileName;

						switch (ObjectName.Length = (USHORT)data.DirInfo->FileNameLength)
						{
						case 2 * sizeof(WCHAR) :
							if (ObjectName.Buffer[1] != '.') break;
						case sizeof(WCHAR) :
							if (ObjectName.Buffer[0] == '.') continue;
						}

						ObjectName.MaximumLength = ObjectName.Length;

#ifndef _REAL_DELETE_
						if (data.DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
#endif
						{
							ntTraverse(&oa, data.DirInfo->FileAttributes, nLevel - 1, prefix - 1);
						}
#ifndef _REAL_DELETE_
						else
#endif
						{
							//DbgPrint("%s%8I64u <%wZ>\n", prefix, data.DirInfo->EndOfFile.QuadPart, &ObjectName);
							  HandleFile(&oa, ObjectName);
						}

					} while (NextEntryOffset = data.DirInfo->NextEntryOffset);

					if (ALLOCSIZE - iosb.Information > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName[256]))
					{
						break;
					}
				}

				ExFreePoolWithTag(buffer, 'tset');

				if (status == STATUS_NO_MORE_FILES)
				{
					status = STATUS_SUCCESS;
				}
			}
		}

		NtClose(oa.RootDirectory);
	}

	if (0 > status)
	{
		DbgPrint("---- %x %wZ\n", status, poa->ObjectName);
	}
}

void ntTraverse_()
{
	char prefix[MAXUCHAR + 1];
	memset(prefix, '\t', MAXUCHAR);
	prefix[MAXUCHAR] = 0;

	STATIC_OBJECT_ATTRIBUTES(oa, "\\??\\C:\\Program Files\\Windows Media Player");
	ntTraverse(&oa, FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY, MAXUCHAR, prefix + MAXUCHAR);
}
 #31397  by Li Yong
 Mon Apr 02, 2018 1:30 am
Here was my last attempt building complete path to file, but still comes STATUS_ACCESS_VIOLATION.
Code: Select all
UNICODE_STRING MyResultString;
OBJECT_ATTRIBUTES myoa;

...

#ifndef _REAL_DELETE_
else
#endif
{
//DbgPrint("%s%8I64u <%wZ>\n", prefix, data.DirInfo->EndOfFile.QuadPart, &ObjectName);

MyResultString.MaximumLength = poa->ObjectName->Length + ObjectName.Length + 100;
MyResultString.Length = poa->ObjectName->Length + ObjectName.Length + 100;
MyResultString.Buffer = ExAllocatePoolWithTag(NonPagedPool, poa->ObjectName->Length + ObjectName.Length + 100, 'TAG');

if (MyResultString.Buffer != NULL) 
{

	RtlCopyUnicodeString(&MyResultString, poa->ObjectName); // DIRECTORY

	status = RtlAppendUnicodeToString(&MyResultString, L"\\"); // DIRECTORY + "\"

	if (!NT_SUCCESS(status)) 
	{
		DbgPrint("RtlAppendUnicodeToString() STATUS...: 0x%X", status);
		ExFreePoolWithTag(MyResultString.Buffer, 'TAG');
		return;
	}
	
	status = RtlUnicodeStringCat(&MyResultString, &ObjectName); // DIRECTORY + FileName.extension

	if (!NT_SUCCESS(status))
	{
		DbgPrint("RtlUnicodeStringCat() STATUS...: 0x%X", status);
		ExFreePoolWithTag(MyResultString.Buffer, 'TAG');
		return;
	}

	InitializeObjectAttributes(&myoa, &MyResultString, OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE, NULL, NULL);

	HandleFile(&myoa, MyResultString);
	ExFreePoolWithTag(MyResultString.Buffer, 'TAG');
}
else 
{
	DbgPrint("Failed to alloc memory to ResultString");
	ExFreePoolWithTag(MyResultString.Buffer, 'TAG');
	return;
}
}
Attachments
Screenshot_1.png
Screenshot_1.png (381.63 KiB) Viewed 171 times
 #31400  by EP_X0FF
 Mon Apr 02, 2018 11:55 am
I don't know from where you copy-pasted this piece of code but it clearly not something you should use if you are unable to set a few breakpoints in it and look what is passed to the API.
If you ask me, then I tried it ported to usermode (replaced ExAlloc/Free with VirtualAlloc/Free), and with small tweaks (adding missing declarations) - it works. I would not discuss if this code is "great" or fucked up. It just works even if it looks like shit tbh, however I would never write this in _that_ unfriendly way and there is no difference in case of complier/API usage optimization and result.

The status STATUS_ACCESS_VIOLATION is because Nt routine failed to validate pointer you gave to it, because you are mixing Nt/Zw calls in your code without real understanding difference between them and what is the point anyway, in driver use Zw, in user mode Nt (to clearly divide where what code used). You don't need this mix in driver (unless you are doing some extraordinary things and system hacks) and it makes code look like a mozaic shit.
Last edited by EP_X0FF on Mon Apr 02, 2018 12:09 pm, edited 1 time in total. Reason: edit: typo
 #31402  by Vrtule
 Mon Apr 02, 2018 4:57 pm
Hello,
I am not sure if this is the case, but if you calling NtOpenFile in your driver as a reaction to an IOCTL from your application, you need to pass usermode buffers to it, otherwise, the checks made by the routine fail.

Or use ZwOpenFile instead, as EP is suggesting. If it does not work for you either, it may return a different error (since it does not perform the checks NtOpenFile does).