一.文件结构及依赖
二.反编译的过程
1.加载file,解释为assembly:
这一块功能主要是由libmono实现的,具体代码在image.c/do_mono_image_load函数中, 这一块是和mono共用的,我们这里所关心的就是load_metadata所调用的load_tables方法,因为MonoImage->tables是后续所有解析的来源,具体这一块的实现这里不详细的讲,因为这个不是本文的重点。
2.由上一步的工作得到了monodis反编译过程中需要的MonoImage结构体数据,下面就进行了如下一系列的工作:
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为例:
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);
}
}
{
.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。以下是其定义:
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的基础。