(Bu yazıyı daha önce yazdığım ve kısıtlı bir çevrede dağıttığım bir belgenin küçük bir kısmından derledim. Internet'te bu yazının bazı bölümlerini aynen içeren bir doküman bulursanız o benim olabilir.)
debug.exe, DOS zamanlarından beri herkeste bulunan; Windows'la birlikte hala dağıtılan (32 bit Windows7'de var.) bir araç. Peki ne işe yarar bu araç? Nasıl kullanılır?
Bu araç elbetteki koddaki bug'ları otomatik temizlemiyor. Adından anlaşılacağı üzere bu bir debugger. Yani programın çalışmasını mikroişlemci düzeyinde inceleyip varsa hataları görmek, tek tek komut komut çalıştırıp kısmen de olsa değişiklikler yapmak olanaklı. Windows için güncel debugger'ları takip edemedim ama 2000'lerin başında Win32Dasm ve SoftICE kullanırdık. Bunlara da yeri gelirse kısaca değinirim ancak baştan belirteyim debug.exe DOS1.0 zamanlarında kalma (1980'lerin başından) bir program. 80286 komut setini bile desteklemiyor. EAX, EDI gibi yazmaçlara erişilemiyor. Daha iyi işler yapabilmek için daha güçlü bir debugger'a gereksinimi olanların TASM paketi içerisindeki Turbo Debugger'ı yada FreeDOS'un debugger'ini kullanmaları daha mantıklı.
Komut satırında debug dediğiniz zaman debug'un kendi iptidai - ile belirtilen komut satırına düşülüyor. Debug'dan sonra "debug \WINDOWS\system32\format.com" şeklinde dosya adı da vermek mümkün hatta format.com'un komut satırından aldığı parametreler de dosya adından sonra eklenebilir. Debug.exe'nin komut satırına düştükten sonra kendi tek harfli komutlarını bilmek gerekiyor. Bu komutlar komut satırında ? yazarak ulaşabileceğiniz bir avuç komut. Şimdi bunları sırasıyla açıklayayım.
* a (Assemble): a komutu girildiği zaman debug size CS:0100 (CS: Code Segment. Kodun bellekteki adresini tutan bir yazmaç. Dolayısıyla siz a yazdığınızca CS'nin içeriği kaçsa o sayıyı gösterecek. Bunu işletim sistemi 640Kb'lik bellekteki boş alana göre ayarlar.) adresini gösterir ve sizden x86 assembly dili komutlarını girmenizi bekler. 0100h offset adresi de DOS'ta bir .com çalıştırılabilir dosya yüklendiğinde onun ilk komutunun bulunduğu yerdir. Segmentin 0000h ile 0100h arasında PSP (Program Segment Prefix) adı verilen başlık bulunur. Komut yazma modunda komut yazmadan enter'a basınca çıkılır. a'dan sonra herhangi bir 16 bitlik sayı girilirse komutun girilmesine o satırdan devam edilir.
-a0200
1381:0200 mov bx,b800
1381:0203
* c (Compare): c komutu iki bellek parçasını karşılaştırmak için kullanılır. Yalnızca farklı olan byte'lar gösterilir. İstenirse farklı segmentler de verilebilir. Aşağıdaki örnek 0000:0100h ile 0000:0110h arası parçayı DS:0400h'den itibaren karşılaştırır.
-c 0000:0100 0110 0400
0000:0100 BB 00 1381:0400
0000:0102 B8 00 1381:0402
0000:010A 01 00 1381:040A
0000:010B 1A 00 1381:040B
* d (Dump): d komutu belleğin bir parçasını görüntüler. Tek parametreyle verilirse segmenti DS olarak seçer ve 128 byte görüntüler. İki parametreyle verilirse verilen iki offset arasını gösterir. Parametresiz verildiğinde en son görüntülediği bellek parçasının ardışığı 128 byte'ı gösterir. Böylece ardarda verilen d komutlarıyla uzun bir bloğa parça parça bakmak olanaklıdır.
-d0110 011F
1383:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
* e (Enter): e komutuyla bir bellek bölgesine elle byte'lar iki türlü girilebilir:
Aşağıdaki komutla DS:0120h'den itibaren 13h, 16h, 19h ve 1Ch byte'ları sırasıyla yazılır.
-e 0120 13 16 19 1C
Aşağıdaki komutla ES:0200h'dan itibaren 13h, 16h, 19h ve 1Ch byte'ları tek tek yazılır. Sayılar girildikten sonra boşluk karakterine basmak gerekir:
-e ES:02001381:0200 00.13 00.16 00.19 00.1C
* f (Fill): f komutu bellek bölgesini verilen karakterlerle belli uzunlukta doldurur.
Aşağıdaki komutla 0100h offsetinden başlayarak 12h, 13h, 14h byte sırası 16 byte boyunca tekrarlanır:
-f 0100L10 12 13 14
* g (Go): g komutu, CS:IP ile gösterilen bellek adresinde hangi komutlar varsa koşulsuz olarak çalıştırır. Program sonlanıp yada kesilip çalışmayı debug.exe'ye geri bırakana kadar sürer. g'den sonra bir bellek offset adresi verilirse debug onu verilen bellek adresine kadar çalıştırıp ardından r komutunu uygular ve tekrar debug.exe'nin komut satırına düşer. Bu bellek adresine breakpoint denir. Debugger'larda breakpoint'ler belirlenerek program parça parça çalıştırılabilir. g komutuna ayrı bir parametre vermek yerine çalışması kesime uğratılacak noktadaki kod parçası 0CCh (INT 3) ile değiştirilebilir.
g komutu için söylenmesi gereken önemli bir şey daha var: Eğer program DOS 1.0 zamanlarından kalma INT 20h kesmesini kullanarak çıkıyorsa debug.exe "Program terminated normally." diyor ve program sorunsuz debug.exe'ye düşüyor. Yanılmıyorsam bu INT 21h,00h için de geçerli. Ancak DOS 2.0 ve sonrası için önerilen program sonlandırma yöntemi INT 21h,4Ch çağırıldığı zaman bu yöntem debug.exe'yi de sonlandırıp DOS komut satırına düşmenize neden olabiliyor (özellikle birden fazla kere çağırıldığında).
* h (Hex): h komutu verilen iki sayıyı toplar ve çıkarır.
-h 2D1 2E8
05B9 FFE9
* i (Input): Bir porttan veri okur. Komutu port numarası takip eder. Windows NT tabanlı tüm işletim sistemlerinde bu veriye pek güvenmemek gerekiyor. Doğrudan portlara erişmek bazı durumlarda olanaksız, örneğin Windows'un diski kontrol eden sürücüsü diske erişmenize izin vermez. Basit görece zararsız portlar (klavye gibi) işletim sistemi üzerinden erişilebilir durumdalar.
-i 60
1C
60h portu klavye denetleyicisinin portudur.
* l (Load): l komutunun iki kullanımı bulunmaktadır. Birinci kullanımda mantıksal bir sektörü (cluster) belleğe okumakta kullanılır. Bunun kullanımı Windows NT tabanlı sistemlerde yine kısıtlı. XP'de yanlış hatırlamıyorsam çekirdek sürücüleri üzerinden okuma yapılabiliyordu, yazmaya izin yoktu. Windows 7'de şöyle şeyler oluyor:
Bu diyalog kutusunda Close diyince debug.exe sonlandırılıyor, Ignore diyince "Disk error reading drive C" hatası veriyor.
Komutun kullanımında, l'den sonra gelen parametre cluster'ın okunacağı bellek adresi, 02h disk sıra numarası. ( 00h = A: , 01h = B:, 02h = C:, 03h = D: ... ) Sonraki sayılar cluster numarası ve okunacak toplam cluster sayısı.
İkinci kullanımında n ile belirtilen dosyayı (çalıştırılabilir olması şart değil) CS:0100h adresine yükler. Eğer dosya bir .exe dosya ise yüklenecek yer CS:0100h olmayabilir. Bu kullanımla ilgili n komutunun açıklamasına da bakılabilir.
* m (Move): m komutu bir bellek parçasını başka bir bellek parçasının üstüne kopyalar.
DS:0200h'dan DS:0400h'a 128 byte kopyala:
-m 0200 l80 0400
* n (Name): n komutu bir dosya yada parametrelerini debug.exe'ye bildirmeye yarar. l ile bir dosya okumak yada w ile bir dosya yazılmak istendiğinde önce n komutuyla dosya bildirilmesi gerekmektedir. Komut satırında debug'dan sonra dosya adı verilebileceğini belirtmiştim. Eğer bu dosya adı komut satırından verilmezse n komutuyla verilip sonra l komutuyla dosya yüklenebilir. n komutu dosyanın olup olmadığını kontrol etmez, yalnızca bir FCB (File Control Block) oluşturur. l komutu da bu FCB'deki dosyayı açmaya çalışır. Dolayısıyla n komutuna ne girilirse girilsin hata vermeyecektir. İlgili hata l komutu verildiği zaman alınır.
-n\Windows\winhlp32.exe
-l
Yazmaya ilgili bilgiler w komutunda daha etraflıca anlatılacaktır.
* o (Output): Bir porta veri yazar. i komutu gibi bunda da komutu port numarası takip
eder. Port numarasından sonra porta gönderilecek veri yazılır. Yine Windows NT tabanlı tüm işletim sistemlerinde i komutundaki gibi güvenlik vb. nedenlerden bu komutun sonucuna pek güvenmemek gerekiyor. Veri porta gönderilmemiş olabilir yada güvenlik hatasıyla debug.exe sonlandırılabilir.
71h portu CMOS adresleme, 70h portu da okuma portudur. Aşağıdaki komutlarla CMOS'un 19h byte'ı okunur:
-o 71 19-i 70
DD
* p (Proceed): p komutu tek başına kullanıldığında CS:IP'teki bir komutu çalıştırır ancak eğer CS:IP'deki komut bir altprogram yada kesme çağrısıysa (CALL yada INT) içine girmeden tamamını çalıştırılır ve çalışmanın sonunda yazmaçların içeriklerini gösterir (r komutunun çıktısı).
p komutundan sonra bir sayı girilirse (p 3 gibi) debug.exe o kadar sayıda komutu çalıştırarak tek tek her seferinde yazmaç içeriklerini gösterir.
p komutundan sonra başında = bulunan bir offset adresi girildiği zaman IP (Instruction Pointer) bu offset adresine gelinceye kadar program çalışır. O adreste komut bitmiyorsa komutun bittiği son yerde çalışma durur. Yani IP'nin son değeri bu değere eşit yada büyüktür.
-p =0107
AX=0900 BX=FFFF CX=FE00 DX=0000 SP=00B8 BP=0000 SI=0000 DI=0000
DS=13DA ES=13DA SS=13EA CS=13EA IP=0107 NV UP EI PL NZ NA PO NC
13EA:0107 CD21 INT 21
* q (Quit): q komutu debug.exe'den çıkar. Herhangi bir parametresi yoktur, verilse de dikkate alınmaz. Yani q, quit yada quake yazılsa da debug.exe'den çıkar.
* r (Register): r komutu yazmaç içeriklerini görüntülemek ve değiştirmek için kullanılır. r komutu tek başına bütün yazmaçları görüntüler.
-r
AX=0900 BX=FFFF CX=FE00 DX=0000 SP=00B8 BP=0000 SI=0000 DI=0000
DS=13DA ES=13DA SS=13EA CS=13EA IP=0007 NV UP EI PL NZ NA PO NC
13EA:0007 CD21 INT 21
Çıktıyı kısaca açıklarsam, AX, BX, CX ve DX x86 serisi işlemcilerin çeşitli komutlarla kullandığı genel amaçlı yazmaçlardır. Accumulator, Base, Counter, Data gibi açılımları olsa da ( http://en.wikipedia.org/wiki/AX_register#Purpose ) genel olarak bunların içeriklerini değiştirmenin komutlar işlemediği sürece bir etkisi yoktur.
SP, stack pointer anlamına gelir ve işlemcinin o anda kullandığı stack'in (yığın) offset adresini tutar. BP, base pointer anlamına gelir ve göstericilerin tutulması için yardımcı bir yazmaçtır. Genelde argümanlarına yığın üzerinden erişen C gibi yüksek seviyeli dillerde fonksiyonların en başında şuna benzer bir kod bloğu bulunur:
k = fonk1(0x0202, 0x0101);
karşılığında üretilen kod:
mov ax, 0101
push ax
mov ax, 0202
push ax
call @fonk1
@fonk1:
push bp
mov bp,sp
...
pop bp
ret
biçimindedir. Alt program blogunda kısa (segment içi) çağrı için ilk arguman [bp+04] ikincisi [bp+06] ve uzun (segment dışı) çağrı için ilk arguman [bp+06] ve ikincisi [bp+08] adresinde bulunur. Yani alt programda BP yedek bir SP işi görür ama elbette ki bu bir zorunluluk değildir. SP genelde doğrudan değiştirilmez, push ve pop gibi komutlarla değeri kendiliğinden değişir.
SI, source index ve DI destination index anlamına gelir. MOVS, STOS, SCAS ... gibi karakter dizisi üzerinden yapılan işlemlerde okumalar DS:SI üzerinden, yazma işlemleriyse ES:DI üzerinden yapılır.
DS, data segment anlamına gelir. Genelde veriler üzerinde yapılacak işlemlerde verilerin tutulduğu segment DS'de bulunur. CS, code segment anlamındadır. CS, IP (instruction pointer) yazmacıyla birlikte mikroişlemcinin o an çalıştıracağı komutu tutar. SS'in anlamı stack segment'tir adından da anlaşılacağı üzere SP ile birlikte yığını gösterir. Son olarak ES, extra segment'dir ve bütün bu segment yazmaçlarının haricinde bir yazmaça ihtiyaç duyulması durumunda yardımcı segment yazmacı olarak kullanılır. IP yazmacı programcı tarafından yalnızca jmp ve koşullu dallanma komutlarıyla değiştirilebilir. Her komutun çalıştırılması sonucunda bir sonraki komutu gösterecek şekilde mikroişlemci tarafından değiştirilir. Örneğin 2 byte'lık bir komutun çalıştırılması sonucunda değeri 2, 3 byte'lık bir komutun çalıştırılması sonucunda değeri 3 arttırılır.
NV UP gibi ifadeler FLAGS yazmacına aittir. Bununla ilgili Temmuz 2012 tarihinde "Peki Neden Trap Flag?" başlıklı yazıda ayrıntılı bilgi bulunuyor. En alt satırdaysa o anki CS:IP'nin değeri ve o değerin gösterdiği yerdeki mikroişlemci komutları ve bunların Assembly karşılığı bulunur.
r komutuyla yazmacın içeriğinin değiştirilmesi için r'den sonra yazmacın adı yazılır. Enter'a basıldığında yazmaçtaki o anki değer görüntülenir ve ardından yeni değer girilmesi beklenir. Herhangi bir değer girmeden enter'a basılırsa yazmacın içeriği değişmez. FLAGS yazmacının içeriğine rf komutuyla erişilebilir ve içerik bu komutla değiştirilebilir.
-rax
AX 0202
:0303
* s (Search): s komutu başlangıcı ve bitişi verilen bir aralıkta verilen bir byte yada byte dizisini aramaya yarar.
Aşağıdaki komut DS:0100h ile 0200h arasındaki bütün 0b8h 00h 01h dizisini listeler:
-s 0100 0200 b8 00 01
1381:0100
1381:01A4
İstenirse aranacak veri çift tırnak içerisinde belirtilerek karakterle arama da yapılabilir:
-s 0100 0200 "h"
* t (Trace): t komutu da p komutu gibi tek başına kullanıldığında CS:IP'teki bir komutu çalıştırır ancak p'den farklı olarak t komutu, CALL yada INT gibi komutların içerisine girerek onları da çalıştırır. Çıktı olarak p gibi yazmaç içeriklerini gösterir. t komutu da p komutu gibi komut sayısı belirtilerek yada başında = olan bir offset adresiyle kullanılabilir. Parametrelerin etkisi de p komutuyla aynıdır.
* u (Unassemble): u komutu belleğin bir parçasındaki byte'ları alarak bunları Assembly kodlarına geri çevirir ve ekrana sığdığı kadarıyla listeler. Eğer tek parametreyle verilirse bu değeri offset değeri olarak alıp listelemeye bu offsetten başlar. İki parametreyle verildiğinde birinci değer başlangıç ikinci değer bitiş olmak üzere aradaki kodlar listelenir. Parametresiz verildiğinde en son görüntülediği kod bloğunun bitişiğindeki bloğu listeler. Ardarda verilen u komutlarıyla uzun bir bloğa parça parça bakmak olanaklıdır.
* w (Write): w komutunun da l komutu gibi iki kullanımı bulunmaktadır. l ye benzer olarak bellekten mantıksal sektörlere yazılabilir. (Windows NT de kısıtlamalar var.) Bu kullanımda parametreler l komutuyla aynıdır.
Dosya yazdırılacaksa yine n komutuyla birlikte kullanılır, kaç byte yazılacağı da BX:CX yazmaçlarında tutulur. Yazılacak verinin büyüklüğü 16lık sayı sistemine çevirilir. Düşük değerli 4 basamak CX'te yüksek değerli 4 basamak BX'de olmak üzere 32 bite kadar büyüklükte dosyalar yazılabilir.
Son olarak bunlardan başka X ile başlayan EMS ile ilgili komutlar bulunsa da EMS konusunda bilgim olmadığından bunlara değinmeyeceğim.
Dipnot: Yeni yazı maalesef yine çok uzunca bir
zaman gecikti. Mayıs ortasında diskim bozuldu, sonrasında önemli bir
sınava girdim ki yedekleme, işletim sistemi kurma, geri yüklemeyle ilgilenmekten sınava doğru dürüst çalışamadım bile. Haziran başında ülkenin içerisinde bulunduğu siyasi durumu izlemekten iş yerinde dahi doğru dürüst
çalışamadım. Haziran ortasında bir daha diskim bozuldu falan filan
derken Nisan'ın 13ünde başladığım yazıyı tamamlamam bugünü buldu.
Gecikmeden ötürü herkesten özür dilerim.
Hiç yorum yok:
Yorum Gönder