Windows內核模糊測試之IoControl Fuzz

一、測試原理
在Windows中,用戶層和內核層通過DeviceIoControl來實現通信得,該函數定義如下:
BOOL WINAPI DeviceIoControl( __in HANDLE hDevice, __in DWORD dwIoControlCode, __in_opt LPVOID lpInBuffer, __in DWORD nInBufferSize, __out_opt LPVOID lpOutBuffer, __in DWORD nOutBufferSize, __out_opt LPDWORD lpBytesReturned, __inout_opt LPOVERLAPPED lpOverlapped);
DeviceIoControl函數最后會通過內核函數NtDeviceIoControlFile來實現通信,該函數定義如下:
NTSTATUS NtDeviceIoControlFile( IN HANDLE FileHandle, IN HANDLE Event, IN PIO_APC_ROUTINE ApcRoutine, IN PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, IN PVOID InputBuffer, IN ULONG InputBufferLength, OUT PVOID OutputBuffer, IN ULONG OutputBufferLength );
由于這兩個函數包含了用戶層和內核層通信的所有需要的數據,因此可以通過對這兩個函數進行操作來實現驅動程序的模糊測試。根據實現模糊測試器實現的方法不同,可以分為以下兩類:
- IoControl MITM(Man-in-the-Middle) Fuzz
- IoControl Driver Fuzz
二、IoControl MITM(Man-in-the-Middle) Fuzz
下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。
這樣,當用戶層與內核層發生通信的時候,我們可以監控到這個操作并且可以對其中的輸入輸出數據進行修改,在將修改以后的數據傳遞給原始的NtDeviceIoControlFile函數。
如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。

下圖則是該方法的數據變異策略,主要是對輸入地址,輸入數據,輸入長度,輸出地址和輸出長度進行變異。

三、IoControl Driver Fuzz
上述方法無法主動對驅動進行測試,只能被動地等待相應地通信發生,然后在對數據進行變異。想要主動地對相應派遣函數進行調用,就需要首先通過逆向地手段獲得驅動的設備名稱以及派遣函數對應的IoControlCode,接著對數據進行變異以后通過主動調用DeviceIoControl函數來完成測試。
根據IoControlCode中Method值的不同,數據變異策略有以下兩種情況:
- Method != METHOD_NEITHER:由于輸入輸出都有系統保護,因此修改地址沒有意義,需要變異的數據只有:輸入數據,輸入長度,輸出長度。
- Method == NMETHOD_NEITHER:驅動中可能直接訪問輸入輸出地址,而沒有探測是否可寫,因此需要變異的數據有:輸入地址,輸入數據,輸出地址,輸出長度。
以下是數據的變異策略:

四、IoctlFuzzer部分源碼介紹
這個模糊測試器的源碼在:https://github.com/Cr4sh/ioctlfuzzer/,該模糊測試器就是通過HOOK NtDeviceIoControlFile函數來構建的IoControl MITM Fuzzer,HOOK代碼如下:
old_NtDeviceIoControlFile = (NT_DEVICE_IO_CONTROL_FILE)InterlockedExchange( (PLONG)&SYSTEM_SERVICE(m_SDT_NtDeviceIoControlFile), (LONG)new_NtDeviceIoControlFile );
在new_NtDeviceIoControlFile中會首先判斷先前模式是否是用戶模式,如果是內核模式就直接調用原函數繼續運行。
NTSTATUS NTAPI new_NtDeviceIoControlFile( HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength){ KPROCESSOR_MODE PrevMode = ExGetPreviousMode(); BOOLEAN bLogOutputBuffer = FALSE; // handle only user mode calls if (PrevMode != KernelMode) { // 省略部分代碼 } // 內核模式,則調用原函數 NTSTATUS status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength ); return status;}
如果先前模式是用戶模式,調用ObReferenceObjectByHandle獲取句柄的對象:
POBJECT_NAME_INFORMATION DeviceObjectName = NULL, DriverObjectName = NULL; PFILE_OBJECT pFileObject = NULL; // get device object by handle NTSTATUS ns = ObReferenceObjectByHandle( FileHandle, 0, 0, KernelMode, (PVOID *)&pFileObject, NULL );
接著驗證一波設備對象和驅動對象的有效性:
PVOID pDeviceObject = NULL; // validate pointer to device object if (MmIsAddressValid(pFileObject->DeviceObject)) { pDeviceObject = pFileObject->DeviceObject; } else { goto end; } if (pDeviceObject == m_DeviceObject) { // don't handle requests to our driver goto end; } // validate pointer to driver object if (!MmIsAddressValid(pFileObject->DeviceObject->DriverObject)) { goto end; } // get loader information entry for the driver module PLDR_DATA_TABLE_ENTRY pModuleEntry = (PLDR_DATA_TABLE_ENTRY) pFileObject->DeviceObject->DriverObject->DriverSection; if (pModuleEntry == NULL) { goto end; } // validate pointer to loader's table and data from it if (!MmIsAddressValid(pModuleEntry) || !ValidateUnicodeString(&pModuleEntry->FullDllName)) { goto end; }
在輸入滿足條件的時候,會繼續調用Fuzz_NtDeviceIoControlFile函數來進行測試。
if (InputBuffer != NULL && InputBufferLength > 0 && (m_FuzzOptions & FUZZ_OPT_FUZZ) && bProcessEvent) { // fuzz this request Fuzz_NtDeviceIoControlFile( PrevMode, &DeviceObjectName->Name, FileHandle, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength ); }
對于Fuzz_NtDeviceIoControlFile函數,該函數首先判斷全局變量m_FuzzIotions是否有FUZZ_OPT_FUZZ_FAIR標志,如果沒有就會調用FuzzContinue_NtDeviceIoControlFile函數完成測試。
void Fuzz_NtDeviceIoControlFile( KPROCESSOR_MODE PrevMode, PUNICODE_STRING usDeviceName, HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength){ MAPPED_MDL InBuffMapped, OutBuffMapped; KAPC_STATE ApcState; BOOLEAN bInBuffMapped = FALSE, bOutBuffMapped = FALSE, bNeedToDetach = FALSE; PVOID TmpInputBuffer = NULL, TmpOutputBuffer = NULL; // save original parameters from the nt!NtDeviceIoControlFile() FUZZ_THREAD_PARAMS ThreadParams; ThreadParams.PrevMode = PrevMode; ThreadParams.hFuzzHandle = FileHandle; ThreadParams.cIoStatusBlock = IoStatusBlock; ThreadParams.IoControlCode = IoControlCode; ThreadParams.cInputBuffer = InputBuffer; ThreadParams.cOutputBuffer = OutputBuffer; ThreadParams.cInputBufferLength = InputBufferLength; ThreadParams.cOutputBufferLength = OutputBufferLength; if (m_FuzzOptions & FUZZ_OPT_FUZZ_FAIR) { // 省略部分代碼 } else { /** * Sending IOCTL's from context of the original process. */ FuzzContinue_NtDeviceIoControlFile( PrevMode, ThreadParams.hFuzzHandle, NULL, NULL, NULL, ThreadParams.cIoStatusBlock, IoControlCode, ThreadParams.cInputBuffer, ThreadParams.cInputBufferLength, ThreadParams.cOutputBuffer, ThreadParams.cOutputBufferLength ); } // 省略部分代碼}
如果帶有FUZZ_OPT_FUZZ_FAIR標記,那么程序附加到模糊測試器的進程中,并通過APC機制來完成模糊測試。
if (m_FuzzProcess && m_FuzzThreadId && m_UserModeData) { if (InputBuffer != NULL && InputBufferLength > 0) { // 保存輸入數據 if (TmpInputBuffer = M_ALLOC(InputBufferLength)) { memcpy(TmpInputBuffer, InputBuffer, InputBufferLength); } } if (OutputBuffer != NULL && OutputBufferLength > 0) { // 保存輸出數據 if (TmpOutputBuffer = M_ALLOC(OutputBufferLength)) { memcpy(TmpOutputBuffer, OutputBuffer, OutputBufferLength); } } // 附加到模糊測試器進程 KeStackAttachProcess(m_FuzzProcess, &ApcState); if (InputBuffer != NULL && InputBufferLength > 0) { // 在傳遞的參數中保存輸入數據 if (bInBuffMapped = AllocateUserMemory(InputBufferLength, &InBuffMapped)) { ThreadParams.cInputBuffer = InBuffMapped.MappedBuffer; memcpy(ThreadParams.cInputBuffer, TmpInputBuffer, InputBufferLength); } } if (OutputBuffer != NULL && OutputBufferLength > 0) { // 在傳遞的參數中保存輸出數據 if (bOutBuffMapped = AllocateUserMemory(OutputBufferLength, &OutBuffMapped)) { ThreadParams.cOutputBuffer = OutBuffMapped.MappedBuffer; memcpy(ThreadParams.cOutputBuffer, TmpOutputBuffer, OutputBufferLength); } } PETHREAD Thread = NULL; KAPC Apc; // 獲取模糊測試器線程對象 ns = PsLookupThreadByThreadId(m_FuzzThreadId, &Thread); if (NT_SUCCESS(ns)) { // 初始化事件對象 KeInitializeEvent( &ThreadParams.OperationComplete, NotificationEvent, FALSE ); // 初始化APC對象 KeInitializeApc( &Apc, (PKTHREAD)Thread, OriginalApcEnvironment, ApcKernelRoutine, NULL, ApcNormalRoutine, KernelMode, &ThreadParams ); // 插入APC隊列 if (KeInsertQueueApc(&Apc, NULL, NULL, 0)) { // waiting for APC execution KeWaitForSingleObject( &ThreadParams.OperationComplete, Executive, KernelMode, FALSE, NULL ); } } }
而要執行的APC函數也是通過FuzzContinue_NtDeviceIoControlFile函數完成測試。
VOID ApcNormalRoutine( PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2){ PFUZZ_THREAD_PARAMS ThreadParams = (PFUZZ_THREAD_PARAMS)NormalContext; // continue fuzzing FuzzContinue_NtDeviceIoControlFile( ThreadParams->PrevMode, ThreadParams->hFuzzHandle, NULL, NULL, NULL, ThreadParams->cIoStatusBlock, ThreadParams->IoControlCode, ThreadParams->cInputBuffer, ThreadParams->cInputBufferLength, ThreadParams->cOutputBuffer, ThreadParams->cOutputBufferLength ); KeSetEvent(&ThreadParams->OperationComplete, 0, FALSE); } VOID ApcKernelRoutine( struct _KAPC *Apc, PKNORMAL_ROUTINE *NormalRoutine, PVOID *NormalContext, PVOID *SystemArgument1, PVOID *SystemArgument2) { /** * This code exeuting in context of the fuzzer's process at APC_LEVEL. * Nothing to do here... */}
真正完成測試的是在FuzzContinue_NtDeviceIoControlFile函數中完成的,變異策略步驟如下:
- 對輸入數據按1字節或4字節為單位進行變異,其中按一字節進行變異的時候,也會變異輸入數據的長度;
- 對輸出數據的地址進行變異;
- 如果通信方式不是METHOD_BUFFERED,則會對輸入輸出的地址與長度進行變異。
具體代碼如下:
void FuzzContinue_NtDeviceIoControlFile( KPROCESSOR_MODE PrevMode, HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength){ // 分配輸入數據長度的內存空間 PUCHAR NewBuff = (PUCHAR)M_ALLOC(InputBufferLength); // 分配成功,則先對輸入數據進行測試 if (NewBuff) { // 保存輸入數據 RtlCopyMemory(NewBuff, InputBuffer, InputBufferLength); // 對輸入數據每個字節進行隨機化 if (m_FuzzingType == FuzzingType_Random) { for (int i = 0; i < RANDOM_FUZZING_ITERATIONS; i++) { ULONG TmpInputLength = InputBufferLength; // 對輸入數據長度進行變異 if (m_FuzzOptions & FUZZ_OPT_FUZZ_SIZE) { TmpInputLength = getrand(1, TmpInputLength * 4); } // 對輸入數據的每個字節進行變異 for (ULONG s = 0; s < InputBufferLength; s++) { *((PUCHAR)InputBuffer + s) = (UCHAR)getrand(1, 0xff); } // 調用原函數 NTSTATUS status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, TmpInputLength, OutputBuffer, OutputBufferLength ); } } else if (m_FuzzingType == FuzzingType_Dword) { // 以4字節為單位變異輸入數據 // 對其輸入數據長度 ULONG FuzzingLength = XALIGN_DOWN(InputBufferLength, sizeof(ULONG)); if (FuzzingLength <= DWORD_FUZZING_MAX_LENGTH && FuzzingLength >= sizeof(ULONG)) { // fuzz each dword value in input buffer for (ULONG i = 0; i < FuzzingLength; i += DWORD_FUZZING_DELTA) { for (ULONG i_v = 0; i_v < sizeof(m_DwordFuzzingConstants) / sizeof(ULONG); i_v++) { // 變異輸入數據 ULONG OldBuffVal = *(PULONG)((PUCHAR)InputBuffer + i); *(PULONG)((PUCHAR)InputBuffer + i) = m_DwordFuzzingConstants[i_v]; // 調用原函數 NTSTATUS status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength ); // 恢復原來的數據 *(PULONG)((PUCHAR)InputBuffer + i) = OldBuffVal; } } } } // 恢復輸入數據 RtlCopyMemory(InputBuffer, NewBuff, InputBufferLength); ExFreePool(NewBuff); } // 對輸出數據進行變異 if (OutputBufferLength > 0) { // 將輸出數據地址改成用戶空間的一個地址 PVOID TmpOutputBuffer = USER_BUFFER_ADDRESS; // 調用原函數 NTSTATUS status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, TmpOutputBuffer, 0 ); // 將輸出數據地址改成內核空間的一個地址 TmpOutputBuffer = KERNEL_BUFFER_ADDRESS; // 調用原函數 status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, TmpOutputBuffer, 0 ); } // 判斷通信方式 ULONG Method = IoControlCode & 3; if (Method != METHOD_BUFFERED) { // try to fuzz buffer addresses, if method is not buffered for (int i = 0; i < BUFFERED_FUZZING_ITERATIONS; i++) { // 變異輸入,輸出地址與長度,其中地址變異為用戶空間的地址 PVOID TmpInputBuffer = USER_BUFFER_ADDRESS; PVOID TmpOutputBuffer = USER_BUFFER_ADDRESS; ULONG TmpInputBufferLength = getrand(0, 0x100); ULONG TmpOutputBufferLength = getrand(0, 0x100); // 調用原函數 NTSTATUS status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, TmpInputBuffer, TmpInputBufferLength, TmpOutputBuffer, TmpOutputBufferLength ); } for (int i = 0; i < BUFFERED_FUZZING_ITERATIONS; i++) { // 變異輸入,輸出地址與長度,其中地址變異為內核空間的地址 PVOID TmpInputBuffer = KERNEL_BUFFER_ADDRESS; PVOID TmpOutputBuffer = KERNEL_BUFFER_ADDRESS; ULONG TmpInputBufferLength = getrand(0, 0x100); ULONG TmpOutputBufferLength = getrand(0, 0x100); // 調用原函數 NTSTATUS status = old_NtDeviceIoControlFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, TmpInputBuffer, TmpInputBufferLength, TmpOutputBuffer, TmpOutputBufferLength ); } }}
其中的一些關鍵的宏定義如下:
#define RANDOM_FUZZING_ITERATIONS 10#define BUFFERED_FUZZING_ITERATIONS 5#define DWORD_FUZZING_MAX_LENGTH 0x200#define DWORD_FUZZING_DELTA 4 #ifdef _X86_ #define KERNEL_BUFFER_ADDRESS (PVOID)(0xFFFF0000)#define USER_BUFFER_ADDRESS (PVOID)(0x00001000) #elif _AMD64_ #define KERNEL_BUFFER_ADDRESS (PVOID)(0xFFFFFFFFFFFF0000)#define USER_BUFFER_ADDRESS (PVOID)(0x0000000000001000) #endif // constants for dword fuzzingULONG m_DwordFuzzingConstants[] ={ 0x00000000, 0x00001000, 0xFFFF0000, 0xFFFFFFFF};
五、IoctlBf部分源碼
這個模糊測試器源碼在:https://github.com/koutto/ioctlbf,該模糊測試器則是IoControl Driver Fuzzer。因此,該模糊測試器會首先獲取驅動的合法IoControlCode,然后在對這些合法的IoControlCode進行測試。
保存合法IoControl的結構體定義如下:
typedef struct IOCTLlist_ { DWORD IOCTL; // 保存的IoControlCode DWORD errorCode; size_t minBufferLength; // 輸入輸出數據最小長度 size_t maxBufferLength; // 輸入輸出數據最大長度 struct IOCTLlist_ *previous; // 指向上一個IOCTLlist_結構,用來連接所有的合法IoControlCode} IOCTLlist, *pIOCTLlist;
在獲取合法IoControlCode的代碼中,beginIoControl和endIoCtl是由用戶指定的,用來指定要測試的IoControlCode范圍,然后從該范圍中一一測試其對應的IoControlCode一一 測試。
測試步驟如下:
- 指定輸入輸出地址為NULL,調用DeviceIoControl。如果函數返回值為0,且GetLastError()的結果為ERROR_ACCESS_DENIED或ERROR_NOT_SUPPORT中的一個,則該IoControlCode不合法,結束本次循環;
- 判斷是否指定了filteralwaysok標志,如果指定了,則測試輸入輸出的數據是否大于4,如果是則結束本次循環;
- 將輸入輸出長度從0到MAX_BUFSIZE一一測試,來查找輸入輸出數據最小長度,成功查找,則將其作為一個合法的IoControlCode,加入listIoctls中;
- 從輸入輸出數據的最小長度+1開始測試,獲取輸入輸出數據的最大長度。
具體代碼實現如下:
for(currentIoctl = beginIoctl; currentIoctl<=endIoctl; currentIoctl++) { // 發送IoControl status = DeviceIoControl(deviceHandle, currentIoctl, NULL, 0, NULL, 0, &nbBytes, NULL); // 如果函數調用失敗 if(status == 0) { errorCode = GetLastError(); // 如果GetLastError的值為ERROR_ACCESS_DENIED或ERROR_NOT_SUPPORTED // 則沒有此IoControlCode,結束本次循環 if(errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_NOT_SUPPORTED) { continue; } } // 是否指定了filteralwaysok標志 if(filteralwaysok) { // 指定輸入輸出緩沖區為最大長度,發送IoControlCode status = DeviceIoControl(deviceHandle, currentIoctl, &bufInput, MAX_BUFSIZE, &bufOutput, MAX_BUFSIZE, &nbBytes, NULL); if(status != 0) { cont = TRUE; status = 1; for(j = 0; j < 4 && status != 0 && cont; j++) { status = DeviceIoControl(deviceHandle, currentIoctl, &bufInput, j, &bufOutput, j, &nbBytes, NULL); } if(j == 4) { // 輸入輸出長度均>4 則結束本次循環 continue; } } } // 查找最小輸入輸出長度 cont = TRUE; for(j = 0; j < MAX_BUFSIZE && cont; j++) { status = DeviceIoControl(deviceHandle, currentIoctl, &bufInput, j, &bufOutput, j, &nbBytes, NULL); if(status != 0) { // 如果發送成功,則將其加入到listIoctls鏈表 listIoctls = addIoctlList(listIoctls, currentIoctl, 0, j, MAX_BUFSIZE); cont = FALSE; i++; } } // 找到了最小數據長度,接下來查找最長數據產犢 if(!cont) { cont = TRUE; // 發送限制的最長的數據長度 status = DeviceIoControl(deviceHandle, currentIoctl, &bufInput, MAX_BUFSIZE, &bufOutput, MAX_BUFSIZE, &nbBytes, NULL); if(status != 0) { // 如果發送成功,則指定最長數據長度 listIoctls->maxBufferLength = MAX_BUFSIZE; cont = FALSE; } // 查找最長數據長度 for(j = listIoctls->minBufferLength + 1; j < MAX_BUFSIZE && cont; j++) { // 發送IoControlCode status = DeviceIoControl(deviceHandle, currentIoctl, &bufInput, j, &bufOutput, j, &nbBytes, NULL); // 發送成功,則更新最長數據長度 if(status == 0) { listIoctls->maxBufferLength = j - 1; cont = FALSE; } } if(cont) { listIoctls->maxBufferLength = MAX_BUFSIZE; } }}
其中的最大長度MAX_BUFSIZE定義如下:
#define MAX_BUFSIZE 4096 // Max length for input buffer
有了合法的IoControlCode,就可以進行測試,測試步驟如下:
- 如果IoControlCode的method不為METHOD_BUFFERED,則將輸入輸出數據地址指定為整型數組invalidAddress中保存的不合法的地址完成測試;
- 將輸入數據初始化為0x41,并指定不同的數據長度測試是否存在緩沖區溢出漏洞;
- 以4字節為單位,變異輸入數據挖出測試;
- 隨機化輸入數據,完成測試。
具體的代碼實現如下:
while(1) { // 從鏈表中獲取IoControl,choice由用戶輸入 posListIoctls = getIoctlListElement(listIoctls, choice); // 如果method != METHOD_BUFFERED,則將輸入輸出數據地址賦值為非法的地址進行測試 if((posListIoctls->IOCTL & 0x00000003) != 0) { cont = TRUE; for(i = 0; cont && i < INVALID_BUF_ADDR_ATTEMPTS; i++) { for(j = 0; cont && j < (sizeof(invalidAddresses) / 4); j++) { // 隨機化輸入輸出長度 randomLength = getrand(posListIoctls->minBufferLength, posListIoctls->maxBufferLength); // 將輸入輸出地址指定為不合法的地址 status = DeviceIoControl(deviceHandle, posListIoctls->IOCTL, (LPVOID)invalidAddresses[j], randomLength, (LPVOID)invalidAddresses[j], randomLength, &nbBytes, NULL); } } } // 測試是否存在緩沖區溢出漏洞 cont = TRUE; // 初始化輸入數據 memset(bufInput, 0x41, 0x10000); // 指定不同輸入數據長度,調用函數完成測試 for(i = 0x100; i <= 0x10000; i += 0x100) { status = DeviceIoControl(deviceHandle, posListIoctls->IOCTL, &bufInput, i, &bufOutput, i, &nbBytes, NULL); } // 以4字節為單位,變異輸入數據 cont = TRUE; if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) { // 初始化輸入數據 memset(bufInput, 0x00, MAX_BUFSIZE); // 以4字節為單位,將輸入數據修改為FuzzConstants整型數組中的某一元素 for(i = 0; cont && i < posListIoctls->maxBufferLength; i = i + 4) { // 隨機化輸入數據 for(j = 0; cont && j < posListIoctls->maxBufferLength; j++) { bufInput[j] = (BYTE)getrand(0x00, 0xff); } for(j = 0; cont && j < (sizeof(FuzzConstants) / 4); j++) { // 指定將輸入數據變異為FuzzConstants中的某一元素 fuzzData = FuzzConstants[j]; bufInput[i] = fuzzData & 0x000000ff; bufInput[i + 1] = (fuzzData & 0x0000ff00) >> 8; bufInput[i + 2] = (fuzzData & 0x00ff0000) >> 16; bufInput[i + 3] = (fuzzData & 0xff000000) >> 24; // 調用函數,開始測試 status = DeviceIoControl(deviceHandle, posListIoctls->IOCTL, &bufInput, posListIoctls->maxBufferLength, &bufOutput, posListIoctls->maxBufferLength, &nbBytes, NULL); } // 將輸入數據隨機賦值為FuzzConstants整型數組中的元素 while(cont) { // 隨機選取輸入數據長度 randomLength = getrand(posListIoctls->minBufferLength, posListIoctls->maxBufferLength); // 初始化輸入數據 memset(bufInput, 0x00, MAX_BUFSIZE); // 為輸入數據賦值 for(i = 0; i < randomLength; i = i + 4) { // 從FuzzConstants隨機選擇元素賦值輸入數據 fuzzData = FuzzConstants[getrand(0, (sizeof(FuzzConstants) / 4) - 1)]; bufInput[i] = fuzzData & 0x000000ff; bufInput[i + 1] = (fuzzData & 0x0000ff00) >> 8; bufInput[i + 2] = (fuzzData & 0x00ff0000) >> 16; bufInput[i + 3] = (fuzzData & 0xff000000) >> 24; } // 調用函數完成測試 status = DeviceIoControl(deviceHandle, posListIoctls->IOCTL, &bufInput, randomLength, &bufOutput, randomLength, &nbBytes, NULL); } } // 將輸入數據隨機初始完成測試 cont = TRUE; if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) { while(cont) { // 隨機選取輸入輸出數據長度 randomLength = getrand(posListIoctls->minBufferLength, posListIoctls->maxBufferLength); // 隨機化輸入數據 memset(bufInput, 0x00, MAX_BUFSIZE); for(i = 0; i < randomLength; i++) { bufInput[i] = (BYTE)getrand(0x00, 0xff); } // 調用函數完成測試 status = DeviceIoControl(deviceHandle, posListIoctls->IOCTL, &bufInput, randomLength, &bufOutput, randomLength, &nbBytes, NULL); } }}
其中的幾個全局變量定義如下:
// Junk data used for fuzzing -------------------------------------------------CHAR asciiString10[0x10];CHAR asciiString100[0x100];CHAR asciiString1000[0x1000]; WCHAR unicodeString10[0x10];WCHAR unicodeString100[0x100];WCHAR unicodeString1000[0x1000]; DWORD tableDwords[0x100]; DWORD FuzzConstants[] = { 0x00000000, 0x00000001, 0x00000004, 0xFFFFFFFF, 0x00001000, 0xFFFF0000, 0xFFFFFFFE, 0xFFFFFFF0, 0xFFFFFFFC, 0x70000000, 0x7FFEFFFF, 0x7FFFFFFF, 0x80000000, (DWORD)asciiString10, (DWORD)asciiString100, (DWORD)asciiString1000, (DWORD)unicodeString10, (DWORD)unicodeString100, (DWORD)unicodeString1000, (DWORD)tableDwords }; DWORD invalidAddresses[] = { 0xFFFF0000, 0x00001000 };