kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

タグアーカイブ: Assemble

とある鈍器と動的再生成- C# Advent Calender 2012


どーも、どーも、どーも。

 Kazuhiko Kikuchi

Kazuhiko Kikuchi @kazuk

C# er であり .NET erな人。(IL erという噂もある) ILDASMで殴る人

でございます。

まぁ、そんなわけで、ILDASMという鈍器を日夜振りかざして( ILDASM Hoge.dll /Out=Hoge.il して、Hoge.il を適当に書き換えて ILASM に食べさせてIL書き換えとか)いるのも結構疲れるわけでございまして、もうちょっと簡単にならないかな的な物をシコシコ作ってこさえましたので、Advent Calender という機会を使っての宣伝でございます。

どこにありますか?

https://github.com/kazuk/ProjectOosaki にソース一式あります。

実行環境は制限がありますか?

.NET Framework 4.5 が必要です。(現状コードは4.0でも動きますが、今後4.5で追加されたメソッドを使う予定なので動かなくなります。)

何ができるものですか?

ILメソッドの逆アセンブルと、動的アセンブリへの再アセンブルができます。

この逆アセンブルから再アセンブルの間にIL列にちょっかい出しをしたり、ILのトークン値の解決に割り込んでトークンの置き換え等ができます。また、再アセンブルをしないIL列の内容チェック等により静的解析で行われるようないくらかの処理ができます。

結果的にはオリジナルメソッドと全く同一ないしは意図的に加工した意図通りのILをもつメソッドが出来上がりますので、トークンの書き換えによってMock/Stubフレームワークでやるような DateTime.Now の呼び出しをほかのメソッドに振り向けるなどの細工ができます。(サンプルでもやってます

逆アセンブラの実行

https://github.com/kazuk/ProjectOosaki/blob/master/Oosaki.Msil.Samples/DisassemblerSamples.cs

逆アセンブルは MethodInfo から GetMethodBase したうえで GetILAsByteArray で取り出したIL命令を解析します。

逆アセンブル結果は内部的にはILバイト列そのものと、各命令のオフセット算出および分岐ターゲットラベル、例外フレームを重畳したint値の列からなります。(その他パラメータ等付随データがいくらかあります)

呼び出し命令の検出等のIL命令の検索等の静的解析はこの逆アセンブル結果に対する処理として実装することができます。

逆アセンブル結果のIL列の順序はオフセットの int 値の列によって定義されますので、必要であればIL列に命令を挿入する事もできますし、IL命令の順序を変える事もできます。

ILアセンブラの実行

https://github.com/kazuk/ProjectOosaki/blob/master/Oosaki.Msil.Samples/ReassembleAndTransformSamples.cs

ILアセンブラは逆アセンブル結果に Assemble メソッドとして実装されています。逆アセンブル結果はMethodInfo を返しますので、Invokeを呼び出せば実行する事ができます。

現状では内部実装に System.Reflection.Emit.ILGenerator を利用していますが、 System.dll の全メソッドの逆アセンブルと再アセンブルを試行した結果として意図通りのIL列を特に例外処理に関して正しく出力できない事が解っていますので完全に別実装に置き換え予定でいます。

ILの動的逆アセンブルと再アセンブルでできるようになる事

ILの動的逆アセンブルと再アセンブルでできるようになる事は結構多岐にわたります。

IL命令列に意図的な加工を施す事で、既存テストが Fail すればテストは正しいという判断を下す物はミューテーションテストと呼ばれます。この手法は結構以前にMSDNマガジンで紹介されているのですが、実際にやっているという人は見たことがありません。

ミューテーションの力: .NET Framework による単純なミューテーション テスト システムの作成

上記では ildasm / ilasm を利用してハーネスを駆動していますが、動的にオンメモリで実行可能であればIL列の書き換えや実行に要する時間を大幅に削減できます。

また、メソッド内の分岐グラフの節点にその節点が実行された事を通知するメソッド呼び出しを埋め込めみ実行する事でカバレッジを測定する事ができるでしょう。カバレッジ測定には CLR のプロファイリングAPIを利用するものもありますが、インストルメンテーションというIL解析から必要なマーカーポイントの打ち込みを行って実行アセンブリを再生成するプロセスによってこれを行うタイプのカバレッジツールと同様の手法となります。

非同期マルチスレッド処理の競合点の検出や競合のシミュレーションもやりようによっては可能です。競合点となる操作はフィールドへのアクセスですので、フィールドの参照操作の前後にロック確認用メソッドの呼び出しを挿入すれば、適切なロックをせずに実行されるフィールドアクセスが存在するかを実行確認する事が可能になります。(ラムダを利用すると、メソッドのローカル変数がクロージャークラスのフィールドに変換される事を知っていますか?そして、この変換されたフィールドへのアクセスがスレッド競合の危険に晒されていることを意識してメソッドを実装していますか?それってあなたのチームの全員が?)

というわけで

まだまだ作りかけの物です、Samples に実装されている内容は常に動くようにという方針でやっていますが再アセンブル処理のベースを完全に再実装しなおすつもり等タスクは結構残っています。んなわけで、開発者募集です。自分自身これで利益得てないので無報酬ですが。

github に晒してる物に pull request 等投げてもらえればマージするかもしれませんし、しないかもしれません(中身と気分に応じてやります宣言)、要望、不具合等 issue に書いてくれれば実現するかもしれませんししないかもしれません。

自分の物としてこちらを気にせず色々いじくりまわしたい人は fork して好き勝手にいじってくれていいです。NuGetにはもうちょっと完成度を上げてから流したいと思っていますのでforkの方から流すのはご遠慮ください。

pull request する際のコーディング規約等

あんまり無いです、緩いです。

  1. 他者の権利、ライセンスを侵さないソースでお願いします
  2. #region は使わないでください。
  3. public メソッドには Code Contracts による契約を入れてください
  4. public な要素については標準的な命名規則に従ってください
  5. 動作確認に使えるテストコードを含めてください
  6. 既存および追加されるテストの完走を確認ください(issue報告の為の不具合再現用テストコードは Fail している物をpull request してもらって構いません)
  7. 利用者に開放される機能の追加は Samples 配下にテストコードの形で機能の利用方法として参照できるコードを収めてください
  8. メソッド名、プロパティ名、パラメータ変数名等名前の類にはそこそこ気を使って下さい。スコープが5行未満のローカル変数については説明的である必要はありませんが、スコープがそれを超えるものについては何が格納されているのかを示す命名をしてください。

まとめではない事

私、菊池和彦は個人事業主として .NET Framework , ASP.NET , Windows , Windows Azure 上のシステムの設計開発、およびそれに関連するチューニング、コンサルティングやアドバイザリーサービスをやっております。お仕事のご用命等ありましたら電子メールにて kazuk.dll@kazuk.jp へお気軽にお願いします。決して安いとは言いませんが値段なりのお仕事をさせて頂きますのでよろしくお願いします。