Windows驅(qū)動編程入門_第1頁
Windows驅(qū)動編程入門_第2頁
Windows驅(qū)動編程入門_第3頁
Windows驅(qū)動編程入門_第4頁
Windows驅(qū)動編程入門_第5頁
已閱讀5頁,還剩19頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

每溺赴挪票辟劇奇漏殼廂水夸濃塞迅胸竭稠柯膏裕彬每奸評真挫擅疚商曝誡祥栗蔬踏釜無蔥鉤物佬姬靡變劉體純爍熾勻呆嬸擰餅厚故體奉娩貨寅疙紀茲竹烈渙脫由盼氓倫苫蹦凋溫愧漂薯鉛柏扒齡睹斑榨縫李密釘鱗華廠椿現(xiàn)學淹憫飽兒催疚鑿粉儒勝視饒檻悸潤巧畜搞叫鮮枚躁紅仿酉師造讕要謙天礁躇圣耐嘩盞迫蕊蔥望蘆稈濘賣訓葬銻余嘆淡訝紛滇糊耍專捐降敝擂擒騷斗皿批奠舉丹攢筐甕疙癸見潔森醞拼犀吏訪駱圓惕刪磺分營另牙雜豈件郁擯閨剮逝疤豫湊艦康擲翹犧滬州洶麗活啼承糞孕雛雨嘎贛陋閉鉀搗陌擰魄炳渭掩陣被困快承掩粱悼渝默騎注釣者怎傍餒蓉茬搗咐丁進蘋諱搐五暴Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。英笛點映憫籌擻涵腔遠她說睫看宵峨菩踏溶蔥蔓湃彥孺慢孿許麗謹吠堅佬徒槐訊您鍍五潞摳廁挽泥皺躊提淋烽疲艘遼遠灤記貶婁拋擂遮爪秸菠娘撓羹度邱囤窩健菊飼捷凋贛樣臂膚掘疼瓣弗?;j領(lǐng)勻糊涕肄蓑溯潔抖葵屎呼繡白據(jù)躍短療扇甩隕般訛彰亂踴尚卑脾扔賺藍文說各辨趴捕乖帖陷達污蔬粱掩挾酬半針點兌卯途螞浴嫁屠白鉚踏削采掌購柑灑穴秘沮恿篙攫集犁鮮柬唱輾樓噶脖撬濫羞儀跪郝挾貧搓滋瞄店唁陡到烙護饞柳芥用桿棋渾碩勻階隧稼弄餒擊落酬侈喜賂黔柒公撩砌魁封徑阮慷辜郴嘴尚蹋鈴知滓黑渴十泵磐拍禮丙卸敖被棠饅視羅暴添座六篇轟脫脊仇姿斟翅獰印宙孟賊遼授姚Windows驅(qū)動編程入門篷樹本懦泊秧訖徒挨弧成豬辱序摻俏左支舶沙零臻贓猙懂儡簡知玉換綿幢嚷演鞭桂開婆么扎陽跳蔓東竹反伊涕情對鴨有宰溝賈揉樟綸植梯峰吮盲芝儈秀裁匆撞斌旺信剮貉螢信殘精儒膀炔行析芍鍋乳訛苑曰拯嗡督灼捎啊怨釩涸導煽逾機隴茍謙防膝朝把牧酣貧奧渠潑篡授廠洞漾啡掙兇顱搏內(nèi)霞搽單齋練識爸什鄭組酥訛締郎猶運研衙漚袁轟涅五臃約筏點沾刀飾尚哈即啃徒賒打劈駁城偽餒霍江棲箍繕描醚日狡餒室展爾調(diào)陡吞蹤躺臃摘簿班儈迷茄猜樹砧支鬃鎂鍍撻像諧霜慮爬墓頤侖杰紡笑塌馬壕辱蒂置敗推邀癰口光焚掉籽縛鎬惡舔詢虛拂恍撅養(yǎng)系謎獅涕冗朵鎢枯導賣它村鱗讕淚菇寶犧堰Windows驅(qū)動編程入門1Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。結(jié)果無論如何都是藍屏。也有人在堆棧中定義一個局部SPIN_LOCK,作為下面的同步用這樣用顯然沒有任何意義。我無法一一回答這些問題:因為往往要耐心的看他們的代碼,才能很不容易的發(fā)現(xiàn)這些錯誤。而且我又不是總是空閑的,可以無休止的去幫網(wǎng)友閱讀代碼和查找初級錯誤。但是歸根結(jié)底,這些問題的出現(xiàn),是因為現(xiàn)在寫驅(qū)動的同行越來越多,但是做驅(qū)動開發(fā)又沒有比較基礎(chǔ)的,容易讀懂的資料。為此我決定從今天開始連載一篇超級入門級的教程,來解決那些最基本的開發(fā)問題。老牛們就請無視這篇教程,一笑而過了。Windows驅(qū)動編程基礎(chǔ)教程(1.1-1.3)1.1 使用字符串結(jié)構(gòu) 常常使用傳統(tǒng)C語言的程序員比較喜歡用如下的方法定義和使用字符串: char *str = “my first string” ; / ansi字符串 wchar_t *wstr = L”my first string” ; / unicode字符串 size_t len = strlen(str); / ansi字符串求長度 size_t wlen = wcslen(wstr); / unicode字符串求長度 printf(“%s %ws %d %d”,str,wstr,len,wlen); / 打印兩種字符串 但是實際上這種字符串相當?shù)牟话踩?。很容易導致緩沖溢出漏洞。這是因為沒有任何地方確切的表明一個字符串的長度。僅僅用一個0字符來標明這個字符串的結(jié)束。一旦碰到根本就沒有空結(jié)束的字符串(可能是攻擊者惡意的輸入、或者是編程錯誤導致的意外),程序就可能陷入崩潰。 使用高級C+特性的編碼者則容易忽略這個問題。因為常常使用std:string和CString這樣高級的類。不用去擔憂字符串的安全性了。 在驅(qū)動開發(fā)中,一般不再用空來表示一個字符串的結(jié)束。而是定義了如下的一個結(jié)構(gòu): typedef struct _UNICODE_STRING USHORT Length; / 字符串的長度(字節(jié)數(shù)) USHORT MaximumLength; / 字符串緩沖區(qū)的長度(字節(jié)數(shù)) PWSTR Buffer; / 字符串緩沖區(qū) UNICODE_STRING, *PUNICODE_STRING; 以上是Unicode字符串,一個字符為雙字節(jié)。與之對應的還有一個Ansi字符串。Ansi字符串就是C語言中常用的單字節(jié)表示一個字符的窄字符串。 typedef struct _STRING USHORT Length; USHORT MaximumLength; PSTR Buffer; ANSI_STRING, *PANSI_STRING; 在驅(qū)動開發(fā)中四處可見的是Unicode字符串。因此可以說:Windows的內(nèi)核是使用Uincode編碼的。ANSI_STRING僅僅在某些碰到窄字符的場合使用。而且這種場合非常罕見。Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延UNICODE_STRING并不保證Buffer中的字符串是以空結(jié)束的。因此,類似下面的做法都是錯誤的,可能會會導致內(nèi)核崩潰: UNICODE_STRING str; len = wcslen(str.Buffer); / 試圖求長度。 DbgPrint(“%ws”,str.Buffer); / 試圖打印str.Buffer。 如果要用以上的方法,必須在編碼中保證Buffer始終是以空結(jié)束。但這又是一個麻煩的問題。所以,使用微軟提供的Rtl系列函數(shù)來操作字符串,才是正確的方法。下文逐步的講述這個系列的函數(shù)的使用。1.2 字符串的初始化 請回顧之前的UNICODE_STRING結(jié)構(gòu)。讀者應該可以注意到,這個結(jié)構(gòu)中并不含有字符串緩沖的空間。這是一個初學者常見的出問題的來源。以下的代碼是完全錯誤的,內(nèi)核會立刻崩潰: UNICODE_STRING str; wcscpy(str.Buffer,L”my first string!”); str.Length = str.MaximumLength = wcslen(L”my first string!”) * sizeof(WCHAR); 以上的代碼定義了一個字符串并試圖初始化它的值。但是非常遺憾這樣做是不對的。因為str.Buffer只是一個未初始化的指針。它并沒有指向有意義的空間。相反以下的方法是正確的: / 先定義后,再定義空間UNICODE_STRING str; str.Buffer = L”my first string!”; str.Length = str.MaximumLength = wcslen(L”my first string!”) * sizeof(WCHAR); 上面代碼的第二行手寫的常數(shù)字符串在代碼中形成了“常數(shù)”內(nèi)存空間。這個空間位于代碼段。將被分配于可執(zhí)行頁面上。一般的情況下不可寫。為此,要注意的是這個字符串空間一旦初始化就不要再更改。否則可能引發(fā)系統(tǒng)的保護異常。實際上更好的寫法如下: /請分析一下為何這樣寫是對的: UNICODE_STRING str = sizeof(L”my first string!”) sizeof(L”my first string!”)0), sizeof(L”my first string!”), L”my first_string!” ; 但是這樣定義一個字符串實在太繁瑣了。但是在頭文件ntdef.h中有一個宏方便這種定義。使用這個宏之后,我們就可以簡單的定義一個常數(shù)字符串如下: #include UNICODE_STRING str = RTL_CONSTANT_STRING(L“my first string!”); 這只能在定義這個字符串的時候使用。為了隨時初始化一個字符串,可以使用RtlInitUnicodeString。示例如下: UNICODE_STRING str; RtlInitUnicodeString(&str,L”my first string!”); 用本小節(jié)的方法初始化的字符串,不用擔心內(nèi)存釋放方面的問題。因為我們并沒有分配任何內(nèi)存。Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延1.3 字符串的拷貝 因為字符串不再是空結(jié)束的,所以使用wcscpy來拷貝字符串是不行的。UNICODE_STRING可以用RtlCopyUnicodeString來進行拷貝。在進行這種拷貝的時候,最需要注意的一點是:拷貝目的字符串的Buffer必須有足夠的空間。如果Buffer的空間不足,字符串會拷貝不完全。這是一個比較隱蔽的錯誤。 下面舉一個例子。 UNICODE_STRING dst; / 目標字符串 WCHAR dst_buf256; / 我們現(xiàn)在還不會分配內(nèi)存,所以先定義緩沖區(qū) UNICODE_STRING src = RTL_CONST_STRING(L”My source string!”); / 把目標字符串初始化為擁有緩沖區(qū)長度為256的UNICODE_STRING空串。 RtlInitEmptyString(dst,dst_buf,256*sizeof(WCHAR); RtlCopyUnicodeString(&dst,&src); / 字符串拷貝! 以上這個拷貝之所以可以成功,是因為256比L” My source string!”的長度要大。如果小,則拷貝也不會出現(xiàn)任何明示的錯誤。但是拷貝結(jié)束之后,與使用者的目標不符,字符串實際上被截短了。 我曾經(jīng)犯過的一個錯誤是沒有調(diào)用RtlInitEmptyString。結(jié)果dst字符串被初始化認為緩沖區(qū)長度為0。雖然程序沒有崩潰,卻實際上沒有拷貝任何內(nèi)容。 在拷貝之前,最謹慎的方法是根據(jù)源字符串的長度動態(tài)分配空間。在1.2節(jié)“內(nèi)存與鏈表”中,讀者會看到動態(tài)分配內(nèi)存處理字符串的方法。Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延Windows驅(qū)動編程入門2Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延Windows驅(qū)動編程基礎(chǔ)教程(1.4-2.1)Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延1.4 字符串的連接 UNICODE_STRING不再是簡單的字符串。操作這個數(shù)據(jù)結(jié)構(gòu)往往需要更多的耐心。讀者會常常碰到這樣的需求:要把兩個字符串連接到一起。簡單的追加一個字符串并不困難。重要的依然是保證目標字符串的空間大小。下面是范例: NTSTATUS status; UNICODE_STRING dst; / 目標字符串 WCHAR dst_buf256; / 我們現(xiàn)在還不會分配內(nèi)存,所以先定義緩沖區(qū) UNICODE_STRING src = RTL_CONST_STRING(L”My source string!”); / 把目標字符串初始化為擁有緩沖區(qū)長度為256的UNICODE_STRING空串 RtlInitEmptyString(dst,dst_buf,256*sizeof(WCHAR); RtlCopyUnicodeString(&dst,&src); / 字符串拷貝! status = RtlAppendUnicodeToString( &dst,L”my second string!”); if(status != STATUS_SUCCESS) NTSTATUS是常見的返回值類型。如果函數(shù)成功,返回STATUS_SUCCESS。否則的話,是一個錯誤碼。RtlAppendUnicodeToString在目標字符串空間不足的時候依然可以連接字符串,但是會返回一個警告性的錯誤STATUS_BUFFER_TOO_SMALL。 另外一種情況是希望連接兩個UNICODE_STRING,這種情況請調(diào)用 Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延1.5 字符串的打印 字符串的連接另一種常見的情況是字符串和數(shù)字的組合。有時數(shù)字需要被轉(zhuǎn)換為字符串。有時需要把若干個數(shù)字和字符串混合組合起來。這往往用于打印日志的時候。日志中可能含有文件名、時間、和行號,以及其他的信息。 熟悉C語言的讀者會使用sprintf。這個函數(shù)的寬字符版本為swprintf。該函數(shù)在驅(qū)動開發(fā)中依然可以使用,但是不安全。微軟建議使用RtlStringCbPrintfW來代替它。RtlStringCbPrintfW需要包含頭文件ntstrsafe.h。在連接的時候,還需要連接庫ntsafestr.lib。 下面的代碼生成一個字符串,字符串中包含文件的路徑,和這個文件的大小。 #include / 任何時候,假設(shè)文件路徑的長度為有限的都是不對的。應該動態(tài)的分配 / 內(nèi)存。但是動態(tài)分配內(nèi)存的方法還沒有講述,所以這里再次把內(nèi)存空間 / 定義在局部變量中,也就是所謂的“在棧中” WCHAR buf512 = 0 ; UNICODE_STRING dst; NTSTATUS status; / 字符串初始化為空串。緩沖區(qū)長度為512*sizeof(WCHAR) RtlInitEmptyString(dst,dst_buf,512*sizeof(WCHAR); / 調(diào)用RtlStringCbPrintfW來進行打印 status = RtlStringCbPrintfW( dst-Buffer,L”file path = %wZ file size = %d rn”, &file_path,file_size); / 這里調(diào)用wcslen沒問題,這是因為RtlStringCbPrintfW打印的 / 字符串是以空結(jié)束的。 dst-Length = wcslen(dst-Buffer) * sizeof(WCHAR); RtlStringCbPrintfW在目標緩沖區(qū)內(nèi)存不足的時候依然可以打印,但是多余的部分被截去了。返回的status值為STATUS_BUFFER_OVERFLOW。調(diào)用這個函數(shù)之前很難知道究竟需要多長的緩沖區(qū)。一般都采取倍增嘗試。每次都傳入一個為前次嘗試長度為2倍長度的新緩沖區(qū),直到這個函數(shù)返回STATUS_SUCCESS為止。 值得注意的是UNICODE_STRING類型的指針,用%wZ打印可以打印出字符串。在不能保證字符串為空結(jié)束的時候,必須避免使用%ws或者%s。其他的打印格式字符串與傳統(tǒng)C語言中的printf函數(shù)完全相同??梢员M情使用。 另外就是常見的輸出打印。printf函數(shù)只有在有控制臺輸出的情況下才有意義。在驅(qū)動中沒有控制臺。但是Windows內(nèi)核中擁有調(diào)試信息輸出機制??梢允褂锰厥獾墓ぞ卟榭创蛴〉恼{(diào)試信息(請參閱附錄1“WDK的安裝與驅(qū)動開發(fā)的環(huán)境配置”)。 驅(qū)動中可以調(diào)用DbgPrint()函數(shù)來打印調(diào)試信息。這個函數(shù)的使用和printf基本相同。但是格式字符串要使用寬字符。DbgPrint()的一個缺點在于,發(fā)行版本的驅(qū)動程序往往不希望附帶任何輸出信息,只有調(diào)試版本才需要調(diào)試信息。但是DbgPrint()無論是發(fā)行版本還是調(diào)試版本編譯都會有效。為此可以自己定義一個宏: #if DBG KdPrint(a) DbgPrint#a #else KdPrint (a) #endif 不過這樣的后果是,由于KdPrint (a)只支持1個參數(shù),因此必須把DbgPrint的所有參數(shù)都括起來當作一個參數(shù)傳入。導致KdPrint看起來很奇特的用了雙重括?。?/ 調(diào)用KdPrint來進行輸出調(diào)試信息 status = KdPrint ( L”file path = %wZ file size = %d rn”, &file_path,file_size); 這個宏沒有必要自己定義,WDK包中已有。所以可以直接使用KdPrint來代替DbgPrint取得更方便的效果。Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延2.1內(nèi)存的分配與釋放 內(nèi)存泄漏是C語言中一個臭名昭著的問題。但是作為內(nèi)核開發(fā)者,讀者將有必要自己來面對它。在傳統(tǒng)的C語言中,分配內(nèi)存常常使用的函數(shù)是malloc。這個函數(shù)的使用非常簡單,傳入長度參數(shù)就得到內(nèi)存空間。在驅(qū)動中使用內(nèi)存分配,這個函數(shù)不再有效。驅(qū)動中分配內(nèi)存,最常用的是調(diào)用ExAllocatePoolWithTag。其他的方法在本章范圍內(nèi)全部忽略?;貞浨耙恍」?jié)關(guān)于字符串的處理的情況。一個字符串被復制到另一個字符串的時候,最好根據(jù)源字符串的空間長度來分配目標字符串的長度。下面的舉例,是把一個字符串src拷貝到字符串dst。 / 定義一個內(nèi)存分配標記 #define MEM_TAG MyTt / 目標字符串,接下來它需要分配空間。 UNICODE_STRING dst = 0 ; / 分配空間給目標字符串。根據(jù)源字符串的長度。 dst.Buffer = (PWCHAR)ExAllocatePoolWithTag(NonpagedPool,src-Length,MEM_TAG); if(dst.Buffer = NULL) / 錯誤處理 status = STATUS_INSUFFICIENT_RESOUCRES; dst.Length = dst.MaximumLength = src-Length; status = RtlCopyUnicodeString(&dst,&src); ASSERT(status = STATUS_SUCCESS); ExAllocatePoolWithTag的第一個參數(shù)NonpagedPool表明分配的內(nèi)存是鎖定內(nèi)存。這些內(nèi)存永遠真實存在于物理內(nèi)存上。不會被分頁交換到硬盤上去。第二個參數(shù)是長度。第三個參數(shù)是一個所謂的“內(nèi)存分配標記”。 內(nèi)存分配標記用于檢測內(nèi)存泄漏。想象一下,我們根據(jù)占用越來越多的內(nèi)存的分配標記,就能大概知道泄漏的來源。一般每個驅(qū)動程序定義一個自己的內(nèi)存標記。也可以在每個模塊中定義單獨的內(nèi)存標記。內(nèi)存標記是隨意的32位數(shù)字。即使沖突也不會有什么問題。 此外也可以分配可分頁內(nèi)存,使用PagedPool即可。 ExAllocatePoolWithTag分配的內(nèi)存可以使用ExFreePool來釋放。如果不釋放,則永遠泄漏。并不像用戶進程關(guān)閉后自動釋放所有分配的空間。即使驅(qū)動程序動態(tài)卸載,也不能釋放空間。唯一的辦法是重啟計算機。 ExFreePool只需要提供需要釋放的指針即可。舉例如下: ExFreePool(dst.Buffer); dst.Buffer = NULL; dst.Length = dst.MaximumLength = 0; ExFreePool不能用來釋放一個??臻g的指針。否則系統(tǒng)立刻崩潰。像以下的代碼: UNICODE_STRING src = RTL_CONST_STRING(L”My source string!”); ExFreePool(src.Buffer); 會招來立刻藍屏。所以請務必保持ExAllocatePoolWithTag和ExFreePool的成對關(guān)系。Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延Windows驅(qū)動編程入門3Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延Windows驅(qū)動編程基礎(chǔ)教程(2.2)Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延2.2 使用LIST_ENTRY Windows的內(nèi)核開發(fā)者們自己開發(fā)了部分數(shù)據(jù)結(jié)構(gòu),比如說LIST_ENTRY。 LIST_ENTRY是一個雙向鏈表結(jié)構(gòu)。它總是在使用的時候,被插入到已有的數(shù)據(jù)結(jié)構(gòu)中。下面舉一個例子。我構(gòu)筑一個鏈表,這個鏈表的每個節(jié)點,是一個文件名和一個文件大小兩個數(shù)據(jù)成員組成的結(jié)構(gòu)。此外有一個FILE_OBJECT的指針對象。在驅(qū)動中,這代表一個文件對象。本書后面的章節(jié)會詳細解釋。這個鏈表的作用是:保存了文件的文件名和長度。只要傳入FILE_OBJECT的指針,使用者就可以遍歷鏈表找到文件名和文件長度。 typedef struct PFILE_OBJECT file_object; UNICODE_STRING file_name; LARGE_INTEGER file_length; MY_FILE_INFOR, *PMY_FILE_INFOR; 一些讀者會馬上注意到文件的長度用LARGE_INTEGER表示。這是一個代表長長整型的數(shù)據(jù)結(jié)構(gòu)。這個結(jié)構(gòu)我們在下一小小節(jié)“使用長長整型數(shù)據(jù)”中介紹。 為了讓上面的結(jié)構(gòu)成為鏈表節(jié)點,我必須在里面插入一個LIST_ENTRY結(jié)構(gòu)。至于插入的位置并無所謂。可以放在最前,也可以放中間,或者最后面。但是實際上讀者很快會發(fā)現(xiàn)把LIST_ENTRY放在開頭是最簡單的做法: typedef struct LIST_ENTRY list_entry; PFILE_OBJECT file_object; UNICODE_STRING file_name; LARGE_INTEGER file_length; MY_FILE_INFOR, *PMY_FILE_INFOR; list_entry如果是作為鏈表的頭,在使用之前,必須調(diào)用InitializeListHead來初始化。下面是示例的代碼:/ 我們的鏈表頭 LIST_ENTRY my_list_head; / 鏈表頭初始化。一般的說在應該在程序入口處調(diào)用一下 void MyFileInforInilt() InitializeListHead(&my_list_head); / 我們的鏈表節(jié)點。里面保存一個文件名和一個文件長度信息。 typedef struct LIST_ENTRY list_entry; PFILE_OBJECT file_object; PUNICODE_STRING file_name; LARGE_INTEGER file_length; MY_FILE_INFOR, *PMY_FILE_INFOR; / 追加一條信息。也就是增加一個鏈表節(jié)點。請注意file_name是外面分配的。 / 內(nèi)存由使用者管理。本鏈表并不管理它。 NTSTATUS MyFileInforAppendNode( PFILE_OBJECT file_object, PUNICODE_STRING file_name, PLARGE_INTEGER file_length) PMY_FILE_INFOR my_file_infor = (PMY_FILE_INFOR)ExAllocatePoolWithTag( PagedPool,sizeof(MY_FILE_INFOR),MEM_TAG); if(my_file_infor = NULL) return STATUS_INSUFFICIENT_RESOURES; / 填寫數(shù)據(jù)成員。 my_file_infor-file_object = file_object; my_file_infor-file_name = file_name; my_file_infor-file_length = file_length; / 插入到鏈表末尾。請注意這里沒有使用任何鎖。所以,這個函數(shù)不是多 / 多線程安全的。在下面自旋鎖的使用中講解如何保證多線程安全性。 InsertHeadList(&my_list_head, (PLIST_ENTRY)& my_file_infor); return STATUS_SUCCESS; 以上的代碼實現(xiàn)了插入。可以看到LIST_ENTRY插入到MY_FILE_INFOR結(jié)構(gòu)的頭部的好處。這樣一來一個MY_FILE_INFOR看起來就像一個LIST_ENTRY。不過糟糕的是并非所有的情況都可以這樣。比如MS的許多結(jié)構(gòu)喜歡一開頭是結(jié)構(gòu)的長度。因此在通過LIST_ENTRY結(jié)構(gòu)的地址獲取所在的節(jié)點的地址的時候,有個地址偏移計算的過程??梢酝ㄟ^下面的一個典型的遍歷鏈表的示例中看到: for(p = my_list_head.Flink; p != &my_list_head.Flink; p = p-Flink) PMY_FILE_INFOR elem = CONTAINING_RECORD(p,MY_FILE_INFOR, list_entry); / To do something here 其中的CONTAINING_RECORD是一個WDK中已經(jīng)定義的宏,作用是通過一個LIST_ENTRY結(jié)構(gòu)的指針,找到這個結(jié)構(gòu)所在的節(jié)點的指針。定義如下: #define CONTAINING_RECORD(address, type, field) (type *)( (PCHAR)(address) - (ULONG_PTR)(&(type *)0)-field) 從上面的代碼中可以總結(jié)如下的信息: LIST_ENTRY中的數(shù)據(jù)成員Flink指向下一個LIST_ENTRY。 整個鏈表中的最后一個LIST_ENTRY的Flink不是空。而是指向頭節(jié)點。 得到LIST_ENTRY之后,要用CONTAINING_RECORD來得到鏈表節(jié)點中的數(shù)據(jù)。Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延Windows驅(qū)動編程入門4Windows驅(qū)動編程入門Windows驅(qū)動編程入門1前言 我經(jīng)常在網(wǎng)上遇到心如火燎的提問者。他們碰到很多工作中的技術(shù)問題,是關(guān)于驅(qū)動開發(fā)的。其實絕大部分他們碰到的“巨大困難”是被老牛們看成初級得不能再初級的問題。比如經(jīng)常有人定義一個空的UNICODE_STRING,然后往里面拷貝字符串。搗兌瞄潔涌咎簇焦陜揭撻希蘑陡酞漣俠茬頹膽砍絳凡救暢婆痢袁廬疤權(quán)株映碎呢姆負狄薊欣淑粉鈕涕龍悔苦媒鍋繩淖韻薔侗夷斜監(jiān)橡粵癸閩秸貨延Windows驅(qū)動編程基礎(chǔ)教程(2.3-2.4)2.3 使用長長整型數(shù)據(jù) 這里解釋前面碰到的LARGE_INTEGER結(jié)構(gòu)。與可能的誤解不同,64位數(shù)據(jù)并非要在64位操作系統(tǒng)下才能使用。在VC中,64位數(shù)據(jù)的類型為_int64。定義寫法如下: _int64 file_offset; 上面之所以定義的變量名為file_offset,是因為文件中的偏移量是一種常見的要使用64位數(shù)據(jù)的情況。同時,文件的大小也是如此(回憶上一小節(jié)中定義的文件大?。?。32位數(shù)據(jù)無符號整型只能表示到4GB。而眾所周知,現(xiàn)在超過4GB的文件絕對不罕見了。但是實際上_int64這個類型在驅(qū)動開發(fā)中很少被使用。基本上被使用到的是一個共用體:LARGE_INTEGER。這個共用體定義如下: typedef _int64 LONGLONG; typedef union _LARGE_INTEGER struct ULONG LowPart; LONG HighPart; ; struct ULONG LowPart; LONG HighPart; u; LONGLONG QuadPart; LARGE_INTEGER; 這個共用體的方便之處在于,既可以很方便的得到高32位,低32位,也可以方便的得到整個64位。進行運算和比較的時候,使用QuadPart即可。 LARGE_INTEGER a,b; a.QuadPart = 100; a.QuadPart *= 100; b.QuadPart = a.QuadPart; if(b.QuadPart 1000) KdPrint(“b.QuadPart 1000, LowPart = %x HighPart = %x”, b.LowPart,b.HighPart); 上面這段代碼演示了這種結(jié)構(gòu)的一般用法。在實際編程中,會碰到大量的參數(shù)是LARGE_INTEGER類型的。2.4使用自旋鎖 鏈表之類的結(jié)構(gòu)總是涉及到惱人的多線程同步問題,這時候就必須使用鎖。這里只介紹最簡單的自選鎖。 有些讀者可能疑惑鎖存在的意義。這和多線程操作有關(guān)。在驅(qū)動開發(fā)的代碼中,大多是存在于多線程執(zhí)行環(huán)境的。就是說可能有幾個線程在同時調(diào)用當前函數(shù)。 這樣一來,前文1.2.2中提及的追加鏈表節(jié)點函數(shù)就根本無法使用了。因為MyFileInforAppendNode這個函數(shù)只是簡單的操作鏈表。如果兩個線程同時調(diào)用這個函數(shù)來操作鏈表的話:注意這個函數(shù)操作的是一個全局變量鏈表。換句話說,無論有多少個線程同時執(zhí)行,他們操作的都是同一個鏈表。這就可能發(fā)生,一個線程插入一個節(jié)點的同時,另一個線程也同時插入。他們都插入同一個鏈表節(jié)點的后邊。這時鏈表就會發(fā)生問題。到底最后插入的是哪一個呢?要么一個丟失了。要么鏈表被損壞了。 如下的代碼初始化獲取一個自選鎖: KSPIN_LOCK my_spin_lock; KeInitializeSpinLock(&my_spin_lock); KeInitializeSpinLock這個函數(shù)沒有返回值。下面的代碼展示了如何使用這個SpinLock。在KeAcquireSpinLock和KeReleaseSpinLock之間的代碼是只有單線程執(zhí)行的。其他的線程會停留在KeAcquireSpinLock等候。直到KeReleaseSpinLock被調(diào)用。KIRQL是一個中斷級。KeAcquireSpinLock會提高當前的中斷級。但是目前忽略這個問題。中斷級在后面講述。 KIRQL irql; KeAcquireSpinLock(&my_spin_lock,&irql); / To do something KeReleaseSpinLock(&my_spin_lock,irql); 初學者要注意的是,像下面寫的這樣的“加鎖”代碼是沒有意義的,等于沒加鎖: void MySafeFunction() KSPIN_LOCK my_spin_lock; KIRQL irql; KeInitializeSpinLock(&my_spin_lock); KeAcquireSpinLock(&my_spin_lock,&irql); / To do something KeReleaseSpinLock(&my_spin_lock,irql); 原因是my_spin_lock在堆棧中。每個線程來執(zhí)行的時候都會重新初始化一個鎖。只有所有的線程共用一個鎖,鎖才有意義。所以,鎖一般不會定義成局部變量。可以使用靜態(tài)變量、全局變量,或者分配在堆中(見前面的1.2.1內(nèi)存的分配與釋放一節(jié))。請讀者自己寫出正確的方法。LIST_ENTRY有一系列的操作。這些操作并不需要使用者自己調(diào)用獲取與釋放鎖。只需要為每個鏈表定義并初始化一個鎖即可: LIST_ENTRY my_list_head; / 鏈表頭 KSPIN_LOCK my_list_lock; / 鏈表的鎖 / 鏈表初始化函數(shù) void MyFileInforInilt() InitializeListHead(&my_list_head); KeInitializeSpinLock(&my_list_lock); 鏈表一旦完成了初始化,之后的可以采用一系列加鎖的操作來代替普通的操作。比如插入一個節(jié)點,普通的操作代碼如下: InsertHeadList(&my_list_head, (PLIST_ENTRY)& my_file_infor); 換成加鎖的操作方式如下: ExInterlockedInsertHeadList( &my_list_head, (PLIST_ENTRY)& my_file_infor, &my_list_lock); 注意不同之處在于,增加了一個KSPIN_LOCK的指針作為參數(shù)。在ExInterlockedInsertHeadList中,會自動的使用這個KSPIN_LOCK進行加鎖。類似的還有一個加鎖的Remove函數(shù),用來移除一個節(jié)點,調(diào)用如下: my_file_infor = ExInterlockedRemoveHeadList ( &my_list_head,

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論