Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   Поиск определенного регистра процессора (http://forum.oszone.net/showthread.php?t=320979)

jkadaba 20-11-2016 16:05 2689095

Поиск определенного регистра процессора
 
Проблема: есть дллка, корректность которой гарантируется за счёт наличия некоторых регистров процессора и которую планируется разворачивать в сети посредством PowerShell. На самом деле таких дллок много, и все они зависят от того или иного регистра процессора, то есть если цпу не имеет этого регистра, будет подобрана нужная в этом случае дллка и установлена (сами дллки написаны на асме лидтимом нашей конторы). Мне ж как админу поручено автоматизировать процесс развёртки. Основная трудность состоит в получении списка поддерживаемых регистров. Возможно ли это сделать средствами PowerShell? Сторонние тулзы в моем случае не вариант, так как в конторе с этим очень строго. Просьба хотя бы указать вектор куда копать.

greg zakharov 21-11-2016 10:57 2689319

Все зависит от конечной цели. Если требуется проверить наличие, скажем, 3DNow, вполне можно обойтись WinAPI функцией IsProcessorFeaturePresent, экспортируемой из kernel32.dll. Правда стоит иметь в виду: из-за, мягко говоря, не совсем удачной реализации, функция может вполне возвращать ложное значение. Поэтому остаются два других варианта: через реестр и вызов cpuid посредством машинных кодов.

Через реестр
Сведения о характерных для процессора особенностях хранятся в значении реестра FeatureSet ключа HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\X (где X - номер процессора). Как расшифровывается данное значение Microsoft документировать наотрез отказывается, но ничего сложного там на самом деле нет - смещения да логические операции, - но вот объяснять как именно эти операции проводятся уж очень муторно. Кстати, значение FeatureSet также можно получить с помощью NtQuerySystemInformation, а именно SystemInformationClass = 1 и структуры SYSTEM_PROCESSOR_INFORMATION, имеющей одинаковый размер в x86 и x64 системах и равной 12 байтам.
Код:

typedef _SYSTEM_PROCESSOR_INFORMATION {
  USHORT ProcessorArchitecture;
  USHORT ProcessorLevel;
  USHORT ProcessorRevision;
  USHORT Reserved;
  ULONG ProcessorFeatureBits;
} SYSTEM_PROCESSOR_INFORMATION, *PSYSTEM_PROCESSOR_INFORMATION;

Пример получения данного значения:
Код:

try {
  $ptr = [Runtime.InteropServices.Marshal]::AllocHGlobal(0xC)
 
  if ([Regex].Assembly.GetType(
    'Microsoft.Win32.NativeMethods'
  ).GetMethod(
    'NtQuerySystemInformation'
  ).Invoke($null, @(1, $ptr, 0xC, $null)) -ne 0) {
    throw New-Object InvalidOperationException(
      'Could not get system processor information.'
    )
  }
 
  [BitConverter]::ToUInt32(
    [BitConverter]::GetBytes(
      [Runtime.InteropServices.Marshal]::ReadInt32($ptr, 8)
    ), 0
  )
}
catch { $_ }
finally {
  if ($ptr) {
    [Runtime.InteropServices.Marshal]::FreeHGlobal($ptr)
  }
}


cpuid
Крайне непопулярная техника, так как требует специфических навыков и знаний (в частности языка Ассемблера). В системе целочисленных команд процессора есть cpuid, которая получает информацию о текущем процессоре; для получения информации необходимо в регистр EAX поместить параметр (одно из значений 0, 1 или 2). Если EAX = 0, то в регистрах EAX, EBX, EDX и ECX формируется следующая информация:
EAX = n, где n - максимально допустимое значение параметра, которое может быть помещено в регистр EAX для задания режима сбора информации;
EBX, EDX, ECX - в них будет содержаться строка-идентификатор процессора, наприер, AuthenticAMD.
Если EAX = 1, формируется следующая информация:
EAX = n - информация о процессоре (SteppingId, Model, Family, Type);
EDX = n, ECX = n - собственно, информация о фичах процессора.
Ну и к слову, если EAX = 2, в регистрах EAX, EBX, ECX и EDX формируется информация о кэш-памяти первого уровня и TLB-буферах.
Если все это перевести в PowerShell, то получим примерно следующее:
Код:

function Get-CpuId {
  begin {
    @(
      [Runtime.InteropServices.CallingConvention],
      [Runtime.InteropServices.GCHandle],
      [Runtime.InteropServices.Marshal],
      [Reflection.Emit.OpCodes]
    ) | ForEach-Object {
      $keys = ($ta = [PSObject].Assembly.GetType(
        'System.Management.Automation.TypeAccelerators'
      ))::Get.Keys
      $collect = @()
    }{
      if ($keys -notcontains $_.Name) { $ta::Add($_.Name, $_) }
      $collect += $_.Name
    } # accelerators
   
    function private:Get-ProcAddress {
      param(
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [String]$Module,
       
        [Parameter(Mandatory=$true, Position=1)]
        [ValidateNotNullOrEmpty()]
        [String[]]$Function
      )
     
      begin {
        [Object].Assembly.GetType(
          'Microsoft.Win32.Win32Native'
        ).GetMethods(
          [Reflection.BindingFlags]40
        ) | Where-Object {
          $_.Name -cmatch '\AGet(ProcA|ModuleH)'
        } | ForEach-Object {
          Set-Variable $_.Name $_
        }
      }
      process {}
      end {
        if (($ptr = $GetModuleHandle.Invoke(
          $null, @($Module)
        )) -eq [IntPtr]::Zero) {
          throw New-Object InvalidOperationException(
            'Could not find specified module.'
          )
        }
       
        $Function | ForEach-Object {$ret = @{}}{
          $ret[$_] = $GetProcAddress.Invoke(
            $null, @($ptr, [String]$_)
          )
        }{$ret}
      }
    } # Get-ProcAddress
   
    function private:New-Delegate {
      param(
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateScript({$_ -ne [IntPtr]::Zero})]
        [IntPtr]$ProcAddress,
       
        [Parameter(Mandatory=$true, Position=1)]
        [ValidateNotNullOrEmpty()]
        [Type]$Prototype,
       
        [Parameter(Position=2)]
        [ValidateNotNullOrEmpty()]
        [CallingConvention]$CallingConvention = 'StdCall'
      )
     
      $method = $Prototype.GetMethod('Invoke')
     
      $returntype = $method.ReturnType
      $paramtypes = $method.GetParameters() |
                  Select-Object -ExpandProperty ParameterType
     
      $holder = New-Object Reflection.Emit.DynamicMethod(
        'Invoke', $returntype, $(
          if (!$paramtypes) { $null } else { $paramtypes }
        ), $Prototype
      )
      $il = $holder.GetILGenerator()
      if ($paramtypes) {
        0..($paramtypes.Length - 1) | ForEach-Object {
          $il.Emit([OpCodes]::Ldarg, $_)
        }
      }
     
      switch ([IntPtr]::Size) {
        4 { $il.Emit([OpCodes]::Ldc_I4, $ProcAddress.ToInt32()) }
        8 { $il.Emit([OpCodes]::Ldc_I8, $ProcAddress.ToInt64()) }
      }
      $il.EmitCalli(
        [OpCodes]::Calli, $CallingConvention, $returntype,
        $(if (!$paramtypes) { $null } else { $paramtypes })
      )
      $il.Emit([OpCodes]::Ret)
     
      $holder.CreateDelegate($Prototype)
    } # New-Delegate
   
    function private:Get-Blocks {
      param(
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNull()]
        [Byte[]]$Bytes,
       
        [Parameter()]
        [Switch]$AsInteger,
       
        [Parameter()]
        [Switch]$AsString
      )
     
      $reg = @{
        eax = $Bytes[0..3]
        ebx = $Bytes[4..7]
        ecx = $Bytes[8..11]
        edx = $Bytes[12..15]
      }
     
      if ($AsInteger) {
        $reg.Keys | ForEach-Object {$num = @{}}{
          $num[$_] = [BitConverter]::ToInt32($reg[$_], 0)
        }
        $reg = $num
      }
     
      if ($AsString) {
        $reg.Keys | ForEach-Object {$str = @{}}{
          $str[$_] = -join [Char[]]$reg[$_]
        }
        $reg = $str
      }
     
      $reg
    } # Get-Blocks
   
    # вспомогательная функция (имитация операторов -shr и -shl для PS v2)
    function private:Set-Shift {
      param(
        [Parameter(Position=0)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Left', 'Right')]
        [String]$Direction = 'Right',
       
        [Parameter(Position=1)]
        [ValidateNotNull()]
        [Object]$Type = [Int32]
      )
     
      @(
        'Ldarg_0'
        'Ldarg_1'
        'Ldc_I4_S, 31'
        'And'
        $(if ($Direction -eq 'Right') { 'Shr' } else { 'Shl' })
        'Ret'
      ) | ForEach-Object {
        $def = New-Object Reflection.Emit.DynamicMethod(
          $Direction, $Type, @($Type, $Type)
        )
        $il = $def.GetILGenerator()
      }{
        if ($_ -notmatch ',') { $il.Emit([OpCodes]::$_) }
        else {
          $il.Emit(
            [OpCodes]::(($$ = $_.Split(','))[0]), ($$[1].Trim() -as $Type)
        )}
      }
     
      $def.CreateDelegate((
        Invoke-Expression "[Func[$($Type.Name), $($Type.Name), $($Type.Name)]]"
      ))
    } # Invoke-Shift
   
    $kernel32 = Get-ProcAddress kernel32 ('VirtualAlloc', 'VirtualFree')
    ($sig = @{
      VirtualAlloc = [Func[IntPtr, UIntPtr, UInt32, UInt32, IntPtr]]
      VirtualFree  = [Func[IntPtr, UIntPtr, UInt32, Boolean]]
    }).Keys | ForEach-Object {
      Set-Variable $_ (New-Delegate $kernel32.$_ $sig[$_])
    }
   
    [Byte[]]$bytes = switch ([IntPtr]::Size) {
      4 {
        0x55,                  #push  ebp
        0x8B, 0xEC,            #mov  ebp,  esp
        0x53,                  #push  ebx
        0x57,                  #push  edi
        0x8B, 0x45, 0x08,      #mov  eax,  dword ptr[ebp+8]
        0x0F, 0xA2,            #cpuid
        0x8B, 0x7D, 0x0C,      #mov  edi,  dword ptr[ebp+12]
        0x89, 0x07,            #mov  dword ptr[edi+0],  eax
        0x89, 0x5F, 0x04,      #mov  dword ptr[edi+4],  ebx
        0x89, 0x4F, 0x08,      #mov  dword ptr[edi+8],  ecx
        0x89, 0x57, 0x0C,      #mov  dword ptr[edi+12], edx
        0x5F,                  #pop  edi
        0x5B,                  #pop  ebx
        0x8B, 0xE5,            #mov  esp,  ebp
        0x5D,                  #pop  ebp
        0xC3                    #ret
      }
      8 {
        0x53,                  #push  rbx
        0x49, 0x89, 0xD0,      #mov  r8,  rdx
        0x89, 0xC8,            #mov  eax, ecx
        0x0F, 0xA2,            #cpuid
        0x41, 0x89, 0x40, 0x00, #mov  dword ptr[r8+0],  eax
        0x41, 0x89, 0x58, 0x04, #mov  dword ptr[r8+4],  ebx
        0x41, 0x89, 0x48, 0x08, #mov  dword ptr[r8+8],  ecx
        0x41, 0x89, 0x50, 0x0C, #mov  dword ptr[r8+12], edx
        0x5B,                  #pop  rbx
        0xC3                    #ret
      }
    }
   
    $features = @{}
  }
  process {
    $func = $ExecutionContext.SessionState.InvokeCommand.GetCommand(
      'New-Delegate', [Management.Automation.CommandTypes]::Function
    ).ScriptBlock
    $shr = Set-Shift
   
    try {
      $ptr = $VirtualAlloc.Invoke(
        [IntPtr]::Zero, (New-Object UIntPtr($bytes.Length)),
        (0x1000 -bor 0x2000), 0x40
      )
     
      $cpuid = {
        param([Int32]$Level, [Byte[]]$Bytes)
       
        $func.Invoke(
          $ptr, [Action[Int32, [Byte[]]]], 'Cdecl'
        )[0].Invoke($Level, $Bytes)
      }
     
      [Marshal]::Copy($bytes, 0, $ptr, $bytes.Length)
      [Byte[]]$buf = New-Object Byte[](16)
      $cpuid.Invoke(0, $buf)
      $vendor = "$(( # вендор
        $str = Get-Blocks $buf -AsString
      ).ebx)$($str.edx)$($str.ecx)"
      # low leaves
      $ids = (Get-Blocks $buf -AsInteger).eax
      $low = @()
      for ($i = 0; $i -le $ids; $i++) {
        $cpuid.Invoke($i, $buf)
       
        if ($i -eq 1) {
          $reg = Get-Blocks $buf -AsInteger
         
          $stepping = $reg.eax -band 0xF
          $model    = $shr.Invoke($reg.eax, 4) -band 0xF
          $family  = $shr.Invoke($reg.eax, 8) -band 0xF
          $logiccpu = $shr.Invoke($reg.ebx, 16) -band 0xFF
         
          $features['fpu']          = $reg.edx -band 0x00000001
          $features['vme']          = $reg.edx -band 0x00000002
          $features['de']          = $reg.edx -band 0x00000004
          $features['pse']          = $reg.edx -band 0x00000008
          $features['tsc']          = $reg.edx -band 0x00000010
          $features['msr']          = $reg.edx -band 0x00000020
          $features['pae']          = $reg.edx -band 0x00000040
          $features['mce']          = $reg.edx -band 0x00000080
          $features['cx8']          = $reg.edx -band 0x00000100
          $features['apic']        = $reg.edx -band 0x00000200
          $features['sep']          = $reg.edx -band 0x00000800
          $features['mtrr']        = $reg.edx -band 0x00001000
          $features['pge']          = $reg.edx -band 0x00002000
          $features['mca']          = $reg.edx -band 0x00004000
          $features['cmov']        = $reg.edx -band 0x00008000
          $features['pat']          = $reg.edx -band 0x00010000
          $features['pse36']        = $reg.edx -band 0x00020000
          $features['psn']          = $reg.edx -band 0x00040000
          $features['clflush']      = $reg.edx -band 0x00080000
          $features['ds']          = $reg.edx -band 0x00200000
          $features['acpi']        = $reg.edx -band 0x00400000
          $features['mmx']          = $reg.edx -band 0x00800000
          $features['fxsr']        = $reg.edx -band 0x01000000
          $features['sse']          = $reg.edx -band 0x02000000
          $features['sse2']        = $reg.edx -band 0x04000000
          $features['ss']          = $reg.edx -band 0x08000000
          $features['htt']          = $reg.edx -band 0x10000000
          $features['tm']          = $reg.edx -band 0x20000000
          $features['ia64']        = $reg.edx -band 0x40000000
          $features['pbe']          = $reg.edx -band 0x80000000
         
          $features['sse3']        = $reg.ecx -band 0x00000001
          $features['pclmulqdq']    = $reg.ecx -band 0x00000002
          $features['dtes64']      = $reg.ecx -band 0x00000004
          $features['monitor']      = $reg.ecx -band 0x00000008
          $features['ds_cpl']      = $reg.ecx -band 0x00000010
          $features['vmx']          = $reg.ecx -band 0x00000020
          $features['smx']          = $reg.ecx -band 0x00000040
          $features['est']          = $reg.ecx -band 0x00000080
          $features['tm2']          = $reg.ecx -band 0x00000100
          $features['ssse3']        = $reg.ecx -band 0x00000200
          $features['cntx_id']      = $reg.ecx -band 0x00000400
          $features['sdbg']        = $reg.ecx -band 0x00000800
          $features['fma']          = $reg.ecx -band 0x00001000
          $features['cx16']        = $reg.ecx -band 0x00002000
          $features['xtpr']        = $reg.ecx -band 0x00004000
          $features['pdcm']        = $reg.ecx -band 0x00008000
          $features['pcid']        = $reg.ecx -band 0x00020000
          $features['dca']          = $reg.ecx -band 0x00040000
          $features['sse4_1']      = $reg.ecx -band 0x00080000
          $features['sse4_2']      = $reg.ecx -band 0x00100000
          $features['x2apic']      = $reg.ecx -band 0x00200000
          $features['movbe']        = $reg.ecx -band 0x00400000
          $features['popcnt']      = $reg.ecx -band 0x00800000
          $features['tsc_deadline'] = $reg.ecx -band 0x01000000
          $features['aes']          = $reg.ecx -band 0x02000000
          $features['xsave']        = $reg.ecx -band 0x04000000
          $features['osxsave']      = $reg.ecx -band 0x08000000
          $features['avx']          = $reg.ecx -band 0x10000000
          $features['f16c']        = $reg.ecx -band 0x20000000
          $features['rdrnd']        = $reg.ecx -band 0x40000000
          $features['hypervisor']  = $reg.ecx -band 0x80000000
        }
       
        $leave = New-Object PSObject -Property (Get-Blocks $buf -AsInteger)
        $low += $leave
      }
      # high leaves
      $cpuid.Invoke(0x80000000, $buf)
      $ids = (Get-Blocks $buf -AsInteger).eax
      $top = @()
      $name = '' # брэндовая строка
      for ($i = 0x80000000; $i -le $ids; $i++) {
        $cpuid.Invoke($i, $buf)
       
        if ($i -eq 0x80000001) {
          $reg = Get-Blocks $buf -AsInteger
         
          $features['syscall']      = $reg.edx -band 0x00000800
          $features['mp']            = $reg.edx -band 0x00080000
          $features['nx']            = $reg.edx -band 0x00100000
          $features['mmxext']        = $reg.edx -band 0x00400000
          $features['fxsr_opt']      = $reg.edx -band 0x02000000
          $features['pdpe1gb']      = $reg.edx -band 0x04000000
          $features['rdtscp']        = $reg.edx -band 0x08000000
          $features['lm']            = $reg.edx -band 0x20000000
          $features['3dnowext']      = $reg.edx -band 0x40000000
          $features['3dnow']        = $reg.edx -band 0x80000000
         
          $features['lahf_lm']      = $reg.ecx -band 0x00000001
          $features['cmp_legacy']    = $reg.ecx -band 0x00000002
          $features['svm']          = $reg.ecx -band 0x00000004
          $features['extapic']      = $reg.ecx -band 0x00000008
          $features['cr8_legacy']    = $reg.ecx -band 0x00000010
          $features['abm']          = $reg.ecx -band 0x00000020
          $features['sse4a']        = $reg.ecx -band 0x00000040
          $features['misalingsse']  = $reg.ecx -band 0x00000080
          $features['3dnowprefetch'] = $reg.ecx -band 0x00000100
          $features['osvw']          = $reg.ecx -band 0x00000200
          $features['ibs']          = $reg.ecx -band 0x00000400
          $features['xop']          = $reg.ecx -band 0x00000800
          $features['skinit']        = $reg.ecx -band 0x00001000
          $features['wdt']          = $reg.ecx -band 0x00002000
          $features['lwp']          = $reg.ecx -band 0x00008000
          $features['fma4']          = $reg.ecx -band 0x00010000
          $features['tce']          = $reg.ecx -band 0x00020000
          $features['nodeid_msr']    = $reg.ecx -band 0x00080000
          $features['tbm']          = $reg.ecx -band 0x00200000
          $features['topoext']      = $reg.ecx -band 0x00400000
          $features['perfctr_core']  = $reg.ecx -band 0x00800000
          $features['perfctr_nb']    = $reg.ecx -band 0x01000000
          $features['dbx']          = $reg.ecx -band 0x04000000
          $features['perftsc']      = $reg.ecx -band 0x08000000
          $features['pcx_l2i']      = $reg.ecx -band 0x10000000
        }
       
        if ($i -eq 0x80000002 -or $i -eq 0x80000003 -or $i -eq 0x80000004) {
          $name += "$((
            $reg = Get-Blocks $buf -AsString
          ).eax)$($reg.ebx)$($reg.ecx)$($reg.edx)"
        }
       
        $leave = New-Object PSObject -Property (Get-Blocks $buf -AsInteger)
        $top += $leave
      }
     
      $cpuid = New-Object PSObject -Property @{
        Vendor          = $vendor
        Name            = $name.Trim()
        SteppingId      = $stepping
        Model          = $model
        Family          = $family
        LogicalCPUCount = $logiccpu
        Features        = $features.Keys | ForEach-Object {
          if ($features[$_]) { $_ }
        } | Sort-Object
        LowLeaves      = $low
        HighLeaves      = $top
      } | Select-Object (
        'Vendor', 'Name', 'SteppingId', 'Model', 'Family',
        'LogicalCPUCount', 'Features', 'LowLeaves', 'HighLeaves'
      )
    }
    catch { $_ }
    finally {
      if ($ptr) { [void]$VirtualFree.Invoke($ptr, [UIntPtr]::Zero, 0x8000) }
    }
  }
  end {
    $cpuid
    $collect | ForEach-Object { [void]$ta::Remove($_) }
  }
}

Пример:
Код:

PS C:\> $cpuid = Get-CpuId
PS C:\> $cpuid.Features
# вывод фич процессора
...

Надеюсь это поможет.

jkadaba 21-11-2016 12:18 2689350

Спасибо! Думаю, вопрос решён.

S0lexV 26-04-2017 12:42 2732447

Доброго времени!
Подскажите пожалуйста такой момент:
Я использую в своей проге код на Ассемблере для получения CPUID. Беру только первый отрезок, т.к. остальные там просто нули.
Например на моей машине CPUID получается: 0001067А
Далее я беру из реестра FeatureSet: 559628286
Складываю CPUID+FeatureSet , Таким образом я получаю уникальный ID для каждой машины.
Вопрос: может ли FeatureSet быть одинаковым у похожих процессоров?
Могут ли у одинаковых процессоров совпадать FeatureSet ?

Другими словами может ли данный прием обеспечить мне уникальность каждой машины ?


Время: 13:28.

Время: 13:28.
© OSzone.net 2001-