monodis是mono发行包里的一个工具,作用类似与ms的ildasm,可以把dotnet pe文件反编译为msil文件(另外有个托管代码的实现Mono.Cecil)。这个工具的实现很简单,就是根据PE文件的格式与规范去解析。选择这个主题的原因有很多,首先PE文件作为进行分析mono的基础,毕竟这里是metadata的来源;另外通过分析msil语言,可以为后续的VM执行引擎做准备,毕竟无论是jit还是aot,都是从msil到x86代码的转换,msil是第一步;当然了解这个还可以为很多其他项目的分析做准备,比如最近我脱离reflector很喜欢的一个工具ilspy,是把msil做ast分析转换为C#。好了,简介到此为止,下面来具体分析下这个工具的源码实现。

一.文件结构及依赖

monodis在mono/dis目录下,通过查看makefile可见mono依赖于libmono-$(API_VER).la,这部分功能主要和mono共用metadata解析部分的代码,除了这些monodis自身的文件包括:
1.main.c:这个主要是提供参数解析及程序的入口,有参数可见monodis可以提供PE文件各部分信息的单独输出。
2.dump.c:这个是main.c里输出各种table直接调用的方法,比如assembly,typeref,class,method,filed,event等等我们非常关心的一些东西。
3.get.c:主要提供一些stringfy的方法,即把MonoImage,MonoMethodSignature,MonoGenericContainer等Mono metadata相关的数据结构表示为disassemble后的字符串。
4.dis-cil.c:这个可能是我们最关心的方法了,因为它的作用就是把MonoMethodHeader输出为msil代码。
5.util.c:这个文件提供了四个工具方法:map和flags是查表方法,根据传入的32位值得到相应的字符串标记,还有hex_dump和datadump,这两个是把data直接输出的。

 二.反编译的过程

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);

 由函数名即可得知,这是处理各个table呢。由于其中大部分工作都类似,所有这里只选择分析其中最具代表性的dis_directive_assemblyref方法和最重要的distypes这个方法。 

2.1:下面来着重讲一下这个通用的工作流程,也就是以上方法通用的解析步骤,以dis_directive_assemblyref为例:

static void
dis_directive_assemblyref (MonoImage 
*m)
{
    MonoTableInfo 
*= &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..

 其他的一些table输出方式与上面一样,只是格式不同而已。

2.2:现在来分析其中最重要的也是我们最关心的distype方法。这里其实有几个重要的部分,依次进行分析:

(1).输出type的基本信息,包括name,namespace以及type相关的attribute,这部分逻辑是通用的,和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中的method的一个抽象,比如locals,exception clause,code等。

其实对_MonoMethodHeader的输出,就是对这个结构的一个stringfy。当然由code到msil还需要一个过程,那就是disassemble_cil方法所做的工作了。这个过程就是把code根据其对应的opcode进行翻译,解释为我们平常熟悉的msil了。这个翻译的过程很简单,具体代码在dis-cil.c中的disassemble_cil方法,其中需要注意的一点就是会根据每个opcode的argument不同而翻译为不同的表示。


好了,这就是monodis的一些关键部分,当然还有很多没有解释也没有必要去解释,因为都是类似的操作,把这个过程搞清楚则对metadata,image,msil等一些基础性的概念有更深的了解,也是进一步分析mono的基础。

 

posted @ 2011-04-23 16:41 zffl 阅读(2373) 评论(0) 推荐(2)
摘要: 本文目的很简单,简单分析一下下面语句是如何被Mono Runtime执行的:public class Hello1{ public static void Main() { System.Console.WriteLine("Hello, World!"); }}由于自己也是刚接触Mono源码,以前一直停留于简单的应用之上,所以本文的分析不全面,只是为了能大致的了解Mono的执行流程,对基本的函数和流程有所了解,所以本文的风格为流水帐。一、先简单的看下Mono源码的目录中的几个重要文件夹:我这里使用的是不知道什么时候下载的某个历史版本2.6.7,目前我觉得重要的就3个文件夹 阅读全文
posted @ 2011-03-13 21:43 zffl 阅读(4787) 评论(8) 推荐(3)
摘要: 这是前两周在部门里做的nodejs分享,可能是由于第一次,没有分享的经验,所以在结束之后感觉讲的不太好。最直接的感受就是最后demo演示和社区讨论的部分比前面分析nodejs是如何实现的分析更受大家关注,这个顺序被我搞错了,开始就和大家讲require是怎么实现的,socket,file对象在javascript中是如何实现的等等这些抽象的主题,在分享之前真没有想到这些问题,总之是一次宝贵的经验。Nodejs部门分享View more presentations from zffl.独立博客地址(需FQ):http://i.zffl.us/?p=1 阅读全文
posted @ 2011-03-13 21:39 zffl 阅读(468) 评论(0) 推荐(0)
摘要: 我们已经在Part V产生了中间代码,并且我们想要把它转换成可执行代码,好让我们能够执行一个程序.但是我已经决定要先建立一个虚拟机,这样我们可以知道该如何处理产生可 执行代码. 虚拟机当然是一个脚本引擎中非常重要的组件.我们的代码将在它那里执行,所以它最好快一些.但是这里 我将不把焦点集中到速度上. Oh yeah:这部分结束后,你将完全免费的得到我那令人惊奇的堆栈模板(Amazing S... 阅读全文
posted @ 2010-05-03 17:55 zffl 阅读(588) 评论(0) 推荐(0)
摘要: 目录 [InAttribute] 和 [OutAttribute] 关键字 Out 和 Ref 以及通过引用传递 返回值 StringBuilder 和封送处理 复制和固定 内存所有权 反向 P/Invoke 和委托生存期 P/Invoke Interop Assistant 尝试一下 让我们面对现实吧。这个世界并不完美。几乎很少有公司在完全用托管代码开发程序,除此之外仍存在很多需要您处... 阅读全文
posted @ 2010-03-07 00:58 zffl 阅读(935) 评论(0) 推荐(0)
摘要: 摘要:所有伟大的架构师都掌握了在抽象的不同层次上概念化解决方案的技能。通过将解决方案组织到离散的层次,架构师可以专注于解决方案的单个方面而忽略所有剩余的复杂性。展示将抽象层次应用到 IT 解决方案的技术,并将其与其他工程学科相比较。本页内容将抽象层次应用到 IT 解决方案抽象层次:所有工程师的强大武器应用抽象层次时的核心原则将抽象层次应用到 IT 系统简单框架:四个抽象层次通过迭代发展层次重访抽象... 阅读全文
posted @ 2010-02-08 13:29 zffl 阅读(436) 评论(0) 推荐(0)
摘要: 转自:http://www.microsoft.com/china/MSDN/library/netFramework/netframework/NETMattersSep.mspx?mfr=true问 我一直在使用 .NET Framework 2.0 中的新 TransactionScope 类,我喜欢它所提供的模型。要启动一个事务,我可以在一个方法中使用 Transaction 创建一个 T... 阅读全文
posted @ 2010-02-08 13:23 zffl 阅读(429) 评论(0) 推荐(0)
摘要: 本文讨论:紧密耦合体系结构的错误之处测试和依赖关系灾难依赖关系反转依赖关系注入本文使用了以下技术:.NET Framework代码下载位置:DependencyInjection2008_03.exe(5408 KB)Browse the Code Online目录内部依赖关系问题依赖关系反转依赖关系注入容器成熟的 IoC 容器生存期管理自动绑定依赖关系轻松实现更改几乎所有人都认为追求松散耦合设计... 阅读全文
posted @ 2010-02-03 16:49 zffl 阅读(256) 评论(0) 推荐(0)
摘要: 随着电子商务的发展,越来越多的购物网站出现啦。由于电子购物网站的进入门槛低,因此在互联网上出现了纵多良莠不齐的购物网站。湖南本地购物网站做为一个地方性购物网站,其发展存在很大的制约,同时也存在一些优势。下面主要从湖南本地购物网站发展的劣势与发展方向来分析。湖南本地购物网站面临的制约:市场份额作为湖南电子商务发展的主力军,湖南本地购物网站发展都比较晚,在一些大型的电子商务购物网站成立后,采取模仿其模... 阅读全文
posted @ 2009-10-08 13:42 zffl 阅读(1070) 评论(0) 推荐(0)
摘要: http://msdn.microsoft.com/zh-cn/library/ms978723.aspx上下文您已经决定使用Model-View-Controller (MVC) 模式将动态 Web 应用程序的用户界面逻辑与业务逻辑分隔开来。您已经考察了Page Controller模式,但您的页面控制器类具有复杂的逻辑,并且是较深的继承层次结构的一部分,或者,您的应用程序是基于可配置的规则来动... 阅读全文
posted @ 2009-09-29 14:06 zffl 阅读(248) 评论(0) 推荐(0)
点击右上角即可分享
微信分享提示