Курс Mit «Безопасность Компьютерных Систем». Лекция 1: «Введение: Модели Угроз», Часть 3



Массачусетский Институт Технологий.

Курс лекций №6.858. «Безопасность компьютерных систем».

Николай Зельдович, Джеймс Микенс.

2014 год Безопасность компьютерных систем — это курс, посвященный проектированию и внедрению безопасных компьютерных систем.

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

Темы включают безопасность операционной системы (ОС), возможности, управление информационными потоками, языковую безопасность, сетевые протоколы, аппаратную безопасность и безопасность веб-приложений.

Лекция 1: «Введение: модели угроз» Часть 1 / Часть 2 / Часть 3 Давайте запустим эту программу с помощью отладчика.

Об этом вы узнаете подробно в первой лаборатории.

Теперь попробуем установить точку останова в этой функции перенаправления, запустим программу и посмотрим, что получим.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Итак, я запустил программу, она начала выполнять основную функцию, и перенаправление произошло довольно быстро.

Отладчик теперь останавливается в начале перенаправления.

Мы можем видеть, что здесь происходит, например, можем попросить показать нам текущие регистры ЦП.

Здесь мы будем рассматривать самый низкий уровень, а не уровень исходного кода C. Мы рассмотрим реальные инструкции, выполняемые моей машиной, чтобы понять, что происходит на самом деле.

Язык C на самом деле может что-то скрывать от нас, поэтому мы попросим показать все регистры.

В 32-битных системах (x86), как вы помните, есть указатель на кадр стека — регистр EBP (stack-frame Base Pointer, указатель на кадр стека).

И моя программа, что неудивительно, тоже имеет стек.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

На x86 стек растет вниз, это тот стек, который показан на слайде, и мы можем продолжать «запихивать» в него наши данные.

Указатель стека в данный момент указывает на конкретную ячейку памяти ffffd010 (регистр ESP, адрес вершины стека).

Здесь есть какое-то значение.

Как оно туда попало? Один из способов понять это — посмотреть на код функции перенаправления.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Переменная Convenience должна иметь целочисленное значение.

Итак, мы можем проанализировать функцию по имени.

Здесь вы можете увидеть, что делает эта функция.

Первым делом он начинает производить какие-то действия с регистром EBP, это не очень интересно.

Но затем он вычитает определенное значение из указателя стека.

По сути, это создает пространство для всех переменных параметров, таких как буфер и целое число, как мы видели в исходном коде C. Теперь мы хотим понять, как работает эта функция.

Значение указателя стека, которое мы видели ранее, теперь находится в середине стека, а над ним находится информация о том, что происходит в буфере, каково целочисленное значение, а также адрес возврата к основной функции, которая реализована на стек.

Значит, где-то здесь у нас должен быть обратный адрес.

Сейчас мы просто пытаемся выяснить, где что находится в стеке.

Мы можем дать команду напечатать адрес этой буферной переменной.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Ее адрес — ffffd02c. Теперь выведем адрес целочисленного значения i — он выглядит так: ffffd0ac. Таким образом, целое число находится над стеком, а буфер — ниже.

То есть мы видим, что в этом месте в стеке находится наш буфер, сверху стоит целое число и возможно ещё что-то, а в самом конце идёт адрес возврата к основной функции, что называется «перенаправление».

».



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Мы видим, что стек растет вниз, потому что над ним есть вещи с «более высокими» адресами.

Внутри нашего буфера элементы будут располагаться так: внизу [0], а затем вверх по возрастанию до элемента [128], как я нарисовал на доске.

Давайте посмотрим, что произойдет, если мы введем те же данные, которые привели к сбою системы.

Но перед этим мы должны определить, где именно находится наш обратный адрес, как он связан с указателем ebp. В x86 есть удобная вещь, называемая соглашением, которая позволяет указатель EBP или регистр, указывающий на что-то, происходящее в работающем стеке, помечается как «сохраненный регистр EBP».

Это отдельный регистр, расположенный после всех переменных, но перед адресом возврата, как показано на этом рисунке.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Сохраняется по нескольким инструкциям, размещенным выше.

Давайте изучим, что такое сохраненный EBP. В отладчике GDB (GNU Debugger) вы можете проверить некоторую переменную X, например переменную-указатель EBP.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Вот его позиция в стеке — ffffd0b8. Действительно, он расположен выше нашей переменной i (регистра edi).

Это отлично.

И у него есть какое-то другое значение, которое EBP принимает перед вызовом функции, а выше находится еще одна ячейка памяти, которая и будет адресом возврата.

Если мы напечатаем ebp+4, он покажет нам содержимое стека 0x08048E5F. Давайте посмотрим, что это означает. Это то, что вы будете делать в лаборатории.

Итак, вы можете взять этот адрес и попытаться его проанализировать.

Что это такое и чем это заканчивается? Таким образом, GDB на самом деле помогает выяснить, какая функция содержит этот адрес.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Что такое 5ф? Об этом говорит обратный адрес.

Как видите, это утверждение следует сразу после перенаправление называется.

Итак, когда мы возвращаемся из перенаправления, это то же самое место, где мы оказываемся и откуда продолжаем выполнение функции.

Так где мы сейчас находимся? Подводя итог, мы можем попытаться разобрать наш указатель инструкций.

Введите «отключить $eip».



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Сейчас мы находимся в самом начале перенаправления.

Давайте попробуем запустить функцию get() и ввести команду «следующий».

А затем печатаем наше невообразимое значение, из-за которого программа остановилась — ААА.

А, чтобы посмотреть, что произойдет.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Итак, мы выполнили get(), но программа все еще работает. Сейчас мы узнаем, что сейчас происходит в памяти и почему потом все станет плохо.

Как вы думаете, ребята, что сейчас происходит? Я набрал последовательность символов A. Что команда get() сделала с памятью? Он поместил эту последовательность в стек памяти, который, если вы помните, содержит элементы от [0] до [128].

И вот эта последовательность А стала заполнять ее снизу вверх, вот так я ее нарисовала, по направлению стрелки.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Но у нас был только один указатель — начало адреса, то есть мы указали, где в буфере нам следует начать размещать A. Но get() не знает длину стека, поэтому просто продолжает заполнять память наши данные, перераспределяя их вверх по стеку, возможно, в обход обратного адреса и всего, что находится над нашим стеком.

Поэтому я набираю команду подсчета повторений A и получаю значение «180», которое превышает наше значение «128».



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Это не так уж и хорошо.

Мы можем еще раз проверить, что происходит с нашим указателем EBP, набрав $ebp. Получаем адрес 41414141.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Отлично, затем я набираю «показать местоположение обратного адреса $ebp+4» и получаю тот же адрес 41414141.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Это совсем нехорошо.

Здесь показано, что произойдет, если программа после перенаправления вернется сюда, то есть перепрыгнет в регистр с адресом 41414141. А там ничего нет! И она остановится.

То есть мы получили ошибку сегментации.

Так что давайте просто придем сюда и посмотрим, что произойдет. Набираем «next» и запускаем программу дальше.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Теперь мы приближаемся к концу функции и можем перешагнуть еще 2 инструкции.

Мы снова набираем «nexti».



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Вы можете видеть, что в конце функции есть инструкция «покинуть», которая восстанавливает стек туда, где он был.

Он как бы все время «подталкивает» указатель стека обратно на адрес возврата, используя тот же EBP, в основном для этого он и нужен.

И теперь стек указывает на обратный адрес, который мы собираемся использовать.

Фактически, это все наши символы А.

А если мы запустим еще одну инструкцию, то процессор перейдет по этому конкретному адресу 41414141, начнет там выполнять код и выйдет из строя, потому что это неверный адрес в таблице страниц.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Давайте проверим, что там происходит. Давайте еще раз распечатаем содержимое нашего буфера и убедимся, что он полностью заполнен 128 символами «А».



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Если вы помните, всего мы ввели в буфер 180 элементов «А».

Итак, что-то еще происходит после того, как произошло переполнение буфера.

Если вы помните, мы преобразовали A в целое число i в целочисленном регистре.

А если у нас есть только буквенные символы А, без каких-либо цифр, то в ячейку памяти записывается 0, так как букву невозможно представить в виде целого числа.

А 0, как известно, в языке Си означает конец строки.

Итак, GDB считает, что у нас есть хорошая, полная строка из 128 символов A.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Но это не имеет большого значения, потому что у нас все еще есть все те А на вершине, которые уже испортили стек.

Отлично, это был действительно важный урок.

Обратите внимание, что существует и другой код, который будет выполнен после того, как вам удастся переполнить буфер и вызвать повреждение памяти.

Вам нужно убедиться, что этот код не делает ничего глупого, например, пытается преобразовать буквенные символы A в целочисленные значения i. Так вот, он должен предусматривать, что если мы встретим нечисловое значение, в нашем случае это A, мы не сможем перейти по адресу 41414141. Таким образом, в некоторых случаях необходимо ограничить входные данные.

В данном случае это может быть не слишком важно, но в других ситуациях нужно внимательно относиться к типу входных данных, то есть указывать, какие данные — числовые или буквенные — должна обрабатывать программа.

Сейчас посмотрим, что будет дальше и перепрыгнем еще раз.

Давайте посмотрим на наш реестр.

Прямо сейчас EIP, тип указателя инструкций, указывает на последний адрес перенаправления.

.

Если мы сделаем еще один шаг, то наконец доберемся до нашего несчастного 41414141.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Действительно, программа следует нашим инструкциям, и если мы попросим GDB напечатать текущий набор регистров, индикатор текущей позиции покажет странное значение.

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



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Это произошло из-за того, что программа попыталась следовать указателю инструкции, который не соответствовал допустимой странице процесса в таблице страниц операционной системы.

Ясно? Отлично, у меня к вам вопрос.

Так в чем именно наша проблема? Аудитория: С этой программой вы можете делать все, что захотите! Совершенно верно! Хотя на самом деле вводить такое огромное количество этих А было довольно глупо.

Но если бы вы точно знали, куда положить эти значения, вы могли бы поместить туда другие значения и перейти по какому-то другому адресу.

Посмотрим, сможем ли мы это сделать.

Давайте остановим нашу программу, перезапустим ее и снова введем много символов A, чтобы переполнить буфер.

Но я не собираюсь выяснять, какое место A находится в стеке.

Но предположим, что на этом этапе я переполняю стек, а затем пытаюсь вручную изменить что-то в стеке, чтобы функция перешла туда, куда я хочу.

Итак, я снова вхожу в NEXTI.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Где мы? Мы снова в самом конце перенаправления.

Давайте посмотрим на наш стек.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Если мы проверим ESP, то увидим наш поврежденный указатель.

Отлично.

Куда нам отсюда прыгнуть? Что интересного мы могли бы сделать? К сожалению, эта программа очень ограничена.

В ее коде нет ничего, что позволило бы нам перепрыгнуть и сделать что-нибудь интересное, но мы все равно попробуем.

Возможно, мы сможем найти функцию PRINTF, перепрыгнуть туда и заставить ее напечатать какое-то значение или эквивалентное значение X. Мы можем дизассемблировать основную функцию — отключить main.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

А функция main делает целую кучу вещей — инициацию, переадресацию звонков, много чего другого, а потом вызывает PRINTF. Так как насчет того, чтобы перейти к этому моменту - <+26> , который устанавливает аргумент PRINTF в %eax в регистре <+22> ? Таким образом, мы можем получить значение в регистре <+26> и прикрепите его к этой стопке.

Это должно быть достаточно легко сделать с помощью отладчика: вы можете сделать этот набор {int} esp равным этому значению.

Вы можете еще раз проверить ESP, и действительно, он имеет это значение.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Давайте продолжим работу с командой «C», и мы увидим, что функция вывела X, равную какой-то ерунде, и я думаю, что именно из-за содержимого этого стека мы попытались напечатать.

Мы неправильно сконфигурировали все аргументы, потому что перешли в середину этой последовательности вызовов (последовательности команд и данных, необходимых для вызова данной процедуры).



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Да, мы напечатали это значение, и после этого система зависла.

Почему это случилось? Мы перешли к функции PRINTF, но что-то пошло не так.

Мы изменили обратный адрес, чтобы при возврате из редиректа переходить на этот новый адрес, в ту же точку сразу после PRINTF. Так откуда же взялся этот глюк? Аудитория: Потому что функция main возвращает результат! Совершенно верно! Вот что происходит - это точка, где мы прыгнули, в регистре <+26> .

Он устанавливает некоторые параметры и вызывает PRINTF. PRINTF работает и готов к возврату.

Пока все в порядке, потому что эта инструкция вызова «заталкивает» адрес возврата в стек, чтобы этот адрес использовался функцией PRINTF.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Основная функция продолжает работать, готовая выдать инструкцию LEAVE, в которой нет ничего интересного, а затем сделать еще один «возврат» в регистр.

<+39> .

Но дело в том, что в этом стеке нет правильного адреса возврата.

Итак, предположительно, мы возвращаемся к кому-то другому, кто знает ячейку памяти над стеком, и прыгаем куда-то еще.

Так что, к сожалению, наши псевдоатаки здесь не работают. Здесь выполняется какой-то другой код. Но затем он «падает».

Вероятно, это не то, что мы хотели сделать.

Так что если вы действительно хотите быть осторожными, вам следует не только аккуратно поместить адрес возврата в стек, но и выяснить, от кого второй RET получит свой адрес возврата.

Затем вам нужно попытаться осторожно поместить в стек что-то еще, чтобы убедиться, что ваша программа продолжает работать «чисто» после взлома и никто не заметит подделку.

Все это вы попробуете сделать в лабораторной работе №1, только более подробно.

Теперь нам нужно подумать еще об архитектуре стека при переполнении буфера.

В данном случае наша проблема в том, что обратный адрес там вверху, верно? Буфер продолжает расти и в конечном итоге переполняет обратный адрес.

Но что, если мы перевернем стек «готово»? Знаете, на некоторых машинах стеки растут. Таким образом, мы могли бы представить себе альтернативную схему, в которой стек начинается снизу и продолжает расти вверх, а не вниз.

Поэтому, если вы таким образом переполните буфер, вы просто продолжите подниматься вверх по стеку, и ничего плохого не произойдет. Сейчас я вам нарисую, чтобы объяснить, как это выглядит. Пусть обратный адрес будет здесь, внизу стека.

Вверху наши переменные, или сохраненные EBP, затем целочисленные переменные, а в самом верху буфер от [0] до [128].

Если мы переполнимся, он пойдет вверх по этой стрелке.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Таким образом, переполнение буфера не повлияет на адрес возврата.

Что нам нужно сделать в нашей программе, чтобы реализовать эту опцию? Правильно, перенаправление! Разместим слева кадр стека, который будет выполнять такое перенаправление, и перенаправим вызов функции вверх.

В результате наша диаграмма будет выглядеть так: адрес возврата располагается вверху стека, затем сохраненный EBP, а все остальные переменные будут располагаться поверх него.

А затем мы начнем переполнять буфер командой get(S).



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Таким образом, функция по-прежнему проблематична.

Главным образом потому, что буфер со всех сторон окружен функциями возврата и в любом случае можно что-то переполнить.

Предположим, что на нашей машине есть стек, который растет вверх.

Тогда в какой момент вы сможете перехватить контроль над выполнением программы? На самом деле, в некоторых случаях это даже проще.

Вам не нужно ждать, пока перенаправление вернется.

Возможно, были даже такие вещи, как превращение A в i. На самом деле это проще, потому что get(S) переполняет буфер.

Это изменит адрес возврата, а затем немедленно вернётся и перейдет туда, где вы пытались создать какую-то конструкцию.

Что будет, если у нас будет такая довольно скучная программа для всяких экспериментов? Кажется, он не содержит какого-либо интересного кода перехода.

Все, что вы можете сделать, это напечатать еще одно значение X здесь, в PRINTF.

Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Давай попробуем! Аудитория: Если у вас есть дополнительный стек, можете ли вы туда поставить произвольный код, который, например, выполняет оболочку программы? Да-да-да, это действительно разумно, потому что тогда вы сможете поддерживать другие «входные» значения.

Но некоторая защита от этого есть, о ней вы узнаете в следующих лекциях.

Но в принципе здесь может быть обратный адрес, который перекрывается на обоих типах машин — стек-вверх и стек-вниз.

И вместо того, чтобы указывать его в существующем коде, например PRINTF в основной функции, мы могли бы иметь адрес возврата в буфере, поскольку это просто какое-то место в буфере.

Но вы можете «прыгнуть» туда и рассматривать его как исполняемый параметр.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

В рамках вашего запроса вы отправляете на сервер несколько байтов данных, а затем получаете обратный адрес или объект, который вы поместили в эту ячейку буфера, и продолжаете выполнение программы с этой точки.

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

Действительно, в Unix-системах злоумышленники часто поступают именно так — они просят операционную систему просто выдать команду BIN SH, которая позволяет выбрать тип произвольных команд оболочки, которые затем выполняются.

В результате эта штука, этот кусок кода, который вы вставили в буфер, по ряду исторических причин называется шелл-кодом.

И в своей лабораторной работе вы попытаетесь сконструировать нечто подобное.



Курс MIT «Безопасность компьютерных систем».
</p><p>
 Лекция 1: «Введение: модели угроз», часть 3

Аудитория: Есть ли здесь разделение между кодом и данными? Исторически многие машины не обеспечивали никакого разделения кода и данных, а просто имели плоское адресное пространство памяти: указатель стека указывает туда, указатель кода указывает туда, и вы просто выполняете то, на что он указывает. Современные машины пытаются обеспечить некоторую защиту от такого рода атак, поэтому они часто создают разрешения, связанные с разными областями памяти, и одно из разрешений является исполняемым.

Таким образом, часть вашего 32-битного или 64-битного адресного пространства, содержащая код, имеет разрешение на выполнение операций.

И если ваш указатель инструкций указывает туда, процессор фактически будет управлять этими вещами.

Но стек и другие области данных в вашем адресном пространстве обычно не имеют разрешения на выполнение.

То есть, если вам случится каким-то образом установить указатель инструкции в какую-то позицию, соответствующую области памяти, где нет кода, процессор откажется выполнять эту инструкцию.

Так что это довольно хороший способ защититься от некоторых типов атак, но он совершенно не предотвращает их возникновение.

Итак, как бы вы обошли это препятствие, если бы у вас был неисполняемый стек? Фактически, вы уже видели этот пример, где мы просто «прыгнули» в середину основной функции.

Так что это был способ использовать переполнение буфера без необходимости вводить новый собственный код. Следовательно, даже если бы этот стек не был исполняемым, я все равно смог бы попасть в середину основной функции.

В данном конкретном случае это довольно скучно, потому что все, что вам нужно сделать, это ввести PRINT X, чтобы привести к сбою системы.

Но в других ситуациях в вашей программе могут быть другие фрагменты кода, позволяющие делать интересные вещи, которые вам действительно хочется делать.

Это называется атакой «возврата к lib c» — атакой переполнения буфера.

В этом случае адрес возврата функции в стеке заменяется адресом другой функции, а параметры вызываемой функции записываются в последующую часть стека.

Это способ обойти меры безопасности.

Таким образом, в контексте переполнения буфера не существует действительно четкого решения, обеспечивающего идеальную защиту от этих ошибок, потому что, в конце концов, программист допустил ошибку при написании этого исходного кода.

И лучший способ исправить это, вероятно, просто изменить исходный код и убедиться, что вы не используете слишком много getS(), о чем вас предупреждал компилятор.

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

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

Например, они делают стек неисполняемым, чтобы вы не могли поместить в него шелл-код и вам приходилось делать что-то более сложное для достижения своей цели.

В следующих двух лекциях мы рассмотрим эти методы защиты.

Они не идеальны, но на практике значительно усложняют жизнь хакера.

Аудитория: будет ли контрольная работа по теме сегодняшней лекции и когда? Да, если вы посмотрите расписание, то увидите там 2 теста.

Итак, подведем итоги.

Что нам делать с проблемами переполнения буфера? Общий ответ должен быть таким: нужно иметь как можно меньше механизмов.

Как мы видели, если вы планируете применять политики безопасности к каждой части программного обеспечения, вы неизбежно допустите ошибки.

И они позволят противнику обойти ваш механизм и воспользоваться некоторыми недостатками веб-сервера.

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

Сама политика безопасности будет реализована с помощью небольшого количества компонентов.

А остальная часть системы, правильная она или нет, не имеет никаких последствий для безопасности, если только она не нарушает саму политику безопасности.

Так что своего рода минимизация надежной вычислительной базы — это достаточно мощная технология, позволяющая обойти ошибки механизма и проблемы, которые мы сегодня рассмотрели более или менее подробно.

На сегодня все, приходите на лекцию в понедельник и не забывайте задавать вопросы на сайте.

Продолжение следует… Доступна полная версия курса Здесь .

Спасибо, что остаетесь с нами.

Вам нравятся наши статьи? Хотите увидеть больше интересных материалов? Поддержите нас, разместив заказ или порекомендовав друзьям, Скидка 30% для пользователей Хабра на уникальный аналог серверов начального уровня, который мы придумали для вас: Вся правда о VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps от 20$ или как правильно расшарить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40 ГБ DDR4).

Dell R730xd в 2 раза дешевле? Только здесь 2 x Intel Dodeca-Core Xeon E5-2650v4 128 ГБ DDR4 6x480 ГБ SSD 1 Гбит/с 100 ТВ от 249 долларов США в Нидерландах и США! Прочтите об этом Как построить корпоративную инфраструктуру класса, используя серверы Dell R730xd E5-2650 v4 стоимостью 9000 евро за копейки?

Теги: #информационная безопасность #программирование #ИТ-инфраструктура #безопасность #Политика #Политика #Политика #Модель угроз #Модель угроз #Модель угроз #Механизм #Механизм #Механизм #EBP #GDB
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.