| Hacks & Crackmes Tests, Fragen oder Hilfestellungen. Crackmes und Hackits werden hier diskutiert. |
Diskussion: .NET schutz im Forum Hacks & Crackmes, in der Kategorie Software Home; Hallo werte Hackerboard-User Vor geraumer Zeit habe ich hier mal ein Thema eröffnet ( Reverse Engineering ) und bin eigentlich ...
![]() |
| | #1 (permalink) |
| Registriert seit: 03.02.09 ![]() Likes: 0 | Hallo werte Hackerboard-User Vor geraumer Zeit habe ich hier mal ein Thema eröffnet (Reverse Engineering) und bin eigentlich positiv gestimmt, was die Qualität der Antworten und allg. hier an Beiträge angeht. Die Beiden folgenden Beiträge: Schutz vor dekompilieren von C#-Anwendungen bzw. von .NET Sicheres programmieren von Anwendungen ... sind schon gut ein Jahr vergangen und klären eingetlich vieles an meinen Fragen und fördern meine Ideen. Nun möchte ich es doch noch genau wissen, bzw. habe Fragen zu gewissen Techniken. Schon mal vorweg: Es geht nicht um eine Trialversion, sondern nur um Schutz gegen 'einfach weiter geben' und Einsicht in den Source. Folgendes habe ich bereits implementiert: 1) Einige Anti-Debugger tricks 2) Anti VMware 3) Obfuscation (eigenbau) 4) PE-Checks 5) Keyfile / Hardware Abhängig 6) Nativwall (eigenabu) a) Stimmt es, dass Olly, Windasm32 & Co. für .NET (fast) unbrauchbar sind? (da ja ausschliesslich mit IL gearbeitet wird?) b) Ich verschlüssle sämtlich Strings. Wieviel mehr Zeitaufwand hat da ein Cracker? wir nehmen an, es sind ca. 20 verscheidene verschlüsslungsroutinen c) Ich prüfe die Processliste/Registry nach bekannten Debugger & tools. Lohnt sich das überhaupt? d) Gibt es Möglichkeiten zu prüfen, ob etwas gepatch wurde? also nicht mittels MD5-Hash, sondern im Programmfluss selbst? e) Wenn ich einen Nativen Packer verwende, und verhindere das an die eigentliche .NET Antwendung ein Debugger sich anhängen kann, wie sicher ist sowas? f) Gibt es, abgesehen von der Hardwarebindung, eine andere Art die einfache Weitergabe zu erschweren? Habt ihr noch weitere Dinge die ich implementieren könnte? Anregungen und tipps allg. zu .NET? Freundlich grüsst Netonator |
| | |
| | #2 (permalink) | |
| Registriert seit: 28.05.08 ![]() Likes: 0 | Hallo Netonator, genau mit dieser Problematik habe ich mich auch schon beschäftigt. Zitat:
Aber den Großteil deiner Fragen wurde schon in dem von mir gestellt Thread(s. dein Beitrag oben) von beantwortet. the_uxreal | |
| | |
| HaBOT | |
| |
| | #3 (permalink) |
| Themenstarter Registriert seit: 03.02.09 ![]() Likes: 0 | Ja. IsDebuggerPresent kann pratkisch von jedem Debugger entschärft werden. Bei der Registry checks geht es eigentlich nur darum die Andwendung zu 'warnen'. Schliesslich haben normale User keine Debugger Installiert. Aja, und wenn du Managed-Debugger detecten willst: Code: Debugger.IsAttached Freundlichst grüsst Net. |
| | |
| | #4 (permalink) | |||||||
| Member of Honour ![]() Registriert seit: 02.04.05 ![]() ![]() ![]() Likes: 64 | Zitat:
Zitat:
Jeder halbwegs gewiefte Cracker wartet einfach bis das Programm die Strings selbst entschlüsselt. Anders sieht es da natürlich bei Decrypt-On-Demand aus. Aber auch dort kann man die Entschlüsselungsroutine, die in das Programm natürlich eingebaut werden muss, für seine Zwecke missbrauchen und selbst für jeden String aufrufen, ohne je die Verschlüsselungsart verstehen zu müssen. Zitat:
Nachteile: Bringt nur gegen Anfänger etwas und gibt auch False Positives, also Ärger bei "ehrlichen" Usern, nur weil irgendein Prozessname identisch ist. Was schaust du in der Registry nach? Verbietest du jemandem auch solche Tools nur installiert zu haben? Zitat:
Zitat:
Es dürfte also leichter sein eine silbern glänzende Kugel als eine Heunadel im Heuhaufen zu finden. Zitat:
Eine schwierige Sache. Andere Ideen wären vlt. eine Gesichtserkennung per Webcam oder bei Notebooks mit Fingerabdruckscannern. Etwas weniger handzuhaben wäre zum Beispiel das Prüfen der IP-Adresse des Kunden übers Internet. (sei sie statisch). Alle Methoden lassen sich natürlich auch aushebeln. Wenn man es geschickt angeht, benutzt man die Daten, die binden sollen, als Key zur Entschlüsselung des Programms im Speicher. (wenn diese genügend abstrakt sind und nicht einfach zu verraten sind, IP: Nein. HardwareID: Ja) In solchen Fällen muss zumindest der Cracker eine gültige Lizenz haben bzw. derjenige muss von einem Cracker angeleitet werden einen Memory-Dump oder ähnliches zu machen. Zitat:
Einen IL-Obfuscator. Der wirklich den Code sehr verkompliziert. Also fast wie Junk-Code, der am Ende aber noch einen kleinen Teil der Gesamtaufgabe erfüllt, so dass man diesen nicht einfach kürzen kann. Oder eine kleine IIL (intermediate-intermedita language xD) erschaffen. Also sozusagen den ganzen IL-Code ein deine IIL übersetzen und diese dann in deiner eigenen VM laufen zu lassen, die daraus wieder IL-Code erzeugt und ausführt zur Laufzeit (interpretiert). | |||||||
| | |
| | #5 (permalink) | ||
| Registriert seit: 28.05.08 ![]() Likes: 0 | Zitat:
| ||
| | |
| | #6 (permalink) | ||||||||
| Themenstarter Registriert seit: 03.02.09 ![]() Likes: 0 | Zitat:
Zitat:
Zitat:
Ich neheme nicht an das ein normales Programmm ebenfalls OllyDbg.exe heisst. Zitat:
Zitat:
Zitat:
Stimmt nicht ganz. Kann man auslassen, wenn man weiss wie. Zitat:
Zitat:
Wie muss das genau aussehen, Beispiel-pseudocode? Muss ich mir da ne riesige Select case Anweisung vorstellen die von einer Binardatei die eigenen Opcdoes liest und diese Auswertet? Freundlich grüsst Net. | ||||||||
| | |
| | #7 (permalink) | ||||
| Member of Honour ![]() Registriert seit: 02.04.05 ![]() ![]() ![]() Likes: 64 | Zitat:
Von da aus hangele ich mich bis zur Entschlüsselungsroutine. Da dürften einige interessante Strings vorbeifliegen. Zitat:
Als Beispiel: Als sehr billige Methode kann man nun nach diese Regeln immer einen Wert irgendwo absetzen, der immer eins höher sein muss als der zuletzt abgegebene Wert. Eine Routine kann dann einen Regelbruch bemerken (Anzeichen für Manipulation): "Ich muss die 8 absetzen, dort steht aber nur eine 5. Da ist was faul." Zitat:
Zitat:
Dann überlegt man sich ein Instruction Set mit einem Turing-vollständigen Repertoire an Anweisungen und einen Register-Satz und übersetzt Schritt für Schritt jede IL-Instruktion in möglicherweise auch mehrere der eigenen Instruktionen. Aber das wird das kein Zuckerschlecken. | ||||
| | |
| | #8 (permalink) | |||
| Themenstarter Registriert seit: 03.02.09 ![]() Likes: 0 | Zitat:
Zitat:
Zitat:
ich werde mich vermutlich an eine dynamische IL generation halten, somit ist es mit Reflector nicht möglich direkt VB/C# Code auszulesen, wobei man auch das umgehen kann, wenn man weiss wie! Hat noch jemand andere ideen. Gruss .NET | |||
| | |
| | #9 (permalink) |
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() Likes: 156 | Für bestimmte Zwischenberechnungen könntest Du auch eine eigene VM erstellen. Hört sich komplizierter an, als es ist: Ich würde da einfach eine Stackmachine nehmen: http://www.ece.cmu.edu/~koopman/stac...rs/sec3_2.html d.h der arbeitet nicht mit Registern, sondern holt sich alle Operanden vom Stack und schreibt da auch das Ergebnis hin. Da braucht man also nur IP=InstructionPointer und Equal/Signed Flags sowie ein Code-Array, Stack-Array/Stackimplementierung und ein Datenarray. Dann definierst Du einen grundsätzlichen Befehlssatz: Code: Opcode Operand 0001: ADD (holt sich 2 Werte vom Stack und addiert diese) 0002: Sub (genauso, nur mit Subtraktion) .... weitere Rechenarten 0010 0000: Load Var (als Var kann man eine Ziffer angeben und intern einfach mit dataarray[var] arbeiten) 0011 0000: store Var 0012: cmp (holt sich die 2 variablen vom Stack und vergleicht diese, stetzt dann bei Übereinstimmung den Equal Flag, bei Var1<Var2 den Signed Flag) 0013 0000: JE Addr jmp if equal zu Adresse (sprich, wenn Equal Flag gesetzt ist) 0014 0000: JS Addr jmp if smaller zu Adresse (wenn Signed Flag gesetzt ist) 0015 0000: jg Addr jmp if greater zu Adresse (wenn weder Equal noch Signed gesetzt sind) 0016 0000: call Addr Mit Macros bzw ein bisschen mehr Aufwand kann man auch die Opcodes für jede Anwendung zufällig generieren. Und natürlich muss man sich nicht an die strenge Nummerierung oder "1 Byte für Operator, 1 Byte für Operand" halten Der Code kann z.B irgendwelche Werte mit der Serial/HardwareID berechnen, die im "richtigen" Code weiterbenutzt werden können. Bei falschen Werten klappt dann z.B das Speichern oder Laden nicht mehr. Du kannst sogar mit etwas Überlegung Opcodes in den Key einbauen und z.B bei der Initialisierung einen "Verbund" aus Key und HardwareID mit ausführen. Denn Deine VM kann auch ruhig auf "richtige" Speicherwerte in der Anwendung zugreifen und diese verändern. Schlägt die Initialisierung fehl, kann man mittels try X catch print "Registrierungscode stimmt net!" die Fehlermeldung ausgeben. Dadurch wird der Key wirklich gut im Programm eingebunden und man wird ohne Key nicht wirklich weiter kommen können. Aber auch mit Key und richtiger HardwareID dürfte es ein ziemlicher Aufwand sein Der Nachteil ist natürlich, dass Du den Code dafür in der definierten Assembly schreiben musst und damit nicht wirklich komplexe Berechnungen anstellen kannst.
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. |
| | |
| | #10 (permalink) |
| Themenstarter Registriert seit: 03.02.09 ![]() Likes: 0 | Danke CWD für die Antwort. Die Grosse Preisfrage wäre da noch: in welcher Sprache programmier ich die VM? Reicht dazu .NET? Oder muss ich da auf eine Nativ sprache zurückgreifen? Weil in .NET gibt es eine Stack Klasse, die imprinzip die grundlegende Funktionen eines Stacks bereit stellt. Vielleicht kann man damit was anfangen. Wie sollen die Instruktionen geladen werden? bau ich mir da eine DLL die mit Parametern gefüttert wird und die Operationen ausführt? Oder wie muss ich mir das vorstellen? Freunldich grüsst Net. |
| | |
| | #11 (permalink) |
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() Likes: 156 | Du kannst dafür NET nutzen Ich glaube, Du denkst zu kompliziert ![]() Der Interpreter liest ja nur Daten aus einem Array und macht (im einfachsten Fall) einen Switch-Case bzw IF-Else durchlauf. Der "Code" ist nichts anderes, als die Abfolge Deiner definierten Opcodes=Zahlen. Im einfachsten Fall löst man das mit Macros/Definitionen: CONST ADD=1 CONST SUB=2 CONST LOAD=3 usw. Damit kann man schon "ASM" Programme schreiben: definierst für die eigentliche Bequemlichkeit zuerst Variablen: Code: CONST var1=1 CONST var2=2 Bsp: X=Y*Y Code: LOAD Var1 LOAD Var1 MUL STORE Var2 Code: int IP=0;
int code[]="Code aus irgendeiner Quelle/Datei/sonstiger Einbindung";
int data[]="array, welches als variablenarray dient"
bool equalflag=false;
bool signedflag=false;
Stack stack=new Stack();
while(ip<code.length)
{
opcode1=code[ip];
opcode2=code[ip+1];
IF (opcode1==ADD)
{
int var1=Stack.pop;
int var2=Stack.pop;
int result=var1+var2;
Stack.push(result);
IP=IP+1; //da es nur eine 1-Opcode Anweisung war, InstrucitonPointer nur um 1 weiterbewegen
}
ELSE IF (opcode1==....)
....
ELSE IF (opcode==STORE)
{
int index=opcode2;
data[index]=Stack.pop;
IP=IP+2; //ist eine 2-Opcodes Anweidung
}
Else IF (opcode1==Load)
{
int index=opcode2;
int wert=data[index];
Stack.push(wert);
IP=IP+2; //ist eine 2-Opcodes Anweidung
}
ELSE IF (opcode1==CMP)
{
int var1=Stack.pop;
int var2=Stack.pop;
if (var1==var2) equalflag=true ELSE equalflag=false;
if (var1>var2) signedflag=true ELSE signedflag=false;
IP=IP+1;
}
ELSE IF (opcode1==JE)
{
if (equalflag==true) ip=opcode2;
}
... usw ...
ELSE IF (opcode1==HALT)
Break; //sofern man einen HALT Opcode definiert, kann man die Ausführung auch frühzeitig beenden
} Bsp berechnet mit "echten" Usereingaben (einlesen im GUI z.B) in der VM die 10te Potenz und gibt es aus. Wieder Pseudo-NET. Code: CONST ADD=1
... wie oben im Bsp...
CONST VAR1=0
CONST VAR2=1
CONST VAR3=2
CONST VAR4=3
int mycode[] = //hier eine Arraydefinition+initialisierungsregeln in Deiner verwendeten sprache
{
ILOAD, 0, //Loadbefehl für Konstanten würde ich noch mit in die VM aufnehmen, ist nützlich
STORE, VAR1,
//4 ter Eintrag: also an der 4-ten Steller im mycode Array
LOAD, VAR2,
LOAD, VAR3,
MUL,
STORE, VAR4,
LOAD, VAR1,
ILOAD, 1,
ADD,
STORE, VAR1,
LOAD, VAR1,
ILOAD, 10,
CMP
JNE,4 //setze IP auf 4 - gehe also zum 4 Eintrag im mycode Array
}
int data[]=new int[4];
data[0]=0;
data[1]=readuserinput_als_integer; //Variablen für VM vorinitialisieren
data[2]=readuserinput_als_integer;
data[3]=0;
myVMInterpreter(mycode,data);
output(data[3])); //VAR4, also Ergebnis, ausgeben
} meine ich die Möglichkeite in den meisten Sprachen einen Array vorzuinitialisieren. Du schreibst also quasi nur Zahlen rein. So siehts dann intern im Code aus: Code: mycode={1,0,2,0,....} Außerdem kann man sich auch schnell einen "Assembler" schreiben, der die konstanten Strings durch Zahlen ersetzt. Nach dem gleichen Muster kann man diesen "Netonator-Opcode" auch aus externen Quellen laden ![]() Code: int keyfile[]=read("\\licence.key");
... junkcode ...
int hardwareid[]=....
... junkcode ...
int verknüpft[]=verknüpfe_hardwareid&keyfile_irgendwie();
... junkcode ...
int mycode[]=copy(verknüpft,10,100); //nutze Keystellen ab 10 bis 100 als Code
int mydata[]=hardwareid;
myVMInterpreter(mycode,mydata);
... junkcode...
interne_variablen_für_sonstwas=data[X]; //initialisiere die vom Programm benutzte Variablen mit berechnetem Code
interne_variablen_für_sonstwas=data[Y];
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. |
| | |
| | #12 (permalink) |
| Themenstarter Registriert seit: 03.02.09 ![]() Likes: 0 | Danke nochmals für dein pseudo Code und Erklärungen. Was mir noch nicht ganz klar ist: 1. Bei deinem Beispiel des Interprets, woran erkennt man denn 2 Opcodes-Anweisungen? 2. Wenn ich das richtig erkenne nutz du das Data-Array als Wert übergabe, also die Werte mit denen gerechnet werden soll? oder dienen die Code: CONST VAR1=0 CONST VAR2=1 CONST VAR3=2 CONST VAR4=3 Wozu soll den der "ILOAD" gut sein? Ich werde mir jetzt mal so ein Teil bauen, das Grund gerüst steht schon. Wie gross sollte den ein Befehlsatz ca. sein? Gruss |
| | |
| | #13 (permalink) | |||
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() Likes: 156 | Zitat:
Man entwirft ja zuerst den Befehlsatz - und da überlegt man sich, ob die Anweidung einen oder zwei Opcodes haben soll. Das erste ist immer eine Anweisungs(ID) und das zweite dann je nach dem - Adresse, Variable usw. Wenn man den Interpreter umsetzt, schaut man sich den eigenen Entwurf an Code: IF (Opcode1=JMP) zu Zitat:
Gemeint ist hier sowas wie in C: Code: #define VAR1 0 #define VAR2 1 Die Variablennummer wird wie erwähnt (bei der einfachsten implementierung) als Indexwert für das Array genutzt. Du kannst natürlich auch alle Werte im Codearray haben - musst nur dann beim Schreiben des "ASMCodes" aufpassen und Platz freilassen. Oder auch direkt vom Interpreter aus in "normale" Variablen schreiben. Zitat:
wäre sowas: Code: Else IF (opcode1==Load)
{
int index=opcode2;
int wert=data[index];
Stack.push(wert);
IP=IP+2; //ist eine 2-Opcodes Anweidung
}
Else IF (opcode1==ILoad)
{
int wert=opcode2;
Stack.push(wert);
IP=IP+2; //ist eine 2-Opcodes Anweidung
} Wie groß der Befehlssatz sein soll - ehrlich gesagt, keine Ahnung ![]() Aber ich meine, größtenteils alle nötigen Befehle schon abgedeckt zu haben. Du kannst gerne noch nach MIC1 Befehlssatz suchen (damit habe ich Stackmachines gelernt und die Befehlsnamen stammen auch aus dem Raum). http://www.ontko.com/mic1/ijvm.conf damit sollte man so ziemlich alles was umsetzen können. Wobei Du eher auf die eigenen Bedürfnisse achten solltest (die VM ist ja notfalls leicht erweiterbar). Naja, beachten sollte man dann noch, dass NET eventuell die Konstantennamen "schlauerweise" beibehält - also unbedingt mal prüfen. Und dass Du nach der Testfase den Interpreter ein bisschen "manuell" Obfuscatest - sprich statt Code: IF (opcode1==ADD)
{
int var1=Stack.pop;
int var2=Stack.pop;
int result=var1+var2;
Stack.push(result);
IP=IP+1; //da es nur eine 1-Opcode Anweisung war, InstrucitonPointer nur um 1 weiterbewegen
} Code: IF (opcode1==ADD)
{
int var1=Stack.pop;
int var2=Stack.pop;
String junk=var1.toString;
int junk2=junk[i]&var2;
Stack.push(junk2);
...junk...
junk[y]=var2*junk2;
Stack.pop;
... usw...
int result=var1+var2;
Stack.push(result);
IP=IP+1; //da es nur eine 1-Opcode Anweisung war, InstrucitonPointer nur um 1 weiterbewegen
} Eventuell lohnt es sich auch einen "ASM Code2Array" Übersetzer zu schreiben. Vor allem weil man dann Labels nutzen kann und nicht manuell die Elemente abzählen muss.
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. | |||
| | |
| | #14 (permalink) |
| Themenstarter Registriert seit: 03.02.09 ![]() Likes: 0 | Hy CWD Danke nochmals für deinen Post. Nun, ich habe das ganze mal in richtigen Code übersetzt. Mich würde interessieren ob ich mit meinem Prototype aufm Holz Weg bin, oder richtig liege: Code:
Shared RET As Integer 'Rückgabe
Sub
Dim Codebase() As String = _
{"ADD", "5", "ADD", "10", "MUL", "RET"} 'CODE
Call XIL(Codebase) 'VM starten
End Sub
Friend Sub XIL(ByVal Codefile() As String)
Dim IP As Integer = 0
Dim Stack(UBound(Codefile)) As Integer
Dim intStack As Integer = 1
While (IP < UBound(Codefile) + 1)
Select Case Codefile(IP)
Case "ADD"
Stack(intStack) = CInt(Codefile(IP + 1))
intStack += 1
IP += 2
Case "MUL"
Stack(intStack) = Stack(intStack - 2) * Stack(intStack - 1)
intStack += 1
IP += 1
Case "RET"
RET = Stack(intStack - 1)
IP += 1
End Select
End While
End Sub Lg |
| | |
| | #15 (permalink) |
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() Likes: 156 | Naja, ich glaube, das Konzept der Stackmaschine hast Du missverstanden Irgendwie wird nämlich der Stack bei Dir immer nur erhöht. Das Prinzip der Stackmaschine ist immer, Operatoren auf dem Stack zu lagern, aber diese auch nach gebrauch wieder vom Stack zu popen. Bps Addition: Code: [stack] 2 2 [Stackend] ADD holt sich seine Parameter von Stack: Par1=Stack.pop [stack] 2 [stackend] Par2=stack.pop [stack] [stackend] pusht Ergebnis wieder auf den Stack: stack.push (2+2) [stack] 4 [stackend] Code: Case "ADD"
Stack(intStack) = CInt(Codefile(IP + 1)) //Weise den zweiten Opcode auf den Stack?
intStack += 1
IP += 2
Case "MUL"
Stack(intStack) = Stack(intStack - 2) * Stack(intStack - 1) //alte Operatoren bleiben auf dem Stack
intStack += 1
IP += 1 Dein MUL ist allerdings so nicht korrekt (bzw:bildet einen Memoryleak, da hier nie der Stackspeicher freigegeben wird Er sollte den Speicher für die beiden Operanden freigeben (beide "popen") und nur das Ergebnis drauf pushen: Code:
Case "MUL"
int result= Stack(intStack - 2) * Stack(intStack - 1)
intStack =intStack-2 'beide Operanden freigeben
Stack(intStack)=result 'Ergebis speichern
intStack=intStack+1 'Stackpointer auf den nächsten freiverfügbaren Platz
IP += 1
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. |
| | |
![]() |
| | |
| |
| Themen-Optionen | |
| Ansicht | |
| |
Ähnliche Themen | ||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Schutz vor batch | BlackPanther | (In)security allgemein | 11 | 23.12.07 01:56 |
| schutz an fremdem pc? | x4nny | (In)security allgemein | 6 | 26.07.07 20:46 |
| schutz vor xxx sites | einstein | Windows | 8 | 12.09.05 23:14 |
| IRC-Schutz? | ????? | (In)security allgemein | 16 | 30.06.04 18:57 |
| Sub7-Schutz?!?? | outbox | (In)security allgemein | 20 | 19.08.03 17:13 |