简单的学习并编写驱动程序
首先要改属性
注意:配置是所有配置,平台不知道为什么我这里只能x86
C/C++->常规->将警告视为错误->否
Driver Settings->Target OS Version->win7
SDL检查也关了
模板
1 |
|
helloworld
基本框架其实最开始就遇到了,在配置环境的时候简单解释,DriverUnload是自己完善的卸载函数,DiverEntry是驱动的主函数,也就是加载的时候就会被引用
参数一-pDiver是该驱动的”对象”,其实这个也好理解,pDriver->DriverUnload是给驱动挂载卸载函数,最终返回正确,第二个参数是安装路径,驱动安装的路径
printf平替是DbgPrintEx,前两个参数别管,后面跟着就好
1 |
|
链表
练习
1 |
|
断链
看懂了,但是感觉写起来很复杂,就是模拟了驱动所在的链表,可以通过链表找到想要断链隐藏的驱动(如果是自身的话要等后续内核对自己的调用结束之后,再断链,可以用休眠的api)蓝屏分析
练习
首先windbg支持tab自动补全,当蓝屏触发时,可以用!analyze -v指令来查看蓝屏信息,那么我们以该程序测试,这个给0地址赋值包崩溃的1 |
|
蓝屏通知:
1 | Fail to read system\currentcontrolset\services\Lmhosts\Parameters\EnableUserMode, error=2 |
可以看到这里他提示我!analyze了, 那我们输入进入
…跳转io通信里的蓝屏
存放地址
默认蓝屏信息存放地址:`C:\Windows\Minidump`IO通信
驱动对象->设备对象<----R0某个函数里 打包成irp------r3能根据名字查找到设备,并且返回一个操作句柄蓝屏分析中...
补药蓝屏口牙┭┮﹏┭┮,但是有意外发生了,他蓝屏了,接上文!an...分析一下
1
2
3
4
5
6
7
8
9
10
11
FAULTING_SOURCE_CODE:
65: RtlInitUnicodeString(&pdsym, DRIVER_SYM);
66:
67: //?????? ???¡§?¨¨¡À????¨®
68: PDRIVER_OBJECT pDevice = NULL;
> 69: NTSTATUS st = IoCreateDevice(pDriver, 0, &pdname, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, pDevice);
70:
71: if (NT_SUCCESS(st))
72: {
73: return STATUS_UNSUCCESSFUL;
74: }
1 | FAULTING_SOURCE_CODE: |
可以看到问题所在,IoCreateDevice
1 | nt!memcpy+0x33: |
再次观察发现是memcpy中赋值出现问题,后面求助ai知道,是pDriver传入的应该是&pDriver好吧。。。,那确实memcpy用到两个指针可能出现问题,最后找到问题了,前面的PUNICODE_STRING 是指针类型,要用UNICODE_STRING,后面就是简单的问题了,学到了可以dd st来查看报错
1 |
|
目前卸载还存在问题
简单通信代码模板
1 |
|
Io符号链接
符号链接:符号链接中用的前缀也就是\??也可以用\\.或者\dosDevice\来代替
api合集
字符串篇
`RtlInitAnsiString` ANSI_STRING初始化RtlInitUnicodeString(&target,Name_value)UNICODE_STRING初始化
RtlAnsiStringToUnicodeString 宽窄字符转换
RtlFreeUnicodeString UNICODE_STRING释放
链表篇
PLIST_ENTRY定义1 | typedef struct _LIST_ENTRY { |
InitializeListHead(a1);a1初始化:InsertHeadList(a1,a2);a2插入a1头部中:
InsertTailList(a1,a2);a2插入a1尾部中:
RemoveEntryList(a2);a2删除
RemoveHeadList(a2);//一般不用
Io篇
`IoCreateDevice();`函数参数比较长,对应着定义慢慢填吧IoCreateSymbolicLink(a1,a2);符号链接,a2生成快捷方式a1
IoDeleteDevice(pDevice);删除对象
IoDeleteSymbolicLink(a1);删除符号链接
IoCompleteRequest(a1,a2);a2是0,a1是Irp,还没有学到,根据名字猜测是完成请求Irp可能是请求
windbg的一些指令
`!analyze -v` 查看蓝屏相关信息k查看堆栈调用
kv查看堆栈调用详情
?? Driver->Object可以查看设备对象详情
!object Object_Addr可以查看设备地址
dt 链表名 下一个/上一个链表地址即可看结构
dt这个指令可用于看很多种结构,并且dt xxx -r1可以深入看结构下的几层结构
bp手动下断点(好用!!)
u查看目标地址字节码
Irp相关参数
Irp
`dt pIrp`首先查看Irp-r1展开AssociatedIrp ,通信函数传来的第二个参数在该部分SystemBuffer中
pIrp->AssociatedIrp.SystemBuffer
紧接着-r1展开Tail-r2展开Overlay,获得存储的结构体CurrentStackLocation
CurrentStackLocation
如果用Irp来调用到这里`pIrp->Tail.Overlay.CurrentStackLocation->`基础是这样很冗杂可以用函数
dt 0x876a8c40 _IO_STACK_LOCATION查看相关参数,MajorFunction是回调函数序号
pIrp->Tail.Overlay.CurrentStackLocation->MajorFunction
-r1展开Parameters找到DeviceIoControl-r2展开
1 | +0x000 DeviceIoControl : <unnamed-tag> |
获得相关参数,改变通信代码来查看对应参数
1 | ULONG x1 = 10; |
改变传参再次查看
1 | [db]: MajorFumction:14 |
如此遍明了DeviceIoControl传参如下
1. 设备句柄
2. IoControlCode
3. 传入参数 SystemBuffer
4. InputBufferLength
5. 返回参数
6. OutputBufferLength
改变返回参数
我的大致感觉就是,传入参数就像const,是不会改变了并且复制了一份在SystemBuffer中,后续如果对SystemBuffer操作,再回调函数结束后SystemBuffer的值会再次赋值给返回参数,从而做到返回注意点:
直接更改SystemBuffer是不会改变的需要有以下操作
1 | pIrp->IoStatus.Status = STATUS_SUCCESS; //成功 |
所以将回调函数改成如下样式
1 |
|
这里的IOCTL_MYDEVICE_OPERATION作用其实就是出了创建和销毁,剩下的回调函数基本都是这个,那么区分回调函数代码作用的自然就是传入的IoControlCode,控制码,这里和传入控制码进行switch筛选
通信代码
1 |
|
好的我们再捋一遍思路:
xin->SystemBuffer->SystemBuffer=0x520->xout=SystemBuffer->xout=0x520
运行一下试试
最终获取正确结果
ps:retLen的存放的是实际返回参数的大小,可以验证一下
Io通信小记
设备对象下的Flag标志位对应的参数与DeviceIoControl的第二个参数中METHOD_BUFFERED这类的标志是相似的,其规定再传参时,标志采用复制,映射还是直接索引实际地址所以在实践时会十分冗杂麻烦,火哥建议,先用一层结构体做建议Irp,类似通信传参的封装,在这个中引用真正参数的结构体,后续实践来更好理解(确实会如此,在我自己写计算器的时候,num参数因为sizeof的问题只传了一个参数,但是需要很繁杂的去dt dd来检索问题)
还有就是这样操作会减少蓝屏,毕竟你再怎么操作,都是对R0层结构体中指向的部分,操作。
有世外桃源的感觉了
最终练习代码
计算器
1 |
|
1 |
|
结构体传参练习
1 |
|
1 |
|
驱动加载
首先我的代码爆蓝屏了,总是蓝屏重启十分麻烦,所以第一个要学的就是异常捕捉异常
1 | __try |
像这样捕捉异常来避免蓝屏
分析
我的蓝屏发生在最后EntryPoint的地方,于是可以1 | DbgPrintEx(77, 0, "EntryPointFunc: %x\n", EntryPointFunc); |
打印进入点,然后下断点,方便后续在windbg中操作
1 | EntryPointFunc: 85854000 |
停住之后我们可以u 85854000来观察字节码
1 | kd> u 85854000 |
可以看到入口点是没有问题的,那么手动下断点运行过去
1 | kd> bp 85854000 |
最后发现是在85854010函数崩掉的,用ida查看源码
1 | INIT:00404000 8B FF mov edi, edi |
可以看到应该是在___security_init_cookie函数中崩了,进去发现
1 | mov dword ptr ds:[00403004h],eax |
最后的赋值重定位表没有正确定位,找到问题原因了,是重定位表写的有问题
排查发现计算获得重定位表基址的时候+的是PIMAGE…而不是IMAGE
PIMAGE_RELOC RelocationBlock = (PIMAGE_RELOC)((PUINT8)pImageReloc + sizeof(PIMAGE_BASE_RELOCATION));