Category Archives: disassembling

ar ve binutils


Binutils

Binutils paketindeki programların büyük çoğunluğu, BFD, yani Binary File Descriptor kütüphanesini kullanır. Bu kütüphane, alt-seviyede manipülasyonlar yapmayı sağlar. Yine, çoğu program opcodes kütüphanesini kullanır. Bu kütüphane, makine komutlarını assemble ve disassemble etmeyi sağlar.

Binutils paketindeki kütüphaneler:

lifeinbeta@lifeinbeta:/usr/lib/ldscripts$ locate libbfd
/usr/lib/libbfd-2.20.1-system.20100303.so
lifeinbeta@lifeinbeta:/usr/lib/ldscripts$ locate libopcodes
/usr/lib/libopcodes-2.20.1-system.20100303.so

ar

ar utility, binutils‘deki utility‘lerden biridir.
Binutils‘in içeriği için, Linux From Scratch – Contents of Binutils‘e bakılabilir.

ar, Arşiv Kütüphaneleri/Statik Kütüphaneler üzerinde oluşturma, değiştirme, içindekileri çıkarma(extract) işlemleri yapmak için kullanılır.

man ar  

Description:(Kısaltılmıştır) `ar`, `s` *modifier*'ı ile birlikte kullanıldığında,
oluşturulacak olan arşivde bulunacak olan *relocatable object module*'lerin içinde bulunan *symbol*'lere bir *index* tablosu oluşturur.

Bir Object File‘ın Symbol tablosu, bir programın sembolik tanımlamalarını ve referanslarını locate ve relocate etmek için gerekli olan bilgileri tutar.

Bu index tablosunu listelemek için nm -sya da nm --print-armap kullanılır. Eğer bir arşiv dosyasında bu tablo yoksa, sadece bu tabloyu eklemek için ar utility‘nin içindeki ranlib kullanılabilir.

Şimdi, ar ile ilgili anlattıklarımın uygulamasını yapalım.

Arşiv Dosyasının – Statik kütüphanenin oluşturulması

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ ar -qcv libdummy.a countdown.o factorial.o
a - countdown.o  <- *Verbose* modda olduğumuz için,
a - factorial.o  <- yapılan işlem hakkında bilgiler veriliyor.
lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ ls  <- libdummy.a gerçekten oluştu mu, görelim.
countdown.c  factorial.c  factorial.o  main    main.o
countdown.o  factorial.h  libdummy.a   main.c

Arşivdeki Index Tablosunun Görülmesi

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ nm -s libdummy.a

Archive index:  <- arşivdeki index table
countdown in countdown.o                  <- countdown.o'nun içinde countdown fonksiyonu var.
__i686.get_pc_thunk.bx in countdown.o     <- bu fonksiyonun ne yaptığını daha sonra açıklayacağım.[TODO]
countdown_by_adding in countdown.o        <- countdown.o'nun içinde countdown_by_adding fonksiyonu var.
countdown_with_modulo in countdown.o      <- countdown.o'nun içinde countdown_with_modulo fonksiyonu var.
fact in factorial.o                       <- factorial.o'nun içinde fact fonksiyonu var.
__i686.get_pc_thunk.bx in factorial.o     <- bu fonksiyonun ne yaptığını daha sonra açıklayacağım.[TODO]

countdown.o: <- Kütüphanedeki birinci parça, yani relocatable object module 1  <- aşağıda bu object modülündeki sembollere olan indexler gösteriliyor.
         U _GLOBAL_OFFSET_TABLE_    <- bir index entry ile GOT Tablosu belirtiliyor.
00000000 T __i686.get_pc_thunk.bx   <- bir index entry
00000000 T countdown                <- bir index entry ile countdown fonksiyonunu gösteren symbol belirtiliyor.
00000044 T countdown_by_adding      <- bir index entry ile countdown_by_adding fonksiyonunu gösteren symbol belirtiliyor.
0000008e T countdown_with_modulo    <- bir index entry ile countdown_with_modulo fonksiyonunu gösteren symbol belirtiliyor.
         U printf                   <- bir index entry ile printf fonksiyonunu gösteren symbol beliriliyor.

factorial.o: <- Kütüphanedeki ikinci parça, relocatable object module 2
         U _GLOBAL_OFFSET_TABLE_
00000000 T __i686.get_pc_thunk.bx
00000000 T fact

Yukarıda archive index table‘ı görüntüledik.

Index Table‘daki entry‘lerin object module‘ler içindeki symbol‘lere referans verdiğini söyledim. Peki, relocatable objecet file‘lardaki bu sembolleri görelim.

Relocatable Object Module/File‘lardaki Sembol Tablolarının Görülmesi

Bunun için objdump utility‘si, -t parametresi ile kullanılabilir.

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ objdump -t libdummy.a
In archive libdummy.a:

countdown.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 countdown.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .rodata    00000000 .rodata
00000000 l    d  .text.__i686.get_pc_thunk.bx   00000000 .text.__i686.get_pc_thunk.bx
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .comment   00000000 .comment
00000000 l    d  .group 00000000 .group
00000000 g     F .text  00000044 countdown     <- bir sembol
00000000 g     F .text.__i686.get_pc_thunk.bx   00000000 .hidden __i686.get_pc_thunk.bx  <- bir sembol
00000000         *UND*  00000000 _GLOBAL_OFFSET_TABLE_  <- UND a dikkat!
00000000         *UND*  00000000 printf                 <- UND a dikkat!
00000044 g     F .text  0000004a countdown_by_adding  <- diğer bir sembol
0000008e g     F .text  00000054 countdown_with_modulo <- diğer bir sembol

factorial.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 factorial.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .text.__i686.get_pc_thunk.bx   00000000 .text.__i686.get_pc_thunk.bx
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .comment   00000000 .comment
00000000 l    d  .group 00000000 .group
00000000 g     F .text  00000035 fact  <- bir sembol
00000000 g     F .text.__i686.get_pc_thunk.bx   00000000 .hidden __i686.get_pc_thunk.bx  <- diğer bir sembol
00000000         *UND*  00000000 _GLOBAL_OFFSET_TABLE   <- UND'a dikkat!

Görüldüğü gibi, her relocatable object module‘ün bir sembol tablosu var.

Arşiv başlıklarının Görülmesi

Ayrıca, binutils‘deki diğer bir utility olan objdump‘ı -a parametresi ile kullanarak arşiv dosyasındaki relocatable object module‘lerin adları ve formatlarını görebiliriz.

lifeinbeta@lifeinbeta:~/CSWORKS/C/soexample$ objdump -a libdummy.a
In archive libdummy.a:

countdown.o:     file format elf32-i386
rw-r--r-- 1000/1000   1460 Jun  5 00:04 2011 countdown.o

factorial.o:     file format elf32-i386
rw-r--r-- 1000/1000   1028 Jun  5 00:04 2011 factorial.o

Yukarıdaki, -a parametresi, --archive-headers, Displays archive header information anlamındadır.

ar utility‘sinin açıklamasına devam edelim…

Aşağıda, Synopsis‘i kısaltarak yazdım.

SYNOPSIS: ar [-]p[mod] archive member...

Buradaki parametreler:

p: çalıştırılacak olan işlemi belirtir. Burada, bunun için q‘yu kullandım.

mod: Yapılacak olan işlem hakkındaki detaylar belirtilir. Burada, bunun için c ve v‘yi kullandık.

-c: Create, aşiv dosyasını oluşturur.

-v: Verbose, işlem yapılırken verbose modunda yapılmasını ister. Yani, yapılan işlemler hakkında özet bilgi gösterilir.

-q: Quick Append, beliritilen member‘ları yani dosyaları, arşive ekle demek içindir.

Advertisements

Leave a comment

Filed under C, disassembling, gcc

C ile Standart Kütüphane olan LIBC’yi Kullanmadan Program Yazmak


Programlama diline, C,  yerleşik herhangi bir fonksiyonu kullanmadan, sadece programlama dilinin yazım kuralında belirtilen ifadeleri kullanarak, şu makalede belirtildiği gibi, program yazabiliriz.
Bunun nasıl olabildiği konusunda Make vs. Ant – Build araçları ile konfigürasyon yönetimi‘nde ilgili kısma(header dosyaları ve ayrı ayrı derlemeden bahsettiğim kısım) bakılabilir.
Yapılacak programın kaynak kodu, alıntı yaptığım makalede hello.c dosyasının içinde; benim yaptığım uygulamada ise libc_free.c içinde yazılıdır:
int main() { 
 char *str = "Hello World"; 
 return 0; 
}
Bu kod, aşağıdaki gibi derlendiğinde,
jesstess@kid-charlemagne:~/c$ gcc -nostdlib -o hello hello.c
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 00000000004000e8
çalıştırılabilir bir dosya oluşturulabilmektedir.
Bu kodda yapılan şey, bellekte 11 byte’lık bir alan ayırmaktır.
Yukarıda, uyarı olarak, _start sembolü bulunamadı deniyor.
Programı çalıştırdığımızda,
jesstess@kid-charlemagne:~/c$ ./hello
Segmentation fault
ile Segmentation Fault hatası alınıyor.
Bu hatayı düzeltmek için öncelikle derleme işlemi sonunda alınan _start sembolü bulunamadı uyarısına bakalım.
Programın çalışması için gerekli olan bu _start sembolü nedir?
libc içinde miydi acaba, peki libc‘deyse, neresinde tanımlıdır?
Linker açısından, _start sembolü,
programın gerçek giriş noktası(actual entry point)dır.
Yani programın giriş noktası, gerçekte, main değildir.
Bu sembol, bir ELF relocatable‘ı olan crt1.o içindedir.
ctr1.o‘yu link edip _start sembolünün artık bulunabildiğini görelim.
(NOT: Bunu düzeltsek de başka libc startup sembolleri bulunamadı diye yine hata verecek.)
# Kaynak kodu derle ama link etme.
jesstess@kid-charlemagne:~/c$ gcc -Os -c hello.c
# Simdi link etmeyi dene.
jesstess@kid-charlemagne:~/c$ ld /usr/lib/crt1.o -o hello hello.o
/usr/lib/crt1.o: In function `_start':
/build/buildd/glibc-2.9/csu/../sysdeps/x86_64/elf/start.S:106: undefined reference to `__libc_csu_fini'
/build/buildd/glibc-2.9/csu/../sysdeps/x86_64/elf/start.S:107: undefined reference to `__libc_csu_init'
/build/buildd/glibc-2.9/csu/../sysdeps/x86_64/elf/start.S:113: undefined reference to `__libc_start_main'
Buradan, _start sembolünün libc içinde start.S içinde olduğunu anlarız.
(NOT: bendeki mesajlarda start.S belirtilmedi[NEDEN?]:
lifeinbeta@lifeinbeta:~/CSWORKS/C$ ld /usr/lib/crt1.o -o libc_free_hello libc_free.o
/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'
/usr/lib/crt1.o: In function `_start':
(.text+0x1d): undefined reference to `__libc_start_main'
)
start.S dosyasına bakıldığında şu işleri yaptığı görülür:
  1. _start sembolünü export eder.
  2. stack‘i hazırlar.
  3. bazı register‘ları hazırlar
  4. __libc_start_main‘yi çağırır.
csu/libc_start.c (sembol tablosu[start.S]na dönüşecek olan kaynak kod.) nun en altında programımızın main fonksiyonuna çağrı yapıldığı görülür:
/* Nothing fancy, just call the function.  */
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
Peki, bu _start sembolüne niye ihtiyaç duyulmuş?
_start ile main arasında gerçekleşen işlemleri şöyle özetleyebiliriz;
 libc için gerekli olan ayarları yap ve sonra programda yazdığımız main fonksiyonunu çağır.
libc‘yi kullanmadan bu işlemi yapmak için, _start sembolünün yaptığı işi kendimiz yazalım.
Bu durumda libc için gerekli ayarları yapmadan sadece main fonksiyonunu çağırmak yeterli olacaktır:
jesstess@kid-charlemagne:~/c$ cat stubstart.S
.globl _start

_start:
	call main
kaynak kodu aşağıdaki gibi derler ve oluşturduğumuz stub olan stubstart.S assembly dosyası ile birleştirdikten sonra çalıştırırsak:
Assembly stub'ını derlenecek olan dosyanın olusturacagı .o ile link et.
jesstess@kid-charlemagne:~/c$ gcc -nostdlib stubstart.S -o hello hello.c
jesstess@kid-charlemagne:~/c$ ./hello
Segmentation fault
Görüldüğü gibi derleme problemleri yine devam ediyor.
(NOT: Burada Segmentation Fault‘a runtime hatası denmiyor, ‘derleme problemi’ deniyor. )
Ancak, hala segfault alınıyor. Neden? Bunun için, derlenecek dosyayı, derlenmiş dosya debug bilgisini de tutacak şekilde, derleyelim.
Bu şekilde, debug bilgisine, gdb ile bakılabilecek.
Bunu yaparken, main‘e bir breakpoint koyulacak ve segfault‘a kadar adım adım ilerlenecek:
jesstess@kid-charlemagne:~/c$ gcc -g -nostdlib stubstart.S -o hello hello.c
jesstess@kid-charlemagne:~/c$ gdb hello
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu"...
// burada breakpoint konuyor...................
(gdb) break main
Breakpoint 1 at 0x4000f4: file hello.c, line 3.
(gdb) run
Starting program: /home/jesstess/c/hello

Breakpoint 1, main () at hello.c:5
5	  char *str = "Hello World";
(gdb) step
6	  return 0;
(gdb) step
7	}
(gdb) step
0x00000000004000ed in _start ()
(gdb) step
Single stepping until exit from function _start,
which has no line number information.
main () at helloint.c:4
4	{
(gdb) step

Breakpoint 1, main () at helloint.c:5
5	  char *str = "Hello World";
(gdb) step
6	  return 0;
(gdb) step
7	}
(gdb) step

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000001 in ?? ()
(gdb)
Dikkat edilirse, main‘den iki defa geçiliyor, neden? Assembly koduna bakalım:
jesstess@kid-charlemagne:~/c$ objdump -d hello

hello:     file format elf64-x86-64

Disassembly of section .text:

00000000004000e8 <_start>:
  4000e8:	e8 03 00 00 00       	callq  4000f0
  4000ed:	90                   	nop
  4000ee:	90                   	nop
  4000ef:	90                   	nop    

00000000004000f0 :
  4000f0:	55                   	push   %rbp
  4000f1:	48 89 e5             	mov    %rsp,%rbp
  4000f4:	48 c7 45 f8 03 01 40 	movq   $0x400103,-0x8(%rbp)
  4000fb:	00
  4000fc:	b8 00 00 00 00       	mov    $0x0,%eax
  400101:	c9                   	leaveq
  400102:	c3                   	retq
Assembly(ASM) kodunu tamamen incelemeden şuna dikkat edelim.
_start sembolünün belirttiği ASM kodunda, CALLQ ile main fonksiyonu çağrılıyor.
Daha sonra RETQ ile tekrar,  _start‘a dönülüyor.
Stack Frame‘den alınan IP(Instruction Pointer) nin belirttiği adres değeri,  NOP komutunun adresini gösteriyor.
NOP (No Operation) komutları çalıştırılıp geçildikten sonra main fonksiyonunun ASM kodları bir daha çalıştırılıyor.
(Yani, main‘e ikinci defa girmiş olduk)
(NOT: main kodunun başlangıç adresi olan 4000F0 adresi,
_start sembolünün belirttiği bloğun bitiş adresi olan 4000EF‘ nin hemen sonrası)
Bu noktada, bu bloğa ikinci girişimizde return instruction pointer‘ı stack‘e atmadığımız için,
RETQ‘ya bu ikinci gelişimizde mevcut stack frame(stack‘in bir parçası)den, return instruction pointer sanarak o anda alabildiği bir adresi alır. Bu adresi instruction pointer‘a yükler.
IP‘da belirtilen adrese hoplar(jump),
bu adres yanlış adres olduğundan,  segment dışına çıkmış olur ve segfault hatası alır.
Bu olayı biraz açarsam;
Öncelikle buradaki RBP(Register Base Pointer) 64-bitlik sistemlerde olan bir register‘dır. 32-bit sistemlerde bu EBP(Extended Base Pointer)dir.
i386 daki fonksiyon çağırma yöntemine göre, çağrılan fonksiyona dallanılmadan önce,
Base Pointer Register olan EBP ile adresine işaret edilen, bir stack frame oluşturulur.
Fonksiyondan dönünce kullanacağı bilgiler burada saklanır.
Saklanan bilgilerden biri, RIP' in değeridir.
RIP‘ de, dallanılan fonksiyonu çağıran fonksiyona dönüşte çalıştırılacak olan ilk komutun adresi bulunur.
 RIP,  fonksiyona dallanırken otomatik olarak oluşturulan ve RBP ile gösterilen stack frame‘e, otomatik olarak atılır.
Biz ikinci defa main‘e girerken bir fonksiyondan dallanmadığımız halde RETQ ile fonksiyondan dallanıldı sanılarak RIP alındığında, alınan değer yanlış değer olmuş oldu.
(NOT: retq, dönüşte bununla birlikte otomatik olarak atılan bayrakları da alır.)
Kaldığımız yerden devam edersek; bizim ikinci defa main‘e girmeden çıkmamız lazım. Bunun için CALLQ ile dallanılan main‘den dönünce;
bir sistem çağrısı olan SYS_exit enumeration item‘ının değeri “1“,
EAX akümülatör register‘ına yazılmalı.
Ayrıca, SYS_exit‘in tek argümanının, status, değeri olarak “0“, EBX register‘ına yazılır.
Bu şekilde, Kernel‘a temiz bir şekilde(“0” değeri bunu belirtir) şu anda çalıştırılan programdan çıkıldığını bildirmiş olduk.
Bundan sonra, INT $0x80 interrupt‘ı ileKernel‘a geri döneriz.,
lifeinbeta@lifeinbeta:~/CSWORKS/C$ vim stubstart.S
lifeinbeta@lifeinbeta:~/CSWORKS/C$ cat stubstart.S
.globl _start

_start:
	call main
	movl $1, %eax
	xorl %ebx, %ebx
	int $0x80
lifeinbeta@lifeinbeta:~/CSWORKS/C$ gcc -nostdlib stubstart.S -o hello hello.c
gcc: hello.c: No such file or directory
lifeinbeta@lifeinbeta:~/CSWORKS/C$ gcc -nostdlib stubstart.S -o libc_free libc_free.c
lifeinbeta@lifeinbeta:~/CSWORKS/C$ ./libc_free

Görüldüğü gibi, başarıyla derlendi, link edildi ve çalıştırılabilir dosya oluştu. Çalıştırılabilir dosya segfault almadan başarıyla çalıştı.

Güncelleme:15.05.2011 – Linkler

http://www.acm.uiuc.edu/sigmil/RevEng/ch02.html – C ve Java Derleme Süreçleri [Kapsamlı], gcc parametreleri, Assembler, Linking Stage

Güzel bir söz

If you want to fly, you gotta give up the shit that weighs you down!

[Uçmak istiyorsan, seni aşağıya çeken o pislikten kurtulacaksın!]

3 Comments

Filed under compiler, disassembling, gdb, linking, symbol table