Aldığın hata çok genel bir hata. Mavi ile işaretlediğim bilgiye bakarak Microsoft'un açıklaması dahilinde şunu söyleyebiliyoruz:
Bu hata için birkaç farklı sebep olabiliyor: LARGE PAGE olan bir belleğe başvurulmuş olabilir, null pointer kullanılmış olabilir veya geçerli ancak sayfalanmamış bir havuz adresi kullanılmış da olabilir ki bunların kendi içinde her zaman ayrı açıklamaları vardır.
Rich (BB code):
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffffcb0a00000008, memory referenced.
Arg2: 0000000000000000, (reserved)
Arg3: fffff80590a25636, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 0000000000000002, (reserved)
Şimdi yeşil ile işaretlediğim ilk parametredeki PageFault'a neden olan adresi !pte komutunu kullanarak dökelim.
Rich (BB code):
7: kd> !pte ffffcb0a00000008
VA ffffcb0a00000008
PXE at FFFFF379BCDE6CB0 PPE at FFFFF379BCD96140 PDE at FFFFF379B2C28000 PTE at FFFFF36585000000
contains 0A00000117A02863 contains 0A00000117A03863 contains 0000000000000000
pfn 117a02 ---DA--KWEV pfn 117a03 ---DA--KWEV contains 0000000000000000
not valid
Gördüğün gibi, adres geçerli değil. (not valid) Bu da fiziksel bellekteki bir sayfayla eşleşmediği anlamına geliyor, bu nedenle bu adres için bir PageFault oluşturulmuş oluyor. Sistemin bunu çözebilmek için yeterli işleycisi varken neden bunu çözemediğini anlamaya çalıştım bu noktada. Bunun için ek olarak bu adresin pointer'ını döktüm.
Rich (BB code):
7: kd> dd ffffcb0a00000008 L2
ffffcb0a`00000008 ???????? ????????
Bu aslında ilgili belleğin freed bir bölgesine işaret ediyor. Bundan dolayı da sistemin işleyicileri tarafından bu geçersizlik durumunun çözülememesinin nedeni de bu oluyor. Buna kimin neden olduğunu da kırmızı ile işaretlediğim spn parametrede verilen adres ile ln komutunu kullanabiliyoruz.
Rich (BB code):
7: kd> ln fffff80590a25636
Browse module
Set bu breakpoint
(fffff805`90a254c4) nt!RtlpHpLfhOwnerMoveSubsegment+0x172 | (fffff805`90a256d0) nt!RtlpHpLfhSubsegmentReformatAsMulti
Aslında burada karşımıza çıkan fonksiyon Windows'a ait bir sembol sunucusuna dahil olduğu için ln komutunu başarıyla çalıştırdı. İlk parametredeki adrese erişmeye çalışan NT API'sine dahil fonksiyon da bu'dur.
Bu fonksiyonlar Microsoft tarafından listelenmiyorlar ve ne oldukları hiçbir zaman açıklanmıyor çünkü Windows'un iç yapısında dahillerdir ama isminden gidersek ne olduğuna dair aşağı yukarı LFH yapısına dahil bir bellek ayırma işleminde eğer ki boşta bir blok yoksa bu fonksiyon devreye girerek boş blokları farklı bir veri yapısına (Kullanılmış olanlar örneğin.) taşıyarak bellek parçalanmasının önüne geçiyor gibi bir yorum yapabiliriz.
Rich (BB code):
7: kd> k
# Child-SP RetAddr Call Site
00 ffffb38d`016fa078 fffff805`90873351 nt!KeBugCheckEx
01 ffffb38d`016fa080 fffff805`908913cf nt!MiSystemFault+0x735
02 ffffb38d`016fa170 fffff805`90c8aacb nt!MmAccessFault+0x2ff
03 ffffb38d`016fa2e0 fffff805`90a25636 nt!KiPageFault+0x38b
04 ffffb38d`016fa470 fffff805`90a251f8 nt!RtlpHpLfhOwnerMoveSubsegment+0x172
05 ffffb38d`016fa510 fffff805`90a25025 nt!RtlpHpLfhOwnerFreeListProcess+0xd8
06 ffffb38d`016fa570 fffff805`9098b0f6 nt!RtlpHpLfhOwnerRunMaintenance+0x119
07 ffffb38d`016fa5d0 fffff805`9098b063 nt!RtlpHpLfhOwnerCompact+0x102
08 ffffb38d`016fa750 fffff805`9098ae63 nt!RtlpHpLfhOwnerCompact+0x6f
09 ffffb38d`016fa8d0 fffff805`9098a7c5 nt!RtlpHpLfhContextCompact+0xa3
0a ffffb38d`016fa910 fffff805`9098a763 nt!RtlpHpHeapCompact+0x21
0b ffffb38d`016fa940 fffff805`90a68bd5 nt!ExpHpCompactHeapCallback+0x23
0c ffffb38d`016fa970 fffff805`90a68b35 nt!ExpHpEnumerateHeaps+0x71
0d ffffb38d`016fa9d0 fffff805`9093bf92 nt!ExpHpCompactionRoutine+0x15
0e ffffb38d`016faa00 fffff805`90a4d1fa nt!ExpWorkerThread+0x1b2
0f ffffb38d`016fabb0 fffff805`90c7ca94 nt!PspSystemThreadStartup+0x5a
10 ffffb38d`016fac00 00000000`00000000 nt!KiStartSystemThread+0x34
Yığının genel olarak LFH üzerinden ilerlediğini görebiliyoruz, kısaca LFH'nin de
Segment Heap yapısında dahil bir Stack yönetimi mekanizması olduğunu söyleyebiliriz. Ve genel olarak da svchost.exe ve diğer sistem işlemleri gibi belirli işlemlerde kullanıldığını ekleyebiliriz.
Rich (BB code):
7: kd> dt _SEGMENT_HEAP
nt!_SEGMENT_HEAP
+0x340 LfhContext : _HEAP_LFH_CONTEXT
Bizim bu noktada, ilk olarak, _SEGMENT_HEAP'te +0x340 ofsetinde olan ve _HEAP_LFH_CONTEXT'te 0x448 ofsetinde bir Bucket Tablosu saklayan bir anahtar olan _HEAP_LFH_CONTEXT'i anlamamız lazım. 0x448 ofsetindeki yapı, aşağıdaki yapısal ilişkilere sahip bir bucket tutuyor.
Rich (BB code):
7: kd> dt _HEAP_LFH_CONTEXT
nt!_HEAP_LFH_CONTEXT
+0x1c0 Buckets : [128] Ptr64 _HEAP_LFH_BUCKET
Farklı boyutlardaki Bucket Manager Pointer'ları bu BucketTable'da saklanıyor, aslında LFH en başta tahsis edilecek durumda değil. Daha sonra SegmentHeap yapısı incelenip doğrulandıktan sonra Nt!Rtlph... ile bu yapıyı başlatıyor.
Dosyada başladığını ve ilerleyişini görmüyoruz çünkü alt process'lerin hiçbir ntdll! yapısı minidump üzerinden tutulmuyor. Bundan dolayı elimizde olana direkt geçelim diyorum.
Bu konu hakkındaki elde edindiğim tüm bilgileri de aşağıdaki makaleden bulabilirsin. Devamına burdan ulaşabiliyorsun direkt olarak:
Senin yığınındaki
nt!RtlpHpLfhOwnerCompact, nt!RtlpHpLfhOwnerRunMaintenance RtlpHpLfhOwnerFreeListProcess gibi işlevlerden de gördüğümüz gibi sistem belli bir bellek aralığında LFH kullanımı ile bir boşluk yaratmaya çalışıyor. Şöyle ki LFH etkinleştirildiğinde bir Bucket Manager oluşturuyor ve bu manager pointer da en başta bahsettiğim BucketTable'ın ilgili Boyut İndeksinin konumuna yerleştiriliyor ve o an çalışan process yığınının bloklarının tahsisini incelemek için ancak bu noktadan başlamamız gerekiyor ki bu imkansız.
Dosyada bir process yok, Olsa bile bu process benim sistemime dahil değil ki o bile olsa Bu process'in çalıştığı bellek aralıklarını bulmak için de benim kodlamam gerekirdi. Bu da nerdeyse 3 imkansız koyuyor önümüze ama yine de yapılarını inceleyebiliyoruz.
Bu manger _HEAP_LFH_BUCKET adlı yukarıda gösterdiğim yapı aslında, bu yapı da önemli bir üye değişken yapısı _HEAP_LFH_SUBSEGMENT_OWNER'ı içeriyor.
Rich (BB code):
7: kd> dt _HEAP_LFH_SUBSEGMENT_OWNER
nt!_HEAP_LFH_SUBSEGMENT_OWNER
+0x000 IsBucket : Pos 0, 1 Bit
+0x000 BucketIndex : Pos 1, 7 Bits
+0x001 SlotCount : UChar
+0x001 AvailableSubsegmentCount : UChar
+0x002 BucketRef : Uint2B
+0x004 PrivateSlotMapRef : Uint2B
+0x006 HeatMapRef : Uint2B
+0x008 OwnerFreeList : _SINGLE_LIST_ENTRY
+0x008 Spare : Pos 0, 12 Bits
+0x010 Lock : Uint8B
+0x010 SlotStandbyEntry : _SINGLE_LIST_ENTRY
+0x010 PrivSlotListEntry : _HEAP_LFH_PTRREF_LIST
+0x014 OwnerThreadId : Uint4B
+0x018 AvailableSubsegmentList : _LIST_ENTRY
+0x028 FullSubsegmentList : _LIST_ENTRY
AvailableSubsegmentList Serbest durumda bulunan herhangi bir bloğun bucket listesini ve FullSubsegmentList de zaten dolu olan Bucket listesini ifade ediyorlar.
Bu iki liste her Bucket'ı depoluyorlar. Eğer ki LFH bir Bloğu tahsis ederse (Dosyada bu var.) Bucket Manager'daki AvailableSubsegementCount değerini kontrol ediyor, değeri 0'dan küçük veya 0'a eşitse, AvailableSubsegementList'i değerlendirmeye devam ediyor eğer ki AvailableSubsegmentList'te kullanılabilir bir liste yoksa değeri kendisine eşit oluyor. Buraya kadar anladığını düşünüyorum. Peki çöktüğümüz yer?
Dahası için de:
https://github.com/k0keoyo/SegmentHeapExt/blob/master/segmentheap/segmentheap.cpp
Sisteminin çöktüğü
nt!RtlpHpLfhOwnerMoveSubsegment fonksiyonu da bu yapıdaki bir bloğun boşaldığını tespit ederse bir alt segmenti (belirli bir boyuttaki blok grubu) bir listeden diğerine, örneğin bir FullSubsegmentList'ten AvailableSubsegmentList'e geri taşıyor. En başta kısaca söylediğim gibi.
Ek olarak basitçe anlatmak gerekiyorsa; Kovanın verecek hazır bellek bloğu yoksa, Windows: Önce “dolu” bir listeden bir tanesini geri dönüştürmeye çalışıyor (belki de artık gerçekten dolu değildir). Eğer yapamazsa, yeni bir tane yaratıyor. Bu fonksiyon bunu yapıyor.
En başta RAM sormamın sebebi de belkide bu yapıdaki bir bozukluğu bir RAM ya da spesifik olarak CPU hatası olarak yorumlamak istememdi. Şöyle ki hala bu olabilir ama fark ettiğim şey sistemin bu LFH biriminde bir error vermemesi oldu.
Ya gerçekten boş bir bellek alanına gitti bu yapıdaki işleyiş ya da bir sürücü gerçekten de işleyen bir dizinin sonunu veya bunu çevreleyen yapıdaki bağlantı pointer'larına bir şekilde zarar verdi. Veya bir object'i double free durumuna düşürdü.
Hatta başka bir ihtimal olarak sürücünün LIST_ENTRY yapısındaki bir şey heap yerine stack üzerinde ayrıldı ki bu olursa o an gönderilen fonksiyon geri döndüğünde sistem çökecektir.
Bunları tam detaylı anlamak bu dump üzerinden de imkansız. Bundan dolayı tek önerim eğer sistem tekrar mavi ekrana düşmediyse olduğu gibi kullanmaya devam etmen olacak. Tekrarlarsa sürücülerini kontrol etmekle başlayacağız.