内核编程之安全字符串

很多系统的安全问题是由于不好的缓冲处理而导致的缓冲区溢出而引起的。不好的缓冲区处理经常与字符串操作有关。标准的字符串处理函数是由C/C++语言运行时库提供的(例如:strcat、strcpy、sprintf等),这些函数都没有防止写操作会超过缓冲区实际长度。

两个新的字符串处理函数集,被称为安全字符串函数(safe string function),为不好的缓冲区处理代码提供了额外的处理。这些安全字符串函数存在于Windows Driver Kit(WDK)、微软针对Windows XP SP1以及之后版本操作系统的Driver Development Kit(DDK)以及Windows SDK之中。这些函数于原有的由Windows提供的内置于C/C++中的字符串函数相对应或类似。

其中一个安全字符串函数集是提供给内核模式代码使用的。这些函数的原型在Ntstrsafe.h头文件中定义。这个头文件和相关联的库由WDK提供。

另一个安全字符串函数集是提供给用户模式下的应用程序使用的。原型在Strsafe.h头文件中。这个头文件和相关联的库由Windows SDK提供。

其中内核模式下的安全字符串函数集由下列两个子集构成:

  • 处理Unicode 和 ANSI 字符的安全字符串函数。

    处理两个字节的Unicode字符的安全字符串函数一般以-W为后缀,而处理一个字节的ANSI字符的安全字符串函数一般以-A为后缀。例如,RtlStringCbCatN,这个函数用于连接两个字符串并且限制附加字符串的长度,针对字符类型由两个调用:RtlStringCbCatNW和RtlStringCbCatN。

  • 处理UNICODE_STRING结构的安全字符串函数。

    每个这样的函数会接收一个UNICODE_STRING结构作为一个输入或者输出参数,或者两者都有。例如:RtlStringCbCopyUnicodeString接收UNICODE_STRING结构作为输入参数,RtlUnicodeStringCopyString接收UNICODE_STRING结构作为一个输出参数,RtlUnicodeStringCopy接收UNICODE_STRING作为一个输入和输出参数。

内核模式下的安全字符串函数提供以下性能:

  • 每个安全字符串函数需要目标缓冲区的大小作为输入参数。因此,这个函数可以保证在写的过程中不会操作缓冲区的结尾。
  • Unicode和ANSI字符串函数将NULL字符作为所有输出字符串的结束标志,即使该操作会缩短期望的结果。
  • 所有的安全字符串函数都会返回一个NTSTATUS值,其中只有一个值表示成功(STATUS_SUCCESS)。
  • 大多安全字符串函数可同时支持以字节来计数和以字符来计数。例如,RtlStringCbCat连接两个以字节计数的字符串,RtlStringCchCat连接两个以字符计数的字符串。
  • 大多安全字符串函数都提供扩展版,以-Ex为后缀的版本提供了额外的功能。例如,RtlStringCbCatEx扩充了RltStringCbCat的功能。

内核模式下安全字符串的摘要信息

下表是内核模式驱动下的安全字符串函数摘要信息,它还表明了安全字符串函数所代替的C/C++运行时库函数。如果一个函数名中包含Cb,则这个函数所处理的字符串是以字节计数的,若一个函数名中包含Cch,则这个函数所处理的字符串是以字符计数的。


Functions


Purpose


Replaces


RtlStringCbCat
RtlStringCbCatEx
RtlStringCchCat
RtlStringCchCatEx
RtlUnicodeStringCat
RtlUnicodeStringCatEx
RtlUnicodeStringCatString
RtlUnicodeStringCatStringEx
RtlUnicodeStringCbCatStringN
RtlUnicodeStringCbCatStringNEx
RtlUnicodeStringCchCatStringN
RtlUnicodeStringCchCatStringNEx


连接两个字符串。


strcat
wcscat


RtlStringCbCatN
RtlStringCbCatNEx
RtlStringCchCatN
RtlStringCchCatNEx
RtlUnicodeStringCbCatN
RtlUnicodeStringCbCatNEx
RtlUnicodeStringCchCatN
RtlUnicodeStringCchCatNEx


连接两个以字节来计数的字符串,并限制附加字符串的大小。


strncat
wcsncat


RtlStringCbCopy
RtlStringCbCopyEx
RtlStringCbCopyUnicodeString
RtlStringCbCopyUnicodeStringEx
RtlStringCchCopy
RtlStringCchCopyEx
RtlStringCchCopyUnicodeString
RtlStringCchCopyUnicodeStringEx
RtlUnicodeStringCopy
RtlUnicodeStringCopyEx
RtlUnicodeStringCopyString
RtlUnicodeStringCopyStringEx


将一个字符串复制到缓冲区中。


strcpy
wcscpy


RtlStringCbCopyN
RtlStringCbCopyNEx
RtlStringCchCopyN
RtlStringCchCopyNEx
RtlUnicodeStringCbCopyN
RtlUnicodeStringCbCopyNEx
RtlUnicodeStringCchCopyN
RtlUnicodeStringCchCopyNEx
RtlUnicodeStringCbCopyStringN
RtlUnicodeStringCbCopyStringNEx
RtlUnicodeStringCchCopyStringN
RtlUnicodeStringCchCopyStringNEx


将一个字符串复制到缓冲区中,并限制被复制字符串的大小。


strncpy
wcsncpy


RtlStringCbLength
RtlStringCchLength
RtlUnalignedStringCbLength
RtlUnalignedStringCchLength


计算字符串的长度。


strlen
wcslen


RtlStringCbPrintf
RtlStringCbPrintfEx
RtlStringCchPrintf
RtlStringCchPrintfEx
RtlUnicodeStringPrintf
RtlUnicodeStringPrintfEx


创建一个格式化文本字符串,这基于一个格式字符串和多个额外的函数参数。


print
swprintf
_snprintf
_snwprintf


RtlStringCbVPrintf
RtlStringCbVPrintfEx
RtlStringCchVPrintf
RtlStringCchVPrintfEx
RtlUnicodeStringVPrintf
RtlUnicodeStringVPrintfEx


创建一个格式化文本字符串,这基于一个格式字符串和一个额外的函数参数。


vsprintf
vswprintf
_vsnprintf
_vsnwprintf


RtlUnicodeStringInit
RtlUnicodeStringInitEx
RtlUnicodeStringValidate
RtlUnicodeStringValidateEx


初始化或验证一个UNICODE_STRING结构。


None

导入内核模式安全字符串函数

内核模式安全字符串函数由以下两个部分支持:

  • 作为内链代码,这被包含在入头文件Ntstrsafe.h中。
  • 在一个你的代码可以被链接到的库中。

如果你的代码只是运行在Windows XP 以及其后版本的操作系统中的话,你应该使用内核模式安全字符串函数的内联版本。如果你的代码需要运行在Windows XP之前的操作系统上,那么你必须使用安全字符串的库版本。

使用内核模式安全字符串函数的内联版本

包含如下头文件:

#include <ntstrsafe.h>

使用内核模式安全字符函数的库版本

  1. 在包含新的头文件之前定义NTSTRSAFE_LIB,如下所示:

    #define NTSTRSAFE_LIB
    #include <ntstrsafe.h>

  2. 在你工程的源文件中,为$(DDK_LIB_PATH)\ntstrsafe.lib.添加一个TARGETLIBS入口

只要你在源文件中包含了Ntstrsafe.h头文件,在C/C++运行时库函数中已经被Ntstrsafe.h代替的函数将不能使用。如果你使用了这些被废除的函数,编译器就会给出错误提示你要使用安全字符串函数。

类似的,若一个头文件中所提供的代码引用了一个或多个已经被废除的字符串,而这个头文件又在Ntstrsafe.h之后才包含进来的话,这些函数将会导致编译器给出错误信息。因此,Ntstrsafe.h应该在所有其他头文件都包含完毕之后再包含进来,也就是说放在Include的最后一行。

如果你想同时使用C/C++运行时库函数和安全字符串函数,那么就在包含Ntstrsafe.h头文件之前加入下面这行代码:

#define NTSTRSAFE_NO_DEPRECATE

你可以只提供以字节计数的或只以字符计数的安全字符串函数。

只提供以字节计数的函数

在包含Ntstrsafe.h头文件之前加入下面这行代码:

#define NTSTRSAFE_NO_CCH_FUNCTIONS

只支持以字符计数的函数

在包含Ntstrsafe.h头文件之前加入下面这行代码:

#define NTSTRSAFE_NO_CB_FUNCTIONS

你可以定义NTSTRSAFE_NO_CB_FUNCTIONS 或NTSTRSAFE_NO_CCH_FUNCTIONS其中一个,但不能都定义。

你可以不支持UNICODE_STRING结构的函数

不支持UNICODE_STRING结构的函数

在包含Ntstrsafe.h头文件之前加入下面这行代码:

#define NTSTRSAFE_NO_UNICODE_STRING_FUNCTIONS

任意一个ANSI或Unicode字符串的最大字符数存储在NTSTRSAFE_MAX_CCH中。UNICODE_STRING结构的最大字符数保存在NTSTRSAFE_UNICODE_MAX_CCH中。这些常量在被定义在Ntstrsafe.h中。

若你想重新指定NTSTRSAFE_MAX_CCH和NTSTRSAFE_UNICODE_STRING_MAX_CCH的值,注意这个值,必须小于原有的默认值。可以在包含Ntstrsafe.h之前加入下面的代码:

#define NTSTRSAFE_MAX_CCH  <new-value>
#define NTSTRSAFE_UNICODE_STRING_MAX_CCH  <new-value>

在Ntstrsafe.h中的指令将会验证你新设置的值不能大于原有的默认值。

时间: 2024-11-13 08:06:07

内核编程之安全字符串的相关文章

C++windows内核编程笔记day07_day08,可视化建菜单、加速键使用、绘图等

可视化操作创建的菜单,加载到窗口. 方法1:注册时指定菜单 wce.lpszMenuName=MAKEINTRESOURCE(IDR_MENUMAIN);//数字形式的资源ID转换为字符串形式的资源 方法2: //创建窗口时加载菜单资源 HMENU menumain= LoadMenu(g_hinstance,MAKEINTRESOURCE(IDR_MENUMAIN)); menumain 传入 CreateWindowEx();//倒数第三个参数 窗口指定小图标: 1.注册时指定 wce.hI

C++windows内核编程笔记day09_day10,对话框和窗口基本控件等的使用

//设置字体颜色 SetTextColor(hdc,RGB(255,0,0)); //窗口背景 //wce.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); //wce.hbrBackground=CreateSolidBrush(RGB(0,0,255)); //设置字体背景 SetBkColor(hdc,RGB(0,0,200)); //设置字体背景模式 SetBkMode(hdc,TRANSPARENT);//字体背景透明 //创建字体,成功返回字体,失败返回

内核编程学习小结

The road to success was trial and error development, recompilation, and lots of crashes. 寒假过去一个月,计划很多时候也没法跟上.不过总体上来说,还是学习和收获了一些东西的.过去的事情不能改变,所以也不必过于纠结和懊悔.假期的前期还计划对英语进行系统性的学习,我个人任务是画错时间,用错力了,看专业英语的时间完全可以用开翻译与内核编程有关的开发文档,这样更有意义一些,在发现问题后也没办法放下,这是很不好的一个缺

Linux内核编程规范与代码风格

source: https://www.kernel.org/doc/html/latest/process/coding-style.html translated by trav, [email protected] 这是一篇阐述Linux内核编程代码风格的文档,译者以学习为目的进行翻译. 1 缩进 Tab的宽度是八个字符,因此缩进的宽度也是八个字符.有些异教徒想让缩进变成四个字符,甚至是两个字符的宽度,这些人和那些把 PI 定义为 3 的人是一个路子的. 注意:缩进的全部意义在于清晰地定义

Windows内核编程时的习惯与注意事项

Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html 一.内核编程注意细节: 在头文件中使用的是 <ntddk.h>,而非普通的 <windows.h>. 在应用层编程时,在内核编程时,要使用自己的WDK文档.https://docs.microsoft.com/zh-cn/windows-hardware/drivers/?redirectedfrom=MSDN 二.获取未公开API的方法: 特征码搜

初探linux内核编程,参数传递以及模块间函数调用

一.前言                                  我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间函数调用 二.准备工作                           首先,在你的linux系统上面安装linux头文件,debian系列: 1 $:sudo apt-get install linux-headers-`uname -r` 安装后,在你的/lib/modules/目录下有你刚

c#编程基础之字符串函数

c#常用的字符串函数 例一: 获取字符串的大小写函数 ToLower():得到字符串的小写形式 ToUpper():得到字符串的大写形式 注意: 字符串时不可变的,所以这些函数都不会直接改变字符串的内容,而是把修改后的字符串通过函数返回值的形式返回. 源码如下: using System; using System.Collections.Generic; using System.Text; namespace 字符串函数学习 { class Program { static void Mai

编程题:字符串输出函数puts()

#include<stdio.h> void main() {char str1[]="student",str2[]="teacher"; puts(str1);puts(str2); printf("%s",str1); printf("%s\n%s\n",str1,str2); } 编程题:字符串输出函数puts(),布布扣,bubuko.com

编程题:字符串的指针引用。用指针法实现。功能:将字符串str2复制连接到str1

#include<stdio.h> void main() { char *s1,*s2, str1[30]="beijing",str2[10]="China"; for(s1=str1;*s1!='\0';s1++); for(s2=str2;*s2!='\0';) *s1++=*s2++; *s1='\0'; printf("%s\n",str1); } 编程题:字符串的指针引用.用指针法实现.功能:将字符串str2复制连接到s