Laden...

Code-Injection mit Cecil

Erstellt von tscherno vor 15 Jahren Letzter Beitrag vor 14 Jahren 21.214 Views
tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren
Code-Injection mit Cecil

Hallo,

hier ein Beispiel wie man den Code eines breits kompilierten Assemblies mit Cecil verändern und erweitern kann.

Vorraussetzungen:
Cecil-Libraries
Grundverständniss vom Aufbau eines .NET Assemblies
CIL-Grundlagen

Her unser "Opfer-Assembly" an dem wir die Veränderungen vornehmen:


using System;

namespace Victim
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.ReadLine();
        }
    }
}

Dieses Programm sollte als "Victim.exe" kompiliert werden und in den selben Ordner gelegt werden, wo die Exe unserers "Manipulators" liegt.
**
Übersicht zur Vorgehensweise:**

-Victim.exe laden
-Neue Methode void Test() in Victim.Program definieren
-Methode mit Code füllen
-Methode Test() "injezieren"
-Main() modifizieren damit Sie "Test()" aufruft.
-Geändertes Assembly auf die Platte schreiben.

Manipulator:


using System;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace AdvancedCodeInjection
{
    class Program
    {
        static void Main(string[] args)
        {
            //Assembly laden
            AssemblyDefinition asm= AssemblyFactory.GetAssembly("Victim.exe"); 
          
            /* Jetz definieren wir in der Klasse Program eine Methode "Test()" mit dem Rückgabetyp void  */

            //Zuerst legen wir den Rückgabe-Typ (hier void) unserer zukünftigen Methode fest
            TypeReference returntype = asm.MainModule.Import(typeof(void));
            
            //Anschließend definieren wir die Methodensignatur (hier static void Test())
            MethodDefinition testmethod = new MethodDefinition("Test", MethodAttributes.Private|MethodAttributes.Static, returntype);

            /* Als nächstes bestücken wir unsere neue Methode mit einem Console.WriteLine() */

            //Wir definieren eine CIL-Instruktion die einen String mit unserer Meldung auf den Stack legt...
            Instruction msg = testmethod.Body.CilWorker.Create(OpCodes.Ldstr, "Hello from Test()");

            //und importieren eine Methoden-Referenz auf Console.WriteLine(string), dazu wird
            //der im .NET Framework eingebaute System.Type verwendet
            MethodReference writeline = asm.MainModule.Import(typeof(Console).GetMethod("WriteLine",new Type[]{typeof(string)}));

            /* Nun generieren wir CIL-Code in userer Methode Test() */

            //Message-String auf den Stack legen
            testmethod.Body.CilWorker.Append(msg);
            //Console.WriteLine aufruf generieren
            testmethod.Body.CilWorker.InsertAfter (msg,testmethod.Body.CilWorker.Create (OpCodes.Call,writeline));

            //Return generieren
            testmethod.Body.CilWorker.Append (testmethod.Body.CilWorker.Create (OpCodes.Ret));

            //Nun "injezieren" wir die Methode "Test()" in die Klasse Victim.Program, welche sich im Hauptmodul befindet.
            asm.MainModule.Inject(testmethod, asm.MainModule.Types["Victim.Program"]);


            /* Jetzt wollen wir die Main-Methode noch modifizieren, damit Sie unsere Test-Methode aufruft */

            //Hier habe ich keine bessere Lösung gefunden um an die Methodenreferenz der Testmethode zu kommen
            //diese brauchen wir um einen Aufruf in Main zu generieren
            MethodReference testmethod_ref = null;
            foreach (MethodDefinition mdef in asm.MainModule.Types["Victim.Program"].Methods)
            {
                if (mdef.Name == "Test")
                {
                    testmethod_ref=asm.MainModule.Import(mdef);
                }
                
            }

            Instruction call_test = testmethod.Body.CilWorker.Create(OpCodes.Call, testmethod_ref);

            asm.EntryPoint.Body.CilWorker.InsertBefore (asm.EntryPoint.Body.Instructions[0],call_test);

            //Abschließend schreiben wir das ganze auf die Platte
            AssemblyFactory.SaveAssembly(asm, "patched.exe");
            Console.ReadLine();

        }
    }
}

Anschließend sieht unsere Victim.exe (im Reflector) dann so aus:


using System;

namespace Victim
{
    class Program
    {
        static void Main(string[] args)
        {
            Test();
            Console.WriteLine("Hello World!");
            Console.ReadLine();
        }

        private static void Test()
        {
            Console.WriteLine("Hello from Test()");


        }
    }
}

Gruss
tscherno

PS: Wenn Interesse besteht, kann ich das ganze zu einem grösseren Artikel ausbauen.

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo tscherno,

ich kenne nun Cecil nicht wirklich, schaut allerdings interessant aus.

Kann ich das denn nicht auch mit einem CodeDomProvider so realisieren (Wirklich eine Frage, ich weiß es wirklich nicht)? Dann würde ich zumindest das Mono so nicht brauchen.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren

Hallo norman_timo,

mit dem CodeDom kann man neue Assemblies erzeugen, aber nur sehr begrenzt bearbeiten, hauptsächlich nur zur Laufzeit via Reflection. Auserdem muss das Assembly in eine AppDomain geladen werden, was bei Cecil nicht nötig ist, da es direkt auf der Binärdatei arbeitet.

Hier ein Artikel dazu.

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

383 Beiträge seit 2006
vor 15 Jahren

hallo tscherno

wie sehen die typischen Anwendungsfälle aus? (in der Praxis)

gruss
wakestar

Gelöschter Account
vor 15 Jahren

wie sehen die typischen Anwendungsfälle aus? (in der Praxis)

hacks, cracks, cheats und einschleusen von viren/trojanern/malware in bestehenden und vermeintlich sicheren produkten.....
(auf jeden fall nichts legales.)

wie handled das teil signed assemblies?

3.825 Beiträge seit 2006
vor 15 Jahren

Nützt es was wenn ich die Programme mit einem Strong Name signiere ?

Wie kann man solche Manipulationen verhindern ?

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo JAck30lena,

(auf jeden fall ncihts legales.)

das sehe ich nun anders, sonst wäre der Thread auch schon geschlossen oder gar entfernt. Ich habe es mir zwar nicht im Detail angeguckt, aber das Ganze geht nach meiner Einschätzung in Richtung AOP. Und für AOP gibt es nun massenhaft sinnvolle und legale Gründe. Natürlich kann man jede Technologie zum Guten wie zum Bösen verwenden. Und sicher gibt es Technologien, die mehr Missbrauchspotential haben als anderen. Aber AOP hat nun soviele Vorzüge und sinnvolle Anwendungen, dass es unangemessen wäre, AOP wegen seines Missbrauchspotentials zu ächten.

herbivore

S
8.746 Beiträge seit 2005
vor 15 Jahren

Mit dieser Technik arbeitet z.B. der O/R-Mapper von Versant.

Gelöschter Account
vor 15 Jahren

ok aop ist ein legaler anwendungsfall, jedoch ist es mithilfe solcher libs sehr einfach ein paar zeilen bösen codes in beliebige .net assemblies zu bringen.

bei seigned assemblies dürfte das nciht mehr gehen, wobei man die signierung ja wiederum einfach entfernen kann....

383 Beiträge seit 2006
vor 15 Jahren

vor einiger Zeit bin ich auf Managed Spy++ gestossen.

Mit der ManagedSpyLib kann man Änderungen am laufenden Prozess vornehmen. Nach dem motto "ich hab' jetzt lust auf lila hintergrundfarbe". Assembly signed hin oder her.

Ich frag' mich ob es irgendwie möglich wäre, zur Laufzeit events (z.Bsp. Drag & Drop-Funktionalität) zu erstellen.

Gelöschter Account
vor 15 Jahren

eigenschaften änder oder code injecten sind 2 paar schuhe. ersteres geht einfach über reflection und verändert die applikation an sich nciht. letzteres kann böse werden.

tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren

Hallo,

wie sehen die typischen Anwendungsfälle aus? (in der Praxis)

AOP
Statische-Codeanalyse,
Um Klassen aus einer DLLs statisch zu linken.
Code-Generation (Compiler),
Programatisches debuggen von Assemblies
Patchen ohne neukompilation.
Code-Obfuscation
Schnittstellen dynamisch implementieren.
Viren
Trainer
Trojaner
Cracks
Um zu verstehen wie .NET auf der untersten Ebene funktioniert.

Um Eigenschaften dynamisch zu ändern sollte man weiterhin Reflection verwenden, da hier die Performance besser ist.

Nützt es was wenn ich die Programme mit einem Strong Name signiere ?
Wie kann man solche Manipulationen verhindern ?

Eigentlich garnicht, da die Signatur relativ leicht entfernt werden kann. Es ist sogar möglich mit Cecil ein Assembliy zur Laufzeit zu signieren.

eigenschaften änder oder code injecten sind 2 paar schuhe. ersteres geht einfach über reflection und verändert die applikation an sich nciht. letzteres kann böse werden.

Mit einem Küchenmesser kann man auch Menschen töten.

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

Gelöschter Account
vor 15 Jahren

ein paar punte sind mir nicht klar.

Statische-Codeanalyse: mir fällt jetzt nciht ein wie einem diese lib dabei helfen kann.
Code-Generation (Compiler): wie meinst du das?
Programatisches debuggen von Assemblies: was meinst du damit?
Patchen ohne neukompilation: jupp kleinere sachen kann man damit machen obwohl das wohl nur selten zum einsatz kommt, da man seine sourcen ja sowiso nachziehen muss.
Code-Obfuscation: wie stellst du dir das vor?
Schnittstellen dynamisch implementieren: wozu soll das gut sein?
AOP: ja das sehe ich ein.

Viren/Trainer/Trojaner/Cracks: ja das ist klar.

tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren

Hallo,

statische-Codeanalyse:

Man benötigt keinen Source. Man kann nach Fehlermustern suchen.

Code-Generation (Compiler):

Ich will z.B. einen Brainfuck-Compiler schreiben der .NET Assemblies erzeugt.

Patchen ohne neukompilation:

Wenn ich irgendein Programm habe das einen Bug hat, habe ich nun die Möglichkeit es selber zu reparieren. Es würden sogar grössere "Operationen" funktionieren. Ich könnte eine gepatchte Methode in VS schreiben und kompilieren. Diese Methode könnte ich jetzt in das verbuggte Assembly kopieren.

Programatisches debuggen von Assemblies:

Eigenen Debugger schreiben. Bevor man zeilenweise mit F11 durchgeht kann man dann z.b. Nach Fehlermustern suchen.
Vieleicht währen auch Programme denkbar die sich selber Debuggen.

Code-Obfuscation:

einige Produkte verwenden Cecil um das verschleierte Assembly zu erzeugen, ohne den Code anzutasten.

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo zusammen,

in diesem Thread geht es um die vorgestellte Komponente. Zum Thema Sinn und Unsinn von Code-Injektion ist damit erstmal genug gesagt, um eine Vorstellung für die Einsetzbarkeit dieser/solcher Komponenten zu gewinnen. Für weitere Diskussionen bitte bei Bedarf einen neuen Thread öffnen.

herbivore

X
38 Beiträge seit 2008
vor 15 Jahren
Binäre Recompilation

Hallo Freunde

Ich hab mir jetzt mal diesen beitrag gestern agesehen mitgenommen und durh gearbeitet jedenfalls denn snippet von tscherno

Was mich jetzt intressiert ob man mit diesem werkzeug auch ein Matritzenkompilat / Antikompilat erzeugen könnte bei dem sich das Cecil Code injector in die erste Load einnistet und von dord aus jeden Buchstaben und code schnipsel den das Programm beim laufen ausgibt abschreiben kann
und zum beispiel in einer TXT datei speichern würde? So könnte man nämlich zumBeispiel Bug bei schon längst kompiliert und assemblierten Programmen aus merzen in dem man sie ohne den original code zu nutzten über ein solche tool einfach wieder flott bekommt

Bis denne Xx tja xX

ein Code ist nur so lange Spaghetti wie du keine Ahnung von ihm hast

tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren

Hallo Xx tja xX,

du könntest ein Assembly schreiben welches die "reparierte" Version einer Methode enthält. Mit Cecil löscht du dann die alte Methode und fügst die neue ein.

Gruss
tscherno

PS: Für den Reflector.NET gibt es das Plug-In Reflexil mit dem man Assemblies direkt bearbeiten kann.

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

X
38 Beiträge seit 2008
vor 15 Jahren

Hallöchen ich bins nochmal...

ist es dann nicht eigentich auch möglich ststt Console dann zumbeispiel die Klasse eines eigennen Objects anzugeben?
Also praktisch hätte man dann ne Klasse geschrieben in der man ne grosse andere Methode hatzum beispiel eine solche Reflection aber weil man halt zu faull ist oder ainfach nicht anders sich zu helfen weiss nimmt man einfach eine zweite klasse hinzu in die man dann eine Methode schreibt wie zum beispiel

public void Family()
{
   Console.WriteLine("HEllo Mama";);Console.WriteLine("Hello Papa";);
   Console.WriteLine("Gehts euch gut?(y/n)");
   if(Console.ReadLine == "y";) { 
      this.close();
   }else {
       Console.WriteLine("Oh my god!!!");
      Console.ReadLine();
   }
}

die man dann durch einneues Object variabl in Program ausführen lässt.

Falls ihr wisst was ich meine sagt mal bescheid.

Bis denn dann

ein Code ist nur so lange Spaghetti wie du keine Ahnung von ihm hast

tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren

Hallo,

dieser Artikel ist jetzt auch in etwas ausführlicherer Form auf englisch in meinem neuen Blog verfügbar.

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

M
205 Beiträge seit 2008
vor 15 Jahren

Hallo tscherno,

hab mir gerade deinen Artikel durchgelesen. Gute Arbeit! Finde ihn interessant und aufschlußreich.
Wie schaut die Sache aus wenn es zu einem Sytaktischen Fehler kommt? zum beispiel wird eine 2te Main Methode injeziert oder sonst ein Member mit gleichem Namen? Macht dann dass Prog zur Laufzeit probleme oder erkennt cecil solch einen fehler?

mfg Markus

tscherno Themenstarter:in
630 Beiträge seit 2007
vor 15 Jahren

Hallo markus.bodlos,

freut mich dass dir der Artikel gefällt. Bei ungültigem code bekommt man meistens eine InvalidProgramException oder eine BadImageException.

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

M
205 Beiträge seit 2008
vor 15 Jahren

danke 🙂

M
205 Beiträge seit 2008
vor 15 Jahren

Bins nochmal 😉
Da für längere Codes, diese Art der generierung ja nicht gerade komfortable ist würd mich interessieren obs auch eine Möglichkeit gibt einen Verweis auf eine DLL zu legen und ledilich den Methodenaufruf sowie den Verweis injizieren?

mfg

X
38 Beiträge seit 2008
vor 15 Jahren
Genau

Genau das hab ich auch gemeint mit Myfamily
aber naja.....🤔

ein Code ist nur so lange Spaghetti wie du keine Ahnung von ihm hast

S
142 Beiträge seit 2007
vor 14 Jahren

Bins nochmal 😉
Da für längere Codes, diese Art der generierung ja nicht gerade komfortable ist würd mich interessieren obs auch eine Möglichkeit gibt einen Verweis auf eine DLL zu legen und ledilich den Methodenaufruf sowie den Verweis injizieren?

mfg

DLL-Injection ist in C# leider nicht möglich. Was Du natürlich machen könntest ist eine native DLL zu schreiben und den Code via Pinvoke aufzurufen.

Gelöschter Account
vor 14 Jahren

DLL-Injection ist in C# leider nicht möglich.

theoretisch schon, du musst nur eine möglichkeit finden, die dll´s im native assembly cache zu manipulieren.

S
142 Beiträge seit 2007
vor 14 Jahren

Hab zu dem Thema ein Tutorial gefunden:

http://www.codingthewheel.com/archives/how-to-inject-a-managed-assembly-dll

Here's the thing. You don't really "inject" a managed assembly into another process. Instead, you inject a native DLL, and that DLL executes some code which invokes the .NET runtime, and the .NET runtime causes your managed assembly to be loaded.

2.921 Beiträge seit 2005
vor 14 Jahren
Verweis auf externe DLL legen

Bins nochmal Augenzwinkern
Da für längere Codes, diese Art der generierung ja nicht gerade komfortable ist würd mich interessieren obs auch eine Möglichkeit gibt einen Verweis auf eine DLL zu legen und ledilich den Methodenaufruf sowie den Verweis injizieren?

mfg

Um das zu erreichen, müßte man eigentlich nur folgendes machen:

Testprojekt anlegen in Visual Studio.

Sagen wir wir wollen eine MessageBox aus einer DLL anzeigen.
Die Funktion in der DLL heißt "ShowDebugMessage", dann gucken wir uns den IL-Code an und sehen nach wie der Verweis eingebunden wird, dann schießen wir den Code in die Exe, entweder über Mono.cecil, wie hier Eingangs gezeigt, oder über
einen Enhancer (s. dazu im Forum Suche nach: Enhancer

Voilá. Fertig.

Außerdem s. dazu auch hier:

zu dürftige Logging-Informationen beim Kunden, was tun?

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.