Есть драйвер. В нём работает поток, поток сохраняет на диске другой драйвер, создаёт ключи в реестре и вызывает ZwLoadDriver. Если после этого поток завершается, система вылетает в BSOD. Если поток не завершать всё работает. С чем это связано? Может ядро выгружает второй драйвер автоматически после завершения моего потока?
В общем драйверы одинаковые. Оба аттачат фильтрующий девайс на файловую систему (точнее только на тот девайс, которому принадлежит SystemRoot). Если поток первого драйвера (из которого был запущен второй) завершается. Система падает (скорее всего при обращении к диску). Оба драйвера не выгружаемые. Оставляют фильтры вплоть до перезагрузки системы. IRQL_NOT_LESS_OR_EQUAL (a) An attempt was made to access a pageable (or completely invalid) address at an interrupt request level (IRQL) that is too high. This is usually caused by drivers using improper addresses. If a kernel debugger is available get the stack backtrace. Arguments: Arg1: 00000004, memory referenced Arg2: 00000002, IRQL Arg3: 00000000, bitfield : bit 0 : value 0 = read operation, 1 = write operation bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status) Arg4: 804ed966, address which referenced memory Debugging Details: ------------------ READ_ADDRESS: 00000004 CURRENT_IRQL: 2 FAULTING_IP: nt!CcFlushCache+6c 804ed966 8b7004 mov esi,dword ptr [eax+4] DEFAULT_BUCKET_ID: CODE_CORRUPTION BUGCHECK_STR: 0xA PROCESS_NAME: System TRAP_FRAME: f9e86c08 -- (.trap 0xfffffffff9e86c08) ErrCode = 00000000 eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=81782008 edi=00000000 eip=804ed966 esp=f9e86c7c ebp=f9e86ce8 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246 nt!CcFlushCache+0x6c: 804ed966 8b7004 mov esi,dword ptr [eax+4] ds:0023:00000004=???????? Resetting default scope LAST_CONTROL_TRANSFER: from 805328e7 to 804e3b25 STACK_TEXT: f9e867bc 805328e7 00000003 f9e86b18 00000000 nt!RtlpBreakWithStatusInstruction f9e86808 805333be 00000003 00000004 804ed966 nt!KiBugCheckDebugBreak+0x19 f9e86be8 804e2158 0000000a 00000004 00000002 nt!KeBugCheck2+0x574 f9e86be8 804ed966 0000000a 00000004 00000002 nt!KiTrap0E+0x233 f9e86ce8 804ee228 00000000 00000000 00000001 nt!CcFlushCache+0x6c f9e86d2c 804e54ad 81bfcd80 80561b40 81bcb640 nt!CcWriteBehind+0xdc f9e86d74 804e47fe 81bfcd80 00000000 81bcb640 nt!CcWorkerThread+0x126 f9e86dac 8057dfed 81bfcd80 00000000 00000000 nt!ExpWorkerThread+0x100 f9e86ddc 804fa477 804e4729 00000000 00000000 nt!PspSystemThreadStartup+0x34 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16 STACK_COMMAND: kb CHKIMG_EXTENSION: !chkimg -lo 50 -d !nt 80505881-80505884 4 bytes - nt!vDbgPrintExWithPrefix+fd [ 64 fe ff ff:2b 8b 59 79 ] 8056f76a-8056f76e 5 bytes - nt!NtEnumerateKey [ 6a 54 68 50 1b:e9 31 95 10 78 ] 9 errors : !nt (80505881-8056f76e) MODULE_NAME: memory_corruption IMAGE_NAME: memory_corruption FOLLOWUP_NAME: memory_corruption DEBUG_FLR_IMAGE_TIMESTAMP: 0 MEMORY_CORRUPTOR: LARGE FAILURE_BUCKET_ID: MEMORY_CORRUPTION_LARGE BUCKET_ID: MEMORY_CORRUPTION_LARGE Followup: memory_corruption ---------
DebugView установлен/запущен? Сервис NtEnumerateKey похукан? Анализ дампа в порядке, если не считать, что система попыталась выполнить инструкцию по левому адресу. Почему адрес левый из дампа особо не видно, показывай код потока, который (по твоему мнению) является причиной падения.
Да там довольно много кода. Наверное проще самому разобраться. Такая ситуация, Win 2003 не падает, только XP. Что это может значить? Я бью какие то данные ядра, которые по счасливой случайности совпали с чем-то в XP? Процедура сохранения вот: static wchar_t gs_szRegPath[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\"; static wchar_t gs_szObjPath[] = L"\\Driver\\"; static wchar_t gs_szLdrDrvPath[] = L"\\SystemRoot\\system32\\drivers\\"; static wchar_t gs_szLdrDrvSys[] = L".sys"; bool ModLoader::SaveDriver(const wchar_t *pDriverName, byte *pBuffer, uint32 uiSize) { CHECK_PASSIVE_LEVEL(); uint32 uiLength = 32 + cdl::WcsLen(pDriverName) + 4 + 1; wchar_t *pFullName = new wchar_t[uiLength]; wcscpy(pFullName, gs_szLdrDrvPath); wcscat(pFullName, pDriverName); wcscat(pFullName, gs_szLdrDrvSys); UNICODE_STRING uniDriverName; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK ioSb; HANDLE hFile; ::RtlInitUnicodeString(&uniDriverName, pFullName); InitializeObjectAttributes(&oa, &uniDriverName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); bool bRes = false; NTSTATUS status = ::IoCreateFile(&hFile, GENERIC_WRITE | SYNCHRONIZE, &oa, &ioSb, 0, 0, FILE_SHARE_READ, FILE_CREATE, FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone, NULL, IO_NO_PARAMETER_CHECKING); if(status == STATUS_SUCCESS) { uint64 uiOffset = 0; WriteFile(hFile, pBuffer, uiSize, &uiOffset); KdPrint((" -> SaveDriver success\n")); ::ZwClose(hFile); bRes = true; } else { KdPrint((" -> Failed to create file %ws\n", pFullName)); } SAFE_DELETE_ARRAY(pFullName); return bRes; } А запуск тут: bool ModLoader::InstallDriver(const wchar_t *pDriverName) { CHECK_PASSIVE_LEVEL(); ULONG ulDisp; OBJECT_ATTRIBUTES oa; HANDLE hKey; // Full registry path uint32 uiRegLen = cdl::WcsLen(gs_szRegPath) + cdl::WcsLen(pDriverName) + 1; wchar_t *pRegName = new wchar_t[uiRegLen]; wcscpy(pRegName, gs_szRegPath); wcscat(pRegName, pDriverName); UNICODE_STRING uRegName; PDRIVER_OBJECT pDriverObject; ::RtlInitUnicodeString(&uRegName, pRegName); bool bRes = false; InitializeObjectAttributes(&oa, &uRegName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); NTSTATUS status = OsZwCreateKey(&hKey, KEY_ALL_ACCESS, &oa, 0, 0, 0, &ulDisp); if(status == STATUS_SUCCESS) { KdPrint((" -> InstallDriver success\n")); SetRegValues(hKey, pDriverName); // Создаёт все ключи ::ZwClose(hKey); // LoadDriver status = OsZwLoadDriver(&uRegName); KdPrint((" -> ZwLoadDriver status: 0x%08X\n", status)); bRes = true; } else { KdPrint((" -> Failed to create key %ws\n", pRegName)); } SAFE_DELETE_ARRAY(pRegName); return bRes; } Все буферы которые создаются здесь по new/delete помечаются маркерами, при удалении если был выход за границы - сразу ассерт. Ассертов нету. Значит ошибка где-то ещё.
Код (Text): 0:000> dt CcFlushCache; ntkrpamp!CcFlushCache CcFlushCache void ( _SECTION_OBJECT_POINTERS*, _LARGE_INTEGER*, unsigned long, _IO_STATUS_BLOCK*) 0:000> dt CcWriteBehind ntkrpamp!CcWriteBehind CcWriteBehind void ( _SHARED_CACHE_MAP*, _IO_STATUS_BLOCK*, unsigned long) 0:000> uf CcWriteBehind ntkrpamp!CcWriteBehind [d:\rtm\base\ntos\cache\cachesub.c @ 4334]: 4334 00440c26 8bff mov edi,edi 4334 00440c28 55 push ebp 4334 00440c29 8bec mov ebp,esp 4334 00440c2b 83e4f8 and esp,0FFFFFFF8h 4334 00440c2e 83ec2c sub esp,2Ch 4334 00440c31 53 push ebx 4334 00440c32 8b5d08 mov ebx,dword ptr [ebp+8] <-- _SHARED_CACHE_MAP* <..> ntkrpamp!CcWriteBehind+0xc7 [d:\rtm\base\ntos\cache\cachesub.c @ 4462]: 4462 00440ced 8b4344 mov eax,dword ptr [ebx+44h] <-- _SHARED_CACHE_MAP+44 4464 00440cf0 648b0d20000000 mov ecx,dword ptr fs:[20h] 4464 00440cf7 83e0f8 and eax,0FFFFFFF8h 4464 00440cfa 8b7014 mov esi,dword ptr [eax+14h] <-- ... 4464 00440cfd 81c140040000 add ecx,440h 4464 00440d03 89742410 mov dword ptr [esp+10h],esi 4464 00440d07 e870290700 call ntkrpamp!KeReleaseQueuedSpinLockFromDpcLevel (004b367c) 4465 00440d0c 8d4c242c lea ecx,[esp+2Ch] 4465 00440d10 ff1550114000 call dword ptr [ntkrpamp!_imp_KeReleaseInStackQueuedSpinLock (00401150)] 4471 00440d16 85ff test edi,edi 4471 00440d18 7410 je ntkrpamp!CcWriteBehind+0x104 (00440d2a) ntkrpamp!CcWriteBehind+0xf4 [d:\rtm\base\ntos\cache\cachesub.c @ 4473]: 4473 00440d1a ff742418 push dword ptr [esp+18h] 4473 00440d1e ff742420 push dword ptr [esp+20h] 4473 00440d22 57 push edi 4473 00440d23 8bfb mov edi,ebx 4473 00440d25 e89adffeff call ntkrpamp!CcFreeActiveVacb (0042ecc4) ntkrpamp!CcWriteBehind+0x104 [d:\rtm\base\ntos\cache\cachesub.c @ 4486]: 4486 00440d2a 8b7d0c mov edi,dword ptr [ebp+0Ch] 4486 00440d2d 57 push edi 4486 00440d2e 6a01 push 1 4486 00440d30 6810615300 push offset ntkrpamp!CcNoDelay (00536110) 4486 00440d35 56 push esi <-- ... 4486 00440d36 e873ebffff call ntkrpamp!CcFlushCache (0043f8ae) <-- the only call to CcFlushCache <..> 0:000> dt _SHARED_CACHE_MAP ntkrpamp!_SHARED_CACHE_MAP +0x000 NodeTypeCode : Int2B +0x002 NodeByteSize : Int2B +0x004 OpenCount : Uint4B +0x008 FileSize : _LARGE_INTEGER +0x010 BcbList : _LIST_ENTRY +0x018 SectionSize : _LARGE_INTEGER +0x020 ValidDataLength : _LARGE_INTEGER +0x028 ValidDataGoal : _LARGE_INTEGER +0x030 InitialVacbs : [4] Ptr32 _VACB +0x040 Vacbs : Ptr32 Ptr32 _VACB +0x044 FileObjectFastRef : _EX_FAST_REF <-- _SHARED_CACHE_MAP+44 <..> 0:000> dt _EX_FAST_REF ntkrpamp!_EX_FAST_REF +0x000 Object : Ptr32 Void +0x000 RefCnt : Pos 0, 3 Bits <-- 8-byte aligned ptr with 3-bit ref count +0x000 Value : Uint4B 0:000> dt _FILE_OBJECT ntkrpamp!_FILE_OBJECT +0x000 Type : Int2B +0x002 Size : Int2B +0x004 DeviceObject : Ptr32 _DEVICE_OBJECT +0x008 Vpb : Ptr32 _VPB +0x00c FsContext : Ptr32 Void +0x010 FsContext2 : Ptr32 Void +0x014 SectionObjectPointer : Ptr32 _SECTION_OBJECT_POINTERS <-- ... makes sense? +0x018 PrivateCacheMap : Ptr32 Void +0x01c FinalStatus : Int4B +0x020 RelatedFileObject : Ptr32 _FILE_OBJECT +0x024 LockOperation : UChar +0x025 DeletePending : UChar +0x026 ReadAccess : UChar +0x027 WriteAccess : UChar +0x028 DeleteAccess : UChar +0x029 SharedRead : UChar +0x02a SharedWrite : UChar +0x02b SharedDelete : UChar +0x02c Flags : Uint4B +0x030 FileName : _UNICODE_STRING +0x038 CurrentByteOffset : _LARGE_INTEGER +0x040 Waiters : Uint4B +0x044 Busy : Uint4B +0x048 LastLock : Ptr32 Void +0x04c Lock : _KEVENT +0x05c Event : _KEVENT +0x06c CompletionContext : Ptr32 _IO_COMPLETION_CONTEXT +0x070 IrpListLock : Uint4B +0x074 IrpList : _LIST_ENTRY +0x07c FileObjectExtension : Ptr32 Void 0:000> dt CcFlushCache; ntkrpamp!CcFlushCache CcFlushCache void ( _SECTION_OBJECT_POINTERS*, <-- ... definitely makes sense _LARGE_INTEGER*, unsigned long, _IO_STATUS_BLOCK*) 0:000> dt _section_object_pointers ntkrpamp!_SECTION_OBJECT_POINTERS +0x000 DataSectionObject : Ptr32 Void +0x004 SharedCacheMap : Ptr32 Void <-- CcFlushCache tries to get that pointer +0x008 ImageSectionObject : Ptr32 Void Поток ленивой записи пытается сохранить изменённые страницы файла на диск. Функция CcCacheMap пытается овладевать указателем SharedCacheMap и дереференсит [почти] нулевой указатель, поскольку у смываемого объекта "файл" обнулён SectionObjectPointer. Осталось 'лишь' понять, почему SectionObjectPointer обнулён, ага. Посмотреть вывод следующей команды для получения информации о смываемом файле: !fileobj (poi(81bfcd80+44)+14)
Код потока забыл, показывай давай. И параметры у CcFlushCache() какие-то странные, это да. Где-то ты память попортил всё таки. Ситуация следующая у тебя: прототип СсFlushCache() вот такой: Код (Text): VOID CcFlushCache ( IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN PLARGE_INTEGER FileOffset OPTIONAL, IN ULONG Length, OUT PIO_STATUS_BLOCK IoStatus OPTIONAL); Второй и четвёртый параметры могут быть равны NULL и это не будет считаться ошибкой. Третий параметр, судя по стеку, равен 1, а первый, судя опять же по стеку, равен NULL, о чём нам и сообщают вот эти строки в дампе: Код (Text): FAULTING_IP: nt!CcFlushCache+6c 804ed966 8b7004 mov esi,dword ptr [eax+4] ... eax=00000000 Вот эта инструкция: Код (Text): 804ed966 8b7004 mov esi,dword ptr [eax+4] В коде ядра соответствует этому куску: Код (Text): CcAcquireMasterLock( &OldIrql ); SharedCacheMap = SectionObjectPointer->SharedCacheMap; Система у тебя однопроцессорная, посему макрос CcAcquireMasterLock() тупо раскрывается ещё во время компиляции в вызов KeRaiseIrqlToDpcLevel(), что приводит к переходу на DISPATCH_LEVEL, а на этом уровне исключение обработано не будет, потому - падение из-за доступа по невалидному адресу eax+4, т.е. 0+4=4. Это чтобы ты понимал что реально произошло, а вот с причиной этого тебе ещё следует разобраться. Либо ты затёр стек потока менеджера кэша, что честно говоря маловероятно, либо сам Cc передал в СсFlushCache() некорректное значение, почему он так сделал - тоже вопрос. Почти всегда это значение берётся из FileObject того файла, запрос на который поставлен в очередь менеджера кэша. Возможно, имела место быть такая ситуация, что в момент извлечения значения поля SectionObjectPointer из файлового объекта оно уже было равно NULL. Вообще, мне это трудно такое представить. Ну либо ты затёр эти значения нулями, но это вообще ещё умудриться надо. Есть мнение, что вот тот самый поток как раз инициирует процедуру уничтожения файлового объекта (лишний дереференс, например, или типа того), в процессе которой (!) менеджер кэша всё ещё работает с полями файлового объекта, что есть ошибка, разумеется. Короче, показывай код этого волшебного потока, может чего и прояснится. P.S. Постом ниже написано примерно то же самое, что и здесь.
Sol_Ksacap, x64 спасибо за столь подробное объяснение. пока сам пороюсь. Код потока реально большой, там вся логика работает (non-blocking sockets, соединение с сервером)
Нашёл код который приводил к бсоду (взял из ReactOS). NTSTATUS FlushFile(HANDLE hFile) { CHECK_PASSIVE_LEVEL(); PFILE_OBJECT pFileObject = NULL; IO_STATUS_BLOCK ioStatusBlock; // Get the File Object NTSTATUS status = ::ObReferenceObjectByHandle(hFile, 0, *IoFileObjectType, KernelMode, (PVOID *)&pFileObject, NULL); if(status != STATUS_SUCCESS) return status; // Get the Device Object PDEVICE_OBJECT pDeviceObject = ::IoGetRelatedDeviceObject(pFileObject); // Clear the event ::KeClearEvent(&pFileObject->Event); // Allocate the IRP PIRP pIrp = ::IoAllocateIrp(pDeviceObject->StackSize, TRUE); if(!pIrp) { ::ObDereferenceObject(pFileObject); return STATUS_UNSUCCESSFUL; } // Set up the IRP pIrp->Flags = 0; pIrp->UserIosb = &ioStatusBlock; pIrp->UserEvent = NULL; pIrp->RequestorMode = KernelMode; pIrp->Tail.Overlay.Thread = PsGetCurrentThread(); pIrp->Tail.Overlay.OriginalFileObject = pFileObject; pIrp->Overlay.AsynchronousParameters.UserApcRoutine = NULL; // Set up Stack Data PIO_STACK_LOCATION pStack = IoGetNextIrpStackLocation(pIrp); pStack->MajorFunction = IRP_MJ_FLUSH_BUFFERS; pStack->FileObject = pFileObject; status = ::IoCallDriver(pDeviceObject, pIrp); if(status == STATUS_PENDING) { status = ::KeWaitForSingleObject(&pFileObject->Event, Executive, KernelMode, FALSE, NULL); // Set the final status status = pFileObject->FinalStatus; } ::ObDereferenceObject(pFileObject); KdPrint(("FlushFile: 0x%08X\n", status)); return status; } Файл открывался при старте потока, в процессе работы туда писались данные, запись заканчивалась вызовом FlushFile. В конце файл закрывался и система падала. Как не странно только после запуска второго драйвера.
Нет, это делает менеджера ввода/вывода для всех запросов, кроме IRP_MJ_CREATE. На самом деле функция IoCompleteRequest() делает несколько больше, чем многие думают. Однако некоторые моменты незадокументированы, в частности, вот этот.