.Netの単一実行ファイルを難読化する

# はじめに
やってみたところ思いのほか簡単にはいかなかったためメモ

まず、.Net系のexeやdllは簡単にソースコードを復元することができます
元々ildasmが同梱されていますし、今やVisualStudioでF12で定義に飛ぶときに逆コンパイルしてコードを確認することもできるようになっています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/127a59f0-d514-f064-ef06-e65487ab32f6.png)
当然適当なライブラリであっても、あるいは商用のプログラムであってもILSpyやdnSpyで中身を解析したりデバッグしたりができてしまいます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/4e835116-6b20-8720-a979-8bd70662605c.png)
上の画像の実際のソースコードは[ここ](https://github.com/microsoft/referencesource/blob/master/System/compmod/system/collections/generic/sorteddictionary.cs)とか[ここ](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Collections/src/System/Collections/Generic/SortedDictionary.cs)です

まあ別に見られても困らないし・・・とか、IL見れたほうが都合がいいし・・・とか、エラー発生時の解析が困難になるからやらない、とかもありますが、時としてそうはいかない場合もあります

# やり方
.NET Frameworkならいける、とか単一実行ファイルではない場合はいける、とかいろいろありましたが最終的にはこちらがうまくいきました

[Confuser.MSBuild](https://www.nuget.org/packages/Confuser.MSBuild)
ConfuserにはいろいろとForkがあるようで、果たしてこれが信用できるのか？ というのはあると思いますが保証はしません

.Net7.0でWPFの新規プロジェクトを作成したら、Nugetを利用してインストールします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/fa482135-ddfe-5421-97f4-7c8f58cf8956.png)

次に設定ファイルを作成します
細かい設定がしたい場合、Confuserの他のForkでGUI付きのものから生成したほうがいいかもしれませんが、ここでは直接ファイルを手書きします

都合がいいのでConfuserをインストールしたプロジェクトと同じ場所に「{プロジェクト名}.crproj」というファイルを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/61b46b9d-537c-a540-38db-39f184f3f730.png)
ファイルの中を開いて、まず以下のように記述します
```xml:{プロジェクト名}.crproj
<project outputDir="$(OutDir)confused\" baseDir="$(MSBuildProjectDirectory)" xmlns="http://confuser.codeplex.com">
  <rule pattern="true" preset="minimum" inherit="false">
      <protection id="rename">
        <argument name="renXaml" value="false" />
      </protection>
   </rule>
</project>
```
2行目から先の内容は要件によって様々です
例えば4行目の内容のリファレンスは[こちら](https://github.com/mkaring/ConfuserEx/wiki/Name-Protection)です

次にプロジェクトファイルを開いて難読化設定を追加します
```xml:{プロジェクト名}.csproj
 <PropertyGroup>
    <Obfuscate>true</Obfuscate>
    <ConfuserReplaceOutput>true</ConfuserReplaceOutput>
    <ConfuserProject>$(MSBuildProjectDirectory)\$(MSBuildProjectName).crproj</ConfuserProject>
  </PropertyGroup>
```
特に2行目はtrueにしておくとアンチデバッグ機能により、デバッグできなくなってしまうので開発時はfalseにしておくといいです
このあたりの設定についてのリファレンスは[こちら](https://github.com/mkaring/ConfuserEx/wiki/MSBuild-Integration)です

# 単一実行ファイルの作成
プロジェクトを右クリックして発行を選びます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/2d7275b9-0550-5e73-d471-54ab61563915.png)
設定がなければダイアログが出てくるので任意の場所を選択します
今回はローカルフォルダに出力します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/d39837fa-3036-fa83-36a6-d7424b473308.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/d9b4d607-b946-b9f9-6cc8-8ef15f4dc89c.png)
デフォルト設定のまま完了を押します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/5db84f8d-57b0-417d-7022-92788c184230.png)

細かい設定を詰めます
ターゲットランタイムの鉛筆マークを押して設定を出します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/b0bdddc8-ec8d-9ed4-7710-f23b464cc70d.png)
ターゲットランタイムが、今回はWPFなのでWIndows専用であることから、「win-x64」にしておきます
その後、下に「ファイルの公開オプション」が出現するので「単一ファイルの作成」にチェックを入れます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/a472da54-38d6-bac6-ba66-9bc8843c1c4f.png)
設定を保存したら発行を押します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/67e77af2-8f3c-72ae-8c83-3fe19bcf4fef.png)
ビルドが走って指定の場所にファイルが作成されているので内容をILSpyで開いて確認します

例えば、これはまったくコードを変更せずに作成したWPFの逆コンパイルです
何やら謎のboolが作成されたりしていますね
「mainwindow.xaml」という文字は見えていますが、このあたり変に名前を変えられると動かなくなったりするのでこれは置いておきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/87176372-046d-011b-45ed-997200b0bc5d.png)

とりあえず基本は以上の内容で終了です

ただし、たまに難読化されていると正しく動かなくなったりする処理があるので、クラス単位での抑制の方法を書いて終わりとします
以下のようにObfuscationAttributeを設定すると、設定した単位で難読化の回避ができます
```csharp:
[ObfuscationAttribute(Exclude = false, Feature = "-rename")]
public partial class MainWindow : Window
```

# おわりに
個人的にはよほど自信がない限り、難読化をかけるのはお勧めしません
例えば、ただViewModelを作るようにして、そのコンストラクタで例外を出すようにしただけの修正を加えたとしましょう

起動すると何も起こらないんですが、仕方がないのでイベントビューワで見るとこうです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073248/540ddec0-a835-7032-94a5-eca657c8286a.png)
当然ソースコード上の記述しか知らないわけですから、まず逆コンパイルにかけてこれがどこで起こったのかを調べないといけません

実際のプロジェクトで何か問題が起こった時、難読化が牙を向くのは開発者のほうです