如何解決R6034錯誤,實如今WIN7以上版本經過LoadLibrary加載msvcr90.dll等DLL

爲何VC編譯的程序在不一樣系統上運行常常報錯?

    在XP(SP2 ?)之前,安裝VC運行時庫時,安裝包只會將各類DLL釋放到system32目錄並註冊相關信息到註冊表,這樣LoadLibrary時加載這些DLL也不會出錯,由於對於應用程序來講,當前只有一個對應的運行時庫被註冊到系統中,不存在多個不一樣版本的問題,但也很容易形成兼容性問題,如使用VS2008編譯的程序,在僅安裝了VS2005運行時庫的系統中可能會出現崩潰錯誤。shell

 

不一樣版本運行時庫帶來的衝突

    假如一個VC2005編譯的程序,一旦出現運行時庫不匹配而致使運行錯誤,咱們就須要安裝匹配的VC2005運行時庫到系統裏就能夠解決了.api

    可是若是問題那麼容易解決就行了,不過對於這種問題如何解決,痛苦的也是微軟的碼農。安全

    在上面VC2005程序問題的基礎上,再擴展一下,假如同時又有一個VC2008編譯的程序運行出現錯誤,這樣咱們又得安裝VC2008的運行時庫了,新的運行時庫又會覆蓋掉System32裏的同名DLL,那麼問題來了,原來的VC2005程序又沒法運行了,崩潰!不可能每運行一次就安裝一次運行時庫把?ide

    因此,微軟在新的補丁中提供了一個叫Side-By-Side的模塊,也就是簡稱SXS,該模塊最初應該不是爲了解決以上問題的,只是順帶而已,SXS應該是爲了解決混亂的.NET Framework而設計的。可是無論怎樣,有了SXS,就能夠在系統中同時安裝各類不一樣版本的運行時庫也不會有衝突了。函數

    既然系統中安裝了不一樣版本的運行時庫,那麼程序怎麼知道本身要使用的究竟是VC2005仍是VC2008的運行庫呢?工具

 

微軟如何設計SXS來解決不一樣運行環境所帶來的兼容性問題的?

在XP sp3,以及Vista, WIN7,WIN8,WIN10 後,爲了在系統中同時提供多種CPU平臺和不一樣VS版本的運行時庫來兼容不一樣的應用軟件,微軟作了大概下面3個改進,開發工具

1)PE文件自帶依賴庫簽名信息ui

2)在C:\Windows\WinSxS中存放各類不一樣版本的運行時庫spa

3)有了上面的2個改進,剩下的無非就是系統運行EXE文件或者加載DLL時,如何根據依賴庫簽名信息來正確加載相應的運行時庫了。設計

以上就是系統的Side-By-Side(SXS)組件設計時所要實現的大致目標和功能了。

 

程序開發中如何讓SXS的識別不一樣版本依賴庫?

      在VS開發時,可在工具中設置manifest來指定當前程序的依賴庫簽名信息(運行時庫名稱,版本,如win32 x86 版本號 等等),manifest能夠嵌入到資源中,也能夠放在本地的xml格式的文本文件,目前最標準的也是VS開發工具默認作法,就是將運行時庫經過導入表靜態連接到PE文件(EXE或DLL),同時將manifest嵌入到PE文件的資源裏,資源號是RT_MANIFEST(24),再交給系統的PE加載器去自動加載對應的運行時庫。

      雖然本文是說MSVCR90.DLL,但原理上是能夠適用於各類各樣的運行時庫的。

 

Delphi開發時無法設置manifest資源,會出現什麼問題?

注意:RAD 10已經能夠設置manifest資源了

      系統在建立進程時,在NTDLL和內核中有部分API和代碼統稱爲PE加載器,在EXE使用LoadLibrary加載DLL時,內部也是一個相似的且專門映射DLL到進程的PE加載器,因此這裏說加載器僅僅是一個模塊概念,並非一個實際的EXE程序。

      在Delphi開發的EXE程序中,若是採用Loadlibrary去動態加載MSVCR90.DLL,在XP上大部分是正常的,在VISTA/WIN7以上可能就會報錯R6034,這個錯誤是MSVCR90.DLL中在入口函數的運行環境自檢中所報的錯誤,大致上是該DLL會判斷當前進程的內存環境中是否包含正確的manifest,沒有則報錯,實際中該錯誤可能並非真的由於錯誤版本的DLL報錯的,只是該DLL爲了安全起見而報錯,固然真正緣由是由於調用LoadLibrary的EXE程序沒有嵌入包含運行時庫依賴信息的manifest,因此加載器沒法經過當前進程的manifest判斷是否加載的DLL就是EXE所指望的正確的MSVCR90.DLL,最終報錯。

 

Delphi中如何解決無法正確加載Manifest資源所致使的問題?

      要解決該問題,就須要讓加載器可以獲取到正確的manifest信息,而manifest信息其實只是一個載體,實際上的使用對象是叫作ActiveContext,也就是說manifest最終會被ActiveContext加載並接管處理。而加載器也是經過ActiveContext來處理被解析映射過的manifest信息。

    整體上,就是隻要進程中存在正確的ActiveContext,加載器就可以取得正確的依賴庫路徑並加載。

    文章到這裏已經相對清晰了,要解決本文提到的問題,如今變成了如何建立包含manifest的ActiveContext,到這裏尚未解釋ActiveContext是什麼,有興趣的朋友能夠查找MSDN中關於「Activation Context」的信息。

    下面給出代碼展現如何使用LoadLibrary加載msvcr90.dll,先看一段正常但會報錯的代碼:

     

procedure LoadMSVCRT();
var
  sDllName: string;
begin
  sDllName := 'MSVCR90.DLL';
  hLib := LoadLibrary(PChar(sDllName));
end;

  上面這段代碼在WIN7或WIN10上一執行,一般狀況下就會彈出R6034的錯誤,而如下代碼則可避免該問題

procedure LoadMSVCRT();
var
  sDllName: string;
  hLib: HModule;
  aActCtx: TActCtx;
  hCtx: THandle;
  lpCookie: ULONG_PTR;
begin
  // 激活清單文件,可正確加載MSVCR90.DLL
  FillChar(aActCtx, SizeOf(TActCtx), 0);
  aActCtx.cbSize := SizeOf(TActCtx);
  aActCtx.lpSource := PChar(ExtractFilePath(ParamStr(0))+'MSVCRT.manifest');
  hCtx := Winapi.Windows.CreateActCtx(aActCtx);
  if hCtx<>INVALID_HANDLE_VALUE then
  begin
    Winapi.Windows.ActivateActCtx(hCtx, lpCookie);
  end;
  sDllName := 'MSVCR90.DLL';
  hLib := LoadLibrary(PChar(sDllName));
  if hCtx<>INVALID_HANDLE_VALUE then
  begin
    DeactivateActCtx(0, lpCookie);
    ReleaseActCtx(hCtx);
  end;
end;

  以上代碼所使用的manifest內容隨便找一下就有,具體以下(從某個VC程序的資源中拷貝出來的):

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

     注意其中的版本號,若是系統中沒有安裝對應的運行時庫,仍然會有可能報錯。這裏說可能會報錯,是由於系統的SXS會在找不到對應的依賴庫時,會自動嘗試匹配與manifest中接近的庫文件(如當PE文件中的IAT沒法被所有正常解析,則會嘗試其餘更新的依賴庫,直到成功或失敗)。

      

     本文提到的方法一樣適用於shellcode對PE文件加殼時須要面對的manifest處理問題,另外,manifest能夠在代碼中直接生成到內存,而ActiveContext除了上面以文件的加載方式外,還能夠直接在內存中加載內容,具體能夠查看MSDN中關於ActiveContext的使用方法。

     

注:

A . 關於WinSxs(即Windows Side-by-Side),更高級的操做可查找Sxs.dll相關API,號稱完美解決不一樣版本COM和DLL兼容性問題(最初應該是解決.NET不一樣framework版本的兼容性問題)

B. manifest是一個範圍較大的配置文件,可在之後爲某些新的功能增長更多配置項,而運行時庫依賴配置僅僅是其中一項

相關文章
相關標籤/搜索