Click here to Skip to main content
14,268,370 members
Rate this:
Please Sign up or sign in to vote.
Dear coders,

I issued problems I weren't able to solve until now, while using my IRP_MJ_DIRECTORY_CONTROL callback function as it will follow in the code snippet below.

Now, first I will mention my goal and an introduction, after that the problem(s) I experienced und I will end up making further proposals regarding a solution to my problem.

The goal is to create or own a working proof of concept file hider for Win7 x64, since I've quite great respect for code or programs that really are able to fully hide any files I think about on such a sophisticated OS.

Since I weren't able to find a working file hider I tried to write one on my own.
And since kernel patching (runtime patching, hooking, detour patching) is no longer allowed due to KPP I looked for another solution and came across layered drivers which were also supposed to be able to hide files.

I then started with trying to unterstand the "KLOG v1.0" key sniffer and to adapt it's code to an own mouse hook which worked surprisingly well.
Afterwards I tried to adapt the file filter driver (also in the "Rootkits" book) as well but in the book unfortunately it's handled only very conceptual and theorectical.
Due to this, big chunks of code are missing in the book, so I weren't able to adapt the full code.

Hence I tried out the "File System Filter Driver Tutorial" by Sergey Podobry and it printed any filename involved in an MJ_CREATE request out to the debug print as expected.
I replaced the IRP_MJ_CREATE callback function with the IRP_MJ_DIRECTORY_CONTROL function and added an irp completion routine.

So according to Hoglund and Butler I actually expected the "Irp->Userbuffer" to contain a list of all directory entries existing in the folder being current scoped(both by me or by operating system), among others.

If all worked correctly I would be able to modify the list of existing files in a directory in order to delete unwanted entries, so these files should no longer show up.
(The goal of the study is as mentioned before, a working proof of concept file hider capable to run on Win7 x64.)

In order to get this file list the "Irp->Userbuffer" is being converted into a "PFILE_BOTH_DIR_INFORMATION" data structure which consists also of a member called "NextEntryOffset" among further data.
Now the existence of "NextEntryOffset" is not "zero" says that there are other files in the folder.

Now my problem is as follows:

I want to look inside the "Irp->UserBuffer" of MN_QUERY_DIRECTORY_CONTROL IRPs, but in most cases the NextEntryOffset of the PFILE_BOTH_DIR_INFORMATION of the UserBuffer is just 0 Bytes.
And that means that there are no other files to show in the directory being examinated.
But even if I press F5 in a folder with 4 files the NextEntryOffset is zero.

And not only that but also are the fileNames extremely strange if displayed on DebugView.
I just don't get it....

The IRPs btw seem to return correctly in my opinion, since unless I'm making an attempt of unloading the driver all computer operations are working properly.

Hence, I'm only going to show you the code of the dispatch c-file.
Code being commented out is like this: //
Valid code explanations are like that: //==>
#include "FsFilter.h"

extern int numPendingIrps;
extern BOOLEAN stopTagging;
//extern BOOLEAN forceFlag;
//extern PIRP irpStoreArray[11];
// PassThrough IRP Handler

//==>Valid for all the IRPs that don't match the "MJ_DIRECTORY_CONTROL" criteria.
NTSTATUS FsFilterDispatchWithoutTouching(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp){  
    return IoCallDriver(pDevExt->AttachedToDeviceObject, Irp);

//==>Tag all MJ_DIRECTORY_CONTROL IRPs with a completition routine.
NTSTATUS FsFilterTagIrpFromUpperside(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp){
   // PFILE_OBJECT pFileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
	//int i = 0;
//	int j = 0;
	//int countedIrps = 0;
	//DbgPrint("IRP \"MJ_DIRECTORY_CONTROL\" came from upper side!");
    //DbgPrint("%wZ\n", &pFileObject->FileName);
	//storeIrp = Irp;

		PIO_STACK_LOCATION currentIrpStack;
		currentIrpStack = IoGetCurrentIrpStackLocation(Irp);   
		nextIrpStack = IoGetNextIrpStackLocation(Irp);   
		*nextIrpStack = *currentIrpStack;   

		IoSetCompletionRoutine(Irp, FsFilterDirCtrlCompletion, DeviceObject, TRUE, TRUE, TRUE);
		//==>Was supposed to give me an opportunity of canceling remaining IRPs if I did't want
		//==>to wait too long for their successful completion. Currently not implemented due to

		/*irpStoreArray[numPendingIrps] = Irp;
		for(i = 0; i < 11; i++){
			if(irpStoreArray[i] != NULL){
		//DbgPrint("Tagged IRP \"MJ_DIRECTORY_CONTROL\"!\nNow about to call lower driver...\ncurrently numPendingIrps = %i, irps in array = %i", numPendingIrps, countedIrps);
	//==>If tagging flag deleted, stop tagging and just forward all further incoming IRPs.
			for(j = 0; j < 11; j++){
				if(irpStoreArray[j] != NULL){
					DbgPrint("Canceling an IRP at location %i.", j);
					DbgPrint("Canceled an IRP at location %i.", j);
					irpStoreArray[j] = NULL;

	//==> call lowerside driver
    return IoCallDriver(pDevExt->AttachedToDeviceObject, Irp);
    //return FsFilterDispatchPassThrough(DeviceObject, Irp);


NTSTATUS FsFilterDirCtrlCompletion(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, __in PVOID Context){
	//int i = 0;
	//int countedIrps = 0;
	if(Irp->IoStatus.Status == STATUS_SUCCESS){
		PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
		//DbgPrint("STATUS SUCCESS!!!!!!!!!!!!!11111111");
		//Code taken from Hoglund's & Butler's "Rootkits Subverting Windows Kernel", But I can't judge if correct..... 
		//==>However we check a few parameters such as Minor IRP function as well as IRQL before going on...
		if((irpSp->MinorFunction == IRP_MN_QUERY_DIRECTORY) /* && (irpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation) */&& (KeGetCurrentIrql() == PASSIVE_LEVEL)){
			//==>My custom test since I don't know which of these three structures does contain my directory entries.
			//DbgPrint("Alright, let's hide s0me filez!!1");
			//PFILE_BOTH_DIR_INFORMATION volatile nextBuffer = NULL;
			//ULONG bufferLenght;
			queryBuffer = (PFILE_BOTH_DIR_INFORMATION)Irp->UserBuffer;
			queryBuffer2 = (PFILE_DIRECTORY_INFORMATION)Irp->UserBuffer;
			queryBuffer3 = (PFILE_FULL_DIR_INFORMATION)Irp->UserBuffer;
			if(queryBuffer != 0){
				/*DbgPrint("\"queryBuffer->AllocationSize\" = %i", queryBuffer->AllocationSize);
				DbgPrint("\"queryBuffer->EaSize\" = %i", queryBuffer->EaSize);
				DbgPrint("\"queryBuffer->EndOfFile\" = %i", queryBuffer->EndOfFile);
				DbgPrint("\"queryBuffer->FileAttributes\" = %i", queryBuffer->FileAttributes);
				DbgPrint("\"queryBuffer->FileIndex\" = %i", queryBuffer->FileIndex);*/
				DbgPrint("\"queryBuffer->FileName\" = %c", queryBuffer->FileName);
				DbgPrint("\"queryBuffer->FileNameLenght\" = %i", queryBuffer->FileNameLength);
				DbgPrint("\"queryBuffer->ShortName\" = %ws", queryBuffer->ShortName);
				DbgPrint("\"queryBuffer->ShortNameLength\" = %c", queryBuffer->ShortNameLength);
				if(queryBuffer->NextEntryOffset != 0){
					DbgPrint("\"queryBuffer->NextEntryOffset\" =															%i", queryBuffer->NextEntryOffset);
					DbgPrint("\"queryBuffer->NextEntryOffset\" =															%i", queryBuffer2->NextEntryOffset);
					DbgPrint("\"queryBuffer->NextEntryOffset\" =															%i", queryBuffer3->NextEntryOffset);
				//DbgPrint("QueryBuffer empty.");
		}/*else if((irpSp->MinorFunction == IRP_MN_QUERY_DIRECTORY) && (irpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation) && (KeGetCurrentIrql() == PASSIVE_LEVEL)){
			//PFILE_BOTH_DIR_INFORMATION volatile queryBuffer = NULL;
			//PFILE_BOTH_DIR_INFORMATION volatile nextBuffer = NULL;
			//ULONG bufferLenght;
		}else if((irpSp->MinorFunction == IRP_MN_QUERY_DIRECTORY) && (irpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation) && (KeGetCurrentIrql() == PASSIVE_LEVEL)){
			//PFILE_BOTH_DIR_INFORMATION volatile queryBuffer = NULL;
			//PFILE_BOTH_DIR_INFORMATION volatile nextBuffer = NULL;
			//ULONG bufferLenght;
	//DbgPrint("Returning IRP back to upperside..");
	//==>Actually if we modified the IRP (successfully) we should return it back to upperside.
	//==>Even if we didn't modify a thing we need to turn it back in order to remain with a fully functional Windows!
	if(Irp->PendingReturned == TRUE){
	//irpStoreArray[numPendingIrps] = NULL;
	/*for(i = 0; i < 11; i++){
		if(irpStoreArray[i] != NULL){
	//DbgPrint("Pending Irps now = %i stored irps = %i", numPendingIrps, countedIrps);
	return Irp->IoStatus.Status;

I'm sorry for the bad format of code...but I don't know how to align at forum format..

So the question is, what am I doing the wrong way?

Further possible solution proposals:
Should I try to intercept other IRPs as well? (such as MJ_READ or MJ_CREATE IRPs?)
Might vanish information due to fast IO which isn't being intercepted?

Thanks alot for helping.

Sincerely, Microwave

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100