Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Section Handles Enumeration. Extending File Unlocking

5.00/5 (11 votes)
19 May 2015CPOL6 min read 17.6K   362  
How to free blocked files, inclusive if they are mapped in memory. Something that many tools are missing.

Introduction

Many security tools are proud to release presumably infected files when they are locked in the file system. This feature is not only used in security, else like a method to unblock a file locked by some buggy process. In that case, you could choose to close the buggy application or if you can't find it, restart the system. Some others would prefer to spend some time reading an article on the internet or looking for an unblocking tool. This tip covers what most of those tools are missing, closing the memory mapped files associated with the open file.

Common File Handle Enumeration

Enumerating file handles is a great opportunity to play in the field of the undocumented with the famous ZwQuerySystemInformation() and the parameter SystemHandleInformation. The tricky part about this is to recognize from every handle in the system which is a file handle. The structure returned from the API call is the following:

C++
typedef struct  _SYSTEM_HANDLE_INFORMATION
{
    ULONG                   ProcessId;
    UCHAR                   ObjectTypeNumber;
    UCHAR                   Flags;
    USHORT                  Handle;
    PVOID                   Object;
    ACCESS_MASK             GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

The interesting field for now is the ObjectTypeNumber. This value will guide you to know if this handle represents a file or not. Sadly, this value is not the same for all the operating system versions and we must expect that could change in future releases. The following table shows the file object type numbers for the current releases:

Operating System File object Type Section Object Type
Windows 2000 26 19
Windows XP 28 19
Windows Vista 28 33
Windows 7 28 33
Windows 8 31 36
Windows 8.1 30 35

These numbers were discovered using Windbg and the debug symbols of the target operating system. The procedure is not hard to repeat for any other operating system version and the internet is full of examples to guide you in the process.

The following example is a listing all the file handles for the process with identifier 0x390. Taking any known file for the experiment, it is possible to get the object header structure for that file object.

kd> !handle 0 3 0390 File
Searching for Process with Cid == 390
Searching for handles of type File
PROCESS a4138040  SessionId: 1  Cid: 0390    Peb: 7fb23000  ParentCid: 06e4
    DirBase: 3db97620  ObjectTable: 8b3e9480  HandleCount: <Data Not Accessible>
    Image: notepad.exe
...
0128: Object: 9b5a5300  GrantedAccess: 00120089 Entry: 8ca25250
Object: 9b5a5300  Type: (8532fc30) File
    ObjectHeader: 9b5a52e8 (new version)
        HandleCount: 1  PointerCount: 30
        Directory Object: 00000000  Name: \Windows\Fonts\StaticCache.dat {HarddiskVolume2}
...
kd> dt _OBJECT_HEADER 9b5a52e8
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n27
   +0x004 HandleCount      : 0n1
   +0x004 NextToFree       : 0x00000001 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0x1e ''
   ...

Giving the ObjectHeader pointer the next step is to obtain the TypeIndex. Depending on the operating system version could be referred as the direct file object type number or the index in the object type table. In any case, this value is enough to identify if the object is a file or not.

A smart programmer could do this calculation programmatically. Anyway, that will not solve the problem of constantly checking if your driver is not going to crash in the next operating system version.

The solution for file handle enumeration is straightforward now. Call ZwQuerySystemInformation and then filter from the output array the interesting file handle information using the known file object type number. In case of looking for a particular file name, it is possible to obtain the file name from the file object in the returned information. Of course, it will always will be saved to obtain all the required reference pointers to avoid the object going away while checking in memory information.

Section Handle Enumeration

Now, it is possible to use the same method to look for memory mapped files handles. The previous table already shows the object type number for section objects. But in this case, there is an additional problem, not all the section objects have names, or at least a meaningful name. So, it will be difficult to make a relation between the section handle information that it is enumerated and a certain file name. Then, it is time to go deeper in the internal structures of the file system. Using again the Windbg and taking a look on the file object structure:

C++
typedef struct _FILE_OBJECT {
CSHORT                    Type;
CSHORT                    Size;
PDEVICE_OBJECT            DeviceObject;
PVPB                      Vpb;
PVOID                     FsContext;
PVOID                     FsContext2;
PSECTION_OBJECT_POINTERS  SectionObjectPointer;
PVOID                     PrivateCacheMap;
NTSTATUS                  FinalStatus;
struct _FILE_OBJECT      *RelatedFileObject;
BOOLEAN                   LockOperation;
BOOLEAN                   DeletePending;
BOOLEAN                   ReadAccess;
BOOLEAN                   WriteAccess;
BOOLEAN                   DeleteAccess;
BOOLEAN                   SharedRead;
BOOLEAN                   SharedWrite;
BOOLEAN                   SharedDelete;
ULONG                     Flags;
UNICODE_STRING            FileName;
LARGE_INTEGER             CurrentByteOffset;
__volatile ULONG          Waiters;
__volatile ULONG          Busy;
PVOID                     LastLock;
KEVENT                    Lock;
KEVENT                    Event;
__volatile PIO_COMPLETION_CONTEXT CompletionContext;
KSPIN_LOCK                IrpListLock;
LIST_ENTRY                IrpList;
__volatile PVOID          FileObjectExtension;
} FILE_OBJECT, *PFILE_OBJECT;

There is a field on this structure that has a name which is very interesting for this study, SectionObjectPointers. This is not the pointer to the section object related with the file object, but close to it. The section object pointer structure holds three pointers:

C++
typedef struct _SECTION_OBJECT_POINTERS {
  PVOID DataSectionObject;
  PVOID SharedCacheMap;
  PVOID ImageSectionObject;
} SECTION_OBJECT_POINTERS, *PSECTION_OBJECT_POINTERS;

The DataSectionObject pointer points to CONTROL_AREA structure used by the Memory Manager to track the state information for a given section. The ShareCacheMap is only initialized when the file system already is caching this file object. The ImageSectionPointer is another CONTROL_AREA but in this case is used when the file is an executable image. The CONTROL_AREA structure is not documented, but Windbg shows the following declaration:

C++
nt!_CONTROL_AREA
   +0x000 Segment          : Ptr32 _SEGMENT
   +0x004 ListHead         : _LIST_ENTRY
   +0x00c NumberOfSectionReferences : Uint4B
   +0x010 NumberOfPfnReferences : Uint4B
   +0x014 NumberOfMappedViews : Uint4B
   +0x018 NumberOfUserReferences : Uint4B
   +0x01c u                : <unnamed-tag>
   +0x020 FilePointer      : _EX_FAST_REF
   +0x024 ControlAreaLock  : Int4B
   +0x028 ModifiedWriteCount : Uint4B
   +0x02c WaitList         : Ptr32 _MI_CONTROL_AREA_WAIT_BLOCK
   +0x030 u2               : <unnamed-tag>
   +0x040 LockedPages      : Uint8B
   +0x048 FileObjectLock   : _EX_PUSH_LOCK

Until now, there isn't a clear way to get the section object for this file object. But doing some steps back, and taking a look at the undocumented SECTION_OBJECT structure:

C++
nt!_SECTION_OBJECT
   +0x000 StartingVa       : Ptr32 Void
   +0x004 EndingVa         : Ptr32 Void
   +0x008 Parent           : Ptr32 Void
   +0x00c LeftChild        : Ptr32 Void
   +0x010 RightChild       : Ptr32 Void
   +0x014 Segment          : Ptr32 _SEGMENT_OBJECT 

Now, we can see a way to relate the file object with the section object. The section object structure holds a pointer to a SEGMENT_OBJECT. After some experiments with Windbg, it is easy to corroborate that for a memory mapped file that segment pointer is the same to the segment pointer in the data section object for the section object pointer of the file object. This relation is commonly used in forensic analysis, the segment object pointer is the key to find the physical memory backing this file, in other words the content of the file in memory. In this case, we only want to know if there is a section object for this file and from this section object the file name. This relation looks pretty simple in the following diagram:

Image 1

After all this, it will just start to write some lines and add some improvements to the known file handle enumeration code. Now, we can extend it and get not only the section handles else the correspondent file name. From a given file name, we could get all the file handles, and all the section handles. From those handles, you could do what you want, at your own risk.

The original purpose was to unblock completely a file in the operating system. So, we have all the handles we need, now we call the API DuplicateHandle() with the option DUPLICATE_CLOSE_SOURCE for every handle. Close your new handles, cross your fingers, and the file will be free again.

Demo Application and Driver

The provided applications are very simple, and the only intention is to show a proof of concept. The HandleBlockApp.exe will create a little file on the same path with the name "unblockme.exe". Then, it will map the file in memory and will loop forever. That will be the moment to try to do any normal operations with the file and see how the access is denied by Windows. After that, you could use the application HandleCloseApp.exe, with administrator rights, because it will need to install the driver (be sure it is in the same path of the application). You must pass the full path to the file you want to release, in our case, the full path to "unblockme.exe". If everything goes fine, after this, you could try again to delete that file. One very interesting result with this demo is that I was able to release inclusive, loaded dynamic libraries.

Please use this application in a test environment. It is very important to remember that to run the driver in x64, you will need to disable the signature enforcement at boot time. I have tested it in many operating systems and I will be happy to fix all the bugs you found.

License

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