64位内开发第二十三讲,分层过滤驱动-键盘过滤
64位内开发第二十三讲,分层过滤驱动-键盘过滤
来自: iBinary - 博客园 禁止爬虫.如果遇到此文章不是 出自 博客园 或者 腾讯云+社区. 请举报目标网站. 或者跳转至 本人博客园进行查看.
因为文章随时更改.可能今天只是写了一部分.或者有错误. 而明天就进行更改重发了.
但是爬虫爬取的文章还是之前错误的文章.会为读者造成文章有错误的假象.
一丶键盘过滤的两种方式
1.1 第一种方式 驱动对象方式绑定
第一种方式是通过 寻找键盘驱动对象. 然后遍历其下面的所有设备. 对于每一个设备创建一个过滤设备,并且附加上去.
此方式可以应用于多个键盘设备.
核心代码如下:
#include "CMain.h"
#include <ntddkbd.h>
// 声明微软未公开的ObReferenceObjectByName()函数
extern "C" NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContest,
PVOID* Object
);
extern "C" POBJECT_TYPE * IoDriverObjectType;
VOID FilterUnload(IN PDRIVER_OBJECT pDriverObject)
{
//跟以往卸载不通.过滤驱动卸载的时候 需要解除挂载.然后删除该设备对象
//循环卸载
//IoEnumerateDeviceObjectList()
KdPrint(("[Filter]-->DriverUnload \r\n"));
PDEVICE_OBJECT next_device = nullptr;
if (pDriverObject->DeviceObject == nullptr)
{
KdPrint(("[Filter]--> Previous Driver Unload \r\n"));
return;
}
next_device = pDriverObject->DeviceObject;
while (next_device != nullptr)
{
PDEVICE_SAVE_INFOMATION device_save_info =
(PDEVICE_SAVE_INFOMATION)next_device->DeviceExtension;
if (device_save_info == nullptr)
{
IoDeleteDevice(next_device);
break;
}
//得到记录的下一个设备.
if (device_save_info->attach_to_device != nullptr)
{
//解除附加
IoDetachDevice(device_save_info->attach_to_device);
device_save_info->attach_to_device = nullptr;
}
//删除设备
IoDeleteDevice(next_device);
device_save_info->src_device = nullptr;
next_device = next_device->NextDevice;
}
KdPrint(("[Filter]--> Perfect Driver Unload \r\n"));
}
NTSTATUS Ctrl2capPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_SAVE_INFOMATION devExt;
devExt = (PDEVICE_SAVE_INFOMATION)DeviceObject->DeviceExtension;
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(devExt->attach_to_device, Irp);
}
NTSTATUS Ctrl2capPnP(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_SAVE_INFOMATION devExt;
PIO_STACK_LOCATION irpStack;
NTSTATUS status = STATUS_SUCCESS;
KIRQL oldIrql;
KEVENT event;
devExt = (PDEVICE_SAVE_INFOMATION)DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MinorFunction) {
case IRP_MN_REMOVE_DEVICE:
IoSkipCurrentIrpStackLocation(Irp);
IoCallDriver(devExt->attach_to_device, Irp);
IoDetachDevice(devExt->attach_to_device);
IoDeleteDevice(DeviceObject);
status = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
//
// Same as a remove device, but don't call IoDetach or IoDeleteDevice.
//
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(devExt->attach_to_device, Irp);
break;
case IRP_MN_START_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
case IRP_MN_STOP_DEVICE:
case IRP_MN_QUERY_DEVICE_RELATIONS:
case IRP_MN_QUERY_INTERFACE:
case IRP_MN_QUERY_CAPABILITIES:
case IRP_MN_QUERY_DEVICE_TEXT:
case IRP_MN_QUERY_RESOURCES:
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
case IRP_MN_READ_CONFIG:
case IRP_MN_WRITE_CONFIG:
case IRP_MN_EJECT:
case IRP_MN_SET_LOCK:
case IRP_MN_QUERY_ID:
case IRP_MN_QUERY_PNP_DEVICE_STATE:
default:
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(devExt->attach_to_device, Irp);
break;
}
return status;
}
PDEVICE_OBJECT FilterAttach(PDEVICE_OBJECT src_device, PDEVICE_OBJECT target_device)
{
PDEVICE_OBJECT attach_to_device = nullptr;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = IoAttachDeviceToDeviceStackSafe(src_device, target_device, &attach_to_device);
if (NT_ERROR(status))
return nullptr;
return attach_to_device;
}
NTSTATUS FilterComplete(IN PDEVICE_OBJECT driver,
IN PIRP pIrp)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
//将自己完成IRP,改成由底层驱动负责
PDEVICE_SAVE_INFOMATION pdx = (PDEVICE_SAVE_INFOMATION)driver->DeviceExtension;
//调用底层驱动
IoSkipCurrentIrpStackLocation(pIrp);
ntStatus = IoCallDriver(pdx->attach_to_device, pIrp);
return ntStatus;
}
NTSTATUS
keyboard(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
KdBreakPoint();
PIO_STACK_LOCATION irp_stack = nullptr;
PKEYBOARD_INPUT_DATA key_data_ptr = nullptr;
ULONG number_keys = 0;
irp_stack = IoGetCurrentIrpStackLocation(Irp);
if (NT_SUCCESS(Irp->IoStatus.Status))
{
//获取Irp中的数据.
key_data_ptr = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
number_keys = Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
for (int i = 0; i < number_keys; i++)
{
KdPrint(
("The Code is [%x] key State = [%s] \r\n",
key_data_ptr[i].MakeCode, key_data_ptr[i].Flags ? "up" : "down")
);
}
}
//处理pending位传播
if (Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
return Irp->IoStatus.Status;
}
NTSTATUS FilterRead(IN PDEVICE_OBJECT driver,
IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
if (pIrp->CurrentLocation == 1)
{
status = STATUS_INVALID_DEVICE_REQUEST;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
IoCopyCurrentIrpStackLocationToNext(pIrp);
//设置一个完成例程,用来进行过滤.当底层按键返回的时候则会触发完成例程.
IoSetCompletionRoutine(
pIrp,
keyboard,
driver,
TRUE,
TRUE,
TRUE);
//调用底层驱动
PDEVICE_SAVE_INFOMATION pdx = (PDEVICE_SAVE_INFOMATION)driver->DeviceExtension;
return IoCallDriver(pdx->attach_to_device, pIrp);
}
PDRIVER_OBJECT GetKeyBoardDriverObjByDriverName(WCHAR* driver_name)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PDRIVER_OBJECT keyboard_driver = nullptr;
UNICODE_STRING ucd_kbd_driver_name = { 0 };
status = RtlUnicodeStringInit(&ucd_kbd_driver_name, driver_name);
if (NT_ERROR(status))
{
return nullptr;
}
//通过驱动名寻找其驱动对象. 然后寻找驱动对象里面记录的设备.找到所属的设备进行绑定.
status = ObReferenceObjectByName(
&ucd_kbd_driver_name,
OBJ_CASE_INSENSITIVE,
NULL,
0,
*IoDriverObjectType,
KernelMode,
NULL,
(PVOID*)&keyboard_driver);
if (NT_ERROR(status))
{
return nullptr;
}
else
{
ObDereferenceObject(keyboard_driver);
return keyboard_driver;
}
return keyboard_driver;
}
PDEVICE_OBJECT CreateDevice(
PDRIVER_OBJECT driver,
PDEVICE_OBJECT target_device)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PDEVICE_OBJECT filter_device = nullptr;
status = IoCreateDevice(driver,
sizeof(DEVICE_SAVE_INFOMATION),
NULL,
target_device->DeviceType,
target_device->Characteristics,
FALSE,
&filter_device);
if (NT_ERROR(status))
{
return nullptr;
}
return filter_device;
}
PDEVICE_OBJECT AttachToDevice(
PDEVICE_OBJECT filter_device,
PDEVICE_OBJECT target_device)
{
PDEVICE_OBJECT stack_low_device = nullptr;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = IoAttachDeviceToDeviceStackSafe(
filter_device,
target_device,
&stack_low_device);
if (NT_ERROR(status))
{
return nullptr;
}
return stack_low_device;
}
void InitDeviceExtension(
PDEVICE_OBJECT filter_device,
PDEVICE_OBJECT target_device,
PDEVICE_OBJECT stack_low_device)
{
PDEVICE_SAVE_INFOMATION save_info = (PDEVICE_SAVE_INFOMATION)filter_device->DeviceExtension;
save_info->src_device = filter_device;
save_info->target_next_device = target_device;
save_info->attach_to_device = stack_low_device;
}
NTSTATUS InitAttach(PDRIVER_OBJECT keyboard_driver, PDRIVER_OBJECT driver)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PDEVICE_OBJECT target_device = nullptr;
PDEVICE_OBJECT filter_device = nullptr;
PDEVICE_OBJECT stack_low_device = nullptr;
//循环遍历驱动对象里面的设备.并且创建相同过滤设备进行绑定.
KdBreakPoint();
target_device = keyboard_driver->DeviceObject;
while (target_device)
{
//创建过滤设备
filter_device = CreateDevice(driver, target_device);
if (filter_device == nullptr)
{
return STATUS_UNSUCCESSFUL;
}
else
{
//进行绑定
stack_low_device = AttachToDevice(filter_device, target_device);
if (stack_low_device == nullptr)
{
if (filter_device != nullptr)
{
IoDeleteDevice(filter_device);
}
filter_device = nullptr;
return status;
}
//记录相应的设备.
InitDeviceExtension(filter_device, target_device, stack_low_device);
//初始化过滤设备的属性
filter_device->DeviceType = stack_low_device->DeviceType;
filter_device->Characteristics = stack_low_device->Characteristics;
filter_device->StackSize = stack_low_device->StackSize + 1;
filter_device->Flags |= stack_low_device->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
}
target_device = target_device->NextDevice;
}
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING regpath)
{
//1.设置驱动的卸载以及派遣函数
driver->DriverUnload = FilterUnload;
for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
driver->MajorFunction[i] = FilterComplete;
}
driver->MajorFunction[IRP_MJ_READ] = FilterRead;
driver->MajorFunction[IRP_MJ_POWER] = Ctrl2capPower;
driver->MajorFunction[IRP_MJ_PNP] = Ctrl2capPnP;
//1.寻找目标设备的驱动对象
KdBreakPoint();
PDRIVER_OBJECT keyboard_driver = nullptr;
keyboard_driver = GetKeyBoardDriverObjByDriverName(L"\\Driver\\Kbdclass");
if (keyboard_driver == nullptr)
{
return STATUS_UNSUCCESSFUL;
}
//2.进行绑定
return InitAttach(keyboard_driver, driver);
}
1.2 第二种方式,直接设备类型绑定.
此方式可以使用Winobj
来查看一下你想绑定的键盘设备. 代码还是使用 上一节所用.
在Winobj如下界面则可以看到键盘驱动有多少了.
只需要微微改动即可.
#include "CMain.h"
#include <ntddkbd.h>
PDEVICE_OBJECT GetAttachDeviceByName(
IN wchar_t* attach_device_name,
OUT PFILE_OBJECT* fileobj)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING ucd_attach_device_name = { 0 };
PDEVICE_OBJECT target_next_device = nullptr;
if (attach_device_name == nullptr)
return nullptr;
status = RtlUnicodeStringInit(&ucd_attach_device_name, attach_device_name);
if (NT_ERROR(status))
return nullptr;
status = IoGetDeviceObjectPointer(&ucd_attach_device_name,
FILE_ALL_ACCESS,
fileobj,
&target_next_device);
if (NT_ERROR(status))
{
KdPrint(("[Filter]--->IoGetDeviceObjectPointer Error\r\n"));
return nullptr;
}
return target_next_device;
}
VOID FilterUnload(IN PDRIVER_OBJECT pDriverObject)
{
//跟以往卸载不通.过滤驱动卸载的时候 需要解除挂载.然后删除该设备对象
//循环卸载
//IoEnumerateDeviceObjectList()
KdPrint(("[Filter]-->DriverUnload \r\n"));
PDEVICE_OBJECT next_device = nullptr;
if (pDriverObject->DeviceObject == nullptr)
{
KdPrint(("[Filter]--> Previous Driver Unload \r\n"));
return;
}
next_device = pDriverObject->DeviceObject;
while (next_device != nullptr)
{
PDEVICE_SAVE_INFOMATION device_save_info =
(PDEVICE_SAVE_INFOMATION)next_device->DeviceExtension;
if (device_save_info == nullptr)
{
IoDeleteDevice(next_device);
break;
}
//得到记录的下一个设备.
if (device_save_info->attach_to_device != nullptr)
{
//解除附加
IoDetachDevice(device_save_info->attach_to_device);
device_save_info->attach_to_device = nullptr;
}
if (device_save_info->target_next_fileobj != nullptr)
{
//解除引用
ObDereferenceObject(device_save_info->target_next_fileobj);
device_save_info->target_next_fileobj = nullptr;
device_save_info->target_next_device = nullptr;
}
//删除设备
IoDeleteDevice(next_device);
device_save_info->src_device = nullptr;
next_device = next_device->NextDevice;
}
KdPrint(("[Filter]--> Perfect Driver Unload \r\n"));
}
PDEVICE_OBJECT CreateFilterDevice(PDRIVER_OBJECT driver)
{
UNICODE_STRING ucd_filter_device_name = { 0 };
NTSTATUS status = STATUS_UNSUCCESSFUL;
PDEVICE_OBJECT filter_device = nullptr;
if (driver == nullptr)
return nullptr;
//创建设备
status = IoCreateDevice(
driver,
sizeof(DEVICE_SAVE_INFOMATION),
nullptr,
FILE_DEVICE_KEYBOARD,
0,
FALSE,
&filter_device);
if (NT_ERROR(status))
return nullptr;
//初始化驱动
filter_device->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
filter_device->Flags &= ~DO_DEVICE_INITIALIZING;
return filter_device;
}
PDEVICE_OBJECT FilterAttach(PDEVICE_OBJECT src_device, PDEVICE_OBJECT target_device)
{
PDEVICE_OBJECT attach_to_device = nullptr;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = IoAttachDeviceToDeviceStackSafe(src_device, target_device, &attach_to_device);
if (NT_ERROR(status))
return nullptr;
return attach_to_device;
}
NTSTATUS FilterComplete(IN PDEVICE_OBJECT driver,
IN PIRP pIrp)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
//将自己完成IRP,改成由底层驱动负责
PDEVICE_SAVE_INFOMATION pdx = (PDEVICE_SAVE_INFOMATION)driver->DeviceExtension;
//调用底层驱动
IoSkipCurrentIrpStackLocation(pIrp);
ntStatus = IoCallDriver(pdx->attach_to_device, pIrp);
return ntStatus;
}
NTSTATUS Ctrl2capPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_SAVE_INFOMATION devExt;
devExt = (PDEVICE_SAVE_INFOMATION)DeviceObject->DeviceExtension;
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(devExt->attach_to_device, Irp);
}
NTSTATUS Ctrl2capPnP(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_SAVE_INFOMATION devExt;
PIO_STACK_LOCATION irpStack;
NTSTATUS status = STATUS_SUCCESS;
KIRQL oldIrql;
KEVENT event;
devExt = (PDEVICE_SAVE_INFOMATION)DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MinorFunction) {
case IRP_MN_REMOVE_DEVICE:
IoSkipCurrentIrpStackLocation(Irp);
IoCallDriver(devExt->attach_to_device, Irp);
IoDetachDevice(devExt->attach_to_device);
IoDeleteDevice(DeviceObject);
status = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
//
// Same as a remove device, but don't call IoDetach or IoDeleteDevice.
//
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(devExt->attach_to_device, Irp);
break;
case IRP_MN_START_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
case IRP_MN_STOP_DEVICE:
case IRP_MN_QUERY_DEVICE_RELATIONS:
case IRP_MN_QUERY_INTERFACE:
case IRP_MN_QUERY_CAPABILITIES:
case IRP_MN_QUERY_DEVICE_TEXT:
case IRP_MN_QUERY_RESOURCES:
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
case IRP_MN_READ_CONFIG:
case IRP_MN_WRITE_CONFIG:
case IRP_MN_EJECT:
case IRP_MN_SET_LOCK:
case IRP_MN_QUERY_ID:
case IRP_MN_QUERY_PNP_DEVICE_STATE:
default:
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(devExt->attach_to_device, Irp);
break;
}
return status;
}
NTSTATUS Ctrl2capReadComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PIO_STACK_LOCATION IrpSp = nullptr;
PKEYBOARD_INPUT_DATA key_data_ptr = nullptr;
ULONG numKeys,i = 0;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (NT_SUCCESS(Irp->IoStatus.Status)) {
key_data_ptr = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
numKeys = Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
for (i = 0; i < numKeys; i++) {
KdPrint(
("The Code is [%x] key State = [%s] \r\n",
key_data_ptr[i].MakeCode, key_data_ptr[i].Flags ? "up" : "down")
);
//替换按键
if (key_data_ptr[i].MakeCode == 0x1f) {
key_data_ptr[i].MakeCode = 0x20;
}
}
}
//传播pending
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
return Irp->IoStatus.Status;
}
NTSTATUS FilterRead(IN PDEVICE_OBJECT device,
IN PIRP pIrp)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IoCopyCurrentIrpStackLocationToNext(pIrp);
IoSetCompletionRoutine(
pIrp,
Ctrl2capReadComplete,
device,
TRUE,
TRUE,
TRUE);
PDEVICE_SAVE_INFOMATION pdx = (PDEVICE_SAVE_INFOMATION)device->DeviceExtension;
return IoCallDriver(pdx->attach_to_device,pIrp);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING regpath)
{
//1.设置驱动的卸载以及派遣函数
driver->DriverUnload = FilterUnload;
for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
driver->MajorFunction[i] = FilterComplete;
}
driver->MajorFunction[IRP_MJ_READ] = FilterRead;
driver->MajorFunction[IRP_MJ_POWER] = Ctrl2capPower;
driver->MajorFunction[IRP_MJ_PNP] = Ctrl2capPnP;
//2.寻找目标设备.也就是我们想要Attah的设备
PDEVICE_OBJECT target_device = nullptr;
PFILE_OBJECT target_fileobj = nullptr;
PDEVICE_OBJECT src_device = nullptr;
PDEVICE_OBJECT attach_to_device = nullptr;
KdBreakPoint();
target_device = GetAttachDeviceByName(L"\\Device\\KeyboardClass0", &target_fileobj);
if (target_device == nullptr)
{
KdPrint(("[Filter]--> GetAttachDeviceByName Fail \r\n"));
return STATUS_UNSUCCESSFUL;
}
KdPrint(("[Filter]--> Mount Target Device = [%p] \r\n", target_device));
//3.创建设备,以及设置设备扩展
src_device = CreateFilterDevice(driver);
if (src_device == nullptr)
{
KdPrint(("[Filter]--> CreateFilterDevice Fail \r\n"));
if (target_fileobj != nullptr)
{
ObReferenceObject(target_fileobj);
target_fileobj = nullptr;
}
return STATUS_UNSUCCESSFUL;
}
KdPrint(("[Filter]--> Filter Device = [%p] \r\n", src_device));
//4.Attach到目标设备
attach_to_device = FilterAttach(src_device, target_device);
if (attach_to_device == nullptr)
{
KdPrint(("[Filter]--> FilterAttach Fail \r\n"));
if (target_fileobj != nullptr)
{
ObReferenceObject(target_fileobj);
target_fileobj = nullptr;
}
return STATUS_UNSUCCESSFUL;
}
KdPrint(("[Filter]--> Attach Device = [%p] \r\n", attach_to_device));
//5.记录一下信息
PDEVICE_SAVE_INFOMATION device_save_info_ptr =
(PDEVICE_SAVE_INFOMATION)src_device->DeviceExtension;
device_save_info_ptr->src_device = src_device; //记录我们的设备
device_save_info_ptr->target_next_device = target_device; //记录我们要挂载的目标设备
device_save_info_ptr->target_next_fileobj = target_fileobj; //记录文件对象
device_save_info_ptr->attach_to_device = attach_to_device; //记录下一层设备
return STATUS_SUCCESS;
}
1.3 效果
第一种方式 只是进行了打印输出。
第二种方式 是把s键替换成了d键 所以此时如果按下s 那么将会被替换成d
注意:
代码只是一个demo 便于说明键盘过滤是怎么一回事. 并不保证运行后不会蓝屏.因为并没有做同步等相关处理.
也可能会有其他问题.
作者:IBinary
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/16609865.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能.![微信公众号](https://images.cnblogs.com/cnblogs_com/iBinary/1827080/o_220306121308_qrcode_for_gh_b8b8f84e4988_258.jpg)
![QQ群](https://images.cnblogs.com/cnblogs_com/iBinary/1827080/o_21062107294320210621152919.png)