• .Net 反射脱壳机代码核心代码详解


    本文主要对 《.Net 反射脱壳机核心源代码 》一文代码的原理和使用进行详细介绍。


    首先介绍一下代码主要流程:
    入口函数
    void DumpAssembly(Assembly ass,string path)
    枚举所有type,调用
    void DumpType(Type tp, BinaryWriter sw)
    枚举所有方法,调用
    void DumpMethod(MethodBase mb, BinaryWriter sw)
    {
    MethodBody mbd = mb.GetMethodBody();
    if (mbd == null)
    return;
    SetOffset(sw, mb.MetadataToken);

    WriteHeader(sw, mbd);

    WriteILCode(sw, mbd);

    WriteSEH(sw, mbd);

    }

    对于 DumpAssembly, DumpType 在很久以前的文章里面就已经介绍过了,这里就不再重复。本次主要介绍 DumpMethod 以及被 DumpMethod直接或间接调用的函数。

    在我之前的一篇文章《Net内存程序集通用脱壳机实现原理(二、反射以及重建方法头) 》 中介绍过,方法体是由三部分组成的, 方法头+IL字节码+SEH Table。

    再来看 DumpMethod 的代码,首先获取 MethodBody ,这里要注意不是所有方法都存在 MethodBody,像PInvoke 调用 api的方法就没有MethodBody。在元数据中 rva 等于 0 的方法,就是没有MethodBody的方法。在C#中我们可以直接判断返回值是否 null 来确定这一点。
    获取方法体后调用SetOffset , 这个函数用来设置当前方法体在文件中的偏移量。设置好偏移量后,我就可以直接把方法体的三部分写入文件了。

    SetOffset函数,通过元数据查找方法体的rva,然后
    int offsetra = (int)(offsetrva - 0x1000);
    计算出文件中的偏移量,注意这里的 0x1000 是硬编码,你可能需要调整这个值,或者根据pe的section自动计算这个值。

    WriteHeader 函数 中,首先调用 IsTiny 判断当前方法体是否 Tiny方法体,然后进行相应的方法头重构并写入文件。

    WriteILCode 这个函数很简单,就是直接把IL字节码写入文件,在这个函数的最后处理了4字节对齐问题,SEH TABLE起始位置需要要4 byte对齐的。

    最后调用 WriteSEH(sw, mbd) 重构SEH TABLE并写入文件,完成一个方体的dump工作。

    WriteSEH 函数中,首先判断当前是否包含异常处理结构,如果没有就直接返回了。
    然后 判断 SEH TABLE 是 Tiny的还是 Fat的。
    再分别重构相应格式的SEH TABLE。

    SEH TABLE 也是由两部分组成的,sehHeader + sehRows。
    其中 不管是tiny还是fat的seh,其sehHeader都占用 4 字节空间。
    按照cli标准重构seh比较简单,其中有一个麻烦事,就是 catch子句中,被catch的异常类在当前程序集中的token值。
    我们能够在C#中直接得到这个 异常类的 type 对象,但是通过 type的metatoken得的值是它在其被定义程序集中的token值,也就是它是一个 typedef值,如果它就是在当前程序集中定义的,那么可以直接使用。如果不是,就需要解析它的 typeref token值了。这个由函数 int GetTypeToken(Type tp) 来实现。

    注意 GetTypeToken 使用了 if (tp.Assembly == Assembly.GetEntryAssembly())
    来判断 是否同一程序集,因为这里假定了 当前dump的就是 EntryAssembly。你可能需要根据实际情况修改。

    查找 typeref值的原理,首先通过type对象获取 异常类的完整名称,然后通过元数据枚举所有引用的 类型,通过 名称比较。名字一样的就是了。


    使用:
    如何使用这个类来自己实现反射脱壳机?
    首先你需要修改 DumpAssembly 为 public的函数。
    然后实例化这个类,调用 DumpAssembly 函数即可。
    第一个参数你你要dump的 Assembly 对象,第二个参数是这个 Assembly dump后的存储路径。注意第二参数,这里没有实现pe dumper 的功能,你需要先用pe dumper把程序集dump到 磁盘,然后把这个路径 作为参数传入。

    尝试过直接用pe dump的人应该都清楚,直接从内存里面整个dump出来的程序集,方法体也是空的,然后这个类实现的功能,就是补充方法体的内容。

    另外前面提到的这个类需要改造的地方
    1,SetOffset函数。
    2,GetTypeToken函数。

    还有就是这个类中使用的 WrapperClass 实际上是 。net 元数据API的包装类,元数据api可以参考 msdn。


     

  • 相关阅读:
    如何让在JAVA中定义常量池
    java常量池概念
    Efficient Counter in Java
    看到关于JS线程的两篇文章
    Java Collection
    java学习书籍介绍--csdn上一位前辈介绍
    myeclipse快捷键
    数组
    错误处理和时间函数
    函数01
  • 原文地址:https://www.cnblogs.com/rick/p/805568.html
Copyright © 2020-2023  润新知