はかせのラボ

私の頭の中を書いていく雑記ブログです

IL C#のルールを超える禁術 ~DynamicMethod~

あいさつ

どうも、はかせです。

前回の記事ではAssemblyBuilderを使って、
動的にdllとexeを生成してみました。
hakase0274.hatenablog.com

今回はDynamicMethodというものを使って
ILを書いてみようと思います。

DynamicMethodとは何ぞや?AssemblyBuilderとの違いは?

DynamicMethodってのはその名の通り
動的にメソッドを作るメソッドです。

「おいおいメソッド作るだけ?」
「AssemblyBuilderならクラスも作れたのに?」
「つまるとこAssemblyBuilderの機能縮小版?」

確かにそう見えます。
おまけにセーブもできないので
DnSpy等でデバッグもできません・・・
(かなりしんどい)

しかし、DynamicMethodでしかできないこともあります。
それはprivateな部分にアクセスできることです。

AssemblyBuilderは本当にVisualStudioなどで
C#を書いて実行したようなものができますが、
制限なども同じようにかかってしまいます。

その制限をすっ飛ばしてやりたい放題できるのが
DynamicMethodです。
(飛び道具大好きな私はめちゃ好きになりそう)

DynamicMethod使ってみる

さてDynamicMethodがどんなものかわかったとこで早速使います。
一発目からC#のルール無視のprivate変数弄りをやります*+。゚・(●`・д・´●)・゚。+*

まずは適当にprivate変数を持つクラスを作ります。

//とりあえずprivate変数持つクラス
public class Hoge
{
    private string name;
    //コンストラクタで初期値受け取っとく
    public Hoge(string name)
    {
        this.name = name;
    }
    //確認用
    public string GetName()
    {
        return name;
    }
}

Hogeクラスのnameを弄ってみます。

static void CreateMethod()
{
    //変数にhogeを格納
    var hoge = new Hoge("hoge");
    //初期値を確認
    Console.WriteLine(hoge.GetName());

    //メソッド名、戻り値、引数、その他オプション
    //Moduleは作ったメソッドが紐づけられる型
    //SkipVisibilityはアクセス範囲のチェックを飛ばすかのフラグ(多分)
    var dynamicMethod = new DynamicMethod("SetName", null, new[] { typeof(Hoge), typeof(string) }, m: typeof(Hoge).Module, skipVisibility: true);

    //IL手書きはいつもどおり
    var il = dynamicMethod.GetILGenerator();
    //インスタンスメソッドじゃないからarg0から引数
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Stfld, typeof(Hoge).GetField("name", BindingFlags.NonPublic | BindingFlags.Instance));
    il.Emit(OpCodes.Ret);

    //最後はやっぱりCreate
    var setName = (Action<Hoge, string>)dynamicMethod.CreateDelegate(typeof(Action<Hoge, string>));

    //private変数のnameを書き換えてみる
    setName(hoge, "Fuga");
    //結果を確認
    Console.WriteLine(hoge.GetName());
}

流れはやっぱりAssemblyBuilderと同じで、
定義→IL手書き→実体化です。保存はできません。

変わったとこはこの辺

//メソッド名、戻り値、引数、その他オプション
//Moduleは作ったメソッドが紐づけられる型
//SkipVisibilityはアクセス範囲のチェックを飛ばすかのフラグ(多分)
var dynamicMethod = new DynamicMethod("SetName", null, new[] { typeof(Hoge), typeof(string) }, m: typeof(Hoge).Module, skipVisibility: true);

基本的にAssemblyBuilderのDefineMethodと同じなんですが、
ModuleSkipVisibilityというオプションが追加されています。

Moduleはどのクラスに紐づくかということです。
メソッド単体で存在はできませんからね。

SkipVisibilityはアクセス範囲のチェックを飛ばすかというフラグ(多分)です。
こいつおかげでprivateな部分にも容赦なく入り込んでいけるっぽいです。

ModuleとSkipVisibilityはとりあえず設定しとけってneuecc神が言ってました。

さて本当にprivate変数を弄れるのかな・・・
f:id:hakase0274:20190913215922p:plain
やべえええええっ!!!private弄れちゃったよ!!
プライバシーなんてこの世に存在しなかった。

あとがき

今回はDynamicMethodでした。
AssemblyBuilderに比べてできることは少ないし、
デバッグもしにくいで良いとこないじゃんって思ってたんですが、
private変数を弄るっていう言語のルールさえ超越する力を持っていました。

もうなんでもありですねw

それでは今回はこの辺でノシ