鍵盤綁定

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.pswp.cn/news/253674.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253674.shtml
英文地址,請注明出處:http://en.pswp.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".或者選中你要加…