Массачусетский Институт Технологий.
Курс лекций №6.858. «Безопасность компьютерных систем».
Николай Зельдович, Джеймс Микенс.
2014 год Безопасность компьютерных систем — это курс, посвященный проектированию и внедрению безопасных компьютерных систем.
Лекции охватывают модели угроз, атаки, ставящие под угрозу безопасность, а также методы обеспечения безопасности, основанные на последних научных работах.
Темы включают безопасность операционной системы (ОС), возможности, управление информационными потоками, языковую безопасность, сетевые протоколы, аппаратную безопасность и безопасность веб-приложений.
Лекция 1: «Введение: модели угроз» Часть 1 / Часть 2 / Часть 3 Лекция 2: «Контроль хакерских атак» Часть 1 / Часть 2 / Часть 3 Лекция 3: «Переполнение буфера: эксплойты и защита» Часть 1 / Часть 2 / Часть 3 Лекция 4: «Разделение привилегий» Часть 1 / Часть 2 / Часть 3 Лекция 5: «Откуда берутся ошибки системы безопасности» Часть 1 / Часть 2 Лекция 6: «Возможности» Часть 1 / Часть 2 / Часть 3 Лекция 7: «Нативная клиентская песочница» Часть 1 / Часть 2 / Часть 3 В правиле С4 Есть одно предостережение.
Вы не можете «перепрыгнуть» через конец выполнения программы.
Последнее, к чему вы можете перейти, — это последняя инструкция.
Таким образом, это правило гарантирует, что при выполнении программы в процессе «движок» не возникнет несоответствий.
Правило С5 говорит, что не может быть инструкций размером более 32 байт. Мы рассмотрели некоторый вариант этого правила, когда говорили о том, что размер инструкции кратен 32 байтам, иначе вы можете перепрыгнуть в середину инструкции и создать проблему с системным вызовом, который может там «прятаться».
Правило С6 утверждает, что все существующие инструкции можно дизассемблировать с самого начала.
Таким образом, это гарантирует, что мы видим каждую инструкцию и можем проверить все инструкции, которые запускаются при выполнении программы.
Правило С7 утверждает, что все прямые прыжки правильные.
Например, вы сразу переходите к той части инструкции, где указана цель, и хотя она не кратна 32, это по-прежнему допустимая инструкция, которую можно дизассемблировать слева направо.
Аудитория: В чем разница между С5 И С3 ? Профессор: я думаю что С5 говорит, что если у меня есть многобайтовая инструкция, она не может пересекать границы соседних адресов.
Допустим, у меня есть поток инструкций, и есть адрес 32 и адрес 64. Итак, инструкция не может пересечь границу, кратную 32 байтам, то есть она не должна начинаться с адреса меньше чем 64 и заканчиваются адресом больше 64.
Вот что говорит правило С5 .
Потому что в противном случае, совершив прыжок с кратным 32, можно оказаться в середине другой инструкции, где не знаешь, что происходит. И правило С3 является аналогом этого запрета на стороне прыжка.
В нем говорится, что каждый раз, когда вы прыгаете, длина вашего прыжка должна быть кратна 32. С5 также утверждает, что все, что находится в диапазоне адресов, кратном 32, является безопасной инструкцией.
Прочитав список этих правил, у меня возникло смешанное чувство, так как я не мог оценить, являются ли эти правила достаточными, то есть минимальным списком или полным списком.
Итак, давайте подумаем о домашнем задании, которое вам предстоит сделать.
Я думаю, что это на самом деле на работе Родной клиент Возникла ошибка при выполнении какой-то сложной инструкции в песочнице.
Я считаю, что у них была неправильная кодировка длины, что могло привести к чему-то плохому, но я не могу точно вспомнить, в чем заключалась ошибка.
Допустим, валидатор песочницы неправильно получает длину некоторой инструкции.
Что плохого может случиться в этом случае? Как бы вы использовали этот промах? Аудитория: например, вы можете скрыть системный вызов или оператор возврата в отставку .
Профессор: Да.
Предположим, есть какая-то причудливая версия инструкции И , который вы записали.
Возможно, валидатор допустил ошибку и посчитал, что его длина равна 6 байтам, тогда как фактическая длина составляла 5 байт.
Что случится? Валидатор считает длину этой инструкции равной 6 байтам и помещает за ней еще одну действительную инструкцию.
Но процессор при выполнении кода использует реальную длину инструкции, то есть 5 байт. В результате у нас есть свободный байт в конце инструкции И , куда мы могли бы вставить системный вызов и использовать его в своих интересах.
И если мы вставим сюда байт CD , это будет похоже на начало другой инструкции.
Далее мы поместим что-то в следующее 6-байтовое пространство, и это будет похоже на инструкцию, которая начинается с байта.
CD , хотя на самом деле это часть инструкции И .
После этого мы можем сделать системный вызов и «выйти» из песочницы.
Итак, валидатор Родной клиент должен синхронизировать свои действия с действиями Процессор , то есть «угадать», как именно процессор будет интерпретировать каждую инструкцию.
И это должно быть на каждом уровне песочницы, что довольно сложно реализовать.
На самом деле в Родной клиент Есть и другие интересные ошибки.
Во-первых, среда процессора не очищается должным образом при переходе в среду доверенных служб.
Доверенная среда выполнения службы .
Я думаю, мы поговорим об этом через секунду.
Но Доверенная среда выполнения службы в основном буду работать с одним и тем же набором регистров Процессор , которые предназначены для запуска ненадежных модулей.
Таким образом, если процессор забудет что-то очистить или перезагрузить, среда выполнения может быть обманута, заставив думать, что ненадежный модуль является доверенным приложением, и сделать что-то, чего он не должен делать или что-то, что задумали разработчики.
Так где мы сейчас находимся? В настоящее время мы понимаем, как дизассемблировать все инструкции и как предотвратить выполнение незаконных инструкций.
Теперь давайте посмотрим, как мы храним память и ссылки как для кода, так и для данных внутри границ модуля.
Родной клиент .
Из соображений производительности ребята из Родной клиент одновременно начиная использовать аппаратную поддержку, чтобы убедиться, что хранение памяти и ссылок на самом деле не приводит к большим накладным расходам.
Но прежде чем я рассмотрю аппаратную поддержку, которую они используют, я хочу услышать предложения о том, как они могли бы сделать то же самое без аппаратной поддержки? Можем ли мы просто предоставить доступ ко всем процессам памяти в пределах границ, ранее установленных машиной? Аудитория: вы можете инструментировать инструкции для очистки всех старших битов.
Профессор: Да, это правильно.
На самом деле мы видим, что у нас есть вот эта инструкция И , и каждый раз, когда мы, например, куда-то прыгаем, он очищает младшие биты.
Но если мы хотим сохранить весь возможный код, который выполняется в пределах 256 МБ, мы можем просто заменить первый атрибут ж на 0 и вместо $0xffffffe0 получать $0x0fffffe0 .
Это очищает младшие биты и устанавливает верхний предел в 256 МБ.
Таким образом, это делает именно то, что вы предлагаете, позволяя вам быть уверенным, что при каждом переходе вы находитесь в пределах 256 МБ.
А тот факт, что мы проводим разборку, также позволяет нам проверить, все ли прыжки вперед находятся в пределах досягаемости.
Причина, по которой они не делают этого для своего кода, заключается в том, что платформа х86 ты можешь очень эффективно кодировать И , где все старшие биты равны 1. Это приводит к существованию 3-байтовой инструкции для И и 2-байтовая инструкция перехода.
Таким образом, мы имеем дополнительную стоимость в 3 байта.
Но если вам нужно больше, чем один старший бит, подобный этому 0 вместо ж , то у вас внезапно появляется 5-байтовая инструкция.
Поэтому я думаю, что в этом случае они беспокоятся о накладных расходах.
Аудитория: Есть ли проблема с существованием некоторых инструкций, которые увеличивают версию, которую вы пытаетесь получить? То есть вы можете сказать, что ваша инструкция может иметь постоянное смещение или что-то в этом роде? Профессор: Я думаю да.
Вы, вероятно, запретите инструкции, которые переходят к некоторой сложной формуле адреса, и будете поддерживать только инструкцию, которая переходит непосредственно к этому значению, и это значение всегда получает И .
Аудитория: это больше для доступа к памяти, чем.
Профессор: да, потому что это всего лишь код. И для доступа к памяти на платформе х86 Существует много странных способов доступа к определенной ячейке памяти.
Обычно сначала следует вычислить ячейку памяти, а затем добавить дополнительные И и только потом открывайте доступ.
Я думаю, что это настоящая причина, по которой они беспокоятся о потере производительности, связанной с использованием этого набора инструментов.
На платформе х86 или, по крайней мере, на 32-битной платформе, описанной в статье, они используют аппаратную поддержку вместо ограничения данных кода и адреса, которые ссылаются на ненадежные модули.
Давайте посмотрим, как он выглядит, прежде чем разбираться, как использовать модуль.
NaCl в песочнице.
Это оборудование называется сегментацией.
Оно возникло еще до появления платформы х86 получил файл подкачки.
На платформе на х86 Пока процесс запущен, есть таблица поддерживаемого оборудования.
Назовем это таблицей дескрипторов сегментов.
Он состоит из набора сегментов, пронумерованных от 0 до конца таблицы любого размера.
Это что-то вроде файлового дескриптора в Юникс , за исключением того, что каждая запись состоит из двух значений: основы база и длина длина .
Эта таблица говорит нам, что у нас есть пара сегментов, и всякий раз, когда мы ссылаемся на конкретный сегмент, это в некотором смысле означает, что мы говорим об участке памяти, который начинается с базового адреса.
база и продолжается по длине длина .
Это помогает нам соблюдать ограничения памяти на платформе.
х86 , поскольку каждая инструкция, обращающаяся к памяти, обращается к определенному сегменту в этой таблице.
Например, когда мы делаем мов (%eax), (%ebx) , то есть перемещаем значение памяти из указателя, хранящегося в регистре ЕАХ , на другой указатель, хранящийся в регистре ЕВХ , программа знает начальный и конечный адреса и сохранит значение во втором адресе.
Но на самом деле на платформе х86 когда мы говорим о памяти, существует неявная вещь, называемая дескриптором сегмента, похожая на дескриптор файла в Юникс .
Это просто индекс таблицы дескрипторов, и, если не указано иное, каждый код операции содержит сегмент по умолчанию.
Итак, когда вы это сделаете mov(%eax) , это относится к %ds Или в регистр сегмента данных, который является специальным регистром вашего процессора.
Если я правильно помню, это 16-битное целое число, указывающее на эту таблицу дескрипторов.
И то же самое касается (%ebx) – это относится к тому же селектору сегмента %ds .
Фактически, в х86 У нас есть группа из 6 селекторов кода: CS, DS, ES, FS, GS И SS .
Выбор сегмента вызова CS (селектор вызова) используется неявно для получения инструкций.
Таким образом, если ваш указатель инструкций указывает на что-то, то он относится к тому, что выбрал селектор сегмента.
К.
С.
.
Большинство ссылок на данные неявно используют Д.
С.
или ES , ФС И Г.
С.
обозначать какие-то особые вещи, и SS всегда используется для операций со стеком.
И если ты это сделаешь нажми и поп , то они неявно берутся из этого селектора сегмента.
Это довольно архаичная механика, но в данном конкретном случае она оказывается чрезвычайно полезной.
Если вы получаете доступ к какому-либо адресу, например, в селекторе %ds: адрес , аппаратное обеспечение ретранслирует его в табличную операцию adrr + T [%ds].
base .
Это означает, что он возьмет адрес длины модуля из той же таблицы.
Поэтому всякий раз, когда вы обращаетесь к памяти, она имеет базу селекторов сегментов в виде записей таблицы дескрипторов, берет указанный вами адрес и сопоставляет его с длиной соответствующего сегмента.
Аудитория: так почему же это не используется, например, для защиты буфера? Профессор: да, это хороший вопрос! Можем ли мы использовать это для защиты от переполнения буфера? Например, для каждого буфера, который у нас есть, мы можем указать здесь базу буфера, а там размер буфера.
Аудитория: что, если вам не нужно помещать его в таблицу, прежде чем вы захотите в нее записать? Вы не хотите, чтобы оно было там все время.
Профессор: Да.
Поэтому я думаю, что причина, по которой такой подход не часто используется для защиты от переполнения буфера, заключается в том, что количество записей в этой таблице не может превышать 2 в 16-й степени, поскольку длина дескрипторов составляет 16 бит, но на самом деле их немного больше.
биты используются для других вещей.
Таким образом, на самом деле в этой таблице можно разместить только 2 записи в 13-й степени.
Поэтому, если в вашем коде есть массив данных размером более 2 13 , эта таблица может переполниться.
Кроме того, было бы странно, чтобы компилятор напрямую манипулировал этой таблицей, поскольку обычно манипуляции с ней производятся с помощью системных вызовов.
Вы не можете писать в эту таблицу напрямую, необходимо сначала сделать системный вызов операционной системы, после чего операционная система будет писать в эту таблицу.
Поэтому я думаю, что большинство компиляторов просто не захотят иметь дело с такой сложной системой управления буфером памяти.
Кстати, Мультикс использует этот подход: у него есть 2 18 записи для разных сегментов и 2 18 записи для возможных смещений.
И каждый фрагмент разделяемой библиотеки или фрагмент памяти — это отдельные сегменты.
Все они проверяются по диапазону и поэтому не могут использоваться на уровне переменных.
Аудитория: Предположительно, постоянная необходимость использования ядра приведет к замедлению процесса.
Профессор: Да, это правильно.
Таким образом, у нас возникнут накладные расходы из-за того, что если в стеке вдруг будет создан новый буфер, нам нужно будет сделать системный вызов для его добавления.
Так сколько же из этих элементов на самом деле используют механизм сегментации? Вы можете догадаться, как это работает. Я думаю, что по умолчанию все эти сегменты находятся в х86 иметь основание 0 и длину от 2 до 32. Таким образом, вы можете получить доступ ко всему диапазону памяти, который вам нужен.
Поэтому для NaCl они кодируют базу 0 и устанавливают длину 256 мегабайт. Затем они указывают на все селекторы сегментов регистра 6 в этой записи для области 256 МБ.
Таким образом, всякий раз, когда оборудование обращается к памяти, оно изменяет ее со смещением в пределах 256 МБ.
Так что возможность смены модуля будет ограничена диапазоном 256 МБ.
Думаю, теперь вы понимаете, как поддерживается это оборудование и как оно работает, поэтому, возможно, вам придется использовать эти селекторы сегментов.
Так что же может пойти не так, если мы просто реализуем этот план? Можем ли мы «выпрыгнуть» из селектора сегмента в ненадежном модуле? Я думаю, следует быть осторожным с тем, что эти регистры похожи на обычные регистры, и вы можете перемещать значения в них и из них.
Поэтому вы должны убедиться, что недоверенный модуль не повредит эти регистры селектора сегмента.
Потому что где-то в таблице дескрипторов вполне может быть запись, которая также является исходным дескриптором сегмента для процесса с основанием 0 и длиной до 2. 32 .
Итак, если ненадежный модуль смог изменить К.
С.
, или Д.
С.
, или ES или любой из этих селекторов, чтобы они начали указывать на исходную операционную систему, охватывающую все ваше адресное пространство, затем вы можете сделать ссылку на память на этот сегмент и выйти из песочницы.
Таким образом, Родной клиент следовало бы добавить еще несколько инструкций к этому запрещенному списку.
Я думаю, они запрещают все утверждения вроде mov %ds, es и так далее.
Поэтому, находясь в песочнице, вы не можете изменить сегмент, на который ссылаются некоторые вещи, связанные с ним.
На платформе х86 инструкции по изменению таблицы дескрипторов сегментов являются привилегированными, но изменение самой таблицы дескрипторов сегментов дс, эс и т. д. в таблице совершенно непривилегирован.
Аудитория: можете ли вы инициализировать таблицу так, чтобы все неиспользуемые слоты были заполнены нулевой длиной? Профессор: Да.
Вы можете установить длину стола для чего-то, где нет неиспользуемых слотов.
Оказывается, что вам действительно нужен, так это дополнительный слот, содержащий 0 и 2. 32 , потому что среда доверенная среда выполнения должен работать в этом сегменте и иметь доступ ко всему диапазону памяти.
Поэтому эта запись необходима для работы доверенной среды.
время выполнения .
Аудитория: Что нужно, чтобы изменить длину вывода таблицы? Профессор: нужно иметь root права.
В Линукс на самом деле существует система под названием модифицировать_ldt() для локальной таблицы дескрипторов, которая позволяет любому процессу изменять свою собственную таблицу, то есть фактически для каждого процесса существует одна таблица.
Но на платформе х86 тут сложнее, есть и глобальная таблица, и локальная таблица.
Локальную таблицу, посвященную конкретному процессу, можно изменить.
Теперь давайте попробуем разобраться, как мы входим в процесс выполнения и выходим из него.
Родной клиент или выпрыгнуть из песочницы.
Что для нас значит «выпрыгнуть»?
Итак, нам нужно запустить этот доверенный код, и этот доверенный код находится где-то выше предела в 256 МБ.
Чтобы прыгнуть туда, нам придется отменить всю установленную нами защиту.
Родной клиент .
В основном они сводятся к изменению этих шести селекторов.
Я думаю, что наш валидатор не будет применять те же правила для вещей, размер которых превышает предел в 256 МБ, поэтому он достаточно прост. Но тогда нам нужно каким-то образом перейти в доверенную среду выполнения.
доверенная среда выполнения и сбросим селекторы сегментов на правильные значения для этого гигантского сегмента, охватывающего адресное пространство всего процесса — этот диапазон от 0 до 2 32 .
Такие механизмы существуют в Родной клиент , их называли "батутами" батут и «трамплины» трамплины .
Они живут в младших 64к модулях.
Самое крутое, что эти «батуты» и «трамплины» — это фрагменты кода, лежащие в нижних 64 КБ пространства процесса.
Это означает, что этот ненадежный модуль может туда перескочить, потому что это действительный кодовый адрес, кратный 32 битам и в пределах 256 МБ.
Так что на этом «батуте» можно попрыгать.
Но время выполнения Родной клиент должен скопировать эти «батуты» откуда-то извне.
Таким образом, модуль Родной клиент не разрешили поддержку собственного кода батута, а код батута взят из доверенной среды выполнения доверенная среда выполнения .
В результате он фактически содержит все эти конфиденциальные инструкции, такие как перемещение Д.
С.
, К.
С.
и так далее, что недопустимо для самого ненадежного кода.
Итак, чтобы выйти из песочницы в доверенную среду выполнения, сделать что-то вроде Малоок или создать поток, нужно запрыгнуть на «батут», который «живет» по смещению в 32 байта.
Допустим, у него есть адрес 4096 + 32, и у него будут некоторые инструкции для переопределения этих селекторов сегментов.
Для этого он, например, выполнит операцию переместить %ds, 7 , то есть перенести запись в реестр дс , где 7 указывает адресное пространство в диапазоне от 0 до 2. 32 .
После эффективного перемещения К.
С.
вы сможете перейти в среду выполнения доверенная среда выполнения службы , и это будет после 256 МБ.
Так что этот прыжок разрешен не регулярно, но все будет хорошо, ведь здесь прыжок совершается именно в эту точку.
доверенная среда выполнения службы , который ожидает этих прыжков.
После этого будут проведены надлежащие проверки, чтобы убедиться в правильности аргументов и всего остального, что здесь происходит. И мы действительно можем переместить DS сюда, потому что знаем, что это действительно безопасно, и код, к которому мы собираемся перейти, не будет делать ничего произвольного или недопустимого с нашим ненадежным модулем.
Так зачем этим ребятам выпрыгивать из сегментов? Например, почему бы просто не поставить все эти вещи на «батут»? Возможно, это будет более трудоемко? Аудитория: У нас всего 64 тыс.
Профессор: да, именно так, ведь на самом деле у вас не так уж много места.
Потенциально этого могло бы хватить, чтобы разместить там Maloc, но проблема не только в этих 64к, но и в ограничении в 32 байта.
А для доверенного кода это не ограничение, поскольку доверенный код может делать здесь все, что захочет, и проверяться он не будет. Проблема в том, что ненадежный код может перейти к любому 32-байтовому смещению, поэтому каждое смещение должно иметь специальные аргументы.
Так что вам, вероятно, будет сложно писать этот код каждые 32 байта, потому что каждые 32 байта происходят проверки аргументов, значений и тому подобного.
Итак, вам придется спрыгнуть с батута и прыгнуть в доверенная среда выполнения в пределах 32 байт кода.
Вот как вы выпрыгиваете из песочницы.
Чтобы прыгнуть обратно в песочницу, нужно отменить эти преобразования, т.е.
вернуть обратно Д.
С.
, К.
С.
и так далее.
Сложность здесь в том, что если вы выходите за пределы лимита в 256 мегабайт, но работаете внутри доверенная среда выполнения , то вы не сможете сбросить эти регистры.
В противном случае вы сможете получить доступ к любой памяти во внешнем пространстве.
Для этого они используют второй инструмент под названием «трамплин», позволяющий прыгать с доверенная среда выполнения за пределами 256 МБ обратно в модуль Родной клиент .
«Трамплин» перезагружает реестр Д.
С.
, например, функция переместить %ds, 7 , сбрасывает остальные аргументы и переходит по адресу, который доверенная среда выполнения хочет вернуться к недоверенному модулю.
Это процедура возврата в песочницу.
Единственная сложность — не допустить прыжка ненадежного кода на сам трамплин, ведь после этого может произойти что-то странное.
Поэтому разработчики разместили инструкцию остановки остановка в первый байт 32-байтовой последовательности батута.
Так что если вы прыгнете к началу «трамплина», то сразу остановитесь.
В этом случае доверенная среда выполнения доверенная среда выполнения службы собирается пройти этот первый байт, перейти к 1 и вернуться назад.
Но эту операцию можно выполнить только доверенная среда выполнения службы , потому что она регулярно проверяет, чтобы больше никому этого не разрешили.
Аудитория: а сам "трамплин" находится в ненадёжном модуле? Профессор: «Трамплин» находится в диапазоне от 0 до 256 МБ ненадежного модуля.
Но на самом деле он живет в 64-битном чанке в самом начале модуля, то есть в той части, куда не может попасть ничего из скачанного вами с какого-то сайта «бинарника».
Его туда добавляет среда Родной клиент когда этот модуль загружается в память впервые.
Аудитория: почему бы просто не создать его во время выполнения? Профессор: да, почему бы нам не получить его во время выполнения? Что произойдет, если среде выполнения разрешить установку батута? Почему это плохо? Аудитория: Откуда же нам знать, куда возвращаться? Профессор: Я думаю, что на самом деле это прыжок к чему-то вроде %еакс , И доверенная среда выполнения говорит: «ой, я хочу вернуться по этому адресу»! Окружающая среда помещает его в реестр ЕАХ , переходит к оператору двигаться , и "трамплин" делает прыжок в любое место ЕАХ , который для него определила доверенная среда выполнения доверенная среда выполнения .
А что будет, если у модуля будет собственный «трамплин»? Аудитория: ну, вы можете сделать это как переход естественного типа, но ему не обязательно ничего знать о таблице дескрипторов.
Это оборудование.
Профессор: да, вообще-то это очень важная инструкция песочницы - то, что мы перезагружаем этот хэндл, чтобы указать на один из этих ограниченных хэндлов в пространстве от 0 до 2 32 .
Это действительно важно.
Потому что, если бы модулю было разрешено установить свой собственный «трамплин», он мог бы просто пропустить эту часть и не быть вынужденным вернуться к 256 МБ.
Таким образом, как только вы перепрыгивали трамплин, вы сразу получали доступ ко всему адресному пространству процесса.
Таким образом, «трамплин» является частью механизма принуждения, как бы устанавливающего границы.
Поэтому они не хотят, чтобы настройка трамплина выполнялась во время выполнения.
Аудитория: Можно ли установить "трамплин" за пределы 256 мегабайт? Профессор: Я думаю, они не хотят этого делать.
Это связано с тем, что вам необходимо установить код дескриптора сегмента.
К.
С.
в этот ограниченный сегмент и в то же время перейти к определенному адресу.
Проще это сделать через «трамплин», потому что сначала вы прыгаете на остановка , затем выполните перемещение, затем установите значение К.
С.
, но вы все равно можете выполнить тот же код, поскольку выполнение происходит в пределах 256 МБ.
Я думаю, что во многом это связано с атомарными примитивами, которые дает вам оборудование.
Итак, вы хотите установить целую кучу сегментов Д.
С.
, регистры селектора, регистр К.
С.
и при этом куда-то прыгнуть.
Вероятно, вы могли бы придумать некоторую последовательность инструкций, если бы попытались х86 , который мог бы сделать это за пределами адресного пространства модуля Родной клиент .
Итак, увидимся на следующей неделе и поговорим о веб-безопасности.
Доступна полная версия курса Здесь .
Спасибо, что остаетесь с нами.
Вам нравятся наши статьи? Хотите увидеть больше интересных материалов? Поддержите нас, разместив заказ или порекомендовав друзьям, Скидка 30% для пользователей Хабра на уникальный аналог серверов начального уровня, который мы придумали для вас: Вся правда о VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps от 20$ или как правильно расшарить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40 ГБ DDR4).
VPS (KVM) E5-2650 v4 (6 ядер) 10 ГБ DDR4 240 ГБ SSD 1 Гбит/с бесплатно до декабря при оплате на срок от шести месяцев и более вы можете заказать здесь .
Dell R730xd в 2 раза дешевле? Только здесь 2 x Intel Dodeca-Core Xeon E5-2650v4 128 ГБ DDR4 6x480 ГБ SSD 1 Гбит/с 100 ТВ от 249 долларов США в Нидерландах и США! Прочтите об этом Как построить корпоративную инфраструктуру класса, используя серверы Dell R730xd E5-2650 v4 стоимостью 9000 евро за копейки? Теги: #информационная безопасность #программирование #ит-инфраструктура #Анализ и проектирование систем #seccomp #Нативный клиент #Нативный клиент #Нативный клиент
-
Теории И Вакуум
19 Oct, 24 -
Интервью С Эдвардом Йордоном
19 Oct, 24 -
Что Такое Медиа-Кампании В Директе?
19 Oct, 24 -
И Они Посчитают Меня
19 Oct, 24