Demontaj analizi ile minimal reprodüksiyon örneği
main.c
void myfunc(char *const src, int len) {
int i;
for (i = 0; i < len; ++i) {
src[i] = 42;
}
}
int main(void) {
char arr[] = {'a', 'b', 'c', 'd'};
int len = sizeof(arr);
myfunc(arr, len + 1);
return 0;
}
Yukarı Git .
Derleyin ve çalıştırın:
gcc -fstack-protector -g -O0 -std=c99 main.c
ulimit -c unlimited && rm -f core
./a.out
istendiği gibi başarısız olur:
*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)
Ubuntu 16.04, GCC 6.4.0'da test edilmiştir.
sökme
Şimdi demontaja bakıyoruz:
objdump -D a.out
içeren:
int main (void){
400579: 55 push %rbp
40057a: 48 89 e5 mov %rsp,%rbp
# Allocate 0x10 of stack space.
40057d: 48 83 ec 10 sub $0x10,%rsp
# Put the 8 byte canary from %fs:0x28 to -0x8(%rbp),
# which is right at the bottom of the stack.
400581: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400588: 00 00
40058a: 48 89 45 f8 mov %rax,-0x8(%rbp)
40058e: 31 c0 xor %eax,%eax
char arr[] = {'a', 'b', 'c', 'd'};
400590: c6 45 f4 61 movb $0x61,-0xc(%rbp)
400594: c6 45 f5 62 movb $0x62,-0xb(%rbp)
400598: c6 45 f6 63 movb $0x63,-0xa(%rbp)
40059c: c6 45 f7 64 movb $0x64,-0x9(%rbp)
int len = sizeof(arr);
4005a0: c7 45 f0 04 00 00 00 movl $0x4,-0x10(%rbp)
myfunc(arr, len + 1);
4005a7: 8b 45 f0 mov -0x10(%rbp),%eax
4005aa: 8d 50 01 lea 0x1(%rax),%edx
4005ad: 48 8d 45 f4 lea -0xc(%rbp),%rax
4005b1: 89 d6 mov %edx,%esi
4005b3: 48 89 c7 mov %rax,%rdi
4005b6: e8 8b ff ff ff callq 400546 <myfunc>
return 0;
4005bb: b8 00 00 00 00 mov $0x0,%eax
}
# Check that the canary at -0x8(%rbp) hasn't changed after calling myfunc.
# If it has, jump to the failure point __stack_chk_fail.
4005c0: 48 8b 4d f8 mov -0x8(%rbp),%rcx
4005c4: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx
4005cb: 00 00
4005cd: 74 05 je 4005d4 <main+0x5b>
4005cf: e8 4c fe ff ff callq 400420 <__stack_chk_fail@plt>
# Otherwise, exit normally.
4005d4: c9 leaveq
4005d5: c3 retq
4005d6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4005dd: 00 00 00
objdump
'In yapay zeka modülü tarafından otomatik olarak eklenen kullanışlı yorumlara dikkat edin .
Bu programı GDB üzerinden birden çok kez çalıştırırsanız şunu göreceksiniz:
- kanarya her seferinde farklı bir rastgele değer alır
- son döngü
myfunc
, kanarya adresini tam olarak değiştiren şeydir
Kanarya %fs:0x28
, şu şekilde açıklandığı gibi rastgele bir değer içeren ayarlanarak randomize edilir :
Hata ayıklama denemeleri
Şu andan itibaren kodu değiştiriyoruz:
myfunc(arr, len + 1);
yerine:
myfunc(arr, len);
myfunc(arr, len + 1); /* line 12 */
myfunc(arr, len);
daha ilginç olmak.
Daha sonra, suçlu + 1
çağrısını sadece tüm kaynak kodunu okumak ve anlamaktan daha otomatik bir yöntemle tespit edip edemeyeceğimizi görmeye çalışacağız .
gcc -fsanitize=address
Adres Temizleyici (ASan)
Bu bayrağı yeniden derlerseniz ve programı çalıştırırsanız, çıktı verir:
#0 0x4008bf in myfunc /home/ciro/test/main.c:4
#1 0x40099b in main /home/ciro/test/main.c:12
#2 0x7fcd2e13d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400798 in _start (/home/ciro/test/a.out+0x40079
ardından biraz daha renkli çıktı.
Bu sorunlu çizgiyi (12) açıkça saptar.
Bunun kaynak kodu şu adrestedir: https://github.com/google/sanitizers, ancak örnekte gördüğümüz gibi zaten GCC'ye aktarıldı.
ASan ayrıca bellek sızıntıları gibi diğer bellek sorunlarını da algılayabilir: C ++ kodunda / projesinde bellek sızıntısı nasıl bulunur?
Valgrind SGCheck
Gibi başkaları tarafından bahsedilen , Valgrind Bu tür sorunları çözmek de iyi değil.
SGCheck adlı deneysel bir aracı var :
SGCheck, yığın ve global dizilerin taşmalarını bulmak için bir araçtır. Olası yığın biçimleri ve küresel dizi erişimleri hakkındaki bir gözlemden türetilmiş sezgisel bir yaklaşım kullanarak çalışır.
Bu yüzden hatayı bulamadığımda çok şaşırmadım:
valgrind --tool=exp-sgcheck ./a.out
Hata mesajı şu şekilde görünmelidir: Valgrind eksik hatası
GDB
Önemli bir gözlem, programı GDB aracılığıyla çalıştırırsanız veya core
dosyayı bundan sonra incelerseniz :
gdb -nh -q a.out core
daha sonra, montajda gördüğümüz gibi, GDB sizi kanarya kontrolünü yapan fonksiyonun sonuna yönlendirmelidir:
(gdb) bt
#0 0x00007f0f66e20428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007f0f66e2202a in __GI_abort () at abort.c:89
#2 0x00007f0f66e627ea in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f0f66f7a49f "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f0f66f0415c in __GI___fortify_fail (msg=<optimized out>, msg@entry=0x7f0f66f7a481 "stack smashing detected") at fortify_fail.c:37
#4 0x00007f0f66f04100 in __stack_chk_fail () at stack_chk_fail.c:28
#5 0x00000000004005f6 in main () at main.c:15
(gdb) f 5
#5 0x00000000004005f6 in main () at main.c:15
15 }
(gdb)
Bu nedenle, bu işlevin yaptığı çağrılardan birinde sorun olması muhtemeldir.
Daha sonra, kanarya ayarlandıktan hemen sonra ilk tek adım ilerleyerek tam olarak başarısız olan çağrıyı bulmaya çalışıyoruz:
400581: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400588: 00 00
40058a: 48 89 45 f8 mov %rax,-0x8(%rbp)
ve adresi izlerken:
(gdb) p $rbp - 0x8
$1 = (void *) 0x7fffffffcf18
(gdb) watch 0x7fffffffcf18
Hardware watchpoint 2: *0x7fffffffcf18
(gdb) c
Continuing.
Hardware watchpoint 2: *0x7fffffffcf18
Old value = 1800814336
New value = 1800814378
myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
3 for (i = 0; i < len; ++i) {
(gdb) p len
$2 = 5
(gdb) p i
$3 = 4
(gdb) bt
#0 myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
#1 0x00000000004005cc in main () at main.c:12
Şimdi, bu bizi doğru rahatsız edici talimatta bırakıyor: len = 5
ve i = 4
bu özel durumda, bizi suçlu hattına 12 işaret etti.
Ancak, geri izleme bozuk ve bazı çöp içeriyor. Doğru bir geri izleme şöyle görünecektir:
#0 myfunc (src=0x7fffffffcf14 "abcd", len=4) at main.c:3
#1 0x00000000004005b8 in main () at main.c:11
belki de bu yığını bozabilir ve izini görmenizi engelleyebilir.
Ayrıca, bu yöntem kanarya kontrol fonksiyonunun son çağrısının ne olduğunu bilmeyi gerektirir, aksi takdirde ters hata ayıklama kullanmazsanız her zaman mümkün olmayacak yanlış pozitiflere sahip olursunuz .