内核的基本操作,多线程和数据结构2022-02-24

image

内核的基本操作,多线程和数据结构

内核的基本操作

UNICODE_STRING

为什么字符串很重要

  • 大型工程中10%~20%的代码都与字符串操作有关
  • 面试里面也有很多字符串相关问题
  • 字符串涉及到指针操作
  • 字符串是编程第二个重要关口

字符串分两类:0结尾和非零结尾

char *

在c语言中使用,以‘\0'结尾

BSTR

在win32编程中使用,前4个字节表示字节长,后面以'\0'结尾,可以理解为是char*UNICODE_STRING的综合

UNICODE STRING

内核函数多以这种字符串作为输入参数,不是以'\0'结尾

定义:

/// unicode编码字符
typedef struct UNICODE_STRING{
USHORT Length; ///< 字节数,不是字符数,而是数据的字节数,字节数 = 字符数 *sizeof(WCHAR) 
USHORT MaximumLength; ///< 字节数,告诉系统函数最多有多少内存可用
PWSTR Buffer; ///< 只是一个指针,一旦定义之后并没有内存,需要正确初始化之后,让Buffer含有真正的数据,才拥有内存.**非零结尾,中间也可能含有0**,PWSTR等价于WCHAR
}UNICODE_STRING,*PUNICODE_STRING;
/// 多字节编码字符
typedef struct _STRING{
USHORT Length; ///< 其他参数同上
USHORT MaximumLength; ///< 其他参数同上
PCHAR Buffer; ///< 其他参数同上
}ANSESTRING,*PANSI_STRING; 

/// Buffer不是以零结尾,中间也可能含有零,wcscpy/wcscmp等操作其中的Buffer不可靠,
///(如果中间UNICODE_STRING.Buffer或者_STRING.Buffer中间也可能含有零,则会提前截断,如果中间不含有零,则会溢出)

对比:

typedef struct XXX{
USHORT Leng;
...
WCHAR Buffer[MAX_PATH]; ///< 字符数组,一旦定义就拥有内存
}XXX,*PXXX

初始化方式:

用字符串常量对其初始化
UNICODE_STRING uStr={0};///< 直接定义,Buffer没有内存(Buffer = NULL),必须正确初始化,让Buffer含有真正的数据。
/// 在栈上的局部变量未初始化,调试的时候内存显示的是"烫烫烫"
/// 在堆上的局部变量未初始化,调试的时候内存显示的是"屯屯屯"
/// L"Hello,world!"字符串常量存放在静态区的.rdata
WCHAR *szHello = L"Hello,world!";
/// buffer是浅拷贝(只把常量字符串的地址拷贝到buffer中),buffer直接指向L"Hello,world!"
/// Length:wcslen(szHello)*sizeof(WCHAR);
/// MaximumLength:(wcslen(szHello)+1)*sizeof(WCHAR);

RtllnitUnicodeString(&ustrTest,szHello);
上面两句等价于:DECLARE_CONST_UNICODE_STRING(ustrTest,L"Hello,world!);
/// buffer直接指向静态常量L"Hello,world!",常量区内存不可被修改,下面的操作必然会出错。
RtlCopyUnicodeString(&ustrTest,&uStr2);
RtlAppendUnicodeToString(&ustrTest,Str1);
RtlAppendUnicodeStringToString(&ustrTest,&uStr2);
RtlAnsiStringToUnicodeString(&ustrTest,&aStr1,FALSE);
用栈上的buffer或静态区的内存对其初始化
/// 定义并初始化一个UNICODE_STRING字符串
UNICODE_STRING ustrTest=(0};
/// 如果把SZHello定义在函数内部就是在栈上
/// 如果把SZHello定义在全局变量就是在静态区.bss                  
WCHAR SZHello[512]=L"Hello,world!"
/// 用栈上的buffer或静态区的内存对其初始化,
ustrTest.Buffer = szHello;
ustrTest.Length = wcslen("Lhello,world")*sizeof(WCHAR);
ustrTest.MaximumLength*sizeof(szHello);
用从堆上分配一个内存对其初始化
/// 定义并初始化一个UNICODE_STRING字符串
UNICODE_STRING ustrTest = {0};
/// 设置有效长度
ULONG ulLength =wcslen(L"Hello world")*sizeof(WCHAR);
/// @param MAX_PATH*sizeof(WCHAR)表示待分配的内存的大小(是字节数,在驱动中UNICODE_STRING一般用来表示设备对象名,符号链接,文件路径,所以**字符数**不会超过MAX_PATH)MAX_PATH宏是260(个),表示在windows系统中一个文件的路径最大个**字符数**。(除去一个盘符,一个':',一个'\',一个'\0',所以实际上文件路径剩下可修改的是256)
/// 为Buffer分配一个堆上的内存
ustrTest.Buffer = ExAllocatePooMWithTag(PagedPool,MAX_PATH*sizeof(WCHAR),'POCU');
/// 如果分配失败,则return
if (ustrTest.Buffer ==NULL)
{
    return;
}
/// 分配成,则对Buffer的指向的堆上内存初始化为0
RtlZeroMemory(ustrTest.Buffer,MAX PATH*sizeof(WCHAR));
/// 把L"Hello,world"拷贝到Buffer指向的内存中去,是深拷贝
memcpy(ustrTest.Buffer,L"Hello,world",ulLength);
/// 设置有效长度
ustrTest.Length =ulLength;
/// 设置最大长度
ustrTest.MaximumLength MAX_PATH*sizeof(WCHAR);
DbgPrint("%wZ\n",&ustrTest);
/// 堆上分配的内存需要手动把它释放掉
ExFreePool(ustrTest.Buffer);
/// 如果下面还需要使用ustrTest,一定要把它设为NEULL
/// 如果执行完ExFreePool(ustrTest.Buffer);函数返回了,就不需要设置为NULL
/// ustrTest.Buffer = NULL;

问题代码:如果文件超过130个字符,就会出问题,文件路径不全。

UNICODE STRING uPath={0};
uPath.Buffer=ExAllocatePooMWithTag(
    PagedPool,
    MAX_PATH,
    'MLFC'
);
uPath.Length=wcslen(L"c:\doc\1.txt")*sizeof(WCHAR);
uPath.MaximumLength=MAX_PATH;

常用对字符串处理的API

初始化
/// 是浅拷贝(只把字符串str1的地址拷贝到uStr1.buffer中),buffer直接指向字符串str1
RtlInitUnicodeString(&uStr1,str1);

浅拷贝只是拷贝的一个地址。在用堆上的内存对UNICODE_STRING初始化时,应该使用memcpy(ustrTest.Buffer,L"Hello,world",ulLength);``ustrTest.Length =ulLength;``ustrTest.MaximumLength MAX_PATH*sizeof(WCHAR);来赋值,要注意不要使用RtllnitunicodeString(ustrTest,L"Hello,world")来赋值(会有风险)

/// eg:UNICODE_STRING初始化不当导致的蓝屏
UNICODE_STRING ustrTest = {0};
ustrTest.Buffer = ExAllocatePoolWithTag(PagedPool,
(wcslen(L"Hello,world"))*sizeof(WCHAR),'POCU');
if (ustrTest.Buffer == NULL)
{
    return;
}
RtlZeroMemory(ustrTest.Buffer,
(wcslen(L"Hello,world"))*sizeof(WCHAR));
/// Buffer原来执行的堆上的内存,执行完之后,Buffer指向了静态区的常量字符串
RtllnitunicodeString(ustrTest,L"Hello,world");
DbgPrint("%wZIn"&ustrTest);
/// 这里释放Buffer所指向的内存,不是堆上内存,内存泄漏了,同时释放的是静态区内存,系统自我保护蓝屏了
ExFreePool(ustrTest.Buffer);
拷贝
/// 深拷贝(copy值,不是地址),很明显名字中有copy字样
RtlCopyUnicodeString(&uStr1,&uStr2); 
拼接
/// @param str2 是简单以'\0'结尾的C语言中的字符串
RtlAppendUnicodeToString(&uStr1,str2);
/// @param ustr2 本身就是UNICODE_STRING的字符串
RtlAppendUnicodeStringToString(&uStr1,&uStr2);

/// eg:
/// 用栈上的buffer或静态区的内存对其初始化
UNICODE_STRING uStr1 = {0};
WCHAR buff[100] = "Hello";
uStr1.Length = 10;
uStr1.Buffer = buff;
/// str1是简单以'\0'结尾的C语言中的字符串
WCHARstr *str1 = L"world";
/// 用str1对uStr2初始化
UNICODE_STRING uStr2 = {0};
uStr2.Length=10:
uStr2.Buffer=str1;

///如果直接拼接一个简单以'\0'结尾的C语言中的字符串则使用下面这个函数
RtlAppendUnicodeToString(&uStr1,str1);
///如果直接拼接一个UNICODE_STRING字符串则使用下面这个和函数
RtlAppendUnicodeStringToString(&uStr1,&uStr2);
拆分
比较
/// @param TRUE/FALSE 是否忽略大小写
/// @return 0表示相等,负数表示uStr1 < uStr1,正数则表示uStr1 > uStr1
RtlcompareUnicodeString(&uStr1,&uStr1
TRUE/FALSE);
编码转换
/// 把多字节字符串转换成宽字节字符串
/// @param TRUE/FALSE 转换的过程中内存的大小会发生变化,涉及内存分配和计算,TRUE表示交给系统去计算内存的大小和分配内存,FALSE则表示程序员自己去计算和分配内存
RtlAnsiStringToUnicodeString(&uStr1,&aStr1,
TRUE/FALSE);
/// 如果前面设置为TRUE,系统帮忙计算和分配内存,用完之后一定要释放掉,否则会造成内存泄漏
/// 实际上很少会遇到字符串编码的转换,因为内核中用的都是UNICODE_STRING
/// DbgPrint在打印unicode中文的话,在debug view里面是看不到的,这种情况下就需要把unicode中文转化成多字节编码才能看到
RtlFreeUnicodeString(&uStr1);

安全函数

/// unicode编码字符
typedef struct UNICODE_STRING{
USHORT Length; ///< 字节数,不是字符数,而是有效数据的字节数
USHORT MaximumLength; ///< 字节数,告诉系统函数最多有多少内存可用
PWSTR Buffer; ///< 只是一个指针,一旦定义之后并没有内存,需要正确初始化之后,让Buffer含有真正的数据,才拥有内存.**非零结尾,中间也可能含有0**,PWSTR等价于WCHAR
}UNICODE_STRING,*PUNICODE_STRING;

在UNICODE_STRNG的类型定义中可以发现,是存在UNICODE_STRING能存储大字符长度是655(USHORT取值范围是0-(2^16-1)),能表示65535/2 = 32,767个字符,而在上述的对字符串操作的函数中,都没有溢出检测的,存溢出风险

//安全函数,溢出检测
#include <ntstrsafe.h>
/// str1,&uStr2超过了32767,或者uStr1+uStr2超过了32767,函数就会返回失败
RtlUnicodeStringInit(&uStr1,str1);
RtlUnicodeStringCopy(&uStr1,&uStr2);
RtlUnicodeStringCat(&uStr1,&uStr2);
/// 不能操作32767个字符
#define NTSTRSAFE UNICODE_STRING_MAX_CCH(Oxffff / sizeof((wchar t))

自己实现所有c语言中与字符串相关的库函数,再与微软的对比

初始化
拷贝
拼接
拆分
比较

文件

文件的表示

  • 应用层:"c\\doc\\hi.txt"
  • 内核:L"\\??\\c:\\hi.txt" --> "\\device\\harddiskvolume3\\hi.txt
    • "\\device\\harddiskvolume3\\hi.txt表示在设备对象上有hi.txt文件,设备对象名"\\device\\harddiskvolume3\\hi.txt是由内核驱动创建的,然后在根据设备对象名创建符号链接。"\\device\\harddiskvolume3\\hi.txt
      • \\??\\c:代表卷设备对象的符号链接,也称为盘符。

设备对象的表示

  • 应用层:
    -设备名: L"\\\\.\\xxxDrv"其中xxxDrv代表符号链接名,把设备对象当作一个特殊的文件打开,打开得到一个句柄。
  • 内核层:
    • 设备名:`"\device\xxxDrv"``
    • 符号链接名:"dosdevices\\xxxDrv"等价于\\??\\xxxDrv"

文件操作

应用层

打开文件获得handle -> 基于handle读写删除查询 -> 关闭

创建文件/文件夹
读/写
拷贝
移动
删除
属性访问与设置
内核层

每个API都有对应的Irp,但复制、粘贴、移动没有对应的Irp,因为这三个动作本质是读和写

ZwCreateFile
  • ZwCreateFile打开文件
  • 接口说明
/**
 *  @brief       DriverEntry NtCreateFile 例程创建一个新文件或打开一个现有文件。
 *  @param[out]  FileHandle 指向接收文件句柄的 HANDLE 变量的指针。
 *  @param[in]   DesiredAccess 指定一个ACCESS_MASK值,该值确定对对象的请求访问权限。
 *  @param[in]   ObjectAttributes 文件路径
 *  @param[out]  IoStatusBlock 操作的结果,指向IO_STATUS_BLOCK结构的指针,该结构接收最终完成状态和有关所请求操作的其他信息
 *  @param[in]   AllocationSize 指向LARGE_INTEGER的指针,该LARGE_INTEGER包含创建或覆盖的文件的初始分配大小(以字节为单位)。
 *                              如果"分配大小"为 NULL,则未指定分配大小。如果未创建或覆盖任何文件,则忽略分配大小。
 *  @param[in]   FileAttributes 指定一个或多个FILE_ATTRIBUTE_XXX 标志,这些标志表示在创建或覆盖文件时要设置的文件属性。
 *                              调用方通常指定FILE_ATTRIBUTE_NORMAL,从而设置默认属性。
 *  @param[in]   ShareAccess 创建/打开这个文件的共享访问的类型,指定为零或以下标志的任意组合。共享读|共享写|共享删除,如果设为0,则进程以独占的方式打开这个文件,
 *                           其他进程没办法再打开它,也就没办法删除它,但还是有其他办法强删的
 *  @param[in]   CreateDisposition 指定在文件存在或不存在时要执行的操作。FILE_OPEN_IF,存在则打开这个文件,不存在则创建这个文件
 *                                 360在处理文件创建拦截的时候,曾经在FILE_OPEN_IF出现过漏洞,流氓软件生成仿造系统软件的图标诱导
 *                                 用户点击,给网站导流获取收益。因为当时360考虑到大部分文件都是以FILE_OPEN、FILE_OPEN_IF方式打开的,
 *                                 如果监控会拖慢系统性能,后来还是把这个标志加入到监控中来了
 *  @param[in]   CreateOptions 指定驱动程序创建或打开文件时要应用的选项。同步操作的标志、普通文件标志、文件夹标志
 *  @param[in]   EaBuffer 对于设备和中间驱动程序,此参数必须是 NULL 指针。
 *  @param[in]   EaLength 对于设备和中间驱动程序,此参数必须为零。
 *  @return      ntStatus NtCreateFile 在成功时返回 STATUS_SUCCESS,或在失败时返回相应的 NTSTATUS 错误代码。在后一种情况下,
 *                        调用方可以通过检查 IoStatusBlock 参数来确定失败的原因。
 *  @see         https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntcreatefile
 *  @author      cisco(微信公众号:坚毅猿)
 *  @date        2022-02-24 22:09
 */
ZwCreateFile(
_Out_ PHANDLE FileHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER AllocationSize,
_In_ ULONG FileAttributes,
_In_ ULONG ShareAccess,
_In_ ULONG CreateDisposition,
_In_ ULONG CreateOptions,
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
_In_ ULONG EaLength
);
ZwWriteFile
  • ZwWriteFile写文件,是相对于应用层来说,数据流向:r3->R0
ZwReadFile
  • ZwReadFile读文件,是相对于应用层来说,数据流向:r0->R3
ZwQuerylnformationFile
  • ZwQuerylnformationFile查询文件信息
ZwQueryFullAttributesFile
  • ZwQueryFullAttributesFile查询文件完整信息
ZwSetinformationFile
  • ZwSetinformationFile设置文件信息 -> irp_mj_set_information
    • 在这里还有两个重要的Irp(重命名和删除,Major)
ZwClose
  • ZwClose关闭文件
ZwQueryDirectoryFile
  • ZwQueryDirectoryFile遍历文件夹
    • ./当前目录
    • ..父目录

注册表

强制删除文件,文件注册表穿越

内核的多线程

内核中的数据结构(代替递归)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348