В некоторых ситуациях возникает потребность перезагрузить операционную систему прямо из ядра. Когда-то давно я был озадачен такой проблемой, так как существующие способы меня не устраивали по разным причинам. Пришлось копать, искать и спрашивать. В результате были найдены следующие варианты.
В режиме пользователя можно использовать функцию ExitWindowsEx (она ведет в NtShutdownSystem):
BOOL WINAPI ExitWindowsEx( __in UINT uFlags, __in DWORD dwReason );
Все достаточно подробно описано в MSDN и, конечно, не забываем про включенную Shutdown привилегию.
Давайте рассмотрим варианты, которые нам доступны из режима ядра. Один из них — это использование функции NtShutdownSystem. Конечно, она недокументированная. Реализация для Windows XP выглядит так (вот такой забавный switch, видимо из-за оптимизаций):
NTSTATUS __stdcall NtShutdownSystem(SHUTDOWN_ACTION Action) { POWER_ACTION SystemAction; if ( Action ) { if ( Action == 1 ) // ShutdownReboot { SystemAction = 5; // PowerActionShutdownReset } else // ShutdownPowerOff { if ( Action != 2 ) return STATUS_INVALID_PARAMETER; SystemAction = 6; // PowerActionShutdownOff } } else // ShutdownNoReboot { SystemAction = 4; // PowerActionShutdown } return NtSetSystemPowerState(SystemAction, PowerSystemSleeping3, 0xC0000004u); }
typedef enum _POWER_ACTION { PowerActionNone = 0, PowerActionReserved, PowerActionSleep, PowerActionHibernate, PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff, PowerActionWarmEject } POWER_ACTION, *PPOWER_ACTION;
Вышеуказанные функции нужно использовать тогда, когда вам необходимо все сделать красиво и чисто.
Следующий вариант — это использовать функцию HalReturnToFirmware. Она также описана в MSDN.
VOID HalReturnToFirmware(IN FIRMWARE_REENTRY Routine);
На вход функция может принимать параметр HalRebootRoutine, что нам собственно и нужно. Честно говоря, меня смущает то, что написано в MSDN:
Requirements
Versions: Available in Microsoft Windows Server 2003 and Windows XP.
DLL: Requires Hal.dll.
Под рукой у меня нет Hal.dll от Windows 2000, так что проверить экспортируется ли она или нет, я не могу, а память меня подводит.
Третий вариант — это спровоцировать BSOD. Шучу, шучу, но близко к правде. Вызовем KeBugcheckEx с параметром POWER_FAILURE_SIMULATE. KeBugcheckEx — это обыкновенная обертка над KeBugcheck2:
int __stdcall KeBugCheck2(int BugCheckCode, ) { if ( BugCheckCode == POWER_FAILURE_SIMULATE ) { KiScanBugCheckCallbackList(); HalReturnToFirmware(3); // HalRebootRoutine } }
Знакомая картина, но помнится мне, что на каких-то системах вместо перезагрузки я получал самый натуральный BSOD с POWER_FAILURE_SIMULATE. Однако поехали дальше.
Мне, как любителю минимализма хотелось чего-нибудь быстрого, без проверок. Хоп и готово. Не знаю, что может быть быстрее этого:
WRITE_PORT_UCHAR( (PUCHAR )0x64, 0xFE ); // пишем RESET
// в контроллер клавиатуры
При таких перезагрузках не забывайте предупреждать ваших пользователей о том, чтобы они сохранили свои документы.
Кто какие способы знает еще?
> Под рукой у меня нет Hal.dll от Windows 2000, так что проверить экспортируется ли она или нет, я не могу, а память меня подводит.
врут, есть такой экспорт, причем даже делаюший то, что требуется.
Я знаю еще пару способов:
1. Triple fault.
2. Jump to BIOS
Отличные способы, но насколько я понимаю Jump to BIOS будет помуторнее Triple fault. Последний к тому же элегантнее всего того, что я видел. Спасибо! Надо будет дополнить статью.
мне Triple fault больше всего нравится.
Мало ли где мой код будет исполняться, может там и клавы не будет.
mov al, 0xFE
out 0×64, al
Посылает код reset в порт контроллера клавиатуры. Не коросплатформенно, но зато не привязано к особенностям конкретной ОС =)
Эм, а ты внимательно читал статью? :)
WRITE_PORT_UCHAR( (PUCHAR )0×64, 0xFE );
Кстати, кроссплатформенность мне и не была нужна. Пока что, во-всяком случае.
ой, незаметил
>VOID HalReturnToFirmware(IN FIRMWARE_REENTRY Routine);
typedef enum _FIRMWARE_REENTRY
{
HalHaltRoutine,
HalPowerDownRoutine,
HalRestartRoutine,
HalRebootRoutine,
HalInteractiveModeRoutine,
HalMaximumRoutine
} FIRMWARE_REENTRY, *PFIRMWARE_REENTRY;
Самый жесткий вариант — напрямую через чипсет рубануть, тоже команда типа
WRITE_PORT_UCHAR( (PUCHAR )0×1, PMBASE+Power );
не скажу на память адреса смещения, где-то в пределах 0х800, надо читать ICHXXX, можно погасить, можно ребутнуть. Жестче некуда, но адреса плавают, надо в спеках копаться, через клавиатуру изящней конечно.
Может чего забыл, а через один из CRX -регистров разве нельзя?
К сожалению ребут и через triple fault, и через out 64h,FEh чреваты непредсказуемым результатом. На большинстве чипов трипл фолт и сброс через контроллер клавы вызывает INIT#. Инит на проце, который не BootStrap — приводит к дауну. Есть правда определенное кол-во чипов, где INIT# вызывается вместе с RESET#. Гарантированной техники здесь вроде нет. Единственное — я бы попробовал
резет через порт CF9h, однако я не уверен что ВСЕ чипы поддерживают фишку
Добавлю. Для SMM-триппера INIT# поровну, SMBASE не меняется