.NET Reflector

Inledning

Vad händer efter att vi har kompilerat vårt program till exe-filer och eventuella dll-filer? Går det att läsa koden i efterhand på något sätt?

Svaret är oftast JA. Vi skall i artikeln undersöka metoder för att läsa andras kod men även titta på metoder för att skydda vår egen kod.

Det kan vara mycket lärorikt att studera andras kod i vissa situationer. Om källkoden inte finns tillgänglig så går det ibland att disassemblera färdigbyggda delar till någon slags läslig källkod. Motiven kan vara många. Du kanske bara vill studera och lära dig från andra eller helt enkelt sno ideer och algoritmer. I vissa fall kan bristen på dokumentation tvinga dig till en djupdykning. Som ett exempel kan vi ta Microsoft Sharepoint 2007 där studering av källkod kan ge värdefulla tips på hur Sharepoint-ramverket fungerar.

Disassembler

Innan språk som C# och Java skrevs de flesta program i C, C++ eller t.o.m. Assembler. Kompileringen skapade ett program översatt till ren maskinkod som kunde köras utan att behöva ha några extra ramverk. Maskinkoden kan sedan översättas tillbaka till Assembler med en disassembler. Efter en disassemblering fås tusentals rader av ganska svårläst kod t.ex:

Exempel assembler

 ...
 MOV   AX,1234H
 MOV   BX,5678H
 PUSH  AX
 PUSH  BX
 POP   AX
 POP   BX
 ...

Koden är mycket svår att tolka utan lång träning. Det blir alltså mycket svårt att se vad programmet gör. Med språk som C# och Java så finns det helt andra förutsättningar för att kunna översätta tillbaka till källkod. Detta tack vare att kompileringen i C# (.NET) inte översätter direkt till maskinkod.

Begreppet "disassemble" betyder numera oftast en övergång från kompilerad kod tillbaka till källkod. Alltså inte nödvändigtvis tillbaka till Assembler. Slut på historielektionen.

Lutz Roeder’s .NET Reflector

bild

Lutz Roeder skrev programmet .NET Reflector för flera år sedan. Reflector disassemblerar .NET kod och presenterar resultatet på ett mycket lättläsligt sätt. I flera fall blir man överraskad av hur bra det egentligen fungerar... vilket vi snart skall se.

Tyvärr har Lutz överlåtit utveckligen av .NET Reflector till företaget Red Gate Software september 2008. Lyckligtvis erbjuder Red Gate Software fortfarande gratisversioner av .NET Reflector samt en Pro-versioner för dem som vill betala för extra features. Programmet laddas ned här:

I skrivande stund är det version 6.1.0.11 som gäller med stöd för .NET 4.0. Programmet levereras som en enkel zip-fil. Denna zip-fil måste packas upp i en katalog någonstans på datorn innan den körs. Förslagsvis i katalogen C:\Program\Reflector samt att du skapar en genväg till filen Reflector.exe på startmenyn.

Första gången du startar programmet så får du välja vilka assemblies du skall ladda genom att välja .NET version. Har du Mono installerat så kan du välja bland dessa också. I bilden nedan har vi valt .NET 3.5.

bild

Trädstrukturen presenterar hela .NET ramverket. I rutan överst kan du välja hur koden skall presenteras, som C# eller kanske Visual Basic? eller t.o.m. IL (Intermediate language, den "råa" kod efter kompilering i .NET) som kan liknas vid Assembler. Navigeringen är enkel.

Saknar du någon dll eller något namnutrymme så kan du lätt lägga till dem manuellt (öppna-funktionen). GAC (Global Assembly Cache) hittar du under C:\Windows\assembly. Andra bra ställen att leta efter ramverks-delar är under C:\Windows\Mirosoft .NET. Du kan även dra och släppa dll-filer direkt till Reflector.

Vi kan väl ta oss en titt på vad klassen Button har för kod i sin OnClick metod. Navigera i trädet under System.Windows.Forms via System.Windows.Forms.dll till namnutrymmet System.Windows.Forms, klassen Button och till sist metoden OnClick. Dubbelklicka på metoden för att disassemblera och du får resultatet:

bild

Spännande? Nu är det fritt fram att utforska ramverket.

Övning 1

Vi ska nu göra ett enkelt testprogram. Programmet får bli av typen "Windows Application" och skall ha en Timer och en Label. Vi skall nämligen se hur bra Relector är på att överätta tillbaka vårt program. Vi skriver koden som:

ReflectTest

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ReflectTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            string format = "{0}:{1}:{2}";
            DateTime now = DateTime.Now;
            string hour = now.Hour.ToString().PadLeft(2, '0');
            string min = now.Minute.ToString().PadLeft(2, '0');
            string sec = now.Second.ToString().PadLeft(2, '0');

            label1.Text = string.Format(format, hour, min, sec);
        }
    }
}

Det blir ett litet program som visar klockan.

bild

Nu startar vi upp .NET Reflector och öppnar upp filen ReflectTest.exe (vi valde att kalla vårt projekt för ReflectTest) från \bin\debug katalogen. Enklast är att bara dra och släppa filen på Reflector. Vi bläddrar oss fram till metoden timer1_Tick och disassemblerar den. Resultatet blir som nedan...

bild

Inte illa va?

Obfuscation

Vi ska nu ta oss en titt på hur vi skyddar vår kod mot insyn. Begreppet obfuscation innebär att det kompilerade resultatet "trasslas" till på ett sådant sätt att en disassemblator har svårt att presentera något läsbart. När vi talar om .NET applikationer så är det IL-koden som byggs om. Det finns många tekniker som används för att göra programmen oläsbara. Vanligt är att byta namn på alla metoder, klasser och variabler (etc) till korta enkla namn som a, A, b, etc. Utöver detta så bryts programmet ned och skapas om den nya ologiska strukturer. Kod som tidigare var placerade i prydliga metoder blir nu placerade i konstruktorer i klasser som tidigare inte fanns. Hade jag en klass innan som hette Form1 så blir det kanske 10 nya klasser i det obfuscerade resultatet.

En nackdel med obfuscation är att prestandan i programmet försämras. Detta sker då strukturen och anropen ändrats totalt för att försvåra tolkning. Hur mycket sämre prestanda som ges är helt beroende på storlek och omständigheter.

Det finns en del olika produkter att välja på men vi har valt att titta på Dotfuscator. Dotfuscator följer med i Visual Studio 2008/2010 Professional som "Community Edition". Du hittar den under "Tools" - "Dotfuscator Community Edition". Annars kan du även ladda hem en evalueringsversion som kräver registrering. Givetvis finns det även en "Professional" version som kostar pengar. Vad som skiljer de åt kan du se i en jämförande tabell.

Efter diverse registrering och nedladdning så ser programmet ut såhär efter start:

bild
Klicka för förstoring.

Övning 2

Vi ska nu testa Dotfuscator på vårt program ReflectTest.exe.

Vi väljer "New project" i Dotfuscator och trycker på knappen Browse under fliken Input (vilket är startfliken).

Välj sedan att spara projektet med "File" - "Save project". Där du spara konfigurationen av projektet kommer senare en mapp kallad Dotfuscated att skapas. Vår valda "Input" kommer att sparas som obfuscated där.

Välj fliken Build och tryck på knappen Build. Se bild nedan.

bild
Klicka för förstoring.

Klart! I fliken Output kan du sedan se hur dina klasser/metoder har mappats om

Vi avslutar med att starta upp Reflector och se om vi kan hitta metoden timer1_tick.

bild

Metoden går att hitta då vårt program var extremt litet samt att vi visste vad vi skulle leta efter. Anropen vi gör bygger på .NET funktioner som inte är obfuscated så det går att hitta ledtrådar. Dock är det mycket svårt att begripa sammanhanget i det vi ser, dvs att koden vi tittar på är en eventhanterare till en timer.

Avslutning

Det finns obfuscators som gör ett sådant bra jobb att Reflector inte klarar av att disassemblera någonting. Annars är det viktigt att veta att i C# och Java så är "binärer" (kompilerat resultat) lika med källkod. "Professional" varianten av Dotfuscator gör ett mycket bättre jobb än gratisvarianten.

En annan sak värd att nämna i sammanhanget är att inte alla metoder/klasser behöver använda obfuscation. Det går att markera valda klasser eller metoder med attribut som tolkas av t.ex. Dotfuscator som då talar om vilka delar som skall använda obfuscation.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *

Scroll to top