Какой смысл иметь __wakeup в исходном коде PHP 7.4 Исключения? - PullRequest
1 голос
/ 10 июля 2019

Существует (довольно новый) метод эксплуатации, называемый phar десериализация , который запускается функциями PHP * файловой системы , такими как file_exists, unlink,... и затем несериализованный код выполняется внутри экземпляра класса, который должен содержать хотя бы один из двух магических методов __wakeup и __destruct.

Моя идея заключалась в том, чтобы искать в исходном коде PHP встроенный PHPклассы, которые могут иметь такие магические методы, изначально реализованные (__wakeup и __destruct) как часть таких классов, и могут быть каким-то образом использованы в процессе десериализации во время обработки Exception (которая может вызывать, например, некоторое использование послебесплатные ошибки).Действительно, я обнаружил __wakeup в нескольких Exception классах (простой способ получить все встроенные классы и проверить их на наличие методов __wakeup и __destruct - это использовать функцию PHP get_declared_classes()).Но на данный момент это кажется невосприимчивым.

Так что вопрос не в том, чтобы эксплуатировать вещи.

Вопрос: Что я не понимаю, почему, например, __wakeup реализовано в Исключения ?Это просто метод-заполнитель для расширения таких Exception классов и последующего написания собственных __wakeup / __destruct методов?

Исходный код: https://github.com/php/php-src/blob/master/Zend/zend_exceptions.c#L316

В строках306-333 ( php-src / Zend / zend_exceptions.c , PHP 7.4):

/* }}} */

/* {{{ proto Exception::__wakeup()
   Exception unserialize checks */
#define CHECK_EXC_TYPE(id, type) \
    pvalue = zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 1, &value); \
    if (Z_TYPE_P(pvalue) != IS_NULL && Z_TYPE_P(pvalue) != type) { \
        zend_unset_property(i_get_exception_base(object), object, ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \
    }

ZEND_METHOD(exception, __wakeup)
{
    zval value, *pvalue;
    zval *object = ZEND_THIS;
    CHECK_EXC_TYPE(ZEND_STR_MESSAGE,  IS_STRING);
    CHECK_EXC_TYPE(ZEND_STR_STRING,   IS_STRING);
    CHECK_EXC_TYPE(ZEND_STR_CODE,     IS_LONG);
    CHECK_EXC_TYPE(ZEND_STR_FILE,     IS_STRING);
    CHECK_EXC_TYPE(ZEND_STR_LINE,     IS_LONG);
    CHECK_EXC_TYPE(ZEND_STR_TRACE,    IS_ARRAY);
    pvalue = zend_read_property(i_get_exception_base(object), object, "previous", sizeof("previous")-1, 1, &value);
    if (pvalue && Z_TYPE_P(pvalue) != IS_NULL && (Z_TYPE_P(pvalue) != IS_OBJECT ||
            !instanceof_function(Z_OBJCE_P(pvalue), zend_ce_throwable) ||
            pvalue == object)) {
        zend_unset_property(i_get_exception_base(object), object, "previous", sizeof("previous")-1);
    }
}
/* }}} */

и позже в строках 788-801: https://github.com/php/php-src/blob/master/Zend/zend_exceptions.c#L788

static const zend_function_entry default_exception_functions[] = {
    ZEND_ME(exception, __clone, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
    ZEND_ME(exception, __construct, arginfo_exception___construct, ZEND_ACC_PUBLIC)
    ZEND_ME(exception, __wakeup, NULL, ZEND_ACC_PUBLIC)
    ZEND_ME(exception, getMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, getCode, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, getLine, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, getTrace, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, getPrevious, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, getTraceAsString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
    ZEND_ME(exception, __toString, NULL, 0)
    ZEND_FE_END
};

Ответы [ 2 ]

0 голосов
/ 21 июля 2019

Думаю, дело в том, что поскольку класс исключения должен быть десериализуем, ему необходимо добавить метод __wakeup() уже на уровне C (поскольку свойства класса исключения определены и в языке C).

0 голосов
/ 10 июля 2019

Насколько я могу судить, этот код проверяет типы свойств исключения и устанавливает их на null, если тип неверен. Например:

CHECK_EXC_TYPE(ZEND_STR_MESSAGE,  IS_STRING);

примерно эквивалентно коду PHP:

if (!is_string($this->message))
    $this->message = null;

Аналогично, код в конце, начинающийся с pvalue = zend_read_property(…), проверяет, что $this->previous является объектом, который реализует Throwable. В PHP это было бы примерно эквивалентно:

if (!is_object($this->previous) || !($this->previous instanceof Throwable)
    $this->previous = null;

Почему этот код является частью интерпретатора PHP? Здесь определяется весь класс Exception в коде C. Ни один из встроенных классов или методов не написан на PHP - они не могут быть такими, потому что язык не позволяет установить реализацию метода PHP для класса, объявленного в C.

...