Компьютерный способ экспоненциальной записи
Вопросам представления действительных чисел в формате с плавающей точкой/запятой, который закреплен в стандарте IEEE754, посвящено немало работ. В том числе и на Хабрахабре. Не являясь программистом, автор попытался разобраться с этим зверем с точки зрения простой школьной математики. Отталкиваясь не от утвержденных в стандарте форматов, а от естественных представлений о числах. Возможно, что такой взгляд со стороны будет интересен и профессионалам-программистам. Особенно это касается вопросов, связанных с денормализованными числами.
1.ЕСТЕСТВЕННАЯ И ЭКСПОНЕНЦИАЛЬНАЯ ФОРМЫ ЗАПИСИ ЧИСЕЛ
Из математики известно, что любое действительное число F в позиционной системе счисления с основанием q записывается на бумаге в виде последовательности цифр. Вес цифры зависит от ее позиции в числе. Основание системы q равно количеству цифр (знаков ее алфавита) и определяет, во сколько раз различаются значения цифр соседних разрядов числа. Такая запись числа называется естественной и выглядит следующим образом:
F = c_(L-1,) c_(L-2)…c_(0.) d_(0 )…d_(N-2,) d_(N-1,) (1)
Где c_(L-1,) c_(L-2)…c_0- цифры целой части, а d_(0 )…d_(N-2,) d_(N-1) — цифры дробной части числа. Число может состоять из сколь угодно большого количества значащих цифр L целой части и N цифр дробной части.
Если точку в числе F, представленном выражением (1) переместить на h разрядов влево, то мы получим новое число М, которое связано с первоначальным числом формулой, представляющей собой экспоненциальную зависимость:
Значение числа F при этом уменьшится в h раз. Чтобы число не изменилось, его умножают на величину q^h. Таким образом, число, записанное в естественном виде (1), может быть представлено в эквивалентном ему экспоненциальном виде:
Если точку в числе F, представленном выражением (1) переместить на h разрядов вправо, то мы получим новое число М, которое связано с первоначальным числом формулой:
Значение числа F при этом увеличится в h раз. Чтобы число не изменилось, его умножают на величину q^(-h). Таким образом, для рассматриваемого случая, число, записанное в естественном виде (1), может быть представлено в следующем эквивалентном ему экспоненциальном виде:
В общем случае, любое действительное число, записанное в естественной форме (1), может быть записано в эквивалентном ему экспоненциальном виде следующим образом:
где М представляет собой число (1) со смещенной точкой на h позиций в ту или иную сторону. Число М в такой записи принято называть мантиссой числа, а q^(±h) характеристикой числа с порядком ±h, который в литературе еще называют экспонентой. Знак и величина порядка h компенсируют величину смещения точки относительно ее первоначального положения в числе (1). Обе записи (1) и (4) суть записи одного и того же числа различным образом.
Число (1) имеет L + N разрядов. Поскольку в естественном представлении числа (1) число разрядов L целой части и N разрядов дробной части могут иметь сколь угодно большое значение, то число M в (2) также может иметь сколь угодно большое количество разрядов. В общем случае, количество разрядов числа М в (2) может быть бесконечным. Например, когда число представляет собой периодическую дробь, или число иррационально. На практике мы имеем дело с ограниченным количеством разрядов для представления действительного числа в естественной форме. Сколько бы разрядов числа мы ни писали справа, рано или поздно мы должны ограничить количество разрядов представляемого числа. Хотя бы потому, что писать будет уже некуда. В результате, число сначала ограничивают, а затем округляют до приемлемой для данной задачи крайней правой цифры. При этом, конечно, теряется точность представления этого числа. Вопросы точности представления чисел в математике мы здесь не затрагиваем. Этому вопросу посвящено огромное число работ. Отметим только, что точность представления числа выбирается в разумных пределах и поэтому действительное число всегда записывается с ограничением разрядности. Тем самым, строго говоря, оно становится рациональным числом. В компьютерной литературе, числа, имеющие дробную часть, принято называть действительными числами. Мы тоже будем придерживаться этой терминологии.
В математике, как правило, к представлению чисел в экспоненциальном виде прибегают тогда, когда число, записанное в естественном виде (1), имеет незначащие нули. Чтобы сократить запись и не писать повторяющиеся незначащие цифры используют запись числа в экспоненциальной форме (2). Тогда порядок характеристики h указывает на количество незначащих нулей до или после точки. В более общем случае, число h со знаком, как мы видели выше, указывает на количество смещений точки относительно ее первоначального положения в числе. В любом случае, когда указывается число сдвигов h, всегда известна и точка отсчета, относительно которой происходит смещение разделительной точки в числе.
2. ПРЕДСТАВЛЕНИЕ ЧИСЕЛ В МАШИННОМ СЛОВЕ
В вычислительном устройстве для записи числа отводится ограниченное разрядное пространство. Поэтому на числа, записываемые в машинное слово, накладываются определенные ограничения, которые определяют точность представления чисел и диапазон принимаемых ими значений.
Двоичное число, представленное в экспоненциальном виде, в компьютере записывается в виде машинного слова, разбитого на специальные области. Структура машинного слова может быть схематично представлена следующим образом:
В этом слове К разрядов отводится для записи мантиссы M, R разрядов под запись порядка h характеристики и по одному разряду отводится под запись знака S числа и z знака порядка. Машинное пространство, выделенное под запись мантиссы числа, будем называть областью машинной мантиссы (ОММ), а число, записанное в эту область, машинной мантиссой. Аналогично, пространство, выделенное в машине под запись порядка характеристики, будем называть областью машинного порядка (ОМП) характеристики, а число, записанное в эту область будем называть машинным порядком. Если в ОММ в явном виде содержится точка, то числа, представленные в таком формате, называются числами с фиксированной точкой. Далее мы будем рассматривать числа, записанные в экспоненциальном виде (2). Числа, представленные в таком формате, называют еще числами с плавающей точкой.
3. НОРМАЛИЗАЦИЯ ЧИСЕЛ
Как уже отмечалось выше, при преобразовании числа, записанного в естественном виде, к экспоненциальному виду, точка в числе вида (1) может смещаться на произвольное количество разрядов вправо или влево. А, чтобы значение числа при этом не изменилось, на количество смещений должен быть откорректирован порядок характеристики экспоненциального числа. Очевидно, что при этом возникает множественность представления одного и того же числа, записанного в экспоненциальном виде.
Возьмем двоичное число 0.001001 и запишем его в экспоненциальном виде в машинное слово, в котором ОММ имеет 3 разряда. В том случае, когда предполагается, что машинная мантисса представлена в виде правильной дроби, мы будем иметь следующие возможные варианты записи этого числа: 0.1001•2^(-2)=0.100•2^(-2)=0.010•2^(-1)=0.001•2^0. Во всех этих случаях младший разряд записываемого числа был утерян, поскольку он вышел за пределы разрядной сетки ОММ. Итак, мы получили запись одного и того же числа различными вариантами.
Налицо неоднозначность представления чисел в машинном слове. Мы должны указать машине критерий выбора, по которому отдается предпочтение той или иной форме записи числа в машинном слове. Запись экспоненциального числа в формате, в котором мантисса числа представляется единственным образом, называется нормализацией.
В настоящее время чаще всего рассматриваются два варианта нормализации чисел. В первом варианте [1], перед записью в машинное слово число представляется в виде двоичного дробного числа, в котором единица стоит сразу после точки. При такой нормализации двоичная мантисса в виде правильной дроби лежит в диапазоне 0.1≤М< 1. Другой вариант нормализации заключается в следующем. Действительное число перед записью в машинное слово приводится к виду, в котором мантисса представляет собой смешанную дробь, в которой в младшем разряде целой части обязательно присутствует значащая цифра. Для двоичного числа эта цифра — единица. Такой вариант нормализации закреплен в стандарте IEEE754
4. ДЕЙСТВИТЕЛЬНЫЕ ЧИСЛА В ПРЕДСТАВЛЕНИИ СТАНДАРТА IEEE754
В настоящее время в компьютерной арифметике, для оперирования с действительными числами, широко используется стандарт IEEE754 [2]. В этом стандарте введен класс нормализованных чисел, который позволяет решить две проблемы. Одна из них, это неоднозначное представление чисел с плавающей запятой. А вторая проблема, это получение возможности представления чисел в широком диапазоне значений. Для решения этих проблем было предложено, перед записью в машинное слово действительного числа, приводить его к нормализованному виду.
Рассмотрим особенности представления действительных чисел с плавающей запятой в стандарте IEEE754.
В соответствие со стандартом, все числа в машинном слове представляются в нормализованном виде. Для этого они преобразуются к виду:
Здесь 1.М – мантисса, состоящая из единицы в целой части и числа М, которое записывается в ОММ сразу за единицей. Значения мантиссы в нормализованных числах располагаются в интервале:
а значения порядков характеристик чисел лежат в диапазоне –(B-1) ≤ h ≤ B. Здесь B – максимальное число, которое можно записать в ОМП машинного слова.
Двоичное нормализованное число в машинном слове с К- разрядной ОММ схематично выглядит следующим образом:
Расположение разрядов для знака порядка z и знака числа S в машинном слове может быть произвольным. На характер представимых чисел это не влияет. Как видно из этого рисунка, К-ый разряд в мантиссе нормализованного числа всегда равен единице. Этот разряд присутствует в машинном слове виртуально. На самом деле его в разрядной сетке нет. Тем не менее, виртуальная единица всегда учитывается при представлении чисел и при проведении математических операций над ними. Предполагается, что в нормализованном числе точка в мантиссе расположена справа от единицы.
В стандарте, чтобы сэкономить на знаке порядка, используется смещенное представление порядков характеристик чисел, записанных в машинное слово. Для простоты изложения, значения порядков характеристик мы представляем здесь и далее без смещения.
При нормализованном представлении чисел сразу возникает проблема, связанная с отсутствием нуля. В нормализованных числах нуль не входит в интервал значений мантиссы. Это обстоятельство не позволяет получить нулевой результат вычислений. При математических операциях над нормализованными числами в компьютере нуль в явном виде не получается, а получается его признак.
И вторая проблема, которая возникает при нормализации чисел, это ограничение диапазона представления малых чисел, из-за того, что значения мантиссы нормализованных чисел не превышают единицы. Это заставило авторов стандарта ввести класс денормализованных чисел, что существенно усложнило алгоритмы работы с такими числами.
Минимальное нормализованное число, которое можно записать в машинное слово, выглядит схематично следующим образом:
Это число, с учетом положения неявной единицы, может быть представлено следующим выражением:
|F_min | = S 2^(-В) (1+0) = S 2^(-В)
Где В- максимальное число, записанное в ОМП. Число 2^(-В) в стандарте считается специальным числом, которое принимается за ±0. Знак при нуле принимает свое значение в соответствие со значением знака S числа. Далее, для простоты, мы будем рассматривать только положительные числа.
Минимальным положительным нормализованным числом в стандарте считается число вида:
Таким образом, минимальное нормализованное число в стандарте представлено формулой:
Равенство порядка характеристики числу (-В) в стандарте зарезервировано под признак особого случая. Шаг, с которым изменяются значения нормализованных чисел равен ξ = 2^(-B)
Максимальное положительное нормализованное число в стандарте представлено в виде
Это число можно записать в виде выражения:
Вычислим диапазон представимых чисел, нормализованных по стандарту:
|F_max |/|F_min | = 2^В• (2-2^(-К))/ 2^(-В+1) = 2^(2В )•(1- 2^(-(К+1))) ≈ 2^2В
Как мы видим, диапазон представимых нормализованных чисел определяется главным образом диапазоном чисел, которые можно записать в ОМП.
Отметим также, что даже при К = 1, когда мантисса числа содержит всего один разряд, выбирая соответствующее значение В можно записать или очень большое, или очень маленькое число. Образно говоря, мантисса в числе, представленном в экспоненциальном виде, по аналогии с географической картой, определяет количество объектов, которое мы рассматриваем, а коэффициент q^(±h) является масштабным коэффициентом, определяющим расстояние между этими объектами. С увеличением масштабного коэффициента q^(±h), при ограниченной площади карты (разрядности ОММ), становятся различимы более мелкие объекты (числа), а количество различимых объектов (чисел) уменьшается. В десятичной системе счисления часто, для описания количественных характеристик физических объектов, вместо коэффициента q^(±h) используют приставки нано, микро, мили, кило, мега и проч.
Для того, чтобы расширить диапазон представимых чисел в сторону близких к нулю значений, в стандарте введен класс денормализованных чисел. Денормализованными числами считаются числа, которые определяются по формуле:
В этом выражении М — целое число, лежащее в диапазоне 1≤ М≤(2^(К-1)). А мантисса (M•2^(-К)), это дробное число, которое лежит в диапазоне
Как отсюда видно, в выражении для денормализованных чисел отсутствует неявная единица. В момент, когда характеристика нормализованного числа становится равной 2^(-B) (особый случай), далее преобразования ведутся так, как будто характеристика числа равна 2^(-(B-1)). А значения дробной мантиссы лежат в диапазоне (3). Это позволяет перейти из области нормализованных чисел к области чисел денормализованных плавно, без скачка. В области денормализованных чисел шаг изменения чисел становится равным ξ = 2^(-(B-1+К)). Когда значение мантиссы денормализованного числа становится равным нулю, считается, что число равно машинному нулю.
Денормализованное положительное число в машинном слове схематично выглядит следующим образом:
Минимальное положительное денормализованное число в машинном слове выглядит как:
Формула для минимального денормализованного числа будет следующей:
Максимальное положительное денормализованное число выглядит в машинном слове схематично следующим образом:
Формула для вычисления максимального денормализованного числа имеет вид:
Диапазон представимых в стандарте денормализованных чисел равен:
F_(den max)/ F_(den min)= 2^(В-1) (1-2^(-К)) /2^(-(В+К-1)) = 2^(2B-2) (2^К-1)
Ниже, для примера, приводится таблица, в которой представлено преобразование последовательности чисел перед записью их в машинное слово, в соответствие со стандартом IEEE754. Предполагается, что ОММ состоит из К=2 разрядов, плюс неявная единица. В ОМП можно записать максимальное число В=2. Минимальное число, с которого начинается отсчет нормализованных чисел, будет |F_min | = 2^(-2+1) = 2^(-1)= 0.1. В таблице эта область представлена числами на белом фоне. С 1 строки по 3-ю, на сером фоне, в таблице представлена область денормализованных чисел, для которых F_(den min) = 2^(-(2+2-1))= 2^(-3). Шаг изменения для этих чисел равен ξ = 0.001.
В 1-ом столбце таблицы пронумерованы шаги изменения числа от минимального значения до максимального. В столбце 2 записаны числа, которые образуются с каждым новым шагом. Значок х здесь означает произвольную цифру, нуль или единицу. Цифры, обозначенные как х, не участвуют в образовании мантиссы машинного слова. В столбце 3 представлена мантисса числа, после его нормализации. В столбце 4 представлены значения порядков характеристик чисел после их нормализации. В столбце 5 представлены значения машинных порядков характеристик чисел, записанных в машинное слово. В столбце 6 представлены денормализованные числа, которые записываются в ОММ. В столбце 7 пронумерованы уникальные денормализованные числа, которые записаны в машинном слове. А в столбце 8 пронумерованы уникальные нормализованные числа.
Как следует из приведенных в таблице значений чисел, записанных в машинное слово, диапазон представимых двоичных нормализованных чисел равен 1.11•2^2/1.0•2^(-1)=7/0.5=14. В то же время диапазон вместе взятых нормализованных и денормализованных чисел будет равен 1.11•2^2/0.01•2^(-1)=7/0.125=56. Видно, что диапазон представимых чисел, определяемый, как отношение максимального представимого числа к минимальному не совпадает с количеством представимых чисел.
Глядя на таблицу можно заметить, что, пока машинный порядок равен минимальному значению (h= -1), увеличение на 1 значения мантиссы приводит к увеличению числа, записанного в машинном слове тоже на 1. После того, как число в стр.2 станет равным двоичному числу 0.111, следующее его приращение на 1 дает число 1.000, которое не помещается в разрядную сетку ОММ. Поэтому мы сдвигаем точку в мантиссе числа на один разряд влево и увеличиваем машинный порядок на единицу. Далее изменение машинных чисел происходит с удвоенным значением шага. С каждым увеличением числа h на единицу происходит увеличение расстояния между соседними значениями машинных чисел вдвое.
Прежде чем мы поговорим о нашей следующей теме, пройдемся по теме экспоненциальной записи.
Экспоненциальная запись (научная нотация) – это удобное сокращение для краткого написания длинных чисел. И хотя сначала экспоненциальная запись может показаться чуждой, ее понимание поможет вам понять, как работают числа с плавающей запятой, и, что более важно, каковы их ограничения.
Числа в экспоненциальном представлении имеют следующий вид: мантисса x 10 порядок . Например, в экспоненциальном представлении 1,2 x 10 4 , 1,2 – это мантисса, а 4 – порядок (показатель степени). Поскольку 10 4 означает 10 000, 1,2 x 10 4 означает 12 000.
По соглашению, числа в экспоненциальном представлении записываются с одной цифрой перед десятичной запятой, а остальные цифры – после нее.
В экспоненциальном представлении оно будет записано как 5,9736 x 10 24 кг, что намного легче прочитать. Экспоненциальная запись имеет дополнительное преимущество, поскольку упрощает сравнение двух действительно больших или действительно маленьких чисел, просто сравнивая показатель степени.
Поскольку в C++ может быть сложно ввести или отобразить показатели степени, для обозначения части выражения «умноженное на 10 в степени» мы используем букву ' e ' (или иногда ' E '). Например, 1,2 x 10 4 будет записано как 1.2e4 , а 5,9736 x 10 24 будет записано как 5.9736e24 .
Для чисел меньше 1 показатель степени может быть отрицательным. Число 5e-2 эквивалентно 5 x 10 -2 , что составляет 5/10 2 или 0,05 . Масса электрона составляет 9.1093822e-31 кг.
Преобразование значений из экспоненциальной записи в распространенные метрические префиксы
Возможно, самый простой способ преобразовать число из экспоненциального представления в общепринятые метрические префиксы – это установить научный калькулятор в «инженерный» или «метрический» режим отображения. Просто установите калькулятор в этот режим отображения, введите в него любое число в экспоненциальном представлении, используя соответствующие клавиши (смотрите предыдущую статью), и нажмите клавишу «равно», и калькулятор должен отобразить то же число в инженерной/метрической записи.
Опять же, на протяжении всей книги я буду использовать SPICE как метод демонстрации базовых схем. Следовательно, в ваших интересах понимать экспоненциальную запись, чтобы вы могли легко понимать формат выходных данных.
Во многих областях науки и техники необходимо манипулировать очень большими и очень маленькими числовыми величинами. Некоторые из этих значений ошеломляют своими размерами, либо очень маленькими, либо чрезвычайно большими. Возьмем, к примеру, массу протона, одной из составляющих частиц ядра атома:
Масса протона = 0,00000000000000000000000167 грамма
Или рассмотрим количество электронов, проходящих через точку в цепи каждую секунду при постоянном электрическом токе 1 ампер:
1 ампер = 6250000000000000000 электронов в секунду
Много нулей, не правда ли? Очевидно, что работа с таким количеством нулей в числах, подобных этому, может стать довольно запутанной, даже с помощью калькуляторов и компьютеров.
Обратите внимание на эти два числа и на относительную редкость в них ненулевых цифр. Что касается массы протона, всё, что у нас есть, – это «167», перед которыми идут 23 нуля до достижения десятичной запятой. Для количества электронов в секунду для 1 ампера у нас есть «625», за которым следуют 16 нулей.
Диапазон ненулевых цифр (от первого до последнего) плюс любые нулевые цифры, которые используются не только в качестве заполнителей, мы называем «значащими цифрами» любого числа.
Значащие цифры в реальности обычно отражают точность измерения. Например, если мы скажем, что автомобиль весит 3000 фунтов, мы, вероятно, будем иметь в виду не то, что рассматриваемый автомобиль весит ровно 3000 фунтов, а то, что мы округлили его вес до значения, более удобного для произнесения и запоминания.
Округленная цифра 3000 имеет только одну значащую цифру: цифру «3» впереди – нули служат просто заполнителями. Однако если бы мы сказали, что автомобиль весит 3005 фунтов, тот факт, что вес не округлен до ближайшей тысячи фунтов, говорит нам, что два нуля в середине не просто заполнители, а все четыре цифры числа «3005» имеют значение для его точности. Таким образом, число «3005» состоит из четырех значащих цифр.
Точно так же числа с множеством нулей не обязательно представляют реальную величину вплоть до десятичной запятой. Когда это известно, такое число, чтобы с ним было легче работать, может быть записано в виде математического «сокращения». Это «сокращение» называется экспоненциальной записью.
В экспоненциальной записи число записывается, представляя его значащие цифры как значение от 1 до 10 (или от -1 до -10, для отрицательных чисел), а нули-заполнители учитываются с помощью множителя степени десяти. Например:
1 ампер = 6250000000000000000 электронов в секунду
. . . можно записать как. . .
1 ампер = 6,25 x 10 18 электронов в секунду
Но как насчет очень малых чисел, таких как масса протона в граммах? Мы по-прежнему можем использовать экспоненциальную запись, но с отрицательной степенью десяти вместо положительной, для смещения десятичной запятой влево, а не вправо:
Масса протона = 0,00000000000000000000000167 грамма
. . . можно записать как. . .
Масса протона = 1,67 x 10 -24 грамма
10 в -24-ой степени (10 -24 ) означает обратное (1/x) число 10, умноженное на себя 24 раза, или «1», перед которой стоит десятичная запятая и 23 нуля. Умножение на 1,67 дает в результате «167», перед которым стоит десятичная запятая и 23 нуля. Как и в случае с очень большим числом, человеку намного проще иметь дело с этой «сокращенной» записью. Как и в предыдущем случае, значащие цифры в этом числе четко выражены.
Поскольку значащие цифры представлены «сами по себе», в отличие от множителя степени десяти, легко показать уровень точности, даже если число выглядит круглым. Взяв наш пример с автомобилем весом 3000 фунтов, мы могли бы выразить округленное число 3000 в экспоненциальной записи как:
вес машины = 3 x 10 3 фунта
Если бы автомобиль на самом деле весил 3005 фунтов (с точностью до фунта), и мы хотели бы иметь возможность выразить точность измерения полностью, это число в экспоненциальной записи можно было бы записать так:
вес машины = 3,005 x 10 3 фунта
Однако что, если машина действительно весит 3000 фунтов (с точностью до фунта)? Если бы мы записали ее вес в «обычной» форме (3000 фунтов), не обязательно было бы ясно, что это число действительно было с точностью до ближайшего фунта, а не просто округлено до ближайшей тысячи фунтов или до ближайшей сотни фунтов, или с точностью до десяти фунтов. Экспоненциальная запись, напротив, позволяет нам без недоразумений показать, что все четыре цифры являются значащими:
вес машины = 3,000 x 10 3 фунта
Поскольку не было бы никакого смысла добавлять дополнительные нули справа от десятичной запятой (поскольку для экспоненциальной записи не нужны дополнительные нули-заполнители), мы знаем, что эти нули должны иметь значение для точности числа.
В 2013 году в презентации BlackHat под названием «Методы оптимизации и обфуксации SQL» Роберто Сальгадо представил несколько методов обхода для SQL-внедрения. Доклад включал методы для MySQL и MariaDB. В 2018 году хакеры из GoSecure пересмотрели эту презентацию и начали проводить тесты с MySQL и MariaDB. Они обнаружили, что ошибка экспоненциальной формы записи чисел, упомянутая в презентации Сальгадо, имела более широкие последствия. Оказывается, с её помощью можно добиться интересных вещей – интересных с точки взломщика. Эта ошибка позволяет синтаксису SQL оставаться рабочим, даже если он не должен быть таковым, что сбивает с толку средства защиты.
Экспоненциальная форма записи чисел (далее экспоненциальная запись) и, в частности, функция «e», была интегрирована во многие языки программирования, включая SQL. Неясно, является ли это частью всех реализаций SQL, но это часть реализации MySQL/MariaDB. Вот пример экспоненциальной записи, интегрированной в SQL-запрос из той самой презентации Роберто Сальгадо 2013 года. Обозначение «e» будет проигнорировано, поскольку оно используется в недопустимом контексте:
SELECT table_name FROM information_schema 1.e.tables
Таким образом, предыдущий запрос будет вести себя так же, как:
SELECT table_name FROM information_schema .tables
С помощью пары тестов обнаружено, что после команды «1.e» можно использовать следующие символы:
Чтобы проиллюстрировать проблему, авторы обзора привели пример набора данных:
Чего можно достичь с помощью команды «1.e» и символов за ней:
Приведённый выше запрос равен следующему запросу:
Примечательно, что цифра или число в команде «1.e» значения не имеет. Между точкой и функцией «е» может быть любое число или любой набор цифр, но точка является обязательной (например, «1337.1337e» также работает).
AWS — продукт CloudFront, который можно комбинировать с AWS WAF с определенными правилами. Они помогают компаниям защитить свои веб-приложения от взлома. Однако во время взаимодействия авторы обнаружили, что правило «База данных SQL» в AWS WAF можно обойти с помощью ошибки, указанной выше.
Простой запрос может показать, что WAF блокирует запрос с помощью известной команды 1′ или ′1′=′1:
Если использовать экспоненциальную запись в этом простом запросе, получится следующее:
Одного такого доказательства достаточно, чтобы объяснить, почему и как работает эта ошибка и продемонстрировать уязвимость безопасности для всех заинтересованных лиц.
Сначала ошибку в MySQL и MariaDB проигнорировали, так как никто не увидел последствий. Это никак не влияло на данные и не позволило повышать права, пока не нашелся обход WAF. Теперь, когда есть уязвимость в безопасности, можно выяснить, почему появилась такая ошибка и из-за чего она так себя ведет.
Во-первых, MySQL и MariaDB работают, находя признаки в запросе, такие как: числа, строки, комментарии, конец строки и т. д. Как только признак распознан, нужная функция анализирует его.
Во-вторых, кусок рассматриваемого кода проверяет целые или вещественные числа, поскольку сначала работает этот сегмент:
В-третьих, сегмент кода находит точку и попадает в функцию действительных чисел, и это тот сегмент, который нужен для понимания ошибки:
В этот момент сегмент кода уже обработал цифры перед точкой и начинает обрабатывать все цифры после точки. Потом условие проверяет, является ли символ буквой «e» или «E» и переходит к следующему символу. Если следующий за «е» символ не является цифрой, состояние устанавливается в «MY_LEX_CHAR», а затем завершается оператором «break», который возвращается в начало варианта переключения.
Наконец, следующим сегментом кода достигнут нужный оператор, и здесь признак полностью забывается и удаляется из запроса:
Как видно из примера, оператор «MY_LEX_CHAR» идёт через оператор «MY_LEX_SKIP», потому что, как можно понять из комментария «Unknown or single char token» (то есть неизвестный признак или символ) MySQL просто не знает, что с этим делать в данный момент. В случае с оператором «MY_LEX_SKIP» всё завершается возвратом символа. Если символ не является закрывающей круглой скобкой - «)», то переходим к оператору «MY_LEX_START», который будет искать новый признак. В любом случае, даже если он заканчивается закрытой скобкой, всё равно признак не возвращается, а отбрасывается.
Для исправления ошибки достаточно было бы прервать запрос, если признак неверен, вместо того, чтобы его пропустить. Когда MySQL или MariaDB находят начало признака с плавающей запятой и что за ним не идет цифра, они должен прервать запрос.
Хакеры отправили исправление в проекты MySQL и MariaDB. В данном случае это не проблема безопасности в MySQL/MariaDB как таковая. Любой WAF или аналогичные продукты игнорируют запросы SQL, сформированные таким образом, поэтому уязвимы. Если запросы в неправильном формате, продукты безопасности не будут рассматривать их как действительный код SQL и просто проигнорируют. Поэтому представленное исправление должно увеличить шансы на быстрое решение.
Позже хакеры решили оценить ModSecurity, популярный WAF для Apache и nginx. Он включает в себя библиотеку libinjection, на которую влияет представленная ошибка.
Ниже демонстрация возможности modsecurity блокировать вредоносный шаблон для SQL-внедрения. Возвращается запрещённая страница, что является следствием обнаружения.
Записи из modsecurity, в которых подчеркивается, что библиотека libinjection была запущена:
Обойти эту защиту можно, поставив перед буквальным выражением экспоненциальную запись «1.e». Библиотека Libinjection маркирует этот признак и определяет типы контекстных разделов как в предыдущем случае: комментарии и строки.
Libinjection рассматривает строку «1.e» как неизвестную команду SQL и приходит к выводу, что это скорее часть текста, чем часть кода. Такое поведение библиотеки libinjection возможно, когда она встречает неизвестную функции SQL. OWASP Core Rule Set (CRS) указали, что эффективная защита доступна ModSecurity, если установлен уровень в классификации ModSecurity «paranoia level 2». В нём есть функционал к обнаружению скрытых атак.
Как преобразовывать числа в экспоненциальное представление
Используйте следующую процедуру:
- ваш показатель степени начинается с нуля;
- сдвиньте десятичную запятую так, чтобы слева от нее была только одна ненулевая цифра;
- каждый знак, на который вы сдвигаете десятичную запятую влево, увеличивает показатель степени на 1;
- каждый знак, на который вы сдвигаете десятичную запятую вправо, уменьшает показатель степени на 1;
Вот несколько примеров:
Самое важное, что нужно понять: цифры в мантиссе (часть перед "е") называются значащими цифрами. Количество значащих цифр определяет точность числа. Чем больше цифр в мантиссе, тем точнее число.
Точность и конечные нули после десятичной запятой
Рассмотрим случай, когда мы просим двух лаборантов взвесить одно и то же яблоко. Один возвращается и говорит, что яблоко весит 87 грамм. Другой возвращается и говорит, что яблоко весит 87,00 грамм. Предположим, что взвешивание правильное. В первом случае фактический вес яблока может составлять от 86,50 до 87,49 грамма. Может быть, точность весов была 1 грамм. Или, может быть, наш помощник немного округлил. Во втором случае мы в большей степени уверены в фактическом весе яблока (оно весит от 86,9950 до 87,0049 граммов, где значительно меньше вариативности).
Поэтому в стандартной экспоненциальной записи мы предпочитаем оставлять конечные нули после десятичной запятой, потому что эти цифры несут полезную информацию о точности числа.
Однако в C++ 87 и 87.00 обрабатываются одинаково, и компилятор сохранит одно и то же значение для каждого из них. Нет никаких технических причин, по которым мы должны предпочесть одно другому (хотя могут быть научные причины, если вы используете исходный код в качестве документации).
Теперь, когда мы рассмотрели экспоненциальную запись (научную нотацию), мы готовы рассмотреть числа с плавающей запятой.
Изменение исходной схемы примера в SPICE
Давайте, модифицируем нашу схему, чтобы в ней был резистор 5 кОм (или 5000 Ом) вместо резистора 5 Ом, и посмотрим, что произойдет.
Рисунок 3 – Модификация схемы
И снова наш файл описания схемы, или «список соединений»:
Буква «k» после цифры 5 в строке резистора говорит SPICE, что это значение 5 кОм, а не 5 Ом. Давайте посмотрим, какой результат мы получим, запустив моделирование этой схемы:
Напряжение батареи, конечно же, не изменилось с момента первого моделирования: оно по-прежнему составляет 24 вольта. Ток в цепи, напротив, на этот раз намного меньше, потому что мы поставили резистор большего номинала, что затрудняет прохождение электронов. SPICE сообщает нам, что ток на этот раз равен -4.800E-03 ампер или -4,800 x 10 -3 ампер. Это эквивалентно тому, как взять число -4,8 и переместить десятичную запятую на три разряда влево.
Конечно, если мы узнаем, что 10 -3 – это то же самое, что и метрический префикс «милли», мы могли бы записать это значение как -4,8 миллиампер или -4,8 мА.
Глядя на «общую рассеиваемую мощность», предоставленную нам SPICE во время второго моделирования, мы видим, что она составляет 1.15E-01 Вт, или 1,15 x 10 -1 Вт. Степень -1 соответствует метрическому префиксу «деци», но обычно в электронике мы ограничиваемся использованием метрических префиксов, которые связаны со степенями десяти, которые кратны трем (десять в степени . -12, - 9, -6, -3, 3, 6, 9, 12 и т.д.).
Итак, если мы хотим следовать этому соглашению, мы должны выразить это значение рассеиваемой мощности как 0,115 Вт или 115 милливатт (115 мВт), а не 1,15 дециватт (1,15 дВт).
Выполнение анализа SPICE на примере схемы
Используя компьютер для запуска анализа SPICE этого файла описания схемы, мы получаем следующие результаты:
SPICE сообщает нам, что напряжение «в» узле номер 1 (фактически, это означает, что напряжение между узлами 1 и 0, узел 0 по умолчанию является опорной точкой для всех измерений напряжения) равно 24 вольт. Ток через батарею "v1" отображается как -4.800E+00 ампер. Это метод обозначения экспоненциальной записи в SPICE.
На самом деле он говорит «-4,800 x 10 0 ампер» или просто -4,800 ампер. Отрицательное значение тока здесь связано с особенностями SPICE и не указывает на что-либо существенное в самой схеме. «Общая рассеиваемая мощность» дается нам как 1.15E+02 ватт, что означает «1,15 x 10 2 Вт» или 115 Вт.
Моделирование простой схемы с помощью SPICE
Давайте начнем с очень простой схемы, состоящей из одного источника напряжения (батареи) и одного резистора:
Рисунок 1 – Простая схема из резистора и источника напряжения
Чтобы смоделировать эту схему с помощью SPICE, мы сначала должны обозначить номера узлов для всех отдельных точек в схеме, а затем перечислить компоненты вместе с соответствующими номерами узлов, чтобы компьютер знал, какой компонент к какому узлу и как подключен. Для такой простой схемы использование SPICE кажется излишним, но она служит для демонстрации практического использования экспоненциальной записи значений:
Рисунок 2 – Узлы схемы
Набрав файл описания схемы, или список соединений (netlist), для этой схемы, мы получим следующее:
Строка " v1 1 0 dc 24 " описывает батарею, расположенную между узлами 1 и 0, с постоянным напряжением 24 вольта. Линия " r1 1 0 5 " описывает резистор 5 Ом, расположенный между узлами 1 и 0.
Небольшой тест
Вопрос 1
Преобразуйте следующие числа в экспоненциальную запись в стиле C++ (используя e для обозначения экспоненты) и определите, сколько значащих цифр каждое имеет число (оставляйте конечные нули после десятичной запятой):
Программа моделирования схем SPICE использует экспоненциальное представление для отображения выходной информации и может интерпретировать как экспоненциальную запись, так и метрические префиксы в файлах описания схемы. Если вы собираетесь интерпретировать результаты анализа SPICE в данной книге, вы должны уметь понимать обозначения, используемые в этой программе для выражения переменных напряжения, тока и т.д.
Читайте также: