И така, в тази част мисля да обърна внимание на структурите, които се на практика описват един обект. За целта на тази статия ще взема за пример, произволен _EPROCESS обект. За момента е необходимо да знаете, че всеки процес в Windows има (освен всички останали) _EPROCESS структура, която съдържа един вид "accounting" информация. За да видим всички работещи процеси е необходимо да се изпълни командата !process 0 0
Ето какво изкарва Windbg (ще пейстна само 1 entry, което ще изпозлвам за дискусията по-долу):
PROCESS 85b4c030 SessionId: 1 Cid: 0520 Peb: 7ffd6000 ParentCid: 0560
DirBase: 3ee8f1a0 ObjectTable: 8ce0fa48 HandleCount: 759.
Image: explorer.exe
Както се вижда _EPROCESS структурата се намира на адрес 0x85b4c030, съответно ако използваме командата dt nt!_EPROCESS 85b4c030 Windbg ще интерпрететира памета от този адрес до 85b4c030 + sizeof(_EPROCESS) като _EPROCESS и ще ни даде стойностите на различните полета (под-структури) за дадения процес:
kd> dt nt!_EPROCESS 85b4c030 +0x000 Pcb : _KPROCESS +0x098 ProcessLock : _EX_PUSH_LOCK +0x0a0 CreateTime : _LARGE_INTEGER 0x1ccb28c`3e22d03a +0x0a8 ExitTime : _LARGE_INTEGER 0x0 +0x0b0 RundownProtect : _EX_RUNDOWN_REF +0x0b4 UniqueProcessId : 0x00000520 Void +0x0b8 ActiveProcessLinks : _LIST_ENTRY [ 0x84fd33f8 - 0x85b0a0e8 ] +0x0c0 ProcessQuotaUsage : [2] 0x6040 +0x0c8 ProcessQuotaPeak : [2] 0x6d3c +0x0d0 CommitCharge : 0x1706 +0x0d4 QuotaBlock : 0x85d57180 _EPROCESS_QUOTA_BLOCK +0x0d8 CpuQuotaBlock : (null) +0x0dc PeakVirtualSize : 0xe9a0000 +0x0e0 VirtualSize : 0xcdec000 +0x0e4 SessionProcessLinks : _LIST_ENTRY [ 0x84fd3424 - 0x85b0a114 ] +0x0ec DebugPort : (null) +0x0f0 ExceptionPortData : 0x841d5c90 Void +0x0f0 ExceptionPortValue : 0x841d5c90 +0x0f0 ExceptionPortState : 0y000 +0x0f4 ObjectTable : 0x8ce0fa48 _HANDLE_TABLE +0x0f8 Token : _EX_FAST_REF +0x0fc WorkingSetPage : 0x104e8 +0x100 AddressCreationLock : _EX_PUSH_LOCK +0x104 RotateInProgress : (null) +0x108 ForkInProgress : (null) +0x10c HardwareTrigger : 0 +0x110 PhysicalVadRoot : (null) +0x114 CloneRoot : (null) +0x118 NumberOfPrivatePages : 0xf61 +0x11c NumberOfLockedPages : 0 +0x120 Win32Process : 0xfe9bfe30 Void +0x124 Job : (null) +0x128 SectionObject : 0x940657d8 Void +0x12c SectionBaseAddress : 0x00170000 Void +0x130 Cookie : 0xe99c86ae +0x134 Spare8 : 0 +0x138 WorkingSetWatch : (null) +0x13c Win32WindowStation : 0x00000030 Void +0x140 InheritedFromUniqueProcessId : 0x00000560 Void +0x144 LdtInformation : (null) +0x148 VdmObjects : (null) +0x14c ConsoleHostProcess : 0 +0x150 DeviceMap : 0x8fc39720 Void +0x154 EtwDataSource : (null) +0x158 FreeTebHint : 0x7ff9f000 Void +0x160 PageDirectoryPte : _HARDWARE_PTE +0x160 Filler : 0 +0x168 Session : 0x8a8b5000 Void +0x16c ImageFileName : [15] "explorer.exe" +0x17b PriorityClass : 0x2 '' +0x17c JobLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x184 LockedPagesList : (null) +0x188 ThreadListHead : _LIST_ENTRY [ 0x85b31fb0 - 0x850f3ef8 ] +0x190 SecurityPort : (null) +0x194 PaeTop : 0x8508f1a0 Void +0x198 ActiveThreads : 0x21 +0x19c ImagePathHash : 0xa80e4f97 +0x1a0 DefaultHardErrorProcessing : 0 +0x1a4 LastThreadExitStatus : 0n0 +0x1a8 Peb : 0x7ffd6000 _PEB +0x1ac PrefetchTrace : _EX_FAST_REF +0x1b0 ReadOperationCount : _LARGE_INTEGER 0x364 +0x1b8 WriteOperationCount : _LARGE_INTEGER 0x4 +0x1c0 OtherOperationCount : _LARGE_INTEGER 0x79c6 +0x1c8 ReadTransferCount : _LARGE_INTEGER 0x2f8dae +0x1d0 WriteTransferCount : _LARGE_INTEGER 0x368 +0x1d8 OtherTransferCount : _LARGE_INTEGER 0x3be11 +0x1e0 CommitChargeLimit : 0 +0x1e4 CommitChargePeak : 0x2029 +0x1e8 AweInfo : (null) +0x1ec SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO +0x1f0 Vm : _MMSUPPORT +0x25c MmProcessLinks : _LIST_ENTRY [ 0x84fd359c - 0x85b0a28c ] +0x264 HighestUserAddress : 0x7fff0000 Void +0x268 ModifiedPageCount : 0x41 +0x26c Flags2 : 0xd000 +0x26c JobNotReallyActive : 0y0 +0x26c AccountingFolded : 0y0 +0x26c NewProcessReported : 0y0 +0x26c ExitProcessReported : 0y0 +0x26c ReportCommitChanges : 0y0 +0x26c LastReportMemory : 0y0 +0x26c ReportPhysicalPageChanges : 0y0 +0x26c HandleTableRundown : 0y0 +0x26c NeedsHandleRundown : 0y0 +0x26c RefTraceEnabled : 0y0 +0x26c NumaAware : 0y0 +0x26c ProtectedProcess : 0y0 +0x26c DefaultPagePriority : 0y101 +0x26c PrimaryTokenFrozen : 0y1 +0x26c ProcessVerifierTarget : 0y0 +0x26c StackRandomizationDisabled : 0y0 +0x26c AffinityPermanent : 0y0 +0x26c AffinityUpdateEnable : 0y0 +0x26c PropagateNode : 0y0 +0x26c ExplicitAffinity : 0y0 +0x270 Flags : 0x144d0801 +0x270 CreateReported : 0y1 +0x270 NoDebugInherit : 0y0 +0x270 ProcessExiting : 0y0 +0x270 ProcessDelete : 0y0 +0x270 Wow64SplitPages : 0y0 +0x270 VmDeleted : 0y0 +0x270 OutswapEnabled : 0y0 +0x270 Outswapped : 0y0 +0x270 ForkFailed : 0y0 +0x270 Wow64VaSpace4Gb : 0y0 +0x270 AddressSpaceInitialized : 0y10 +0x270 SetTimerResolution : 0y0 +0x270 BreakOnTermination : 0y0 +0x270 DeprioritizeViews : 0y0 +0x270 WriteWatch : 0y0 +0x270 ProcessInSession : 0y1 +0x270 OverrideAddressSpace : 0y0 +0x270 HasAddressSpace : 0y1 +0x270 LaunchPrefetched : 0y1 +0x270 InjectInpageErrors : 0y0 +0x270 VmTopDown : 0y0 +0x270 ImageNotifyDone : 0y1 +0x270 PdeUpdateNeeded : 0y0 +0x270 VdmAllowed : 0y0 +0x270 CrossSessionCreate : 0y0 +0x270 ProcessInserted : 0y1 +0x270 DefaultIoPriority : 0y010 +0x270 ProcessSelfDelete : 0y0 +0x270 SetTimerResolutionLink : 0y0 +0x274 ExitStatus : 0n259 +0x278 VadRoot : _MM_AVL_TABLE +0x298 AlpcContext : _ALPC_PROCESS_CONTEXT +0x2a8 TimerResolutionLink : _LIST_ENTRY [ 0x0 - 0x0 ] +0x2b0 RequestedTimerResolution : 0 +0x2b4 ActiveThreadsHighWatermark : 0x25 +0x2b8 SmallestTimerResolution : 0 +0x2bc TimerResolutionStackRecord : (null)
Самата структура е доста обемна и сама по себе си заслужава отделен постинг, за да се опишат по-интересните полета, особено от гледна точка на rootkit development, но за момента искам да илюстрирам нещо друго - ако изпълним командата !object 85b4c030 ще видим следното:
kd> !object 85b4c030 Object: 85b4c030 Type: (841338f0) Process ObjectHeader: 85b4c018 (new version) HandleCount: 5 PointerCount: 368
Малко проста математика: 0x85b4c030 - 0x85b4c018 = 0x18 - тоест големината на _OBJECT_HEADER структурата е 0x18 байта (или 24 байта в десетична бройна система) и на практика тази структура е винаги _ПРЕДИ_ _EPROCESS структурата (или всяка друга, която дефинира executive обектите. Hint: ако искате да видите всички обекти, използвайте командата: !object \objecttypes ). Ето я и _OBJECT_HEADER структурата на Explorer.exe:
kd> dt _OBJECT_HEADER (0x85b4c030 - 0x18) nt!_OBJECT_HEADER +0x000 PointerCount : 0n398 +0x004 HandleCount : 0n5 +0x004 NextToFree : 0x00000005 Void +0x008 Lock : _EX_PUSH_LOCK +0x00c TypeIndex : 0x7 '' +0x00d TraceFlags : 0 '' +0x00e InfoMask : 0x8 '' +0x00f Flags : 0 '' +0x010 ObjectCreateInfo : 0x85d57180 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x85d57180 Void +0x014 SecurityDescriptor : 0x9024301a Void +0x018 Body : _QUAD
Полетата на тази структура са горе-долу самодокументиращи се - брояч колко pointer-а има отворени към този обект (Explorer.exe в случая), колко handles има отворени, NextToFree не го знам какво е :Д, Lock - използващ се, когато object manager-а оперира върху този обект, за да се избегнат race conditions, Flags - флагове, които описват как точно трябва да се държи този обект - например тук са флагове като OBJ_INHERIT, OBJ_EXCLUSIVE, OBJ_KERNEL_HANDLE etc.
TypeIndex
И така. TypeIndex полето е индекс в таблица, която указва кой обект от какъв тип е. Тази таблица е глобална и следният и може да бъде достъпена като nt!ObTypeIndexTable. За да се разбере какъв тип обект отговаря на даден header просто трябва да се използва следната формула: nt!ObTypeIndexTable + ( TypeIndex * sizeof(void *) ), в WinDBG това би изглеждало по следният начин: dt _OBJECT_TYPE poi(nt!ObTypeIndexTable + (7 * @$ptrsize)) или dt _OBJECT_TYPE poi(nt!ObTypeIndexTable + (7 * 4)) (големината на pointer на 32 битова машина е винаги 4 байта (32 бита), респективно за 64 битова архитектура това би било 8 байта/64 бита)).
kd> dt _OBJECT_TYPE poi(nt!ObTypeIndexTable + (7 * @$ptrsize)) ntdll!_OBJECT_TYPE +0x000 TypeList : _LIST_ENTRY [ 0x841338f0 - 0x841338f0 ] +0x008 Name : _UNICODE_STRING "Process" +0x010 DefaultObject : (null) +0x014 Index : 0x7 '' +0x018 TotalNumberOfObjects : 0x25 +0x01c TotalNumberOfHandles : 0xd6 +0x020 HighWaterNumberOfObjects : 0x28 +0x024 HighWaterNumberOfHandles : 0xde +0x028 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x078 TypeLock : _EX_PUSH_LOCK +0x07c Key : 0x636f7250 +0x080 CallbackList : _LIST_ENTRY [ 0x84133970 - 0x84133970 ]
Както виждаме правилно успяхме да получим _OBJECT_TYPE за обект от тип Process. Полетата тук също бих казал, че са самодокументиращи се - първо виждаме, че всички _OBJECT_TYPE структури са навързани в двоен linke-list, всяка има име, (незадължителен) обект по подразбиране за синхронизация, index е индексът във ObTypeIndexTable. Броят обекти от даден тип - в случаят 0x25 или 37 процеса, броят handles, които са отворени към _EPROCESS обекти, HighWater не ги знам за какво са, TypeInfo ще го дискутирам в следващ постинг, но точно там нещата стават интересни ;-) TypeLock се използва за синхронизация, когато се оперира върху тази структура (която има глобално значение, за всички обекти от тип _EPROCESS), CallbackList е двоен linked-list с callbackovete, които трябва да бъдат извикани (Hint: ObRegisterCallbacks)
И така смятам да спра до тук, а в следващият постинг вече ще дам реален пример какво точно може да се направи с _OBJECT_TYPE_INITIALIZER структурата, за да може да се извиква нащ код винаги, когато се случва нещо с обект от даден тип.