CX's VBScript Diary

 | 

2009-08-13VBScript で PE ヘッダを読み込む方法

VBScript で PE ヘッダを読み込む方法

17:29 | VBScript で PE ヘッダを読み込む方法 - CX's VBScript Diary を含むブックマーク はてなブックマーク - VBScript で PE ヘッダを読み込む方法 - CX's VBScript Diary

VBScript から EXE/OCX/DLL 作成時のコンパイラのバージョンが取得できないか?と思い、調査してみました。

コンパイラのバージョンは PE ヘッダのリンカバージョンが参考になりそうです。

また、VBScript から、モジュールの PE ヘッダを読み込むには、ADO Stream オブジェクトを使用すれば可能そうです。

 ADDRESS   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F   0123456789ABCDEF 
------------------------------------------------------------------------------
 00000000  4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00   MZ.............. 
 00000010  B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00   ク.......@....... 
 00000020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
 00000030  00 00 00 00 00 00 00 00 00 00 00 00 E0 00 00 00   ................ 
                                               ^^^^^^^^^^^ <-- PE ヘッダ相対位置
 00000040  0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68   ..コ..エ.ヘ!ク.Lヘ!Th 
 00000050  69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F   is program canno 
 00000060  74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20   t be run in DOS  
 00000070  6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00   mode....$....... 
     :                           :                                   :
 000000E0  50 45 00 00 4C 01 03 00 87 52 02 48 00 00 00 00   PE..L...⑲.H.... 
           ^^^^^^^^^^^ PE ヘッダシグネチャ
 000000F0  00 00 00 00 E0 00 0F 01 0B 01 07 0A 00 78 00 00   .............x.. 
                                         ^^^^^ リンカバージョン
 00000100  00 8C 00 00 00 00 00 00 9D 73 00 00 00 10 00 00   ........捏...... 

と、いうことで、以下のやり方で情報を取得してみることにしました。

  1. ADO Stream にてファイル読み込み
  2. IMAGE_DOS_HEADER の e_lfanew(PE ヘッダの開始位置)を取得
  3. PE ヘッダの開始位置に移動し IMAGE_OPTIONAL_HEADER の MajorLinkerVersion を取得
  4. PE ヘッダの開始位置に移動し IMAGE_OPTIONAL_HEADER の MinorLinkerVersion を取得

サンプルコード

' File : ShowLinkerVer.vbs
' Usage : CScript ShowLinkerVer.vbs //Nologo /F:FileName[.exe|.dll|.ocx]
' Description : モジュール作成時のリンカバージョンを表示

Option Explicit

Call Main()

Sub Main()
    Dim param
    Set param = New CmdParamParser
    If param.Initialize( WScript.Arguments ) = False Then
        param.ShowUsage
        WScript.Quit
    End If

    Dim pe
    Set pe = New PEReader
    pe.Load param.FileName
    pe.Show
End Sub

Class CmdParamParser
    Private m_strFileName
    
    Public Property Get FileName
        FileName = m_strFileName
    End Property

    Public Function Initialize( objArgs )
        If objArgs.Count = 0 Then
            Initialize = False
            Exit Function
        End If
        
        m_strFileName = objArgs.Named.Item( "F" ) ' WSH 5.6 以上でのみサポート
        If Len(m_strFileName) = 0 Then
            Initialize = False
            Exit Function
        End If
    
        Initialize = True
    End Function

    Public Sub ShowUsage()
        WScript.Echo "Usage : CScript ShowLinkerVer.vbs //Nologo /F:FileName[.exe|.dll|.ocx]" & vbCrLf
    End Sub
End Class

Class PEReader
    Private m_stm
    Private m_strFileName
    Private m_szBuf
    Private m_nHeaderSize
    Private m_nMajorLinkerVer
    Private m_nMinorLinkerVer
    
    Public Property Get PEHeaderSize
        PEHeaderSize = m_nHeaderSize
    End Property
    
    Public Property Get MajorLinkerVer
        MajorLinkerVer = m_nMajorLinkerVer
    End Property
    
    Public Property Get MinorLinkerVer
        MinorLinkerVer = m_nMinorLinkerVer
    End Property
    
    Public Property Get LinkerVer
        LinkerVer = CStr(MajorLinkerVer) & "." & CStr(MinorLinkerVer)
    End Property

    Public Property Get FileName
        FileName = m_strFileName
    End Property
    
    Private Sub Class_Initialize
        Set m_stm = CreateObject("ADODB.Stream")
        m_stm.Type = 1 ' adTypeBinary
        m_stm.Open
    End Sub
    
    Private Sub Class_Terminate
        Set m_stm = Nothing
    End Sub
    
    Public Function Load( strFileName )
        On Error Resume Next
        m_strFileName = ExpandEnv( strFileName )
        m_stm.LoadFromFile m_strFileName
        If Err.Number <> 0 Then
            Load = False
            Exit Function
        End If
        m_stm.Position = 1
        m_szBuf = m_stm.Read( 512 ) ' 多めに読み込み

        m_nHeaderSize     = GetPEHeaderSize( m_szBuf )
        m_nMajorLinkerVer = GetMajorLinkerVer( m_szBuf )
        m_nMinorLinkerVer = GetMinorLinkerVer( m_szBuf )
        Load = True
    End Function
    
    Public Function ExpandEnv( strFileName )
        Dim strResult
        Dim shell
        Set shell = CreateObject("WScript.Shell")
        strResult = shell.ExpandEnvironmentStrings( strFileName )
        Set shell = Nothing
        ExpandEnv = strResult
    End Function
    
    Public Sub Show()
        WScript.Echo "FileName        = [" & Me.FileName & "]"
        WScript.Echo "PEHeaderSize    = [" & Me.PEHeaderSize & "]"
        WScript.Echo "MajorLinkerVer  = [" & Me.MajorLinkerVer & "]"
        WScript.Echo "MinorLinkerVer  = [" & Me.MinorLinkerVer & "]"
        WScript.Echo "LinkerVer       = [" & Me.LinkerVer & "]"
    End Sub

    Private Function GetPEHeaderSize( szBuf )
        Dim nResult
        Dim nPosition
        Dim nSize

        nPosition = 60 ' IMAGE_DOS_HEADER の e_lfanew の位置
        nSize = 4
        nResult = GetFieldValueFromBinary( szBuf, nPosition, nSize )

        GetPEHeaderSize = nResult
    End Function

    Private Function GetMajorLinkerVer( szBuf )
        Dim nResult
        Dim nPosition
        Dim nSize

        nPosition = GetPEHeaderSize( szBuf )
        nPosition = nPosition + 4 + 20 + 2     ' IMAGE_OPTIONAL_HEADER の MajorLinkerVersion の位置
        nSize = 1
        nResult = GetFieldValueFromBinary( szBuf, nPosition, nSize )

        GetMajorLinkerVer = nResult
    End Function

    Private Function GetMinorLinkerVer( szBuf )
        Dim nResult
        Dim nPosition
        Dim nSize

        nPosition = GetPEHeaderSize( szBuf )
        nPosition = nPosition + 4 + 20 + 2 + 1 ' IMAGE_OPTIONAL_HEADER の MinorLinkerVersion の位置
        nSize = 1
        nResult = GetFieldValueFromBinary( szBuf, nPosition, nSize )

        GetMinorLinkerVer = nResult
    End Function

    Private Function GetFieldValueFromBinary( szBuf, nPosition, nSize )
        Dim nResult

        Dim szField
        szField = MidB( szBuf, nPosition, nSize )
        nResult = ConvertBinaryToNumber( szField, nSize )

        GetFieldValueFromBinary = nResult
    End Function

    Private Function ConvertBinaryToNumber( szBuf, nSize )
        Dim nResult
        nResult = 0
        Dim ch
        Dim i
        For i = 1 To nSize
            ch = AscB( MidB( szBuf, i, 1 ) )
            nResult = nResult + ch * 256 ^ (i-1) ' リトルエンディアンを想定
        Next
        ConvertBinaryToNumber = nResult
    End Function
End Class
コマンド例
CScript ShowLinkerVer.vbs //Nologo /F:"C:\WINDOWS\SYSTEM32\notepad.exe"
実行結果
FileName        = [C:\WINDOWS\SYSTEM32\notepad.exe]
PEHeaderSize    = [224]
MajorLinkerVer  = [7]
MinorLinkerVer  = [10]
LinkerVer       = [7.10]

<リンカバージョンに対応する主な開発環境>
 5.1  … VC++5.0 (Visual Studio 97)
 6.0  … VC++6.0 (Visual Studio 6.0)
 7.0  … VC++7.0 (Visual Studio .NET 2002)
 7.10 … VC++7.1 (Visual Studio .NET 2003)
 8.0  … VC++8.0 (Visual Studio 2005)
 9.0  … VC++9.0 (Visual Studio 2008)
10.0  … VC++10.0 (Visual Studio 2010)
----------------------------------------
 2.25 … Delphi or BCB?

<不明なリンカバージョン>
 3.1  … DBGRID32.OCX, COMCT232.OCX 等
 5.12 … MSDXM.OCX 等
 5.2  … MSFLXGRD.OCX, RICHTX32.OCX, COMDLG32.OCX 等

参考情報

 |