键盘绑定

1.理论:

从击键到内核,前后的执行情况,之间没有必然关联

按键->csrss.exe->win32!RawInputThread->win32k!OpenDevice->ZwCreateFile->NtCreateFile->ntIopParseDevice->nt!IoGetAttachedDevice

->IoAllocateIrp->nt!ObCreateObject->nt!IopfCallDriver->nt!ObOpenObjectByName->ntObpCreateHandle->nt!ZwReadFile->中断.........

简单点就是csrss程序的 win32!RawInputThread 发起一个IRP_READ的请求给键盘驱动,当没有按键时这个请求将等待,知道按下了键.

当按下键后将中断,中断服务程序由键盘驱动程序提供,然后键盘驱动从端口读取扫描码处理后发给csrss提交的IRP,最后win32!RawInputThread

通过nt!ZwReadFile读取了按键的信息.

 

如果没有其他过滤的程序,设备栈:

最顶层的设备对象是KdbClass生成的设备对象

中间层的设备对象是驱动i8042prt生成的设备对象

最底层的设备对象是驱动acpi生成的设备对象

 

 

 

 

键盘输出键原理:

键盘与cpu交互方式是中断和读取端口,是串行的.发生一次中断等于键盘发送一个通知(事件):某个键被按下或者某个键被弹起.cpu只接受通知并读取端口的扫描码

一般来说按下某个键的扫描码比弹起该键的扫描码低0x80

 

实例代码:

//__stdcall
#include<ntddk.h>
#include<Ntddkbd.h>
#include<ntstrsafe.h>
#pragma code_seg("INT")extern POBJECT_TYPE IoDriverObjectType;
#define KBD_DRIVER_NAME L"\\Driver\\kbdClass"
//用于计时
#define  DELAY_ONE_MICROSECOND  (-10)
#define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)
NTSTATUS ObReferenceObjectByName(PUNICODE_STRING objectName, ULONG Attributes, PACCESS_STATE AccessState, ACCESS_MASK DesiredAccess, POBJECT_TYPE objectType, KPROCESSOR_MODE AccessMode, PVOID ParseContext, PVOID *Object);ULONG keyCount=0;//设置大写锁定,小键盘锁定和shift键状态
#define    S_SHIFT                1
#define    S_CAPS                2
#define    S_NUM                4
static int kb_status = S_NUM;#define KEY_UP 1 
#define KEY_DOWN 0 //定义大写锁定键和ctrl键的扫描码,其实可以定义一个全键盘的扫描码
#define LCONTROL ((USHORT)0x1D) 
#define CAPS_LOCK ((USHORT)0x3A) unsigned char asciiTbl[] = {0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,    //normal0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E,0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,    //caps0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E,0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,    //shift0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E,0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,    //caps + shift0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E
};
typedef struct _DEV_EXT
{// 这个结构的大小
    ULONG NodeSize;// 过滤设备对象
    PDEVICE_OBJECT pFilterDeviceObject;// 绑定的设备对象
    PDEVICE_OBJECT TargetDeviceObject;// 绑定前底层设备对象
    PDEVICE_OBJECT LowerDeviceObject;
} DEV_EXT, *PDEV_EXT;VOID initDevExt(PDEV_EXT pDevExt, PDEVICE_OBJECT pFdo, PDEVICE_OBJECT pTopDev, PDEVICE_OBJECT pKbdDeviceObject)
{memset(pDevExt, 0, sizeof(pDevExt));pDevExt->NodeSize = sizeof(pDevExt);pDevExt->LowerDeviceObject = pTopDev;pDevExt->pFilterDeviceObject = pFdo;pDevExt->TargetDeviceObject = pKbdDeviceObject;}NTSTATUS MyAttachDevices(PDRIVER_OBJECT pDriverObject)
{NTSTATUS status = STATUS_SUCCESS;PDRIVER_OBJECT kbdDriver = NULL;UNICODE_STRING kbdDriverName;PDEVICE_OBJECT pKbdDeviceObject = NULL;PDEVICE_OBJECT filterDeviceObject = NULL;PDEVICE_OBJECT pTopDev = NULL;PDEV_EXT pDevExt = NULL;//获取驱动对象RtlInitUnicodeString(&kbdDriverName, KBD_DRIVER_NAME);status = ObReferenceObjectByName(&kbdDriverName, OBJ_CASE_INSENSITIVE, 0, 0, IoDriverObjectType, KernelMode, 0, &kbdDriver);if (status != STATUS_SUCCESS){DbgPrint("获取驱动对象失败\n");return status;}else{ObDereferenceObject(pDriverObject);//先解除引用,驱动对象引用计数-1,不影响后面代码以免忘记//驱动对象的设备对象形成一个链表,一个一个生成过滤设备并绑定到它的设备栈栈顶pKbdDeviceObject = kbdDriver->DeviceObject;while (pKbdDeviceObject){status = IoCreateDevice(pDriverObject, sizeof(DEV_EXT), 0, pKbdDeviceObject->DeviceType, pKbdDeviceObject->Characteristics, 0, &filterDeviceObject);if (status != STATUS_SUCCESS){DbgPrint("创建设备失败\n");return status;}else{pTopDev = IoAttachDeviceToDeviceStack(filterDeviceObject, pKbdDeviceObject);if (!pTopDev){DbgPrint("附加失败\n");IoDeleteDevice(filterDeviceObject);status = STATUS_UNSUCCESSFUL;return status;}else{//绑定成功后先将实际被绑定的设备对象和驱动对象的设备对象添加到过滤设备的                    //拓展对象(自定义的东西方便后续访问他们)上pDevExt = (PDEV_EXT)filterDeviceObject->DeviceExtension;initDevExt(pDevExt, filterDeviceObject, pTopDev, pKbdDeviceObject);//复制特征filterDeviceObject->DeviceType = pTopDev->DeviceType;filterDeviceObject->Characteristics = pTopDev->Characteristics;filterDeviceObject->StackSize = pTopDev->StackSize + 1;filterDeviceObject->Flags |= pTopDev->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);}}//移动到下一个设备对象pKbdDeviceObject = pKbdDeviceObject->NextDevice;}return STATUS_SUCCESS;}
}void __stdcall showKey(USHORT ch)
{UCHAR c = 0;int off = 0;if ((ch & 0x80) == 0){if (ch<0x47||((ch>=0x47&&ch<0x54)&&(kb_status&S_NUM))){c = asciiTbl[ch + off];}switch (ch){case 0x3a:kb_status ^= S_CAPS;break;case 0x45:kb_status ^= S_NUM;break;//左shift和右shiftcase 0x2a:case 0x36:kb_status |= S_SHIFT;break;default:break;}}else{if (ch==0xaa||ch==0xb6){kb_status &= ~S_SHIFT;}}if (c>=0x20&&c<0x7f){DbgPrint("%c \n", c);}}
NTSTATUS readComplete(PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID context)
{//所有的其他代码都是为这个函数准备的.
    PIO_STACK_LOCATION pIrpStack;ULONG buf_len = 0;PUCHAR buf = NULL;size_t i, numKeys;PKEYBOARD_INPUT_DATA KeyData;pIrpStack = IoGetCurrentIrpStackLocation(pIrp);if (NT_SUCCESS(pIrp->IoStatus.Status)){//获取读取的缓冲区和长度buf = pIrp->AssociatedIrp.SystemBuffer;buf_len = pIrp->IoStatus.Information;//    //该缓冲区其实是KEYBOARD_INPUT_DATA结构体,KeyData获取一个结构体,有numKeys个//    //typedef struct _KEYBOARD_INPUT_DATA {//    USHORT UnitId;//    USHORT MakeCode; //扫描码//    USHORT Flags;        //1是按下0是弹起//    USHORT Reserved;//    ULONG ExtraInformation;//} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;KeyData = (PKEYBOARD_INPUT_DATA)buf;numKeys = pIrp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);//对这些结构体包含的按键信息输出即扫描码和键是按下还是弹起for ( i = 0; i < numKeys; i++){DbgPrint("numkeys: %d\n", numKeys);DbgPrint("scanf code is %x\n", KeyData->MakeCode);DbgPrint("%s\n", KeyData->Flags ? "up" : "down");showKey(KeyData->MakeCode);//对大写锁定键进行过滤,替换为ctrlif (KeyData->MakeCode==CAPS_LOCK){KeyData->MakeCode = LCONTROL;}}}keyCount--;if (pIrp->PendingReturned){IoMarkIrpPending(pIrp);}return pIrp->IoStatus.Status;
}NTSTATUS MyDisPatcher(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{//非power,pnp,read类型的irp一律跳过
    IoSkipCurrentIrpStackLocation(pIrp);return IoCallDriver(((DEV_EXT*)pDeviceObject->DeviceExtension)->LowerDeviceObject, pIrp);
}
NTSTATUS MyPowerDisPatcher(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{//处理powerirp,其实和其他irp一样只是将power类型irp交给栈下面的设备对象处理
    PoStartNextPowerIrp(pIrp);IoSkipCurrentIrpStackLocation(pIrp);return PoCallDriver(((DEV_EXT*)pDeviceObject->DeviceExtension)->LowerDeviceObject,pIrp);
}
NTSTATUS MyPnpDisPatcher(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{//处理即插即用,对于大多数即插即用的设备基本都这样处理就行DEV_EXT* devExt = (DEV_EXT*)pDeviceObject->DeviceExtension;PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);NTSTATUS status = STATUS_SUCCESS;KIRQL oldIrql; //优先级KEVENT event;//设备被移除,插入的时候发生这些副irp消息.直接跳过即可,但是因为移除的时候需要将附加的设备//对象解除绑定并删除switch (pIrpStack->MinorFunction){case IRP_MN_REMOVE_DEVICE:DbgPrint("键盘被移除\n");IoSkipCurrentIrpStackLocation(pIrp);IoCallDriver(devExt->LowerDeviceObject,pIrp);        IoDetachDevice(devExt->LowerDeviceObject);IoDeleteDevice(pDeviceObject);status = STATUS_SUCCESS;break;default:IoSkipCurrentIrpStackLocation(pIrp);status = IoCallDriver(devExt->LowerDeviceObject, pIrp);status = STATUS_SUCCESS;break;}return status;
}NTSTATUS MyReadDisPatcher(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{NTSTATUS status =STATUS_SUCCESS ;DEV_EXT* devExt;PIO_STACK_LOCATION pIrpStack;KEVENT waitEvent;KeInitializeEvent(&waitEvent, NotificationEvent, FALSE);if (pIrp->CurrentLocation==1){ULONG returnInformation = 0;DbgPrint("bogus current\n");status = STATUS_INVALID_DEVICE_REQUEST;pIrp->IoStatus.Status = status;pIrp->IoStatus.Information = returnInformation;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return status;}else{//这里处理irp,因为irp从栈顶过来时是有I/O管理器发来的,还未被底层的设备对象处理//所以要先将irp向下发,然后等irp从栈底被写入键盘按键信息后再从栈底往上发,这时//就可以截获irp中的按键信息. 怎么截获呢?通过注册一个完成例程,因为当irp被处理完//会从底层向上调用他们注册的完成例程.所以这里需要通过注册完成例程获取irp并处理//详细原理参考:windows内核原理与实现 462页//这里和跳过其实是一样的,只是因为需要注册完成例程而需要调用//函数IoCopyCurrentIrpStackLocationToNext而不是IoSkipCurrentIrpStackLocationkeyCount++;devExt = (DEV_EXT*)pDeviceObject->DeviceExtension;pIrpStack = IoGetCurrentIrpStackLocation(pIrp);IoCopyCurrentIrpStackLocationToNext(pIrp);//注册完成例程函数IoSetCompletionRoutine(pIrp, readComplete, pDeviceObject, 1, 1, 1);return IoCallDriver(devExt->LowerDeviceObject, pIrp);}
}
VOID MyDetach(PDEVICE_OBJECT pDeviceObject)
{DEV_EXT* devExt = pDeviceObject->DeviceExtension;__try{__try{IoDetachDevice(devExt->TargetDeviceObject);devExt->TargetDeviceObject = 0;IoDeleteDevice(pDeviceObject);devExt->pFilterDeviceObject = 0;DbgPrint("解除绑定\n");}__except(EXCEPTION_EXECUTE_HANDLER){}}__finally{}
}
VOID UnLoadDriver(PDRIVER_OBJECT pDriverObject)
{PDEVICE_OBJECT pDeviceObject, pOldDeivceObject;DEV_EXT devExt;LARGE_INTEGER lDelay;PRKTHREAD CurrentThread;lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND);//设置时间对象CurrentThread = KeGetCurrentThread();//将优先级降低
    KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY);UNREFERENCED_PARAMETER(pDriverObject);DbgPrint("开始卸载\n");pDeviceObject = pDriverObject->DeviceObject;while (pDeviceObject){MyDetach(pDeviceObject);//解除绑定并删除pDeviceObject = pDeviceObject->NextDevice;}while (keyCount){KeDelayExecutionThread(KernelMode, FALSE, &lDelay);}DbgPrint("卸载完成!\n");return;}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING us)
{NTSTATUS status=STATUS_SUCCESS;//设置分发函数,专门处理电源irp,pnp的irp和read的irpULONG i = 0;for (; i < IRP_MJ_MAXIMUM_FUNCTION; i++){pDriverObject->MajorFunction[i] = MyDisPatcher;}pDriverObject->MajorFunction[IRP_MJ_READ] = MyReadDisPatcher;pDriverObject->MajorFunction[IRP_MJ_POWER] = MyPowerDisPatcher;pDriverObject->MajorFunction[IRP_MJ_PNP] = MyPnpDisPatcher;pDriverObject->DriverUnload = UnLoadDriver;MyAttachDevices(pDriverObject);return status;
}

 

转载于:https://www.cnblogs.com/freesec/p/6207737.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/253674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Linux内核设计与实现》读书笔记(七)- 中断处理

中断处理一般不是纯软件来实现的&#xff0c;需要硬件的支持。通过对中断的学习有助于更深入的了解系统的一些底层原理&#xff0c;特别是驱动程序的开发。 主要内容&#xff1a; 什么是中断中断类型中断相关函数中断处理机制中断控制方法总结1. 什么是中断 为了提高CPU和外围硬…

入门视频采集与处理(学会分析YUV数据)

标签&#xff1a;分析码流 视频采集 RGB YUV 原创作品&#xff0c;允许转载&#xff0c;转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://ticktick.blog.51cto.com/823160/555791做视频采集与处理&#xff0c;自然少不了要学会分析…

数字后端——时序验证

时序验证则是采用时序分析等方法验证设计是否满足时序收敛&#xff0c;这些时序检验工作包括反向标定(back-annotation)、时序与功耗的检验、时序与信号完整性的检验和当代低功耗纳米先进设计中的“多模式多端角”(MMMC&#xff0c;multi-mode multi-comer)检验。 一、反向标定…

Hadoop系列(三)MapReduce Job的几种提交运行模式

Job执行可以分为本地执行或者集群执行。hadoop集群安装部署在远程centos系统中。使用经典的WordCount代码为例。 1. 本地执行模式&#xff08;本地为MacOS环境&#xff09;&#xff0c;无需启动远程的hadoop集群&#xff0c;本地job会提交给本地执行器LocalJobRunner去执行。 1…

2600: [Ioi2011]ricehubh

Description 乡间有一条笔直而长的路称为“米道”。沿着这条米道上 R 块稻田&#xff0c;每块稻田的坐标均为一个 1 到 L 之间(含 1 和 L)的整数。这些稻田按照坐标以不减的顺序给出&#xff0c;即对于 0 ≤ i <R&#xff0c;稻田 i 的坐标 X[i]满足 1 ≤ X[0] ≤ ... ≤ X[…

常见视频接口介绍,VGA,YPbPr,DVI,HDMI,DisplayPort

1&#xff0c;VGA(D-SUB) 这种是显示器最常见的&#xff0c;用了很多年&#xff0c;色域空间是RGB&#xff0c;也就是红绿蓝&#xff0c;模拟信号&#xff0c;无音频 插头是15针的&#xff0c;实际所需的最小针数应该是5针&#xff0c;也就是RGB三色信号&#xff0c;水平…

js 对已知数组数据的导出EXCEL

1. 方法一 <a id"frontExportLogLink" href"javascript:void(0)" ng-click"exportLog()" class"btn btn-danger">导出<span class"glyphicon glyphicon-question-sign mgl10" tooltip"{{不支持ie | translate…

芯片面积估计方法

一、概念 芯片面积的主要涵盖部分分为三部分 IO&#xff1a;芯片的信号及电源pad等Standard cell : 实现芯片的功能逻辑Macro block &#xff1a;第三方IP( PLL DAC POR Memory .etc )芯片面积估计就是通过目标工艺的库信息&#xff0c;设计的spec、以往设计的信息及&#xff…

WordPress开发之WP Custom Register Login插件试用

简介 WP Custom Register Login可以为你的WordPress网站前台增加注册、登录、找回密码的功能&#xff1b;你可以通过简码在任何页面上调用。此外&#xff0c;该插件还支持设置自动通过用户的电子邮件验证新帐户激活&#xff0c;自带算术验证码&#xff0c;有效防护垃圾注册。对…

Java数据类型(基本数据类型)学习

Java数据类型&#xff08;基本数据类型&#xff09;学习 与其他语言一样&#xff0c;Java编程同样存在&#xff0c;比如int a&#xff0c;float b等。在学习变量之前我就必须先了解Java的数据类型啦。 Java的数据类型包括基本数据类型和引用数据类型。具体如下&#xff1a; 各数…

电视信号——行场同步

电视信号分NTSC制和PAL制两种制式, NTSC制每秒刷新60次,而PAL制每秒刷新50次。 水平消隐&#xff1a;电子枪从左到右画出象素&#xff0c;它每次只能画一条扫描线&#xff0c;画下一条之前要先回到左边并做好画下一条扫描线的准备&#xff0c;这之间有一段时间叫做水平消隐&…

SLVS-EC接口学习

SLVS summarize 一、概述 SLVS-EC高速串行接口技术&#xff0c;在CIS和DSP&#xff08;数字信号处理器&#xff09;之间实现了高帧率的宽带像素数据传输。 SLVS-EC引入了一个优化的数据包格式和控制协议&#xff0c;几乎没有冗余&#xff0c;而且结构简单&#xff0c;仅由两层…

关于Unity中NGUI的Pivot和锚点

Pivot 1.创建一个Sprite类型的Sprite1节点&#xff0c;关联一个图集和一张贴图&#xff0c;用图中的六个按钮调整这个贴图的Pivot点&#xff0c;一共有八个点可以选择 2.再创建一个Sprite类型的Sprite2节点&#xff0c;作为Sprite1节点的子节点&#xff0c;关联一个图集和一张贴…

PrimeTime指南——概述和基本流程

PrimeTime&#xff08;PT&#xff09;是Synopsys的sign-off quality的静态时序分析工具。PrimeTime可以集成于逻辑综合和物理综合的流程&#xff0c;让设计者分析并解决复杂的时序问题&#xff0c;并提高时序收敛的速度。 一、概述 PT最大的两个特点是&#xff1a; 基于时序路…

yuv和yCbCr的差异

yuv和yCbCr的差异 一、和rgb之间换算公式的差异 yuv<-->rgb Y 0.299*R 0.587*G 0.114*B U -0.147*R - 0.289*G 0.436*B 0.492*(B- Y) V 0.615*R - 0.515*G - 0.100*B 0.877*(R- Y) R Y 1.140*V G Y - 0.394*U - 0.581*V B Y 2.032*U yCbCr<-->rgb Y’ 0…

配置zentaophp

原理&#xff1a; 首先&#xff0c;我们要明白为什么访问localhost就可以访问到我们的apache主页。 解析域名的时候&#xff0c;首先是从本地的hosts文件开始的。 如果查不到&#xff0c;才会去DNS服务器查询。 如果你在这里面写一行&#xff1a;127.0.0.1 www.baidu.com 百…

Android开发——RecyclerView特性以及基本使用方法(二)

0. 前言随着Android的发展&#xff0c;虽然ListView依旧重要&#xff0c;但RecyclerView确实越来越多的被大家使用。但显然并不能说RecyclerView就一定优于ListView&#xff0c;而是应该根据不同的需求选择最合适的进行使用。本篇将介绍我们为什么要使用RecyclerView&#xff…

pycharm中使用scrapy命命

2019独角兽企业重金招聘Python工程师标准>>> 这篇博客写的不错&#xff0c;亲测 https://blog.csdn.net/MAOZEXIJR/article/details/80678133 转载于:https://my.oschina.net/u/2511906/blog/1934993

PrimeTime指南——合理设置约束

完整的STA需要满足以下两点&#xff1a; 完整的设计约束&#xff08;完整并不意味着正确&#xff09;运行所有需要的时序检查可以用以下两条命令来进行完整性的检查&#xff1a; check_timing // 检查是否缺少了约束条件 report_analysis_cove…

Matlab增加块注释

1&#xff09;方法一选中你要加注释的内容&#xff0c;然后选择工具菜单“text|comment”就可以了&#xff0c;如果要把注释变为语句&#xff0c;同样选中要转变的语句&#xff0c;然后用鼠标选择“text|uncomment”就可以了。用键盘的快捷键是"CtrlR".或者选中你要加…