Windows 7 ve WinAPI ile Doğrudan Disk Erişimi

Windows 7 işletim sistemi altında Windows API (WinAPI)’ları kullanarak doğrudan disk erişimin nasıl gerçekleştirilebileceğinden ve karşılaştığım birkaç problemden bahsetmeye çalışacağım elimden geldiğince.

Doğrudan disk erişimine, Bootloader programını yazıp derledikten sonra derlenmiş programları diskin ilgili sektörlerine yazdırma aşamasında ihtiyaç duydum. Örnek olarak MBR sektörüne müdahale ederken kullandığım kod bloğu üzerinden gidelim:

Kodu şöyle bir açıklayalım:

  • mbrBuffer ve fileBuffer dizilerinin genişliklerini, fiziksel sektör boyutu olan 512-Byte değerine ayarlıyorum. Dizi tipi TBytes olduğundan her bir eleman 1-Byte uzunluğunda olacak dolayısıyla değişkenlerim 512-Byte genişliğinde bir alanı temsil ediyor olacaklar.
  • CreateFile WinAPI kullanarak hedef dosyayı okuma izinleriyle açıyorum ve SetFilePointer WinAPI ile dosya üzerindeki işaretçiyi dosyanın başına taşıyorum.
  • ExcludePartTable boolean değişkeni, VCL form üzerinde setlenen bir değişken. 446 değeri de MBR içinde PartitionTable yapısının başladığı index değeri. MBR yapısı hakkında ön bilgiyi tezim ve wiki adreslerinden edinebilirsiniz. Kod içindeki kontrol de gene form üzerinden gelen bir atama ile dosyadan okuduğumuz veriyi diske yazarken, disk üzerindeki orjinal PartitionTable bilgilerini değiştirip değiştirmeyeceğimiz kararı ataması. Nihayetinde 446. byte tan itibaren 64-Byte PTable ve 2-Byte BootSigniture geliyor. 446’ya kadar istediğimiz yazabiliriz yani… Bunun haricinde eğer PTable’ı da diske yazacaksak yazılacak tüm veri 512-Byte olmalı. Yani mbrBuffer ı ileriki satırlarda düzenlerken tüm veriyi düzenlemiş olmalıyız. Bu da demek oluyor ki ya daha önceden MBR içeriğini yedekledim ve olduğu gibi geri koyuyorum, ya da kendi istediğim PTable değerlerini diske yazmak istiyorum. (Tüm bunları, disk bölümleme programlarının gerçekte disk üzerinde ne gibi değişiklikler yaptığı konusunda kafanızda ışık yakabilmesi adına böylesine uzatıyorum ;))
  • ReadFile WinAPI ile fileBuffer değişkenime en fazla 512-Byte okuma yapıyorum. Gerçekte ne kadar okunduğu bilgisi de API dökümantasyonunda belirtildiği üzere Read_File değişkenim aracılığıyla geri döndürülüyor. En fazla ifadesini özellikle kullanıyorum, onu da şöyle açıklayayım: N byte uzunluğunda bir dosya okumak istiyorsunuz ve M byte kadar okuyacaksınız.
    • Herhangi bir hata ile karşılaştığınızda ReadFile False değerini döndürecektir ve Read_File değişkeni 0 a setlenecektir.
    • Hata yok ve N=M ise dosyanın tamamını okumuş olacaksınız.
    • Hata yok ve N>M ise dosyadan M-Byte okuma yapmış olacaksınız.
    • N<M ise dosyadan M-Byte okumak istemenize rağmen N-Byte okumuş olacaksınız.

    Okuma işlemi sonrasında da CloseHandle ile dosyaya olan kaynağı serbest bırakıyorum.

  • Bir sonraki adım gene CreateFile ile fiziksel diske bir handle oluşturmak. Gene okuma izinleriyle açıp, SetFilePointer ile diskin en başına konumlanıp ilk sektörü mbrBuffer değişkenime okuyorum, 512-Byte kadar. DriveToManipulate değişkeni form üzerinde PhysicalDrive(n) olarak ayarlanıyor ve CreateFile a öylece parametre olarak iletiliyor. Bu durumu anlatan şöyle bir ekran görüntüsü ekleyebilirim mesela:
    Selecting physical drive to operate on

    Selecting physical drive to operate on

  • Bir sonraki adım dosyadan okuduğum veriyi diskten okuduğum veri içine yerleştirmek. Gayet basit bir şekilde bir repeat-until döngüsü içinde bu verileri birleştiriyorum-üzerine yazıyorum.
  • Son adım da gene CreateFile kullarak diske erişim sağlamak ve hazırlamış olduğum yeni MBR verisini diske geri yazmak. Diske bu sefer yazma izinleriyle erişim sağlamam gerekiyor.

Buraya kadar hiçbir sıkıntı yok. Her şey tek bir sektör üzerinden işliyor ve diskten okuma, diske geri yazma işlemleri sorunsuz bir şekilde halledilebiliyor. Bir diğer durum da Eğer diskteki MBR nin içeriğini incelerseniz disk üzerindeki ilk bölümün başlangıcının her zaman 0-1-1 CHS değerleri sınırından başladığını görürsünüz. Burada ilginç olan nokta, diskin, sisteme bildirdiği geometri bilgileridir. Biraz daha mercek altına alırsam, mevcut birincil diskimin 0-1-1 CHS değerleri, LBA modunda 63 numaralı fiziksel sektöre denk düştüğünü görüyorum (Cylinder 0, Head 1, Sector 1) (farklı disk geometrilerinde farklı bir sektörden başlayabilir, örneğin test ettiğim bir USB bellekte LBA 1023. sektöre denk gelmekteydi; ve evet, USB belleklerin de bir PartitionTable ı var; ve evet USB belleklerde birden fazla bölüm oluşturabilirsiniz :)). Bu durumun sebebi de disk bölümlerinin Cylinder yapısına aligned(hizalı) olması gerektiğidir. Bölüm başlangıç noktasına kadar herhangi bir dosya sisteminden bahselimeyeceğinden bu noktaya kadar olan sektörlere hiçbir sıkıntı oluşmadan erişip kafanıza göre düzenleyebilirsiniz :)

Şimdi yazının başlığını oluşturan konuya geçelim. Windows Vista’ya kadar olan işletim sistemlerinde yukarıda anlattığım teknikle diske doğrudan erişim sağlayıp herhangi bir sektör içeriğini okuyabilir ya da değiştirebiliyorsunuz. Windows Vista’dan itibaren işler biraz karışıyor. Şöyle ki, ReadFile işlemlerinde herhangi bir sorun olmuyorken WriteFile işlemlerinde bazı problemlerle kaşılaşıyoruz. Bazı spesifik durumlarda, ki birazdan bahsedeceğim, yazma işlemleri başarısızlıkla sonuçlanıyor. WriteFile hata ile karşılaşıp da oluşan hatayı GetLastError WinAPI ile aldığımızda, hatanın 5 kodlu AccessDenied hatası olduğunu görüyoruz. Önce örnek kodu vereyim ve sonrasında da bu hataya neden olan durumları kod üzerinden, alternatifler dahilinde değerlendirelim:

Önceki örneğe göre mevcut farklar, SetFilePointer WinAPI’a gönderilen konum bilgisinin hesaplanması durumu ve WriteFile WinAPI’da kullanılan VBR_TotalSize değeridir. Merak etmeyin, yazının sonunda kullanmış olduğum bu API’ların orjinal dökümantasyonlarına erişebileceğiniz adresleri vereceğim :)

Peki problemler neler? Öncelikle şunu söyleyeyim, tüm problemler, ilgili sektörün bir bölüm içinde olması durumunda, yani hedeflenen sektörün bir dosya sistemi üzerinde olması durumunda ortaya çıkıyor. Vista’dan itibaren gelen değişiklik bu. İşletim sistemi, artık, hiçbir kontrol mekanizması kurulmadan dosya sistemi üzerinde bu tarz doğrudan değişikliklere izin vermiyor. AccessDenied hatasının alınmasının sebebi tam olarak bu, Dosya Sistemi Koruması. Güzel bir uygulama aslında. Aslında-gerçekte ne yaptığını bilmeyenlere karşı alınmış güzel bir önlem. Problem ise şu, disk sektörlerine veri yazmak istediğimde, eğer yazmak istediğim veri birkaç sektöre yazılacaksa (n*512-Byte gibi), bunun tamamını işletim sistemi alt-sistemlerine gönderir işlemin tamamlanmasını beklerim. Performans açısından sektörlere teker teker yazmaktansa veriyi kümülatif göndermeyi ve geri kalan işlemleri işletim sisteminin halletmesini isterim. İşte bunu gerçekleştirmek istediğim zaman WriteFile hata ile geri dönüyor: AccessDenied. Çünkü az önce belirttiğim üzere işletim sistemi, mevcut dosya sistemine bu şekilde bir erişim izni vermiyor, CreteFile ile gerekli tüm izinleri almış olmama rağmen (Generic_Write “ve özellikle ve özellikle” File_Share_Write).

Performansı bir kenara bırakalım, verilerimiz doğru düzgün yazılsın yeter deyip de bir döngü içinde 512-Byte, 512-Byte yazmaya kalkıştığımızda ise ilk yazım işlemi sorunsuz gerçekleşirken, ikinci yazım işlemi ve takip eden tüm yazım işlemleri gene AccessDenied ile sonuçlanıyor. Ve sebebi gene işletim sisteminin dosya sistemini korumaya yönelik almış olduğu önlem… Yalnız, ilk işlemin neden başarıyla sonuçlandığına dair henüz herhangi bir bilgi edinemedim, araştırmalarım sürmekte.

Performansı daha da kötüleştirip mevcut uygulamaya daha bir overhead getirerek döngü içine tüm handle açma, konumlanma, yazma ve handle bırakma işlemlerini yerleştirip her seferinde yeni bir yazma işlemiymiş gibi davranmaya çalıştığımızda da gene aynı sonuçla karşılaşıyoruz: ilk işlem sorunsuz, ikinci işlemden itibaren AccessDenied… (Şimdi aklıma geldi de, buna bi de ses efekti olsa: <<Zoonnk!!>> Access DENIED!! Denied da büyük harflerle yalnız… :)))

Pekiiii, sorunun etrafından nasıl dolanacağız? Yardıma gene WinAPI yetişecek tabi:

Burada dikkat edilmesi gereken birkaç nokta var:

  • Bunlardan birincisi CreateFile ile aldığımız handle artık direkt olarak fiziksel disk değil de üzerine yazım yapılacak olan disk bölümü olmalı. Yazım izinlerinde herhangi bir değişiklik yok, yalnızca PhsicalDrive(n) den bölümün mount edilmiş sürücü harfine dönüşüm yapılmalı ki benim durumumda bu harf I: idi.
  • Bir diğeri işletim sisteminin I/O manager’ına ilgili bölümü unmount etmesi için bir emir gönderilmesi gerektiği. Bunu da DeviceIoControl WinAPI ile gerçekleştiriyoruz. Böylece mevcut oturumda, çalışan uygulama ilgili bölümü tekrar mount edene kadar ya da aldığı handle ı bırakana kadar başka hiçbir uygulamanın, ve işletim sisteminin user mode uygulamaları da dahil olmak üzere, bu bölüme erişememesini sağlıyoruz.
  • DefineDosDevice WinAPI ile de ilgili bölümün sürücü harfini serbest bırakıyoruz ve handle ı elinde tutan bu uygulamanın haricinde başka hiçbir uygulamanın bölüme erişememesini garanti altına alıyoruz.
  • SetFilePointer ile gerçek sektör numarasına konumlanmamamız gerekiyor, çünkü aldığımız handle bölüm üzerine olduğundan dolayı mantıksal sektör organizasyonu, daha doğrusu mantıksal dosya başı 0. sektöre denk düşüyor.

Bu gibi noktalara dikkat edip kodu ihtiyaçlara yönelik düzenlediğimiz zaman, o Zonnk! larla, AccessDenied larla karşılaşmıyoruz ve diske doğrudan yazım işlemleri sorunsuzca gerçekleşiyor :))

Dikkat ettiyseniz verdiğim örneklerde herhangi bir hata kontrolü uygulanmamış. Mazeretim, vaktimin dar olmasıydı :) Bu tarz sistem kaynakları üzerinde manipülasyonlar yapıyorken kesinlikle hata kontrollerini eksik etmeyin, her API’ın dönüş değerini kesinlikle değerlendirme altına alın ve sıradaki kodu işlemeye öyle devam ettirin.. Bu tarz sistem kaynaklarına erişim işlemleri, üzerinde birkaç defa düşünme gerektiren işlemlerdir. Örneğin MBR’ı bir kez bozarsanız, geri almak için baya uğraşırsınız. Gene MBR üzerinden gidersem, kodu bozmanız sıkıntı değil, işletim sisteminin Recovery Console uygulamaları var, ama Partition Table’ı bozarsanız başınıza büyük bir iş açarsınız.. Şimdiden uyarayım :)

Ve bahsini ettiğim WinAPI dökümantasyonları:

Ve son tavsiye: MSDN dökümantasyonlarını didik didik edin, çok sağlam bilgiler var :)

Yer işareti koy Kalıcı Bağlantı.

Windows 7 ve WinAPI ile Doğrudan Disk Erişimi için 1 cevap

  1. Geri izleme:HaPG - Deneysel Parser Generator - ASM Syntax

Bir Cevap Yazın