首 页最新软件下载排行文章资讯投稿发布下载专题
维维下载站
您的位置:首页编程开发网络编程编程其它 → 利用monodis工具分析pe文件结构及msil反汇编

利用monodis工具分析pe文件结构及msil反汇编

来源:维维整理 发布时间:2011-4-24 9:42:00 人气:

利用monodis工具分析pe文件结构及msil反汇编分享,monodis是mono发行包里的一个工具,作用类似与ms的ildasm,可以把dotnet pe文件反编译为msil文件(另外有个托管代码的实现Mono.Cecil)。这个工具的实现很简单,就是根据PE文件的格式与规范去解析。选择这个主题有很多原因。首先,PE文件被用作单声道分析的基础。毕竟,它是元数据的来源。另外,通过分析msil语言,您可以为后续的VM执行引擎做好准备。毕竟,jit和aot都是从msil到x86代码转换,msil是第一步;当然了解这一点也可以为许多其他项目的分析做准备,比如我最近从反射镜中取出的工具就像ilspy一样,是将msil做的ast分析转换为C#。那么到目前为止,这里详细分析了这个工具的源代码实现。

一,文件结构和依赖性
Monodis在mono / dis目录下,看到makefile中可见的mono取决于libmono- $(API_VER).la,这部分主要和mono共享元数据分析部分代码,除了这些monodis自己的文件还包括:

1.main.c:这主要是提供参数分析和程序的录入。有些参数表明monodis可以在PE文件的每个部分提供单独的信息输出。

2.dump.c:这是main.c中的一个方法,用于输出各种类型的表调用,例如汇编,类型参考,类,方法,文件,事件等。我们非常关心某些事情。

3.get.c:主要提供了一些stringfy方法,即将MonoImage,MonoMethodSignature,MonoGenericContainer和其他Mono元数据相关的数据结构表示为反汇编字符串。

4.dis-cil.c:这可能是我们最关心的方法,因为它的作用是将MonoMethodHeader作为msil代码输出。

5.util.c:这个文件提供了四种工具方法:map和flags是表查找方法,根据传递的32位值和hex_dump和datadump获得相应的字符串标签,这两者都是数据直接输出。

 二.反编译的过程

1.加载file,解释为assembly:

该功能主要由libmono实现。具体代码位于image.c / do_mono_image_load函数中。这个是与mono分享的。这里我们关心的是由load_metadata调用的load_tables方法,因为MonoImage-> tables是后续解析的来源,这个块的具体实现在这里没有详细说明,因为这不是本文的重点。

2.从前一步的工作中,获得Monodis反编译过程所需的MonoImage结构数据。以下是一系列的工作:

dump_header_data (img);
dis_directive_assemblyref (img);
dis_directive_assembly (img);
dis_directive_file (img);
dis_directive_mresource (img);
dis_directive_module (img);
dis_directive_moduleref (img);
dis_exported_types (img);
dis_nt_header (img);
dis_mresource (img);
dis_types (img, 0);
dis_data (img);

从函数名可以知道,它是处理每个表。由于大部分工作都是相似的,所以这里选择分析最具代表性的dis_directive_assemblyref方法和最重要的distypes方法。

2.1:让我们关注这个通用工作流程,这是上述方法的常见解析步骤,以dis_directive_assemblyref为例:

static void
dis_directive_assemblyref (MonoImage *m)
{
    MonoTableInfo *t = &m->tables [MONO_TABLE_ASSEMBLYREF];
    guint32 cols [MONO_ASSEMBLYREF_SIZE];
    int i;
    
    if (t->base == NULL)
        return;

    for (i = 0; i < t->rows; i++){
        char *esc, *flags;

        mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);

        esc = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_NAME]));
        flags = assembly_flags (cols [MONO_ASSEMBLYREF_FLAGS]);
        
        fprintf (output,
             ".assembly extern %s%s\n"
             "{\n"
             "  .ver %d:%d:%d:%d\n",
             flags,
             esc,
             cols [MONO_ASSEMBLYREF_MAJOR_VERSION], cols [MONO_ASSEMBLYREF_MINOR_VERSION], 
             cols [MONO_ASSEMBLYREF_BUILD_NUMBER], cols [MONO_ASSEMBLYREF_REV_NUMBER]
            );
        dump_cattrs (m, MONO_TOKEN_ASSEMBLY_REF | (i + 1), "  ");
        if (cols [MONO_ASSEMBLYREF_CULTURE]){
            fprintf (output, "  .locale %s\n", mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_CULTURE]));
        }
        if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]){
            const char* b = mono_metadata_blob_heap (m, cols [MONO_ASSEMBLYREF_PUBLIC_KEY]);
            int len = mono_metadata_decode_blob_size (b, &b);
            char *dump = data_dump (b, len, "\t\t");
            fprintf (output, "  .publickeytoken =%s", dump);
            g_free (dump);
        }
        fprintf (output, "}\n");
        g_free (flags);
        g_free (esc);
    }

首先从m->tables取得相应的table得到MonoTableInfo,来自哪里的具体数据是第一步完成的工作。获取tableinfo后,它将遍历它,然后使用mono_metadata_decode_row解析为已定义的col变量。 col的大小和每个字段的定义在row-indexes.h中定义的各种枚举中。至于为什么和每个字段的含义,请直接参考PE文件格式。另外,这本书也很不错。加密和解密“。在获取col数据后,根据msil文件的文件格式输出数据。上面的例子和我测试的PE文件的输出是:

.assembly extern mscorlib
{
  .ver 2:0:0:0
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..

 其他一些表格输出方法与上述相同,但格式不同。

2.2:最重要的是分析我们现在最关心的分类方法。实际上有几个重要的部分,这些部分依次进行分析:

(1)。输出类型的基本信息,包括名称,名称空间和类型相关的属性。这部分逻辑是通用的,与dis_directive_assemblyref相同。

(2).输出field,method,property,event等type的成员。分别对应dis_field_list,dis_method_list,dis_property_list,dis_event_list方法。这里只分析其中典型的dis_method_list方法其中的dis_code方法。

首先由mono_image_rva_map获取一个方法的偏移量指针,然后再由mono_metadata_parse_mh_full得到这个方法的MonoMethodHeader。以下是其定义:

struct _MonoMethodHeader {
    const unsigned char  *code;
#ifdef MONO_SMALL_CONFIG
    guint16      code_size;
#else
    guint32      code_size;
#endif
    guint16      max_stack   : 15;
    unsigned int is_transient: 1; /* mono_metadata_free_mh () will actually free this header */
    unsigned int num_clauses : 15;
    /* if num_locals != 0, then the following apply: */
    unsigned int init_locals : 1;
    guint16      num_locals;
    MonoExceptionClause *clauses;
    MonoType    *locals [MONO_ZERO_LEN_ARRAY];
};

如果你以特别亲切的方式观察这个结构,你是对的。这是msil中的方法的抽象,如本地,异常子句和代码。

实际上,_MonoMethodHeader的输出是这个结构的一个stringfy。当然,从代码到msil仍然需要一个过程,也就是由disassemble_cil方法完成的工作。这个过程是根据其相应的操作码翻译代码,解释为我们通常熟悉的msil。这个翻译过程非常简单。具体代码是dis-cil.c中的disassemble_cil方法。需要注意的一点是,根据每个操作码的参数,它将被转换为不同的表示形式。

那么这是monodis的一个关键部分,当然这里面有很多无法解释的,不需要解释的,因为所有的都是类似的操作,理解这个过程是一个更深层的基础概念,比如元数据,图像和msil。理解也是进一步分析mono的基础。

相关下载
栏目导航
本类热门阅览