Идея возникла несколько лет назад по мере чтения документации Debugging Tools — набора инструментов, должно отметить, в хозяйстве нужного и необходимого, — а ее суть основывается на возможности просматривать типы данных, описание которых в MSDN относительно, либо отсутствует вовсе. Конечно же то, о чем речь пойдет далее не заменит полностью LiveKd, но вполне имеет право на жизнь в виду того, что, во-первых, не требует прав адинистратора, во-вторых, позволяет ознакомиться с большинством различных типов, являющихся «строительными блоками» системы.
Слова об отсутсвии прав администратора ничуть не преувеличены: kd и WinDbg вполне допускают запускать себя для просмотра дампов из-под учетной записи с ограниченными правами, — собственно, это и есть основа идеи: создать дамп процесса, содержащего наибольшее количество системных модулей, и загрузить оный дамп в отладчик, а тот в свою очередь подгрузит нужные отладочные символы и выдаст интересующие данные. Но давате по-порядку.
Из доступных пользователю с ограниченными правами процессов наибольший интерес вызывает explorer, так как соответствует критериям обозначенным в преамбуле.
Львиная доля данных, способных заинтересовать исследователя, содержится в ntdll.dll и в urlmon.dll, если речь об ХР (есть кто еще ею пользуется?) или ole32.dll в случае «семерки» (другими системами, увы, не располагаю, ибо та же «семерка» поднята в VirtualBox под Linux'ом). Но здесь мы явно забегаем вперед, так как еще нужно автоматизировать процесс запуска отладчика с дампом explorer'а. И здесь, пожалуй, лучше всего начать с настройки переменных окружения, а именно добавить в PATH путь до отладочных утилит и задать переменную _NT_SYMBOL_PATH, например, srv*C:\symbols*http://msdl.microsoft.com/download/symbols, после чего можно переходить непосредственно к кодингу. Запускам Vim или свою любимую IDE и пишем:
Приведенную выше функцию удобно разместить в профиле PowerShell, но на вкус и цвет, а фломастеры у всех разные, можно оформить ее и как модуль. Лично мне удобней, чтобы эта функция была именно в профиле, так как вызвать ее можно сразу после запуска хоста PowerShell:
И снова таки, мне больше нравится консоль, поэтому:
Ну и ради чего все затевалось.
Зная это, а также то, что NtQuerySystemInformation, которой и следует скармливать данную структуру, точнее ее сигнатура, имеется в сборке System.dll, можно получить uptime системы.

Само собой, что получением uptime дело не ограничивается, напротив, перспективы очень даже впечатляют. Единственное, что может напрягать PowerShell разработчика, так это неизбежное, казалось бы, использование Add-Type для вызова апишных функций, но альтернативный взгляд на решение данной проблемы составляет отдельную тему для разговора.
Слова об отсутсвии прав администратора ничуть не преувеличены: kd и WinDbg вполне допускают запускать себя для просмотра дампов из-под учетной записи с ограниченными правами, — собственно, это и есть основа идеи: создать дамп процесса, содержащего наибольшее количество системных модулей, и загрузить оный дамп в отладчик, а тот в свою очередь подгрузит нужные отладочные символы и выдаст интересующие данные. Но давате по-порядку.
Из доступных пользователю с ограниченными правами процессов наибольший интерес вызывает explorer, так как соответствует критериям обозначенным в преамбуле.
PS C:\> (ps explorer).Modules | sort ModuleName | ft -a
Львиная доля данных, способных заинтересовать исследователя, содержится в ntdll.dll и в urlmon.dll, если речь об ХР (есть кто еще ею пользуется?) или ole32.dll в случае «семерки» (другими системами, увы, не располагаю, ибо та же «семерка» поднята в VirtualBox под Linux'ом). Но здесь мы явно забегаем вперед, так как еще нужно автоматизировать процесс запуска отладчика с дампом explorer'а. И здесь, пожалуй, лучше всего начать с настройки переменных окружения, а именно добавить в PATH путь до отладочных утилит и задать переменную _NT_SYMBOL_PATH, например, srv*C:\symbols*http://msdl.microsoft.com/download/symbols, после чего можно переходить непосредственно к кодингу. Запускам Vim или свою любимую IDE и пишем:
function Invoke-Debugger {
param(
[Switch]$WinDbg
)
begin {
$MiniDumpWriteDump = [PSObject].Assembly.GetType(
'System.Management.Automation.WindowsErrorReporting+NativeMethods'
).GetMethod('MiniDumpWriteDump', [Reflection.BindingFlags]40)
$DumpFile = "$($env:tmp)\crash.dmp"
$Debugger = "$(switch ($WinDbg) {$true {'windbg'} $false {'kd'}}).exe"
# на случай, если используется что-то вроде Sysinternals Desktops
$Process = if (($$ = @(Get-Process explorer)).Length -gt 1) {
($$ | Sort-Object StartTime)[0]
} else { $$ }
}
process {
try {
$fs = [IO.File]::Create($DumpFile)
if (!$MiniDumpWriteDump.Invoke($null, @(
$Process.Handle, $Process.Id, $fs.SafeFileHandle, [UInt32]0,
[IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero
))) {
throw New-Object ComponentModel.Win32Exception(
[Runtime.InteropServices.Marshal]::GetLastWin32Error()
)
}
}
catch { Write-Error $_.Exception }
finally {
if ($fs -ne $null) {
$fs.Dispose()
$fs.Close()
}
}
}
end {
# если не удалось создать дамп
if ((Get-Item $DumpFile).Length -eq 0) {
Remove-Item $DumpFile -Force -ErrorAction 0
return
}
# в противном случае запускаем отладчик
Start-Process $Debugger -ArgumentList "-z $DumpFile $(if (
$Debugger -eq 'kd.exe'
) {'-y ' + $env:_NT_SYMBOL_PATH} else {'-Q'})"
}
}
Приведенную выше функцию удобно разместить в профиле PowerShell, но на вкус и цвет, а фломастеры у всех разные, можно оформить ее и как модуль. Лично мне удобней, чтобы эта функция была именно в профиле, так как вызвать ее можно сразу после запуска хоста PowerShell:
PS C:\> Invoke-Debugger -WinDbg
И снова таки, мне больше нравится консоль, поэтому:
PS C:\> Invoke-Debugger
Ну и ради чего все затевалось.
0:000> dt ole32!_system* /t
...
0:000> dt ole32!_system_information_class
...
SystemTimeOfDayInformation = 0n3
...
0:000> dt ole32!_system_timeofday_information
+0x000 BootTime : _LARGE_INTEGER
+0x008 CurrentTime : _LARGE_INTEGER
+0x010 TimeZoneBias : _LARGE_INTEGER
+0x018 TimeZoneId : Uint4B
+0x01c Reserved : Uint4B
+0x020 BootTimeBias : Uint8B
+0x028 SleepTimeBias : Uint8B
0:000> ?? sizeof(ole32!_system_timeofday_information)
unsigned int 0x30
Зная это, а также то, что NtQuerySystemInformation, которой и следует скармливать данную структуру, точнее ее сигнатура, имеется в сборке System.dll, можно получить uptime системы.

try {
$ptr = [Runtime.InteropServices.Marshal]::AllocHGlobal(48)
if ([Regex].Assembly.GetType(
'Microsoft.Win32.NativeMethods'
).GetMethod(
'NtQuerySystemInformation'
).Invoke($null, @(3, $ptr, 48, 0)) -ne 0) {
throw New-Object InvalidOperationException('Could not retrieve system uptime.')
}
'{0}.{1:D2}:{2:D2}:{3:D2}' -f ($$ = [TimeSpan]::FromMilliseconds((
[Runtime.InteropServices.Marshal]::ReadInt64($ptr, 8) -
[Runtime.InteropServices.Marshal]::ReadInt64($ptr)
) / 10000)).Days, $$.Hours, $$.Minutes, $$.Seconds
}
catch {
$_.Exception
}
finally {
if ($ptr -ne $null) {
[Runtime.InteropServices.Marshal]::FreeHGlobal($ptr)
}
}
Само собой, что получением uptime дело не ограничивается, напротив, перспективы очень даже впечатляют. Единственное, что может напрягать PowerShell разработчика, так это неизбежное, казалось бы, использование Add-Type для вызова апишных функций, но альтернативный взгляд на решение данной проблемы составляет отдельную тему для разговора.
kITerE
Небольшое дополнение: не всегда удобно дампить процесс, но Debug Engine позволяет загружать исполняемые файлы, как дампы. Например:
C:\>kd.exe -z c:\Windows\System32\ole32.dll
Microsoft ® Windows Debugger Version 10.0.10586.567 AMD64
Copyright © Microsoft Corporation. All rights reserved.
Loading Dump File [c:\Windows\System32\ole32.dll]
ModLoad: 000007ff`7a5f0000 000007ff`7a7f3000 c:\Windows\System32\ole32.dll
ole32!_DllMainCRTStartup:
000007ff`7a612bd0 48895c2408 mov qword ptr [rsp+8],rbx ss:00000000`00000008=????????????????
0:000> dt ole32!_system_timeofday_information
+0x000 BootTime: _LARGE_INTEGER
+0x008 CurrentTime: _LARGE_INTEGER
+0x010 TimeZoneBias: _LARGE_INTEGER
+0x018 TimeZoneId: Uint4B
+0x01c Reserved: Uint4B
+0x020 BootTimeBias: Uint8B
+0x028 SleepTimeBias: Uint8B