i am trying a piece of code.
<?php
$tmp = ord('F'); //gives the decimal value of character F (equals 70)
$tmp = $tmp - 55; //gives 15 - decimal equivalent of 0x0F
$tmp = dechex($tmp); // converts 15 to 0x0F
$fp = fopen("testing.data","wb+");
fwrite($fp,$tmp);
fclose($fp);
?>
When i open the file called testing.data in a hex editor, i see 2 bytes written. The 2 bytes are 0x36 and 0x33.
I am expecting that only 1 byte i.e. 0x0f will be written to the file. This doesn't happen.
Please help me out with this.
If you want to write the byte 0x0f to the file, simply write the character with that ASCII code. You effectively want to undo ord, and the reverse function is chr:
<?php
$tmp = ord('F'); //gives the decimal value of character F (equals 70)
$tmp = $tmp - 55; //gives 15 - decimal equivalent of 0x0F
$tmp = chr($tmp); // converts 15 to a character
$fp = fopen("testing.data","wb+");
fwrite($fp,$tmp);
fclose($fp);
?>
You are writing the string representation of the number 0x0F to the file (which will use 1 byte per character).
In PHP you would use the pack function to create binary strings.
$bindata = pack('n', 0x0F);
file_put_contents('testing.data', $bindata);
Related
I have a large binary buffer in PHP script, finding a specific position. I need to convert 4-byte value to 32bit integer.
$seg = hex2bin("AABBCC00010014AABBCC");
$findStart=3;
echo bin2hex($seg[$findStart+0]);
echo bin2hex($seg[$findStart+1]);
echo bin2hex($seg[$findStart+2]);
echo bin2hex($seg[$findStart+3]);
Prints:
00010014
I need to convert seg[findStart+0 .. findStart+3] to 32bit integer. How to do it in PHP script? This example is a decimal number 65556.
This is exactly the purpose of the unpack function, which takes a format string and extracts data from a binary string.
Looking at the list of format codes, and your example, I believe you want
N: unsigned long (always 32 bit, big endian byte order)
So it would look something like this:
$int = unpack('Nvalue', $seg, $findStart)['value'];
For compatibility with older versions of PHP (<7.1), you can emulate the offset argument by consuming a fixed number of bytes into an ignored variable:
$int = unpack("c{$findStart}ignore/Nvalue", $seg)['value'];
You can use ord() to convert a single byte into an integer, then use the left shift and the bitwise-or operator. The first byte you will shift 24 bits to the left, the second byte you will shift 16 bytes to the left, the third 8 bytes.
$a = ord($seg[$findStart+0]);
$b = ord($seg[$findStart+1]);
$c = ord($seg[$findStart+2]);
$d = ord($seg[$findStart+3]);
$newInt = ($a << 24) | ($b << 16) | ($c << 8) | $d;
I want to encode an int16 value in 15 bits and leave the leftmost bit as a flag. Then I want to read the bytes and decode to decimal the rightmost 15 bits as the integer. Is this possible?
$value = 49;
$packed = pack('S', $value); // returns correct hex
$formatted = $packed ^ 0b1000000000000000; // returns too long number - 4 bytes instead of 2
How can I do this?
Use bitwise operators.
You have got your number in to $packed.
Let the $encoded be your target.
Save the $packed to the $encoded
$encoded = $packed;
$encoded = $encoded | 0b1000000000000000; //Set the MSB
Lets read it back
$encoded = $encoded $ 0b0111111111111111; //Clear the MSB first
$packed = $encoded; //The rest is automatically the value
What is a simple way to convert a 64 bit integer encoded as a hex string to a decimal string on a 32 bit system. It needs to be the full value, it can not be in scientific notation or truncated :/
"0c80000000000063" == "900719925474099299"
"0c80000000000063" != 9.007199254741E+17
PHP's base_convert() and hexdec() don't do the job right.
You need to use BC Math PHP extension (bundled).
First split your input string to get high and low bytes, next convert it to decimal and then do calculation via BC functions like this:
$input = "0C80000000000063";
$str_high = substr($input, 0, 8);
$str_low = substr($input, 8, 8);
$dec_high = hexdec($str_high);
$dec_low = hexdec($str_low);
//workaround for argument 0x100000000
$temp = bcmul ($dec_high, 0xffffffff);
$temp2 = bcadd ($temp, $dec_high);
$result = bcadd ($temp2, $dec_low);
echo $result;
/*
900719925474099299
*/
Have you seen the first comment to hexdec's help page on php.net?
When given large numbers, the hexdec function automatically converts
the value to scientific notation. So, "aa1233123124121241" as a
hexadecimal value will be converted to "3.13725790445E+21". If you're
converting a hexadecimal value that represents a hash value (md5 or
sha), then you need every single bit of that representation to make it
useful. By using the number_format function, you can do that
perfectly. For example :
<?php
// Author: holdoffhunger#gmail.com
// Example Hexadecimal
// ---------------------------------------------
$hexadecimal_string = "1234567890abcdef1234567890abcdef";
// Converted to Decimal
// ---------------------------------------------
$decimal_result = hexdec($hexadecimal_string);
// Print Pre-Formatted Results
// ---------------------------------------------
print($decimal_result);
// Output Here: "2.41978572002E+37"
// .....................................
// Format Results to View Whole All Digits in Integer
// ---------------------------------------------
// ( Note: All fractional value of the
// Hexadecimal variable are ignored
// in the conversion. )
$current_hashing_algorithm_decimal_result = number_format($decimal_result, 0, '', '');
// Print Formatted Results
// ---------------------------------------------
print($current_hashing_algorithm_decimal_result);
// Output Here: "24197857200151253041252346215207534592"
// .....................................
?>
I have to debug an old PHP script from a developer who has left the company. I understand the most part of the code, except the following function. My question: What does...
if($seq == 0x03 || $seq == 0x30)
...mean in context of extracting the signature out of an X.509 certificate?
public function extractSignature($certPemString) {
$bin = $this->ConvertPemToBinary($certPemString);
if(empty($certPemString) || empty($bin))
{
return false;
}
$bin = substr($bin,4);
while(strlen($bin) > 1)
{
$seq = ord($bin[0]);
if($seq == 0x03 || $seq == 0x30)
{
$len = ord($bin[1]);
$bytes = 0;
if ($len & 0x80)
{
$bytes = ($len & 0x0f);
$len = 0;
for ($i = 0; $i < $bytes; $i++)
{
$len = ($len << 8) | ord($bin[$i + 2]);
}
}
if($seq == 0x03)
{
return substr($bin,3 + $bytes, $len);
}
else
{
$bin = substr($bin,2 + $bytes + $len);
}
}
else
{
return false;
}
}
return false;
}
An X.509 certificate contains data in multiple sections (called Tag-Length-Value triplets). Each section starts with a Tag byte, which indicates the data format of the section. You can see a list of these data types here.
0x03 is the Tag byte for the BIT STRING data type, and 0x30 is the Tag byte for the SEQUENCE data type.
So this code is designed to handle the BIT STRING and SEQUENCE data types. If you look at this part:
if($seq == 0x03)
{
return substr($bin,3 + $bytes, $len);
}
else // $seq == 0x30
{
$bin = substr($bin,2 + $bytes + $len);
}
you can see that the function is designed to skip over Sequences (0x30), until it finds a Bit String (0x03), at which point it returns the value of the Bit String.
You might be wondering why the magic number is 3 for Bit String and 2 for Sequence. That is because in a Bit String, the first value byte is a special extra field which indicates how many bits are unused in the last byte of the data. (For example, if you're sending 13 bits of data, it will take up 2 bytes = 16 bits, and the "unused bits" field will be 3.)
Next issue: the Length field. When the length of the Value is less than 128 bytes, the length is simply specified using a single byte (the most significant bit will be 0). If the length is 128 or greater, then the first length byte has bit 7 set, and the remaining 7 bits indicates how many following bytes contain the length (in big-endian order). More description here. The parsing of the length field happens in this section of the code:
$len = ord($bin[1]);
$bytes = 0;
if ($len & 0x80)
{
// length is greater than 127!
$bytes = ($len & 0x0f);
$len = 0;
for ($i = 0; $i < $bytes; $i++)
{
$len = ($len << 8) | ord($bin[$i + 2]);
}
}
After that, $bytes contains the number of extra bytes used by the length field, and $len contains the length of the Value field (in bytes).
Did you spot the error in the code? Remember,
If the length is 128 or greater, then the first length byte has bit 7
set, and the remaining 7 bits indicates how many following bytes
contain the length.
but the code says $bytes = ($len & 0x0f), which only takes the lower 4 bits of the byte! It should be:
$bytes = ($len & 0x7f);
Of course, this error is only a problem for extremely long messages: it will work fine as long as the length value will fit within 0x0f = 15 bytes, meaning the data has to be less than 256^15 bytes. That's about a trillion yottabytes, which ought to be enough for anybody.
As Pateman says above, you just have a logical if, we're just checking if $seq is either 0x30 or 0x03.
I have a feeling you already know that though, so here goes. $seq is the first byte of the certificate, which is probably either the version of the certificate or the magic number to denote that the file is a certificate (also known as "I'm guessing this because 10:45 is no time to start reading RFCs").
In this case, we're comparing against 0x30 and 0x03. These numbers are expressed in hexadecimal (as is every number starting with 0x), which is base-16. This is just really a very convenient shorthand for binary, as each hex digit corresponds to exactly four binary bits. A quick table is this:
0 = 0000
1 = 0001
2 = 0010
3 = 0011
...
...
E = 1110
F = 1111
Equally well, we could have said if($seq == 3 || $seq == 48), but hex is just much easier to read and understand in this case.
I'd hazard a guess that it's a byte-order-independent check for version identifier '3' in an x.509 certificate. See RFC 1422, p7. The rest is pulling the signature byte-by-byte.
ord() gets the value of the ASCII character you pass it. In this case it's checking to see if the ASCII character is either a 0 or end of text (according to this ASCII table).
0x03 and 0x30 are hex values. Look that up and you'll have what $seq is matching to
I have a binary file that is all 8 bit integers. I have tried to use the php unpack() functions but I cant get any of the arguments to work for 1 byte integers. I have tried to combine the data with a dummy byte so that I can use the 'n'/'v' arguments. I am working with a windows machine to do this. Ultimately I would like a function to return an array of integers based on a string of 8 bit binary integers. The code I have tried is below -
$dat_handle = "intergers.dat";
$dat_file = fopen($dat_handle, "rb");
$dat_data = fread($dat_file, 1);
$dummy = decbin(0);
$combined = $dummy.$dat_data;
$result = unpack("n", $combined);
What your looking for is the char datatype. Now there are two version of this, signed (lowercase c) and unsigned (uppercase C). Just use the one that's correct for your data.
<?php
$byte = unpack('c', $byte);
?>
Also, if the data file is just a bunch of bytes and nothing else, and you know it's length, you can do this. (If the length is 16 signed chars in a row.)
<?php
$bytes = unpack('c16', $byte);
?>
If you don't know how many bytes will be in the file, but you know there is only going to be bytes you can use the asterisk code to read until EOF.
<?php
$bytes = unpack('c*', $byte);
?>
The following should do what you want (ord):
$dat_handle = "intergers.dat";
$dat_file = fopen($dat_handle, "rb");
$dat_data = ord(fread($dat_file, 1));
What you are trying to do is retrieve the integer value of the single byte. Because you are reading in single bytes at a time, you will always have exactly one valid ASCII character. ord returns the binary value of that one character.