ひっそりと生きるプログラマのブログ

日頃気になった事なりを書き留めるブログです。関心ごとは多くもう少し更新頻度を上げたいところです。

【dnlib】DLL(.NET assembly/module) からちょっとした情報を抽出したい(1)

ソースを解析する際に、人力でコードを追う方法以外に、
DLL のメタ情報から欲しい情報を抽出出来ないかと調べた所、
↓のような素敵なライブラリが合ったので試しに使ってみました。
github.com
www.nuget.org

まずは、nuget にて dnlib をインストール。
ライブラリ自体は dll への read/write 両方とも出来るようですが、
一先ず、色々と抽出したいので read に絞って実験。

private static IEnumerable<Tuple<MethodDef, IMethodDefOrRef>> GetUsedMethodsAndProperties(IEnumerable<string> moduleFiles)
{
    // func を再帰呼び出しする為に定義と実装を分離する。(ここは定義)
    Func<MethodDef, HashSet<MethodDef>, IEnumerable<Tuple<MethodDef, IMethodDefOrRef>>>? func = null;

    // ここは実装
    func = (method, hashSet) =>
    {
        var result = new List<Tuple<MethodDef, IMethodDefOrRef>>();
        // 無限ループになる可能性がある為、1度処理したメソッドは処理しない。
        if (method == null || hashSet.Contains(method)) return result;
        hashSet.Add(method);

        var methodBody = method.Body;
        if (methodBody == null) return result;

        foreach (var calledMethodOrProperty in methodBody.Instructions
            .Where(m => m.Operand is IMethodDefOrRef)
            .Select(m => (IMethodDefOrRef)m.Operand))
        {
            // 出力したい条件を↓に指定する。
            // ToString を呼んでいる処理を抽出
            if (calledMethodOrProperty.Name.Contains("ToString"))
                result.Add(Tuple.Create(method, calledMethodOrProperty));
            result.AddRange(func!(calledMethodOrProperty.ResolveMethodDef(), hashSet));
        }
        return result;
    };

    var h = new HashSet<MethodDef>();
    return moduleFiles
        .Select(m => ModuleDefMD.Load(m))
        .SelectMany(m => m.GetTypes())
        .SelectMany(m => m.Methods)
        .SelectMany(m => func(m, h));
}

GetUsedMethodsAndProperties の引数は解析したい DLL のフルパス。
こんな感じで、一先ず欲しい情報は抽出出来た。
これからは、 example や readme を参考にもう少し調べてみる。