[LINUX] Segfo mit 0 Zeichen mit gcc

Einführung

[Beliebtes Material](https://qiita.com/search?q=%E3%82%BB%E3%82%B0%E3%83%95%E3%82%A9%E3%82%89%E3 Es ist eine Huckepackfahrt auf% 81% 9B% E3% 82% 8B).

Schnelle Demonstration

Ich verwende x86-64 Ubuntu 19.04 + gcc 8.3.0 als Betriebsüberprüfungsumgebung.

Bauen und ausführen


$ gcc -xc /dev/null -nostdlib -Wl,-e0xfa -Wl,-Ttext=0x2504890000 -zexecstack && ./a.out
Segmentation fault (core dumped)
$ 

Kommentar

Versuchen Sie zunächst, das kleinste C-Programm zu kompilieren, das keine Warnung ausgibt.

int main(){}

Da es schwierig ist, den Quellcode vorzubereiten, verwende ich den Befehl echo, um mit | eine Verbindung zum Compiler herzustellen und ihn direkt als Standardeingabe einzugeben. Da der Compiler den Typ der Eingabedatei nicht durch die Erweiterung der Eingabedatei bestimmen kann, verwenden Sie die Befehlszeilenoption "-xc", um anzugeben, dass es sich um eine C-Quelle handelt.

$ echo 'int main(){}' | gcc -xc - && ls -l a.out
-rwxr-xr-x 1 fujita fujita 16344 Jul  8 22:31 a.out
$ 

Eine ausführbare Datei von ca. 16 KB wurde erstellt. Wenn ich das generierte a.out an den Befehl readelf weitergebe

$ readelf -S a.out
There are 28 section headers, starting at offset 0x38d8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         00000000000002a8  000002a8
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.build-i NOTE             00000000000002c4  000002c4
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .note.ABI-tag     NOTE             00000000000002e8  000002e8
       0000000000000020  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000308  00000308
       0000000000000024  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           0000000000000330  00000330
       0000000000000090  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000000003c0  000003c0
       000000000000007d  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000000043e  0000043e
       000000000000000c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000000450  00000450
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000470  00000470
       00000000000000c0  0000000000000018   A       5     0     8
  [10] .init             PROGBITS         0000000000001000  00001000
       0000000000000017  0000000000000000  AX       0     0     4
  [11] .plt              PROGBITS         0000000000001020  00001020
       0000000000000010  0000000000000010  AX       0     0     16
  [12] .plt.got          PROGBITS         0000000000001030  00001030
       0000000000000008  0000000000000008  AX       0     0     8
  [13] .text             PROGBITS         0000000000001040  00001040
       0000000000000151  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         0000000000001194  00001194
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000004  0000000000000004  AM       0     0     4
  [16] .eh_frame_hdr     PROGBITS         0000000000002004  00002004
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000002040  00002040
       0000000000000108  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000003df0  00002df0
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000003df8  00002df8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          0000000000003e00  00002e00
       00000000000001c0  0000000000000010  WA       6     0     8
  [21] .got              PROGBITS         0000000000003fc0  00002fc0
       0000000000000040  0000000000000008  WA       0     0     8
  [22] .data             PROGBITS         0000000000004000  00003000
       0000000000000010  0000000000000000  WA       0     0     8
  [23] .bss              NOBITS           0000000000004010  00003010
       0000000000000008  0000000000000000  WA       0     0     1
  [24] .comment          PROGBITS         0000000000000000  00003010
       0000000000000023  0000000000000001  MS       0     0     1
  [25] .symtab           SYMTAB           0000000000000000  00003038
       00000000000005b8  0000000000000018          26    43     8
  [26] .strtab           STRTAB           0000000000000000  000035f0
       00000000000001e9  0000000000000000           0     0     1
  [27] .shstrtab         STRTAB           0000000000000000  000037d9
       00000000000000f9  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)
$ 

Sie können sehen, dass es viele Abschnitte enthält.

Versuchen Sie dann, die 0-Zeichen-Quelle zu kompilieren. Verwenden Sie / dev / null anstelle der Quelldatei, die beim Lesen immer eine EOF zurückgibt. Sie benötigen nicht einmal einen Start, um main aufzurufen. Geben Sie daher die Option \ `-nostdlib' an. Wenn Sie dies verwenden, wird die Standard-Startadresse für die Programmausführung _start nicht gefunden und der Linker warnt Sie. Verwenden Sie daher die Option -Wl, -e0, um 0 als Startadresse für die temporäre Ausführung anzugeben.

$ gcc -xc /dev/null -nostdlib -Wl,-e0 && ls -l a.out
-rwxr-xr-x 1 fujita fujita 9512 Jul  8 22:35 a.out
$ 

Eine ausführbare Datei von ca. 9,5 KB wurde erstellt. Wenn Sie dies dem Befehl readelf zuführen

$ readelf -S a.out
There are 12 section headers, starting at offset 0x2228:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000200  00000200
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.build-i NOTE             000000000000021c  0000021c
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .gnu.hash         GNU_HASH         0000000000000240  00000240
       000000000000001c  0000000000000000   A       4     0     8
  [ 4] .dynsym           DYNSYM           0000000000000260  00000260
       0000000000000018  0000000000000018   A       5     1     8
  [ 5] .dynstr           STRTAB           0000000000000278  00000278
       0000000000000001  0000000000000000   A       0     0     1
  [ 6] .eh_frame         PROGBITS         0000000000001000  00001000
       0000000000000000  0000000000000000   A       0     0     8
  [ 7] .dynamic          DYNAMIC          0000000000001f20  00001f20
       00000000000000e0  0000000000000010  WA       5     0     8
  [ 8] .comment          PROGBITS         0000000000000000  00002000
       0000000000000023  0000000000000001  MS       0     0     1
  [ 9] .symtab           SYMTAB           0000000000000000  00002028
       0000000000000168  0000000000000018          10    12     8
  [10] .strtab           STRTAB           0000000000000000  00002190
       0000000000000027  0000000000000000           0     0     1
  [11] .shstrtab         STRTAB           0000000000000000  000021b7
       000000000000006c  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)
$ 

Sie können sehen, dass die ausführbare Datei immer noch mehrere Abschnitte enthält, jedoch weniger als das kleinste Hauptbeispiel.

Einige Befehlszeilenoptionen für den Linker geben die Startadresse des Abschnitts an. Versuchen wir, den Wert 0x123456789abcdef0 als Startadresse des Textabschnitts anzugeben, der nicht in der Liste der obigen Abschnitte enthalten ist. Die folgende Befehlszeilenoption "-Wl, -Ttext = 0x123456789abcdef0" ist anwendbar.

$ gcc -xc /dev/null -nostdlib -Wl,-e0 -Wl,-Ttext=0x123456789abcdef0 && ls -l a.out
-rwxr-xr-x 1 fujita fujita 9512 Jul  8 22:41 a.out
$ readelf -S a.out
There are 12 section headers, starting at offset 0x2228:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.build-i NOTE             0000000000000254  00000254
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .gnu.hash         GNU_HASH         0000000000000278  00000278
       000000000000001c  0000000000000000   A       4     0     8
  [ 4] .dynsym           DYNSYM           0000000000000298  00000298
       0000000000000018  0000000000000018   A       5     1     8
  [ 5] .dynstr           STRTAB           00000000000002b0  000002b0
       0000000000000001  0000000000000000   A       0     0     1
  [ 6] .eh_frame         PROGBITS         123456789abce000  00001000
       0000000000000000  0000000000000000   A       0     0     8
  [ 7] .dynamic          DYNAMIC          123456789abcef20  00001f20
       00000000000000e0  0000000000000010  WA       5     0     8
  [ 8] .comment          PROGBITS         0000000000000000  00002000
       0000000000000023  0000000000000001  MS       0     0     1
  [ 9] .symtab           SYMTAB           0000000000000000  00002028
       0000000000000168  0000000000000018          10    12     8
  [10] .strtab           STRTAB           0000000000000000  00002190
       0000000000000027  0000000000000000           0     0     1
  [11] .shstrtab         STRTAB           0000000000000000  000021b7
       000000000000006c  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)
$ 

Ich weiß nicht, was der Abschnitt ist, aber Sie können sehen, dass der Abschnitt ".eh_frame" betroffen ist und eine ähnliche Adresse hat. Es scheint, dass die unteren 16 Bits der Adresse, die der Wert des Versatzelements ist, von "def0" bis "e000" ausgerichtet sind. Diese Abschnittsinformationen sollten in der ausführbaren Datei a.out enthalten sein. Versuchen Sie einen hexadezimalen Speicherauszug mit dem Befehl hexdump

$ hexdump -C a.out | head -24
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  03 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  40 00 00 00 00 00 00 00  28 22 00 00 00 00 00 00  |@.......("......|
00000030  00 00 00 00 40 00 38 00  09 00 40 00 0c 00 0b 00  |[email protected]...@.....|
00000040  06 00 00 00 04 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000050  40 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |@.......@.......|
00000060  f8 01 00 00 00 00 00 00  f8 01 00 00 00 00 00 00  |................|
00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |................|
00000080  38 02 00 00 00 00 00 00  38 02 00 00 00 00 00 00  |8.......8.......|
00000090  38 02 00 00 00 00 00 00  1c 00 00 00 00 00 00 00  |8...............|
000000a0  1c 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
000000b0  01 00 00 00 04 00 00 00  00 00 00 00 00 00 00 00  |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000d0  b1 02 00 00 00 00 00 00  b1 02 00 00 00 00 00 00  |................|
000000e0  00 10 00 00 00 00 00 00  01 00 00 00 04 00 00 00  |................|
000000f0  00 10 00 00 00 00 00 00  00 e0 bc 9a 78 56 34 12  |............xV4.|
00000100  00 e0 bc 9a 78 56 34 12  00 00 00 00 00 00 00 00  |....xV4.........|
00000110  00 00 00 00 00 00 00 00  00 10 00 00 00 00 00 00  |................|
00000120  01 00 00 00 06 00 00 00  20 1f 00 00 00 00 00 00  |........ .......|
00000130  20 ef bc 9a 78 56 34 12  20 ef bc 9a 78 56 34 12  | ...xV4. ...xV4.|
00000140  e0 00 00 00 00 00 00 00  e0 00 00 00 00 00 00 00  |................|
00000150  00 10 00 00 00 00 00 00  02 00 00 00 06 00 00 00  |................|
00000160  20 1f 00 00 00 00 00 00  20 ef bc 9a 78 56 34 12  | ....... ...xV4.|
00000170  20 ef bc 9a 78 56 34 12  e0 00 00 00 00 00 00 00  | ...xV4.........|
$ 

Sie können sehen, dass 8 Bytes von der Adresse 000000f8, die die Startadresse von ".eh_frame" zu sein scheint, in Little Endian gespeichert sind. Bei Verwendung von \ -Wl, -Ttext = 0xXXXXXXXXXXXX0000 'wird unter der Annahme, dass die unteren 16 Bits ignoriert werden, um ein Ausrichten der Adressen zu vermeiden, jede Bytezeichenfolge von Adresse 000000fa als Adresse von .eh_framein die Ausführungsdatei eingebettet. Ich denke ich kann es schaffen. Geben Sie als Test \-Wl, -Ttext = 0x2504890000' an und platzieren Sie 89 04 25 von der Adresse 000000fa. Geben Sie die 000000fa-Adresse als Ausführungsadresse mit \ -e0xfa an und geben Sie außerdem -zexecstack an, um die Ausführung in anderen Bereichen als dem Codebereich zu ermöglichen.

$ gcc -xc /dev/null -nostdlib -Wl,-e0xfa -Wl,-Ttext=0x2504890000 -zexecstack && ls -l a.out
-rwxr-xr-x 1 fujita fujita 9512 Jul  8 22:49 a.out
$ ./a.out
Segmentation fault (core dumped)
$ 

Ich konnte bestätigen, dass segfo durch Ausführen des generierten a.out generiert wird. Verwenden Sie den Befehl objdump, um die Anweisungen anzuzeigen, die in die ausführbare Datei von Adresse 000000fa eingebettet sind.

$ objdump -D -bbinary -mi386 -Mintel,x86-64 --start-address=0xfa a.out | head -8

a.out:     file format binary


Disassembly of section .data:

000000fa <.data+0xfa>:
      fa:	89 04 25 00 00 00 00 	mov    DWORD PTR ds:0x0,eax
$ 

Der Inhalt besteht darin, den Wert des EAX-Registers auf die Adresse 0 zu schreiben. Wenn Sie dies versuchen, können Sie feststellen, dass der Betriebssystemschutz in Segfo beendet wurde.

Mit dem oben genannten wurde der Titel "Segfo mit 0 Zeichen mit gcc" erreicht. Darüber hinaus ist es möglich, einen anderen Fehler als Segfo auszugeben, indem der Inhalt von \ `-Wl, -Ttext = 0xXXXXXXXXXXXX0000 'erstellt wird.

Rufen Sie den Software-Interrupt INT3 zum Debuggen auf


$ gcc -xc /dev/null -nostdlib -Wl,-e0xfa -Wl,-Ttext=0xcc0000 -zexecstack && ls -l a.out
-rwxr-xr-x 1 fujita fujita 9512 Jul  8 22:55 a.out
$ objdump -D -bbinary -mi386 -Mintel,x86-64 --start-address=0xfa a.out | head -8

a.out:     file format binary


Disassembly of section .data:

000000fa <.data+0xfa>:
      fa:	cc                   	int3   
$ ./a.out
Trace/breakpoint trap (core dumped)
$ 

Führen Sie UD2 aus, das als unzulässige Anweisung reserviert ist


$ gcc -xc /dev/null -nostdlib -Wl,-e0xfa -Wl,-Ttext=0x0b0f0000 -zexecstack && ls -l a.out
-rwxr-xr-x 1 fujita fujita 9512 Jul  8 22:56 a.out
$ objdump -D -bbinary -mi386 -Mintel,x86-64 --start-address=0xfa a.out | head -8

a.out:     file format binary


Disassembly of section .data:

000000fa <.data+0xfa>:
      fa:	0f 0b                	ud2    
$ ./a.out
Illegal instruction (core dumped)
$ 

Teilen Sie den Wert des AX-Registers durch den Inhalt der Adresse 0x100, in der 0 gespeichert ist, und erzeugen Sie einen 0-Teilungsfehler.


$ gcc -xc /dev/null -nostdlib -Wl,-e0xfa -Wl,-Ttext=0x35f60000 -zexecstack && ls -l a.out
-rwxr-xr-x 1 fujita fujita 9512 Jul  8 22:58 a.out
$ objdump -D -bbinary -mi386 -Mintel,x86-64 --start-address=0xfa a.out | head -9

a.out:     file format binary


Disassembly of section .data:

000000fa <.data+0xfa>:
      fa:	f6 35 00 00 00 00    	div    BYTE PTR [rip+0x0]        # 0x100
     100:	00 00                	add    BYTE PTR [rax],al
$ ./a.out
Floating point exception (core dumped)
$ 

Es macht Spaß, verschiedene Dinge auszuprobieren.

abschließend

Das Ende.

Recommended Posts

Segfo mit 0 Zeichen mit gcc
Segfo Python mit 33 Zeichen
Segfo mit 16 Zeichen in C-Sprache
[Python] Einzeilige Starlin-Sortierung mit 50 Zeichen
[Anmerkung] Japanische Schriftzeichen sind mit Atom-Runner verstümmelt
Geben Sie Farbzeichen mit Python zu hübsch aus
Gesichtserkennung von Anime-Charakteren mit Keras
Zählen Sie die Anzahl der Zeichen mit Echo