~ overflow ~

Archive for November, 2010

Memcached PHP Class

by z3n on Nov.21, 2010, under Coding

Problem:

I needed a realiable memcached php class. I know there should be a bunch of them around, but i wanted to write my own anyway, just for fun maybe.

Solution:

<?php

/**
 * WSA Memcached
 *
 * @category Class
 * @package  WSA/Memcached/
 * @author   Rodrigo Moraes Orph <rodrigo.orph@gmail.com>
 * @license  Copyright 2010 http://www.overflow.biz
 * @version  1.01
 * @note     This is a standalone script, it will be able to work without all the
 *           rest of WSA framework.
 */

interface iWSAMemcached {
	public function set($key, $val);
	public function get($key);
	public function delete($key);
	public function increment($key);
	public function decrement($key);
	public function flush();
	public function debug($bool);
	public function addServer($host, $port);
}

class WSAMemcached implements iWSAMemcached {
	/**
	 * Memcache_id key prefix
	 * Will by applied to all memcache fetch/save related functions
	 *
	 * @string
	 */
	public $key_prefix;

	/**
	 * Always serialize a set string
	 * This will also speed up get function by avoiding is_serialized function
	 *
	 * @bool default true
	 */
	public $always_serialize = true;

	/**
	 * Default Expiration Time of a set in seconds
	 *
	 * @int default 86400
	 */
	public $expires = 86400;

	/**
	 * Connection Max Retries
	 * Sets a limit to retry when connection fails
	 *
	 * @int default 10
	 */
	public $max_retry = 10;

	/**
	 * Connection Retry Delay
	 * Sets the delay in ms between connection retries
	 *
	 * @int default 2500
	 */
	public $retry_delay = 2500;

	/**
	 * Connection Timeout in seconds
	 *
	 * @int default 30
	 */
	public $timeout = 30;

	/**
	 * Enables Persistent Connection Mode
	 *
	 * @bool default false
	 */
	public $pmode = false;

	/**
	 * Change key process hash algorithm
	 *
	 * @string hash() compatible algo default md5 (eg: md5 , sha1 , crc32 , haval160 etc)
	 */
	public $key_hash = "md5";

	/**
	 * Connected Flag
	 *
	 * @bool default true
	 */
	protected $connected = false;

	/**
	 * Memcached object
	 *
	 * @object memcache
	 */
	protected $memcache;

	/**
	 * Memcached host
	 *
	 * @string
	 */
	protected $host;

	/**
	 * Memcached port
	 *
	 * @int
	 */
	protected $port;

	/**
	 * Current retry number
	 *
	 * @int
	 */
	private $retry = 0;

	/**
	 * Constructor
	 *
	 * @param $host string memcached host
	 * @param $port int memcached port
	 * @opt param $key_prefix string memcached key prefix
	 */
	public function __construct($host, $port, $key_prefix = "") {
		$this->host = $host;
		$this->port = $port;
		$this->key_prefix = $key_prefix;
	}

	/**
	 * Destruct
	 *
	 * Will try to gracefully close memcached connection
	 */
	public function __destruct() {
		$this->close();
	}

	/**
	 * Add Server to memcache cluster
	 *
	 * @requires memcache 2.0.0+
	 *
	 * @param $host string memcached host
	 * @param $port int memcached port
	 *
	 * @return $bool
	 */
	public function addServer($host, $port, $persistent = true, $weight = 0, $timeout = 1, $retry_interval = 15, $status = false, $failure_callback = null) {
		return $this->memcache->addServer(
			$host,
			$post,
			$persistent,
			$weight,
			$timeout,
			$retry_interval,
			$status,
			$failure_callback
		);
	}

	/**
	 * Stores a value on memcache
	 *
	 * @param $key string memcache key
	 * @param $val mixed value to be stored
	 * @opt param $flag mixed memcache flag
	 * @opt param $expires int cache expiration in seconds
	 *
	 * @return bool
	 */
	public function set($key, $val, $flag = MEMCACHE_COMPRESSED, $expires = 0, $no_serialize = false) {
		if (empty($expires) || intval($expires) <= 0)
			$expires = $this->expires;

		$this->connect();

		// check key
		if (empty($key))
			throw new Expection("Empty key when setting a value", 2);

		// encode key
		$key = $this->key_process($key);

		// make sure val is a valid entry
		if (!$no_serialize && ($this->always_serialize || is_array($val)))
			$val = serialize($val);

		return $this->memcache->set($key, $val, $flag, $expires);
	}

	/**
	 * Fetches a value on memcache
	 *
	 * @param $key string memcache key
	 *
	 * @return mixed serialized values will be unserialized automatically
	 */
	public function get($key, $no_unserialize = false) {
		if (empty($key))
			throw new Expection("Empty key when fetching a value", 3);

		$this->connect();

		// encode key
		$key = $this->key_process($key);

		// fetch value
		$val = $this->memcache->get($key);

		if (!$no_unserialize && ($this->always_serialize || $this->is_serialized($val)))
			return unserialize($val);
		else
			return $val;
	}

	/**
	 * Deletes a vlue from memcache
	 *
	 * @param $key string memcache key
	 * @opt param $timeout int delete timeout
	 * @note timeout don't work with memcached version i've tested.
	 *
	 * @return bool delete result
	 */
	public function delete($key, $timeout = 0) {
		if (empty($key))
			throw new Expection("Empty key when deleting a value", 5);

		if (empty($timeout) || intval($timeout) <= 0)
			$timeout = 1;

		$this->connect();

		// encode key
		$key = $this->key_process($key);

		// delete value
		return $this->memcache->delete($key); //, $timeout);
	}

	/**
	 * Increment a int value
	 *
	 * @param $key string memcache key
	 *
	 * @return int current value
	 */
	public function increment($key, $val = 1) {
		if (empty($key))
			throw new Expection("Empty key when incrementing a value", 5);

		if (empty($val))
			$val = 1;

		$this->connect();

		// encode key
		$key = $this->key_process($key);

		// increment value
		return $this->memcache->increment($key, abs($val));
	}

	/**
	 * Increment a int value
	 *
	 * @param $key string memcache key
	 *
	 * @return int current value
	 */
	public function decrement($key, $val = 1) {
		if (empty($key))
			throw new Expection("Empty key when decrementing a value", 6);

		if (empty($val))
			$val = 1;

		$this->connect();

		// encode key
		$key = $this->key_process($key);

		// increment value
		return $this->memcache->decrement($key, abs($val));
	}

	/**
	 * Flush - Mark all entries as expired
	 *
	 * @return bool
	 */
	public function flush() {
		$this->connect();

		return $this->memcache->flush();
	}

	/**
	 * Debug - Enables/Disables Memcache debug mode
	 *
	 * @note This is a global function
	 *
	 * @return bool
	 */
	public function debug($bool) {
		return memcache_debug($bool ? "on" : "off");
	}

	/**
	 * Main memcache connector
	 * Will try to connect to memcached, on fail retries a specified number of
	 * times with a configurable delay between attempts; If a connection is
	 * already established will return it's state (bool);
	 *
	 * @return bool
	 */
	protected function connect() {
		if (!$this->connected) {
			$this->memcache = new Memcache();

			// connect to memcache
			if ($this->pmode)
				$ret = $this->memcache->pconnect($this->host, $this->port, $this->timeout);
			else
				$ret = $this->memcache->connect($this->host,  $this->port, $this->timeout);

			if (!$ret && $this->retry < $this->max_retry) {
				$this->retry++;
				usleep($this->retry_delay);
				return $this->connect();
			} elseif (!$ret) {
				throw new Exception("Unable to connect to memcached - Tried: ".$this->max_retry." times", 1);
			}

			$this->connected = $ret;
			return $this->connected;
		}
	}

	/**
	 * Main memcache connection close
	 *
	 * @return bool
	 */
	protected function close() {
		if ($this->connected) {
			$this->memcache->close();
			$this->connected = false;
			$this->memcache = null;

			return true;
		}

		return false;
	}

	/**
	 * Process a key value adding prefix and encoding it to a suitable hash
	 *
	 * @param $key string key value
	 *
	 * @return encoded key value
	 */
	protected function key_process($key) {
		if (empty($key))
			throw new Expection("Empty key when processing key.", 4);

		return hash($this->key_hash, $this->key_prefix.$key);
	}

	/**
	 * Checks if a variable is serialized
	 *
	 * @param $val mixed
	 * @opt param &$result bool return value
	 *
	 * @return bool
	 */
	protected function is_serialized($value, &$result = null) {
		// Bit of a give away this one
		if (!is_string($value)) {
			return false;
		}

		// Serialized false, return true. unserialize() returns false on an
		// invalid string or it could return false if the string is serialized
		// false, eliminate that possibility.
		if ($value === 'b:0;') {
			$result = false;
			return true;
		}

		$length = strlen($value);
		$end = '';

		switch ($value[0]) {
			case 's':
				if ($value[$length - 2] !== '"') {
					return false;
				}
			case 'b':
			case 'i':
			case 'd':
				// This looks odd but it is quicker than isset()ing
				$end .= ';';
			case 'a':
			case 'O':
				$end .= '}';

			if ($value[1] !== ':') {
				return false;
			}

			switch ($value[2]) {
				case 0:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
				case 7:
				case 8:
				case 9:
				break;

				default:
					return false;
			}
			case 'N':
				$end .= ';';

				if ($value[$length - 1] !== $end[0]) {
					return false;
				}
			break;

			default:
				return false;
		}

		if (($result = @unserialize($value)) === false) {
			$result = null;
			return false;
		}
		return true;
	}
}

More:

PHPClasses.org Project

Leave a Comment :, , , , more...

Hacking eval and base64 php “encrypt”

by z3n on Nov.17, 2010, under Coding

Problem:

There are some paranoid coders that nest many eval + base64 one inside the other in order to protect the code. This is not a real ecrypt method, just a lame way to avoid users from removing the annyoing banner by the cost or overloading the server.

example of this type of scheme:

<?php eval(gzinflate(base64_decode('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'))); ?>

Solution:

I wrote a little function for this before, to break reviewitonline.net’s “protection”. Now i have a full class with better handling and able to decode subfolders.

There you go:

<?php

// (c) z3n - R1V1@100503 - www.overflow.biz - rodrigo.orph@gmail.com

class eval_hack {
	protected $temp;
	protected $dest;

	protected $tdir = array();
	protected $tfile = array();

	protected $ext = array("php");

	public function __construct($folder, $temp = "temp/", $dest = "decoded/") {

		$this->o("(c) z3n - R1V1@100503 - www.overflow.biz - rodrigo.orph@gmail.com");

		$this->temp = $temp;
		$this->dest = $dest;

		if ($this->check())
			$this->process($folder);

	}

	protected function check() {
		if (file_exists($this->temp) && is_writable($this->temp)) {
			if (file_exists($this->dest) && is_writable($this->dest)) {
				return true;
			} else {
				$this->o("Destination path (".$this->dest.") is not writable / don't exists");
			}
		} else {
			$this->o("Temp path (".$this->temp.") is not writable / don't exists");
		}

		return false;
	}

	protected function save($fn, $content) {
		$fn = $this->dest . $fn;
		@ mkdir (dirname($fn), 0777, true);
		return file_put_contents($fn, $content);
	}

	protected function process($folder) {
		if (file_exists($folder) && is_readable($folder)) {
			for ($this->tdir[0] = $folder, $i = 0;$i < count($this->tdir);$i++) {
				$this->o("Processing: ".$this->tdir[$i]."...");
				$this->rddir($this->tdir[$i]);

				for ($j = 0,$k = count($this->tfile), $this->o("Found: ".$k." files");$j < $k;$j++) {
					$fn = $this->tdir[$i] . "/" . $this->tfile[$j];
					if (file_exists($fn) && !is_dir($fn) && filesize($fn) > 0) {
						$ext = strtolower(substr($fn,strrpos($fn,".")-strlen($fn)+1));
						if (in_array($ext,$this->ext)) {
							$this->o("Decoding: ".$fn);
							$str = $this->decode(file_get_contents($fn));
							if ($str === false) {
								$this->o("Error decoding: ".$fn);
							} else { // save file to new path
								$this->save($fn, $str);
							}
						}
					} elseif (is_dir($fn) && $this->tfile[$j] != "." && $this->tfile[$j] != "..") {
						$this->tdir[] = $fn;
					}
				}

				$this->tfile = array();
			}

		} else {
			$this->o("Process path (".$folder.") is invalid");
		}
	}

	protected function rddir($dir) { /* v2.17-OO */
		if (is_dir($dir)) {
			if ($dh = opendir($dir)) {
				while (($file = readdir($dh)) !== false) {
					if ($file != "." && $file != "..") {
						if (is_dir($dir . $file))
							$this->tdir[] = $dir . $file;
						else
							$this->tfile[] = $file;
					}
				}
				closedir($dh);
			}
		}
	}

	public function decode($x, $cut_crap = true) {
		/**
		 * @param $x string with the file
		 * @return false | string
		 */

		for ($i = 0;strpos($x,"eval(") !== false;$i++) {
			$this->o(".", false);
			file_put_contents($this->temp . "x" . $i . ".php",str_replace("eval(","file_put_contents('".$this->temp."y".$i.".php',",$x));
			exec("php ".$this->temp."x".$i.".php");
			unlink($this->temp . "x".$i.".php");
			if ($i > 0)
				unlink($this->temp . "y".($i - 1).".php");

			$x = file_get_contents($this->temp . "y".$i.".php");
		}

		$this->o($i . " nested");

		if (file_exists($this->temp . "y". ($i - 1).".php")) {
			unlink($this->temp . "y" . ($i - 1) . ".php");

			if ($cut_crap) {
				if (substr($x, 0, 7) == "?><?php")
					$x = substr($x, 2);

				if (substr($x, -4) == "?><?")
					$x = substr($x, 0, -4);
			}

			return $x;
		} else {
			return false;
		}
	}

	private function o($msg, $nl = true) {
		echo $msg.($nl ? "\n" : "");
	}

}

if (php_sapi_name() != "cli")
	die("You must run this on CLI");

if (!isset($argv[1]))
	die("Usage: ".$_SERVER['PHP_SELF']." <folder/to/hack/> [temp folder] [destination folder]");

$hack = new eval_hack($argv[1], isset($argv[2]) ? $argv[2] : "temp/", isset($argv[3]) ? $argv[3] : "decoded/");

?>

As for the people who really want to protect their code, you might want to have it on zend or ioncube, although they are also engineering reverible, just a bit harder.

Leave a Comment :, , , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!