Désassembler, éditer et recompiler une application DotNet

Salut à tous, aujourd’hui un petit article sur les bases du dotnet, le MSIL ou Microsoft Inter médiate Langage de son doux et chaleureux nom. Le MSIL est en fait le code obtenus suite à la compilation d’une application dotnet et qui sera ensuite exécuté dans le CLR. C’est grâce à cela que l’on peut facilement porter d’une architecture à une autre. En effet la compilation d’un application dotnet ne produit pas directement du code binaire directement executable mais un code intermédiaire appel (MS)IL interpreté ensuite par la CLR. Ce pseudo langage peut facilement être lu et éditeé. Pour vous montrer cela je vais prendre comme exemple une application dans lequel nous allons rajouter une méthode qui affichera « Salut » dans la console. Nous utiliserons ILDasm pour dumper et ILAsm pour compiler.

Le premier se trouve dans le windows SDK et, est, normalement fournit avec Visual Studio (sinon suffit d’installer le windows sdk) le second est fournis avec le framework dotnet qui se trouve dans %WINDIR%/Microsoft.Net/Framework/. Pour moi ce sera : %WINDIR%/Microsoft.Net/Framework64/v4.0.30319/ilasm.exe et le premier C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\ILdasm.exe ^^
Le premier outil dispose d’un ihm mais le second non. (Il s’agit d’un compiler donc l’ihm serait un peu superflue).
Notre application console contient ce code :

using System;

namespace DemoDissamble
{
    class Program
    {
        static void Main(string[] args)
        {
            Print1();
            Console.ReadKey();
        }

        static void Print1()
        {
            Console.WriteLine("Je vous dis");
        }

        static void Print2()
        {
            Console.WriteLine("salut");
        }
    }
}

On a donc deux fonctions, mais print2 n’est pas appelé. On va régler ça.
On lance donc ILDasm.exe, on va dans Fichier->Ouvrir et on ouvre notre exe.
On devrait obtenir le résultat suivant :


On voit donc notre namespace qui contient une classe avec notre méthode main, print1 et print2. Pour le moment, on est encore en read-only. On va donc dumper en allant dans Fichier->Dump
On a toute une série de choses que l’on peut exporter, certaines très intéressantes, d’autres moins. Mais ici nous allons juste exporter le code IL, je laisse à votre discrétion l’exploration des meta-inf et cie 😉


Maintenant, j’ouvre le fichier sous visual studio (ne cherchez pas une coloration syntaxique native 😉 ), voici son contenu :

//  Microsoft (R) .NET Framework IL Disassembler.  Version 3.5.30729.1
//  Copyright (c) Microsoft Corporation. Tous droits r�serv�s.

// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly DemoDissamble
{
  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0D 44 65 6D 6F 44 69 73 73 61 6D 62 6C 65   // ...DemoDissamble
                                                                                              00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 09 4D 69 63 72 6F 73 6F 66 74 00 00 )       // ...Microsoft..
  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0D 44 65 6D 6F 44 69 73 73 61 6D 62 6C 65   // ...DemoDissamble
                                                                                                00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 1B 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright ..
                                                                                                  4D 69 63 72 6F 73 6F 66 74 20 32 30 31 31 00 00 ) // Microsoft 2011..
  .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 31 30 66 62 37 30 65 65 2D 63 34 37 30   // ..$10fb70ee-c470
                                                                                                  2D 34 66 63 62 2D 61 38 37 37 2D 35 65 30 33 66   // -4fcb-a877-5e03f
                                                                                                  36 31 38 37 39 32 34 00 00 )                      // 6187924..
  .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
  .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 29 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ..).NETFramework
                                                                                                        2C 56 65 72 73 69 6F 6E 3D 76 34 2E 30 2C 50 72   // ,Version=v4.0,Pr
                                                                                                        6F 66 69 6C 65 3D 43 6C 69 65 6E 74 01 00 54 0E   // ofile=Client..T.
                                                                                                        14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61   // .FrameworkDispla
                                                                                                        79 4E 61 6D 65 1F 2E 4E 45 54 20 46 72 61 6D 65   // yName..NET Frame
                                                                                                        77 6F 72 6B 20 34 20 43 6C 69 65 6E 74 20 50 72   // work 4 Client Pr
                                                                                                        6F 66 69 6C 65 )                                  // ofile

  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )

  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module DemoDissamble.exe
// MVID: {7CD7C5F3-4B77-4150-8FC1-D694703BE62D}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000003    //  ILONLY 32BITREQUIRED
// Image base: 0x00970000

// =============== CLASS MEMBERS DECLARATION ===================

.class private auto ansi beforefieldinit DemoDissamble.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       12 (0xc)
    .maxstack  8
    IL_0000:  call       void DemoDissamble.Program::Print1()
    IL_0005:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
    IL_000a:  pop
    IL_000b:  ret
  } // end of method Program::Main

  .method private hidebysig static void  Print1() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldstr      "Je vous dis"
    IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ret
  } // end of method Program::Print1

  .method private hidebysig static void  Print2() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldstr      "salut"
    IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ret
  } // end of method Program::Print2

  .method public hidebysig specialname rtspecialname
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class DemoDissamble.Program

// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// AVERTISSEMENT : le fichier de ressources Win32 C:\dump.res a été créé

Dans mon cas il n’est pas très long, 100 lignes.
Notre main va de 58 à 67 suivi de Print1 de 69 à 76 et Print2 de 78 à 85.
Regardons un peu ce qui se fait, pour la suite j’utiliserai les offset qui prennent la forme IL_ pour une question de clarté par rapport aux numéros de lignes.
Dans main, IL_0000 et IL_0005 sont deux call. On comprend aisément qu’il s’agit d’appels de fonctions, ensuite on pop le top la stack et on return. En langage plus « haut niveau », on appelle deux méthodes et on return.
Ensuite nos deux méthodes Print sont sensiblement les mêmes. On crée une référence à un objet string sur le top de la stack, on y stock la valeur entre parenthèse et on appelle Writeline qui se chargera d’afficher dans la console la string sur le top de la stack. Ensuite on return. En langage plus « haut niveau », on crée une variable, que l’on passe en argument à WriteLine et on return. (Rappel de base, ce n’est pas parce que c’est void qu’il n’y a pas de return ;))
Avant de le modifier, on va le tester. Voici la ligne de commande a utiliser :
C:\Windows\Microsoft.NET\Framework64\v4.0.30319>ilasm.exe “C:\dump.il” /exe /out:”C:\run.exe” && C:\run.exe
Donc, en premier argument le chemin du dump à recompiler, ensuite le type d’output (exe ou dll) et enfin le chemin de l’output. Le && C:\run.exe me sert à exécuter le programme à condition que la compile ai réussi (c’est un & conditionnel). Ce qui devrait vous produire quelque chose comme cela :

Microsoft (R) .NET Framework IL Assembler.  Version 4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.
Assembling 'C:\dump.il'  to EXE --> 'C:\run.exe'
Source file is UTF-8

Assembled method DemoDissamble.Program::Main
Assembled method DemoDissamble.Program::Print1
Assembled method DemoDissamble.Program::Print2
Assembled method DemoDissamble.Program::.ctor
Creating PE file

Emitting classes:
Class 1:        DemoDissamble.Program

Emitting fields and methods:
Global
Class 1 Methods: 4;
Resolving local member refs: 1 -> 1 defs, 0 refs, 0 unresolved

Emitting events and properties:
Global
Class 1
Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Operation completed successfully
Je vous dis

Comme on le voit il nous manque un output, le salut. On va donc rajouter l’appel à cette fonction après le premier appel. Mais on a un petit « soucis ». En effet, chaque instruction a un offset, il faut donc recalculer les offset afin de pouvoir le compiler. Cela nous donnera :

.method private hidebysig static void  Main(string[] args) cil managed
{
	.entrypoint
	// Code size       12 (0xc)
	.maxstack  8
	IL_0000:  call       void DemoDissamble.Program::Print1()
	IL_0005:  call       void DemoDissamble.Program::Print2()
	IL_000a:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
	IL_000b:  pop
	IL_000c:  ret
} // end of method Program::Main

Et notre output :

Pour aller plus loin voici la liste des OpCodes : http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.pop.aspx
Et la référence de ILAsm : http://msdn.microsoft.com/fr-fr/library/496e4ekx%28v=vs.80%29.aspx

Inform@tiquement
Istace Emmanuel

One thought on “Désassembler, éditer et recompiler une application DotNet

  1. dotnet

    Merci pour ce post. Tout ce qui concerne la plateforme DotNET est très important pour mes développements. J’attends avec impatience tous vos posts sur ce sujet, merci.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s