При работе с символьными объектами (управляющими последовательностями, &-сущностями, экранирующими последовательностями, мнемониками и т.д.) в моём случае Escape-последовательностями типа "\xnn", возникла необходимость их динамического формирования. Такая задача может возникнуть, например, при разборе вредоносного кода атакованного сайта.

image

Собственно проблема — не работает следующий код:

$inx = '50';
$str = "\x".$inx;
$str .= "\x51";
var_dump($str);
// выведет \x50Q


При разборе выяснил, что такой код работает:

$str = "\x50";
$str .= "\x51";
var_dump($str);
// выведет PQ

И совсем не работает код в одинарных кавычках, т.к. последовательность вообще не интерпретируется:

$str = '\x50';
$str .= ‘\x51’;
var_dump($str);
// выведет \x50\x51

Постановка задачи


Теперь задачу можно сформулировать так: необходима правильная интерпритация строки, содержащей динамически сформированную последовательность типа "\xnn", символы которой составлены из фрагментов.

В данном случае выполняются две операции — конкатенация и интерпритация символьных объектов. И выполняется они в строгой последовательности. Сначала выполняется интерпритация символьных объектов, затем конкатенация, но не наоборот.

Таким образом, в первом примере один из зашифрованных символов последовательности разбивается на фрагменты.

"\x51" преобразуется в символ «Q», а для строки "\х" php ищет последующие 2 символа, не находит их и при совмещении частей выводит в строку 4-мя символами. Такая строка уже не является для php-интерпритатора командой к преобразованию.

Во втором примере php заменяет содержимое двойных кавычек на символы и «склеивает» их в единую строку.

Решение


Поскольку заставить php сначала склеивать символы, а затем их интерпретировать не удалось, попробуем получить строку как есть и заменить неинтерпретирующиеся последовательности на готовые символы.

Мои поиски встроенной или готовой функции преобразования последовательности типа "\xnn" в символы к внятному результату не привели. «Говорят, что плохо искал» (В.Цой). В итоге данная задача была решена использованием функция strtr(). Здесь заменяются неинтерпритируемые последовательности '\х50' на преобразуемые"\x50". Разница только в кавычках.

Вот готовая функция для символьных объектов типа "\xnn" и вызов:

function str2escape($string) {
$sym_tbl  = array(
	'\x00' => "\x00", '\x01' => "\x01", '\x02' => "\x02", '\x03' => "\x03", '\x04' => "\x04", '\x05' => "\x05",
	'\x06' => "\x06", '\x07' => "\x07", '\x08' => "\x08", '\x09' => "\x09", '\x0a' => "\x0a",
	'\x0b' => "\x0b", '\x0c' => "\x0c", '\x0d' => "\x0d", '\x0e' => "\x0e", '\x0f' => "\x0f", 
	'\x11' => "\x11", '\x12' => "\x12", '\x13' => "\x13", '\x14' => "\x14", '\x15' => "\x15",
	'\x16' => "\x16", '\x17' => "\x17", '\x18' => "\x18", '\x19' => "\x19", '\x1a' => "\x1a",
	'\x1b' => "\x1b", '\x1c' => "\x1c", '\x1d' => "\x1d", '\x1e' => "\x1e", '\x1f' => "\x1f", 
	'\x21' => "\x21", '\x22' => "\x22", '\x23' => "\x23", '\x24' => "\x24", '\x25' => "\x25",
	'\x26' => "\x26", '\x27' => "\x27", '\x28' => "\x28", '\x29' => "\x29", '\x2a' => "\x2a",
	'\x2b' => "\x2b", '\x2c' => "\x2c", '\x2d' => "\x2d", '\x2e' => "\x2e", '\x2f' => "\x2f", 
	'\x31' => "\x31", '\x32' => "\x32", '\x33' => "\x33", '\x34' => "\x34", '\x35' => "\x35",
	'\x36' => "\x36", '\x37' => "\x37", '\x38' => "\x38", '\x39' => "\x39", '\x3a' => "\x3a",
	'\x3b' => "\x3b", '\x3c' => "\x3c", '\x3d' => "\x3d", '\x3e' => "\x3e", '\x3f' => "\x3f", 
	'\x41' => "\x41", '\x42' => "\x42", '\x43' => "\x43", '\x44' => "\x44", '\x45' => "\x45",
	'\x46' => "\x46", '\x47' => "\x47", '\x48' => "\x48", '\x49' => "\x49", '\x4a' => "\x4a",
	'\x4b' => "\x4b", '\x4c' => "\x4c", '\x4d' => "\x4d", '\x4e' => "\x4e", '\x4f' => "\x4f", 
	'\x51' => "\x51", '\x52' => "\x52", '\x53' => "\x53", '\x54' => "\x54", '\x55' => "\x55",
	'\x56' => "\x56", '\x57' => "\x57", '\x58' => "\x58", '\x59' => "\x59", '\x5a' => "\x5a",
	'\x5b' => "\x5b", '\x5c' => "\x5c", '\x5d' => "\x5d", '\x5e' => "\x5e", '\x5f' => "\x5f", 
	'\x61' => "\x61", '\x62' => "\x62", '\x63' => "\x63", '\x64' => "\x64", '\x65' => "\x65",
	'\x66' => "\x66", '\x67' => "\x67", '\x68' => "\x68", '\x69' => "\x69", '\x6a' => "\x6a",
	'\x6b' => "\x6b", '\x6c' => "\x6c", '\x6d' => "\x6d", '\x6e' => "\x6e", '\x6f' => "\x6f", 
	'\x71' => "\x71", '\x72' => "\x72", '\x73' => "\x73", '\x74' => "\x74", '\x75' => "\x75",
	'\x76' => "\x76", '\x77' => "\x77", '\x78' => "\x78", '\x79' => "\x79", '\x7a' => "\x7a",
	'\x7b' => "\x7b", '\x7c' => "\x7c", '\x7d' => "\x7d", '\x7e' => "\x7e", '\x7f' => "\x7f", 
	'\x81' => "\x81", '\x82' => "\x82", '\x83' => "\x83", '\x84' => "\x84", '\x85' => "\x85",
	'\x86' => "\x86", '\x87' => "\x87", '\x88' => "\x88", '\x89' => "\x89", '\x8a' => "\x8a",
	'\x8b' => "\x8b", '\x8c' => "\x8c", '\x8d' => "\x8d", '\x8e' => "\x8e", '\x8f' => "\x8f", 
	'\x91' => "\x91", '\x92' => "\x92", '\x93' => "\x93", '\x94' => "\x94", '\x95' => "\x95",
	'\x96' => "\x96", '\x97' => "\x97", '\x98' => "\x98", '\x99' => "\x99", '\x9a' => "\x9a",
	'\x9b' => "\x9b", '\x9c' => "\x9c", '\x9d' => "\x9d", '\x9e' => "\x9e", '\x9f' => "\x9f", 
	'\xa1' => "\xa1", '\xa2' => "\xa2", '\xa3' => "\xa3", '\xa4' => "\xa4", '\xa5' => "\xa5",
	'\xa6' => "\xa6", '\xa7' => "\xa7", '\xa8' => "\xa8", '\xa9' => "\xa9", '\xaa' => "\xaa",
	'\xab' => "\xab", '\xac' => "\xac", '\xad' => "\xad", '\xae' => "\xae", '\xaf' => "\xaf", 
	'\xb1' => "\xb1", '\xb2' => "\xb2", '\xb3' => "\xb3", '\xb4' => "\xb4", '\xb5' => "\xb5",
	'\xb6' => "\xb6", '\xb7' => "\xb7", '\xb8' => "\xb8", '\xb9' => "\xb9", '\xba' => "\xba",
	'\xbb' => "\xbb", '\xbc' => "\xbc", '\xbd' => "\xbd", '\xbe' => "\xbe", '\xbf' => "\xbf", 
	'\xc1' => "\xc1", '\xc2' => "\xc2", '\xc3' => "\xc3", '\xc4' => "\xc4", '\xc5' => "\xc5",
	'\xc6' => "\xc6", '\xc7' => "\xc7", '\xc8' => "\xc8", '\xc9' => "\xc9", '\xca' => "\xca",
	'\xcb' => "\xcb", '\xcc' => "\xcc", '\xcd' => "\xcd", '\xce' => "\xce", '\xcf' => "\xcf", 
	'\xd1' => "\xd1", '\xd2' => "\xd2", '\xd3' => "\xd3", '\xd4' => "\xd4", '\xd5' => "\xd5",
	'\xd6' => "\xd6", '\xd7' => "\xd7", '\xd8' => "\xd8", '\xd9' => "\xd9", '\xda' => "\xda",
	'\xdb' => "\xdb", '\xdc' => "\xdc", '\xdd' => "\xdd", '\xde' => "\xde", '\xdf' => "\xdf", 
	'\xe1' => "\xe1", '\xe2' => "\xe2", '\xe3' => "\xe3", '\xe4' => "\xe4", '\xe5' => "\xe5",
	'\xe6' => "\xe6", '\xe7' => "\xe7", '\xe8' => "\xe8", '\xe9' => "\xe9", '\xea' => "\xea",
	'\xeb' => "\xeb", '\xec' => "\xec", '\xed' => "\xed", '\xee' => "\xee", '\xef' => "\xef", 
	'\xf1' => "\xf1", '\xf2' => "\xf2", '\xf3' => "\xf3", '\xf4' => "\xf4", '\xf5' => "\xf5",
	'\xf6' => "\xf6", '\xf7' => "\xf7", '\xf8' => "\xf8", '\xf9' => "\xf9", '\xfa' => "\xfa",
	'\xfb' => "\xfb", '\xfc' => "\xfc", '\xfd' => "\xfd", '\xfe' => "\xfe", '\xff' => "\xff", 
	);
return strtr($string, $sym_tbl);
}
// вызов:
$str = str2escape($str);

Теперь всё хорошо:


$inx = '50';
$str = "\x".$inx;
$str .= "\x51";
var_dump(str2escape($str));
// выведет PQ


Пользователь Habra-Mikhail предложил вариант без использования собственной функции

$inx = '50';
$str = chr(hexdec($inx));
$str .= "\x51";
var_dump($str);
// выведет PQ


Поиск причины и решения заняло некоторое количество времени, думаю пройденный мной путь поможет быстрее разобраться всем, кто столкнётся с подобной задачей в дальнейшем.
Поделиться с друзьями
-->

Комментарии (5)


  1. Habra-Mikhail
    28.06.2017 15:09

    А зачем потребовалось интерпретировать строку?
    Возможно, было бы правильнее в данном случае воспользоваться встроенной функцией chr:
    php.net manual — chr

    Т.к в программе известны коды символов


  1. drtropin
    28.06.2017 15:21

    Вирус прописывал смешанную последовательность

    @include "\x2fus\x72/l\x6fca\x6c/w\x77w/
    

    Одна из задач была расшифровать и сохранить в читабельном виде в файл.

    $inx = '50';
    var_dump(chr(hexdec($inx)));
    

    Да, так работает


    1. michael_vostrikov
      30.06.2017 17:42

      Одна из задач была расшифровать и сохранить в читабельном виде в файл.

      echo "\x2fus\x72/l\x6fca\x6c/w\x77w/";
      //  /usr/local/www/


  1. Habra-Mikhail
    28.06.2017 15:21
    +3

    Также таблица символов будет работать неправильно если будет использован двойной обратный слэш(\\), который уберёт экранирование спец символа.

    И последняя ошибка: В последовательности \xnn — nn — это 16-ричное число, следовательно в коде будет пропущена половина символов.


  1. drtropin
    29.06.2017 09:37

    Да, совершенно верно, спасибо!
    Но если будет использован двойной слэш это уже не будет последовательность типа \xnn.