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


    此篇文章说是原创有些牵强。就像题目所说的,更多的是对前人方法的总结。写作的初衷倒也不是技术方面的研究,不过是工作的需求罢了。
    方法中涉及到一些函数需要提权,其实我一直以为网上那个标准的提权函数没什么用,直到这次写程序我才知道原来有的时候是真的需要提权的。现附上一份比较好看的提权代码,也方便自己以后使用。
     
    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 ", Info->aSH[r].uIdProcess, Info->aSH[r].Handle, Name);
          }
          
          CloseHandle(hFile);
         }
         CloseHandle(hProcess);
        }
       }
      }
      HeapFree(hHeap, 0, Info);
     }
     printf("Duplicate Finish. ");
    }


    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;


    时间有点紧,总结的有些粗略。回头抽出时间来再完善吧。
  • 相关阅读:
    品Spring:实现bean定义时采用的“先进生产力”
    品Spring:bean定义上梁山
    品Spring:帝国的基石
    【面试】我是如何在面试别人Redis相关知识时“软怼”他的
    【面试】足够“忽悠”面试官的『Spring事务管理器』源码阅读梳理(建议珍藏)
    睡梦中被拉起来执行Spring事务
    Middleware的艺术
    突破自我,开源NetWorkSocket通讯组件
    化茧成蝶,开源NetWorkSocket通讯组件
    理解IEnumerator+IEnumerable这种接口思想
  • 原文地址:https://www.cnblogs.com/vcerror/p/4289142.html
Copyright © 2020-2023  润新知