Home Derinlemesine Windows Objects ve Handles (0x02)
Post
Cancel

Derinlemesine Windows Objects ve Handles (0x02)

banner

Object Structure

Objelerin her birinin aynı structure’da olduğunu ve sadece birer bellek parçalarından ibaret olduğundan önceki yazıda bahsetmiştik. Her obje kabaca header ve body kısımlarından oluşur. Object manager sadece nesnenin header’ını ve footer’ını yönetir ve body, nesne türüne göre bir executive sub-system tarafından yönetilir. Örneğin, bir file objesini, executive sub-system olan I/O manager tarafından yönetilir.

Her objenin header’ı vardır ve aynı yapıdadır, fakat her objenin body kısmı aynı değildir. File (FILE_OBJECT), Device (DEVICE_OBJECT), Process (EPROCESS) vb. aslında body kısmındaki verilerdir ve bir objenin tipi ise objenin header’ında tutulan verilerle anlaşılır. Aşağıda objelerin genel yapısının diyagramı verilmiştir:

Object Header

Objelerin structure’ına bakarken ilk öğrenmemiz gereken kısım objenin başlığıdır. Bu başlık; objeyi etkileyen önemli flagler, objede kullanılacak sub-header’lar, objenin tipi vb. gibi önemli bilgileri barındırır. Burada x64 sistemdeki object header yapısını görebilirsiniz:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lkd> dt nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int8B
   +0x008 HandleCount      : Int8B
   +0x008 NextToFree       : Ptr64 Void
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : UChar
   +0x019 TraceFlags       : UChar
   +0x019 DbgRefTrace      : Pos 0, 1 Bit
   +0x019 DbgTracePermanent : Pos 1, 1 Bit
   +0x01a InfoMask         : UChar
   +0x01b Flags            : UChar
   +0x01b NewObject        : Pos 0, 1 Bit
   +0x01b KernelObject     : Pos 1, 1 Bit
   +0x01b KernelOnlyAccess : Pos 2, 1 Bit
   +0x01b ExclusiveObject  : Pos 3, 1 Bit
   +0x01b PermanentObject  : Pos 4, 1 Bit
   +0x01b DefaultSecurityQuota : Pos 5, 1 Bit
   +0x01b SingleHandleEntry : Pos 6, 1 Bit
   +0x01b DeletedInline    : Pos 7, 1 Bit
   +0x01c Reserved         : Uint4B
   +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : Ptr64 Void
   +0x028 SecurityDescriptor : Ptr64 Void
   +0x030 Body             : _QUAD

Bu yazı windows 10 x64 işletim sistemi versiyonuna göre yazıldığı için, bu versiyonun object header structure’ı aşağıdaki gibidir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//0x38 bytes (sizeof)
struct _OBJECT_HEADER
{
    LONGLONG PointerCount;                                                  //0x0
    union
    {
        LONGLONG HandleCount;                                               //0x8
        VOID* NextToFree;                                                   //0x8
    };
    struct _EX_PUSH_LOCK Lock;                                              //0x10
    UCHAR TypeIndex;                                                        //0x18
    union
    {
        UCHAR TraceFlags;                                                   //0x19
        struct
        {
            UCHAR DbgRefTrace:1;                                            //0x19
            UCHAR DbgTracePermanent:1;                                      //0x19
        };
    };
    UCHAR InfoMask;                                                         //0x1a
    union
    {
        UCHAR Flags;                                                        //0x1b
        struct
        {
            UCHAR NewObject:1;                                              //0x1b
            UCHAR KernelObject:1;                                           //0x1b
            UCHAR KernelOnlyAccess:1;                                       //0x1b
            UCHAR ExclusiveObject:1;                                        //0x1b
            UCHAR PermanentObject:1;                                        //0x1b
            UCHAR DefaultSecurityQuota:1;                                   //0x1b
            UCHAR SingleHandleEntry:1;                                      //0x1b
            UCHAR DeletedInline:1;                                          //0x1b
        };
    };
    ULONG Reserved;                                                         //0x1c
    union
    {
        struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo;                //0x20
        VOID* QuotaBlockCharged;                                            //0x20
    };
    VOID* SecurityDescriptor;                                               //0x28
    struct _QUAD Body;                                                      //0x30
}; 

Object header, Windows x86 mimarilerde 0x18 byte, x64 mimarilerde ise 0x30 byte’tır. Bu detay objenin header kısmını ayrıştırırken veya header’ı/objeyi ilgilendiren Ob fonksiyonlarını anlamada işimize yarayabilir.

1
2
lkd> ?? sizeof(nt!_OBJECT_HEADER)
unsigned int64 0x38

PointerCount, Windows’taki nesnelerin geçerli işaretçi sayısını takip etmek için kullanılan bir alanı ifade eder. Bu sayı, nesneye doğrudan veya dolaylı olarak yapılan referansların toplam sayısını ifade eder. Yani nesneye yapılan tüm belirteçlerin sayısı, PointerCount değerini oluşturur.

HandleCount, bir nesneye atanmış olan ve halen kullanımda olan handle sayısını belirtir. Bu, nesnenin aktif handle sayısını ifade eder. Handle, bir nesneye erişmek için kullanılan bir numaralı referanstır. HandleCount, bir nesneye açık handle sayısını kaydeder ve silinen (ancak hala bekleyen) handle’ları da hesaba katar. Bir nesnenin bellekten silinip silinmeyeceğini belirlemek için HandleCount alanı kullanılır. HandleCount, açık handle sayısını takip eder ve 0 olunca nesne bellekten silinir. Bu nedenle, hangi nesnenin bellekten silineceği, HandleCount’un 0 olması ile belirlenir.

Her iki alan da, bir nesneye olan referansları takip etmek için kullanılır. Ancak, PointerCount, nesneye olan toplam referans sayısını kaydederken, HandleCount yalnızca kullanımda olan handle’ların sayısını kaydeder. Kısacası PointerCount nesneye olan açık referansları ve HandleCount kullanımda olan açık handle’ları sayar. Bu nedenle iki alan arasındaki fark nesne için henüz silinmemiş olan ancak açık olmayan referansları temsil eder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
lkd> dx (nt!_OBJECT_HEADER*)0xffffab8443d3e080
(nt!_OBJECT_HEADER*)0xffffab8443d3e080                 : 0xffffab8443d3e080 [Type: _OBJECT_HEADER *]
    [+0x000] PointerCount     : 3 [Type: __int64]
    [+0x008] HandleCount      : -92890414718840 [Type: __int64]
    [+0x008] NextToFree       : 0xffffab8443d3e088 [Type: void *]
    [+0x010] Lock             [Type: _EX_PUSH_LOCK]
    [+0x018] TypeIndex        : 0x98 [Type: unsigned char]
    [+0x019] TraceFlags       : 0xe0 [Type: unsigned char]
    [+0x019 ( 0: 0)] DbgRefTrace      : 0x0 [Type: unsigned char]
    [+0x019 ( 1: 1)] DbgTracePermanent : 0x0 [Type: unsigned char]
    [+0x01a] InfoMask         : 0xd3 [Type: unsigned char]
    [+0x01b] Flags            : 0x43 [Type: unsigned char]
    [+0x01b ( 0: 0)] NewObject        : 0x1 [Type: unsigned char]
    [+0x01b ( 1: 1)] KernelObject     : 0x1 [Type: unsigned char]
    [+0x01b ( 2: 2)] KernelOnlyAccess : 0x0 [Type: unsigned char]
    [+0x01b ( 3: 3)] ExclusiveObject  : 0x0 [Type: unsigned char]
    [+0x01b ( 4: 4)] PermanentObject  : 0x0 [Type: unsigned char]
    [+0x01b ( 5: 5)] DefaultSecurityQuota : 0x0 [Type: unsigned char]
    [+0x01b ( 6: 6)] SingleHandleEntry : 0x1 [Type: unsigned char]
    [+0x01b ( 7: 7)] DeletedInline    : 0x0 [Type: unsigned char]
    [+0x01c] Reserved         : 0xffffab84 [Type: unsigned long]
    [+0x020] ObjectCreateInfo : 0xffffab8443d3e098 [Type: _OBJECT_CREATE_INFORMATION *]
    [+0x020] QuotaBlockCharged : 0xffffab8443d3e098 [Type: void *]
    [+0x028] SecurityDescriptor : 0x1d7bd0002 [Type: void *]
    [+0x030] Body             [Type: _QUAD]
    ObjectName       : ""
    ObjectType       : WmiGuid

Diğer önemli kısımlardan devam etmek gerekirse, TypeIndex obje tipini bulmak için kullanılan 8 bitlik bir değerdir. Bu tip, nesnenin body kısmını yönetecek executive sub-system’ı belirleyecektir.

InfoMask bölümü ise hangi sub-header’ın kullanılacağını belirten bir bit maskesidir. Windows 10’da 8 adet optional header vardır. Bu bölüm ayrı bir başlıkta detaylı olarak yazının ilerisinde işlenmiştir.

Objenin özelliklerini temsil eden bölüm ise Flags bölümüdür. Object header’da dikkat ederseniz Flags bölümünün altında 8 adet char tipi bölüm vardır.

1
2
3
4
5
6
7
8
[+0x01b ( 0: 0)] NewObject        : 0x1 [Type: unsigned char]
[+0x01b ( 1: 1)] KernelObject     : 0x1 [Type: unsigned char]
[+0x01b ( 2: 2)] KernelOnlyAccess : 0x0 [Type: unsigned char]
[+0x01b ( 3: 3)] ExclusiveObject  : 0x0 [Type: unsigned char]
[+0x01b ( 4: 4)] PermanentObject  : 0x0 [Type: unsigned char]
[+0x01b ( 5: 5)] DefaultSecurityQuota : 0x0 [Type: unsigned char]
[+0x01b ( 6: 6)] SingleHandleEntry : 0x1 [Type: unsigned char]
[+0x01b ( 7: 7)] DeletedInline    : 0x0 [Type: unsigned char]

Bu alanlar aslında hangi flag’lerin açık olduğunu gösterir. Her birisi farklı bir anlam taşır, bu bölüm hakkında daha fazla bilgi yazısının ileriki kısımlarında verilmiştir.

SecurityDescriptor objeyi kimin kullanabileceğini ve objeyle neler yapabileceğine dait güvenlik özelliklerini belirler.

Optional Header’lar

Windows’un eski sürümlerinde sırasıyla:

  • OBJECT_HEADER_NAME_INFO

  • OBJECT_HEADER_HANDLE_INFO

  • OBJECT_HEADER_QUOTA_INFO

sub-header’lar varlığını belirtmek için OBJECT_HEADER old version’da olan

  • NameInfoOffset

  • HandleInfoOffset

  • QuotaInfoOffset

bölümlerini kullanırdı. Bu xxxInfoOffset alanları, OBJECT_HEADER yapısının başlangıcından itibaren karşılık gelen OBJECT_HEADER_xxx_INFO yapısının ofsetini belirleyen bir sayı (0 ile 265 arasında) içerir. Bu alanlardan herhangi birinin değeri sıfırsa, object manager karşılık gelen başlığın var olmadığını varsayar. Varlığı xxxInfoOffset alanları yerine OBJECT_HEADER->Flag alanında bir bit tarafından kontrol edilen başka bir sub-header, yani OBJECT_HEADER_CREATOR_INFO vardır.

Object manager basitçe hangilerinin mevcut olduğunu öğrenmek için OBJECT_HEADER başlangıcından itibaren ilgili ofsetlerini hesaplamak için kullanabilir.

Aşağıdaki şekilde, sub-header’lar ve bunların ofsetlerinin yerleşimi gösterilmektedir:

Yukarıdaki 4 optional header’ların boyutları sabit olduğundan ve her zaman belirli bir sırada saklandıklarından, bu yapılara ofsetleri depolamaya çok az ihtiyaç vardır. Bu nedenle, Windows 7’de, 3 xxInfoOffsets alanı, her bir sub-header için bir bit içeren ve varlığını gösteren tek bir OBJECT_HEADER->InfoMask alanıyla değiştirilmiştir.

Yukarıdaki yapı windows object header’larda old version için geçerliydi. New version için InfoMask adında bir bölüm getirilerek sub-header’ları belirtmek için bir bit maskeleme yöntemi kullanılmaya karar verildi. Ayrıca new version ile farklı sub-header’lar da eklendi. Tüm sub-header’ları ve bit flag’lerini görebilmek için aşağıdaki tabloya bakabilirsiniz:

BitBitmask HeaderOptional Object HeaderDescriptionOffset
0x01ObjectCreatorInfont!_OBJECT_HEADER_CREATOR_INFOAynı türdeki tüm nesnelerin listesini ve nesneyi oluşturan işlemi işaret eden bir işaretçi içerirObpInfoMaskToOffset[0])
0x02ObjectNameInfont!_OBJECT_HEADER_NAME_INFOAdlandırılmış bir nesne ise nesnenin adını ve nerede olduğunu belirten bir _OBJECT_DIRECTORY yapısı işaretçisi içerirObpInfoMaskToOffset[InfoMask & 0x3]
0x04ObjectHandleInfont!_OBJECT_HEADER_HANDLE_INFONesneye açık bir handle’ı olan tüm process’leri track’lemek için kullanılır. Bu yüzden _OBJECT_HANDLE_COUNT_ENTRY yapısını veya bir _OBJECT_HANDLE_COUNT_DATABASE yapısı içerir, bu da _OBJECT_HANDLE_COUNT_ENTRY listesi ve toplam girdi sayısını içerirObpInfoMaskToOffset[InfoMask & 0x7]
0x08ObjectQuotaInfont!_OBJECT_HEADER_QUOTA_INFONesneye ayrılan bellek kaynaklarıyla ilgili bilgileri içerir.ObpInfoMaskToOffset[InfoMask & 0xF]
0x10ObjectProcessInfont!_OBJECT_HEADER_PROCESS_INFONesnenin sahibinin yürütme işlem yapısı işaretçisine sahiptir.ObpInfoMaskToOffset[InfoMask & 0x1F]
0x20ObjectAuditInfont!_OBJECT_HEADER_AUDIT_INFOFile objeleri için Audit yani denetim yapılacaksa bu header kullanılır.ObpInfoMaskToOffset[InfoMask & 0x3F]
0x40ObjectExtendedInfont!_OBJECT_HEADER_EXTENDED_INFOObject Footer’ının pointer’ını tutar. Yani bu header sadece objenin footer’ı varsa kullanılır.ObpInfoMaskToOffset[InfoMask & 0x7F]
0x80ObjectPaddingInfont!_OBJECT_HEADER_PADDING_INFOProcess ve Thread objeleri bu bayrağı set etmelidir. Object Body kısmının bellekteki hizzalamasını ayarlar.ObpInfoMaskToOffset[InfoMask & 0xFF]

Peki bu optional header’lardan hangileri açık? Ve açık olan header’ların bilgilerini nasıl görüntüleyebiliriz? Bunun için önce InfoMask alanına bakmalıyız. Bu alan hangi header’ların açık olduğunu bize gösterebilir.

Aşağıda adım adım hangi optional header’ın objede olup olmadığını bulma gösterilmiştir:

  1. Sub-header’ların tablosuna baktığınızda Bit sütununu göreceksiniz.

  2. Tüm sub-header’ların bitleri ile InfoMask değeri AND işlemine sokulur.

  3. Eğer sonuç False değilse (0 harici bir değer) ilgili objede AND işlemine sokulan bite sahip optional header açık demektir.

Örneğin aşağıdaki gibi tek tek AND işlemine sokularak açık olan sub-header’lar bulunmuştur:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lkd> dt nt!_OBJECT_HEADER ffff948ed404c050 -y Inf*
   +0x01a InfoMask : 0x88 ''
lkd> ? 0x88 & 0x1
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0x88 & 0x2
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0x88 & 0x4
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0x88 & 0x8
Evaluate expression: 8 = 00000000`00000008
lkd> ? 0x88 & 0x10
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0x88 & 0x20
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0x88 & 0x40
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0x88 & 0x80
Evaluate expression: 128 = 00000000`00000080

Görüldüğü gibi 8 ve 128 yani ObjectQuotaInfo ve ObjectPaddingInfo header’ı şu anda açık. Peki açık olan header’ları nasıl görüntüleyebilirim? Sub-header’ların bellekteki yerlerini alma sırasını ve işletim sistemi mimarisine göre boyutlarını bilmeliyiz. x64 sistemde optional header’ların boyutları aşağıdaki gibidir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lkd> ?? sizeof(nt!_OBJECT_HEADER_PADDING_INFO)
unsigned int64 4
lkd> ?? sizeof(nt!_OBJECT_HEADER_EXTENDED_INFO)
unsigned int64 0x10
lkd> ?? sizeof(nt!_OBJECT_HEADER_AUDIT_INFO)
unsigned int64 0x10
lkd> ?? sizeof(nt!_OBJECT_HEADER_PROCESS_INFO)
unsigned int64 0x10
lkd> ?? sizeof(nt!_OBJECT_HEADER_QUOTA_INFO)
unsigned int64 0x20
lkd> ?? sizeof(nt!_OBJECT_HEADER_HANDLE_INFO)
unsigned int64 0x10
lkd> ?? sizeof(nt!_OBJECT_HEADER_NAME_INFO)
unsigned int64 0x20
lkd> ?? sizeof(nt!_OBJECT_HEADER_CREATOR_INFO)
unsigned int64 0x20
lkd> ?? sizeof(nt!_OBJECT_HEADER)
unsigned int64 0x38

Açık olan sub-header’ın içerisindeki bilgileri görüntüleyebilmek için, offsetini hesaplamamız gerekiyor. Deminki AND işlemini yaptığımız örnekte ObjectQuotaInfo ve ObjectPaddingInfo header’larının açık olduğunu bulduk. Yukarıdaki boyutlara göre 0x20 ve 0x4 bizim offset hesaplayabilmemiz için gerekli verilerdir. Fakat ondan önce aşağıdaki diyagrama iyice bakın. Çünkü offset hesaplanırken bu diyagramda verilen sıra referans alınacaktır (bellek dağılımına göre çizilmiştir).

Object Header’a en yakın ObjectQuotaInfo’nun boyutu 0x4 idi. O zaman aşağıdaki gibi Object Header’ın adresinden 0x4 çıkarıp ObjectQuotaInfo başlığını aşağıdaki gibi görüntüleyebiliriz:

1
2
3
4
5
6
7
lkd> dt nt!_OBJECT_HEADER_QUOTA_INFO ffff948ed404c050-0x20
   +0x000 PagedPoolCharge  : 0x1000
   +0x004 NonPagedPoolCharge : 0xc48
   +0x008 SecurityDescriptorCharge : 0x78
   +0x00c Reserved1        : 0
   +0x010 SecurityDescriptorQuotaBlock : 0xffff948e`c4cb1d40 Void
   +0x018 Reserved2        : 0

İkinci açık olan header ise ObjectPaddingInfo’dur ve header boyutu 0x20 dir. Fakat bu başlıktan önce ObjectQuotaInfo başlığı açık olduğundan bu başlığın da değerini (0x4) üzerine eklenip offset bulunur. Aşağıdaki gibi ObjectPaddingInfo başlığının offsetini hesaplayıp görüntüleyebiliriz:

1
2
lkd> dt nt!_OBJECT_HEADER_PADDING_INFO ffff948ed404c050-0x20-0x4
   +0x000 PaddingAmount    : 0x20

Bu uygulamanın daha iyi anlaşılması adına yapılan örneği temsil eden diyagram aşağıdaki gibidir:

Optional header’ların offsetini hesaplamak bu yöntemle görüldüğü gibi uzun bir zaman alıyor. Daha kolay ikinci yol; tüm sub-header’ların offset yerleşimlerini tutan bir pointer array vardır ve bu array’e ObpInfoMaskToOffset denir. Bu array, object header’ınızın versiyonuna göre değişiklik gösterir.

Bu ofsetler, optional header’ların InfoMask bit dağılımı kombinasyonlarının tümü için mevcuttur

Toplamda 8 adet sub-header olduğuna göre pointer array boyutunun 2^8 olması gerekiyor, yani 256 byte’lık bir hafızayı ObInitSystem içerisinde allocate eder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ULONG i = 0;
ULONG offset = 0;
BYTE ObpInfoMaskToOffset[256];

do
{
    offset = 0;
    if ( i & OB_INFOMASK_CREATOR_INFO )
        offset = sizeof(_OBJECT_HEADER_CREATOR_INFO);
    if ( i & OB_INFOMASK_NAME )
        offset += sizeof(_OBJECT_HEADER_NAME_INFO);
    if ( i & OB_INFOMASK_HANDLE )
        offset += sizeof(_OBJECT_HEADER_HANDLE_INFO);
    if ( i & OB_INFOMASK_QUOTA )
        offset += sizeof(_OBJECT_HEADER_QUOTA_INFO);
    if ( i & OB_INFOMASK_PROCESS_INFO )
        offset += sizeof(_OBJECT_HEADER_PROCESS_INFO);
    if ( i & OB_INFOMASK_AUDIT_INFO )
        offset += sizeof(_OBJECT_HEADER_AUDIT_INFO);
    if ( i & OB_INFOMASK_EXTEND_INFO )
        offset += sizeof(_OBJECT_HEADER_EXTEND_INFO);
    if ( i & OB_INFOMASK_PADDING_INFO )
        offset += sizeof(_OBJECT_HEADER_PADDING_INFO);

    ObpInfoMaskToOffset[i++] = offset; // anlık hesaplanan toplam offset 

} while(i<256);

Şimdi yukarıdaki kodu doğrulamak adına ObpInfoMaskToOffset’i görüntüleyelim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
lkd> db nt!ObpInfoMaskToOffset l100
fffff800`0aa25e60  00 20 20 40 10 30 30 50-20 40 40 60 30 50 50 70  .  @.00P @@`0PPp
fffff800`0aa25e70  10 30 30 50 20 40 40 60-30 50 50 70 40 60 60 80  .00P @@`0PPp@``.
fffff800`0aa25e80  10 30 30 50 20 40 40 60-30 50 50 70 40 60 60 80  .00P @@`0PPp@``.
fffff800`0aa25e90  20 40 40 60 30 50 50 70-40 60 60 80 50 70 70 90   @@`0PPp@``.Ppp.
fffff800`0aa25ea0  10 30 30 50 20 40 40 60-30 50 50 70 40 60 60 80  .00P @@`0PPp@``.
fffff800`0aa25eb0  20 40 40 60 30 50 50 70-40 60 60 80 50 70 70 90   @@`0PPp@``.Ppp.
fffff800`0aa25ec0  20 40 40 60 30 50 50 70-40 60 60 80 50 70 70 90   @@`0PPp@``.Ppp.
fffff800`0aa25ed0  30 50 50 70 40 60 60 80-50 70 70 90 60 80 80 a0  0PPp@``.Ppp.`...
fffff800`0aa25ee0  04 24 24 44 14 34 34 54-24 44 44 64 34 54 54 74  .$$D.44T$DDd4TTt
fffff800`0aa25ef0  14 34 34 54 24 44 44 64-34 54 54 74 44 64 64 84  .44T$DDd4TTtDdd.
fffff800`0aa25f00  14 34 34 54 24 44 44 64-34 54 54 74 44 64 64 84  .44T$DDd4TTtDdd.
fffff800`0aa25f10  24 44 44 64 34 54 54 74-44 64 64 84 54 74 74 94  $DDd4TTtDdd.Ttt.
fffff800`0aa25f20  14 34 34 54 24 44 44 64-34 54 54 74 44 64 64 84  .44T$DDd4TTtDdd.
fffff800`0aa25f30  24 44 44 64 34 54 54 74-44 64 64 84 54 74 74 94  $DDd4TTtDdd.Ttt.
fffff800`0aa25f40  24 44 44 64 34 54 54 74-44 64 64 84 54 74 74 94  $DDd4TTtDdd.Ttt.
fffff800`0aa25f50  34 54 54 74 44 64 64 84-54 74 74 94 64 84 84 a4  4TTtDdd.Ttt.d...

sonuncu index tüm header’ların açık olduğu offset değeridir. Bu da demek oluyorki 0x100 yani decimal olarak 255 değeri InfoMask olmasıdır. O halde verilen koda bakılırsa tüm header’ların toplam boyutu da son index değeri yani a4 olması gerekiyor. Hızlıca bir hesap yapmak gerekirse:

1
2
lkd> ? 4 + 10 + 10 + 10 + 20 + 10 + 20 + 20
Evaluate expression: 164 = 00000000`000000a4

Peki nasıl kullanılır bu ObpInfoMaskToOffset? Tam olarak aşağıdaki gibi bir mantıkla kullanılır

Offset = ObpInfoMaskToOffset[OBJECT_HEADER->InfoMask & (DesiredHeaderBit | (DesiredHeaderBit-1))]

En son manuel olarak hespaladığımız offset değerini padding header için 24, quota header için ise 20 olarak bulduk. Şimdi bu offsetleri kolay yoldan bulmak için şöyle bir yol izlenebilir:

1
2
3
4
lkd> ?? ((unsigned char *)@@masm(nt!ObpInfoMaskToOffset))[0x88 & (0x8 | (0x8-1))]
unsigned char 0x20 ' '
lkd> ?? ((unsigned char *)@@masm(nt!ObpInfoMaskToOffset))[0x88 & (0x80 | (0x80-1))]
unsigned char 0x24 '$'

Fakat her türlü InfoMask değerinden hangi optional header’ların set edildiğini bulmak gereklidir.

Son olarak bilinmesi gerekilen iki farklı

Obje gövdesinin(body) sonunda ayrılır ve ancak _OBJECT_HEADER_EXTEND_INFO set edilmişse footer objeye dahil olur. Neden mi? Çünkü zaten ExtendedUserInfo, footer’ın pointer’ını tutar. Aşağıya x64 Extended sub-header c++ structure’ını bırakıyorum.

1
2
3
4
5
6
//0x10 bytes (sizeof)
struct _OBJECT_HEADER_EXTENDED_INFO
{
    struct _OBJECT_FOOTER* Footer;                                          //0x0
    ULONGLONG Reserved;                                                     //0x8
}; 

Object footer internalde _OBJECT_FOOTER olarak geçer ve iki bölümden oluşur:

  1. HandleRevocationInfo: File ve Key objeleri bu footer ile oluşturulur. Objenin ObCreateObjectEx fonksiyonu ile oluşturulması şarttır.

  2. ExtendedUserInfo: Silo Context objeleri bu footer ile oluşturulur. Objenin ObCreateObjectEx fonksiyonu ile oluşturulması şarttır.

Örnek olarak random bir File objesinin adresini bulalım:

random_file_object_find_01

Daha sonra bu objenin header’ındaki InfoMask’i hesaplamak için görüntüleyelim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lkd> dt nt!_OBJECT_HEADER 0xffff948ed285c9b0-30
   +0x000 PointerCount     : 0n32767
   +0x008 HandleCount      : 0n1
   +0x008 NextToFree       : 0x00000000`00000001 Void
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : 0x68 'h'
   +0x019 TraceFlags       : 0x1 ''
   +0x019 DbgRefTrace      : 0y1
   +0x019 DbgTracePermanent : 0y0
   +0x01a InfoMask         : 0x4c 'L'
   +0x01b Flags            : 0 ''
   +0x01b NewObject        : 0y0
   +0x01b KernelObject     : 0y0
   +0x01b KernelOnlyAccess : 0y0
   +0x01b ExclusiveObject  : 0y0
   +0x01b PermanentObject  : 0y0
   +0x01b DefaultSecurityQuota : 0y0
   +0x01b SingleHandleEntry : 0y0
   +0x01b DeletedInline    : 0y0
   +0x01c Reserved         : 0
   +0x020 ObjectCreateInfo : 0xffff948e`c4cb1d40 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : 0xffff948e`c4cb1d40 Void
   +0x028 SecurityDescriptor : (null)
   +0x030 Body             : _QUAD

InfoMask 0x4c = 0100 1100 değerindedir ve ObjectExtendedInfo 0x40 = 0100 0000 değerindedir. Görüldüğü gibi bu objenin footer’ı var ve geriye kalan sadece _OBJECT_HEADER_EXTEND_INFO yapısının offsetini hesaplayıp görüntülemek olacak:

1
2
3
4
5
lkd> ?? ((unsigned char *)@@masm(nt!ObpInfoMaskToOffset))[0x4c & (0x40 | (0x40-1))]
unsigned char 0x40 '@'
lkd> dt nt!_OBJECT_HEADER_EXTENDED_INFO 0xffff948ed285c9b0-30-40
   +0x000 Footer           : 0xffff948e`d285ca88 _OBJECT_FOOTER
   +0x008 Reserved         : 0x4e564441`00000134

Footer bölümündeki pointer’ı da _OBJECT_FOOTER structure’ına uyarladığımızda objenin footer’ına ulaşmış olacağız:

1
2
3
lkd> dt nt!_OBJECT_FOOTER 0xffff948e`d285ca88
   +0x000 HandleRevocationInfo : _HANDLE_REVOCATION_INFO
   +0x020 ExtendedUserInfo : _OB_EXTENDED_USER_INFO

Hatırlarsanız File ve Key objeleri HandleRevocationInfo ile oluşuyordu. Son adımda objenin footer’ı aşağıdaki gibidir:

1
2
3
4
5
6
7
lkd> dx ((nt!_OBJECT_FOOTER*)0xffff948e`d285ca88)->HandleRevocationInfo
((nt!_OBJECT_FOOTER*)0xffff948e`d285ca88)->HandleRevocationInfo                 [Type: _HANDLE_REVOCATION_INFO]
    [+0x000] ListEntry        [Type: _LIST_ENTRY]
    [+0x010] RevocationBlock  : 0x0 [Type: _OB_HANDLE_REVOCATION_BLOCK *]
    [+0x018] AllowHandleRevocation : 0x1 [Type: unsigned char]
    [+0x019] Padding1         [Type: unsigned char [3]]
    [+0x01c] Padding2         [Type: unsigned char [4]]

Objelerin Flag’leri

Bir objeyi oluşturma sırasında ve/veya objeye erişildiğinde davranışını değiştirmek için kullanılan başka bir bit maskeleme yöntemi kullanır. Bu alan, obje oluşturulurken _OBJECT_ATTRIBUTES yapısının Attributes alanından bazı verilerle doldurulur ve bu veriler Object Manager’a iletilir. Bu alana gelebilecek değerler aşağıdaki gibi tanımlanmıştır:

1
2
3
4
5
6
7
8
#define OB_FLAG_NEW_OBJECT              0x01
#define OB_FLAG_KERNEL_OBJECT           0x02
#define OB_FLAG_CREATOR_INFO            0x04
#define OB_FLAG_EXCLUSIVE_OBJECT        0x08
#define OB_FLAG_PERMANENT_OBJECT        0x10
#define OB_FLAG_DEFAULT_SECURITY_QUOTA  0x20
#define OB_FLAG_SINGLE_HANDLE_ENTRY     0x40
#define OB_FLAG_DELETED_INLINE          0x80

Bununla beraber 3 adet daha flag vardır fakat sadece runtime’da kullanılır. Yani bu bayraklar diğer bayraklar gibi store edilmiyor. OBJ_CASE_INSENSITIVE, OBJ_OPENIF, OBJ_OPENLINK.

Bitmask FlagObject Manager FlagAçıklama
NewObjectOB_FLAG_NEW_OBJECT (0x01)Objenin yaratıldığını ancak objenin namespace’e henüz yerleştirilmediğini belirler.
KernelObjectOB_FLAG_KERNEL_OBJECT (0x02)Objenin yalnızca kernel handle’larına sahip olabileceğini belirler.
KernelOnlyAccessOB_FLAG_CREATOR_INFO (0x04)Objenin yalnızca çekirdek modunda (kernel’in) handle açılabileceğini belirler.
ExclusiveObjectOB_FLAG_EXCLUSIVE_OBJECT (0x08)Objenin yalnızca oluşturan process tarafından erişilebileceğini ve kullanılabileceğini belirler.
PermanentObjectOB_FLAG_PERMANENT_OBJECT (0x010)Bir objenin handle sayısı 0’a düştüğünde bile objenin yok edilmeyeceğini belirler.
DefaultSecurityQuotaOB_FLAG_DEFAULT_SECURITY_QUOTA (0x20)Security Descriptor’ın default olarak 2 KB kotasını kullanacağını belirler.
SingleHandleEntryOB_FLAG_SINGLE_HANDLE_ENTRY (0x40)Opsiyonel OBJECT_HEADER_HANDLE_INFO yapısının sadece bir giriş içerdiğini ve handle listesi değil, yalnızca tek bir handle olduğunu belirtir.
DeletedInlineOB_FLAG_DELETED_INLINE (0x80)Objenin IRQL 0’daki işçi thread tarafından silinmek üzere kuyrukta olduğunu belirtir. Bu, dead-lock ve sistem çökmelerini önlemek için kullanılır.

Hangi flag’lerin açık olduğunu bulabilmek için _OBJECT_HEADER->Flags & Flag Biti yapılır ve sonuç 0’ın harici bir değerse, seçilen Flag Biti objede set edilmiş demektir.

1
2
3
4
5
6
7
8
9
+0x01b Flags            : 0xcf ''
+0x01b NewObject        : 0y1
+0x01b KernelObject     : 0y1
+0x01b KernelOnlyAccess : 0y1
+0x01b ExclusiveObject  : 0y1
+0x01b PermanentObject  : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y1
+0x01b DeletedInline    : 0y1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lkd> ? 0xcf & 0x01
Evaluate expression: 1 = 00000000`00000001
lkd> ? 0xcf & 0x02
Evaluate expression: 2 = 00000000`00000002
lkd> ? 0xcf & 0x04
Evaluate expression: 4 = 00000000`00000004
lkd> ? 0xcf & 0x08
Evaluate expression: 8 = 00000000`00000008
lkd> ? 0xcf & 0x10
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0xcf & 0x20
Evaluate expression: 0 = 00000000`00000000
lkd> ? 0xcf & 0x40
Evaluate expression: 64 = 00000000`00000040
lkd> ? 0xcf & 0x80
Evaluate expression: 128 = 00000000`00000080

Tabiiki de bununla uğraşmayacağız, yukarı kısımlarda bahsedildiği gibi object header’daki bölümlere bakarak anlayabiliriz :)

Objelerin Tipi

Windows objeler için object header’ın “old version”” ve “new versiyon” olmak üzere iki farklı sürümü vardır (dikkat ettiyseniz object header bölümünde de new version yazsını görmüşsünüzdür) Sistemin x86 veya x64 komut kümesinde olması fark etmeksizin:

  • old version, windows sistemlerin 7’den önceki sürümlerini kapsıyor.

  • new version, windows sistemlerin 7 ve sonraki sürümlerini kapsıyor.

Bu versiyonlar arası değişiklikler aşağıdaki gibidir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Windows Vista | 2008 - SP2 x64
//0x38 bytes (sizeof)
struct _OBJECT_HEADER
{
    LONGLONG PointerCount;                                                  //0x0
    union
    {
        LONGLONG HandleCount;                                               //0x8
        VOID* NextToFree;                                                   //0x8
    };
    struct _OBJECT_TYPE* Type;                                              //0x10
    UCHAR NameInfoOffset;                                                   //0x18
    UCHAR HandleInfoOffset;                                                 //0x19
    UCHAR QuotaInfoOffset;                                                  //0x1a
    UCHAR Flags;                                                            //0x1b
    union
    {
        struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo;                //0x20
        VOID* QuotaBlockCharged;                                            //0x20
    };
    VOID* SecurityDescriptor;                                               //0x28
    struct _QUAD Body;                                                      //0x30
}; 
1
2
3
4
5
6
7
8
9
10
11
12
13
kd> dt nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B
   +0x004 HandleCount      : Int4B
   +0x004 NextToFree       : Ptr32 Void
   +0x008 Type             : Ptr32 _OBJECT_TYPE
   +0x00c NameInfoOffset   : UChar
   +0x00d HandleInfoOffset : UChar
   +0x00e QuotaInfoOffset  : UChar
   +0x00f Flags            : UChar
   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body             : _QUAD

Type, NameInfoOffset, HandleInfoOffset ve QuotaInfoOffset bölümleri new version ile kaldırılmıştır. Yerine Lock, TypeIndex, TraceFlags, InfoMask bölümleri getirilmiştir.

Farkı incelemek için yazının başında anlatılan OBJECT_HEADER ile kıyaslama yapabilirsiniz.

Windows’ta index numaralarının tutulduğu bir pointer array vardır ve bu array’e ObTypeIndexTable denir. Bu array’deki her pointer (1. ve 2. pointer’lar istisnadır), o obje tipine özgü özellikler içeren bir OBJECT_TYPE veri yapısına işaret eder. Eğer eski versiyon kullanılıyorsa direkt olarak _OBJECT_HEADER->Type bölümünden obje tipi bulunabilirdi. Fakat Windows yeni versiyonunda obje tiplerini belirten bölümde XOR’lanmış bir index numarası saklar, yani TypeIndex bölümünde.

Windows her bir nesne türü için özel işlevler ve yöntemler içeren OBJECT_TYPE yapıları kullanır. Bu yapılar, ObTypeIndexTable’daki her TypeIndex numarasına karşılık gelir.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
lkd> !object ffff948e`d18e0340
Object: ffff948ed18e0340  Type: (ffff948eb86d17a0) Process
    ObjectHeader: ffff948ed18e0310 (new version)
    HandleCount: 6  PointerCount: 196557
lkd> dps nt!ObTypeIndexTable
fffff800`0aafce80  00000000`00000000
fffff800`0aafce88  ffffbc00`b6760000
fffff800`0aafce90  ffff948e`b86d1380
fffff800`0aafce98  ffff948e`b86d1900
fffff800`0aafcea0  ffff948e`b86d1e80
fffff800`0aafcea8  ffff948e`b86d1a60
fffff800`0aafceb0  ffff948e`b86d1640
fffff800`0aafceb8  ffff948e`b86d17a0
fffff800`0aafcec0  ffff948e`b86c5140
fffff800`0aafcec8  ffff948e`b86c5980
fffff800`0aafced0  ffff948e`b86c52a0
fffff800`0aafced8  ffff948e`b86c5da0
fffff800`0aafcee0  ffff948e`b86c4900
fffff800`0aafcee8  ffff948e`b86c56c0
fffff800`0aafcef0  ffff948e`b86c5400
fffff800`0aafcef8  ffff948e`b86c5ae0

Yukarıdaki çıktılara bakarsanız Type: (ffff948eb86d17a0) ObTypeIndexTable dizisinde bulunuyor. Obje tipinin çözülmesini şematize etmek gerekirse şöyle bir akış görebiliriz:

Index = TypeIndex ^ OBJECT_HEADER adresinin LSB 2. baytı ^ nt!ObHeaderCookie

Örnek olması açısından bir objenin tipini yeni versiyona göre bulalım.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lkd> !process 0 0 notepad.exe
PROCESS ffff948ed18e0340
    SessionId: 8  Cid: 5898    Peb: 1002a8000  ParentCid: 5130
    DirBase: 15cdbd002  ObjectTable: ffffe284a08f7d80  HandleCount: 236.
    Image: notepad.exe

lkd> !object ffff948ed18e0340
Object: ffff948ed18e0340  Type: (ffff948eb86d17a0) Process
    ObjectHeader: ffff948ed18e0310 (new version)
    HandleCount: 6  PointerCount: 195193
lkd> dt nt!_OBJECT_HEADER ffff948ed18e0310 -y TypeIndex
   +0x018 TypeIndex : 0x80 ''
lkd> db nt!ObHeaderCookie l1
fffff800`0aafc72c  84                                               .
lkd> ? 0x80 ^ 0x03 ^ 0x84
Evaluate expression: 7 = 00000000`00000007

Çıkan sonuçta gerçek index numarası olan 7 çıktısını almış alıyoruz. Bu objenin bir process objesi olduğunu yukarıdaki çıktıdan anlayabiliyoruz fakat hesaplarımızı sağlama almak almak için aşağıdaki gibi bir uygulama yapılabilir:

1
2
3
4
5
6
7
8
9
10
11
12
13
lkd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable + ( 7 * @$ptrsize ))
   +0x000 TypeList         : _LIST_ENTRY [ 0xffff948e`b86d17a0 - 0xffff948e`b86d17a0 ]
   +0x010 Name             : _UNICODE_STRING "Process"
   +0x020 DefaultObject    : (null)
   +0x028 Index            : 0x7 ''
   +0x02c TotalNumberOfObjects : 0x1ca
   +0x030 TotalNumberOfHandles : 0xed6
   +0x034 HighWaterNumberOfObjects : 0x1e0
   +0x038 HighWaterNumberOfHandles : 0x121e
   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x0b8 TypeLock         : _EX_PUSH_LOCK
   +0x0c0 Key              : 0x636f7250
   +0x0c8 CallbackList     : _LIST_ENTRY [ 0xffffe284`97a91a00 - 0xffffe284`97a91a00 ]

Tüm bunları yapan fonksiyonu disassemble ederek incelemek istersek:

1
2
3
4
5
6
7
8
9
10
11
12
lkd> uf nt!ObGetObjectType
nt!ObGetObjectType:
fffff800`0a43d370 488d41d0        lea     rax,[rcx-30h]
fffff800`0a43d374 0fb649e8        movzx   ecx,byte ptr [rcx-18h]
fffff800`0a43d378 48c1e808        shr     rax,8
fffff800`0a43d37c 0fb6c0          movzx   eax,al
fffff800`0a43d37f 4833c1          xor     rax,rcx
fffff800`0a43d382 0fb60da3f36b00  movzx   ecx,byte ptr [nt!ObHeaderCookie (fffff800`0aafc72c)]
fffff800`0a43d389 4833c1          xor     rax,rcx
fffff800`0a43d38c 488d0dedfa6b00  lea     rcx,[nt!ObTypeIndexTable (fffff800`0aafce80)]
fffff800`0a43d393 488b04c1        mov     rax,qword ptr [rcx+rax*8]
fffff800`0a43d397 c3              ret

Bu konu hakkında detaylı bir şekilde breakpoint kullanılarak debug yapılmayacaktır fakat verilen komutlara bakılırsa;

  1. İlk XOR komutuna baktığınızda RAX ile RCX, yani TypeIndex ile OBJECT_HEADER adresinin LSB 2. baytı XOR işlemine tutuluyor.

  2. Daha sonra ObHeaderCookie değeri ECX register’ına atanarak önceki XOR çıktısı ile tekrar XOR işlemine tutuluyor.

  3. Son adımda da ObTypeIndexTable’ın hafıza adresi RCX register’ına atanarak XOR ile elde edilen çıktı adresi @$ptrsize ile çarpılır ve çıkan sonuç alınan bellek adresine eklenir yani rax,qword ptr [rcx+rax*8]. Böylece objeye uygun OBJECT_TYPE yapısı elde edilmiş olunur.

Object Tracing

Object Lock

Object Oluşma Bilgisi (CreateInfo)

ObjectCreateInfo bölümü, objenin yaratılma anında sağlanan bilgileri tutar. Bu bilgiler arasında, objenin hangi process tarafından yaratıldığı, hangi erişim haklarının verildiği, hangi dosya yolunu işaret ettiği gibi bilgiler yer alabilir.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
lkd> dt nt!_OBJECT_HEADER ffff948ecce15050 -y Obj*
   +0x020 ObjectCreateInfo : 0xffff948e`c4cb1d40 _OBJECT_CREATE_INFORMATION
lkd> dx ((nt!_OBJECT_CREATE_INFORMATION*)0xffff948e`c4cb1d40)
((nt!_OBJECT_CREATE_INFORMATION*)0xffff948e`c4cb1d40)                 : 0xffff948ec4cb1d40 [Type: _OBJECT_CREATE_INFORMATION *]
    [+0x000] Attributes       : 0x2141be0 [Type: unsigned long]
    [+0x008] RootDirectory    : 0x2991864 [Type: void *]
    [+0x010] ProbeMode        : 0 [Type: char]
    [+0x014] PagedPoolCharge  : 0x0 [Type: unsigned long]
    [+0x018] NonPagedPoolCharge : 0x0 [Type: unsigned long]
    [+0x01c] SecurityDescriptorCharge : 0x0 [Type: unsigned long]
    [+0x020] SecurityDescriptor : 0x0 [Type: void *]
    [+0x028] SecurityQos      : 0x0 [Type: _SECURITY_QUALITY_OF_SERVICE *]
    [+0x030] SecurityQualityOfService [Type: _SECURITY_QUALITY_OF_SERVICE]
lkd> dx ((nt!_OBJECT_CREATE_INFORMATION*)0xffff948e`c4cb1d40)->SecurityQualityOfService
((nt!_OBJECT_CREATE_INFORMATION*)0xffff948e`c4cb1d40)->SecurityQualityOfService                 [Type: _SECURITY_QUALITY_OF_SERVICE]
    [+0x000] Length           : 0x0 [Type: unsigned long]
    [+0x004] ImpersonationLevel : SecurityAnonymous (0) [Type: _SECURITY_IMPERSONATION_LEVEL]
    [+0x008] ContextTrackingMode : 0x0 [Type: unsigned char]
    [+0x009] EffectiveOnly    : 0x0 [Type: unsigned char]

Kaynakça ve Referanslar

  1. Windows Internals Seventh Edition Part 2 (2022), Microsoft
  2. A Light on Windows 10’s “OBJECT_HEADER->TypeIndex”
  3. CodeMachine - Article - Windows Object Headers
  4. 【旧文章搬运】Win7可变对象头结构之InfoMask解析 - 黑月教主 - 博客园
  5. WINNT内核文档
  6. Vergilius Project | Kernels
This post is licensed under CC BY 4.0 by the author.