dnlib使用
# 前言
看了老外的白皮书兴高采烈去挖洞了,然后发现人家挖洞是黑名单,我挖洞是白名单。需要自己找gadget,可是一个一个找构造函数、getter、setter是真的慢,干脆学一下 https://github.com/0xd4d/dnlib 看能不能整个.net的tabby出来算求。
接下来我将以下面这个类作为测试的程序集,讲解dnlib的使用。
using System;
using System.Collections.Generic;
namespace TestApp
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("asd");
}
}
abstract class A
{
public int AProp { get; set; }
public abstract void M();
}
interface ITest
{
void IMethod();
void IMethod(string s);
int IProp { get; set; }
}
class Test
{
public int MyProperty { get; set; }
private int MyValue { get; set; }
private int Count;
public delegate void Handler();
public event Handler Change;
public static string Hello(string s)
{
Console.WriteLine(s);
return "hello";
}
public Test() { Console.WriteLine("ctor"); }
public Test(string s) { Console.WriteLine("ctor string " + s); }
}
class Container
{
public class Nested
{
private Container parent;
public Nested()
{
}
public Nested(Container parent)
{
this.parent = parent;
}
}
}
enum E
{
None = 0,
}
class G : Comparer<string>
{
public override int Compare(string x, string y)
{
return x.CompareTo(y);
}
}
}
# dnlib的常用类
- AssemblyDef assembly程序集类
- ModuleDef 模块类
- TypeDef 类型定义 包含了事件、属性、字段等
- EventDef 事件类
- FieldDef 字段类
- PropertyDef 属性类
会发现,都是以XXDef命名,还有一些是以XXRef命名的类,表示引用类,这里暂不讨论。
XXDef为抽象类,真正实现类在XXDefMD,如ModuleDef
其实现类为ModuleDefMD,我们用也是用它。
# 载入程序集
首先要先引入命名空间
using dnlib.DotNet;
using dnlib.DotNet.Emit;
dnlib中提供了多种读取dotnet程序集的方式
ModuleContext modCtx = ModuleDef.CreateModuleContext();
ModuleDefMD module = ModuleDefMD.Load(@"C:\path\to\file.exe", modCtx);
Load有多个重载,提供从文件中、byte中、内存中或者是System.Reflection.Module示例读取程序集
比如以下代码就将从type void拿到System.Reflection.Module实例,从而加载mscorlib.dll
System.Reflection.Module reflectionModule = typeof(void).Module; // Get mscorlib.dll's module
ModuleContext modCtx = ModuleDef.CreateModuleContext();
ModuleDefMD module = ModuleDefMD.Load(reflectionModule, modCtx);
# 读取程序集
在dnlib的Example1 https://github.com/0xd4d/dnlib/blob/master/Examples/Example1.cs 中给了例子
using System;
using dnlib.DotNet;
namespace dnlib.Examples {
// This example will open mscorlib.dll and then print out all types
// in the assembly, including the number of methods, fields, properties
// and events each type has.
public class Example1 {
public static void Run() {
// Load mscorlib.dll
var filename = typeof(void).Module.FullyQualifiedName;
var mod = ModuleDefMD.Load(filename);
int totalNumTypes = 0;
// mod.Types only returns non-nested types.
// mod.GetTypes() returns all types, including nested types.
foreach (var type in mod.GetTypes()) {
totalNumTypes++;
Console.WriteLine();
Console.WriteLine("Type: {0}", type.FullName);
if (type.BaseType is not null)
Console.WriteLine(" Base type: {0}", type.BaseType.FullName);
Console.WriteLine(" Methods: {0}", type.Methods.Count);
Console.WriteLine(" Fields: {0}", type.Fields.Count);
Console.WriteLine(" Properties: {0}", type.Properties.Count);
Console.WriteLine(" Events: {0}", type.Events.Count);
Console.WriteLine(" Nested types: {0}", type.NestedTypes.Count);
if (type.Interfaces.Count > 0) {
Console.WriteLine(" Interfaces:");
foreach (var iface in type.Interfaces)
Console.WriteLine(" {0}", iface.Interface.FullName);
}
}
Console.WriteLine();
Console.WriteLine("Total number of types: {0}", totalNumTypes);
}
}
}
通过mod.GetTypes()拿到type定义后,我们可以获取type的method、field、prop、event等等属性,我们写一下代码解析下我们的TestApp.exe
ModuleContext modCtx = ModuleDef.CreateModuleContext();
ModuleDefMD module = ModuleDefMD.Load(@"E:\code\ConsoleApp2\TestApp\bin\Debug\TestApp.exe", modCtx);
AssemblyDef assembly = module.Assembly;
foreach (ModuleDef m in assembly.Modules)
{
Console.WriteLine("Module: " + m.FullName);
//遍历类
foreach (TypeDef type in m.Types)
{
Console.WriteLine("Class: " + type.Name);
//遍历方法
foreach (MethodDef method in type.Methods)
{
Console.WriteLine("Method: " + method.Name);
}
//遍历字段
foreach (FieldDef field in type.Fields)
{
Console.WriteLine("Field: " + field.Name);
}
}
}
Console.ReadKey();
输出
Module: TestApp.exe
Class: <Module>
Class: Program
Method: Main
Method: .ctor
Class: A
Method: get_AProp
Method: set_AProp
Method: M
Method: .ctor
Field: <AProp>k__BackingField
Class: ITest
Method: IMethod
Method: IMethod
Method: get_IProp
Method: set_IProp
Class: Test
Method: get_MyProperty
Method: set_MyProperty
Method: get_MyValue
Method: set_MyValue
Method: add_Change
Method: remove_Change
Method: Hello
Method: .ctor
Method: .ctor
Field: <MyProperty>k__BackingField
Field: <MyValue>k__BackingField
Field: Count
Field: Change
Class: Container
Method: .ctor
Class: E
Field: value__
Field: None
Class: G
Method: Compare
Method: .ctor
# 解析call关系
解析call关系其实是解析method body的opcode,比如静态方法调用的OpCodes.Call https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.calli?view=net-7.0
using System;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace tabby
{
internal class Program
{
static void Main(string[] args)
{
ModuleContext modCtx = ModuleDef.CreateModuleContext();
ModuleDefMD module = ModuleDefMD.Load(@"E:\code\ConsoleApp2\TestApp\bin\Debug\TestApp.exe", modCtx);
AssemblyDef assembly = module.Assembly;
foreach (ModuleDef m in assembly.Modules)
{
Console.WriteLine("Module: " + m.FullName);
//遍历类
foreach (TypeDef type in m.Types)
{
Console.WriteLine("Class: " + type.Name);
//遍历方法
foreach (MethodDef method in type.Methods)
{
Console.WriteLine("Method: " + method.Name);
if (method.HasBody && method.Body.HasInstructions)
{
var instructions = method.Body.Instructions;
foreach (var ins in instructions)
{
if (ins.OpCode == OpCodes.Call || ins.OpCode == OpCodes.Callvirt)
{
// 获取调用的方法
IMethod calledMethod = (IMethod)ins.Operand;
// 输出调用关系
Console.WriteLine("{0} {1} {2}", method.FullName, ins.OpCode, calledMethod.FullName);
}
}
}
}
//遍历字段
foreach (FieldDef field in type.Fields)
{
Console.WriteLine("Field: " + field.Name);
}
}
}
Console.ReadKey();
}
}
}
输出
Module: TestApp.exe
Class: <Module>
Class: Program
Method: Main
System.Void TestApp.Program::Main(System.String[]) call System.Void System.Console::WriteLine(System.String)
Method: .ctor
System.Void TestApp.Program::.ctor() call System.Void System.Object::.ctor()
Class: A
Method: get_AProp
Method: set_AProp
Method: M
Method: .ctor
System.Void TestApp.A::.ctor() call System.Void System.Object::.ctor()
Field: <AProp>k__BackingField
Class: ITest
Method: IMethod
Method: IMethod
Method: get_IProp
Method: set_IProp
Class: Test
Method: get_MyProperty
Method: set_MyProperty
Method: get_MyValue
Method: set_MyValue
Method: add_Change
System.Void TestApp.Test::add_Change(TestApp.Test/Handler) call System.Delegate System.Delegate::Combine(System.Delegate,System.Delegate)
System.Void TestApp.Test::add_Change(TestApp.Test/Handler) call TestApp.Test/Handler System.Threading.Interlocked::CompareExchange<TestApp.Test/Handler>(TestApp.Test/Handler&,TestApp.Test/Handler,TestApp.Test/Handler)
Method: remove_Change
System.Void TestApp.Test::remove_Change(TestApp.Test/Handler) call System.Delegate System.Delegate::Remove(System.Delegate,System.Delegate)
System.Void TestApp.Test::remove_Change(TestApp.Test/Handler) call TestApp.Test/Handler System.Threading.Interlocked::CompareExchange<TestApp.Test/Handler>(TestApp.Test/Handler&,TestApp.Test/Handler,TestApp.Test/Handler)
Method: Hello
System.String TestApp.Test::Hello(System.String) call System.Void System.Console::WriteLine(System.String)
Method: .ctor
System.Void TestApp.Test::.ctor() call System.Void System.Object::.ctor()
System.Void TestApp.Test::.ctor() call System.Void System.Console::WriteLine(System.String)
Method: .ctor
System.Void TestApp.Test::.ctor(System.String) call System.Void System.Object::.ctor()
System.Void TestApp.Test::.ctor(System.String) call System.String System.String::Concat(System.String,System.String)
System.Void TestApp.Test::.ctor(System.String) call System.Void System.Console::WriteLine(System.String)
Field: <MyProperty>k__BackingField
Field: <MyValue>k__BackingField
Field: Count
Field: Change
Class: Container
Method: .ctor
System.Void TestApp.Container::.ctor() call System.Void System.Object::.ctor()
Class: E
Field: value__
Field: None
Class: G
Method: Compare
System.Int32 TestApp.G::Compare(System.String,System.String) callvirt System.Int32 System.String::CompareTo(System.String)
Method: .ctor
System.Void TestApp.G::.ctor() call System.Void System.Collections.Generic.Comparer`1<System.String>::.ctor()
# 修改method body
这个案例我们直接看Example2.cs就行了 https://github.com/0xd4d/dnlib/blob/master/Examples/Example2.cs
用dnspy看看
确实多了这个类
# 总结
简单写了写怎么用,和javassist差的还是比较远,需要实现IL的翻译。
不过只是为了找到call关系的话,足够了,优化下加个多线程,加个neo4j就是下一个tabby for dotnet。
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。

如果你觉得这篇文章对你有所帮助,欢迎赞赏或关注微信公众号~


