Linking File Objects to Processes

I was not completely satisfied with my first file object scanner. It revealed a lot of objects, including files that were hidden through malicious activity. But it was lacking the ability to connect those files to processes. A couple of days of research later I'm excited to release an improved version.

The problem is: How can we link a file to a (probably hidden or terminated) process? Obviously there is no such information in the _FILE_OBJECT structure. I finally found the solution in a part of the _OBJECT_HEADER that I had till now.

The matter is slightly complicated and I'll try to explain it with two examples. The first example shows a very common case. Let me start with the predominant case.

I pick a "File" allocation from the nonpaged pool, it starts at 0x810c2dc8. Skip over the _POOL_HEADER, 8 unknown bytes and the _OBJECT_HEADER to reach the _FILE_OBJECT at 0x810c2df0. The !object command of WinDbg confirms it's a file object.

kd> !object 810c2dc8+8+8+18
Object: 810c2df0  Type: (812b5730) File
    ObjectHeader: 810c2dd8 (old version)
    HandleCount: 1  PointerCount: 2
    Directory Object: 00000000  Name: \WINDOWS\system32\oobe {HarddiskVolume1}

We're now at the end of the object header. Let's go back to its beginning:

kd> dt _OBJECT_HEADER 810c2dd8
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 2
   +0x004 HandleCount      : 1
   +0x004 NextToFree       : 0x00000001 
   +0x008 Type             : 0x812b5730 _OBJECT_TYPE
   +0x00c NameInfoOffset   : 0 
   +0x00d HandleInfoOffset : 0x8 
   +0x00e QuotaInfoOffset  : 0 
   +0x00f Flags            : 0x40
   +0x010 ObjectCreateInfo : 0x80560580 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x80560580 
   +0x014 SecurityDescriptor : (null) 
   +0x018 Body             : _QUAD

There are two things worth mentioning here. First, the HandleInfoOffset member points at 8 bytes in front of the object header. These are the "unknown 8 bytes" we skipped earlier. The second thing are the Flags. But we'll get back to that later.

kd> db 810c2dd8-8 L 8
810c2dd0  10 64 1c 81 01 00 00 00                          .d......

Just two DWORDs. The first one turns out to be the base address of the owning process object, while the second is the handle count.

kd> !object 811c6410
Object: 811c6410  Type: (81292e38) Process
    ObjectHeader: 811c63f8 (old version)
    HandleCount: 11  PointerCount: 227

Finally we have found a way to trace an open file back to its owning process!

But we're not finished yet. An improved version of my fileobscan plugin worked fine on the overwhelming part of file objects. Sometimes, however, it returned nonsensical values instead of object pointers. Here is one of those file objects that caused me trouble. We'll go through the same sequence of steps as before:

kd> !object 81197ee0
Object: 81197ee0  Type: (812b5730) File
    ObjectHeader: 81197ec8 (old version)
    HandleCount: 1  PointerCount: 1
    Directory Object: 00000000  Name: \Program Files\test.exe

kd> dt _OBJECT_HEADER 81197ec8
nt!_OBJECT_HEADER +0x000 PointerCount : 1 +0x004 HandleCount : 1 +0x004 NextToFree : 0x00000001 +0x008 Type : 0x812b5730 _OBJECT_TYPE +0x00c NameInfoOffset : 0 +0x00d HandleInfoOffset : 0x8 +0x00e QuotaInfoOffset : 0 +0x00f Flags : 0 +0x010 ObjectCreateInfo : 0x8125c218 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x8125c218 +0x014 SecurityDescriptor : (null) +0x018 Body : _QUAD kd> db 81197ec8-8 L 8 81197ec0 88 8f 4e e1 01 00 00 00 ..N..... kd> !object e14e8f88 Object: e14e8f88 Type: (00005d68) ObjectHeader: e14e8f70 (old version) HandleCount: 3781919672 PointerCount: 3779971048 Directory Object: 4841e000 Name: (*** Name not accessible ***)

It's not an object at all! But what went wrong?!

Did you notice the difference in the Flags? In the first example bit 0x40 is set, while in the second example all flags are cleared. As it can be seen in nt!ObpIncrementHandleDataBase this bit is if there's a single owner.

If there are multiple owners (flag is cleared), the first DWORD does not point at an _EPROCESS, but a list of entries. The second DWORD states the number of entries. Let's do it again:

kd> db 81197ec8-8 L 8
81197ec0  88 8f 4e e1 01 00 00 00                          ..N.....

The table starts at 0xe14e8f88 and contains 1 entry. Don't ask me why the system did not chose to use the other encoding. Possibly there were more owners associated with that file earlier. The list is stored in an "ObHd" allocation (object handle count database) in the paged pool. And here is tour first and only entry:

kd> db e14e8f88 L 8
e14e8f88  02 00 00 00 80 93 1f 81                          ........

This time the first DWORD states the number of open handles and the second is a pointer to the owning process. Is it a process?

kd> !object 811f9380
Object: 811f9380  Type: (81292e38) Process
    ObjectHeader: 811f9368 (old version)
    HandleCount: 1  PointerCount: 9

The updated fileobjscan plugin is available here. Please backup your Volatility installation and unzip the archive in your Volatility base directory. If you use the "bleeding edge" version from the Volatility SVN repository, then please install only the plugin file.

And finally here's some (truncated) sample output for you.

Phys.Addr. Obj Type   #Ptr #Hnd Access EProcess    PID Name
0x0021a1e8 0x812b5730    1    0 R--r-d 0x00000000    0 \WINDOWS\system32\netdde.exe
0x0021a950 0x812b5730    2    1 ------ 0xffb406b8 1052 \DAV RPC SERVICE
0x0021aa28 0x812b5730    2    1 ------ 0xffb406b8 1052 \DAV RPC SERVICE
0x002b78a8 0x812b5730    2    1 ------ 0x81104d50 1216 \NetDDE
0x002caf70 0x812b5730    1    0 R--r-d 0x00000000    0 \WINDOWS\system32\pjlmon.dll

Based on the EProcess and PID information given, we now can continue with our investigation (i.e. by running "volatility pslist").

Name                 Pid    PPid   Thds   Hnds   Time
svchost.exe          1052   556    8      129    Thu May 17 15:34:32 2007
netdde.exe           1216   556    10     67     Thu May 17 15:34:35 2007

Archives

Imprint

This blog is a project of:
Andreas Schuster
Im Äuelchen 45
D-53177 Bonn
impressum@forensikblog.de

Copyright © 2005-2012 by
Andreas Schuster
All rights reserved.
Powered by Movable Type 5.12