как программно открыть beep.sys чтобы звук прозвучал... --- Сообщение объединено, 8 июн 2021 --- или еще рабочий драйвер... ring0 интересует --- Сообщение объединено, 8 июн 2021 --- Пример рабочего драйвера есть у кого нибудь?
я уже данного автора по названию темы могу опознать . Откройте для себя Walter Oney что ли уже книгу с примерами дров. И сорцы wdk - там есть дрова.
asm0day01, так есть же уроки Four-F на этом сайте, как раз там ассемблер и как раз пример с этим самым beep
Пожалуйста, фальшивый бипер: Код (C): static NTSTATUS OpenDevice(PWSTR DevName, HANDLE* hDev) { UNICODE_STRING DevPathUS; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK iost; UINT Length = 8; wchar_t DevPath[MAX_PATH] = {'\\','D','e','v','i','c','e','\\'}; for(int idx=0;*DevName;DevName++,Length++)DevPath[Length] = *DevName; DevPathUS.Buffer = DevPath; DevPathUS.Length = Length * sizeof(wchar_t); DevPathUS.MaximumLength = DevPathUS.Length + sizeof(wchar_t); //RtlInitUnicodeString(&str, L"\\Device\\xxx"); attr.Length = sizeof(OBJECT_ATTRIBUTES); attr.RootDirectory = 0; attr.ObjectName = &DevPathUS; attr.Attributes = 0; attr.SecurityDescriptor = 0; attr.SecurityQualityOfService = 0; return NtCreateFile(hDev, GENERIC_READ | GENERIC_WRITE, &attr, &iost, 0, 0, 0, FILE_OPEN, 0, 0, 0); } //------------------------------------------------------------------------------------ static HANDLE OpenBeep(void) { HANDLE hBeep = NULL; OpenDevice(L"Beep", &hBeep); return hBeep; } //------------------------------------------------------------------------------------ static NTSTATUS DoBeep(HANDLE hBeep, DWORD Freq, DWORD Duration) { struct { ULONG uFrequency; ULONG uDuration; } param; IO_STATUS_BLOCK iost; param.uFrequency = Freq; // short param.uDuration = Duration; return NtDeviceIoControlFile(hBeep, 0, 0, 0, &iost, 0x00010000, ¶m, sizeof(param), 0, 0); } //------------------------------------------------------------------------------------ { HANDLE hBeep = OpenBeep(); DoBeep(hBeep, 1000, 1000); }
в общем тема не закрыта, народ дайте работающий драйвер и обьясните на "пальцах" как это работает именно _рабочее_ что то а не лом
В системе есть аппаратный программируемый таймер (PIT, Programmable Interval Timer): Programmable Interval Timer - OSDev Wiki У этого таймера есть три выхода (канала): Channel 0 - идёт на IRQ0, что позволяет периодически генерировать прерывания. Channel 1 - в старых системах использовался для регенерации ячеек DRAM, но давно не используется. Channel 2 - идёт напрямую на пищалку. Этот таймер тикает с фиксированной частотой 1.193182 МГц и может работать в режиме генератора прямоугольных импульсов. Режим таймера настраивается путём записи в порт 0x43 следующей битовой маски (я отметил нужное): Bits: Usage 6 and 7: Select channel: 0 0 = Channel 0 0 1 = Channel 1 1 0 = Channel 2 1 1 = Read-back command (8254 only)4 and 5: Access mode: 0 0 = Latch count value command 0 1 = Access mode: lobyte only 1 0 = Access mode: hibyte only 1 1 = Access mode: lobyte/hibyte1 to 3: Operating mode: 0 0 0 = Mode 0 (interrupt on terminal count) 0 0 1 = Mode 1 (hardware re-triggerable one-shot) 0 1 0 = Mode 2 (rate generator) 0 1 1 = Mode 3 (square wave generator) 1 0 0 = Mode 4 (software triggered strobe) 1 0 1 = Mode 5 (hardware triggered strobe) 1 1 0 = Mode 2 (rate generator, same as 010b) 1 1 1 = Mode 3 (square wave generator, same as 011b)0: BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD Собрав битовую маску, получим число: 0b10'11'011'0 = 0xB6, которое означает, что мы настраиваем Channel 2 на работу в режиме генератора прямоугольных импульсов, задавая делитель базовой частоты в виде 16-битного числа. Применим эту настройку, записав это число в порт 0x43. Для этого воспользуемся интринсиком __outbyte(), который объявлен в intrin.h: Код (C++): #include <intrin.h> void setBeeperRegime() { __outbyte(0x43, 0xB6); } После настройки режима таймера Channel 2 можно будет настраивать, задавая делитель базовой частоты 16-битным числом. За настройку каналов отвечают следующие порты: I/O port Usage 0x40 Channel 0 data port (read/write) 0x41 Channel 1 data port (read/write) 0x42 Channel 2 data port (read/write) 0x43 Mode/Command register (write only, a read is ignored) Нам нужен порт 0x42. Делитель записывается в этот порт двумя порциями: сначала младший байт, затем старший. Код (C++): void setBeeperDivider(unsigned short divider) { __outbyte(0x42, static_cast<unsigned char>(divider)); __outbyte(0x42, static_cast<unsigned char>(divider >> 8)); } Результирующая частота в Герцах будет равна 1193182 Гц / divider. Для удобства определим функцию, задающую частоту в Герцах: Код (C++): void setBeeperFrequency(unsigned short freq) { if (freq < 19) freq = 19; const auto divider = static_cast<unsigned short>(1193182u / freq); setBeeperDivider(divider); } Здесь стоит сделать отступление: для чего нужна проверка (freq < 19)? Т.к. делитель ограничен 16ю битами, максимальное число, которое можно в него записать - 65'536. Соответственно, 1193182 / 65536 = ~18.2 Гц. Округлив вверх, получим 19 Гц. Если бы мы попробовали записать частоту ниже (18, 17 и т.д.) - мы бы потеряли часть делителя из-за битов, которые бы в него просто не влезли. Итак, таймер подготовлен, частота задана. Осталось дело за малым: присоединить спикер к выходу таймера. Для настройки спикера выделен отдельный порт - 0x61. Бит 0 присоединяет вход спикера к Channel 2 у PIT (1 = подключен; 0 = отключен). Бит 1 задаёт положение мембраны динамика (1 = "out", мембрана в верхнем положении; 0 = "in", мембрана в нижнем положении). Код (C++): void startBeeper() { const auto prev = __inbyte(0x61); __outbyte(0x61, prev | 0b11); } void stopBeeper() { const auto prev = __inbyte(0x61); __outbyte(0x61, prev & ~0b11); } Итоговый код включения спикера: Код (C++): #include <wdm.h> void sleep(unsigned long ms) { LARGE_INTEGER interval{}; interval.QuadPart = -1 * static_cast<long long>(ms) * 10000ll; KeDelayExecutionThread(KPROCESSOR_MODE::KernelMode, false, &interval); } void makeSomeNoise() { setBeeperRegime(); setBeeperFrequency(1000); startBeeper(); sleep(1000); stopBeeper(); } Всё то же самое можно сделать в юзермоде, используя любой драйвер, умеющий работать с портами: WinRing0, GiveIO, EnjoyTheRing0, Kernel-Bridge, etc. Пример: мой синтезатор на основе WinRing0 (бинарники и исходники в аттаче):
Так я тебе написал на ассемблере. Интринсики inbyte/outbyte - это прямые аналоги инструкций in и out.
HoShiMin, KbRaiseIopl() - как это работает ? w7 x86 после её вызова: Код (Text): invoke GetProcAddress, Ebx, addr Raise$ Call Eax pushfd pop ecx push 3000h popfd pushfd pop ebx mov edx,41h in al,dx #GP на in соотв, контекст: Код (Text): $+A4 0012FC00 00000202 ebx $+A8 0012FC04 00000041 edx $+AC 0012FC08 00000246 ecx $+B0 0012FC0C 00000001 eax $+B4 0012FC10 0012FF88 ebp $+B8 0012FC14 004010A5 eip $+BC 0012FC18 0000001B cs $+C0 0012FC1C 00010202 efl.rf ?? Не меняется iopl.
x86 сейчас нет под рукой, чтобы проверить. Работает путём поиска EFLAGS и патча IOPL в KTRAP_FRAME. Там для x86, где не определён KTRAP_FRAME, захардкожены оффсеты до EFLAGS, что не очень надёжно. Возможно, они слетели и пропатчилось не то: Код (C++): constexpr unsigned char KTrapFrame32Size = 0x8C; constexpr unsigned char EFlagsOffsetInKTrapFrame32 = 0x70; PULONG eflags = IoGetInitialStack() - KTrapFrameSize + EFlagsOffsetInKTrapFrame32; *eflags |= ...; Попробуй на 64х-битной системе: там KTRAP_FRAME документирован и проблем быть не должно. Но спасибо за репорт, проверю и поправлю уже в новом кб.
HoShiMin, IOPL плохо поддерживается и вообще вроде не поддерживается в 64 и более новых ос чем 7, но я не помню точно. Следует заводить IOPM, а не флажки.
Вовсе нет. Последний раз я тестил на 64х-битной десятке и IOPL работал. Но есть нюанс: поток не должен крутиться в оконном цикле: система сбросит IOPL при получении оконного сообщения. В остальном же ограничений нет: достаточно его поднять, и in/out/cli/sti можно использовать в юзермоде. IOPM "легально" доступна только в 32х-битных системах. В своём древнем драйвере EnjoyTheRing0 открывал порты и через IOPL, и через IOPM (Ke386SetAccessMap/Ke386IoSetAccessProcess, которых нет в x64), и руками патча EFLAGS в TSS. Но в итоге оставил только IOPL, т.к. он показался самым простым, самым универсальным (и x32, и x64) и также давал доступ к cli/sti, чего нельзя сделать через IOPM.
HoShiMin, Понятно, а зачем колхозить вытягивая фрейм со дна стека, он ведь рекурсивный и указатель в поток сохраняется(KTHREAD) ? > система сбросит IOPL Кто его знает что она сбросит. Я на днях импорт вмп пытался восстановить, так вот не смотря на сурки и такой же диз в чём этого быть не должно, планировщик сбрасывает селекторы. Так что эта среда - контекст очень не надёжна..
Указатель-то, может, и сохраняется, только как его найти? Вытягивание со дна хотя бы более-менее стабильно: размер KTRAP_FRAME фиксирован, структура документирована (пусть и только на x64). А как быть с KTHREAD? Она же рискует поменяться при первом же обновлении системы. --- Сообщение объединено, 25 окт 2021 --- В вмп что-то основано на их изменении?
HoShiMin, > А как быть с KTHREAD? Не проблема же смещение найти, ловушку распарсить вариантов очень много. > В вмп что-то основано на их изменении? Нет, решение такое - передать управление в RX сегмент. Если загрузить в legacy cs -> ds, то шедулер поправит на кванте в дефолтное значение и сегмент, те всё ап станет открыто на запись. Это так же не надёжно как и манипуляции флажками.
А что именно тебе неясно из того, что написано выше? Я тебе дал ссылку на скачивание драйвера и подробное описание