C Verständnisfrage bzgl. Dekompilieren

KingJoshii1000

Lieutenant
Registriert
Apr. 2010
Beiträge
879
Hallo,
ich habe gestern mir ein kleines Programm in C geschrieben, welches eine Passwortabfragefunktion beinhaltet und sich nur "freischalten" lässt, wenn man das "hardcoded" Passwort eingegeben hat. Nun wollte ich mit Ollydbg den jeweiligen Jump invertieren, was auch problemlos klappte und das Programm akzeptierte jede mögliche Eingabe als richtiges Passwort. Die exe ist im Release Modus kompiliert worden. Dabei ist mir aufgefallen, dass Ollydbg mir für die jeweilige Assemblerzeile meinen originalen Sourcecode angezeigt hat bzw. mir sogar meinen ganzen Sourcecode auf Wunsch anzeigen konnte. Ich habe mich früher mit dem Thema dekompilieren (oberflächlich) auseinander gesetzt und hatte nur im Kopf, dass .NET und Java sich dort ohne großen Aufwand dekompilieren lassen, es jedoch bei C / C++ fast nicht möglich ist. Zum Test habe ich mal CCleaner (war gerade greifbar) geöffnet, dort wurde mir kein Sourcecode angezeigt und der Assemblercode war viel schwerer zu entziffern.

Wie genau kommt das zu Stande, dass sich mein selbstgeschriebenes Programm so leicht dekompilieren und lesen lässt?
Ich dachte, dass wäre auf C / C++ wie gesagt fast nicht möglich. Und wie kann ich das verhindern bzw. erschweren?

Mit freundlichen Grüßen
 
KingJoshii1000 schrieb:
Wie genau kommt das zu Stande, dass sich mein selbstgeschriebenes Programm so leicht dekompilieren und lesen lässt?
Also wenn wirklich Dein Originalprogramm im Decompiler 1:1 stand, dann ist da wirklich beim kompilieren etwas schiefgelaufen oder der hat sich die Info direkt aus den Quellen gezogen. Keine Ahnung. Ich kenne das Programm nicht und ich kenne auch Deiner Compilereinstellungen nicht.

KingJoshii1000 schrieb:
Ich dachte, dass wäre auf C / C++ wie gesagt fast nicht möglich. Und wie kann ich das verhindern bzw. erschweren?
Also normalerweise fällt etwas aus nem Decompiler das zwar aussieht wie C aber eigentlich schon etwas verfremdet ist. Die Bezeichner sind ja unbekannt. Evtl. wurden auch Funktionen wegoptimiert usw.
Umso komplexer das Programm, umso schwieriger wird es natürlich dann ein brauchbares Dekompilat zu bekommen. Ein kurzes Hello World dagegen wird man sehr gut zurückkompilieren können, weil der Compiler da nicht allzuviele Möglichkeiten hat was "kaputtzuoptimieren" etc.

Passwörter sollte man übrigens nie ins Programm reincoden.

Und wie kann ich das verhindern bzw. erschweren?
Es gibt Programm, die "verwurschteln" das Kompilat noch um zu verschleiern was da passiert.
Stichwort: Obfuscator
 
Allgemein ist ein Dekompilieren von C/C++-Code schwierig. Einige Faktoren, die eine Mapping von Binary auf Sourcecode möglich machen:
- Kompiliert mit Symbolen "-g" bei gcc
- Source Code wird vom Debugger oder Profiler gefunden
- Debugger oder Profiler verwenden, der das Mapping auch durchführen kann (z.B. perf oder vtune)
Allgemein sind dafür aber Symbole in the Binary notwendig.

Theoretisch ist ein halbwegs sinnvolles Dekompilieren auch in C/C++ möglich, wenn mit "-O0" kompiliert wird. Dann wird der Kompilieren den Sourcecode fast 1-zu-1 abbilden (sehr nützlich für Debugging). Wenn dann auch noch Symbole dabei sind, kann man den Sourcecode wieder nahezu herstellen.
 
Hallo,
das ist der Sourcecode:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef PASSWORD
#define PASSWORD "_[ZZQZNMZWkRXMb[a^\n"
#endif
void keep_window_open() {
	char buffer[2];
	printf("Press any key.\n");
	fgets(buffer, 2, stdin);
}
void clearInput() {
	char c;
	printf("Clearing input.\n");
	while ((c = getchar()) != '\n' && c != EOF);
}
int login() {
	char buffer[100];
	memset(buffer, 0,sizeof(buffer));
	printf("Enter password:");
	fgets(buffer, 100, stdin);
	if (isalpha(buffer[98]) != 0) {
		clearInput();
	}
	for (int i = 0; i < sizeof(buffer); i++) {
		buffer[i] = (int)buffer[i] + 12;
		if (buffer[i + 1] == '\n')break;
	}
	if (strcmp(buffer, PASSWORD) == 0) {
		return 1;
	}
	else {
		return 0;
	}
}
int main(int argc, char** argv) {
	for (;;) {
		if (login()) {
			printf("Login Successful.\n");
		}
		else {
			printf("Login failed.\n");
		}
	}
}
Diesen finde ich, 1:1 mit passenden Bezeichner auch in Ollydbg. Ich hab ein Bild mal in den Anhang gepackt.
Die Compilereinstellungen sind noch die Standarteinstellungen, die Release voreingestellt hat. Geschrieben und kompiliert wurde mit Visual Studio 2015.

Nachtrag:
Habe den Code mit der Debug Einstellung kompiliert, er ist deutlich komplizierter und der original Sourcecode, der unten angezeigt wurde, ist verschwunden.
Welche Einstellung beim Compiler (?) kann das hervorrufen? Ich hätte eher vermutet, dass der Code im Debug-Modus gut auszulesen sei und im Release nicht.

Mit freundlichen Grüßen
 

Anhänge

  • bild.jpg
    bild.jpg
    460,4 KB · Aufrufe: 342
Zuletzt bearbeitet:
KingJoshii1000 schrieb:
Die Compilereinstellungen sind noch die Standarteinstellungen, die Release voreingestellt hat. Geschrieben und kompiliert wurde mit Visual Studio 2015.
Keine Ahnung wie die Standardeinstellungen sind und hab auch kein Visual Studio aussehen. Guck doch mal explizit in der Debugging-Page nach oder kompiliere per Hand mit
/DEBUG:NONE
 
Visual Studio erzeugt normalerweise immer Debuginformationen. In der Exe steht dann ein Pfad zu dieser Datei drin (was meistens nur auf deinem Computer eine Bedeutung hat), jeder (halbwegs) gut Decompiler/Disassambler/Debugger ließt dann diese Datei und zeigt dir dann den Quellcode an.

Wenn du Debug kompilierst, wird nichts optimiert, daher ist dein Programm deutlich länger (aber für den Experten einfacher zu lesen). Warum OllyDbg in dem Fall keinen Code mehr anzeigt: Keine Ahnung, mal neustarten.

Tipp: In VS ist ja schon ein Debugger dabei, der zeigt auch den dazugehörigen Code im Disassembly an.

Falls du das Generieren unterbinden willst: Projekt-><Name>-Eingeschaften->Linker->Debugger->Debunginfo generieren->Nein
 
Zurück
Oben