私はLinuxがメモリを割り当てて保護する方法を試しています。
いくつかの実験では、Cで小さなプログラムを作成しました。
#include <stdio.h>
int gv=10;
int main(){
char *v=(char*)0x601000;//0x601030
printf("gv=%p\n", &gv);
scanf("%s", v);
printf("You gave=%s\n", v);
}
コンパイル(部分relro)後、readelf -t a.out
次を返します。
There are 30 section headers, starting at offset 0x1a18:
Section Headers:
[Nr] Name
Type Address Offset Link
Size EntSize Info Align
Flags
[ 0]
NULL NULL 0000000000000000 0000000000000000 0
0000000000000000 0000000000000000 0 0
[0000000000000000]:
[ 1] .interp
PROGBITS PROGBITS 0000000000400238 0000000000000238 0
000000000000001c 0000000000000000 0 1
[0000000000000002]: ALLOC
[ 2] .note.ABI-tag
NOTE NOTE 0000000000400254 0000000000000254 0
0000000000000020 0000000000000000 0 4
[0000000000000002]: ALLOC
[ 3] .note.gnu.build-id
NOTE NOTE 0000000000400274 0000000000000274 0
0000000000000024 0000000000000000 0 4
[0000000000000002]: ALLOC
[ 4] .gnu.hash
GNU_HASH GNU_HASH 0000000000400298 0000000000000298 5
000000000000001c 0000000000000000 0 8
[0000000000000002]: ALLOC
[ 5] .dynsym
DYNSYM DYNSYM 00000000004002b8 00000000000002b8 6
0000000000000078 0000000000000018 1 8
[0000000000000002]: ALLOC
[ 6] .dynstr
STRTAB STRTAB 0000000000400330 0000000000000330 0
0000000000000058 0000000000000000 0 1
[0000000000000002]: ALLOC
[ 7] .gnu.version
VERSYM VERSYM 0000000000400388 0000000000000388 5
000000000000000a 0000000000000002 0 2
[0000000000000002]: ALLOC
[ 8] .gnu.version_r
VERNEED VERNEED 0000000000400398 0000000000000398 6
0000000000000030 0000000000000000 1 8
[0000000000000002]: ALLOC
[ 9] .rela.dyn
RELA RELA 00000000004003c8 00000000000003c8 5
0000000000000018 0000000000000018 0 8
[0000000000000002]: ALLOC
[10] .rela.plt
RELA RELA 00000000004003e0 00000000000003e0 5
0000000000000060 0000000000000018 12 8
[0000000000000042]: ALLOC, INFO LINK
[11] .init
PROGBITS PROGBITS 0000000000400440 0000000000000440 0
000000000000001a 0000000000000000 0 4
[0000000000000006]: ALLOC, EXEC
[12] .plt
PROGBITS PROGBITS 0000000000400460 0000000000000460 0
0000000000000050 0000000000000010 0 16
[0000000000000006]: ALLOC, EXEC
[13] .text
PROGBITS PROGBITS 00000000004004b0 00000000000004b0 0
00000000000001b2 0000000000000000 0 16
[0000000000000006]: ALLOC, EXEC
[14] .fini
PROGBITS PROGBITS 0000000000400664 0000000000000664 0
0000000000000009 0000000000000000 0 4
[0000000000000006]: ALLOC, EXEC
[15] .rodata
PROGBITS PROGBITS 0000000000400670 0000000000000670 0
0000000000000029 0000000000000000 0 8
[0000000000000002]: ALLOC
[16] .eh_frame_hdr
PROGBITS PROGBITS 000000000040069c 000000000000069c 0
0000000000000034 0000000000000000 0 4
[0000000000000002]: ALLOC
[17] .eh_frame
PROGBITS PROGBITS 00000000004006d0 00000000000006d0 0
00000000000000f4 0000000000000000 0 8
[0000000000000002]: ALLOC
[18] .init_array
INIT_ARRAY INIT_ARRAY 0000000000600e10 0000000000000e10 0
0000000000000008 0000000000000000 0 8
[0000000000000003]: WRITE, ALLOC
[19] .fini_array
FINI_ARRAY FINI_ARRAY 0000000000600e18 0000000000000e18 0
0000000000000008 0000000000000000 0 8
[0000000000000003]: WRITE, ALLOC
[20] .jcr
PROGBITS PROGBITS 0000000000600e20 0000000000000e20 0
0000000000000008 0000000000000000 0 8
[0000000000000003]: WRITE, ALLOC
[21] .dynamic
DYNAMIC DYNAMIC 0000000000600e28 0000000000000e28 6
00000000000001d0 0000000000000010 0 8
[0000000000000003]: WRITE, ALLOC
[22] .got
PROGBITS PROGBITS 0000000000600ff8 0000000000000ff8 0
0000000000000008 0000000000000008 0 8
[0000000000000003]: WRITE, ALLOC
[23] .got.plt
PROGBITS PROGBITS 0000000000601000 0000000000001000 0
0000000000000038 0000000000000008 0 8
[0000000000000003]: WRITE, ALLOC
[24] .data
PROGBITS PROGBITS 0000000000601038 0000000000001038 0
0000000000000008 0000000000000000 0 4
[0000000000000003]: WRITE, ALLOC
[25] .bss
NOBITS NOBITS 0000000000601040 0000000000001040 0
0000000000000008 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[26] .comment
PROGBITS PROGBITS 0000000000000000 0000000000001040 0
000000000000002d 0000000000000001 0 1
[0000000000000030]: MERGE, STRINGS
[27] .shstrtab
STRTAB STRTAB 0000000000000000 000000000000106d 0
0000000000000108 0000000000000000 0 1
[0000000000000000]:
[28] .symtab
SYMTAB SYMTAB 0000000000000000 0000000000001178 29
0000000000000648 0000000000000018 45 8
[0000000000000000]:
[29] .strtab
STRTAB STRTAB 0000000000000000 00000000000017c0 0
0000000000000255 0000000000000000 0 1
[0000000000000000]:
そしてreadelf -l a.out
リターン:
Elf file type is EXEC (Executable file)
Entry point 0x4004b0
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000007c4 0x00000000000007c4 R E 200000
LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x0000000000000230 0x0000000000000238 RW 200000
DYNAMIC 0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
0x00000000000001d0 0x00000000000001d0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000000069c 0x000000000040069c 0x000000000040069c
0x0000000000000034 0x0000000000000034 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x00000000000001f0 0x00000000000001f0 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
このプログラムの1つのインスタンスでは、gdbを介して実行されたときに/proc/self/mapsが次を返します。
00400000-00401000 r-xp 00000000 00:30f 2262070116 .../a.out
00600000-00601000 r--p 00000000 00:30f 2262070116 .../a.out
00601000-00602000 rw-p 00001000 00:30f 2262070116 .../a.out
7ffff7a18000-7ffff7bd0000 r-xp 00000000 fd:00 137613 /usr/lib64/libc-2.17.so
7ffff7bd0000-7ffff7dd0000 ---p 001b8000 fd:00 137613 /usr/lib64/libc-2.17.so
7ffff7dd0000-7ffff7dd4000 r--p 001b8000 fd:00 137613 /usr/lib64/libc-2.17.so
7ffff7dd4000-7ffff7dd6000 rw-p 001bc000 fd:00 137613 /usr/lib64/libc-2.17.so
7ffff7dd6000-7ffff7ddb000 rw-p 00000000 00:00 0
7ffff7ddb000-7ffff7dfc000 r-xp 00000000 fd:00 137605 /usr/lib64/ld-2.17.so
7ffff7fbd000-7ffff7fc0000 rw-p 00000000 00:00 0
7ffff7ff7000-7ffff7ffa000 rw-p 00000000 00:00 0
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00021000 fd:00 137605 /usr/lib64/ld-2.17.so
7ffff7ffd000-7ffff7ffe000 rw-p 00022000 fd:00 137605 /usr/lib64/ld-2.17.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
これを念頭に置いて、0x601000-0x602000の間のメモリ領域が書き込み可能であると予想します。ただし、24文字以上の文字でプログラムを実行すると競合が発生しますv=0x601000
。
v
0x6010に変更すると20その後、0x1000と入力した後、プログラムがクラッシュしました。0x20数値。
この行動をどのように説明すべきでしょうか?ページの粒度でメモリ保護が施行されていませんか? 0x601018と0x601020の間のメモリ領域はある程度読み取り専用であるようです。
問題が.got.pltセクション(0x601000、サイズ0x38にロードされている)にあるようですが、正確には何ですか?
編集する:
出力はld --verbose | fgrep -A 3 -B 3 -i relro
次のとおりです
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
.data :
これはRELROがそれに関連していることを私たちに伝えますが、ページサイズの粒度からメモリを保護する必要はありませんか?
答え1
.got.plt
通貨を含むがこれに限定されない図書館通貨には不可欠ですlibc
。このセクションに違反すると、すべてのlibc呼び出しが禁止されます。
経由でバイナリを実行している場合、gdb
segfaultは発生しないでください。破損した部分で動的呼び出しを行うことができないため、scanf
segfaultが発生する必要があります。printf@plt
.got.plt
それでも、非ダイナミックコールとシステムコールができる必要があります。
#include <stdio.h>
__asm(
"finish:"
"mov $60, %rax\n"
"mov $42, %rdi\n"
"syscall\n"
);
int gv=10;
int main(){
char *v=(char*)0x601000;//0x601030
printf("gv=%p\n", &gv);
scanf("%s", v);
_Noreturn void finish(void);
finish();
//should exit with 42
printf("You gave=%s\n", v);
}
答え2
問題を発見しました。この領域をゴミで埋めることで断片化を起こした最終呼び出しであるprintfのget項目を上書きしました。
したがって、コードを次のように変更すると:
#include <stdio.h>
int gv=10;
int main(){
char *v=(char*)0x601000;
printf("gv=%p\n", &gv);
scanf("%s", v);
//printf("You gave=%s\n", v);
}
vを0x1000文字で埋めるのに問題はないはずです(\ 0を参照)。
最初の記事へのRELRO参照は関係ありません。