西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
軟件
軟件
文章
搜索

首頁編程開發(fā)其它知識(shí) → 使用monodis工具分析pe文件結(jié)構(gòu)與msil反匯編

使用monodis工具分析pe文件結(jié)構(gòu)與msil反匯編

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:西西整理時(shí)間:2011/4/24 7:47:55字體大。A-A+

作者:西西點(diǎn)擊:172次評(píng)論:0次標(biāo)簽: PE

  • 類型:音頻處理大。1M語言:中文 評(píng)分:5.1
  • 標(biāo)簽:
立即下載

monodis是mono發(fā)行包里的一個(gè)工具,作用類似與ms的ildasm,可以把dotnet pe文件反編譯為msil文件(另外有個(gè)托管代碼的實(shí)現(xiàn)Mono.Cecil)。這個(gè)工具的實(shí)現(xiàn)很簡(jiǎn)單,就是根據(jù)PE文件的格式與規(guī)范去解析。選擇這個(gè)主題的原因有很多,首先PE文件作為進(jìn)行分析mono的基礎(chǔ),畢竟這里是metadata的來源;另外通過分析msil語言,可以為后續(xù)的VM執(zhí)行引擎做準(zhǔn)備,畢竟無論是jit還是aot,都是從msil到x86代碼的轉(zhuǎn)換,msil是第一步;當(dāng)然了解這個(gè)還可以為很多其他項(xiàng)目的分析做準(zhǔn)備,比如最近我脫離reflector很喜歡的一個(gè)工具ilspy,是把msil做ast分析轉(zhuǎn)換為C#。好了,簡(jiǎn)介到此為止,下面來具體分析下這個(gè)工具的源碼實(shí)現(xiàn)。

一.文件結(jié)構(gòu)及依賴
monodis在mono/dis目錄下,通過查看makefile可見mono依賴于libmono-$(API_VER).la,這部分功能主要和mono共用metadata解析部分的代碼,除了這些monodis自身的文件包括:

1.main.c:這個(gè)主要是提供參數(shù)解析及程序的入口,有參數(shù)可見monodis可以提供PE文件各部分信息的單獨(dú)輸出。

2.dump.c:這個(gè)是main.c里輸出各種table直接調(diào)用的方法,比如assembly,typeref,class,method,filed,event等等我們非常關(guān)心的一些東西。

3.get.c:主要提供一些stringfy的方法,即把MonoImage,MonoMethodSignature,MonoGenericContainer等Mono metadata相關(guān)的數(shù)據(jù)結(jié)構(gòu)表示為disassemble后的字符串。

4.dis-cil.c:這個(gè)可能是我們最關(guān)心的方法了,因?yàn)樗淖饔镁褪前袽onoMethodHeader輸出為msil代碼。

5.util.c:這個(gè)文件提供了四個(gè)工具方法:map和flags是查表方法,根據(jù)傳入的32位值得到相應(yīng)的字符串標(biāo)記,還有hex_dump和datadump,這兩個(gè)是把data直接輸出的。

 二.反編譯的過程

1.加載file,解釋為assembly:

這一塊功能主要是由libmono實(shí)現(xiàn)的,具體代碼在image.c/do_mono_image_load函數(shù)中, 這一塊是和mono共用的,我們這里所關(guān)心的就是load_metadata所調(diào)用的load_tables方法,因?yàn)镸onoImage->tables是后續(xù)所有解析的來源,具體這一塊的實(shí)現(xiàn)這里不詳細(xì)的講,因?yàn)檫@個(gè)不是本文的重點(diǎn)。

2.由上一步的工作得到了monodis反編譯過程中需要的MonoImage結(jié)構(gòu)體數(shù)據(jù),下面就進(jìn)行了如下一系列的工作:

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

 由函數(shù)名即可得知,這是處理各個(gè)table呢。由于其中大部分工作都類似,所有這里只選擇分析其中最具代表性的dis_directive_assemblyref方法和最重要的distypes這個(gè)方法。 

2.1:下面來著重講一下這個(gè)通用的工作流程,也就是以上方法通用的解析步驟,以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取得相應(yīng)的table得到MonoTableInfo,具體這個(gè)數(shù)據(jù)是從哪里來的,是第一步所做的工作,得到tableinfo之后,會(huì)對(duì)其進(jìn)行遍歷,然后用mono_metadata_decode_row解析到定義過的col變量里。這個(gè)col的大小及每個(gè)字段的定義都在row-indexes.h定義的各種枚舉里面,至于為什么以及每個(gè)字段的含義,請(qǐng)直接參考PE文件格式,另外有本書寫的也非常好《加密與解密》。取得col數(shù)據(jù)之后,就是把這些數(shù)據(jù)根據(jù)msil的文件格式要求輸出,如上例以及我測(cè)試用的一個(gè)PE文件輸出為:

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

 其他的一些table輸出方式與上面一樣,只是格式不同而已。

2.2:現(xiàn)在來分析其中最重要的也是我們最關(guān)心的distype方法。這里其實(shí)有幾個(gè)重要的部分,依次進(jìn)行分析:

(1).輸出type的基本信息,包括name,namespace以及type相關(guān)的attribute,這部分邏輯是通用的,和dis_directive_assemblyref一樣的方式。

(2).輸出field,method,property,event等type的成員。分別對(duì)應(yīng)dis_field_list,dis_method_list,dis_property_list,dis_event_list方法。這里只分析其中典型的dis_method_list方法其中的dis_code方法。

首先由mono_image_rva_map獲取一個(gè)方法的偏移量指針,然后再由mono_metadata_parse_mh_full得到這個(gè)方法的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];
};

如果看著這個(gè)結(jié)構(gòu)體特別親切,那就對(duì)了。這個(gè)就是msil中的method的一個(gè)抽象,比如locals,exception clause,code等。

其實(shí)對(duì)_MonoMethodHeader的輸出,就是對(duì)這個(gè)結(jié)構(gòu)的一個(gè)stringfy。當(dāng)然由code到msil還需要一個(gè)過程,那就是disassemble_cil方法所做的工作了。這個(gè)過程就是把code根據(jù)其對(duì)應(yīng)的opcode進(jìn)行翻譯,解釋為我們平常熟悉的msil了。這個(gè)翻譯的過程很簡(jiǎn)單,具體代碼在dis-cil.c中的disassemble_cil方法,其中需要注意的一點(diǎn)就是會(huì)根據(jù)每個(gè)opcode的argument不同而翻譯為不同的表示。


好了,這就是monodis的一些關(guān)鍵部分,當(dāng)然還有很多沒有解釋也沒有必要去解釋,因?yàn)槎际穷愃频牟僮,把這個(gè)過程搞清楚則對(duì)metadata,image,msil等一些基礎(chǔ)性的概念有更深的了解,也是進(jìn)一步分析mono的基礎(chǔ)。

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過難過
    • 5 囧
    • 3 圍觀圍觀
    • 2 無聊無聊

    熱門評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(0)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)