玩转Windows /dev/(k)mem

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)

    现在我们的主要目的是
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值