NT架構
NT 就是new techonology 的英文單詞縮寫,是微軟1993年推出操作系統的重大升級,如內存管理,安全機制,多任務,多線程支持。在此之前操作系統都是基于MS-DOS上面的圖形化界面,只有有限的內存管理和多任務處理能力。 后續微軟在NT架構上陸續推出了服務器操作系統Windows server 2003 -- 2025系列,桌面操作系統windows xp --windows 11.
主要的動態庫 kernel32, user32,gdi32, ntdll
早期,NT架構從內核到ntdll.dll,kernel32, user32,gdi32都是純32位實現,后續都進行優化和改造64位的kernel32, user32,gdi32支持兼容運行32位程序。
kernel32
kernel32.dll 是Windows重要的動態鏈接庫,主要用于作用如下:
- 進程,線程管理。提供了線程和進程的創建,銷毀,控制的相關函數。
- 內存管理。包括內存分配和釋放等API,比virtualAlloc, virtualFree等。
- 文件和設備IO通信相關API,比如文件創建,關閉讀寫等。
- 同步機制,提供各種同步對象,信號量等用于線程交互通信。Mutex, CriticalSection,event等。
- 時間和日期函數的管理。
user32
- user32.dll 是Windows提供用于windows 消息管理,界面管理相關功能,作用如下。
- 處理和分發窗口消息相關API,使得程序能夠響應用戶的的操作。比如GetMessage等。
- 處理鼠標和鍵盤事件的方法,比如setCursorPos 設置鼠標位置。
- 窗口對象的創建和銷毀,隱藏顯示等。
- 菜單對話框相關的處理。
gdi32
- gdi32是windows提供的圖像設備接口動態鏈接庫。用于圖形管理和繪制相關
- 圖形對象的管理,通過CreatePen, CreateBrush等。
- 圖形的繪制,比如moveToEx, LineTo等。
- 位圖的處理,圖標的加載,釋放,以及位圖的輸出等。
- 提供文本展示相關處理
ntdll
ntdll 是應用層面和Windows內核交互的重要動態鏈接庫, 上面提到的 kernel32,user32,gid32都是通過ntdll 與內核交互的, 我這里專門測試了下, 下面是OllyDbg查到的數據, 可以看到我們代碼調用CreateFileW, 是走了kernel32庫, 然后庫里面右去調用相關ntdll 的方法。(這里說明下,我也是學著使用OD,所以后面ntdll 的入口并不確定找到了對應的,但是可以看到kernel32 里面的確有相關ntdll 的調用,說明我們的理解是沒有錯誤的。)
字符集
學習這一集的時候, 我們先問自己一個問題, 什么是字符集,都有哪些字符集? 為什么要有字符集,字符集作用?帶著上面的一些列問題,我們開始今天的字符集之旅~~~~~~~~~
什么是字符集,都有哪些字符集??
字符集對照表
字符集其實就是一個預先定義好的表格, 里面將每個文字都羅列出來,并且為每個文字指定一個獨一無二的數字來代替。
以淬淵閣中的“淬”舉例:
ASCLL:
這個編碼無法表示中文,所以他的字符集找不到
Unicode:?
十六進制:?0x6DEC
十進制:28140
二進制:0110 1101 1110 1100
字母A舉例:
ASCLL:
十六進制:?0x41
十進制:65
二進制:01000001
Unicode:?
十六進制:?0x41
十進制:65
二進制:01000001
所以記住字符集: 就是一個表格,一個對照字符和數值對應的表格。除了上面說的 ASCLL ,Unicode外,還有GBK相關中國發布的編碼規則。
ASCLL和Unicode前世今生
從上面舉例大家可能也發現了,字符A在ASCLL 和Unicode 中都是一樣, 這個是因為最初美國指定了ASCLL字符集,英文世界里面ASCLL的編碼就足夠表示相關字符和文字。但是隨這個計算機普及,世界各地都希望都能使用計算機,中文,阿拉伯,柬埔寨....,于是后來人們就開始在ASCLL基礎上統一推出了Unicode字符集,后者兼容了ASCLL字符集,也就是為什么ASCLL字符集的字符在Unicode 中是一樣的。
utf8&utf16&utf32是字符集嗎?
可能聽utf8這樣的描述太多,有時候會混淆編碼和字符集。 首先明確的說他們不是字符集,他們是一種編碼關系,是Unicode 字符的存儲形勢。這樣的形式有很多種:
官網描述:
所以記住:utf8&utf16&utf32是一種Unicode字符的存儲算法,他通過多種格式來存儲一個字符。
我們還是以淬淵閣中的“淬”舉例 utf8編碼后:
這里可以看到”淬“在Unicode 編碼字符集中對應的數值是0x6DEC,但是存入計算機就是0xE6 B7AC, 這里就可以看出UTF8是一個編碼存儲方案, 大家有興趣也可以將?淬淵閣中的“淬”(0x6DEC)用utf16或32 加上BOM 或不帶BOM的方式展示出來看看。(BOM 就數據大小端存儲方式的標記。)
為什么要有字符集,字符集作用?
主要是以下兩個作用:
- 支持字符在計算機中存儲和傳遞
- 支持字符的打印和屏幕渲染
計算機只知道二進制,所以字符并沒有辦法存入計算機,只有采用數字,進而轉化成二進制才能在計算機存儲。所以字符集對應的數字就可以存入計算機,ASCLL 碼比較簡單,對應的數字轉換成二進制就可以直接存入計算機了, 但是Unicode編碼推出后,考慮到了字節浪費,所以通過UTF(8,16,32)編碼方案將Unicode對應的編碼優化到了1-4個字節大小來存入計算機。
這個字符集除了在存儲的時候使用外,還在字符輸出UI時也有用到, 比如一個字符”淬“通過二進制在計算機中傳遞,當要打印出來的時候,他們會去獲取相關字體,字體里面會保存”淬“這個字對應的矢量數據(或者簡單理解為這個字的像素點位位置)和這個字對應的字符編碼。 當二進制數據和這個字體中存入的編碼一致的時候, 計算機就將矢量數據渲染到屏幕上和打印機中,從而實現打印。
?字符查詢網站
?ASCLL
Unicode? ?or? ??Unicode
UTF8&Unicode常見問題答案
?__stdcall和__cdecl約定
stdcall: stdandard call?
cdecl: c? declaration
__stdcall和__cdecl 用于c/c++ 調用函數方式的約定,決定了不同的參數訪問和入棧的順序,決定由誰來清理棧數據。
__stdcall
調用函數的時候,參數是從右到左順序入棧。然后函數執行完成后,是由被調用者來負責清理棧數據。
__cdecl
調用函數的時候,參數是從右到左順序入棧,然后函數執行完成后, 是由調用者來負責清理棧數據。?
__cdecl 由于是由調用者來負責清理棧,所以他可以支持可變參數。
其實這里我是有疑問的? 為啥__stdcall是被調用者清理棧就不支持可變參數呢?
后來我查了下資料,解釋是這樣的,?__stdcall修飾的時候時候,編譯器知道有幾個參數,所以就會生成對應的棧清理代碼,被調用函數調用出棧的時候就能準確清理參數個數。 如果用__stdcall來修飾可變參函數,那么編譯期間就不知道運行的時候會傳入幾個參數的,所以__stdcall不可以用于可變參函數。?__cdecl是由調用者清理,因為調用者是動態傳入的參數個數,所以函數執行完成后,他是知道傳入了幾個,所以知道清理幾個參數,這就是為什么它可以支持可變參的原因。