天天看點

.NET 6 使用 Obfuscar 進行代碼混淆

本文來安利大家 Obfuscar 這個好用的基于 MIT 協定開源的混淆工具。這是一個非常老牌的混淆工具,從 2014 年就對外分發,如今已有累計 495.5K 的 nuget 下載下傳量。而且此工具也在不斷持續疊代更新,完全支援 dotnet 6 版本,對 WPF 和 WinForms 等等的支援也是非常好,支援多個不同混淆方式和等級的配置,支援混淆之後生成符号檔案。本文将來告訴大家如何使用此混淆工具,以及此工具能達成的效果和此工具混淆的原理

開源

此工具是由 Lex Li 主導開發的,在 GitHub 上使用 MIT 最友好協定開源,開源位址是 https://github.com/obfuscar/obfuscar

使用方法

此工具的使用方式有多個不同的方式,比較推薦的是采用 dotnet tool 的方式進行使用。因為使用 dotnet tool 可以非常友善接入自己已有的 CI CD 上,而且也可以實作非常友善的更新邏輯

按照約定,使用 dotnet tool 的第一步是進行安裝,安裝的方法就是在指令行輸入以下代碼

dotnet tool install --global Obfuscar.GlobalTool
           

如此即可完成安裝。安裝完成之後的使用方法是調用 

obfuscar.console

 指令,傳入混淆配置 Obfuscar.xml 檔案即可,如下面指令行例子

obfuscar.console Obfuscar.xml
           

也就是說最關鍵的隻有兩點,第一個就是指令行的工作路徑,另一個就是混淆配置 Obfuscar.xml 檔案

假定我有一個項目是 HeenerholiCeleehano 項目,此項目我需要對他進行混淆。此項目輸出的是 HeenerholiCeleehano.dll 檔案,放在 

C:\lindexi\Code\lindexi\HeenerholiCeleehano\HeenerholiCeleehano\bin\Release\net6.0-windows\

 檔案夾下

那麼在混淆之前,請設定好指令行的工作路徑,如使用 

cd

 指令進入到輸出檔案夾,如以下指令

cd C\lindexi\Code\lindexi\HeenerholiCeleehano\HeenerholiCeleehano\bin\Release\net6.0-windows\
           

接下來是要在 

C:\lindexi\Code\lindexi\HeenerholiCeleehano\HeenerholiCeleehano\bin\Release\net6.0-windows\

 檔案夾下放一個混淆配置 Obfuscar.xml 檔案,檔案内容可以是如下

<?xml version='1.0'?>
<Obfuscator>
  <Var name="InPath" value="." />
  <Var name="OutPath" value=".\Obfuscar" />
  <Var name="KeepPublicApi" value="true" />
  <Var name="HidePrivateApi" value="true" />
  <Var name="HideStrings" value="false" />
  <Var name="UseUnicodeNames" value="true" />
  <Var name="ReuseNames" value="true" />
  <Var name="RenameFields" value="true" />
  <Var name="RegenerateDebugInfo" value="true" />
  <Module file="$(InPath)\HeenerholiCeleehano.dll" />

  <AssemblySearchPath path="C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.1\" />
  <AssemblySearchPath path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.1\" />
</Obfuscator>
           

以上配置檔案的含義如下,我一一帶上注釋辨別

<?xml version='1.0'?>
<Obfuscator>
  <!-- 輸入的工作路徑,采用如約定的 Windows 下的路徑表示法,如以下表示目前工作路徑 -->
  <!-- 推薦使用目前工作路徑,因為 DLL 的混淆過程,需要找到 DLL 的所有依賴。剛好目前工作路徑下,基本都能滿足條件 -->
  <Var name="InPath" value="." />
  <!-- 混淆之後的輸出路徑,如下面代碼,設定為目前工作路徑下的 Obfuscar 檔案夾 -->
  <!-- 混淆完成之後的新 DLL 将會存放在此檔案夾裡 -->
  <Var name="OutPath" value=".\Obfuscar" />
  <!-- 以下的都是細節的配置,配置如何進行混淆 -->

  <!-- 使用 KeepPublicApi 配置是否保持公開的 API 不進行混淆簽名,如公開的類型公開的方法等等,就不進行混淆簽名了 -->
  <!-- 文法的寫法就是 name 表示某個開關,而 value 表示值 -->
  <!-- 對于大部分的庫來說,設定公開的 API 不進行混淆是符合預期的 -->
  <Var name="KeepPublicApi" value="true" />
  <!-- 設定 HidePrivateApi 為 true 表示,對于私有的 API 進行隐藏,隐藏也就是混淆的意思 -->
  <!-- 可以通過後續的配置,設定混淆的方式,例如使用 ABC 字元替換,或者使用不可見的 Unicode 代替 -->
  <Var name="HidePrivateApi" value="true" />
  <!-- 設定 HideStrings 為 true 可以設定是否将使用的字元串進行二次編碼 -->
  <!-- 由于進行二次編碼,将會稍微傷一點點性能,二次編碼需要在運作的時候,調用 Encoding 進行轉換為字元串 -->
  <Var name="HideStrings" value="false" />
  <!-- 設定 UseUnicodeNames 為 true 表示使用不可見的 Unicode 字元代替原有的命名,通過此配置,可以讓反編譯看到的類和命名空間和成員等内容都是不可見的字元 -->
  <Var name="UseUnicodeNames" value="true" />
  <!-- 是否複用命名,設定為 true 的時候,将會複用命名,如在不同的類型裡面,對字段進行混淆,那麼不同的類型的字段可以是重名的 -->
  <!-- 設定為 false 的時候,全局将不會有重複的命名 -->
  <Var name="ReuseNames" value="true" />
  <!-- 配置是否需要重命名字段,預設配置了 HidePrivateApi 為 true 将都會打開重命名字段,是以這個配置的存在隻是用來配置為 false 表示不要重命名字段 -->
  <Var name="RenameFields" value="true" />
  <!-- 是否需要重新生成調試資訊,生成 PDB 符号檔案 -->
  <Var name="RegenerateDebugInfo" value="true" />

  <!-- 需要進行混淆的程式集,可以傳入很多個,如傳入一排排 -->
  <!-- <Module file="$(InPath)\Lib1.dll" /> -->
  <!-- <Module file="$(InPath)\Lib2.dll" /> -->
  <Module file="$(InPath)\HeenerholiCeleehano.dll" />

  <!-- 程式集的引用加載路徑,對于 dotnet 6 應用,特别是 WPF 或 WinForms 項目,是需要特别指定引用加載路徑的 -->
  <!-- 這裡有一個小的需要敲黑闆的知識點,應該讓 Microsoft.WindowsDesktop.App 放在 Microsoft.NETCore.App 之前 -->
  <!-- 對于部分項目,如果沒有找到如下順序,将會在混淆過程中,将某些程式集解析為舊版本,進而失敗 -->
  <AssemblySearchPath path="C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.1\" />
  <AssemblySearchPath path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.1\" />
</Obfuscator>
           

詳細的配置,還請參閱官方文檔

執行如上的指令行進行混淆,即可實作命名混淆效果

.NET 6 使用 Obfuscar 進行代碼混淆

使用 dotPeek 反編譯可以看到字段被混淆為如下内容

private readonly int \u00A0;
    private readonly short \u00A0;
           

這裡需要複習一下 IL 的知識,在 IL 裡面,是允許不同的字段重名的,隻要字段的類型不相同即可。因為使用的時候,是用類型名加字段名的方式使用的。這也能讓代碼更加混淆

以上就是我推薦的使用 dotnet tool 的方式

除了使用 dotnet tool 的方式之外,還可以自己将混淆過程嵌入到建構過程裡面,如此可以實作在開發階段對混淆的結果進行調試。也就是開發時調試的 DLL 就是混淆過後的

使用建構過程的方式需要編輯一下 csproj 項目檔案,先在項目檔案使用下面代碼安裝 Obfuscar 庫,代碼如下

<ItemGroup>
    <PackageReference Include="Obfuscar" Version="2.2.33">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
           

在需要寫建構過程下,預計實際需求都是千奇百怪的,很難有統一的方式,本文隻是提供一個簡單的例子

完成安裝之後,在項目上放一個混淆配置 Obfuscar.xml 檔案,設定此檔案如果較新則輸出

<ItemGroup>
    <None Update="Obfuscar.xml">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
           

最後編寫一個 Target 用來在建構完成之後調用指令行進行混淆,代碼如下

<Target Name="ObfuscarTask" AfterTargets="AfterBuild">
    <PropertyGroup>
      <ObfuscateCommand>$(Obfuscar) "Obfuscar.xml"</ObfuscateCommand>
    </PropertyGroup>
    <Exec WorkingDirectory="$(OutputPath)" Command="$(ObfuscateCommand)" />
  </Target>
           

如此即可實作在建構完成之後,自動調用

以上的代碼放在 github 和 gitee 歡迎通路

可以通過如下方式擷取本文的源代碼,先建立一個空檔案夾,接着使用指令行 cd 指令進入此空檔案夾,在指令行裡面輸入以下代碼,即可擷取到本文的代碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin b0b402abe4f32008d383d984bff677ac45cccde8
           

以上使用的是 gitee 的源,如果 gitee 不能通路,請替換為 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
           

擷取代碼之後,進入 HeenerholiCeleehano 檔案夾

以上代碼的混淆配置 Obfuscar.xml 檔案裡使用的程式集引用路徑寫的是 

C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.1\

 路徑,這是因為代碼是之前寫的,部落格是鴿子很久才寫的,還請大家自行更新

混淆原理

此混淆工具底層使用 Mono.Cecil 進行程式集的讀取和編織,使用 Mono.Cecil 可以讀取出程式集的資訊,從讀取到的資訊進行更改,更改也就是混淆的核心邏輯,更改完成之後,再通過 Mono.Cecil 生成新的程式集檔案,如此即可完成混淆

此工具在 GitHub 上完全開源,請看 https://github.com/obfuscar/obfuscar

更多細節邏輯還請自己去閱讀源代碼

更多閱讀

其他的混淆工具還有非常多,可以從 https://github.com/NotPrab/.NET-Obfuscator 找到更多的混淆工具清單

當然,有混淆工具,也就有反混淆工具。可以從 https://github.com/NotPrab/.NET-Deobfuscator 找到更多反混淆清單