通过PHP获取exe版本

时间:2010-01-08 17:32:28

标签: php

使用php获取exe的版本?我想打印一个可以下载的文件版本......

Windows exe和php在linux服务器上运行

8 个答案:

答案 0 :(得分:21)

我想要同样的东西,所以我编码了这个:  它返回FALSE,如果它无法获取版本信息,或者返回带有文件版本字段的四个元素的ARRAY(数字,用\ n分隔。)它仅适用于32位PE文件(因为我不需要其他文件)格式)。

function GetFileVersion($FileName) {

$handle=fopen($FileName,'rb');
if (!$handle) return FALSE;
$Header=fread ($handle,64);
if (substr($Header,0,2)!='MZ') return FALSE;
$PEOffset=unpack("V",substr($Header,60,4));
if ($PEOffset[1]<64) return FALSE;
fseek($handle,$PEOffset[1],SEEK_SET);
$Header=fread ($handle,24);
if (substr($Header,0,2)!='PE') return FALSE;
$Machine=unpack("v",substr($Header,4,2));
if ($Machine[1]!=332) return FALSE;
$NoSections=unpack("v",substr($Header,6,2));
$OptHdrSize=unpack("v",substr($Header,20,2));
fseek($handle,$OptHdrSize[1],SEEK_CUR);
$ResFound=FALSE;
for ($x=0;$x<$NoSections[1];$x++) {      //$x fixed here
    $SecHdr=fread($handle,40);
    if (substr($SecHdr,0,5)=='.rsrc') {         //resource section
        $ResFound=TRUE;
        break;
    }
}
if (!$ResFound) return FALSE;
$InfoVirt=unpack("V",substr($SecHdr,12,4));
$InfoSize=unpack("V",substr($SecHdr,16,4));
$InfoOff=unpack("V",substr($SecHdr,20,4));
fseek($handle,$InfoOff[1],SEEK_SET);
$Info=fread($handle,$InfoSize[1]);
$NumDirs=unpack("v",substr($Info,14,2));
$InfoFound=FALSE;
for ($x=0;$x<$NumDirs[1];$x++) {
    $Type=unpack("V",substr($Info,($x*8)+16,4));
    if($Type[1]==16) {             //FILEINFO resource
        $InfoFound=TRUE;
        $SubOff=unpack("V",substr($Info,($x*8)+20,4));
        break;
    }
}
if (!$InfoFound) return FALSE;
$SubOff[1]&=0x7fffffff;
$InfoOff=unpack("V",substr($Info,$SubOff[1]+20,4)); //offset of first FILEINFO
$InfoOff[1]&=0x7fffffff;
$InfoOff=unpack("V",substr($Info,$InfoOff[1]+20,4));    //offset to data
$DataOff=unpack("V",substr($Info,$InfoOff[1],4));
$DataSize=unpack("V",substr($Info,$InfoOff[1]+4,4));
$CodePage=unpack("V",substr($Info,$InfoOff[1]+8,4));
$DataOff[1]-=$InfoVirt[1];
$Version=unpack("v4",substr($Info,$DataOff[1]+48,8));
$x=$Version[2];
$Version[2]=$Version[1];
$Version[1]=$x;
$x=$Version[4];
$Version[4]=$Version[3];
$Version[3]=$x;
return $Version;
}

答案 1 :(得分:11)

在win32计算机上,您可以使用COM extensionFileSystemObject.GetFileVersion()方法检索版本信息。 e.g。

$path = getenv('SystemRoot').'\\NOTEPAD.EXE';
$fso = new COM('Scripting.FileSystemObject');
echo $path, ' : ', $fso->GetFileVersion($path);

打印(在我的机器上)C:\WINDOWS\NOTEPAD.EXE : 5.1.2600.5512

答案 2 :(得分:11)

我最近将我们的托管从Windows迁移到了Linux。考虑到Microsoft对象,使用VBScript非常容易,但在Linux和PHP上我找不到任何东西。我们在PHP中编写了这个函数来扫描.exe文件并提取VB.Net应用程序中的“产品版本”。您可以将$ key更改为您可以找到的具有版本信息和空终止符的任何字符串。

请注意,这会扫描64k块中的文件,以查找$ key字符串。如果你有一个大的.exe,可能需要几秒钟。我的.exe是52k所以它几乎是即时的。如果您有更大的exe,则可以更改扫描。

<?php

function get_product_version($file_name)
{
   $key = "P\x00r\x00o\x00d\x00u\x00c\x00t\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00\x00\x00";
   $fptr = fopen($file_name, "rb");
   $data = "";
   while (!feof($fptr))
   {
      $data .= fread($fptr, 65536);
      if (strpos($data, $key)!==FALSE)
         break;
      $data = substr($data, strlen($data)-strlen($key));
   }
   fclose($fptr);
   if (strpos($data, $key)===FALSE)
      return "";
   $pos = strpos($data, $key)+strlen($key);
   $version = "";
   for ($i=$pos; $data[$i]!="\x00"; $i+=2)
      $version .= $data[$i];
   return $version;
}

echo get_product_version("/path_to_file/foo.exe");
?>

答案 3 :(得分:5)

我假设你不在Windows上,你的意思是可以存储在Windows可执行文件中的版本信息,并在Windows资源管理器中弹出这样一个文件的属性对话框。

此信息似乎存储在可执行文件的VS_VERSION_INFO块中(请参阅例如this question)。我不知道任何工具以简单的方式提取这些信息,甚至在Windows本身也是如此。

似乎有几种方法可以通过Windows API获取此信息(请参阅Perl示例here),但我无法通过解析可执行文件看到任何“从头开始”工作的方法。

如果您稍微挖掘一下,您可能会找到一个文件格式说明,解释如何从EXE文件中读取VS_VERSION_INFO信息。但是,要做好大量的工作,以使其可靠地工作。

如果你想这样做,请准备好投入大量的时间和精力。

答案 4 :(得分:1)

阐述托尼提供的答案 其中一个项目在该资源部分中有一个自定义属性。在linux机器上,我们不想安装perl来使用windows函数来获取值。 相反,可以使用GetFileVersion的这个变体来获取您正在寻找的任何值。 (FileVersion,ProductVersion,ProductName,CompanyName,CompanyWebsite) 对于不存在的值,它应该返回false,并且它的功能相当快。     

    $handle=fopen($FileName,'rb');
    if (!$handle) return FALSE;
    $Header=fread ($handle,64);
    if (substr($Header,0,2)!='MZ') return FALSE;
    $PEOffset=unpack("V",substr($Header,60,4));
    if ($PEOffset[1]<64) return FALSE;
    fseek($handle,$PEOffset[1],SEEK_SET);
    $Header=fread ($handle,24);
    if (substr($Header,0,2)!='PE') return FALSE;
    $Machine=unpack("v",substr($Header,4,2));
    if ($Machine[1]!=332) return FALSE;
    $NoSections=unpack("v",substr($Header,6,2));
    $OptHdrSize=unpack("v",substr($Header,20,2));
    fseek($handle,$OptHdrSize[1],SEEK_CUR);
    $ResFound=FALSE;
    for ($x=0;$x<$NoSections[1];$x++) {
            $SecHdr=fread($handle,40);
            if (substr($SecHdr,0,5)=='.rsrc') {         //resource section
                    $ResFound=TRUE;
                    break;
            }
    }

    if (!$ResFound) return FALSE;
    $InfoVirt=unpack("V",substr($SecHdr,12,4));
    $InfoSize=unpack("V",substr($SecHdr,16,4));
    $InfoOff=unpack("V",substr($SecHdr,20,4));
    fseek($handle,$InfoOff[1],SEEK_SET);
    $Info=fread($handle,$InfoSize[1]);
    $NumDirs=unpack("v",substr($Info,14,2));
    $InfoFound=FALSE;
    for ($x=0;$x<$NumDirs[1];$x++) {
            $Type=unpack("V",substr($Info,($x*8)+16,4));
            if($Type[1]==16) {                          //FILEINFO resource
                    $InfoFound=TRUE;
                    $SubOff=unpack("V",substr($Info,($x*8)+20,4));
                    //echo $Info;
                    break;
            }
    }
    if (!$InfoFound) return FALSE;

    // i bypassed this, but if you knew the layout you could prolly do a little better then $ulgyRemainderOfData
    /*
    $SubOff[1]&=0x7fffffff;
    $InfoOff=unpack("V",substr($Info,$SubOff[1]+20,4)); //offset of first FILEINFO
    $InfoOff[1]&=0x7fffffff;
    $InfoOff=unpack("V",substr($Info,$InfoOff[1]+20,4));    //offset to data
    $DataOff=unpack("V",substr($Info,$InfoOff[1],4));
    $DataSize=unpack("V",substr($Info,$InfoOff[1]+4,4));
    $CodePage=unpack("V",substr($Info,$InfoOff[1]+8,4));
    $DataOff[1]-=$InfoVirt[1];
    $Version=unpack("v4",substr($Info,$DataOff[1]+48,8));

    // swap 1-2 3-4 / endian ecoding issue
    $x=$Version[2];
    $Version[2]=$Version[1];
    $Version[1]=$x;
    $x=$Version[4];
    $Version[4]=$Version[3];
    $Version[3]=$x;
    return $Version;
    */

    //view data...
    //echo print_r(explode("\x00\x00\x00", $Info));
    // could prolly substr on VS_VERSION_INFO
    $encodedKey = implode("\x00",str_split($seeking));
    $StartOfSeekingKey = strpos($Info, $encodedKey);
    if ($StartOfSeekingKey !== false) {
        $ulgyRemainderOfData = substr($Info, $StartOfSeekingKey);

        $ArrayOfValues = explode("\x00\x00\x00", $ulgyRemainderOfData);
        // the key your are seeking is 0, where the value is one
        return trim($ArrayOfValues[1]);
    }

    return false;
}


$fileVersion = GetValueOfSeeking("./the/path/to/some.exe", 'FileVersion');
$myAttribute = GetValueOfSeeking("./the/path/to/some.exe", 'CustomAttribute');

答案 5 :(得分:1)

我将答案组合在一起并添加了NamedDirs修复程序。另外要强调不要使用NeuD代码,偏移量应保持为16; 14绝对是错的。希望它可以帮到某人。

function GetFileVersion($FileName)
{
    return GetValueOfSeeking($FileName, "FileVersion");
}

function GetValueOfSeeking($FileName, $seeking)
{
    $handle = fopen($FileName, 'rb');
    if (!$handle) return FALSE;
    $Header = fread($handle, 64);

    if (substr($Header, 0, 2) != 'MZ') return FALSE;

    $PEOffset = unpack("V", substr($Header, 60, 4));
    if ($PEOffset[1]<64) return FALSE;

    fseek($handle, $PEOffset[1], SEEK_SET);
    $Header = fread ($handle, 24);

    if (substr($Header, 0, 2) != 'PE') return FALSE;

    $Machine = unpack("v", substr($Header, 4, 2));
    if ($Machine[1] != 332) return FALSE;

    $NoSections = unpack("v", substr($Header, 6, 2));
    $OptHdrSize = unpack("v", substr($Header, 20, 2));
    fseek($handle, $OptHdrSize[1], SEEK_CUR);

    $ResFound = FALSE;
    for ($x = 0; $x < $NoSections[1]; $x++)
    {
        //$x fixed here
        $SecHdr = fread($handle, 40);
        if (substr($SecHdr, 0, 5) == '.rsrc')
        {
            //resource section
            $ResFound = TRUE;
            break;
        }
    }

    if (!$ResFound) return FALSE;
    $InfoVirt = unpack("V", substr($SecHdr, 12, 4));
    $InfoSize = unpack("V", substr($SecHdr, 16, 4));
    $InfoOff = unpack("V", substr($SecHdr, 20, 4));

    fseek($handle, $InfoOff[1], SEEK_SET);
    $Info = fread($handle, $InfoSize[1]);

    $NumNamedDirs = unpack("v",substr($Info, 12, 2));
    $NumDirs = unpack("v", substr($Info, 14, 2));

    $InfoFound = FALSE;
    for ($x = 0; $x < ($NumDirs[1] + $NumNamedDirs[1]); $x++)
    {
        $Type = unpack("V", substr($Info, ($x * 8) + 16, 4));
        if($Type[1] == 16)
        {
            //FILEINFO resource
            $InfoFound = TRUE;
            $SubOff = unpack("V", substr($Info, ($x * 8) + 20, 4));
            break;
        }
    }

    if (!$InfoFound) return FALSE;

    if (0)
    {
        $SubOff[1]  &= 0x7fffffff;
        $InfoOff    = unpack("V", substr($Info, $SubOff[1] + 20, 4)); //offset of first FILEINFO
        $InfoOff[1] &= 0x7fffffff;
        $InfoOff    = unpack("V", substr($Info, $InfoOff[1] + 20, 4));    //offset to data
        $DataOff    = unpack("V", substr($Info, $InfoOff[1], 4));
        $DataSize   = unpack("V", substr($Info, $InfoOff[1] + 4, 4));
        $CodePage   = unpack("V", substr($Info, $InfoOff[1] + 8, 4));
        $DataOff[1] -= $InfoVirt[1];
        $Version    = unpack("v4", substr($Info, $DataOff[1] + 48, 8));
        $x          = $Version[2];
        $Version[2] = $Version[1];
        $Version[1] = $x;
        $x          = $Version[4];
        $Version[4] = $Version[3];
        $Version[3] = $x;

        return $Version;
    }

    //view data...
    //echo print_r(explode("\x00\x00\x00", $Info));
    // could prolly substr on VS_VERSION_INFO
    $encodedKey = implode("\x00",str_split($seeking));
    $StartOfSeekingKey = strpos($Info, $encodedKey);
    if ($StartOfSeekingKey !== false) {
        $ulgyRemainderOfData = substr($Info, $StartOfSeekingKey);
        $ArrayOfValues = explode("\x00\x00\x00", $ulgyRemainderOfData);
        // the key your are seeking is 0, where the value is one
        return trim($ArrayOfValues[1]);
    }

    return false;
}

答案 6 :(得分:0)

为了让我从Toni获得代码,我必须做一些小改动:

function GetFileVersion($FileName) {

$handle=fopen($FileName,'rb');
if (!$handle) return FALSE;
$Header=fread ($handle,64);
if (substr($Header,0,2)!='MZ') return FALSE;
$PEOffset=unpack("V",substr($Header,60,4));
if ($PEOffset[1]<64) return FALSE;
fseek($handle,$PEOffset[1],SEEK_SET);
$Header=fread ($handle,24);
if (substr($Header,0,2)!='PE') return FALSE;
$Machine=unpack("v",substr($Header,4,2));
if ($Machine[1]!=332) return FALSE;
$NoSections=unpack("v",substr($Header,6,2));
$OptHdrSize=unpack("v",substr($Header,20,2));
fseek($handle,$OptHdrSize[1],SEEK_CUR);
$ResFound=FALSE;
for ($x=0;$x<$NoSections[1];$x++) {      //$x fixed here
    $SecHdr=fread($handle,40);
    if (substr($SecHdr,0,5)=='.rsrc') {         //resource section
        $ResFound=TRUE;
        break;
    }
}
if (!$ResFound) return FALSE;
$InfoVirt=unpack("V",substr($SecHdr,12,4));
$InfoSize=unpack("V",substr($SecHdr,16,4));
$InfoOff=unpack("V",substr($SecHdr,20,4));
fseek($handle,$InfoOff[1],SEEK_SET);
$Info=fread($handle,$InfoSize[1]);
$NumDirs=unpack("v",substr($Info,16,2));
$InfoFound=FALSE;
for ($x=0;$x<$NumDirs[1];$x++) {
    $Type=unpack("V",substr($Info,($x*8)+16,4));
    if($Type[1]==16) {             //FILEINFO resource
        $InfoFound=TRUE;
        $SubOff=unpack("V",substr($Info,($x*8)+20,4));
        break;
    }
}
if (!$InfoFound) return FALSE;
$SubOff[1]&=0x7fffffff;
$InfoOff=unpack("V",substr($Info,$SubOff[1]+20,4)); //offset of first FILEINFO
$InfoOff[1]&=0x7fffffff;
$InfoOff=unpack("V",substr($Info,$InfoOff[1]+20,4));    //offset to data
$DataOff=unpack("V",substr($Info,$InfoOff[1],4));
$DataSize=unpack("V",substr($Info,$InfoOff[1]+4,4));
$CodePage=unpack("V",substr($Info,$InfoOff[1]+8,4));
$DataOff[1]-=$InfoVirt[1];
$Version=unpack("v4",substr($Info,$DataOff[1]+48,8));
$x=$Version[2];
$Version[2]=$Version[1];
$Version[1]=$x;
$x=$Version[4];
$Version[4]=$Version[3];
$Version[3]=$x;
return $Version;
}

我只在行中改变了14到16

$NumDirs=unpack("v",substr($Info,16,2));

这可能与j_schultz已在评论中添加的内容有关。

答案 7 :(得分:0)

我写了一个小代码,可以直接从可执行文件中提取文件版本。

PHP

<?php

function exeparser_fileversion($file) {
    $parser_model = array('begin'=>"F\x00i\x00l\x00e\x00V\x00e\x00r\x00s\x00i\x00o\x00n",'end'=>"\x00\x00\x00");
    if (file_exists($file) && is_readable($file)) {
        $version = file_get_contents($file);
        $version = explode($parser_model['begin'], $version);
        $version = explode($parser_model['end'], $version[1]);
        $version = str_replace("\x00", null, $version[1]);
        return ((!empty($version) ? "\x1b[32m$file version: $version\x1b[0m" : "\x1b[31mNo version\x1b[0m"));
    } else {
        print "\x1b[31m".(is_dir($file) ? "Specified path points to a directory, not a file." : "The specified path to the file may not exist or is not a file at all.")."\x1b[0m";
        return false;
    }
}

@print exeparser_fileversion($argv[1]);

?>


Windows命令(可选):

@php "%~dp0\exeparser.php" %*

结果:

commandline result