PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Decoder/Decoder.inc.php

https://github.com/bwall/PHP-RFI-Payload-Decoder
PHP | 551 lines | 537 code | 13 blank | 1 comment | 52 complexity | c7a857078fc0cffe23179a8968777225 MD5 | raw file
  1. <?php
  2. class DecodedPayload
  3. {
  4. public $rawPayload = "";
  5. public $decodedPayload = "";
  6. public $origin = "";
  7. public $type = "";
  8. public $hash = "";
  9. public $submitter = "";
  10. public $timestamp = "";
  11. }
  12. class Decoder
  13. {
  14. private $dumpFolder = "temp/";
  15. private $aliases = array();
  16. public $list = array();
  17. public $htmllist = "";
  18. public $details = "";
  19. public function __construct($folder)
  20. {
  21. $this->dumpFolder = $folder;
  22. if ($handle = opendir($folder))
  23. {
  24. while (false !== ($entry = readdir($handle)))
  25. {
  26. if ($entry != "." && $entry != "..")
  27. {
  28. $f = unserialize(gzinflate(base64_decode(file_get_contents($folder.$entry))));
  29. $ctime = strtotime($f->timestamp);
  30. $this->list[$ctime] = $f;
  31. }
  32. }
  33. closedir($handle);
  34. krsort($this->list);
  35. foreach($this->list as $time => $payload)
  36. {
  37. $name = $payload->timestamp;
  38. if(preg_match("/\/([^\/]+)$/", $payload->origin, $matches) != 0)
  39. {
  40. $name = htmlentities($matches[1]);
  41. }
  42. $this->htmllist .= '<a href="?hash='.$payload->hash.'" title="'.htmlentities($payload->origin).'">'.$name.'</a><br/>';
  43. }
  44. }
  45. }
  46. private function GetFileName($uri, $tail = "", $canFail = true)
  47. {
  48. if(file_exists($this->dumpFolder.md5($uri).$tail))
  49. {
  50. if($canFail)
  51. return false;
  52. }
  53. return $this->dumpFolder.md5($uri).$tail;
  54. }
  55. function RemoveComments($str)
  56. {
  57. $done = false;
  58. while($done === false)
  59. {
  60. if(preg_match('/\/\*.*\*\//m', $str, $matches))
  61. {
  62. $str = str_replace($matches[0], "", $str);
  63. }
  64. else
  65. {
  66. $done = true;
  67. }
  68. }
  69. return $str;
  70. }
  71. function ExpandLines($str)
  72. {
  73. $str = preg_replace("/;([^\n])/", ";\n$1", $str);
  74. return $str;
  75. }
  76. function ClearEmptyEvals(&$str)
  77. {
  78. $done = false;
  79. while($done === false)
  80. {
  81. if(preg_match('/eval\(["\'][[:space:]]*["\']\);/m', $str, $matches))
  82. {
  83. $str = str_replace($matches[0], "", $str);
  84. }
  85. else
  86. $done = true;
  87. }
  88. }
  89. function Concatenate($str)
  90. {
  91. if(preg_match_all("/'([^']*)'[\s]*\.[\s]*'([^']*)'/", $str, $matches) != 0)
  92. {
  93. $count = count($matches[0]);
  94. for($i = 0; $i < $count; $i++)
  95. {
  96. $value = $matches[2][$i];
  97. if($str !== preg_replace("/'([^']*)'[\s]*\.[\s]*'([^']*)'/", "'$1$2'", $str))
  98. {
  99. $str = preg_replace("/'([^']*)'[\s]*\.[\s]*'([^']*)'/", "'$1$2'", $str);
  100. }
  101. }
  102. }
  103. if(preg_match_all("/\"([^\"]*)\"[\s]*\.[\s]*\"([^\"]*)\"/", $str, $matches) != 0)
  104. {
  105. $count = count($matches[0]);
  106. for($i = 0; $i < $count; $i++)
  107. {
  108. $value = $matches[2][$i];
  109. if($str !== preg_replace("/\"([^\"]*)\"[\s]*\.[\s]*\"([^\"]*)\"/", "\"$1$2\"", $str))
  110. {
  111. $str = preg_replace("/\"([^\"]*)\"[\s]*\.[\s]*\"([^\"]*)\"/", "\"$1$2\"", $str);
  112. }
  113. }
  114. }
  115. return $str;
  116. }
  117. function Unescape($data)
  118. {
  119. if(preg_match_all('/'.preg_quote('\x', '/').'([a-fA-F0-9][a-fA-F0-9])/', $data, $matches) != 0)
  120. {
  121. $count = count($matches[0]);
  122. for($i = 0; $i < $count; $i++)
  123. {
  124. $value = hexdec($matches[1][$i]);
  125. if($data !== preg_replace('/'.preg_quote('\x', '/').$matches[1][$i]."/", chr($value), $data))
  126. {
  127. $data = preg_replace('/'.preg_quote('\x', '/').$matches[1][$i]."/", chr($value), $data);
  128. }
  129. }
  130. }
  131. if(preg_match_all('/'.preg_quote('\\', '/').'([0-7][0-7]?[0-7]?)/', $data, $matches) != 0)
  132. {
  133. $count = count($matches[0]);
  134. for($i = 0; $i < $count; $i++)
  135. {
  136. $value = octdec($matches[1][$i]);
  137. if($data !== preg_replace('/'.preg_quote('\\', '/').$matches[1][$i]."/", chr($value), $data))
  138. {
  139. $data = preg_replace('/'.preg_quote('\\', '/').$matches[1][$i]."/", chr($value), $data);
  140. }
  141. }
  142. }
  143. return $data;
  144. }
  145. function Decode($funcArray, &$str)
  146. {
  147. $count = count($funcArray);
  148. $funcs = "";
  149. $tail = "";
  150. $toEval = "";
  151. $endEval = "";
  152. for($i = 0; $i < $count; $i++)
  153. {
  154. $funcs .= $funcArray[$i]."[[:space:]]*\([[:space:]]*";
  155. $tail .= '[[:space:]]*\)';
  156. $toEval .= $funcArray[$i]."(";
  157. $endEval .= ")";
  158. }
  159. $endEval .= ";";
  160. if(preg_match('/'.$funcs.'(?<data>"[^"]+")'.$tail.'/mi', $str, $matches))
  161. {
  162. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval)).$matches["data"].$endEval))."'", $str);
  163. return true;
  164. }
  165. else if(preg_match('/'.$funcs.'(?<data>\'[^\']+\')'.$tail.'/m', $str, $matches))
  166. {
  167. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval)).$matches["data"].$endEval))."'", $str);
  168. return true;
  169. }
  170. else if(preg_match('/'.$funcs.'(?<data>\'[^\']+\')/mi', $str, $matches))
  171. {
  172. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval)).$matches["data"].$endEval))."'", $str);
  173. return true;
  174. }
  175. else if(preg_match('/'.$funcs.'(?<data>"[^"]+")/mi', $str, $matches))
  176. {
  177. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval)).$matches["data"].$endEval))."'", $str);
  178. return true;
  179. }
  180. else if(preg_match('/'.$funcs.'(?<data>"[^"]+)/mi', $str, $matches))
  181. {
  182. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval)).$matches["data"].'"'.$endEval))."'", $str);
  183. return true;
  184. }
  185. else if(preg_match('/'.$funcs.'(?<data>\'[^\']+)/mi', $str, $matches))
  186. {
  187. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval)).$matches["data"]."'".$endEval))."'", $str);
  188. return true;
  189. }
  190. else if(preg_match('/'.$funcs.'(?<data>[a-zA-Z0-9\+\/\=]+)'.$tail.'/mi', $str, $matches))
  191. {
  192. $str = str_replace($matches[0], "'".$this->ExpandLines(eval("return ".str_replace("'", "", str_replace('"', "", $toEval))."'".$matches["data"]."'".$endEval))."'", $str);
  193. return true;
  194. }
  195. else
  196. {
  197. return false;
  198. }
  199. }
  200. function AutoDecode(&$str)
  201. {
  202. $str = $this->RemoveComments($str);
  203. $str = $this->ExpandLines($str);
  204. $done = FALSE;
  205. $variables = array();
  206. while($done === FALSE)
  207. {
  208. //concatenation
  209. $str = $this->Concatenate($str);
  210. if($this->Decode(array("gzinflate", "str_rot13", "base64_decode"), $str)
  211. || $this->Decode(array("gzuncompress", "str_rot13", "base64_decode"), $str)
  212. || $this->Decode(array("gzinflate", "base64_decode", "str_rot13"), $str)
  213. || $this->Decode(array('"gzinflate"', '"base64_decode"', '"str_rot13"'), $str)
  214. || $this->Decode(array("gzinflate", "str_rot13"), $str)
  215. || $this->Decode(array("gzuncompress", "str_rot13"), $str)
  216. || $this->Decode(array("gzinflate", "base64_decode"), $str)
  217. || $this->Decode(array('"gzinflate"', '"base64_decode"'), $str)
  218. || $this->Decode(array("gzuncompress", "base64_decode"), $str)
  219. || $this->Decode(array("base64_decode"), $str)
  220. || $this->Decode(array("gzinflate", "str_rot13"), $str)
  221. || $this->Decode(array("base64_decode", "str_rot13"), $str)
  222. || $this->Decode(array('"base64_decode"'), $str)
  223. || $this->Decode(array("'base64_decode'"), $str)
  224. || $this->Decode(array("urldecode"), $str)
  225. )
  226. {
  227. }
  228. else
  229. {
  230. $done = true;
  231. if(preg_match_all('/(\$[[:alnum:]_]+)[[:space:]]*\.=[[:space:]]*([^;]+);/s', $str, $matches) != 0)
  232. {
  233. $count = count($matches[0]);
  234. for($i = 0; $i < $count; $i++)
  235. {
  236. $name = $matches[1][$i];
  237. if(strlen($matches[0][$i]) < 50000 && preg_match_all('/('.preg_quote($name, '/').')[[:space:]]*=[[:space:]]*([^;]+);/s', $str, $m) != 0)
  238. {
  239. $value = $this->Unescape($m[2][count($m[2]) - 1]).".".$this->Unescape($matches[2][$i]);
  240. $value = $this->Concatenate($value);
  241. $value = $this->Unescape($value);
  242. $str = preg_replace('/'.preg_quote($matches[0][$i], '/').'/', $matches[1][$i]." = ".$value.";", $str, 1);
  243. $done = false;
  244. }
  245. }
  246. }
  247. if(preg_match_all('/(\$[[:alnum:]_]+)[[:space:]]*=[[:space:]]*("[^"]+");/s', $str, $matches) != 0)
  248. {
  249. $count = count($matches[0]);
  250. for($i = 0; $i < $count; $i++)
  251. {
  252. $name = $matches[1][$i];
  253. if(in_array($name, $variables) === true)
  254. {
  255. continue;
  256. }
  257. if(preg_match_all('/('.preg_quote($name, '/').')[[:space:]]*=[[:space:]]*([^;]+);/s', $str, $m) != 0)
  258. {
  259. $value = $this->Unescape($m[2][count($m[2]) - 1]);
  260. if($str !== preg_replace('/('.preg_quote($name, '/').')([^<>[:alnum:]_ \=])/m', $value."$2", $str) && strstr($value, $name) === false)
  261. {
  262. $done = false;
  263. array_push($variables, $name);
  264. $str = preg_replace('/('.preg_quote($name, '/').')([^<>[:alnum:]_ \=])/m', $value."$2", $str);
  265. }
  266. }
  267. }
  268. }
  269. if(preg_match_all('/(\$[[:alnum:]_]+)[[:space:]]*=[[:space:]]*(\'[^\']+\');/s', $str, $matches) != 0)
  270. {
  271. $count = count($matches[0]);
  272. for($i = 0; $i < $count; $i++)
  273. {
  274. $name = $matches[1][$i];
  275. if(in_array($name, $variables) === true)
  276. {
  277. continue;
  278. }
  279. if(preg_match_all('/('.preg_quote($name, '/').')[[:space:]]*=[[:space:]]*([^;]+);/s', $str, $m) != 0)
  280. {
  281. $value = $this->Unescape($m[2][count($m[2]) - 1]);
  282. if($str !== preg_replace('/('.preg_quote($name).')([^<>[:alnum:]_ \=])/m', $value."$2", $str) && strstr($value, $name) === false)
  283. {
  284. $done = false;
  285. array_push($variables, $name);
  286. $str = preg_replace('/('.preg_quote($name).')([^<>[:alnum:]_ \=])/m', $value."$2", $str);
  287. }
  288. }
  289. }
  290. }
  291. if(preg_match_all('/\$([^\=^\s^\)^{]+)[\s]*\=[\s]*array\(([^\)]*)\)[\s]*\;/im', $str, $matches) != 0)
  292. {
  293. $count = count($matches[0]);
  294. for($i = 0; $i < $count; $i++)
  295. {
  296. $name = $matches[1][$i];
  297. if(in_array($name, $variables) === true)
  298. {
  299. continue;
  300. }
  301. array_push($variables, $name);
  302. $value = $matches[2][$i];
  303. if(preg_match_all("/'([^']*)'/im", $value, $nmatches) != 0)
  304. {
  305. foreach($nmatches[1] as $index => $data)
  306. {
  307. $nname = preg_quote($name."[".$index."]");
  308. if($str !== preg_replace('/\$'.$nname.'/m', $data, $str) && strstr($data, $nname) === false)
  309. {
  310. $done = false;
  311. $str = preg_replace('/\$'.$nname.'/m', $data, $str);
  312. }
  313. }
  314. }
  315. }
  316. }
  317. if(preg_match_all("/\'(?<string>[^\']+)\'{(?<index>[0-9]+)}/", $str, $matches) != 0)
  318. {
  319. $count = count($matches[0]);
  320. for($i = 0; $i < $count; $i++)
  321. {
  322. $value = $matches['string'][$i]{$matches['index'][$i]};
  323. if($str !== preg_replace('/'.preg_quote($matches[0][$i]).'/mi', "'$value'", $str))
  324. {
  325. $done = false;
  326. $str = preg_replace('/'.preg_quote($matches[0][$i]).'/mi', "'$value'", $str);
  327. }
  328. }
  329. }
  330. if(preg_match_all('/(\$[^\=^\s^\)^{]+){(?<index>[0-9]+)}/', $str, $matches) != 0)
  331. {
  332. $count = count($matches[0]);
  333. for($i = 0; $i < $count; $i++)
  334. {
  335. $name = $matches[1][$i];
  336. if(preg_match_all('/('.preg_quote($name, '/').')[[:space:]]*=[[:space:]]*\'([^\']+)\'[^\s]*;/s', $str, $m) != 0)
  337. {
  338. if($matches['index'][$i] > strlen($m[2][count($m[2]) - 1]))
  339. {
  340. continue;
  341. }
  342. $value = $m[2][count($m[2]) - 1]{$matches['index'][$i]};
  343. if($str !== preg_replace('/'.preg_quote($matches[0][$i]).'/mi', "'$value'", $str))
  344. {
  345. $done = false;
  346. $str = preg_replace('/'.preg_quote($matches[0][$i]).'/mi', "'$value'", $str);
  347. }
  348. }
  349. }
  350. }
  351. //Autocomputes a function that just does a base64 decode of an internal array and returns the results
  352. if(preg_match_all('/function[\s]+([^\(^\s]+)\(\$[^\)]+\)[\s]*{[\s]*\$[^\s^=]+[\s]*=[\s]*array\(([^\)]+)\)[\s]*;[\s]*return[\s]+base64_decode\(\$[^\)]+\)[\s]*;[\s]*}/im', $str, $matches) != 0)
  353. {
  354. $count = count($matches[0]);
  355. for($i = 0; $i < $count; $i++)
  356. {
  357. $name = $matches[1][$i];
  358. if(in_array("function ".$name, $variables) === true)
  359. {
  360. continue;
  361. }
  362. array_push($variables, $name);
  363. $value = $matches[2][$i];
  364. if(preg_match_all("/'([^']*)'/im", $value, $nmatches) != 0)
  365. {
  366. foreach($nmatches[1] as $index => $data)
  367. {
  368. $nname = preg_quote($name."(".$index.")");
  369. if($str !== preg_replace('/'.$nname.'/m', "'".base64_decode($data)."'", $str) && strstr(base64_decode($data), $nname) === false)
  370. {
  371. $done = false;
  372. $str = preg_replace('/'.$nname.'/m', "'".base64_decode($data)."'", $str);
  373. }
  374. }
  375. }
  376. }
  377. }
  378. //Process specific functions
  379. //round
  380. if(preg_match_all('/round\(([0-9\+\.\-\s]+)\)/im', $str, $matches) != 0)
  381. {
  382. $count = count($matches[0]);
  383. for($i = 0; $i < $count; $i++)
  384. {
  385. $value = eval("return round(".$matches[1][$i].");");
  386. if($str !== preg_replace('/'.preg_quote($matches[0][$i]).'/mi', "$value", $str))
  387. {
  388. $done = false;
  389. $str = preg_replace('/'.preg_quote($matches[0][$i]).'/mi', "$value", $str);
  390. }
  391. }
  392. }
  393. if($str != $this->Unescape($str))
  394. {
  395. $done = false;
  396. $str = $this->Unescape($str);
  397. }
  398. //surrogate base64
  399. if(preg_match_all('/function[\s]+([^\(^\s]+)\(\$[^\)]+\)[\s]*{[\s]*return[\s]*base64_decode\(\$[^\)]+\);}/im', $str, $matches) != 0)
  400. {
  401. $count = count($matches[0]);
  402. for($i = 0; $i < $count; $i++)
  403. {
  404. if($str !== preg_replace('/'.preg_quote($matches[1][$i]).'\(/mi', "base64_decode(", $str))
  405. {
  406. $done = false;
  407. $str = preg_replace('/'.preg_quote($matches[1][$i]).'\(/mi', "base64_decode(", $str);
  408. }
  409. }
  410. }
  411. }
  412. $this->ClearEmptyEvals($str);
  413. }
  414. $str = $this->ExpandLines($str);
  415. $varCount = 0;
  416. $variables = array();
  417. if(preg_match_all('/(\$[[:alnum:]_]+)/', $str, $matches) != 0)
  418. {
  419. $count = count($matches[0]);
  420. for($i = 0; $i < $count; $i++)
  421. {
  422. $name = $matches[1][$i];
  423. if(in_array($name, $variables) === true)
  424. {
  425. continue;
  426. }
  427. $value = "\$var_".$varCount;
  428. if($str !== preg_replace('/('.preg_quote($name).')([^[:alnum:]^_])/m', "$value$2", $str) && strstr($value, $name) === false)
  429. {
  430. $done = false;
  431. array_push($variables, $name);
  432. array_push($variables, $value);
  433. $str = preg_replace('/('.preg_quote($name).')([^[:alnum:]^_])/m', "$value$2", $str);
  434. $varCount++;
  435. }
  436. }
  437. }
  438. }
  439. public function DecodeFromHash($hash)
  440. {
  441. foreach($this->list as $time => $payload)
  442. {
  443. if($payload->hash == $hash)
  444. {
  445. $payload->decodedPayload = $payload->rawPayload;
  446. $this->AutoDecode($payload->decodedPayload);
  447. return $payload;
  448. }
  449. }
  450. return false;
  451. }
  452. public function DecodeFromText($raw)
  453. {
  454. $str = $raw;
  455. $hash = md5($raw);
  456. foreach($this->list as $time => $payload)
  457. {
  458. if($payload->hash == $hash)
  459. {
  460. return $payload;
  461. }
  462. }
  463. $file = $this->GetFileName($str, ".DecodedOnWeb");
  464. if($file !== false)
  465. {
  466. $this->AutoDecode($str);
  467. $decoded = new DecodedPayload();
  468. $decoded->timestamp = strftime('%c');
  469. $decoded->submitter = $_SERVER['REMOTE_ADDR'];
  470. $decoded->decodedPayload = $str;
  471. $decoded->rawPayload = $raw;
  472. $decoded->hash = $hash;
  473. $decoded->type = ".DecodedOnWeb";
  474. $decoded->origin = false;
  475. $toFile = base64_encode(gzdeflate(serialize($decoded), 9));
  476. file_put_contents($file, $toFile);
  477. return $decoded;
  478. }
  479. return false;
  480. }
  481. function startsWith($haystack, $needle)
  482. {
  483. $length = strlen($needle);
  484. return (substr($haystack, 0, $length) === $needle);
  485. }
  486. public function DecodeFromUrl($url)
  487. {
  488. if(!($this->startsWith($url, "http://") || $this->startsWith($url, "ftp://") || $this->startsWith($url, "https://")))
  489. {
  490. $url = "http://".$url;
  491. }
  492. $str = "";
  493. $hash = md5($url);
  494. foreach($this->list as $time => $payload)
  495. {
  496. if($payload->hash == $hash)
  497. {
  498. return $payload;
  499. }
  500. }
  501. $file = $this->GetFileName($url, ".DecodedByUrl");
  502. if($file !== false)
  503. {
  504. $str = file_get_contents($url, false, null, 0, 1024 * 1024 * 16);
  505. $raw = $str;
  506. if($str !== false)
  507. {
  508. $this->AutoDecode($str);
  509. $decoded = new DecodedPayload();
  510. $decoded->timestamp = strftime('%c');
  511. $decoded->submitter = $_SERVER['REMOTE_ADDR'];
  512. $decoded->origin = $url;
  513. $decoded->hash = $hash;
  514. $decoded->decodedPayload = $str;
  515. $decoded->rawPayload = $raw;
  516. $decoded->type = ".DecodedByUrl";
  517. $toFile = base64_encode(gzdeflate(serialize($decoded), 9));
  518. file_put_contents($file, $toFile);
  519. return $decoded;
  520. }
  521. }
  522. return false;
  523. }
  524. }
  525. ?>