Websocket - client doesn't receive data - php

I'm writing some app based on websockets (RFC 6455). Unfortunetly it looks like the client (testing on Chrome 18) doesn't receive data, but the server says it is sending...
Chrome doesn't say anything
Here are main server methods:
private function decode($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
} elseif ($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
} else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
$text = base64_decode($text);
return $text;
}
private function encode($text) {
$text = base64_encode($text);
// 0x1 text frame (FIN + opcode)
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCS', $b1, 126, $length);
else
$header = pack('CCN', $b1, 127, $length);
return $header . $text;
}
protected function process($user, $msg) {
echo '<< '.$msg.N;
if (empty($msg)) {
$this->send($user->socket, $msg);
return;
}
}
protected function send($client, $msg) {
$msg = $this->encode($msg);
echo '>> '.$msg.N;
socket_write($client, $msg, strlen($msg));
}

If you're sending a test message >125 bytes but <65536, your problem might be caused by a faulty format string to pack. I think this one should be 'CCn' (your current code writes the 2 bytes of the length in the wrong order).
If that doesn't help, you could try some client-side logging:
Does the onopen callback run to prove that the initial handshake completed successfully?
Do the onerror or onclose callbacks run, either after connection or after your server sends its message?

Related

How to parse websocket data frames

I get some PHP code, but I don't understand why it's written this way. Can somebody please explain it to me?
function unmask($text)
{
$length = ord($text[1]) & 127;
if ($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
} elseif ($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
} else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
Specifically, what does:
$length = ord($text[1]) & 127
...mean, exactly?

PHP socket - Can someone explain me these functions?

I'm trying to make a php socket server and I found two functions that mask and unmask a text message (frame).
I think I don't understand clearly how it works.
This is the functions :
//encode message for transfer to client
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif ($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header . $text;
}
//unmask incoming framed message
function unmask($text)
{
$length = ord($text[1]) & 127;
if ($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
} elseif ($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
} else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
What I think I've understood :
mask convert the binary representation of the message and create a frame (by concatenating the header) of the right size according to the message length. (by adding bytes with pack() right?)
unmask -> reverse process.
What I don't understand :
What is the purpose of this variable $b1 used in mask? The syntax of this code is not clear for me.
$b1 = 0x80 | (0x1 & 0x0f);
That line is a bit strange to write that way, but here is what is going on. & is a binary AND operator, which takes the two values and returns only the bits that match. 0x1 is 00000001 and 0x0f is 00001111 in binary.
00000001
&00001111
=00000001
so (0x1 & 0x0f) is just 0x1 or 1.
The | operator is like &, but is a binary OR. If either side has a 1, the result will be a 1. 0x80 is 01000000, so
01000000
|00000001
=01000001
So the total result is 0x81. Why not just write $b1 = 0x81? I'm guessing that the author of this code copied it from some C code where the 0x1 part was a variable:
byte b1 = 0x80 | (someVariable & 0x0f);
In this case, the binary & with 0x0f ensures that only the last 4 bits of someVariable will be used, and the first 4 bits of b1 will always be 0x8 (which is probably necessary according to the frame specification).

Encoding/Unmasking Websocket binary data in PHP

I have set up a PHP WebSocket server that is able to read string data from clients. My question is on how to handle binary data types. Below is the code on the client side that records microphone input as a Float32Array object and sends the data over a WebSocket connection in binary.
websocket = new WebSocket("ws://...");
websocket.binaryType = "arraybuffer";
recorder.onaudioprocess = function(stream) {
var inputData = stream.inputBuffer.getChannelData(0);
websocket.send(inputData);
}
As for the server side, I am using the following functions which I've found online for encoding/unmasking.
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if( $length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCS', $b1, 126, $length);
elseif ($length >= 65536)
$header = pack('CCN', $b1, 127, $length);
return $header.$text;
}
function unmask($payload)
{
$length = ord($payload[1]) & 127;
if($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
$len = (ord($payload[2]) << 8) + ord($payload[3]);
}
elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
$len = (ord($payload[2]) << 56) + (ord($payload[3]) << 48) +
(ord($payload[4]) << 40) + (ord($payload[5]) << 32) +
(ord($payload[6]) << 24) +(ord($payload[7]) << 16) +
(ord($payload[8]) << 8) + ord($payload[9]);
}
else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
$len = $length;
}
$text = '';
for ($i = 0; $i < $len; ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
The code works great except they only work on string data and not for binary type. My question is how do I handle binary type and push them to clients?
I don't really get what you are trying to do. WebRTC can be used to create Peer2Peer connections. Thus you would use your PHP Server as a signaling server. The actual data is not passed to your server unless it is needed for forwarding because a p2p connection could not be established. Is that what you want to do?
Otherwise create a RtcPeerConnection and send data from peer to peer as it is intended by webrtc.

How to send WebSocket hybi-17 frame with php server

I try to implement a WebSocket Server in PHP handshaking and recieving data works well but if i try to send data to the client, Chrome 19 says
"A server must not mask any frames that it sends to the client."
But I don't mask the data.
My code looks like this:
function wrap($msg=""){
$byte1 = 0x80 | (0x1 & 0x0f);
if(strlen($msg) <= 125){
$header = pack('CC', $byte1, strlen($msg));
}
elseif(strlen($msg) >= 126 && strlen($msg) <= 65535){
$header = pack('CCn', $byte1, 126, strlen($msg));
}
else{
$header = pack('CCN', $byte1, 126, strlen($msg));
}
$this->log($header);
return $header.$msg;
}
I send it to the Client using socket_write()
EDIT: Firefox 13 closes the connection too
I had the same problem: for some messages sent from the server there was no response in the browser, for some the error "A server must not mask any frames ..." was displayed, though I did not add any mask.
The reason was in the handshake sent.
The handshake was:
"HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
...
"WebSocket-Location: ws://{$host}{$resource}\r\n\r\n" . chr(0)
That chr(0) was the reason, after I removed it everything works.
function for encoding the messages:
protected function hybi10Encode($payload, $type = 'text', $masked = true) {
$frameHead = array();
$frame = '';
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
// first byte indicates FIN, Text-Frame (10000001):
$frameHead[0] = 129;
break;
case 'close':
// first byte indicates FIN, Close Frame(10001000):
$frameHead[0] = 136;
break;
case 'ping':
// first byte indicates FIN, Ping frame (10001001):
$frameHead[0] = 137;
break;
case 'pong':
// first byte indicates FIN, Pong frame (10001010):
$frameHead[0] = 138;
break;
}
// set mask and payload length (using 1, 3 or 9 bytes)
if ($payloadLength > 65535) {
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 255 : 127;
for ($i = 0; $i < 8; $i++) {
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
}
// most significant bit MUST be 0 (close connection if frame too big)
if ($frameHead[2] > 127) {
$this->close(1004);
return false;
}
} elseif ($payloadLength > 125) {
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 254 : 126;
$frameHead[2] = bindec($payloadLengthBin[0]);
$frameHead[3] = bindec($payloadLengthBin[1]);
} else {
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
}
// convert frame-head to string:
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
// generate a random mask:
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
// append payload to frame:
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}

Get Version of exe via PHP

Is getting the version of a exe possible with php? I'd like to print the version of a file that can be downloaded...
Windows exe and php is running on linux server
I wanted the same thing, so I coded this:
It returns FALSE, if it can't get the version info, or an ARRAY of four elements with file version fields (numbers, which are separated by .) It works only for 32-bit PE files (as I had no need for other formats).
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++)
{
$SecHdr = fread($handle, 40);
if (substr($SecHdr, 0, 5) == '.rsrc')
{
$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;
}
I recently moved our hosting from Windows to Linux. This was quite easy to do with VBScript given the Microsoft objects, but on Linux and PHP I couldn't find anything. We wrote this function in PHP to scan the .exe file and extract the "Product Version" that a VB.Net application had in it. You can change the $key to be whatever string you can find that has the version info and a null terminator.
Note that this scans the files in 64k chunks looking for the $key string. If you have a large .exe, it may take a few seconds. My .exe is 52k so it is nearly instant. If you have a larger exe, you can change the scan.
<?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");
?>
On a win32 machine you can use the COM extension and FileSystemObject.GetFileVersion() method to retrieve the version info.
e.g.
$path = getenv('SystemRoot').'\\NOTEPAD.EXE';
$fso = new COM('Scripting.FileSystemObject');
echo $path, ' : ', $fso->GetFileVersion($path);
prints (on my machine) C:\WINDOWS\NOTEPAD.EXE : 5.1.2600.5512
I am assuming you're not on Windows, and you mean the Version information that can be stored in Windows executables, and pops up in the properties dialog for such a file in Windows Explorer.
This information seems to be stored in the VS_VERSION_INFO block of an executable (see for example this question). I don't know any tool that extracts this information in a simple way, not even on Windows itself.
There seem to be several ways to get hold of this information via the Windows API (See a Perl example here) but I can't see any approach that works "from scratch" by just parsing the executable.
If you dig around a bit, you might be able to find a file format description that explains how to read the VS_VERSION_INFO information from an EXE file. Be prepared for a lot of work to get this to work reliably, though.
Be prepared to invest a lot of time and effort if you want to do this.
I combined the answers together and added the NamedDirs fix. Also want to highlight not to use the code by NeuD, the offset should remain 16; 14 is definitely wrong. Hope it helps someone.
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;
}
Elaborating on the answer Toni provided
One of the projects at work had a custom attribute baked into that resource section. On a linux machine we didnt want to install perl to use the windows function to get the values.
Instead one can use this variant of the GetFileVersion to get any value you are seeking. (FileVersion, ProductVersion, ProductName, CompanyName, CompanyWebsite)
It should return false for values that dont exist, and it functions rather quick.
$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');
For me to get the code from Toni to work I had to do a small change:
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;
}
Where I only changed 14 to 16 in the line
$NumDirs=unpack("v",substr($Info,16,2));
Maybe this has something to do with what j_schultz already added in the comments.
I wrote a small code that will pull the file version directly from the executable.
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 command (optional):
#php "%~dp0\exeparser.php" %*
Result:

Categories