zoukankan      html  css  js  c++  java
  • base_convert()函数探秘及小bug记录

    php base_convert函数原型:

     string base_convert ( string $number , int $frombase , int $tobase )

    base_convert — 在任意进制之间转换数字
    返回一字符串,包含 numbertobase 进制的表示。number 本身的进制由 frombase 指定。frombasetobase 都只能在 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。

    内核源码如下:

     1 /* {{{ proto string base_convert(string number, int frombase, int tobase)
     2    Converts a number in a string from any base <= 36 to any base <= 36 */
     3 PHP_FUNCTION(base_convert)
     4 {
     5     zval **number, temp;
     6     long frombase, tobase;
     7     char *result;
     8 
     9     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
    10         return;
    11     }
    12     convert_to_string_ex(number);
    13 
    14     if (frombase < 2 || frombase > 36) {
    15         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
    16         RETURN_FALSE;
    17     }
    18     if (tobase < 2 || tobase > 36) {
    19         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
    20         RETURN_FALSE;
    21     }
    22 
    23     if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
    24         RETURN_FALSE;
    25     }
    26     result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
    27     RETVAL_STRING(result, 0);
    28 }
    View Code

    PHP_FUNCTION(base_convert)首先对输入的参数进行了存储及校验,将$number以string的形式存储于zval中,并校验$frombase和$tobase是否在2和36之间,不在则报错返回。之后调用\_php\_math_basetozval()函数根据frombase将存储于zval中的string转换成对应的数值,代码如下:

     1 /* }}} */
     2 
     3 /* {{{ _php_math_basetozval */
     4 /*
     5  * Convert a string representation of a base(2-36) number to a zval.
     6  */
     7 PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
     8 {
     9     long num = 0;
    10     double fnum = 0;
    11     int i;
    12     int mode = 0;
    13     char c, *s;
    14     long cutoff;
    15     int cutlim;
    16 
    17     if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
    18         return FAILURE;
    19     }
    20 
    21     s = Z_STRVAL_P(arg);
    22 
    23     cutoff = LONG_MAX / base;
    24     cutlim = LONG_MAX % base;
    25 
    26     for (i = Z_STRLEN_P(arg); i > 0; i--) {
    27         c = *s++;
    28 
    29         /* might not work for EBCDIC */
    30         if (c >= '0' && c <= '9')
    31             c -= '0';
    32         else if (c >= 'A' && c <= 'Z')
    33             c -= 'A' - 10;
    34         else if (c >= 'a' && c <= 'z')
    35             c -= 'a' - 10;
    36         else
    37             continue;
    38 
    39         if (c >= base)
    40             continue;
    41 
    42         if (c >= base)
    43             continue;
    44 
    45         switch (mode) {
    46         case 0: /* Integer */
    47             if (num < cutoff || (num == cutoff && c <= cutlim)) {
    48                 num = num * base + c;
    49                 break;
    50             } else {
    51                 fnum = num;
    52                 mode = 1;
    53             }
    54             /* fall-through */
    55         case 1: /* Float */
    56             fnum = fnum * base + c;
    57         }
    58     }
    59 
    60     if (mode == 1) {
    61         ZVAL_DOUBLE(ret, fnum);
    62     } else {
    63         ZVAL_LONG(ret, num);
    64     }
    65     return SUCCESS;
    66 }
    View Code

    由上面的代码可以看到,\_php_math_basetozval()根据输入字符串的字面值大小,计算出对应数值并保存在num或fnum中(当数值能被long存储时存在num中,大于long的最大值时,则存储于fnum中,fnum是double类型),然后保存在zval中返回。这里就又一个小bug,该函数在基于from_base将字符串转换为数值时,如果遇到不是0-9或者a-z或者A-Z的字符时,是跳过该字符,并继续处理下一个字符(36行-37行代码),这个地方就隐含着bug了。即base_convert('122348738947.653',10,8)和echo base_convert('122348738947653',10,8)的输出相同。另外,如果$number中的字符超过了from_base时,也是跳过该字符不做处理(39行-40行代码)。

    通过_php_math_basetozval()我们已经确定了$number所代表的数值,接着调用\_php\_math\_zvaltobase()将数值转换为以$to_base为基数的数值字符串,

     1 /* {{{ _php_math_zvaltobase */
     2 /*
     3  * Convert a zval to a string containing a base(2-36) representation of
     4  * the number.
     5  */
     6 PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
     7 {
     8     static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
     9 
    10     if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
    11         return STR_EMPTY_ALLOC();
    12     }
    13 
    14     if (Z_TYPE_P(arg) == IS_DOUBLE) {
    15         double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
    16         char *ptr, *end;
    17         char buf[(sizeof(double) << 3) + 1];
    18 
    19         /* Don't try to convert +/- infinity */
    20         if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
    21             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
    22             return STR_EMPTY_ALLOC();
    23         }
    24 
    25         end = ptr = buf + sizeof(buf) - 1;
    26         *ptr = '';
    27 
    28         do {
    29             *--ptr = digits[(int) fmod(fvalue, base)];
    30             fvalue /= base;
    31         } while (ptr > buf && fabs(fvalue) >= 1);
    32 
    33         return estrndup(ptr, end - ptr);
    34     }
    35 
    36     return _php_math_longtobase(arg, base);
    37 }
    View Code

    这部分代码的主体部分是用来处理用double的数值,下面的函数_php_math_longtobase()则是用来处理long存储的数字。

     1 /* {{{ _php_math_longtobase */
     2 /*
     3  * Convert a long to a string containing a base(2-36) representation of
     4  * the number.
     5  */
     6 PHPAPI char * _php_math_longtobase(zval *arg, int base)
     7 {
     8     static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
     9     char buf[(sizeof(unsigned long) << 3) + 1];
    10     char *ptr, *end;
    11     unsigned long value;
    12 
    13     if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
    14         return STR_EMPTY_ALLOC();
    15     }
    16 
    17     value = Z_LVAL_P(arg);
    18 
    19     end = ptr = buf + sizeof(buf) - 1;
    20     *ptr = '';
    21 
    22     do {
    23         *--ptr = digits[value % base];
    24         value /= base;
    25     } while (ptr > buf && value);
    26 
    27     return estrndup(ptr, end - ptr);
    28 }
    View Code

    bug演示:

     1 php > echo base_convert('122348738947.653',10,8);
     2 3364321107725105
     3 php > echo base_convert('122348738947',10,8);
     4 1617443314603
     5 php > echo base_convert('122348738947653',10,8);
     6 3364321107725105
     7 php > echo base_convert('13526~~009',10,10);
     8 13526009
     9 php > echo base_convert('13526bb009',10,10);
    10 13526009
    11 php >
  • 相关阅读:
    DRUPAL-PSA-CORE-2014-005 && CVE-2014-3704 Drupal 7.31 SQL Injection Vulnerability /includes/database/database.inc Analysis
    WDCP(WDlinux Control Panel) mysql/add_user.php、mysql/add_db.php Authentication Loss
    Penetration Testing、Security Testing、Automation Testing
    Tomcat Server Configuration Automation Reinforcement
    Xcon2014 && Geekpwn2014
    phpMyadmin /scripts/setup.php Remote Code Injection && Execution CVE-2009-1151
    Linux System Log Collection、Log Integration、Log Analysis System Building Learning
    The Linux Process Principle,NameSpace, PID、TID、PGID、PPID、SID、TID、TTY
    Windows Management Instrumentation WMI Security Technology Learning
    IIS FTP Server Anonymous Writeable Reinforcement, WEBDAV Anonymous Writeable Reinforcement(undone)
  • 原文地址:https://www.cnblogs.com/jade640/p/7581350.html
Copyright © 2011-2022 走看看