Debugging Tools в помощь PowerShell разработчику

от автора

Идея возникла несколько лет назад по мере чтения документации 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 системы.

NtQuerySystemInformation

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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *