1、介绍
2、介绍WINDOWS对象
2.1 它们是什么
2.2 它们的结构
2.3 对象操作
3、介绍 /Device/PhysicalMemory
3.1 对象
3.2 需要写权限?
4、玩 /Device/PhysicalMemory
4.1 读/写内存
4.2 什么是Callgate
4.3 不用驱动运行ring0代码
4.4 深入到进程表
4.5 Bonus Track
5、代码示例
5.1 kmem.h
5.2 chmod_mem.c
5.3 winkdump.c
5.2 winkps.c
5.4 fun_with_ipd.c
6、结论
7、参考
--[ 1 介绍
本文介绍Windows /dev/kmem,我的研究是在Windows 2000 professional上实施的,这意味着本文中的
多数代码都可以在windows 2000版本上运行,稍经改动,也可以运行在XP上。很明显Windows 9x/Me不会支持,
因为它们的核心结构并不相同。
--[ 2 介绍Windows对象
Windows2000使用对象模型来提供非常简单的方式操作多数基本的核心元素。我们可以在本节看看这些对
象和怎么去操作它们。
----[ 2.1 What are they?
按照微软的说法,设计对象管理器只要来达到下面这些目的:
* 使用命名对象方便识别
* 支持POSIX子系统
* 提供方便的方法来操作系统资源
* 提供一种装填机制来限制进程使用的资源
* 顺应C2安全的要求。
有27种不同的对象类型:
* Adapter * File * Semaphore
* Callback * IoCompletion * SymbolicLink
* Controler * Job * Thread
* Desktop * Key * Timer
* Device * Mutant * Token
* Directory * Port * Type
* Driver * Process * WaitablePort
* Event * Profile * WindowStation
* EventPair * Section * WmiGuid
多数这些对象从命令就能够看出它们是关于什么的了。我会解释一些模糊的名字:
* 一个EventPair只是2个Event对象
* Mutant也被称为互斥体(Mutex),是一种处理资源访问的同步机制
* Port被LPC(Local Procedure Call)作Inter-Processus 通讯。
* Semaphore是限制访问资源的计数器
* Token (Access Token)是安全对象
* WindowStation是桌面对象容器。
这些对象可能类似目录树结构一样组织成:
- /
- ArcName (symbolic links to harddisk partitions)
- NLS (sections ...)
- Driver (installed drivers)
- WmiGuid
- Device (/dev linux like)
- DmControl
- RawDmVolumes
- HarddiskDmVolumes
- PhysicalDmVolumes
- Windows
- WindowStations
- RPC Control
- BaseNamedObjects
- Restricted
- ?? (current user directory)
- FileSystem (information about installable files system)
- ObjectTypes (contains all avaible object types)
- Security
- Callback
- KnownDlls (Contains sections of most used DLL)
"??"目录是当前用户目录,"Device"可以被看作跟LINUX上的/dev一样。你可以在Sysinternals网站上
找到这个结构。
----[ 2.2 他们的结构
每个对象都包含两部分:对象头和对象实体。Sven B. Schreiber在"Windows 2000 Undocumented Secrets"
一书中定义了多数没公开的头部结构。我们可以来看看这些头结构。
---
from w2k_def.h:
typedef struct _OBJECT_HEADER {
/*000*/ DWORD PointerCount; // number of references
/*004*/ DWORD HandleCount; // number of open handles
/*008*/ POBJECT_TYPE ObjectType; // pointer to object type struct
/*00C*/ BYTE NameOffset; // OBJECT_NAME offset
/*00D*/ BYTE HandleDBOffset; // OBJECT_HANDLE_DB offset
/*00E*/ BYTE QuotaChargesOffset; // OBJECT_QUOTA_CHARGES offset
/*00F*/ BYTE ObjectFlags; // OB_FLAG_*
/*010*/ union
{ // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock
/*010*/ PQUOTA_BLOCK QuotaBlock;
/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;
};
/*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor;
/*018*/ } OBJECT_HEADER, *POBJECT_HEADER;
---
头部中的每个偏移量都是负数偏移量,因此你如果想从头结构中找到OBJECT_NAME结构,你应该这样计算:
address = object_header_address - name_offset
OBJECT_NAME结构允许创建者通过赋一个名字让对象对其他进程可见。
OBJECT_HANDLE_DB结构允许核心跟踪当前谁正在使用该对象。
OBJECT_QUOTA_CHARGES结构用来定义了进程访问对象的配额。
OBJECT_TYPE结构存储关于对象类型的全局信息,比如:默认的安全权限,对象大小,进程使用对象的默
认配额等。
对象绑定的安全描述符可以让核心来限制对象访问。
每一个对象类型的内部程序都十分接近C++对象中的构造和析构:
* dump method -可能是为了调试目的,总为NULL
* open method -当对象句柄打开时被调用
* close method -当对象句柄关闭时被调用
* delete method -当对象句柄删除时被调用
* parse method -查询对象列表时被调用
* security method -读写保护的当前的对象时被调用
* query method -当线程查询对象名时调用
* "ok to close" -线程关闭句柄时调用
对象体的结构完全依靠对象类型,在DDK中只有很少部分的对象体结构被公开。如果你对这些结构感兴
趣,你可以使用google :) 或者查看chapeaux-noirs的主页(参见[4])
--- [ 2.3 对象操作
以用户模式的观点来看,对象操作只要是通过Windows API来执行。比如,为了访问文件对象,可以使
用 fopen()/open(),它们调用 CreateFile(). 我们转化到核心模式 (NtCreateFile())在ntoskrnl.exe
中调用IoCreateFile()。通过反编译IoCreateFile(),可以看见一些函数,比如:ObOpenObjectByName,
ObfDereferenceObject,……
(BTW:如果用在DDK站点(参见[2])下载的win2k symbols,只能看见这些函数,用支持Windows Symbols
文件的反编译器比如IDA/kd/Softice 因为这些函数没有被导出。)
每个函数都以Ob开头,表示同对象管理器相关。基本上,普通的开发者不必去处理这些对象,但我们要
去看看。
对于用户模式,所有的对象管理器相关函数都可以被ntdll.dll输出,这里有一些例子:
NtCreateDirectoryObject, NtCreateSymbolicLinkObject, NtDuplicateObject,
NtMakeTemporaryObject, NtOpenDirectoryObject, ...
有些函数在MSDN中公开了,但是多数没有。
如果你真想理解对象的工作方法,最好看看ntoskrnl.exe中导出的以Ob开头的函数。有21个导出函数,
其中6个是公开的。
如果你想看看其他15个的原型,去ntifs.h的主页(参见[3])或者去chapeaux-noirs站点(参见[4])。
--[ 3 - 介绍/Device/PhysicalMemory
为了查看对象信息,我们需要一个类似微软DDK中的核心调试工具。好,让我们现在开始……
Microsoft(R) Windows 2000 Kernel Debugger
Version 5.00.2184.1
Copyright (C) Microsoft Corp. 1981-1999
Symbol search path is: c:/winnt/symbols
Loading Dump File [livekd.dmp]
Full Kernel Dump File
Kernel Version 2195 UP Free
Kernel base = 0x80400000 PsLoadedModuleList = 0x8046a4c0
Loaded kdextx86 extension DLL
Loaded userkdx extension DLL
Loaded dbghelp extension DLL
f1919231 eb30 jmp f1919263
kd> !object /Device/PhysicalMemory
!object /Device/PhysicalMemory
Object: e1001240 Type: (fd038880) Section
ObjectHeader: e1001228
HandleCount: 0 PointerCount: 3
Directory Object: fd038970 Name: PhysicalMemory
从kd(kernel debugger)剖析基本对象告诉我们一些信息。不必解释所有内容的意义,他们中的大多数
都非常清楚只要你读了文章开头,如果没有,请"jmp dword Introduction_to_Windows_Objects"。 :)
感兴趣的是,它是一个Section对象,清楚地表明我们要处理内存。现在我们要dump对象的头结构。
kd> dd e1001228 L 6
dd e1001228 L 6
e1001228 00000003 00000000 fd038880 12200010
e1001238 00000001 e1008bf8
details:
--> 00000003 : PointerCount = 3
--> 00000000 : HandleCount = 0
--> fd038880 : pointer to object type = 0xfd038880
--> 12200010 --> 10 : NameOffset
--> 00 : HandleDBOffset
--> 20 : QuotaChargeOffset
--> 12 : ObjectFlags = OB_FLAG_PERMANENT & OB_FLAG_KERNEL_MODE
--> 00000001 : QuotaBlock
--> e1008bf8 : SecurityDescriptor
NameOffset存在,不要惊讶,这个对象有一个名字……,但是没有HandleDBOffset。这意味着这个对象
不跟踪分配的句柄。QuotaChargeOffset并不有意思,ObjectFlags告诉我们这个对象是永久对象,并且被核
心创建。
到现在还没有非常有意思的东西……
dump这个对象的名字结构,只是确信我们没有走错方向 :)。 (记住偏移量是负数)
kd> dd e1001228-10 L3
dd e1001228-10 L3
e1001218 fd038970 001c001c e1008ae8
--> fd038970 : pointer to object Directory
--> 001c001c --> 001c : UNICODE_STRING.Length
--> 001c : UNICODE_STRING.MaximumLength
--> e1008ae8 : UNICODE_STRING.Buffer (pointer to wide char string)
kd> du e1008ae8
du e1008ae8
e1008ae8 "PhysicalMemory"
现在出现有意思的部分了,安全描述符:
kd> !sd e1008bf8
!sd e1008bf8
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8004
SE_DACL_PRESENT
SE_SELF_RELATIVE
->Owner : S-1-5-32-544
->Group : S-1-5-18
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x44
->Dacl : ->AceCount : 0x2
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize: 0x14
->Dacl : ->Ace[0]: ->Mask : 0x000f001f
->Dacl : ->Ace[0]: ->SID: S-1-5-18
->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize: 0x18
->Dacl : ->Ace[1]: ->Mask : 0x0002000d
->Dacl : ->Ace[1]: ->SID: S-1-5-32-544
->Sacl : is NULL
总之,这意味着/Device/PhysicalMemory对象有下面的权限:
user SYSTEM: Delete, Change Permissions, Change Owner, Query Data,
Query State, Modify State
user Administrator: Query Data, Query State
基本上,管理员用户没有权限写,但是SYSTEM可以,这实际上意味着管理员也可以做到!
一定注意到实际上这不象/dev/kmem!!在LINUX中/dev/kmem映射虚拟内存,/Device/PhysicalMemory
映射物理内存,本文更确切的标题应该是"Playing with Windows /dev/mem"因为/dev/mem映射物理内存,
但/dev/kmem听起来更熟悉些。:)
据我所知,在我写这篇文章的时候,Section对象体结构还没有解剖开,因此我们还不能分析它的结构。
----[ 3.2 需要“写”权限?
好,我们是用户administrator,并且打算玩玩感兴趣的对象,该怎么做呢?正如多数Windows管理员
所知,可以用schedule服务运行任何进程作为SYSTEM用户。如果你想确信你可以,只要启动schedule用
“net start schedule”并且去打开一个任务执行regedit.exe
c:/>at <when> /interactive regedit.exe
之后可以试试看看SAM注册表,如果能查看,那么你SYSTEM用户,如果不能,你就仍旧是administrator
因为只有用户SYSTEM才拥有读的权力。
好,如果我们是administrator用户,但如果我们允许任何人写/Device/PhysicalMemory会发生什么
呢?(当然是为了学习的目的)
我们只要给这个对象添加另一个ACL,就可以了。按照下面的步骤:
1、打开/Device/PhysicalMemory句柄 (NtOpenSection)
2、找到它的安全描述符 (GetSecurityInfo)
3、在当前ACL中添加Read/Write授权 (SetentriesInAcl)
4、更新安全描述符 (SetSecurityInfo)
5、关闭先前打开的句柄
可以参考示例代码:chmod_mem.c
当运行了chmod_mem.exe,我们再一次dump/Device/PhysicalMemory的安全描述符。
kd> !object /Device/PhysicalMemory
!object /Device/PhysicalMemory
Object: e1001240 Type: (fd038880) Section
ObjectHeader: e1001228
HandleCount: 0 PointerCount: 3
Directory Object: fd038970 Name: PhysicalMemory
kd> dd e1001228+0x14 L1
dd e1001228+0x14 L1
e100123c e226e018
kd> !sd e226e018
!sd e226e018
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8004
SE_DACL_PRESENT
SE_SELF_RELATIVE
->Owner : S-1-5-32-544
->Group : S-1-5-18
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x68
->Dacl : ->AceCount : 0x3
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize: 0x24
->Dacl : ->Ace[0]: ->Mask : 0x00000002
->Dacl : ->Ace[0]: ->SID: S-1-5-21-1935655697-436374069-1060284298-500
->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize: 0x14
->Dacl : ->Ace[1]: ->Mask : 0x000f001f
->Dacl : ->Ace[1]: ->SID: S-1-5-18
->Dacl : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[2]: ->AceFlags: 0x0
->Dacl : ->Ace[2]: ->AceSize: 0x18
->Dacl : ->Ace[2]: ->Mask : 0x0002000d
->Dacl : ->Ace[2]: ->SID: S-1-5-32-544
->Sacl : is NULL
新的ACE(access-control entry)是Ace[0],拥有0x00000002权限(SECTION_MAP_WRITE)。需要更多
信息,可以查看MSDN中的Security win32 API [9]
--[ 4 - 玩转/Device/PhysicalMemory
为什么要来处理/Device/PhysicalMemory?我可以说用来读、写、修补内存。这已经足够了。 :)
----[ 4.1 读写内存
我们开始吧……
为了读写/Device/PhysicalMemory,必须:
1、打开对象句柄 (NtOpenSection)
2、转化虚拟内存地址为物理地址
3、映射section到物理空间 (NtMapViewOfSection)
4、在被映射的内存中读写数据
5、关闭section的映射 (NtUnmapViewOfSection)
6、关闭对象句柄 (NtClose)
现在我们的主要目的是
2、介绍WINDOWS对象
2.1 它们是什么
2.2 它们的结构
2.3 对象操作
3、介绍 /Device/PhysicalMemory
3.1 对象
3.2 需要写权限?
4、玩 /Device/PhysicalMemory
4.1 读/写内存
4.2 什么是Callgate
4.3 不用驱动运行ring0代码
4.4 深入到进程表
4.5 Bonus Track
5、代码示例
5.1 kmem.h
5.2 chmod_mem.c
5.3 winkdump.c
5.2 winkps.c
5.4 fun_with_ipd.c
6、结论
7、参考
--[ 1 介绍
本文介绍Windows /dev/kmem,我的研究是在Windows 2000 professional上实施的,这意味着本文中的
多数代码都可以在windows 2000版本上运行,稍经改动,也可以运行在XP上。很明显Windows 9x/Me不会支持,
因为它们的核心结构并不相同。
--[ 2 介绍Windows对象
Windows2000使用对象模型来提供非常简单的方式操作多数基本的核心元素。我们可以在本节看看这些对
象和怎么去操作它们。
----[ 2.1 What are they?
按照微软的说法,设计对象管理器只要来达到下面这些目的:
* 使用命名对象方便识别
* 支持POSIX子系统
* 提供方便的方法来操作系统资源
* 提供一种装填机制来限制进程使用的资源
* 顺应C2安全的要求。
有27种不同的对象类型:
* Adapter * File * Semaphore
* Callback * IoCompletion * SymbolicLink
* Controler * Job * Thread
* Desktop * Key * Timer
* Device * Mutant * Token
* Directory * Port * Type
* Driver * Process * WaitablePort
* Event * Profile * WindowStation
* EventPair * Section * WmiGuid
多数这些对象从命令就能够看出它们是关于什么的了。我会解释一些模糊的名字:
* 一个EventPair只是2个Event对象
* Mutant也被称为互斥体(Mutex),是一种处理资源访问的同步机制
* Port被LPC(Local Procedure Call)作Inter-Processus 通讯。
* Semaphore是限制访问资源的计数器
* Token (Access Token)是安全对象
* WindowStation是桌面对象容器。
这些对象可能类似目录树结构一样组织成:
- /
- ArcName (symbolic links to harddisk partitions)
- NLS (sections ...)
- Driver (installed drivers)
- WmiGuid
- Device (/dev linux like)
- DmControl
- RawDmVolumes
- HarddiskDmVolumes
- PhysicalDmVolumes
- Windows
- WindowStations
- RPC Control
- BaseNamedObjects
- Restricted
- ?? (current user directory)
- FileSystem (information about installable files system)
- ObjectTypes (contains all avaible object types)
- Security
- Callback
- KnownDlls (Contains sections of most used DLL)
"??"目录是当前用户目录,"Device"可以被看作跟LINUX上的/dev一样。你可以在Sysinternals网站上
找到这个结构。
----[ 2.2 他们的结构
每个对象都包含两部分:对象头和对象实体。Sven B. Schreiber在"Windows 2000 Undocumented Secrets"
一书中定义了多数没公开的头部结构。我们可以来看看这些头结构。
---
from w2k_def.h:
typedef struct _OBJECT_HEADER {
/*000*/ DWORD PointerCount; // number of references
/*004*/ DWORD HandleCount; // number of open handles
/*008*/ POBJECT_TYPE ObjectType; // pointer to object type struct
/*00C*/ BYTE NameOffset; // OBJECT_NAME offset
/*00D*/ BYTE HandleDBOffset; // OBJECT_HANDLE_DB offset
/*00E*/ BYTE QuotaChargesOffset; // OBJECT_QUOTA_CHARGES offset
/*00F*/ BYTE ObjectFlags; // OB_FLAG_*
/*010*/ union
{ // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock
/*010*/ PQUOTA_BLOCK QuotaBlock;
/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;
};
/*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor;
/*018*/ } OBJECT_HEADER, *POBJECT_HEADER;
---
头部中的每个偏移量都是负数偏移量,因此你如果想从头结构中找到OBJECT_NAME结构,你应该这样计算:
address = object_header_address - name_offset
OBJECT_NAME结构允许创建者通过赋一个名字让对象对其他进程可见。
OBJECT_HANDLE_DB结构允许核心跟踪当前谁正在使用该对象。
OBJECT_QUOTA_CHARGES结构用来定义了进程访问对象的配额。
OBJECT_TYPE结构存储关于对象类型的全局信息,比如:默认的安全权限,对象大小,进程使用对象的默
认配额等。
对象绑定的安全描述符可以让核心来限制对象访问。
每一个对象类型的内部程序都十分接近C++对象中的构造和析构:
* dump method -可能是为了调试目的,总为NULL
* open method -当对象句柄打开时被调用
* close method -当对象句柄关闭时被调用
* delete method -当对象句柄删除时被调用
* parse method -查询对象列表时被调用
* security method -读写保护的当前的对象时被调用
* query method -当线程查询对象名时调用
* "ok to close" -线程关闭句柄时调用
对象体的结构完全依靠对象类型,在DDK中只有很少部分的对象体结构被公开。如果你对这些结构感兴
趣,你可以使用google :) 或者查看chapeaux-noirs的主页(参见[4])
--- [ 2.3 对象操作
以用户模式的观点来看,对象操作只要是通过Windows API来执行。比如,为了访问文件对象,可以使
用 fopen()/open(),它们调用 CreateFile(). 我们转化到核心模式 (NtCreateFile())在ntoskrnl.exe
中调用IoCreateFile()。通过反编译IoCreateFile(),可以看见一些函数,比如:ObOpenObjectByName,
ObfDereferenceObject,……
(BTW:如果用在DDK站点(参见[2])下载的win2k symbols,只能看见这些函数,用支持Windows Symbols
文件的反编译器比如IDA/kd/Softice 因为这些函数没有被导出。)
每个函数都以Ob开头,表示同对象管理器相关。基本上,普通的开发者不必去处理这些对象,但我们要
去看看。
对于用户模式,所有的对象管理器相关函数都可以被ntdll.dll输出,这里有一些例子:
NtCreateDirectoryObject, NtCreateSymbolicLinkObject, NtDuplicateObject,
NtMakeTemporaryObject, NtOpenDirectoryObject, ...
有些函数在MSDN中公开了,但是多数没有。
如果你真想理解对象的工作方法,最好看看ntoskrnl.exe中导出的以Ob开头的函数。有21个导出函数,
其中6个是公开的。
如果你想看看其他15个的原型,去ntifs.h的主页(参见[3])或者去chapeaux-noirs站点(参见[4])。
--[ 3 - 介绍/Device/PhysicalMemory
为了查看对象信息,我们需要一个类似微软DDK中的核心调试工具。好,让我们现在开始……
Microsoft(R) Windows 2000 Kernel Debugger
Version 5.00.2184.1
Copyright (C) Microsoft Corp. 1981-1999
Symbol search path is: c:/winnt/symbols
Loading Dump File [livekd.dmp]
Full Kernel Dump File
Kernel Version 2195 UP Free
Kernel base = 0x80400000 PsLoadedModuleList = 0x8046a4c0
Loaded kdextx86 extension DLL
Loaded userkdx extension DLL
Loaded dbghelp extension DLL
f1919231 eb30 jmp f1919263
kd> !object /Device/PhysicalMemory
!object /Device/PhysicalMemory
Object: e1001240 Type: (fd038880) Section
ObjectHeader: e1001228
HandleCount: 0 PointerCount: 3
Directory Object: fd038970 Name: PhysicalMemory
从kd(kernel debugger)剖析基本对象告诉我们一些信息。不必解释所有内容的意义,他们中的大多数
都非常清楚只要你读了文章开头,如果没有,请"jmp dword Introduction_to_Windows_Objects"。 :)
感兴趣的是,它是一个Section对象,清楚地表明我们要处理内存。现在我们要dump对象的头结构。
kd> dd e1001228 L 6
dd e1001228 L 6
e1001228 00000003 00000000 fd038880 12200010
e1001238 00000001 e1008bf8
details:
--> 00000003 : PointerCount = 3
--> 00000000 : HandleCount = 0
--> fd038880 : pointer to object type = 0xfd038880
--> 12200010 --> 10 : NameOffset
--> 00 : HandleDBOffset
--> 20 : QuotaChargeOffset
--> 12 : ObjectFlags = OB_FLAG_PERMANENT & OB_FLAG_KERNEL_MODE
--> 00000001 : QuotaBlock
--> e1008bf8 : SecurityDescriptor
NameOffset存在,不要惊讶,这个对象有一个名字……,但是没有HandleDBOffset。这意味着这个对象
不跟踪分配的句柄。QuotaChargeOffset并不有意思,ObjectFlags告诉我们这个对象是永久对象,并且被核
心创建。
到现在还没有非常有意思的东西……
dump这个对象的名字结构,只是确信我们没有走错方向 :)。 (记住偏移量是负数)
kd> dd e1001228-10 L3
dd e1001228-10 L3
e1001218 fd038970 001c001c e1008ae8
--> fd038970 : pointer to object Directory
--> 001c001c --> 001c : UNICODE_STRING.Length
--> 001c : UNICODE_STRING.MaximumLength
--> e1008ae8 : UNICODE_STRING.Buffer (pointer to wide char string)
kd> du e1008ae8
du e1008ae8
e1008ae8 "PhysicalMemory"
现在出现有意思的部分了,安全描述符:
kd> !sd e1008bf8
!sd e1008bf8
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8004
SE_DACL_PRESENT
SE_SELF_RELATIVE
->Owner : S-1-5-32-544
->Group : S-1-5-18
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x44
->Dacl : ->AceCount : 0x2
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize: 0x14
->Dacl : ->Ace[0]: ->Mask : 0x000f001f
->Dacl : ->Ace[0]: ->SID: S-1-5-18
->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize: 0x18
->Dacl : ->Ace[1]: ->Mask : 0x0002000d
->Dacl : ->Ace[1]: ->SID: S-1-5-32-544
->Sacl : is NULL
总之,这意味着/Device/PhysicalMemory对象有下面的权限:
user SYSTEM: Delete, Change Permissions, Change Owner, Query Data,
Query State, Modify State
user Administrator: Query Data, Query State
基本上,管理员用户没有权限写,但是SYSTEM可以,这实际上意味着管理员也可以做到!
一定注意到实际上这不象/dev/kmem!!在LINUX中/dev/kmem映射虚拟内存,/Device/PhysicalMemory
映射物理内存,本文更确切的标题应该是"Playing with Windows /dev/mem"因为/dev/mem映射物理内存,
但/dev/kmem听起来更熟悉些。:)
据我所知,在我写这篇文章的时候,Section对象体结构还没有解剖开,因此我们还不能分析它的结构。
----[ 3.2 需要“写”权限?
好,我们是用户administrator,并且打算玩玩感兴趣的对象,该怎么做呢?正如多数Windows管理员
所知,可以用schedule服务运行任何进程作为SYSTEM用户。如果你想确信你可以,只要启动schedule用
“net start schedule”并且去打开一个任务执行regedit.exe
c:/>at <when> /interactive regedit.exe
之后可以试试看看SAM注册表,如果能查看,那么你SYSTEM用户,如果不能,你就仍旧是administrator
因为只有用户SYSTEM才拥有读的权力。
好,如果我们是administrator用户,但如果我们允许任何人写/Device/PhysicalMemory会发生什么
呢?(当然是为了学习的目的)
我们只要给这个对象添加另一个ACL,就可以了。按照下面的步骤:
1、打开/Device/PhysicalMemory句柄 (NtOpenSection)
2、找到它的安全描述符 (GetSecurityInfo)
3、在当前ACL中添加Read/Write授权 (SetentriesInAcl)
4、更新安全描述符 (SetSecurityInfo)
5、关闭先前打开的句柄
可以参考示例代码:chmod_mem.c
当运行了chmod_mem.exe,我们再一次dump/Device/PhysicalMemory的安全描述符。
kd> !object /Device/PhysicalMemory
!object /Device/PhysicalMemory
Object: e1001240 Type: (fd038880) Section
ObjectHeader: e1001228
HandleCount: 0 PointerCount: 3
Directory Object: fd038970 Name: PhysicalMemory
kd> dd e1001228+0x14 L1
dd e1001228+0x14 L1
e100123c e226e018
kd> !sd e226e018
!sd e226e018
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8004
SE_DACL_PRESENT
SE_SELF_RELATIVE
->Owner : S-1-5-32-544
->Group : S-1-5-18
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x68
->Dacl : ->AceCount : 0x3
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize: 0x24
->Dacl : ->Ace[0]: ->Mask : 0x00000002
->Dacl : ->Ace[0]: ->SID: S-1-5-21-1935655697-436374069-1060284298-500
->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize: 0x14
->Dacl : ->Ace[1]: ->Mask : 0x000f001f
->Dacl : ->Ace[1]: ->SID: S-1-5-18
->Dacl : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[2]: ->AceFlags: 0x0
->Dacl : ->Ace[2]: ->AceSize: 0x18
->Dacl : ->Ace[2]: ->Mask : 0x0002000d
->Dacl : ->Ace[2]: ->SID: S-1-5-32-544
->Sacl : is NULL
新的ACE(access-control entry)是Ace[0],拥有0x00000002权限(SECTION_MAP_WRITE)。需要更多
信息,可以查看MSDN中的Security win32 API [9]
--[ 4 - 玩转/Device/PhysicalMemory
为什么要来处理/Device/PhysicalMemory?我可以说用来读、写、修补内存。这已经足够了。 :)
----[ 4.1 读写内存
我们开始吧……
为了读写/Device/PhysicalMemory,必须:
1、打开对象句柄 (NtOpenSection)
2、转化虚拟内存地址为物理地址
3、映射section到物理空间 (NtMapViewOfSection)
4、在被映射的内存中读写数据
5、关闭section的映射 (NtUnmapViewOfSection)
6、关闭对象句柄 (NtClose)
现在我们的主要目的是