Embedding Code und die Gefahr von Raubkopien

Informationen darüber, wie Sie Code ohne JMP in den Codeabschnitt einbetten und unsichtbar bleiben können, wenn Sie nicht gründlich disassemblierten Code studieren. Wen kümmert es, bitte, unter der Katze.

Ich entwickle einen C-Disassembler für Linux. Über mich selbst würde ich nicht sagen, dass ich ein professioneller Programmierer bin. Ich arbeite nicht als Programmierer, aber was ich gelernt habe, ist entweder Bücher zu lesen oder es selbst zu erfinden oder den Quellcode anderer Programme zu studieren. Wenn Ihnen der Code kindisch vorkommt, schwören Sie nicht.

Für den Anfang habe ich einen Disassembler gemacht und mein Code sieht nun so aus, dh ein Byte wird gelesen und an die gewünschte Funktion übergeben.

void disasm_intel ( unsigned char *ptr, int size, int byte_order, int show ) { show_asm = show; virt = global_virt_text; unsigned char *start = ptr; start_op = ptr; for ( int index = 0; index < size; index++ ) { if ( show_asm == TRUE ) printf ( "%lx: ", virt ); switch ( *ptr ) { case 0x30: intel_opcode_1_0x30 ( &ptr, &index, byte_order ); break; case 0x31: intel_opcode_1_0x31 ( &ptr, &index, byte_order ); break; case 0x66: intel_opcode_1_0x66 ( &ptr, &index, byte_order ); break; case 0x67: intel_opcode_1_0x67 ( &ptr, &index, byte_order ); break; case 0x83: intel_opcode_1_0x83 ( &ptr, &index, byte_order ); break; case 0x88: intel_opcode_1_0x88 ( &ptr, &index, byte_order ); break; // mov register to register byte case 0x89: intel_opcode_1_0x89 ( &ptr, &index, byte_order ); break; case 0x8a: intel_opcode_1_0x8a ( &ptr, &index, byte_order ); break; case 0x8b: intel_opcode_1_0x8b ( &ptr, &index, byte_order ); break; // mov esp, %x : mov ebp, %x case 0x8d: intel_opcode_1_0x8d ( &ptr, &index, byte_order ); break; // lea case 0xb0: intel_opcode_1_0xb0 ( &ptr, &index ); break; // mov al, %x case 0xb1: intel_opcode_1_0xb1 ( &ptr, &index ); break; // mov cl, %x case 0xb2: intel_opcode_1_0xb2 ( &ptr, &index ); break; // mov dl, %x case 0xb3: intel_opcode_1_0xb3 ( &ptr, &index ); break; // mov bl, %x case 0xb4: intel_opcode_1_0xb4 ( &ptr, &index ); break; // mov ah, %x case 0xb5: intel_opcode_1_0xb5 ( &ptr, &index ); break; // mov ch, %x case 0xb6: intel_opcode_1_0xb6 ( &ptr, &index ); break; // mov dh, %x case 0xb7: intel_opcode_1_0xb7 ( &ptr, &index ); break; // mov bh, %x case 0xb8: intel_opcode_1_0xb8 ( &ptr, &index, byte_order ); break; // mov eax, %x case 0xb9: intel_opcode_1_0xb9 ( &ptr, &index, byte_order ); break; // mov ecx, %x case 0xba: intel_opcode_1_0xba ( &ptr, &index, byte_order ); break; // mov edx, %x case 0xbb: intel_opcode_1_0xbb ( &ptr, &index, byte_order ); break; // mov ebx, %x case 0xbe: intel_opcode_1_0xbe ( &ptr, &index, byte_order ); break; // mov esi, %x case 0xbf: intel_opcode_1_0xbf ( &ptr, &index, byte_order ); break; // mov edi, %x case 0xc3: intel_opcode_1_0xc3 ( ); break; // ret case 0xcd: intel_opcode_1_0xcd ( &ptr, &index ); break; // int 0x%x } ptr++; virt += ptr - start; start = ptr; start_op = ptr; } show_asm = FALSE; } 

Und solche Funktionen sind schon ein Haufen. An einigen Stellen machte ich Kommentare, um die Verknüpfung von Maschinenanweisungen zu erfassen und später vielleicht einen kompetenteren Zerleger zu machen. Aber in dieser Form, in der ich jetzt den Code habe, kann ich problemlos alle Bedingungen für jeden Bediener einstellen.

Und als ich dies tat, hatte ich eine Idee, ist es möglich, Code in der Mitte des Codeabschnitts hinzuzufügen? Es stellt sich heraus, dass Sie können, aber auf jeden Fall? Bisher verwende ich zum Hinzufügen von Code bereits vorbereitete Maschinencodes. Wenn ich später kann, dann mache ich den Assembler-Übersetzer in Maschinencode, um Code hinzuzufügen, war bequemer. In meinem Fall müssen Sie den Offset im Codeabschnitt angeben, und die Bytes werden an die richtige Stelle kopiert. Es gab auch ein bestimmtes Problem: die Adressierung im Speicher. Ich habe dem Befehl lea einen Code hinzugefügt, der die erforderlichen Daten in der Struktur speichert. Wenn Sie neue Operatoren in den Codeabschnitt einfügen, werden alle Offsets so ausgerichtet, dass sie Daten bei neuen Offsets anzeigen. Nun, es ist nicht sehr schwierig, wenn Sie den Code eingefügt haben, der Codeabschnitt um die gleiche Anzahl von Bytes erhöht wurde und alle anderen Abschnitte nach dem Codeabschnitt bereits neue Offsets enthalten. Ich habe es so gemacht, dass es Unterschiede gibt, wo Sie den Code einfügen, alle Offsets funktionieren korrekt. Dann stellte sich das Problem, dass bei der Adressierung z

 mov eax, [eax + eax + 0x100] 

Tatsache ist, dass es bei einer solchen Adressierung ebp geben kann und auf den Stapel und nicht auf einen anderen Abschnitt verweist. Ich habe beschlossen, dies so zu gestalten, dass, wenn die Adresse auf den Datenabschnitt zeigt, die Offsets beim Einfügen des Codes berücksichtigt werden und wenn sie auf den Stapel zeigt, dh nicht auf die Adresse im Datenabschnitt, die Offsets nicht berücksichtigt werden.

Und auf diese Weise können Angreifer ausnutzen. Schließlich kann böswilliger Code beispielsweise zu Beginn einer Funktion in das Programm eingefügt werden, sodass ein Fork-Child erstellt und eine spezielle Datei heruntergeladen wird. Unter Linux ist dies problemlos möglich. Tatsächlich gibt es in / usr / include eine Datei mit allen Systemfunktionen des Betriebssystems. Das heißt, Sie können den Netzwerkteil verwenden, auch wenn das Programm keine Netzwerkfunktionen hat. Ich weiß nicht, wie es in Windows geht, aber ich werde später versuchen, Arbeit im Pe-Format hinzuzufügen. Es kann sich herausstellen, dasselbe wie unter Linux zu tun. Bisher habe ich eine Konsolenversion. Aber dann habe ich vor, auf GTK zu tun.

Vielen Dank, dass Sie sich mit meinem Artikel beschäftigt haben.

Source: https://habr.com/ru/post/de477590/


All Articles