DataLife Engine > Windows > Цветной экран смерти
Цветной экран смерти6 января 2008. Разместил: podpole |
Если ты кодил под Ring 0, то знаешь, что за показ экрана смерти ответственны функции ядра KeBugCheck и KeBugCheckEx. Правда, это реализовано немного по-разному в Windows 2000 и ниже и в Windows XP и выше. В Windows 2000 всю работу делает KeBugCheckEx, а KeBugCheck - обертка. В Windows XP KeBugCheckEx и KeBugCheck - обертки, всю работу делает внутренняя неэкспортируемая функция ядра KeBugCheck2. Где-то в теле функции происходит задание фона для экрана смерти. Мы попробуем его найти и пропатчить прямо в памяти. Для простоты мы будем писать для Windows XP, а отчасти и из-за того, что сейчас она установлена на большем числе компьютеров, чем Windows 2000. Поскольку все смещения и т.п. сильно зависят от билда, тут есть два пути - прописать смещения жестко для каждого билда или искать нужные данные динамически. Чтобы не усложнять себе жизнь, мы выберем первый вариант и пропишем смещения только для системы "Windows XP build 2600". Посмотрим на код функции KeBugCheck2: // // Enable InbvDisplayString calls to make it through to bootvid driver. // if (InbvIsBootDriverInstalled()) { InbvAcquireDisplayOwnership(); InbvResetDisplay(); InbvSolidColorFill(0,0,639,479,4); // make the screen blue InbvSetTextColor(15); InbvInstallDisplayStringFilter((INBV_DISPLAY_STRING_FILTER)NULL); InbvEnableDisplayString(TRUE); // enable display string InbvSetScrollRegion(0,0,639,479); // set to use entire screen } интересующую нас строчку я выделил. тут происходит задание цвета фона. Нам надо только лишь его поменять. Поехали! Итак, мы пишем под Windows XP 2600. Откроем в IDA Pro код KeBugCheckEx: .text:0045C303 _KeBugCheckEx@20 proc near .text:0045C303 BugCheckCode = dword ptr 8 .text:0045C303 BugCheckParameter1= dword ptr 0Ch .text:0045C303 BugCheckParameter2= dword ptr 10h .text:0045C303 BugCheckParameter3= dword ptr 14h .text:0045C303 BugCheckParameter4= dword ptr 18h .text:0045C303 .text:0045C303 mov edi, edi .text:0045C305 push ebp .text:0045C306 mov ebp, esp .text:0045C308 push 0; int .text:0045C30A push [ebp+BugCheckParameter4]; int .text:0045C30D push [ebp+BugCheckParameter3]; int .text:0045C310 push [ebp+BugCheckParameter2]; int .text:0045C313 push [ebp+BugCheckParameter1]; int .text:0045C316 push [ebp+BugCheckCode]; int .text:0045C319 call _KeBugCheck2@24; KeBugCheck2(x,x,x,x,x,x) Вот тут как раз и вызывается внутренняя функция KeBugCheck2. Опкод этой команды CALL выглядит так: E8 9CF4FFFF. E8 - код команды call, 9CF4FFFF - смещение KeBugCheck2 относительно следующей команды. Адрес команды call равен 0045C319, + 1 байт, мы получим 0045C31A - адрес смещения. 0045C31Ah - 0045C303h (адрес KeBugCheckEx) = 17h - то есть адрес смещения функции KeBugCheck2 лежит через 17h байт после начала KeBugCheckEx. Что ж.. нам это пригодится. Глянем внутрь KeBugCheck2 и найдем вызов InbvSolidColorFill, устанавливающей цвет фона экрана: .text:0045BDA3 push 4 .text:0045BDA5 push 1DFh .text:0045BDAA mov ebx, 27Fh .text:0045BDAF push ebx .text:0045BDB0 push esi .text:0045BDB1 push esi .text:0045BDB2 call _InbvSolidColorFill@20; InbvSolidColorFill(x,x,x,x,x) Так-так. PUSH 4 - как раз заталкивание в стек цвета фона (4 - синий). Опкод команды PUSH 4 выглядит так: 6A 04 PUSH 4. Нам нужно изменить операнд команды PUSH. Адрес начала KeBugCheck2 - 0045B7BA, адрес операнда команды PUSH - 0045BDA4. Не нужно быть гением, чтобы посчитать разницу - 5EA. Вот мы и собрали все нужные нам данные. Приступим к кодерской части. Реализация будет такова - мы напишем драйвер ядра, у которого по DeviceIoControl можно вызвать 2 функции - прочитать текущий цвет и записать новый. С учетом всего вышенаписнаного, вот вспомогательная функция для получения адреса байта, где лежит цвет фона: PUCHAR GetBugcheckColorAddress() { DWORD addr = (DWORD) &KeBugCheckEx; addr += 0x17; addr = addr + 4 + *(DWORD*)addr; addr += 0x5EA; return (PUCHAR) addr; } Определим следующие два кода для DeviceIoControl: #define IOCTL_FAULTDRIVER_GET_BUGCHECK_COLOR 0x0014 #define IOCTL_FAULTDRIVER_SET_BUGCHECK_COLOR 0x0024 Проследим, чтобы младшие 2 бита были сброшены - они показывают тип ввода-вывода при DeviceIoControl. 00 означает буферизированный ввод-вывод, он нам и нужен. Диспетчер ввода-вывода выделит системный буфер, скопирует пользовательский буфер в системный, мы его будем изменять как хотим, а потом системный буфер скопируется обратно в выходной. Напишем вот такую функцию для обработки IRP_MJ_DEVICE_CONTROL: NTSTATUS DriverIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION pisl = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG BuffSize = pisl->Parameters.DeviceIoControl.InputBufferLength; PUCHAR pBuff = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; Irp->IoStatus.Information = 0; switch(pisl->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_FAULTDRIVER_GET_BUGCHECK_COLOR: if (pBuff && pisl->Parameters.DeviceIoControl.OutputBufferLength >= 1) { DPRINT("[~] DeviceIoControl : IOCTL_FAULTDRIVER_GET_BUGCHECK_COLOR"); __try { if(*NtBuildNumber != 2600) { DPRINT("[!] This driver supports only Windows XP build 2600, current build is %d", *NtBuildNumber); status = STATUS_ILLEGAL_FUNCTION; break; } PUCHAR lpBugcheckColor = GetBugcheckColorAddress(); *pBuff = *lpBugcheckColor; DPRINT("[+] Search completed. Address is 0x%08x, bugcheck color is %02x", lpBugcheckColor, *pBuff); Irp->IoStatus.Information = 1; status = STATUS_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { Irp->IoStatus.Status = status = GetExceptionCode(); Irp->IoStatus.Information = 0; DPRINT("[!] Unhandled exception %.x", status); break; } } break; case IOCTL_FAULTDRIVER_SET_BUGCHECK_COLOR: DPRINT("Buffer size = %d", BuffSize); if (pBuff && BuffSize >= 1 && pisl->Parameters.DeviceIoControl.OutputBufferLength >= 1) { DPRINT("[~] DeviceIoControl : IOCTL_FAULTDRIVER_SET_BUGCHECK_COLOR"); __try { if(*NtBuildNumber != 2600) { DPRINT("[!] This driver supports only Windows XP build 2600, current build is %d", *NtBuildNumber); status = STATUS_ILLEGAL_FUNCTION; break; } PUCHAR lpBugcheckColor = GetBugcheckColorAddress(); BYTE oldcolor = *lpBugcheckColor; DPRINT("[+] Search completed. Address is 0x%08x, bugcheck color is %02x", lpBugcheckColor, oldcolor); DPRINT("[~] Requested color is %02x", *pBuff); *lpBugcheckColor = *pBuff; *pBuff = oldcolor; DPRINT("[+] New color set: %02x, color returned: %02x", *lpBugcheckColor, *pBuff); Irp->IoStatus.Information = 1; status = STATUS_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { Irp->IoStatus.Status = status = GetExceptionCode(); Irp->IoStatus.Information = 0; DPRINT("[!] Unhandled exception %.x", status); break; } } break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } Я думаю, стоит кое-что пояснить. Сначала мы получаем текущий стек ввода-вывода, адрес системного буфера и размеры входного и выходного буферов. Дальше смотрим, что же от нас хотят - прочитать или установить новый цвет. Основной код обернут в __try/__except, чтобы, не дай Бог, случайно не уронить систему, если что. Мы проверяем NtBuildNumber - номер билда ОС и ругаемся, если он не 2600. Дальше мы получаем адрес байта с цветом фона и химичим с ним. Выставляем в Irp->IoStatus.Information число байт, которое нужно скопировать в пользовательский буфер из системного. Остальной код драйвера комментировать, думаю, не стоит: #define _X86_ #include #define DPRINT DbgPrint UNICODE_STRING DeviceName; UNICODE_STRING SymbolicLinkName; PDEVICE_OBJECT deviceObject; void DriverUnload(IN PDRIVER_OBJECT DriverObject) { IoDeleteSymbolicLink (&SymbolicLinkName); if(deviceObject) IoDeleteDevice (deviceObject); DPRINT ("[+] Driver unloaded"); } extern "C" PUSHORT NtBuildNumber; NTSTATUS DriverIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); NTSTATUS DriverCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS status; DPRINT("[~] Driver loading"); RtlInitUnicodeString(&DeviceName, L"\\Device\\faultdriver"); RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\faultdriver"); status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &deviceObject); if (!NT_SUCCESS(status)) { DPRINT("[-] Failed to create device. IoCreateDevice returned %x", status); return STATUS_UNSUCCESSFUL; } deviceObject->Flags |= DO_BUFFERED_IO; status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName); if (!NT_SUCCESS(status)) { DPRINT("[-] Failed to create symbolic link. IoCreateSymbolicLink returned %x", status); IoDeleteDevice(deviceObject); return STATUS_UNSUCCESSFUL; } // Set dispath routines DriverObject->DriverUnload = DriverUnload; DriverObject->MajorFunction [IRP_MJ_CREATE] = DriverObject->MajorFunction [IRP_MJ_CLOSE ] = DriverCreateClose; DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL ] = DriverIoControl; DPRINT("[+] Driver initialization successful"); return STATUS_SUCCESS; } Теперь у нас готов работающий драйвер, который умеет оперировать с цветом фона для экрана смерти. Напишем несложное GUI для этого. Я приведу только код юзермодных функций для работы с цветом: // User mode API #define IOCTL_FAULTDRIVER_GET_BUGCHECK_COLOR 0x0014 #define IOCTL_FAULTDRIVER_SET_BUGCHECK_COLOR 0x0024 UCHAR GetCurrentBugcheckColor() { HANDLE hDevice = CreateFile("\\\\.\\faultdriver", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if(hDevice == INVALID_HANDLE_VALUE) return -1; DWORD ret; char buffer; if(!DeviceIoControl( hDevice, IOCTL_FAULTDRIVER_GET_BUGCHECK_COLOR, &buffer, sizeof(buffer), &buffer, sizeof(buffer), &ret, 0 )) { CloseHandle(hDevice); return -1; } else { CloseHandle(hDevice); return buffer; } } UCHAR SetCurrentBugcheckColor(UCHAR cNew) { HANDLE hDevice = CreateFile("\\\\.\\faultdriver", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if(hDevice == INVALID_HANDLE_VALUE) return -1; DWORD ret; char buffer = cNew; if(!DeviceIoControl( hDevice, IOCTL_FAULTDRIVER_SET_BUGCHECK_COLOR, &buffer, sizeof(buffer), &buffer, sizeof(buffer), &ret, 0 )) { CloseHandle(hDevice); return -1; } else { CloseHandle(hDevice); return buffer; } } Вот и все Бинарники и сорсы можно найти тут: http://cribble.by.ru/bugcheck.rar Для запуска нужно распаковать GUI-программку и бинарник драйвера в одну директорию и запустить EXEшник (он сам загрузит драйвер и выгрузит по окончании работы). В соотв. каталогах в rar'e находятся проекты с сорцами для компилера Microsoft Visual C++ 6.0 Для тестирования пригодится программка М.Руссиновича "NotMyFault", которую можно скачать тут: http://www.sysinternals.com/Files/Notmyfault.zip Удачи, пока! |