前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Windows Kernel-crash 回调(写入有效的二次数据)

Windows Kernel-crash 回调(写入有效的二次数据)

原创
作者头像
franket
发布2020-05-10 13:32:09
12.9K0
发布2020-05-10 13:32:09
举报
文章被收录于专栏:技术杂记技术杂记

在写驱动代码时,总是难免会崩溃,所以经常要使用到crash回调函数

代码语言:javascript
复制
BOOLEAN
KeRegisterBugCheckReasonCallback (
	__out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
	__in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
	__in KBUGCHECK_CALLBACK_REASON Reason,
	__in PUCHAR Component
	);

我们可以看下源码模拟:

代码语言:javascript
复制
NTKERNELAPI
BOOLEAN
KeRegisterBugCheckReasonCallback (
	__out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
	__in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
	__in KBUGCHECK_CALLBACK_REASON Reason,
	__in PUCHAR Component
	)
{
	BOOLEAN Inserted;
	KIRQL OldIrql;
 
	// 提权到HIGH_LEVEL(15!),并请求KeBugCheckCallbackLock自旋锁
	KeRaiseIrql(HIGH_LEVEL, &OldIrql);
	KiAcquireSpinLock(&KeBugCheckCallbackLock);
 
	// CallbackRecord->State == BufferEmpty表示未注册
	Inserted = FALSE;
	if (CallbackRecord->State == BufferEmpty) {
		CallbackRecord->CallbackRoutine = CallbackRoutine;
		CallbackRecord->Reason = Reason;
		CallbackRecord->Component = Component;
		CallbackRecord->Checksum =
			((ULONG_PTR)CallbackRoutine + Reason + (ULONG_PTR)Component);
 
		CallbackRecord->State = BufferInserted;
 
		//把当前的Record压栈,LOFT调用
		InsertHeadList(&KeBugCheckReasonCallbackListHead,
					   &CallbackRecord->Entry);
		Inserted = TRUE;
	}
 
	// 释放锁,恢复原有的IRQL,返回CallbackRecord是否成功注册
	KiReleaseSpinLock(&KeBugCheckCallbackLock);
	KeLowerIrql(OldIrql);
 
	return Inserted;
}

可以看到这个函数运行的IRQL提到了15!

CallbackRecord

这个明显是主角,比对上面源码,

可以发现这个结构中的State有以下状态:

代码语言:javascript
复制
typedef enum _KBUGCHECK_BUFFER_DUMP_STATE {
	BufferEmpty,//未注册
	BufferInserted,//已注册
	BufferStarted,//开始
	BufferFinished,//结束
	BufferIncomplete//未完成
} KBUGCHECK_BUFFER_DUMP_STATE;

CallbackRoutine

为回调函数原型,具体由Reason的类型来确定

Reason

KbCallbackSecondaryDumpData 指定要写入二次数据,CallbackRoutine此时为BugCheckSecondaryDumpDataCallback

Component:

指定一个字符串标识,一般使用驱动名或设备名即可

示例:

代码语言:javascript
复制
lpCallbackRecord = (PKBUGCHECK_REASON_CALLBACK_RECORD)ExAllocatePoolWithTag(NonPagedPool,1024,'d00g');
	KeInitializeCallbackRecord(lpCallbackRecord);
 
	UCHAR pComponent[] = "DriverTest";
	if(KeRegisterBugCheckReasonCallback(lpCallbackRecord,&BugCheckSecondaryDumpDataCallback,
		KbCallbackSecondaryDumpData,pComponent))
	{
		KdPrint(("KeRegisterBugCheckReasonCallback ok\n"));
	}

回过来看下回调BugCheckSecondaryDumpDataCallback:

代码语言:javascript
复制
VOID BugCheckSecondaryDumpDataCallback(KBUGCHECK_CALLBACK_REASON  Reason,struct _KBUGCHECK_REASON_CALLBACK_RECORD  *Record,PVOID  ReasonSpecificData,ULONG  ReasonSpecificDataLength)
{
//Reason为KbCallbackSecondaryDumpData
//Record为被注册的那货!
//ReasonSpecificData为_KBUGCHECK_SECONDARY_DUMP_DATA ---唯一有用的
//ReasonSpecificDataLength=sizoof(_KBUGCHECK_SECONDARY_DUMP_DATA)
}

回调这个在MSDN写得极为难懂(简要对译了下):

代码语言:javascript
复制
The system uses BugCheckSecondaryDumpDataCallback routines to poll drivers for crash dump data.
系统使用BugCheckSecondaryDumpDataCallback函数来给驱动轮询崩溃dump数据
The system sets the InBuffer, InBufferLength, OutBuffer, and MaximumAllowed members of the KBUGCHECK_SECONDARY_DUMP_DATA structure that ReasonSpecificData points to. The MaximumAllowed member specifies the maximum amount of dump data the routine can provide.
系统设置ReasonSpecificData 指向的KBUGCHECK_SECONDARY_DUMP_DATA 结构体中成员InBuffer, InBufferLength, OutBuffer, 和MaximumAllowed,成员MaximumAllowed 指定了这个函数能提供的dump数据的最大值
The value of the OutBuffer member determines whether the system is requesting the size of the driver’s dump data, or the data itself, as follows:
成员OutBuffer 的值决定了系统是在请求驱动的dump数据的大小,还是数据自身,如下:
If the OutBuffer member of KBUGCHECK_SECONDARY_DUMP_DATA is NULL, the system is only requesting size information. The BugCheckSecondaryDumpDataCallback routine fills in the OutBuffer and OutBufferLength members.
如果KBUGCHECK_SECONDARY_DUMP_DATA 的成员OutBuffer 为空,系统仅仅是请求大小信息,BugCheckSecondaryDumpDataCallback 填充成员OutBuffer 和OutBufferLength
If the OutBuffer member of KBUGCHECK_SECONDARY_DUMP_DATA equals the InBuffer member, the system is requesting the driver’s secondary dump data. The BugCheckSecondaryDumpDataCallback routine fills in the OutBuffer and OutBufferLength members, and writes the data to the buffer specified by OutBuffer.
如果KBUGCHECK_SECONDARY_DUMP_DATA 的成员OutBuffer 等于InBuffer ,系统在请求驱动的二次dump数据,BugCheckSecondaryDumpDataCallback 填充成员OutBuffer 和OutBufferLength ,并且写入指定的数据到OutBuffer.
The InBuffer member of KBUGCHECK_SECONDARY_DUMP_DATA points to a small buffer for the routine’s use. The InBufferLength member specifies the size of the buffer. If the amount of data to be written is less than InBufferLength, the callback routine can use this buffer to supply the crash dump data to the system. The callback routine then sets OutBuffer to InBuffer and OutBufferLength to the actual amount of data written to the buffer.
这个InBuffer 成员指向函数使用的一小份Buffer,InBufferLength成员指定了buffer的大小,如果写入的二次数据小于InBufferLength, 这个回调函数能使用这个buffer写入二次数据到dump data,然后这个回调函数设置OutBuffer 指向InBuffer ,并把OutBufferLength 设置为实际写入长度
A driver that must write an amount of data that is larger than InBufferLength can use its own buffer to provide the data. This buffer must have been allocated before the callback routine is executed, and must reside in resident memory (such as nonpaged pool). The buffer must be page-aligned. The callback routine then sets OutBuffer to point to the driver’s buffer, and OutBufferLength to the amount of data in the buffer to be written to the crash dump file.
一个驱动如果要写入大于InBufferLength 的数据,必须使用它自己的buffer,这个buffer必须在回调函数之前分配,并且必须是非分页的,然后回调函数设置OutBuffer 指向驱动的buf,OutBufferLength指向 要写入的buf数据

意思差不多就是:

1.它会轮询两次

2.第一次是请求大小

3.第二次是让你写入数据

规则基本如下:(试了几十次蓝屏测试!)

1.第一次总是使用如下代码来赋值即可:

代码语言:javascript
复制
Data->OutBuffer = Data->InBuffer;
Data->OutBufferLength = Data->InBufferLength; //无论第二次OutBufferLength是否大于InBufferLength,这次都要写等于,不然就到不了第二次,我靠!!

注意点:

1.尝试过在第一次改变InBufferLength的值,发现走不到第二次了!2.尝试过第一次改变MaximumAllowed的值,走第二次时又变回来了!3.OutBuffer必须赋值,但无论赋什么值,在第二次都指向InBuffer!

2.第二次到来时,OutBuffer总是指向InBuffer,这可以从MSDN看出

注意点:1.如果OutBuffer不改变指向,最多只能写InBufferLength大小的数据!2.如果OutBuffer改变指向,指向新的已分配内存(此内存必须提前分配,无分页的),则可以写最多MaximumAllowed大小的数据!3.OutBufferLength必须赋值!

4.测试时不要使用.crash命令,只会蓝屏,不会走到回调函数里

回调示例如下:

代码语言:javascript
复制
VOID  BugCheckSecondaryDumpDataCallback(KBUGCHECK_CALLBACK_REASON  Reason,struct _KBUGCHECK_REASON_CALLBACK_RECORD  *Record,PVOID  ReasonSpecificData,ULONG  ReasonSpecificDataLength)
{
	KBUGCHECK_SECONDARY_DUMP_DATA*  Data=(KBUGCHECK_SECONDARY_DUMP_DATA*)ReasonSpecificData;
	Data->Guid={0};//GUID是唯一标识
	ULONG InBufferLength=Data->InBufferLength;
	if (NULL == Data->OutBuffer)
	{
		Data->OutBuffer = Data->InBuffer;
		Data->OutBufferLength = Data->InBufferLength;
	}
	else if(Data->OutBuffer)//==Data->InBuffer
	{
		memset(Data->OutBuffer, 0x41,Data->InBufferLength);
		Data->OutBufferLength=Data->InBufferLength;
	}
}

具体可在dump中使用.enumtag查看:(也可通过扩展dll来实现,可以参看windbg扩展)

如下图效果:

enumtag.png
enumtag.png

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档