714 views
首页 > 内核编程 > 获取文件对象的名称

获取文件对象的名称

2010年5月25日

一.取文件对象名称

我们可以使用函数ObQueryNameString 来查询获取文件对象(FILE_OBJECT )的名称。由于文件对象有专门的名称查询函数IopQueryName ,所以ObQueryNameString 在内部会直接调用这个函数来查询文件对象名。

我们还有另外一种方法比较“直接”地获得文件对象名称。我们知道:文件对象名包括驱动器名和文件路径名。

FILE_OBJECT 结构中有一个成员FileName ,它只包括文件路径名(注意:不包括驱动器名)。

FILE_OBJECT 结构中另外有一个成员DeviceObject ,它是指向DEVICE_OBJECT 的设备对象。该设备就是包含该文件的驱动器设备对象。我们可以调用ObQueryNameString 查询获取此设备对象的名称。但是设备名称的格式是这样的:\\Device\\HarddiskvolumeX (X 为数字),并不是常见的C/D/E… 驱动器名格式。这是因为我们说的驱动器名其实是上面这些设备对象的符号链接(Symbolic link) 名,我们可以通过IoVolumeDeviceToDosName (在XP/2003 等下使用,在NT/2000 下使用RtlVolumeDeviceToDosName )来将设备名称转成驱动器名。

但是若直接使用ObQueryNameString 查询文件对象的话,返回的名称就是设备名称和文路径名,如:
\Device\HarddiskVolume1\WINDOWS\system32\smss.exe
此时我们是没有设备对象,所以就没有办法调用IoVolumeDeviceToDosName 来转化了,此时该怎么办?有一个比较笨的方法,就是调用ZwOpenSymbolcLink 对象对所有A~Z 字母进行打开链接对象,并调用ZwQuerySymbilicLink 来获得对应的设备对象名,并将这个设备名与上面的ObQueyNameString 返回的设备名进行比较,以此来确定驱动器名。见下面的代码片段:

  …
RtlInitUnicodeString(&SymbolicLink, L"\\??\\C:");
LinkTarget.MaximumLength = 200;
LinkTarget.Buffer = ExAllocatePoolWithTag(NonPagedPool, 200, 'test');
for (c = 'A'; c <='Z'; c++)
{
     SymbolicLink.Buffer[4] = c;
     InitializeObjectAttributes(&oa, &SymbolicLink, OBJ_KERNEL_HANDLE, 0, NULL);
     Status = ZwOpenSymbolicLinkObject(&LinkHandle, GENERIC_READ, &oa);
     if (Status != STATUS_SUCCESS)
          continue;
     Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL);
     //LinkTarget 返回的就是设备名称
     ZwClose(LinkHandle);
     if (Status == STATUS_SUCCESS)
     {
          RetLength = LinkTarget.Length;
          if (RtlCompareMemory(LinkTarget.Buffer, ObjectNameInfo->Name.Buffer, RetLength) == RetLength)
          break; //ObjectNameInfo 为ObQueryName 返回的文件对象名
     }
}
ExFreePoolWithTag(LinkTarget.Buffer, 'test');
…


二.引申:获取进程的主程序名

首先说一下每个进程的映像名称,这不是进程对象名。在_EPROCESS 结构中有一个ImageFileName 得成员(在XPSP3 下偏移为174H ), 它是一个16 个字节的数组,它就是进程的映像名称,当有名称超过15 位是就截取前15 个字节,最后一个字节为NULL 。

当需要知道进程的主程序名时,我们通过下面的关系获得该进程对应的文件对象 : EPROCESS-> SectionObject ->Segment->controlArea->FilePointer ,只有一点要注意: SectionObject 的 Segment 结构为 _SEGMENT ,而不是 _SEGMENT_OBJECT ,这个常常搞错。

例如:下例就是返回进程对象对应的文件对象的过程:

VOID NTAPI GetFilePointer(PULONG ProcessObject, PULONG *FileObject)
{
     ULONG Section, Segment ,ControlArea;
     *FileObject = NULL;
     if (ProcessObject)
         Section = *(PULONG)((PCHAR)ProcessObject + 0x138);
     else
         return;
     if (Section)
         Segment = *(PULONG)((PCHAR)Section + 0x14);
     else
         return;
     if (Segment)
         ControlArea = *(PULONG)((PCHAR)Segment + 0x0);
     else
         return;
     if (ControlArea)
         *FileObject = *(PULONG *)((PCHAR)ControlArea + 0x24);
}

内核编程

  1. 目前还没有任何评论.
  1. 目前还没有任何 trackbacks 和 pingbacks.