Wie manipuliert man Software? Und wie sieht so eine manipulierte Software aus?



  • Ich lesen ständig von irgendwelchen Backdoors, Exploit usw. Leider reichen meine Kenntnisse nicht aus, um so etwas zu analysieren.
    Ich würde aber trotzdem gerne wissen, wie so etwas theoretisch aussehen könnte.
    Hier habe ich ein einfaches Programm geschrieben.
    Machen wir das ganze etwas spannend und stellen uns vor, dass zahl1+zahl die Abschusscodes für die neuen russischen Atomraketen
    ergeben. Jetzt muss der NSA-SuperHacker dies verhindern und das Programm so manipulieren, dass zahl1 mit zahl2 subtrahiert wird.
    Der Russe darf es aber nicht bemerken. 😃

    Hoffentlich versteht ihr was ich meine.^^ Ich würde nur gerne wissen, wie so ne manipulierte Software aussieht und wie ihr da theoretisch vorgeht.
    Ich brauche keine 1:1 Anleitung. Nur eine triviale Lösung, damit ich mir es besser vorstellen kann.

    Danke für eure Antworten 🙂

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        int zahl1, zahl2;
        cout << "Bitte Zahl 1 eingeben!" << endl;
        cin >> zahl1;
        cout << "Bitte Zahl 2 eingeben!" << endl;
        cin >> zahl2";
    
        cout << "Ihr Ergebnis" << zahl1+zahl2 << endl;
        return 0;
    }
    

    An die anderen: Nein, ich möchte kein Hacker werden und Ja, ich habe den Klassiker schon gelesen 😉



  • Das Beispiel ist Blödsinn, daher kann man anhand dieses Beispiels auch nichts erklären.
    (Genaugenommen ist die Frage schon Blödsinn, aber naja.)

    MixxerMarvin schrieb:

    An die anderen: Nein, ich möchte kein Hacker werden und Ja, ich habe den Klassiker schon gelesen 😉

    Hast du auch die Antworten zu "F: Wirst du mir beibringen, wie man hackt?" gelesen?



  • MixxerMarvin schrieb:

    Hoffentlich versteht ihr was ich meine.^^

    Nee. Oder doch. Auf jeden Fall stellst du dir das alles viel zu einfach vor und in der Form macht deine Frage keinen Sinn.



  • Als starke Vereinfachung:

    vorher:

    mov ebx, zahl1
    mov eax, zahl2
    add eax, ebx
    

    nachher:

    mov ebx, zahl1
    mov eax, zahl2
    sub eax, ebx
    

    Das ganze musst du dir jetzt nur noch als Opcodes vorstellen die mit einem Hexeditor verändert wurden und mit Speicheradressen arbeiten.

    Der Russe darf es aber nicht bemerken. 😃

    Wenn er seine SW anhand von Prüfsummen überwacht, dann wird er das.
    Und wenn er das Rechenergebnis überprüft, dann auch.



  • Um zu verstehen, wie ein Programm funktioniert, hängt man sich am besten mit einem Debugger dran.
    Du siehst dann genau, was das Programm macht. Natürlich siehst du nicht deinen C++ Code, denn der wurde ja in ein ausführbares Programm übersetzt. Aber die Debugger sind schlau genug, um aus dem Binärformat dir den Assembercode anzuzeigen.
    Du siehst also was dein Programm auf Assembler Ebene so macht. Du kannst schauen, welche Variablen wie gesetzt werden, welche Register verwendet werden, usw...
    Und wenn du die Addition auf eine Subtraktion ändern willst, musst du zuerst mal wissen, wo diese stattfindet. Dann kannst du einfach den Assemberbefehl austauschen.

    Jetzt in der Praxis:

    Ich hab dein Programm ein bisschen angepasst, damit ich dir das besser erklären kann:

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        int zahl1=0xAABBCCDD, zahl2=0xFFAABBCC; // <-- Variablen mit eindeutigen Werten initialisieren
        cout << "Bitte Zahl 1 eingeben!" << endl;
        cin >> zahl1;
        cout << "Bitte Zahl 2 eingeben!" << endl;
        cin >> zahl2;
    
        cout << "Ihr Ergebnis" << zahl1+zahl2 << endl;
        return 0;
    }
    

    Aus dem C++ Code hab ich nun ein ausführbares Programm erstellt.

    Mit einem Debugger (ich verwende gdb als Debugger) häng ich mich nun bei dem erstellten Programm dran, um zu sehen, wie es funktioniert.
    Als erstes schau ich mir den Assemblercode von main an.
    Zuerst schau ich mir an, wo die beiden Variablen "zahl1" und "zahl2" zu finden sind. Du erkennst das typische Hexmuster wieder, mit dem zahl1 und zahl2 initialisiert werden.
    zahl1 liegt also an der Speicheradresse esp+0x18, zahl2 bei esp+0x1c.
    esp ist der Stackpointer, Stack kennst du ja sicher, dort werden die lokalen Funktionsvariablen und Übergabeparameter für Funktionen abgelegt.

    0x080486ae <+10>:	mov    DWORD PTR [esp+0x18],0xaabbccdd
       0x080486b6 <+18>:	mov    DWORD PTR [esp+0x1c],0xffaabbcc
    

    Mit dem Wissen über die Adressen suchen wir weiter. Irgendwo müssen die beiden Variablen ja addiert werden.
    Man möchte ein add zahl1,zahl2 erwarten. Der Compiler hat sich für eine etwas optimiertere Variante zum Addieren entschieden:

    0x0804872e <+138>:	mov    edx,DWORD PTR [esp+0x18]
       0x08048732 <+142>:	mov    eax,DWORD PTR [esp+0x1c]
       0x08048736 <+146>:	lea    ebx,[edx+eax*1]
    

    Es wird also zahl1 ins Register edx kopiert, zahl2 nach eax.
    Der Befehlt lea addiert nun die beiden Register und schreibt das Ergebnis ins Register ebx.

    Ich hab 1 für zahl1 eingegeben, und 2 für zahl2.
    Ein Blick auf die Register vor und nach dem lea Befehl bestätigt die Vermutung, dass das die richtige Stelle ist:

    Davor:

    eax            0x2	2
    ecx            0xbffff2a8	-1073745240
    edx            0x1	1
    ebx            0x3bdff4	3923956
    

    Danach: ebx=3

    eax            0x2	2
    ecx            0xbffff2a8	-1073745240
    edx            0x1	1
    ebx            0x3	3
    

    Hier wäre also der richtige Ort, um einzugreifen.
    Man könnte das lea gegen einen anderen Befehl austauschen, sprich, den Addierbefehl gegen einen Substrahierbefehl zu tauschen.

    Es gibt Debugger, die einem da ziemlich unter die Arme greifen, OllyDbg ist ein Programm, wo man recht schnell den Assembercode manipulieren kann und das Ergebnis dann auch wieder als Programm abspeichern kann.

    Das war mal ein kleine Einführung in das Thema.
    Wenn dich das interessiert, installiere dir OllyDbg oder gdb, und kauf dir ein gutes Buch, denn ohne das nötige Hintergrundwissen kommt man nicht weit.
    Als Buch empfehle ich "Hacking: The Art of Exploitation".

    Hier der Assemblercode der gesamten main Funktion:

    0x080486a4 <+0>:	push   ebp
       0x080486a5 <+1>:	mov    ebp,esp
       0x080486a7 <+3>:	push   ebx
       0x080486a8 <+4>:	and    esp,0xfffffff0
       0x080486ab <+7>:	sub    esp,0x20
       0x080486ae <+10>:	mov    DWORD PTR [esp+0x18],0xaabbccdd
       0x080486b6 <+18>:	mov    DWORD PTR [esp+0x1c],0xffaabbcc
       0x080486be <+26>:	mov    DWORD PTR [esp+0x4],0x80488a0
       0x080486c6 <+34>:	mov    DWORD PTR [esp],0x804a0e0
       0x080486cd <+41>:	call   0x80485b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
       0x080486d2 <+46>:	mov    DWORD PTR [esp+0x4],0x80485e0
       0x080486da <+54>:	mov    DWORD PTR [esp],eax
       0x080486dd <+57>:	call   0x80485d0 <_ZNSolsEPFRSoS_E@plt>
       0x080486e2 <+62>:	lea    eax,[esp+0x18]
       0x080486e6 <+66>:	mov    DWORD PTR [esp+0x4],eax
       0x080486ea <+70>:	mov    DWORD PTR [esp],0x804a040
       0x080486f1 <+77>:	call   0x80485c0 <_ZNSirsERi@plt>
       0x080486f6 <+82>:	mov    DWORD PTR [esp+0x4],0x80488b7
       0x080486fe <+90>:	mov    DWORD PTR [esp],0x804a0e0
       0x08048705 <+97>:	call   0x80485b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
       0x0804870a <+102>:	mov    DWORD PTR [esp+0x4],0x80485e0
       0x08048712 <+110>:	mov    DWORD PTR [esp],eax
       0x08048715 <+113>:	call   0x80485d0 <_ZNSolsEPFRSoS_E@plt>
       0x0804871a <+118>:	lea    eax,[esp+0x1c]
       0x0804871e <+122>:	mov    DWORD PTR [esp+0x4],eax
       0x08048722 <+126>:	mov    DWORD PTR [esp],0x804a040
       0x08048729 <+133>:	call   0x80485c0 <_ZNSirsERi@plt>
       0x0804872e <+138>:	mov    edx,DWORD PTR [esp+0x18]
       0x08048732 <+142>:	mov    eax,DWORD PTR [esp+0x1c]
       0x08048736 <+146>:	lea    ebx,[edx+eax*1]
    => 0x08048739 <+149>:	mov    DWORD PTR [esp+0x4],0x80488ce
       0x08048741 <+157>:	mov    DWORD PTR [esp],0x804a0e0
       0x08048748 <+164>:	call   0x80485b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
       0x0804874d <+169>:	mov    DWORD PTR [esp+0x4],ebx
       0x08048751 <+173>:	mov    DWORD PTR [esp],eax
       0x08048754 <+176>:	call   0x8048550 <_ZNSolsEi@plt>
       0x08048759 <+181>:	mov    DWORD PTR [esp+0x4],0x80485e0
       0x08048761 <+189>:	mov    DWORD PTR [esp],eax
       0x08048764 <+192>:	call   0x80485d0 <_ZNSolsEPFRSoS_E@plt>
       0x08048769 <+197>:	mov    eax,0x0
       0x0804876e <+202>:	mov    ebx,DWORD PTR [ebp-0x4]
       0x08048771 <+205>:	leave  
       0x08048772 <+206>:	ret
    


  • Hallo,

    zum Thema findest du unter diesem Link Anleitungen. Vor allem findet man eine gute Einführung in den beliebten Debugger "OllyDbg".
    http://thelegendofrandom.com/blog/sample-page

    Aber parallel dazu wirst du dich auch mit Assemblerprogrammierung, der x86 Architektur und dem Grundprinzip von Betriebssytemen auseinandersetzen müssen.


Anmelden zum Antworten