0x00 前言
上期的三个白帽挑战题已经结束,但是大家依旧意犹未尽,讨论着writeup中的知识点,其中比较有意思的是关于php弱类型的:
[PHP] 纯文本查看 复制代码 array(0)>999999999
这个结果是true的。
群里各位大牛给了各种思考,和相关文章:
php.net/manual/zh/language.operators.comparison.php
但是基本都是别人给出的结论,我不太喜欢结论性的东西,这只让我知道了结果,并不知道为什么有这样的结果。
0x01 php动态调试(php5.6为例)
1.下载解压并安装
[PHP] 纯文本查看 复制代码 # wget [url]http://cn2.php.net/distributions/php-5.6.0.tar.xz[/url]
# xz -d php-5.6.0.tar.xz
# tar vxf php-5.6.0.tar
# cd php-5.6.0
# ./configure --enable-debug
# make
# sudo make install
2.相关文章推荐
《深入理解Zend执行引擎(PHP5)》
《使用vld查看OPCode》
《调式PHP源码》
0x02 OPCode分析
可以看出关键操作是IS_SMALLER,如果你仔细看过上面推荐文章就可以找到关键函数
[PHP] 纯文本查看 复制代码 ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
0x03 gdb动态调试
注意红框内容,相信看过上边内容的都能看懂(ps:详细内容太多,请先学习推荐文章)
大致逻辑是两个参数进来经过zendi_convert_scalar_to_number函数处理,由于我们一个是数组一个是整形,所以两个参数类型和值都不变
[PHP] 纯文本查看 复制代码 #define zendi_convert_scalar_to_number(op, holder, result)
if (op==result) {
if (Z_TYPE_P(op) != IS_LONG) {
convert_scalar_to_number(op TSRMLS_CC);
}
} else {
switch (Z_TYPE_P(op)) {
case IS_STRING:
{
if ((Z_TYPE(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1)) == 0) {
ZVAL_LONG(&(holder), 0);
}
(op) = &(holder);
break;
}
case IS_BOOL:
case IS_RESOURCE:
ZVAL_LONG(&(holder), Z_LVAL_P(op));
(op) = &(holder);
break;
case IS_NULL:
ZVAL_LONG(&(holder), 0);
(op) = &(holder);
break;
case IS_OBJECT:
(holder) = (*(op));
zval_copy_ctor(&(holder));
convert_to_long_base(&(holder), 10);
if (Z_TYPE(holder) == IS_LONG) {
(op) = &(holder);
}
break;
}
}
然后再次进入循环到达
[PHP] 纯文本查看 复制代码 } else if (Z_TYPE_P(op1)==IS_ARRAY) {
ZVAL_LONG(result, 1);
return SUCCESS;
} else if (Z_TYPE_P(op2)==IS_ARRAY) {
ZVAL_LONG(result, -1);
return SUCCESS;
从opcode可以看到op2为array(0)所以这儿返回-1
最后(Z_LVAL_P(result) < 0)成立,返回true
[PHP] 纯文本查看 复制代码 if (compare_function(result, op1, op2 TSRMLS_CC) == FAILURE) {
return FAILURE;
}
ZVAL_BOOL(result, (Z_LVAL_P(result) < 0));
return SUCCESS; |