zoukankan      html  css  js  c++  java
  • .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。


     

  • 相关阅读:
    Leetcode 349. Intersection of Two Arrays
    hdu 1016 Prime Ring Problem
    map 树木品种
    油田合并
    函数学习
    Leetcode 103. Binary Tree Zigzag Level Order Traversal
    Leetcode 102. Binary Tree Level Order Traversal
    Leetcode 101. Symmetric Tree
    poj 2524 Ubiquitous Religions(宗教信仰)
    pat 1009. 说反话 (20)
  • 原文地址:https://www.cnblogs.com/rick/p/805568.html
Copyright © 2011-2022 走看看