original in en Guido Socher
en to tr:Erdal Mutlu
Guido, sorunları araştırma fırsatı tanıdıkları için Linux gibi açık kaynak kodlu sistemlerden hoşlanmaktadır. Eğer, zaman ayırabilirseniz, sorunların ana kaynağını bulmanız olasıdır.
Kaynak kodu kapalı olan sistemlerde bir sorun ortaya çıktığında, yapabilecekleriniz genelde aşağıdaki iki seçenekle sınırlıdır:
Bu engellere karşın, programın tüm kaynak kodunu okumadan ve hasıl çalıştığını öğrenmeden önce, yapabileceğiniz şeyler vardır.
#includeC programlama dilinde yazılan her program sistem çetele dosyasına yazabilir. Diğer programlama dillerin de kendi olanakları vardır. Kabuk betikleri bile çetele tutabilir:void openlog(const char *ident, int option, int facility); void syslog(int priority, const char *format, ...); void closelog(void); facility (olanak) iletiyi gönderenin hangi türden bir uygulama olduğunu belirler. priority (öncelik) iletinin önemini belirtmektedir. priority (öncelik) için olası değerler şunlardır: LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG
logger -p err "Bu yazı /var/log/messages dosyasına yazılacaktır."syslog'un standart yapılandırma dosyasında (/etc/syslog.conf) başka yapılandırmaların yanısıra, aşağıdakine benzer bir satır olması gerekir:
# Log anything (except mail) of level info or higher. # Don't log private authentication messages. *.info;mail.none;authpriv.none /var/log/messages"*.info" öncelik seviyesi LOG_INFO veya daha yüksek olan herşeyi çetele dosyasına yazacaktır. Daha fazla bilgi görmek isterseniz, bunu "*.debug" yapıp syslog'u yeniden başlatabilirsiniz (/etc/init.d/syslog restart).
1) tail -f /var/log/messages komutunu çalıştırın ve başka bir kabuktan hatalı uygulamayı başlatın. Belkide daha şimdiden yanlış giden şeyler hakkında bilgi elde edebilrsiniz. 2) Eğer, 1. adım yeterli olmazsa, /etc/syslog.conf'daki *.info yu *.debug olarak değiştirin ve "/etc/init.d/syslog restart" komutuyla syslog servisini yeniden başlatıp ve 1. adıma dönün.Bu yöntemin eksiği, yazılımcının kaynak kodunda ne kadar denetim yaptığına ve bunları çetele dosyasına gönderip göndermemesine olmasıdır. Eğer, önemli yerlere syslog fonksiyonunu yereştirmemişse, hiçbir şey göremeyebilirsiniz. Başka bir deyişle, sorunları belirleyebilmenin şartı, yazılımcının bazı yerlerde sorunların çıkabileceğini öngörmüş olmasıdır.
strace your_applicationİşte bir örnek:
# strace /usr/sbin/uucico execve("/usr/sbin/uucico", ["/usr/sbin/uucico", "-S", "uucpssh", "-X", "11"], [/* 36 vars */]) = 0 uname({sys="Linux", node="brain", ...}) = 0 brk(0) = 0x8085e34 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40014000 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=70865, ...}) = 0 mmap2(NULL, 70865, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40015000 close(3) = 0 open("/lib/libnsl.so.1", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300;\0"..., 1024) = 1024 fstat64(3, {st_mode=S_IFREG|0755, st_size=89509, ...}) = 0 mmap2(NULL, 84768, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40027000 mprotect(0x40039000, 11040, PROT_NONE) = 0 mmap2(0x40039000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x11) = 0x40039000 mmap2(0x4003a000, 6944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x4003a000 close(3) = 0 open("/lib/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`X\1\000"..., 1024) = 1024 fstat64(3, {st_mode=S_IFREG|0755, st_size=1465426, ...}) = 0 mmap2(NULL, 1230884, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x4003c000 mprotect(0x40163000, 22564, PROT_NONE) = 0 mmap2(0x40163000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x126) = 0x40163000 mmap2(0x40166000, 10276, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40166000 close(3) = 0 munmap(0x40015000, 70865) = 0 brk(0) = 0x8085e34 brk(0x8086e34) = 0x8086e34 brk(0) = 0x8086e34 brk(0x8087000) = 0x8087000 open("/usr/conf/uucp/config", O_RDONLY) = -1 ENOENT (No such file or directory) rt_sigaction(SIGINT, NULL, {SIG_DFL}, 8) = 0 rt_sigaction(SIGINT, {0x806a700, [], SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0 rt_sigaction(SIGHUP, NULL, {SIG_DFL}, 8) = 0 rt_sigaction(SIGHUP, {0x806a700, [], SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0 rt_sigaction(SIGQUIT, NULL, {SIG_DFL}, 8) = 0 rt_sigaction(SIGQUIT, {0x806a700, [], SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0 rt_sigaction(SIGTERM, NULL, {SIG_DFL}, 8) = 0 rt_sigaction(SIGTERM, {0x806a700, [], SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0 rt_sigaction(SIGPIPE, NULL, {SIG_DFL}, 8) = 0 rt_sigaction(SIGPIPE, {0x806a700, [], SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0 getpid() = 1605 getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=1024}) = 0 close(3) = -1 EBADF (Bad file descriptor) close(4) = -1 EBADF (Bad file descriptor) close(5) = -1 EBADF (Bad file descriptor) close(6) = -1 EBADF (Bad file descriptor) close(7) = -1 EBADF (Bad file descriptor) close(8) = -1 EBADF (Bad file descriptor) close(9) = -1 EBADF (Bad file descriptor) fcntl64(0, F_GETFD) = 0 fcntl64(1, F_GETFD) = 0 fcntl64(2, F_GETFD) = 0 uname({sys="Linux", node="brain", ...}) = 0 umask(0) = 022 socket(PF_UNIX, SOCK_STREAM, 0) = 3 connect(3, {sa_family=AF_UNIX, path="/var/run/.nscd_socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 open("/etc/nsswitch.conf", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=499, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000 read(3, "# /etc/nsswitch.conf:\n# $Header:"..., 4096) = 499 read(3, "", 4096) = 0 close(3) = 0 munmap(0x40015000, 4096) = 0 open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=70865, ...}) = 0 mmap2(NULL, 70865, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40015000 close(3) = 0 open("/lib/libnss_compat.so.2", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\25"..., 1024) = 1024 fstat64(3, {st_mode=S_IFREG|0755, st_size=50250, ...}) = 0 mmap2(NULL, 46120, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40169000 mprotect(0x40174000, 1064, PROT_NONE) = 0 mmap2(0x40174000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0xa) = 0x40174000 close(3) = 0 munmap(0x40015000, 70865) = 0 uname({sys="Linux", node="brain", ...}) = 0 brk(0) = 0x8087000 brk(0x8088000) = 0x8088000 open("/etc/passwd", O_RDONLY) = 3 fcntl64(3, F_GETFD) = 0 fcntl64(3, F_SETFD, FD_CLOEXEC) = 0 fstat64(3, {st_mode=S_IFREG|0644, st_size=1864, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000 _llseek(3, 0, [0], SEEK_CUR) = 0 read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1864 close(3) = 0 munmap(0x40015000, 4096) = 0 getuid32() = 10 geteuid32() = 10 chdir("/var/spool/uucp") = 0 open("/usr/conf/uucp/sys", O_RDONLY) = -1 ENOENT (No such file or directory) open("/var/log/uucp/Debug", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0600) = 3 fcntl64(3, F_GETFD) = 0 fcntl64(3, F_SETFD, FD_CLOEXEC) = 0 fcntl64(3, F_GETFL) = 0x401 (flags O_WRONLY|O_APPEND) fstat64(3, {st_mode=S_IFREG|0600, st_size=296, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000 _llseek(3, 0, [0], SEEK_CUR) = 0 open("/var/log/uucp/Log", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0644) = 4 fcntl64(4, F_GETFD) = 0 fcntl64(4, F_SETFD, FD_CLOEXEC) = 0 fcntl64(4, F_GETFL) = 0x401 (flags O_WRONLY|O_APPEND)Burada ne görüyoruz? İsterseniz aşağıdaki satırlara bir göz atalım:
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3Elde edilen çıktıya bir bakalım. Program ilk önce /etc/ld.so.preload dosyasını okumaya çalışıyor ve başarısız oluyor, daha sonra /etc/ld.so.cache dosyasını okumaktadır. Burada başarılı olmakta ve 3 nolu dosya tanımlayıcısını elde etmektedir. /etc/ld.so.preload dosyasını okuyamaması tam olarak bir hata anlamına gelmeyebilir, çünkü belkide program varsa ilk önce bu dosyayı okumak istemiştir. Başka bir deyişle, bir dosyayı okuyamamak, mutlaka hata anlamına gelmeyebilir. Herşey programın tasarlanma şekline bağlıdır. strace çıktısındaki tüm open sistem çağrılarına bir göz atalım:
open("/usr/conf/uucp/config", O_RDONLY)= -1 ENOENT (No such file or directory) open("/etc/nsswitch.conf", O_RDONLY) = 3 open("/etc/ld.so.cache", O_RDONLY) = 3 open("/lib/libnss_compat.so.2", O_RDONLY) = 3 open("/etc/passwd", O_RDONLY) = 3 open("/usr/conf/uucp/sys", O_RDONLY) = -1 ENOENT (No such file or directory) open("/var/log/uucp/Debug", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0600) = 3 open("/var/log/uucp/Log", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0644) = 4 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3Şimdi program /usr/conf/uucp/config dosyasını okumaya çalışıyor. Ooo! Ama bu çok garip benim dosyam /etc/uucp/config dir! Ve /etc/uucp/config dosyasını açmak isteyen hiçbir satır çıktıda görünmüyor. Hata bu olmalı işte. Herhalde yapılandırma dosyasının yeri, programın derlenmesi sırasında yanlış belirtildi.
# ulimit -c unlimited # ./lshref -i index.html,index.htm test.html Segmentation fault (core dumped) Exit 139Oluşan core dosyası, gdb hata ayıklayıcısı kullanılarak sorunun neden kaynaklandığı araştırması yapılabilir. gdb'yi çalıştırmadan önce, doğru core dosyasına baktığınızı denetlemede yarar vardır:
# file core.16897 core.16897: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from 'lshref'Tamam, bozuk olan program lshref dir ve onu şimdi gdb'ye yükleyebiliriz. gdb'yi bir core dosyasını incelemek için çalıştırmak istediğinizde, core dosyasının adıyla birlikte, bu dosyanın oluşmasına neden olan programı da belirtmeniz gerekmektedir.
# gdb ./lshref core.23061 GNU gdb Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. Core was generated by `./lshref -i index.html,index.htm test.html'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x40095e9d in strcpy () from /lib/libc.so.6 (gdb)Şimdi artık programın strcpy fonksiyonunu kullanırken çöktüğünü biliyoruz. Ancak, program içerisinde strcpy birden fazla yerde kullanılıyor olabilir.
gdb ./lshref core.23061 GNU gdb Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. Core was generated by `./lshref -i index.html,index.htm test.html'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x40095e9d in strcpy () from /lib/libc.so.6 (gdb) backtrace #0 0x40095e9d in strcpy () from /lib/libc.so.6 Cannot access memory at address 0xbfffeb38 (gdb) run ./lshref -i index.html,index.htm test.html Starting program: /home/guido/lshref ./lshref -i index.html,index.htm test.html Program received signal SIGSEGV, Segmentation fault. 0x40095e9d in strcpy () from /lib/libc.so.6 (gdb) backtrace #0 0x40095e9d in strcpy () from /lib/libc.so.6 #1 0x08048d09 in string_to_list () #2 0x080494c8 in main () #3 0x400374ed in __libc_start_main () from /lib/libc.so.6 (gdb)Şimdi görüyoruz ki, sorun yarat strcpy fonksiyonunu string_to_list() fonksiyonu, onu da main() çağırmıştır. string_to_list() fonksiyonuna bir bakalım:
char **string_to_list(char *string){ char *dat; char *chptr; char **array; int i=0; dat=(char *)malloc(strlen(string))+5000; array=(char **)malloc(sizeof(char *)*51); strcpy(dat,string);malloc satırında bir yazım hatası var gibi gözüküyor. Aslında şöyle olmalıydı:
dat=(char *)malloc(strlen(string)+5000);
#includeProgramı derliyor:#include int add(int *p,int a,int b) { *p=a+b; return(*p); } int main(void) { int i; int *p = 0; /* a null pointer */ printf("result is %d\n", add(p,2,3)); return(0); }
gcc -Wall -g -o exmp exmp.cve çalıştırıyoruz ...
# ./exmp Segmentation fault (core dumped) Exit 139
gdb exmp core.5302 GNU gdb Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. Core was generated by `./exmp'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x08048334 in add (p=Cannot access memory at address 0xbfffe020 ) at exmp.c:6 6 *p=a+b;gdb, hatanın 6. satırda ve 'p' işaretçisinin, erişim hakkı olmadığı bir bellek alanına erişmek isterken oluştuğunu söylemektedir.