现在的位置: 首页 > 综合 > 正文

有关遍历进程中句柄的方法总结

2017年10月20日 ⁄ 综合 ⁄ 共 9386字 ⁄ 字号 评论关闭

此篇文章说是原创有些牵强。就像题目所说的,更多的是对前人方法的总结。写作的初衷倒也不是技术方面的研究,不过是工作的需求罢了。
方法中涉及到一些函数需要提权,其实我一直以为网上那个标准的提权函数没什么用,直到这次写程序我才知道原来有的时候是真的需要提权的。现附上一份比较好看的提权代码,也方便自己以后使用。
 
BOOL AdjustProcessPrivilege(HANDLE hProcess, LPCTSTR lpPrivilegeName, DWORD dwPrivilegeAttribute)
{
 BOOL bRetValue = FALSE;
 HANDLE hProcessToken;
 
 //如果hProcess是NULL,说明调用者想要调整当前进程的权限,使用GetCurrentProcess获得的进程句柄无需关闭
 HANDLE hOpenProcess = (hProcess != NULL) ? hProcess : GetCurrentProcess();
 //打开进程令牌,期望的权限为可以调整权限和查询,得到进程令牌句柄
 if(OpenProcessToken(hOpenProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken) != FALSE)
 {
  LUID stPrivilegeLuid;
  //通过权限名称,查找指定的权限的LUID值
  if(LookupPrivilegeValue(NULL, lpPrivilegeName, &stPrivilegeLuid) != FALSE)
  {
   //设置新的权限
   TOKEN_PRIVILEGES stNewTokenPrivilege;
   stNewTokenPrivilege.PrivilegeCount = 1;
   stNewTokenPrivilege.Privileges[0].Luid = stPrivilegeLuid;
   stNewTokenPrivilege.Privileges[0].Attributes = dwPrivilegeAttribute;
   //调整权限
   if(AdjustTokenPrivileges(hProcessToken, FALSE, &stNewTokenPrivilege, sizeof(stNewTokenPrivilege), NULL, NULL) != FALSE)
   {
    bRetValue = TRUE;
   }
  }
  //关闭进程令牌句柄
  CloseHandle(hProcessToken);
 }
 return bRetValue;
}

调用方法如下:
 
if(AdjustProcessPrivilege((HANDLE)NULL, SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED) == FALSE ||
  AdjustProcessPrivilege((HANDLE)NULL, SE_TAKE_OWNERSHIP_NAME, SE_PRIVILEGE_ENABLED) == FALSE ||
  AdjustProcessPrivilege((HANDLE)NULL, SE_SECURITY_NAME, SE_PRIVILEGE_ENABLED) == FALSE ||
  AdjustProcessPrivilege((HANDLE)NULL, SE_AUDIT_NAME, SE_PRIVILEGE_ENABLED) == FALSE)
 {
  printf(("Adjust process privilege failed!/n"));
 }

整个提权流程实现起来比较傻瓜化,很符合我的喜好。
好了,步入正体。开始总结如何遍历一个进程中所打开的句柄。首先,介绍两个结构体。

typedef struct _SYSTEM_HANDLE
{
 ULONG  uIdProcess;
 UCHAR  ObjectType;    // OB_TYPE_* (OB_TYPE_TYPE, etc.)
 UCHAR  Flags;         // HANDLE_FLAG_* (HANDLE_FLAG_INHERIT, etc.)
 USHORT  Handle;
 PVOID  pObject;
 ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
 
typedef struct _SYSTEM_HANDLE_INFORMATION
{
 ULONG   uCount;
 SYSTEM_HANDLE aSH[];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

这两个结构体定义了系统句柄的信息。接下来,我们看代码:
 
void MyCloseHandle(DWORD pid)
{
 //获取进程中的句柄
 PSYSTEM_HANDLE_INFORMATION Info;
 ULONG r;
 CHAR Name[MAX_PATH];
 HANDLE hProcess, hFile;
 hHeap = GetProcessHeap();
 Info = (PSYSTEM_HANDLE_INFORMATION)GetInfoTable(SystemHandleInformation);
 if (Info)
 {
  for (r = 0; r < Info->uCount; r++)
  {
   if (Info->aSH[r].uIdProcess == pid)
   {
    hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, Info->aSH[r].uIdProcess);
    if (hProcess)
    {
     if (DuplicateHandle(hProcess, (HANDLE)Info->aSH[r].Handle, GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
     {
      GetFileName(hFile, Name);
      if(strstr(Name, "Intel") != NULL)
      {
       printf("PID=%d FileHandle %d FileName=%s\n", Info->aSH[r].uIdProcess, Info->aSH[r].Handle, Name);
      }
      
      CloseHandle(hFile);
     }
     CloseHandle(hProcess);
    }
   }
  }
  HeapFree(hHeap, 0, Info);
 }
 printf("Duplicate Finish.\n");
}

hHeap是一个HANDLE型的全局变量,由于用的地方多并且我比较懒,所以定义成了一个全局变量。最好还是作为局部变量从函数中传递。至于GetInfoTable留到下面再说。SystemHandleInformation则是用来获取系统中句柄信息的一个重要参数。参见结构体:

typedef enum _SYSTEMINFOCLASS
{
 SystemBasicInformation,             // 0x002C
 SystemProcessorInformation,         // 0x000C
 SystemPerformanceInformation,       // 0x0138
 SystemTimeInformation,              // 0x0020
 SystemPathInformation,              // not implemented
 SystemProcessInformation,           // 0x00C8+ per process
 SystemCallInformation,              // 0x0018 + (n * 0x0004)
 SystemConfigurationInformation,     // 0x0018
 SystemProcessorCounters,            // 0x0030 per cpu
 SystemGlobalFlag,                   // 0x0004 (fails if size != 4)
 SystemCallTimeInformation,          // not implemented
 SystemModuleInformation,            // 0x0004 + (n * 0x011C)
 SystemLockInformation,              // 0x0004 + (n * 0x0024)
 SystemStackTraceInformation,        // not implemented
 SystemPagedPoolInformation,         // checked build only
 SystemNonPagedPoolInformation,      // checked build only
 SystemHandleInformation,            // 0x0004  + (n * 0x0010)
 SystemObjectTypeInformation,        // 0x0038+ + (n * 0x0030+)
 SystemPageFileInformation,          // 0x0018+ per page file
 SystemVdmInstemulInformation,       // 0x0088
 SystemVdmBopInformation,            // invalid info class
 SystemCacheInformation,             // 0x0024
 SystemPoolTagInformation,           // 0x0004 + (n * 0x001C)
 SystemInterruptInformation,         // 0x0000, or 0x0018 per cpu
 SystemDpcInformation,               // 0x0014
 SystemFullMemoryInformation,        // checked build only
 SystemLoadDriver,                   // 0x0018, set mode only
 SystemUnloadDriver,                 // 0x0004, set mode only
 SystemTimeAdjustmentInformation,    // 0x000C, 0x0008 writeable
 SystemSummaryMemoryInformation,     // checked build only
 SystemNextEventIdInformation,       // checked build only
 SystemEventIdsInformation,          // checked build only
 SystemCrashDumpInformation,         // 0x0004
 SystemExceptionInformation,         // 0x0010
 SystemCrashDumpStateInformation,    // 0x0004
 SystemDebuggerInformation,          // 0x0002
 SystemContextSwitchInformation,     // 0x0030
 SystemRegistryQuotaInformation,     // 0x000C
 SystemAddDriver,                    // 0x0008, set mode only
 SystemPrioritySeparationInformation,// 0x0004, set mode only
 SystemPlugPlayBusInformation,       // not implemented
 SystemDockInformation,              // not implemented
 SystemPowerInfo,             // 0x0060 (XP only!)
 SystemProcessorSpeedInformation,    // 0x000C (XP only!)
 SystemTimeZoneInformation,          // 0x00AC
 SystemLookasideInformation,         // n * 0x0020
 SystemSetTimeSlipEvent,
 SystemCreateSession,    // set mode only
 SystemDeleteSession,    // set mode only
 SystemInvalidInfoClass1,   // invalid info class
 SystemRangeStartInformation,   // 0x0004 (fails if size != 4)
 SystemVerifierInformation,
 SystemAddVerifier,
 SystemSessionProcessesInformation, // checked build only
 MaxSystemInfoClass
} SYSTEMINFOCLASS, *PSYSTEMINFOCLASS;

通篇文章用到了很多系统未公开函数和结构体。其实这些东西网上都可以查到,我就不细说了。其实水平太菜,想说清楚也不太容易。
当找到属于目标进程中的句柄后,通过DuplicateHandle将该句柄再次打开,只不过这次打开为当前进程所用,以备获取该句柄的其它信息。接下来通过GetFileName获得该句柄的名称。当然这个函数也留到后面再说。我们先来看一下GetInfoTable。

 
PVOID GetInfoTable(IN ULONG ATableType)
{
 ULONG    mSize = 0x8000;
 PVOID    mPtr;
 NTSTATUS status;
 do
 {
  mPtr = HeapAlloc(hHeap, 0, mSize);
  if (!mPtr) return NULL;
  memset(mPtr, 0, mSize);
  
  HMODULE hNtDLL = LoadLibrary("NTDLL.DLL");
  if ( !hNtDLL )
  {
   return FALSE;
  }
  
  ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDLL, "ZwQuerySystemInformation");
  if( ZwQuerySystemInformation == NULL)
  {
   return FALSE;
  }
 
  status = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL);
  
  if (status == STATUS_INFO_LENGTH_MISMATCH)
  {
   HeapFree(hHeap, 0, mPtr);
   mSize = mSize * 2;
  }
 } while (status == STATUS_INFO_LENGTH_MISMATCH);
 if (NT_SUCCESS(status)) return mPtr;
 HeapFree(hHeap, 0, mPtr);
 return NULL;
}

这个函数则是利用ZwQuerySystemInformation获取当前系统中的句柄信息。而ZwQuerySystemInformation则是一个未公开函数。

typedef NTSTATUS (WINAPI *ZWQUERYSYSTEMINFORMATION)(unsigned long, PVOID, ULONG, PULONG);

NTSTATUS
ZwQuerySystemInformation(
 IN SYSTEMINFOCLASS SystemInformationClass,
 OUT PVOID SystemInformation,
 IN ULONG SystemInformationLength,
 OUT PULONG ReturnLength OPTIONAL
);

然后我们再看GetFileName,看看是如何获取到句柄所对应的文件名称的。

void GetFileName(HANDLE hFile, PCHAR TheName)
{
 HANDLE   hThread;
 PNM_INFO Info = (PNM_INFO)HeapAlloc(hHeap, 0, sizeof(NM_INFO));
 Info->hFile = hFile;
 hThread = CreateThread(NULL, 0, GetFileNameThread, Info, 0, NULL);
 if (WaitForSingleObject(hThread, INFINITE) == WAIT_TIMEOUT) TerminateThread(hThread, 0);
 CloseHandle(hThread);
 memset(TheName, 0, MAX_PATH);
 WideCharToMultiByte(CP_ACP, 0, Info->Info.FileName, Info->Info.FileNameLength >> 1, TheName, MAX_PATH, NULL, NULL);
 HeapFree(hHeap, 0, Info);
}

其中,PNM_INFO结构体如下:

typedef struct _FILE_NAME_INFORMATION {
 ULONG  FileNameLength;
 WCHAR  FileName[1];
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
typedef struct _NM_INFO
{
 HANDLE  hFile;
 FILE_NAME_INFORMATION Info;
 WCHAR Name[MAX_PATH];
} NM_INFO, *PNM_INFO;

接下来这里通过创建线程GetFileNameThread来进一步获取句柄信息。
 
DWORD WINAPI GetFileNameThread(PVOID lpParameter)
{
 PNM_INFO NmInfo = (PNM_INFO)lpParameter;
 IO_STATUS_BLOCK IoStatus;
 HMODULE hNtDLL = LoadLibrary("NTDLL.DLL");
 if ( !hNtDLL )
 {
  return FALSE;
 }
 ZWQUERYINFORMATIONFILE ZwQueryInformationFile = (ZWQUERYINFORMATIONFILE)GetProcAddress(hNtDLL, "ZwQueryInformationFile");
 if( ZwQueryInformationFile == NULL)
 {
  return FALSE;
 }
 ZwQueryInformationFile(NmInfo->hFile, &IoStatus, &NmInfo->Info, sizeof(NM_INFO) - sizeof(HANDLE), FileNameInformation);
 return 0;
}

我们发现,函数中利用到了ZwQueryInformationFile来获取句柄详细信息。而ZwQueryInformationFile则又是一个未公开函数。

typedef NTSTATUS (WINAPI *ZWQUERYINFORMATIONFILE)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG,
FILE_INFORMATION_CLASS);

NTSTATUS
ZwQueryInformationFile(
 IN HANDLE  FileHandle,
 OUT PIO_STATUS_BLOCK  IoStatusBlock,
  OUT PVOID  FileInformation,
 IN ULONG  Length,
 IN FILE_INFORMATION_CLASS  FileInformationClass
);

而FILE_INFORMATION_CLASS则是一个枚举类型。

typedef enum _FILE_INFORMATION_CLASS {
 FileDirectoryInformation=1,
 FileFullDirectoryInformation,
 FileBothDirectoryInformation,
 FileBasicInformation,
 FileStandardInformation,
 FileInternalInformation,
 FileEaInformation,
 FileAccessInformation,
 FileNameInformation,
 FileRenameInformation,
 FileLinkInformation,
 FileNamesInformation,
 FileDispositionInformation,
 FilePositionInformation,
 FileFullEaInformation,
 FileModeInformation,
 FileAlignmentInformation,
 FileAllInformation,
 FileAllocationInformation,
 FileEndOfFileInformation,
 FileAlternateNameInformation,
 FileStreamInformation,
 FilePipeInformation,
 FilePipeLocalInformation,
 FilePipeRemoteInformation,
 FileMailslotQueryInformation,
 FileMailslotSetInformation,
 FileCompressionInformation,
 FileCopyOnWriteInformation,
 FileCompletionInformation,
 FileMoveClusterInformation,
 FileQuotaInformation,
 FileReparsePointInformation,
 FileNetworkOpenInformation,
 FileObjectIdInformation,
 FileTrackingInformation,
 FileOleDirectoryInformation,
 FileContentIndexInformation,
 FileInheritContentIndexInformation,
 FileOleInformation,
 FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

时间有点紧,总结的有些粗略。回头抽出时间来再完善吧。

抱歉!评论已关闭.