2017-09-25 213 views
4

我嘗試使用下面的代碼來枚舉從64位應用的32位程序的模塊名稱:GetModuleFileNameEx對32位進程從Windows 64位進程10

if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL)) 
{ 
    for (i = 0; i < (cbNeeded/sizeof(HMODULE)); i++) 
    { 
     TCHAR szModName[MAX_PATH] = { 0 }; 

     if (GetModuleFileNameEx(hProcess, hMods[i], szModName, 
      sizeof(szModName)/sizeof(TCHAR))) 
     { 
      printf("module name is: %S", szModName); 
     } 
    } 
} 

代碼將按預期在Windows 7中,隨着部分結果是:

... 

C:\Windows\**SysWOW64**\ntdll.dll 

... 

在Windows 10上述代碼返回完整路徑,但與System32而不是SysWOW64。 e.g,

... 

C:\Windows\**System32**\ntdll.dll 

... 

尋找病因更深,我注意到,GetModuleFileNameEx讀取遠程進程PEB和LDR_TABLE_ENTRY,以及從Windows 10開始LDR_TABLE_ENTRY包含System32下,而不是Syswow64資料的完整路徑 - 也爲32位應用程序。

我也嘗試過使用GetMappedFileName,但它不是直接轉換並且高效地將路徑從dos路徑(\ device \ harddiskvolume)轉換爲標準(c:\)路徑。

我不知道是否有任何其他簡單的方法來提取完整​​的syswow64路徑。

+0

這可能是一個超跛腳的建議,但是你有沒有檢查過你的目標/檢查過程實際上是Windows 10中的一個32位程序? –

+0

哈哈,當然..... – AK87

+1

我檢查 - 這真是系統錯誤。當你使用'LIST_MODULES_ALL'時 - 系統真的像你設置'LIST_MODULES_32BIT'一樣工作。你需要調用'EnumProcessModulesEx'兩次​​:一次用'LIST_MODULES_64BIT'(你有4個64位模塊 - ntdll,wow64,wow64win,wow64cpu),一次用'LIST_MODULES_32BIT' – RbMm

回答

3

爲得到文件NT-路徑有效的Win32文件路徑 - 最簡單的方法 - 添加L"\\\\?\\globalroot"\\?\globalroot)前綴。這是因爲從CreateFileW\??\目錄一看,globalroot\??\符號鏈接,這讓作爲跳轉到NT命名空間的根。

例如 - \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll是nt絕對路徑。和\\?\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dllCreateFileW有效的win32路徑 - 這個API轉換衆所周知的前綴\\?\新臺幣前綴\??\並通過名稱\??\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll內核。當解析這個名字 - 後處理符號鏈接globalroot哪個指向命名空間的根 - 我們再次得到\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll - 正確的nt路徑。

所以如果我們需要在CreateFileW中使用有效的win32路徑 - 只需將此前綴追加到nt路徑。但是有些shell32 api不接受這種形式的路徑。在UI中看起來也不好看。如果我們想要得到DOS驅動器的信件形式路徑(這是有效的win32路徑的子集) - 我們可以使用IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH將設備名稱轉換爲驅動器號。此ioctl以輸入MOUNTDEV_NAME(在mountmgr.h中聲明)和輸出緩衝區爲MOUNTMGR_VOLUME_PATHS。在MOUNTDEV_NAME緩衝區必須是完全設備名稱,沒有文件路徑。所以我們需要break返回2個組件的路徑。例如在\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll

  • \Device\HarddiskVolume9 - 設備路徑
  • \Windows\SysWOW64\ntdll.dll - 文件系統路徑

正確這裏第一次打開文件,並調用GetFileInformationByHandleExFileNameInfo方式 - 我們得到了文件系統路徑輸出。有了這個,我們可以使用wcsstr作爲單獨的設備路徑。此外,如果我們打開的文件句柄 - 我們可以在通話GetFinalPathNameByHandleWVOLUME_NAME_DOS使用它。這個API做我們將要做的 - 查詢文件路徑,單獨的設備路徑和呼叫IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH。 +打開/關閉安裝管理器。

但通常的NT文件路徑從\Device\HarddiskVolumeX開始。這允許先嚐試快速的方式 - 避免打開文件並查詢它的路徑。

所以首先我們需要打開安裝管理器:

#include <mountmgr.h> 
HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, 
    0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0); 

然後我們可以運行下面的代碼:

void dumpModules(HANDLE hMountManager, HANDLE hProcess) 
{ 
    ULONG cb = 0, cbNeeded = 16; 

    volatile static UCHAR guz; 
    PVOID stack = alloca(guz); 
    HMODULE *hMods, hmod; 

__continue: 

    // cumulative allocate memory in stack, not need free it 
    cb = RtlPointerToOffset(hMods = (HMODULE*)alloca(cbNeeded - cb), stack); 

    if (EnumProcessModulesEx(hProcess, hMods, cb, &cbNeeded, LIST_MODULES_32BIT)) 
    { 
     if (cb < cbNeeded) 
     { 
      goto __continue; 
     } 

     if (cbNeeded /= sizeof(HMODULE)) 
     { 
      //i use hard coded size buffers, for reduce code and show main idea 
#define FILE_NAME_INFO_buffer_size FIELD_OFFSET(FILE_NAME_INFO, FileName[MAX_PATH]) 
#define MOUNTDEV_NAME_buffer_size FIELD_OFFSET(MOUNTDEV_NAME, Name[MAX_PATH]) 
#define MOUNTMGR_VOLUME_PATHS_buffer_size FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz[64]) 

      // + space for 0 at the end 
      PFILE_NAME_INFO pfni = (PFILE_NAME_INFO)alloca(FILE_NAME_INFO_buffer_size + sizeof(WCHAR)); 

      PMOUNTMGR_VOLUME_PATHS pmvp = (PMOUNTMGR_VOLUME_PATHS)alloca(MOUNTMGR_VOLUME_PATHS_buffer_size); 
      PMOUNTDEV_NAME pmdn = (PMOUNTDEV_NAME)alloca(MOUNTDEV_NAME_buffer_size); 

      static WCHAR globalroot[] = L"\\\\.\\globalroot"; 

      alloca(sizeof(globalroot)); 
      PWSTR win32Path = pmdn->Name - RTL_NUMBER_OF(globalroot) + 1; 

      memcpy(win32Path, globalroot, sizeof(globalroot)); 
      USHORT NameLength = pmdn->NameLength; 

      do 
      { 
       hmod = *hMods++; 

       if (GetMappedFileNameW(hProcess, hmod, pmdn->Name, MAX_PATH)) 
       { 
        DbgPrint("%p %S\n",hmod, pmdn->Name); 

        PWSTR c = 0; 

        static const WCHAR HarddiskVolume[] = L"\\Device\\HarddiskVolume"; 

        // fast way 
        if (!memcmp(pmdn->Name, HarddiskVolume, sizeof(HarddiskVolume) - sizeof(WCHAR))) 
        { 
         c = wcschr(pmdn->Name + RTL_NUMBER_OF(HarddiskVolume) - 1, '\\'); 
        } 
        // else - for demo 
        { 
         pmdn->NameLength = NameLength; 

         HANDLE hFile = CreateFile(win32Path, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0); 

         if (hFile != INVALID_HANDLE_VALUE) 
         { 
          //++ just for demo 
          WCHAR DosPath[MAX_PATH]; 
          if (GetFinalPathNameByHandleW(hFile, DosPath, RTL_NUMBER_OF(DosPath), VOLUME_NAME_DOS)) 
          { 
           DbgPrint("%S\n", DosPath); 
          } 
          RtlGetLastNtStatus(); 
          //-- just for demo 

          BOOL fOk = GetFileInformationByHandleEx(hFile, FileNameInfo, pfni, FILE_NAME_INFO_buffer_size); 

          CloseHandle(hFile); 

          if (fOk) 
          { 
           // FileName not 0 terminated 
           pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] = 0; 

           c = wcsstr(pmdn->Name, pfni->FileName); 
          } 
         } 

        } 

        if (c) 
        { 
         pmdn->NameLength = (USHORT)RtlPointerToOffset(pmdn->Name, c); 

         if (DeviceIoControl(hMountManager, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, 
          pmdn, MOUNTDEV_NAME_buffer_size, 
          pmvp, MOUNTMGR_VOLUME_PATHS_buffer_size, &cb, NULL)) 
         { 
          DbgPrint("%S%S\n", pmvp->MultiSz, c); 
         } 
        } 
       } 

      } while (--cbNeeded); 
     } 
    } 
} 

和演示輸出記事本:

0000000000170000 \Device\HarddiskVolume9\Windows\SysWOW64\notepad.exe 
\\?\C:\Windows\SysWOW64\notepad.exe 
C:\Windows\SysWOW64\notepad.exe 
0000000077A90000 \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll 
\\?\C:\Windows\SysWOW64\ntdll.dll 
0000000075460000 \Device\HarddiskVolume9\Windows\SysWOW64\kernel32.dll 
\\?\C:\Windows\SysWOW64\kernel32.dll 
C:\Windows\SysWOW64\kernel32.dll 
0000000074A30000 \Device\HarddiskVolume9\Windows\SysWOW64\KernelBase.dll 
\\?\C:\Windows\SysWOW64\KernelBase.dll 
C:\Windows\SysWOW64\KernelBase.dll 
00000000749B0000 \Device\HarddiskVolume9\Windows\SysWOW64\advapi32.dll 
\\?\C:\Windows\SysWOW64\advapi32.dll 
+0

你不斷地發佈帶有未定義或未初始化的神祕變量的代碼。什麼是'guz'? –

+0

@JonathanPotter - 忘記複製它的定義。 'volatile static UCHAR guz;'我用它來獲得堆棧指針。如果使用優化編譯器寫入'stack = alloca(0)',則放棄該指令。但是當我使用'alloca(guz)'而不是 - 這是工作正常,因爲我聲明'guz'爲'volatile'。當然我們可以使用'alloca(1)'。但是當'guz == 0'時我更喜歡'alloca(guz)' – RbMm

+1

這隻適用於工具幫助'TH32CS_SNAPMODULE32'快照。它調用'RtlQueryProcessModuleInformation',它使用'RtlGetNtSystemRoot'和'RtlReplaceSystemDirectoryInPath'等函數來重寫使用SysWOW64而不是System32的路徑。 – eryksun