Skip to content

Instantly share code, notes, and snippets.

@dogeow
Created July 11, 2019 04:37
Show Gist options
  • Save dogeow/abc67955bed34bfcfa90aaf3d221c975 to your computer and use it in GitHub Desktop.
Save dogeow/abc67955bed34bfcfa90aaf3d221c975 to your computer and use it in GitHub Desktop.
PHP转换 JavaScript 中规则为 String(!![])+(1+[])
<?php
/*
JavaScript 表达式,如下:
String((((![]+!![])+(!![]-!+[]+[]))/(!+[]-![]+!![]-!+[]+!![]-[]-![]-[])))+(((![]+!![])+(!![]-!+[]+[]))/(!+[]-![]+!![]-!+[]+!![]-[]-![]-[]))
结构是:String(...)+(...),为字符串拼接,所以我们要依次获取左右两边的表达式,然后替换「[]」之类的为数字进行运算
注意:偶尔 (...) 里面的也是字符串拼接,所以需要判断。比如 1+[] 的结果就是字符串 "1"
*/
// HTML 源码里面的 JavaScript 代码片段
$html = 'String((((![]+!![])+(!![]-!+[]+[]))/(!+[]-![]+!![]-!+[]+!![]-[]-![]-[])))+(((![]+!![])+(!![]-!+[]+[]))/(!+[]-![]+!![]-!+[]+!![]-[]-![]-[]))';
if (preg_match('/String(.*)/', $html, $match)) {
$string = $match[1]; // 去掉 String
}
// 拆分 「String(...)+(...)」
$pos = getPos($string, 0);
$a = substr($string, 0, $pos + 1);
$b = substr($string, $pos + 2);
// 删掉「String()」的「()」
$a = substr($a, 1, -1);
resolve($a); // 解析器
cal($a); // 解析器迭代完后,计算被拆分表达式左右两边的结果
$a = eval("return $a[0]$a[2]$a[1];"); // 两边表达式运算
resolve($b);
cal($b);
$b = eval("return $b[0]$b[2]$b[1];");
echo $a . $b;
/**
* 加减乘除、拼接
* 计算完,替换数组为结果
*
* @param $array array 表达式解析完后的数组
*/
function cal(&$array)
{
if (is_array($array)) {
foreach ($array as $key => &$v) {
cal($v);
if (is_array($v)) {
$a = toNumber(serialize($v[0]));
$b = toNumber(serialize($v[1]));
$array[0] = $a . $b;
}
}
} else {
if (!in_array($array, ['/', '*', '-', '+'])) {
$array = toNumber(serialize($array));
}
}
}
/**
* 表达式解析
* 遇到字符串拼接的才会拆分,不是的话,直接 eval() 计算结果
* 拆分的话,替换字符串为数组存储起来
*
* @param $array String 表达式,比如「(!![]+![])」
*/
function resolve(&$array)
{
// 第一次
if (!is_array($array)) {
$pos = getPos($array);
$array = array(
substr($array, 1, $pos),
substr($array, $pos + 2, -1),
$array[$pos + 1]
);
resolve($array);
} else {
if (strpos($array[0], ']+[]') !== false) {
$pos = getPos($array[0]);
if ($pos) {
$array[0] = array(
substr($array[0], 1, $pos),
substr($array[0], $pos + 2, -1),
$array[0][$pos + 1]
);
resolve($array[0]); // 迭代
}
}
if (strpos($array[1], ']+[]') !== false) {
$pos = getPos($array[1]);
if ($pos) {
$array[1] = array(
substr($array[1], 1, $pos),
substr($array[1], $pos + 2, -1),
$array[1][$pos + 1]
);
resolve($array[1]); // 迭代
}
}
}
}
/**
* 转换为字符串并运算结果
* 「!![]」 转换为 「1」等等,然后加减乘除
*
* @param string $string JavaScript 表达式
* @return int
*/
function toNumber($string)
{
$string = unserialize($string);
$string = str_replace('!![]', 1, $string);
$string = str_replace('!-[]', 1, $string);
$string = str_replace('!+[]', 1, $string);
$string = str_replace('![]', 0, $string);
$string = str_replace('+[]', null, $string);
$string = str_replace('-[]', '-0', $string);
return eval("return {$string};");
}
/**
* 获取「()」的最后位置
*
* @param string $string JavaScript 表达式
* @param int $sub 拆分表达式的话,需要减 1;不拆分不用。
* @return int
*/
function getPos($string, $sub = 1)
{
$pos = null;
$array = str_split($string);
if ($array[1] !== '(') { // 拆分到开头只剩下一个 「(」 不再拆分
return null;
}
// 开头连续几个「(」
$total = charContinuousCount($array, '(');
// 几个「(」需要几个「)」来匹配,如果是拆分表达式,最外层右边的「)」的不用匹配,所以减 1
$remaining = $total - $sub;
foreach ($array as $key => $v) {
// 从不是「(」的开始算
if ($key < $total) {
continue;
}
if ($v === ')') {
$remaining--;
if ($remaining === 0) {
$pos = $key;
break;
}
} elseif ($v === '(') {
$remaining++;
}
}
return $pos;
}
/**
* 字符连续几个
*
* @param $array
* @param string $char 需要判定的字符
* @return int
*/
function charContinuousCount($array, $char)
{
$total = 0;
foreach ($array as $v) {
if ($v === $char) {
$total++;
} else {
break;
}
}
return $total;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment