返回
顶部

references:

在代码中我们可以看到他是这样获取ioctl的起始和终止范围的:

beginIoctl = (DWORD)parseHex(singleIoctl) & 0xffffc000;
endIoctl = ((DWORD)parseHex(singleIoctl) & 0xffffc000) | 0x00003fff;

那他为什么这样写呢,这就和ioctl的编码方式有关了

Bits Field Meaning
31–16 DeviceType Type of device
15–14 Access Required access (read, write, etc.)
13–2 Function Function code (custom or system)
1–0 Method Transfer method (buffered, direct, etc)

可以看到从14bit开始就是不会变化的东西了,他们是devicetype和access,这个我们不用管,我们只用爆破低14bit即可,0xffffc000就是低14bit为0的一个值,和他进行与操作就可以把低14bit置0,就得到了起始地址,终止地址就是把低14bit置1,即0x3fff

其实这个ioctl fuzz的原理非常简单,就是确定一个起始和终止范围,然后循环测试每一个iocode,得到每一个iocode对应的input buffer的最小和最大长度,因为deviceiocontrol函数会在正常的时候返回非0值,我们只需要循环测试每一个输入buffer长度并检测返回值即可

注意

并不是所有的驱动都会在失败的时候返回0,存在一些驱动,不管在什么情况下返回的status都不为0

ioctlfuzzer会在filteralwaysok选项开启的时候过滤掉这些iocode,过滤原理就是下面这个循环

for (j = 0; j < 4 && status != 0 && cont; j++) {
                status = DeviceIoControl(deviceHandle,
                    currentIoctl,
                    &bufInput,
                    j,
                    &bufOutput,
                    j,
                    &nbBytes,
                    NULL);

                /*
                if(status == 0)
                    printf("0x%08x (size %d) -> error code %03d \n", currentIoctl, j, GetLastError());
                else
                    printf("0x%08x (size %d) -> status != 0 \n", currentIoctl, j);
                */

}
if (j == 4) {
                //printf("Skip 0x%08x\n", currentIoctl);
                continue;
}

他循环5次,这5次循环中,inputlength依次为0~5,如果5次全部都不为0,说明这个驱动在任何条件下都会返回status!=0

此时就跳过对该IOCODE的input length的测试,我在这个地方添加了一个pause指令,以便引起我的注意,我需要调试看一下,下一步会产生何种反应

另外一点就是,原版的fuzz代码会在fuzz开始前要求我们去指定一个iocode,但是我给他改成直接就从起始范围开始测,如果崩溃了,我们就去调试器分析即可

项目文件

密码是1