Windows內核函數


字符串處理

在驅動中一般使用的是ANSI字符串和寬字節字符串,在驅動中我們仍然可以使用C中提供的字符串操作函數,但是在DDK中不提倡這樣做,由於C函數容易導致緩沖區溢出漏洞,針對字符串的操作它提供了一組函數分別用來處理ANSI字符串和UNICODE字符串。
針對兩種字符串,首先定義了它們的結構體

typedef struct _STRING {
  USHORT  Length;//字符串的長度
  USHORT  MaximumLength;//字符緩沖的長度
  PCHAR  Buffer;//字符緩沖的地址
} ANSI_STRING, *PANSI_STRING;

typedef struct _UNICODE_STRING {
  USHORT  Length;
  USHORT  MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

對於這兩個字符串的打印,可以使用%wZ打印UNICODE_STRING用%Z打印ANSI_STRING

字符串的初始化

VOID 
  RtlInitAnsiString(
    IN OUT PANSI_STRING  DestinationString,
    IN PCSZ  SourceString
    );

VOID 
  RtlInitUnicodeString(
    IN OUT PUNICODE_STRING  DestinationString,
    IN PCWSTR  SourceString
    );

這兩個函數只是簡單的將SourceString 的首地址賦值給Buffer成員,並初始化相關的長度,所以在使用時需要考慮緩沖的生命周期,權限,同時如果我們改變SourceString 里面存儲的字符串,那么對應的UNICODE_STRING 或者ANSI_STRING中的值也會改變,比如下面的代碼

RtlInitUnicodeString(&uTest, L"Hello World");
RtlCopyMemory(uTest.Buffer, L"Test");

由於Buffer指向的是不可修改的常量內存部分,所以后面試圖修改它的時候會造成程序崩潰。

void InitString(&pUnicodeString)
{
    WCHAR szBuf[255] = L"Hello world";
    RtlInitUnicodeString(pUnicodeString, szBuffer);
} 

void test()
{
    UNICODE_STRING uTest;
    InitString(&uTest);
    //后面的操作
}

我們在另外一個函數中利用局部變量來初始化這個字符串的時候由於當函數調用完成,函數中局部變量被銷毀,這個時候指向的那塊內存可能已經被其他函數所占用,而我們后面通過操作UNICODE_STRING,又要操作這段內存,這個時候一定會出現問題,所以一般如果要在多個函數中使用這個UNICODE_STRING時一般申請一段堆內存,但是在使用完成后一定要記得自己回收這段內存,否則會造成內存泄露,對此DDK專門提供了一組函數來銷毀字符串中的堆內存

VOID 
  RtlFreeAnsiString(
    IN PANSI_STRING  AnsiString
    );

VOID 
  RtlFreeUnicodeString(
    IN PUNICODE_STRING  UnicodeString
    );

字符串拷貝:

VOID 
  RtlCopyString(
    IN OUT PSTRING  DestinationString,
    IN PSTRING  SourceString  OPTIONAL
    );

VOID 
  RtlCopyUnicodeString(
    IN OUT PUNICODE_STRING  DestinationString,
    IN PUNICODE_STRING  SourceString
    );

字符串比較

LONG 
  RtlCompareString(
    IN PSTRING  String1,
    IN PSTRING  String2,
    BOOLEAN  CaseInSensitive//是否忽略大小寫
    );

LONG 
  RtlCompareUnicodeString(
    IN PUNICODE_STRING  String1,
    IN PUNICODE_STRING  String2,
    IN BOOLEAN  CaseInSensitive
    );

字符串轉化為大寫

VOID 
  RtlUpperString(
    IN OUT PSTRING  DestinationString,
    IN PSTRING  SourceString
    );

NTSTATUS 
  RtlUpcaseUnicodeString(
    IN OUT PUNICODE_STRING  DestinationString,
    IN PCUNICODE_STRING  SourceString,
    IN BOOLEAN  AllocateDestinationString//是否要求該函數自行為輸出參數分配內存
    );

這兩個函數在調用是目標字符串和源字符串可以是同一個字符串
字符串與整形數字之間的轉化可以使用函數

NTSTATUS
  RtlUnicodeStringToInteger(
    IN PUNICODE_STRING  String,
    IN ULONG  Base  OPTIONAL,//需要的數的進制
    OUT PULONG  Value
    );

NTSTATUS 
  RtlIntegerToUnicodeString(
    IN ULONG  Value,
    IN ULONG  Base  OPTIONAL,
    IN OUT PUNICODE_STRING  String
    );

ANSI與UNICODE字符串的相互轉化可以使用下面的函數

NTSTATUS 
  RtlUnicodeStringToAnsiString(
    IN OUT PANSI_STRING  DestinationString,
    IN PUNICODE_STRING  SourceString,
    IN BOOLEAN  AllocateDestinationString
    );

NTSTATUS 
  RtlAnsiStringToUnicodeString(
    IN OUT PUNICODE_STRING  DestinationString,
    IN PANSI_STRING  SourceString,
    IN BOOLEAN  AllocateDestinationString
    );

文件操作

創建或者打開一個文件

文件的創建和打開都是使用函數ZwCreateFile

NTSTATUS  
  ZwCreateFile(
    OUT PHANDLE  FileHandle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_ATTRIBUTES  ObjectAttributes,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN PLARGE_INTEGER  AllocationSize  OPTIONAL,
    IN ULONG  FileAttributes,
    IN ULONG  ShareAccess,
    IN ULONG  CreateDisposition,
    IN ULONG  CreateOptions,
    IN PVOID  EaBuffer  OPTIONAL,
    IN ULONG  EaLength
    );
  1. FileHandle:這個函數通過這個參數返回文件句柄
  2. DesiredAccess:以何種權限打開或者創建這個文件,GENERIC_READ可讀,GENERIC_WRITE可寫,GENERIC_EXECUTE可執行,GENERIC_ALL所有權限
  3. ObjectAttributes:這是一個文件屬性的結構體,里面包含有要打開的文件的名稱
  4. IoStatusBlock:接受函數操作文件的結果狀態
  5. AllocationSize:指定在創建愛女或者寫文件時初始大小,如果給0,則文件大小會隨着寫入數據的增加而動態的增加
  6. FileAttributes:指定新創建文件的屬性,一般給0或者FILE_ATTRIBUTE_NORMAL
  7. ShareAccess:文件的共享權限,其他線程或者進程通過這個句柄訪問文件的權限,給0表示不允許其他進程通過這個句柄訪問,FILE_SHARE_READ讀, FILE_SHARE_WRITE寫,FILE_SHARE_DELETE刪除
  8. CreateDisposition:指定當文件存在或者不存在時這個函數的動作。它的取值可以有下面幾個
取值 文件存在 文件不存在
FILE_SUPERSEDE 新建一個文件替代 新建文件
FILE_CREATE 返回一個錯誤 創建文件
FILE_OPEN 打開文件 返回一個錯誤
FILE_OPEN_IF 打開文件 創建文件
FILE_OVERWRITE 打開,並且將之前的內容覆蓋 返回錯誤
FILE_OVERWRITE_IF 打開,並且將之前的內容覆蓋 創建文件

9. CreateOptions打開或者創建文件時的附加操作,一般給FILE_SYNCHRONOUS_IO_NONALERT
10. EaBuffer指向擴展空間的指針
11. EaLength擴展空間的大小
這個函數與應用層的CreateFile不同的時,在指定打開或者創建文件名時是使用結構OBJECT_ATTRIBUTES來指定,針對這個結構,有一個函數能夠初始化它

VOID 
  InitializeObjectAttributes(
    OUT POBJECT_ATTRIBUTES  InitializedAttributes,
    IN PUNICODE_STRING  ObjectName,//文件名
    IN ULONG  Attributes,
    IN HANDLE  RootDirectory,
    IN PSECURITY_DESCRIPTOR  SecurityDescriptor
    );

Attributes:該對象的描述信息,一般給OBJ_CASE_INSENSITIVE 表示對大小寫敏感
RootDirectory :該文件的根目錄,一般給NULL
SecurityDescriptor :安全描述符,一般也是給NULL
另外這里的名稱必須使用符號鏈接名或者設備名,而不是我們熟悉的“C:\”這種形式對於C盤可以使用名稱“\??\C”或者“\Device\HarddiskVolum1”這種形式
當程序結束時需要調用ZwClose來清理文件句柄這個函數的參數比較簡單,只是簡單的傳入文件句柄即可

獲取和設置文件的相關信息

可以下面兩個函數分別獲取和設置文件的相關信息

NTSTATUS 
  ZwQueryInformationFile(
    IN HANDLE  FileHandle,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    OUT PVOID  FileInformation,
    IN ULONG  Length,
    IN FILE_INFORMATION_CLASS  FileInformationClass
    );

NTSTATUS 
  ZwSetInformationFile(
    IN HANDLE  FileHandle,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN PVOID  FileInformation,
    IN ULONG  Length,
    IN FILE_INFORMATION_CLASS  FileInformationClass
    );

其中FileInformationClass是一個枚舉值,根據這個值得不同FileInformation可以被解析成不同的內容。
1. 當這個參數為FileStandardInformation時,使用結構體FILE_STANDARD_INFORMATION

typedef struct FILE_STANDARD_INFORMATION {
  LARGE_INTEGER  AllocationSize; //為文件分配簇所占空間的大小
  LARGE_INTEGER  EndOfFile;//距離文件結尾還有多少字節,當文件指針位於文件頭時,這個值就是文件本身大小
  ULONG  NumberOfLinks;//有多少個鏈接文件
  BOOLEAN  DeletePending;//是否准備刪除
  BOOLEAN  Directory;//是否為目錄
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
  1. 當這個參數為FileBasicInformation使用結構體FILE_BASIC_INFORMATION
typedef struct FILE_BASIC_INFORMATION {
  LARGE_INTEGER  CreationTime; //創建時間
  LARGE_INTEGER  LastAccessTime;//上次訪問時間
  LARGE_INTEGER  LastWriteTime;//上次寫文件時間
  LARGE_INTEGER  ChangeTime;//上次修改時間
  ULONG  FileAttributes;//文件屬性
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;

其中時間參數是一個LARGE_INTEGER類型的整數,代表從1601年到現在經過多少個100ns。文件屬性參數如果為FILE_ATTRIBUTE_DIRECTORY表示這是一個目錄文件,FILE_ATTRIBUTE_NORMAL表示是一個普通文件,FILE_ATTRIBUTE_HIDDEN表示這是一個隱藏文件,FILE_ATTRIBUTE_SYSTEM表示這是一個系統文件,FILE_ATTRIBUTE_READONLY表示這是一個只讀文件
3. 當這個參數為FileNameInformation時,使用結構體FILE_NAME_INFORMATION

typedef struct _FILE_NAME_INFORMATION {
  ULONG  FileNameLength;//文件名長度
  WCHAR  FileName[1];//文件名
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
  1. 當這個參數是FilePositionInformation時,使用結構體FILE_POSITION_INFORMATION
typedef struct FILE_POSITION_INFORMATION {
  LARGE_INTEGER  CurrentByteOffset;//當前文件指針的位置
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;

讀寫文件

寫文件調用函數ZwCreateFile

NTSTATUS 
  ZwWriteFile(
    IN HANDLE  FileHandle,//文件句柄
    IN HANDLE  Event  OPTIONAL,//時間對象一般給NULL
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,//一般給NULL
    IN PVOID  ApcContext  OPTIONAL,//一般給NULL
    OUT PIO_STATUS_BLOCK  IoStatusBlock,//記錄寫操作的狀態用里面的Information成員記錄實際寫了多少字節
    IN PVOID  Buffer,//寫入文件中緩沖區的指針
    IN ULONG  Length,//緩沖區中數據的長度
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL,//從文件的多少地址開始寫
    IN PULONG  Key  OPTIONAL//一般給NULL
    );

讀文件使用函數ZwReadFile

NTSTATUS 
  ZwReadFile(
    IN HANDLE  FileHandle,//文件句柄
    IN HANDLE  Event  OPTIONAL,//一般給NULL
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,//一般給NULL
    IN PVOID  ApcContext  OPTIONAL,//一般給NULL
    OUT PIO_STATUS_BLOCK  IoStatusBlock, //讀取的字節數保存在結構的成員Information中
    OUT PVOID  Buffer,//緩沖區的指針
    IN ULONG  Length,//緩沖區的長度
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL,//從文件的多少位置開始讀
    IN PULONG  Key  OPTIONAL//一般給NULL
    );

注冊表操作

注冊表中有下面幾個概念:
1. 注冊表項:注冊表項類似於目錄的概念,下面可以有子項或者注冊表的鍵-值對
2. 注冊表子項:類似於子目錄的概念
3. 鍵名:通過鍵名可以尋找到相應的鍵值
4. 鍵值類別:每個鍵值在存儲的時候有不同的類型,相當於變量的類型,主要有字符串和整型
5. 鍵值:鍵名下對應存儲的數據
這里寫圖片描述

創建和關閉注冊表

創建注冊表使用函數ZwCreateKey

NTSTATUS 
  ZwCreateKey(
    OUT PHANDLE  KeyHandle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_ATTRIBUTES  ObjectAttributes,
    IN ULONG  TitleIndex,
    IN PUNICODE_STRING  Class  OPTIONAL,
    IN ULONG  CreateOptions,
    OUT PULONG  Disposition  OPTIONAL
    );
  1. KeyHandle:輸出一個注冊表對應項的句柄,以后針對這個項操作都是以這個句柄作為標示
  2. DesiredAccess:訪問權限,一般都設置為KEY_ALL_ACCESS
  3. ObjectAttributes:用法與文件操作中的用法相同
    其中應用層中注冊表項與內核中注冊表項的對應關系如下:
應用層中的子健 內核中的路徑
HKEY_CLASSES_ROOT 沒有對應的路徑
HKEY_CURRENT_USER 沒有簡單的對應路徑,但是可以求得
HKEY_USERS \Registry\User
HKEY_LOCAL_MACHINE \Registry\Machine

4. TitleIndex:一般設置為0
5. Class 一般給NULL
6. CreateOptions:創建選項,一般給REG_OPTION_NON_VOLATILE
7. Disposition:返回創建的狀態,如果是REG_CREATED_NEW_KEY表示創建了一個新的注冊表項如果是REG_OPENED_EXISTING_KEY表示打開一個已有的注冊表項
8. ### 添加、修改注冊表鍵
注冊表中的鍵是類似與字典中的鍵值對,通過鍵名找到對應的值,鍵值的類型大致可以分為下面幾種

分類 描述
REG_BINARY 鍵值采用二進制存儲
REG_SZ 鍵值用寬字符串,以\0結尾
REG_EXPAND_SZ 與上面的REG_SZ相同,它是上面那個字符串的擴展字符
REG_MULTI_SZ 能夠存儲多個字符串,每個都以\0隔開
REG_DWORD 鍵值用4字節整型存儲(這個類型的數據在驅動中使用ULONG來替代)
REG_QWORD 鍵值用8字節存儲(這個用LONGLONG)

用函數ZwSetValueKey可以添加和修改注冊表的一項內容

 NTSTATUS 
  ZwSetValueKey(
    IN HANDLE  KeyHandle, //注冊表句柄
    IN PUNICODE_STRING  ValueName,//要修改或者新建的鍵名
    IN ULONG  TitleIndex  OPTIONAL,//一般設置為0
    IN ULONG  Type,//在上面的表中選擇一個
    IN PVOID  Data,//鍵值
    IN ULONG  DataSize//鍵值數據的大小
    );

當傳入的鍵值不存在則創建一個新鍵值,否則就修改原來的鍵值

查詢注冊表

查詢注冊表使用函數ZwQueryValueKey

NTSTATUS 
  ZwQueryValueKey(
    IN HANDLE  KeyHandle, //注冊表句柄
    IN PUNICODE_STRING  ValueName,//注冊表鍵名
    IN KEY_VALUE_INFORMATION_CLASS  KeyValueInformationClass,
    OUT PVOID  KeyValueInformation,//接收返回信息的緩沖區
    IN ULONG  Length,//緩沖區的大小
    OUT PULONG  ResultLength//真實緩沖區的大小
    );

使用這個函數時利用參數KeyValueInformationClass來指定接收數據的類型,根據這個值的不同,函數會返回不同的結構體放到一個緩沖區中。一般這個值可取:KeyValueBasicInformation 返回注冊表項的基礎信息
KeyValueFullInformation 返回注冊表的全部信息
KeyValuePartialInformation 返回注冊表的部分信息
一般情況下使用KeyValuePartialInformation查詢鍵值數據
利用這個函數來查詢時一般也是采用兩次調用的方式,第一次返回數據所需緩沖,然后分配緩沖並進行第二次調用

枚舉子項

DDK提供了兩個函數用於這個功能

NTSTATUS 
  ZwQueryKey(
    IN HANDLE  KeyHandle,//注冊表句柄
    IN KEY_INFORMATION_CLASS  KeyInformationClass,//保存注冊表信息的結構體的類型
    OUT PVOID  KeyInformation,//返回查詢到信息的緩沖
    IN ULONG  Length,//緩沖的大小
    OUT PULONG  ResultLength//真正信息的大小
    );

NTSTATUS 
  ZwEnumerateKey(
    IN HANDLE  KeyHandle,//句柄
    IN ULONG  Index,//這個值是表示第幾個子項
    IN KEY_INFORMATION_CLASS  KeyInformationClass,//查詢到的信息的結構體
    OUT PVOID  KeyInformation,//返回信息的緩沖
    IN ULONG  Length,//緩沖長度
    OUT PULONG  ResultLength//返回信息的長度
    );

其中ZwQueryKey函數用於查詢某個注冊表項中有多少個子項,在調用這個函數時傳入的KeyInformationClass的值一般給KeyFullInformation,在這個結構體中的SubKeys表示有多少個子項,而ZwEnumerateKey則是用於查詢各個子項中的具體內容,通過指定Index表示我們要查詢該項中的第幾個子項,將KeyInformationClass填入KeyBasicInformation,這樣在結構體的Name里面可以得到具體的注冊表子項的名稱

枚舉子健

枚舉子鍵的方法於上面的大致相同,首先利用ZwQueryKey查詢注冊表,然后取結構體KeyFullInformation的成員Values,根據這個值在循環中依次調用函數ZwEnumerateValueKey,結構體類填入 KeyValueBasicInformation查詢基本信息即可

刪除子項

刪除子項使用的內核函數是ZwDeleteKey

NTSTATUS 
  ZwDeleteKey(
    IN HANDLE  KeyHandle
    );

這個函數只能刪除沒有子項的項目,如果有子項,則需要先刪除所有子項。

其他注冊表函數

為了簡化注冊表操作,DDK提供了另外一組以Rtl開頭的函數,把之前的Zw函數進行了封裝,下面是這些函數與它們功能的對應關系

函數名 描述
RtlCreateRegistryKey 創建注冊表項
RtlCheckRegistryKey 查看注冊表中的某項是否存在
RtlWriteRegistryValue 寫注冊表
RtlDeleteRegistryValue 刪除注冊表的子鍵


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM