To deface είναι μια "διαδικασία" κατά την οποία "χαλάμε" ή αλλάζουμε προς το συμφέρον μας κάποιο αντικείμενο, όπως μια
εικόνα, ένα πρόγραμμα ακόμα κι ένα web site. Η διαδικασία του deface δεν δηλώνει (σχέδον ποτέ) ικανότητα και μαγκιά!
Συνήθως πραγματοποιείται από πιτσιρικάδες που έχουν βρεί 2,3 έτοιμα προγράμματα και εφαρμόζουν τις "ικανότητες" τους σε
αθώους στόχους. Οι λόγοι που κάποιος κάνει ένα deface σε ένα πραγματικό περιβάλλον και όχι για εκπαιδευτικούς σκοπούς
είναι πολλοί... βασικά όμως είναι η επίδειξη που συνεπάγεται μετριότητα και βλακεία!
Αυτό που θέλω να πω με αυτό το post, συνδέοντάς το με κάποιο τρόπο με ένα προηγούμενο μου... που ίσως θεωρήθηκε
αναχρονιστικό, είναι πως η λογική assembler και stack processing είναι αναπόσπαστο κομμάτι όχι μόνο των εφαρμογών του
1980, 1990 αλλά και του 2000+.
Κοινώς ... "τα ίδια παλιά καλά κρασιά σε νέα μπουκάλια".
Ο στόχος δεν είναι να αποδείξω το προφανές ούτε βέβαια να δείξω οτι λέω κάτι καινούριο (μιας και στο internet υπάρχουν
πολλές αναφορές πολύ πιο advanced). Ο στόχος είναι να γίνει μια επικοδομιτική συζήτηση για το τι υπάρχει πίσω από τις
νέες τεχνολογίες, πόσο έχουμε εξελιχθεί και μήπως τελικά είμαστε δέσμιοι των αρχικών μας προσεγγίσεων... και στην ουσία
δεν έχει γίνει καμιά τεράστια αλλαγή...
Επιτρέψτε μου να το εξηγήσω με ένα πάρα πολύ απλό παράδειγμα: Το παράδειγμα μου θα ακολουθεί επίτηδες τον παραδοσιακό
τρόπο προγραμματισμού (από command line) πιο πολύ για να δείξω οτι υποστηρίζεται ακόμα (μετά από 10+ χρόνια windows
domination).
Πολοί είναι αυτοί που υποστηρίζουν οτι τα καλύτερα "κόλπα" γίνονται από command line... και δεν μιλώ μόνο για τους
linuxάδες (που ίσχύει 100%) αλλά κ για τους win32...!
Έστω οτι γράφουμε ένα πρόγραμμα το ίδιο χαζό με αυτό που είχαμε γράψει όταν κάναμε το παράδειγμα στο post για το "Πως
λειτουργεί ένα πρόγραμμα...."
Αυτή τη φορά θα γράψουμε σε C# και managed κώδικα:
// Test3.cs
public class test3
{
public static void Main()
{
function1 (10, 20, 30);
}
public static void function1 (int a, int b, int c)
{
string s;
s = "Hello World!";
System.Console.WriteLine(s);
}
}
Κάνουμε compile από command line:
c:\>csc test3.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
c:\>_
Έχει δημιουργηθεί το test3.exe. Αυτό το 'exe' δεν ακολουθεί την δομή των "κλασικών" PE executables αλλά έχει πληροφορίες
για τα περιεχόμενα του σε επίπεδο κλάσεων, μεταβλητών, members κλπ...
Θα μπορούσαμε να φτιάξουμε έυκολα ένα πρόγραμμα που να τις διαβάζει αυτές τις πληροφορίες, αλλά δεν χρειάζεται γιατι το
έχει κάνει η Microsoft για εμάς.
Είναι το ildasm.exe (intermidiate language disassembler <- μάλλον )
Καλώντας λοιπόν :
c:\>ildasm test3.exe /all=test.il
θα δημιουργήσει το αρχείο test.il με τον κώδικα σε CIL (common intermidiate language). Αυτός ο κώδικας μοιάζει πάρα πολύ με την
assembly και αποτελείται από assembly-like intructions. Ο assembler αυτός δεν διαφέρει από τον assembler της "μηχανής"
(π.x. τον περίφημο macro-assembler) και μαλιστα είναι stack oriented όπως θα δούμε παρακάτω.
Πέρα από σημαντικά και ενδιαφέροντα στοιχεία για το πρόγραμμα μας που αναφέρονται μέσα στο test.il, για τις παραπάνω
member functions θα δούμε:
.class public auto ansi beforefieldinit test3
extends [mscorlib]System.Object
{
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 8
IL_0000: nop --> Mην κάνεις τίποτε (no operation).
IL_0001: ldc.i4.10 --> LDC (load numeric constant) φόρτωσε από την μνήμη στο stack την τιμή 10
IL_0002: ldc.i4.20 --> LDC (load numeric constant) φόρτωσε από την μνήμη στο stack την τιμή 20
IL_0003: ldc.i4.30 --> LDC (load numeric constant) φόρτωσε από την μνήμη στο stack την τιμή 30
IL_0004: call void test3::function1(int32,
int32,
int32) --> Kάλεσε την function1 με παραμέτρους 3 intergers 32bits
--> από το stack.
IL_0009: nop --> μην κάνεις τίποτε (no operation).
IL_000a: ret --> επέστρεψε στο λειτουργικό.
} // end of method test3::Main
Ομοίως για την function1 έχουμε:
.method public hidebysig static void function1(int32 a,
int32 b,
int32 c) cil managed
{
// Code size 15 (0xf)
.maxstack 1
.locals init ([0] string s) --> Kαθορισμός της σχετικής address 0->μεταβλητής για την αποθήκευση της s.
IL_0000: nop
IL_0001: ldstr "Hello World!" --> Φορτώνεται η σταθερά στο stack.
IL_0006: stloc.0 --> Καταχωρείται στην μνήμη στη θέση 0 η μεταβλητή που
--> τα περιεχόμενά της είναι ήδη στο stack (από την προηγ. εντολή).
IL_0007: ldloc.0 --> Καταχωρείται από την μνήμη (θέση 0) στο stack.
IL_0008: call void [mscorlib]System.Console::WriteLine(string) --> Εμφάνισε στην οθόνη οτι έχεις στο stack
IL_000d: nop
IL_000e: ret --> Eπέστρεψε απο όπου εκεί που κλήθηκες.
} // end of method test3::function1
Άν τρέξω το πρόγραμμα μου θα δω το εξής:
c:\>test3
Hello World!
Τρομερό πρόγραμμα ε?
Χμ...
Έστω τώρα οτι εγώ έκανα deploy αυτό το πρόγραμμα και έβαζα κάπου μόνο το test3.exe.
Κάποιος μπορούσε να παράξει τον intermidiate κωδικα του, δίνοντας όπως είπαμε:
c:> ildasm test3.exe /out=test3.il
Μετά θα μπορούσε να μπεί μέσα στον intermidiate κώδικα και να τον αλλάξει αλλάζοντας π.χ. τα λεκτικά ή καλύτερα βάζοντας
δικά του.
Στην συγκεκριμένη περίπτωση θα αλλάξει την function 'funtion1' ως εξής
.method public hidebysig static void function1(int32 a,
int32 b,
int32 c) cil managed
{
// Code size 15 (0xf)
.maxstack 1
.locals init ([0] string s) --> καθορισμός της σχετικής address 0->μεταβλητής για την αποθήκευση της s
IL_0000: nop
IL_0001: ldstr "Hello World!" --> Φορτώνεται η σταθερά στο stack.
IL_0006: stloc.0 --> Καταχωρείται στην μνήμη στη θέση 0 η μεταβλητή που
--> τα περιεχόμενά της είναι ήδη στο stack (από την προηγ. εντολή)
IL_0007: ldloc.0 --> Καταχωρείται από την μνήμη (θέση 0) στο stack.
IL_0008: call void [mscorlib]System.Console::WriteLine(string) --> Εμφάνισε στην οθόνη οτι έχεις στο stack
IL_000100: ldstr "Defaced by Thiseas!" --> Φορτώνεται η σταθερά στο stack.
IL_000600: stloc.0 --> Καταχωρείται στην μνήμη στη θέση 0 η μεταβλητή που
--> τα περιεχόμενά της είναι ήδη στο stack (από την προηγ. εντολή)
IL_000700: ldloc.0 --> Καταχωρείται από την μνήμη (θέση 0) στο stack.
IL_000800: call void [mscorlib]System.Console::WriteLine(string) --> Εμφάνισε στην οθόνη οτι έχεις στο stack.
IL_000d: nop
IL_000e: ret --> επέστρεψε απο όπου εκεί που κλήθηκες.
} // end of method test3::function1
Μπορώ τώρα όμως να παράξω το test3.exe από τον παραπάνω αλλαγμένο intermidiate κώδικα?
Φυσικά.
Καλώντας τον assembler:
c:\>ilasm test3.il
Microsoft (R) .NET Framework IL Assembler. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
Assembling 'test3.il' to EXE --> 'test3.exe'
Source file is ANSI
Assembled method test3::Main
Assembled method test3::function1
Assembled method test3::.ctor
Creating PE file
Emitting classes:
Class 1: test3
Emitting fields and methods:
Global
Class 1 Methods: 3;
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
Άν τρέξω το πρόγραμμα μου θα δω το εξής:
c:\>test3
Hello World!
Defaced by Thiseas!
Η διαδικασία ήταν πάρα πολύ απλή. Μάλιστα είναι πολύ πιο απλή από το όταν είχαμε traditional ΕΧΕs... που μπορούσανε
βέβαια και αυτά να "πειραχτούν" υπο τις κατάλληλες προυποθέσεις ή με έναν απλό hex-editor. Για μεγάλες αλλάγες όμως
συνήθως ήταν πιο δύσκολο.
Συμπέρασμα:
Παλαιότερα το πάρεις όλες τις πληροφορίες, την ονοματολογία, τα μεγέθοι, τις κλάσεις κλπ κλπ από ένα EXE ήτανε απλά αδύνατον.
Τώρα είναι δυνατόν.
Το θεωρείται καλύτερο ή χειρότερο?
Ασφαλέστερο ή όχι?
Να μπορεί ο καθένας να δει σχεδόν τον κώδικά σας...επεμβαίνοντας κατά βούληση?
Nothing to declare...