Идея возникла несколько лет назад по мере чтения документации Debugging Tools — набора инструментов, должно отметить, в хозяйстве нужного и необходимого, — а ее суть основывается на возможности просматривать типы данных, описание которых в MSDN относительно, либо отсутствует вовсе. Конечно же то, о чем речь пойдет далее не заменит полностью LiveKd, но вполне имеет право на жизнь в виду того, что, во-первых, не требует прав адинистратора, во-вторых, позволяет ознакомиться с большинством различных типов, являющихся «строительными блоками» системы.
Слова об отсутсвии прав администратора ничуть не преувеличены: 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 для вызова апишных функций, но альтернативный взгляд на решение данной проблемы составляет отдельную тему для разговора.
ссылка на оригинал статьи https://habrahabr.ru/post/282515/
Добавить комментарий