专注网站搭建的公司,上海青浦网站建设公司,一家公司为什么要建官方网站,网站美工用什么软件字符串处理 在驱动中一般使用的是ANSI字符串和宽字节字符串#xff0c;在驱动中我们仍然可以使用C中提供的字符串操作函数#xff0c;但是在DDK中不提倡这样做#xff0c;由于C函数容易导致缓冲区溢出漏洞#xff0c;针对字符串的操作它提供了一组函数分别用来处理ANSI字符… 字符串处理 在驱动中一般使用的是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, LHello World);
RtlCopyMemory(uTest.Buffer, LTest); 由于Buffer指向的是不可修改的常量内存部分所以后面试图修改它的时候会造成程序崩溃。 void InitString(pUnicodeString)
{WCHAR szBuf[255] LHello 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//是否要求该函数自行为输出参数分配内存); 这两个函数在调用是目标字符串和源字符串可以是同一个字符串 字符串与整形数字之间的转化可以使用函数 NTSTATUSRtlUnicodeStringToInteger(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); FileHandle这个函数通过这个参数返回文件句柄DesiredAccess以何种权限打开或者创建这个文件GENERIC_READ可读GENERIC_WRITE可写GENERIC_EXECUTE可执行GENERIC_ALL所有权限ObjectAttributes这是一个文件属性的结构体里面包含有要打开的文件的名称IoStatusBlock接受函数操作文件的结果状态AllocationSize指定在创建爱女或者写文件时初始大小如果给0则文件大小会随着写入数据的增加而动态的增加FileAttributes指定新创建文件的属性一般给0或者FILE_ATTRIBUTE_NORMALShareAccess文件的共享权限其他线程或者进程通过这个句柄访问文件的权限给0表示不允许其他进程通过这个句柄访问FILE_SHARE_READ读 FILE_SHARE_WRITE写FILE_SHARE_DELETE删除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; 当这个参数为FileBasicInformation使用结构体FILE_BASIC_INFORMATIONtypedef 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; 当这个参数是FilePositionInformation时使用结构体FILE_POSITION_INFORMATIONtypedef struct FILE_POSITION_INFORMATION {LARGE_INTEGER CurrentByteOffset;//当前文件指针的位置
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; 读写文件 写文件调用函数ZwCreateFile NTSTATUS ZwWriteFile(IN HANDLE FileHandle,//文件句柄IN HANDLE Event OPTIONAL,//时间对象一般给NULLIN PIO_APC_ROUTINE ApcRoutine OPTIONAL,//一般给NULLIN PVOID ApcContext OPTIONAL,//一般给NULLOUT 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,//一般给NULLIN PIO_APC_ROUTINE ApcRoutine OPTIONAL,//一般给NULLIN PVOID ApcContext OPTIONAL,//一般给NULLOUT 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); KeyHandle输出一个注册表对应项的句柄以后针对这个项操作都是以这个句柄作为标示DesiredAccess访问权限一般都设置为KEY_ALL_ACCESSObjectAttributes用法与文件操作中的用法相同 其中应用层中注册表项与内核中注册表项的对应关系如下应用层中的子健内核中的路径HKEY_CLASSES_ROOT没有对应的路径HKEY_CURRENT_USER没有简单的对应路径但是可以求得HKEY_USERS\Registry\UserHKEY_LOCAL_MACHINE\Registry\Machine4. 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,//一般设置为0IN 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删除注册表的子键 转载于:https://www.cnblogs.com/lanuage/p/7725717.html