2009 03 18 11 41 [winddk] 如何寫 NT native application (5)

目前 Native Application 就只差沒辦法讓使用者輸入東西,

現在就來看看如何取得鍵盤的按鍵。

一開始,也是都沒有頭緒。

在網路上 google 的結果,

也都只是說個大概,

後來才看到一些原始碼,

才比較清楚整個作法。

需要自己去開啟 /device/KeyboardClass0 這個 device ,

但是因為在電腦上可能有不止一組鍵盤,

理論上要開啟所有的鍵盤 device,

在網路論壇上似乎說明著 USB 鍵盤在那個時間點並不一定已經初始化了,

也意謂著不一定可以使用 USB 鍵盤,

而我印象中 USB 鍵盤的工作並不是在 kernel mode 處理,

所以即使一開始 USB 鍵盤就插著,也不一定就可以取得。

而要取得現在鍵盤按鍵,

是直接去讀取 device ,

它會傳回一個 KEYBOARD_INPUT_DATA 的資料結構。

因為它是直接從鍵盤讀取出來的,

所以裡面所記載的按鍵都是 Scan Code ,

並非在 windows 中常見的 Virtual Key 。

需要做一些轉換,

或者像目前一樣,

只是讀取鍵盤的按鍵,

並沒有真的要輸入什麼字串。

就比較沒有關係。

如果你的需要轉換按鍵,

因為絕大多數鍵盤都是 101鍵的鍵盤,

你可以參考微軟這篇文章

而更詳細的資料則是這篇 Keyboard Scan Code Specification

 

但是因為 keyboard 這個 device 會 pending 你的讀取,

所以會使用一個 event 去得到讀取是否已經完成。

一旦發現目前讀取是 pending 中,

就會去等待 envent 是否已經有被更動。

直到這個讀取完成為止。

因此寫法稍有點不同,

目前這段程式碼看起來是沒有問題。

nativeKey.c 原始碼如下:

#include "ntddk.h"
#include "ntddkbd.h"

void *malloc(unsigned long ulSize);
void free(void *pMem);
NTSTATUS NtClose(HANDLE Handle);
NTSTATUS NTAPI NtCreateEvent(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
EVENT_TYPE, BOOLEAN);

struct WIN32N_KEYBOARD_INPUT
{
HANDLE KeyboardHandle;
HANDLE KeyboardEvent;
KEYBOARD_INPUT_DATA aKeyboardInputData;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER offset;
int status;
};




struct WIN32N_KEYBOARD_INPUT *
newWin32nKeyBoardInput()
{
HANDLE KeyboardHandle;
UNICODE_STRING KeyboarName;
OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
IO_STATUS_BLOCK Iosb;
NTSTATUS status;

struct WIN32N_KEYBOARD_INPUT * pWin32nKeyboardInput = NULL;
pWin32nKeyboardInput = (struct WIN32N_KEYBOARD_INPUT *)malloc(sizeof(struct WIN32N_KEYBOARD_INPUT));

if (pWin32nKeyboardInput == NULL) {
DbgPrint("malloc failed !\n");
return NULL;
}

memset(pWin32nKeyboardInput,0,sizeof(struct WIN32N_KEYBOARD_INPUT));

RtlInitUnicodeString(&KeyboarName, L"\\Device\\KeyboardClass0");
InitializeObjectAttributes(&ObjectAttributes, &KeyboarName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ZwCreateFile(&(pWin32nKeyboardInput->KeyboardHandle), // returned file handle
(GENERIC_READ|SYNCHRONIZE|FILE_READ_ATTRIBUTES), // desired access
&ObjectAttributes, // ptr to object attributes
&Iosb, // ptr to I/O status block
0, // allocation size
FILE_ATTRIBUTE_NORMAL, // file attributes
0, // share access
FILE_OPEN, // create disposition
1, // create options
NULL, // ptr to extended attributes
0); // length of ea buffer

if (!NT_SUCCESS(status)) {
free(pWin32nKeyboardInput);
DbgPrint("Can't open \\Device\\KeyboardClass0 %08x !\n",status);
return NULL;
}

InitializeObjectAttributes(&ObjectAttributes, // ptr to structure
NULL, // ptr to file spec
0, // attributes
NULL, // root directory handle
NULL); // ptr to security descriptor
status = NtCreateEvent(&(pWin32nKeyboardInput->KeyboardEvent),
EVENT_ALL_ACCESS,
&ObjectAttributes,
SynchronizationEvent, FALSE);

if (!NT_SUCCESS(status)) {
DbgPrint("Can't init event for keyboard ! %08x\n",status);
ZwClose(pWin32nKeyboardInput->KeyboardHandle);
free(pWin32nKeyboardInput);
return NULL;
}


do { // clear keyboard
pWin32nKeyboardInput->status = ZwReadFile(pWin32nKeyboardInput->KeyboardHandle,
pWin32nKeyboardInput->KeyboardEvent,
NULL,
NULL,
&pWin32nKeyboardInput->Iosb,
(PVOID) &(pWin32nKeyboardInput->aKeyboardInputData),
sizeof(KEYBOARD_INPUT_DATA),
&pWin32nKeyboardInput->offset,
NULL);
} while (pWin32nKeyboardInput->status == STATUS_SUCCESS);

return pWin32nKeyboardInput;
}

void delWin32nKeyBoardInput(struct WIN32N_KEYBOARD_INPUT ** ppWin32nKeyboardInput)
{
if (ppWin32nKeyboardInput == NULL || *ppWin32nKeyboardInput == NULL) {
return;
}
if ((*ppWin32nKeyboardInput)->KeyboardEvent != NULL) {
NtClose((*ppWin32nKeyboardInput)->KeyboardEvent);
(*ppWin32nKeyboardInput)->KeyboardEvent = NULL;
}
if ((*ppWin32nKeyboardInput)->KeyboardHandle != NULL) {
ZwClose((*ppWin32nKeyboardInput)->KeyboardHandle);
(*ppWin32nKeyboardInput)->KeyboardHandle = NULL;
}
free(*ppWin32nKeyboardInput);
(*ppWin32nKeyboardInput) = NULL;
}

NTSTATUS NTAPI NtWaitForSingleObject(HANDLE Handle, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
NTSTATUS NTAPI NtCancelIoFile(IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock);

KEYBOARD_INPUT_DATA *getWin32nKeyBoardInput(struct WIN32N_KEYBOARD_INPUT *pWin32nKeyboardInput,
PLARGE_INTEGER pTimeout)
{


if (pWin32nKeyboardInput->status != STATUS_PENDING) {
memset(&(pWin32nKeyboardInput->aKeyboardInputData), 0, sizeof(KEYBOARD_INPUT_DATA));
pWin32nKeyboardInput->status = ZwReadFile(pWin32nKeyboardInput->KeyboardHandle,
pWin32nKeyboardInput->KeyboardEvent,
NULL,
NULL,
&pWin32nKeyboardInput->Iosb,
(PVOID) &(pWin32nKeyboardInput->aKeyboardInputData),
sizeof(KEYBOARD_INPUT_DATA),
&pWin32nKeyboardInput->offset,
NULL);
} else {
pWin32nKeyboardInput->status = STATUS_PENDING;
}
if (pWin32nKeyboardInput->status == STATUS_PENDING) {
pWin32nKeyboardInput->status = NtWaitForSingleObject(pWin32nKeyboardInput->KeyboardEvent, 1, pTimeout);
DbgPrint("wait status : %08x\n",pWin32nKeyboardInput->status);
if (pWin32nKeyboardInput->status != STATUS_SUCCESS) {
pWin32nKeyboardInput->status = STATUS_PENDING;
//NtCancelIoFile(pWin32nKeyboardInput->KeyboardHandle, &pWin32nKeyboardInput->Iosb);
return NULL;
}
} else {
DbgPrint("status : %08x\n",pWin32nKeyboardInput->status);
}
if (pWin32nKeyboardInput->status != STATUS_SUCCESS) {
return NULL;
}
return &(pWin32nKeyboardInput->aKeyboardInputData);
}




native.c 原始碼如下:

#include "ntifs.h"
#include "ntddk.h"
#include "ntddkbd.h"
#include "stdio.h"

NTSTATUS NTAPI NtDisplayString(PUNICODE_STRING String);
NTSTATUS NTAPI NtTerminateProcess(HANDLE ProcessHandle, LONG ExitStatus);

struct WIN32N_KEYBOARD_INPUT;
struct WIN32N_KEYBOARD_INPUT *newWin32nKeyBoardInput();
void delWin32nKeyBoardInput(struct WIN32N_KEYBOARD_INPUT ** ppWin32nKeyboardInput);
KEYBOARD_INPUT_DATA *getWin32nKeyBoardInput(struct WIN32N_KEYBOARD_INPUT *pWin32nKeyboardInput,
PLARGE_INTEGER pTimeout);

HANDLE g_hHeap = NULL;

HANDLE InitHeapMemory(void)
{
g_hHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0x100000, 0x1000, NULL,NULL);

return g_hHeap;
}

BOOLEAN DeinitHeapMemory()
{
PVOID pRet;

pRet = RtlDestroyHeap(g_hHeap);
if (pRet == NULL) {
g_hHeap = NULL;
return TRUE;
}
return FALSE;
}

void free(void *pMem)
{
RtlFreeHeap(g_hHeap, 0, pMem);
}


void *malloc(unsigned long ulSize)
{
return RtlAllocateHeap(g_hHeap, 0, ulSize);
}

typedef struct _RTL_USER_PROCESS_PARAMETERS {
UCHAR Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS,
*PRTL_USER_PROCESS_PARAMETERS;

typedef struct _PEB_LDR_DATA {
UCHAR Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef PVOID PPS_POST_PROCESS_INIT_ROUTINE;

struct _PEB {
UCHAR Reserved1[2];
UCHAR BeingDebugged;
UCHAR Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
UCHAR Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
UCHAR Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
};

void print(PWCHAR pwmsg)
{
UNICODE_STRING msg;
RtlInitUnicodeString(&msg,pwmsg);
NtDisplayString(&msg);
}

int parseArgs(WCHAR * pArgs, int nArgsLen, int *pArgc, WCHAR *** pArgv)
{
int argc = 0;
int i;
int nArgv = 1;

*pArgc = 0;
*pArgv = NULL;
if (pArgs == NULL) {
return 0;
}
(*pArgv) = (WCHAR **) malloc(sizeof(WCHAR *) * nArgv);
if (pArgv == NULL) {
return -1;
}
memset((*pArgv), 0, sizeof(WCHAR *) * nArgv);

for (i = 0; i < nArgsLen; i++) {
while (pArgs[i] == L' ') {
if ((i + 1) >= nArgsLen) {
break;
}
i++;
}
if (i >= nArgsLen)
break;

(*pArgv)[argc] = (pArgs + i);
argc++;
if (argc >= nArgv) {
WCHAR **pTmpArgv;
pTmpArgv = (WCHAR **) malloc(sizeof(WCHAR *) * (nArgv + 16));
if (pTmpArgv == NULL) {
free(*pArgv);
*pArgv = NULL;
return -1;
}
memset(pTmpArgv, 0, sizeof(WCHAR *) * (nArgv + 16));
memcpy(pTmpArgv, (*pArgv), argc * sizeof(WCHAR *));
free(*pArgv);
*pArgv = pTmpArgv;
nArgv = nArgv + 16;
}

if (pArgs[i] == L'\"') {
(*pArgv)[argc - 1] = (pArgs + i + 1);
do {
i++;
if ((i + 1) < nArgsLen && pArgs[i] == L'\\') {
i += 2;
}
} while (i < nArgsLen && pArgs[i] != L'\"');
if (i < nArgsLen && pArgs[i] == L'\"') {
pArgs[i] = 0;
}
} else {
do {
i++;
} while (i < nArgsLen && pArgs[i] != L' ');
if (i < nArgsLen && pArgs[i] == L' ') {
pArgs[i] = 0;
}
}
}
*pArgc = argc;
return 0;
}

int UserMain(int argc, WCHAR * argv[])
{
int i;
WCHAR buf[64];

struct WIN32N_KEYBOARD_INPUT * pWin32nKeyboardInput = NULL;
KEYBOARD_INPUT_DATA *pKeyboardInputData;
LARGE_INTEGER timeout;

pWin32nKeyboardInput = newWin32nKeyBoardInput();
if (pWin32nKeyboardInput == NULL) {
DbgPrint("openKeyBoard failed\n");
}

for (i = 0; i < argc; i++) {
_snwprintf((WCHAR *)buf, 64, L"%d : ",i);
print(buf);
print(argv[i]);
print(L"\n");
}


DbgPrint("Please Press 'ESC' to exit...\n");
print(L"Please Press 'ESC' to exit...\n");
while (pWin32nKeyboardInput) {
timeout.QuadPart=-1; //-10000000;
pKeyboardInputData = getWin32nKeyBoardInput(pWin32nKeyboardInput,&timeout);

if (pKeyboardInputData) {
DbgPrint("MakeCode: %0x\t", pKeyboardInputData->MakeCode);
DbgPrint("Flags: %0x\n", pKeyboardInputData->Flags);
if (pKeyboardInputData->MakeCode == 1) {
DbgPrint("Do you want to exit ? (Y/N) ");
print(L"Do you want to exit ? (Y/N) ");
do {
pKeyboardInputData = getWin32nKeyBoardInput(pWin32nKeyboardInput,NULL);
if (pKeyboardInputData == NULL) {
continue;
}
} while (pKeyboardInputData->MakeCode != 0x15 && pKeyboardInputData->MakeCode != 0x31);
if (pKeyboardInputData->MakeCode == 0x15 ) {
DbgPrint("Y\n");
print(L"Y\n");
break;
}
DbgPrint("N\n");
print(L"N\n");
}
}
}
DbgPrint("Bye...\n");
print(L"Bye..\n");

delWin32nKeyBoardInput(&pWin32nKeyboardInput);

return i;
}

void NtProcessStartup(PPEB pPEB)
{
int rc = 0;
WCHAR *pArgs = NULL;
int argc;
WCHAR **argv;

if (InitHeapMemory() == NULL) {
DbgPrint("%s:%d InitHeapMemory failed\n",__FILE__,__LINE__);
}

print(L"ImagePathName:");
NtDisplayString(&pPEB->ProcessParameters->ImagePathName);
print(L"\n");

print(L"CommandLine:");
NtDisplayString(&pPEB->ProcessParameters->CommandLine);
print(L"\n\n");

pArgs = (WCHAR *) malloc(pPEB->ProcessParameters->CommandLine.Length + 2);
if (pArgs != NULL) {
memset(pArgs, 0, pPEB->ProcessParameters->CommandLine.Length + 2);
memcpy(pArgs,
pPEB->ProcessParameters->CommandLine.Buffer,
pPEB->ProcessParameters->CommandLine.Length);
}

rc = parseArgs(pArgs,
pPEB->ProcessParameters->CommandLine.Length / 2,
&argc,
&argv);

if (rc != 0) {
print(L"Parsing Args failed.\n");
}

rc = UserMain(argc, argv);
if (argv) {
free(argv);
}
if (pArgs) {
free(pArgs);
}
DeinitHeapMemory();
NtTerminateProcess( NtCurrentProcess(), rc );
}


 

只需要把這兩個檔案一起編譯,

編譯完後,

放進 c:\windows\system32\ 下,

重開機後,

你就可以看到以下畫面,

按下『ESC』鍵,

再按下『Y』,

就可以真正離開這個程式~~~