和第二篇隔了好久。。。但是其实是懒得推上博客(吗
内核对象 (Kernel Object)
在 Windows 操作系统中,有很多涉及到底层的操作,例如文件操作,进程操作,注册表操作,网络操作,事件操作等等。为了能够将用户从繁琐的内核操作中分离出来,Windows 提供了很多的 API 函数来帮助用户能够更好的操作,例如对于文件操作,我们有CreateFile
,WriteFile
; 对于进程,我们可以使用CreateProcess
,也有CreateToolhelp32Snapshot
这样的操作函数。这些函数操作的对象就是我们本文要讨论的内核对象 。例如:
文件对象
事件对象
互斥量(Mutex)对象
管道(Pipe)对象
进程对象
线程对象
…
这些内核对象都是一个内存块,由操作系统内核进行分配,并且也只能由操作系统内核访问。内核对象中的属性随着内核种类的变化而变化。形如进程对象拥有 PID 的属性,而文件对象有一个字节偏移数作为参数。但是大部分的内核对象都有下列两个属性
这些属性只能够由操作系统内核进行修改和使用,从而避免应用程序对内核进行过多的修改。
句柄(Handle)
上文中提到,内核对象只能够由内核进行处理,那么应用程序应该如何访问这些对象呢?答案就是使用句柄(Handle)。当调用一个创建内核对象的函数后,系统会生成一个句柄返回到应用层。之后应用层将会使用这个句柄对对应的内核对象进行操作
// ###(插图:句柄指向内核)
为了保证操作系统的可靠性,句柄的值是与进程相关 的,但是同时,句柄也支持在不同的进程间共享。
内核对象的生命周期
由于内核对象可以被多个进程共享,所以即使说由进程A创建了一个内核对象,在进程A结束之后。在进程B中也使用了同一个内核对象,此对象也不应该被回收。为了实现这点,每个内核对象都由一个使用计数的参数,用来表示当前有多少个进程在使用当前内核对象 。当没有进程使用当前内核对象的时候,该使用计数会变为0。操作系统会检测内核对象的引用次数,当次数为0的时候就会 销毁当前对象(不完全是这样) ,这样的话就能够保证系统中不存在没有被任何进程引用的内核对象。
让引用计数减少的最基本的方法就是调用函数CloseHandle
,每当关闭一个句柄,就算是减少了一次对当前内核次数的引用
**: 上文提到,内核对象的引用计数变为0的时候会销毁对象,并且调用CloseHandle
的时候会减少引用次数这种说法不完全正确。比如说当使用CreateProcess
创建的进程对象在被CloseHandle
之后也会继续运行直到程序结束,而CreateFileMapping
创建的共享空间句柄就会因为CloseHandle
而导致内核对象被销毁。个人理解,销毁与否关键取决于当前内核对象所管理的对象是否结束(比如说进程的话要等进程运行结束才算是结束,而内存空间只要没有任何东西运行,所以关闭之后就相当于是当前引用结束了)
安全描述符(SD)
安全描述符之前接触比较少,之后应该会单独开篇来讲一下这个东西,这里就简单介绍一下。
内核对象的访问限制。一个安全描述符主要描述这些事情:
当前对象的拥有着(一般就是当前对象的创建者)
哪些组/用户能够访问或者使用该对象
哪些组/用户被拒绝访问当前对象
在内核中可以通过一个叫做 SECURITY_ATTRIBUTES 的结构进行设置。
句柄表
这个概念是由这本书提出来的,并不保证实际情况就是如此
进程初始化的时候,将会由操作系统为其分配一个句柄表。这个句柄表相当于是一个数据结构类似的东西,然后里面包含一个内核对象的指针,一个访问掩码(access masl)还有一些标志
1 2 3 4 5 6 7 +-------+-------------------------+-------------------------------+------------+ | index | 指向内核对象内存块的指针 | 访问掩码(包含标志未的一个DWORD) | 标志 | +-------+-------------------------+-------------------------------+------------+ | 0x10 | 0xF0001000 | 0x???????? | 0x00000001 | +-------+-------------------------+-------------------------------+------------+ | 0x20 | 0x00000000 | (不可用) | (不可用) | +-------+-------------------------+-------------------------------+------------+
如上,为一个记录了有效句柄的句柄表。其中索引1为一个有效的内核对象的句柄。
当使用创建内核对象的函数时,就会对当前的表格进行填充。这类函数形如:
CreateThread
CreateFile
CreateFileMapping
CreateSemaphre(信号量)
通过这些函数得到的句柄可以被同一进程中的所有线程共同使用。在使用句柄的时候,Windows进程往往会将当前的句柄值右移两位作为真正的句柄值。所以第一个有效的句柄值往往是4。(众所周知,在 Windows 操作系统中,System 进程的 PID 值为4,这里可能有所关联)
作用域
正如前文提到的,内核对象是存放在内核空间中的,部分的API会听过为内核对象命名的操作。通过使用名字访问的话,就相当于是跨进程,此时被称为放在全局命名空间 。若不使用命名的话,则是在同一进程中共享。
然而,在 Windows Vista 之后,对于命名有了要求。全局命名空间名必须要为
的形式。
同样我们也能够显示的指明一个对象放入当前会话空间
这是因为, Windows Vista 之后,在 Windows 登陆的时候会创建一个叫做 Session 0 的会话。在这个会话中的服务都是在登陆期间执行的。通过这样做能够有效的将应用程序和系统服务进行隔离。同样,每一个登陆的用户都将获得一个会话(一般是从 Session 1开始)不同的用户可以登陆不同的会话。
内核对象观测
推荐一个 Sysinternals 提供的工具(似乎是被微软收购了),叫做Process Explorer
严肃警告:这个公司的软件都会装驱动,还想玩游戏的个位千万千万不要装到跑游戏的机器里面,相关故事请参考我的惨痛经历 ,顺便里面提到的Procmon其实在检测文件行为的时候还是蛮好用的
2020.5.2 更新
关于内存对象的观测
上面的讲述都过于干涩,毕竟没有实际的例子。那么我们如何去观察一个内核对象呢?首先我们知道在内核调试模式下,使用
能够列出当前进程中所有使用的句柄对象。举个例子,我们可以看到:
1 2 3 4 5 6 7 8 9 10 11 12 1: kd> !handle 94 PROCESS aed07600 SessionId: 1 Cid: 1b90 Peb: 00451000 ParentCid: 0d24 DirBase: 3ffd35c0 ObjectTable: af3f4540 HandleCount: 38. Image: Exploit.exe Handle table at af3f4540 with 38 entries in use 0094: Object: aed07600 GrantedAccess: 00001400 Entry: 8b83a128 Object: aed07600 Type: (8639b480) Process ObjectHeader: aed075e8 (new version) HandleCount: 7 PointerCount: 217
这个句柄94表示的是一个叫做Exploit.exe进程的进程对象 。Windows 将所有的一切都封装成了对象,进程也一样,所以我们可以看到这里:
1 2 3 4 5 6 7 0094: Object: aed07600 GrantedAccess: 00001400 Entry: 8b83a128 Object: aed07600 Type: (8639b480) Process ObjectHeader: aed075e8 (new version) ^ | 这里正是这个对象(object)在内存中的位置 HandleCount: 7 PointerCount: 217
如果我们需要观察这个对象的话,只需要键入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1: kd> dt _Object_header aed075e8 nt!_OBJECT_HEADER +0x000 PointerCount : 0n217 +0x004 HandleCount : 0n7 +0x004 NextToFree : 0x00000007 Void +0x008 Lock : _EX_PUSH_LOCK +0x00c TypeIndex : 0xe1 '' +0x00d TraceFlags : 0 '' +0x00d DbgRefTrace : 0y0 +0x00d DbgTracePermanent : 0y0 +0x00e InfoMask : 0x88 '' +0x00f Flags : 0 '' +0x00f NewObject : 0y0 +0x00f KernelObject : 0y0 +0x00f KernelOnlyAccess : 0y0 +0x00f ExclusiveObject : 0y0 +0x00f PermanentObject : 0y0 +0x00f DefaultSecurityQuota : 0y0 +0x00f SingleHandleEntry : 0y0 +0x00f DeletedInline : 0y0 +0x010 ObjectCreateInfo : 0x8d2952c0 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x8d2952c0 Void +0x014 SecurityDescriptor : 0xa7a77596 Void +0x018 Body : _QUAD
就能看到这个内存对象的基本信息啦。后续如果想要知道如何通过_OBJECT_HEADER
查看当前对象的类型的话,可以使用https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a 提到的方法
关于内核对象的一些路径问题
当使用!object \
的时候,可以观测到位于\
目录的对象
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 1: kd> !object \ Object: ffff938d802163a0 Type: (ffffaa858f22c990) Directory ObjectHeader: ffff938d80216370 (new version) HandleCount: 0 PointerCount: 50 Directory Object: 00000000 Name: \ Hash Address Type Name ---- ------- ---- ---- 01 ffffaa859002bba0 Mutant PendingRenameMutex ffffaa858fc14680 FilterConnectionPort DfsrRoFltCommunicationPort ffff938d8021ab50 Directory ObjectTypes 02 ffffaa85913a8fb0 FilterConnectionPort WcnfsPort ffffaa85911e8220 FilterConnectionPort storqosfltport 05 ffff938d8022c1c0 SymbolicLink SystemRoot 06 ffff938d817a8080 Directory Sessions 08 ffff938d8022c700 Directory ArcName 09 ffffaa8590251d30 FilterConnectionPort WcifsPort ffff938d802bfc90 Directory NLS 10 ffffaa859102db20 Event LanmanServerAnnounceEvent ffffaa85913b8ad0 ALPC Port ThemeApiPort ffff938d8179ea60 Directory Windows ffff938d8021a7c0 Directory GLOBAL?? 11 ffff938d815ff640 Directory RPC Control ffffaa858fa74b10 ALPC Port PdcPort 13 ffffaa85907d1130 Event EFSInitEvent 14 ffff938d815eb530 SymbolicLink Dfs ffffaa858fc26620 Device clfs 15 ffffaa8590470550 Event CsrSbSyncEvent ffffaa85900498b0 ALPC Port SeRmCommandPort ffffaa85902f4500 Device DfsServer 16 ffff938d8021a1d0 SymbolicLink DosDevices 17 ffffaa85911cb230 Event DSYSDBG.Debug.Trace.Memory.2b8 ffff938d81783330 Directory KnownDlls32 18 ffff938d8023c220 Key \REGISTRY 19 ffff938d824cbea0 Directory BaseNamedObjects 20 ffff938d81a11850 Section Win32kCrossSessionGlobals
通过
我们能展开看这个对象的一些细节信息。例如:
1 2 3 4 5 6 7 1: kd> !object ffff938d8021a1d0 <--- 这个是 Object: ffff938d8021a1d0 Type: (ffffaa858f2b1f20) SymbolicLink ObjectHeader: ffff938d8021a1a0 (new version) HandleCount: 0 PointerCount: 1 Directory Object: ffff938d802163a0 Name: DosDevices Flags: 00000000 ( Local ) Target String is '\??'
这里提到的Target String
可以在用户态使用。
其次,我们能发现,形如ObjectTypes
这种用于表示一个对象的类型的对象也会存放在这里。然后也可以用同样的方法检查类型
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 1: kd> !object \ObjectTypes Object: ffff938d8021ab50 Type: (ffffaa858f22c990) Directory ObjectHeader: ffff938d8021ab20 (new version) HandleCount: 0 PointerCount: 60 Directory Object: ffff938d802163a0 Name: ObjectTypes Hash Address Type Name ---- ------- ---- ---- 00 ffffaa858f38d560 Type TmTm 01 ffffaa858f234c60 Type Desktop ffffaa858f226510 Type Process 02 ffffaa858f37af20 Type RegistryTransaction 03 ffffaa858f230f20 Type DebugObject 04 ffffaa8590078e60 Type VRegConfigurationContext ffffaa858f5d5250 Type DmaDomain ffffaa858f392f20 Type TpWorkerFactory 05 ffffaa858f389f20 Type Adapter ffffaa858f234350 Type Token 06 ffffaa8590106890 Type DxgkSharedResource 07 ffffaa858f2bfdc0 Type PsSiloContextPaged 08 ffffaa858fc656c0 Type NdisCmState 09 ffffaa858f9a38c0 Type PcwObject ffffaa858f3ef320 Type WmiGuid 11 ffffaa858f5d0220 Type DmaAdapter
然后,我们能看到一个叫做BaseNamedObjects
的目录,这个目录中存放的都是global namespace
(全局对象),与之相对的是session namespace
(会话对象)
然后在Sessions
包含了每一个会话空间。一般创建的对象都在单独的会话空间,除非声明要创建到全局对象空间中。此时要使用Global\
前缀
而Glibal\??
表示一个符号链接,用于为每一个应用使用。例如:
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 1: kd> !object \global?? Object: ffff938d8021a7c0 Type: (ffffaa858f22c990) Directory ObjectHeader: ffff938d8021a790 (new version) HandleCount: 2 PointerCount: 32786 Directory Object: ffff938d802163a0 Name: GLOBAL?? Hash Address Type Name ---- ------- ---- ---- 00 ffff938d844ba750 SymbolicLink SWD#PRINTENUM#{466D5266-8EE9-4A99-8464-95665F2C8B67}#{0ecef634-6ef0-472a-8085-5ad023ecbccd} ffff938d8241ff00 SymbolicLink DISPLAY5 ffff938d8119ab60 SymbolicLink HID#VID_0E0F&PID_0003&MI_00#8&33601b94&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd} ffff938d817c7110 SymbolicLink D: 22 ffff938d817a82d0 SymbolicLink MAILSLOT ffff938d812ebb70 SymbolicLink HarddiskVolume1 23 ffff938d817df9a0 SymbolicLink INTELPRO_{3A3CEE8A-970D-4E0F-971A-18138E118FB2} ffff938d813c8570 SymbolicLink HarddiskVolume2 ffff938d812e2560 SymbolicLink Ntfs ///.......... 31 ffff938d844837c0 SymbolicLink MQAC ffff938d817f39a0 SymbolicLink HDAUDIO#FUNC_01&VEN_15AD&DEV_1975&SUBSYS_15AD1975&REV_1001#5&322e5e46&0&0001#{86841137-ed8e-4d97-9975-f2ed56b4430e} ffff938d812848d0 SymbolicLink PartmgrControl 32 ffff938d817a9f20 SymbolicLink PIPE ffff938d812a6680 SymbolicLink PciControl ffff938d80216560 SymbolicLink GLOBALROOT 33 ffff938d824f08a0 SymbolicLink DISPLAY#Default_Monitor#4&31be19fa&0&UID0#{866519b5-3f07-4c97-b7df-24c5d8a8ccb8} ffff938d811a7840 SymbolicLink USB#VID_0E0F&PID_0003#6&38eee119&0&5#{a5dcbf10-6530-11d2-901f-00c04fb951ed} ffff938d817a2cb0 SymbolicLink AUX ffff938d813d0fd0 SymbolicLink C: ffff938d802727d0 SymbolicLink ACPI#FixedButton#2&daba3ff&1#{4afa3d53-74a7-11d0-be5e-00a0c9062857}