Фаззинг ядра Linux

Фаззинг инструмент icon

В сегодняшней статье я расскажу о способах фаззинга ядра Linux. Но сначала пару слов о том, как я докатился до этого. Пос­ледние несколько лет я с помощью фаз­зинга занимаюсь поиском уяз­вимос­тей в ядре Linux. За 5 лет я работал над тремя про­ектами: фаз­зил сетевую под­систе­му со сто­роны сис­темных вызовов (и написал нес­коль­ко экс­пло­итов для най­ден­ных багов), после чего фаз­зил ту же сеть с внеш­него периметра и, в конце, фаз­зил под­систе­му USB со сто­роны устрой­ств. За эти несколько лет работы над фаз­зингом ядра у меня собралась большая кол­лекция полезных ссы­лок и нарабо­ток. Я их всех упо­рядо­чил и готов с вами поделиться в этой статье.

Для тех кто не в теме фаззинга, рекомендую для начала прочитать статью «Что такое Фаззинг и как искать уязвимости в программах».

Фаззинг ядра Linux

Идея пер­вая: для генера­ции вво­дов исполь­зовать под­ход coverage-guided — на осно­ве сбор­ки пок­рытия кода.

Как он работа­ет? Помимо генери­рова­ния слу­чай­ных вво­дов с нуля, мы под­держи­ваем набор ранее сге­нери­рован­ных «инте­рес­ных» вво­дов — кор­пус. И иног­да, вмес­то слу­чай­ного вво­да, мы берем один ввод из кор­пуса и его слег­ка модифи­циру­ем.

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

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

Фаззинг ядра Linux
Фаззинг ядра Linux

Этот под­ход исполь­зует­ся в двух основных инс­тру­мен­тах для фаз­зинга при­ложе­ний в юзер­спей­се: AFL и libFuzzer.

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

Для coverage-guided-фаз­зинга ядра нам нужен спо­соб собирать информа­цию о пок­рытии кода. Для этой цели был раз­работан инс­тру­мент KCOV. Он тре­бует дос­тупа к исходни­кам, но для ядра у нас они есть.

Что­бы вклю­чить KCOV, нуж­но пересоб­рать ядро с вклю­чен­ной опци­ей CONFIG_KCOV, пос­ле чего пок­рытие кода ядра мож­но собирать через /sys/kernel/debug/kcov.

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

Ловим баги

Те­перь при­дума­ем что‑нибудь получ­ше для обна­руже­ния багов, чем выпаде­ние в kernel panic.

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

Для решения этих проб­лем при­дума­ли динами­чес­кие детек­торы багов. Сло­во «динами­чес­кие» озна­чает, что они работа­ют в про­цес­се исполне­ния прог­раммы. Они ана­лизи­руют ее дей­ствия в соот­ветс­твии со сво­им алго­рит­мом и пыта­ются пой­мать момент, ког­да про­изош­ло что‑то пло­хое.

Для ядра таких детек­торов нес­коль­ко. Самый кру­той из них — KASAN. Крут он не потому, что я над ним работал, а потому, что он находит глав­ные типы пов­режде­ний памяти: выходы за гра­ницы мас­сива и use-after-free. Для его исполь­зования дос­таточ­но вклю­чить опцию CONFIG_KASAN, и KASAN будет работать в фоне, записы­вая репор­ты об ошиб­ках в лог ядра при обна­руже­нии.

Боль­ше о динами­чес­ких детек­торах для ядра мож­но узнать из док­лада Mentorship Session: Dynamic Program Analysis for Fun and Profit Дмит­рия Вьюко­ва (слай­ды).

Автоматизация

Что каса­ется авто­мати­зации, то тут мож­но при­думать мно­го все­го инте­рес­ного. Авто­мати­чес­ки мож­но:

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

Как это все сде­лать? Написать код и вклю­чить его в наш фаз­зер. Исклю­читель­но инже­нер­ная задача.

Все вместе

Возь­мем эти три идеи — coverage-guided-под­ход, исполь­зование динами­чес­ких детек­торов и авто­мати­зацию про­цес­са фаз­зинга — и вклю­чим в наш фаз­зер. У нас получит­ся сле­дующая кар­тина.

Как запус­кать ядро? В QEMU или на реаль­ном железе
Что будет вход­ными дан­ными? Сис­темные вызовы
Как вход­ные дан­ные переда­вать ядру? Че­рез запуск исполня­емо­го фай­ла
Как генери­ровать вво­ды? Зна­ние API + KCOV
Как опре­делять наличие багов? KASAN и дру­гие детек­торы
Как авто­мати­зиро­вать? Все перечис­ленные выше шту­ки

Ес­ли опять‑таки спро­сить зна­юще­го челове­ка, какой фаз­зер ядра исполь­зует эти под­ходы, вам сра­зу отве­тят: syzkaller. Сей­час syzkaller — это передо­вой фаз­зер ядра Linux. Он нашел ты­сячи оши­бок, вклю­чая экс­плу­ати­руемые уяз­вимос­ти. Прак­тичес­ки любой, кто занимал­ся фаз­зингом ядра, имел дело с этим фаз­зером.

Иног­да мож­но услы­шать, что KASAN явля­ется неот­делимой частью syzkaller. Это не так. KASAN мож­но исполь­зовать и с Trinity, а syzkaller — и без KASAN.

Другой способ фаззинга ядра Linux

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

Вытаскиваем код в юзерспейс

Мы обсужда­ли два вари­анта, как запус­тить ядро для фаз­зинга: исполь­зовать вир­туал­ки или желез­ки. Но есть еще один спо­соб: мож­но вытащить код ядра в юзер­спейс. Для это­го нуж­но взять какую‑нибудь изо­лиро­ван­ную под­систе­му и ском­пилиро­вать ее как биб­лиоте­ку. Тог­да ее мож­но будет пофаз­зить с помощью инс­тру­мен­тов для фаз­зинга обыч­ных при­ложе­ний.

Для некото­рых под­систем это сде­лать нес­ложно. Если под­систе­ма прос­то выделя­ет память с помощью kmalloc и осво­бож­дает ее через kfree и на этом при­вяз­ка к ядер­ным фун­кци­ям закан­чива­ется, тог­да мы можем заменить kmalloc на malloc и kfree на free. Даль­ше мы ком­пилиру­ем код как биб­лиоте­ку и фаз­зим с помощью того же libFuzzer.

Для боль­шинс­тва под­систем с этим под­ходом воз­никнут слож­ности. Тре­буемая под­систе­ма может исполь­зовать API, которые в юзер­спей­се поп­росту недос­тупны. Нап­ример, RCU.

RCU (Read-Copy-Update) — механизм син­хро­низа­ции в ядре Linux.

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

Этот под­ход исполь­зовал­ся для фаз­зинга eBPF, ASN.1-пар­серов и се­тевой под­систе­мы ядра XNU.

Фаззинг внешних внешних интерфейсов

Дан­ные из юзер­спей­са в ядро могут переда­вать­ся через сис­колы; о них мы уже говори­ли. Но пос­коль­ку ядро — это прос­лой­ка меж­ду железом и прог­рамма­ми поль­зовате­ля, у него есть так­же вхо­ды и со сто­роны устрой­ств.

Фаззинг Linux
Фаззинг Linux

Дру­гими сло­вами, ядро обра­баты­вает дан­ные, при­ходя­щие через Ethernet, USB, Bluetooth, NFC, мобиль­ные сети и про­чие железяч­ные про­токо­лы.

Нап­ример, мы пос­лали на сис­тему TCP-пакет. Ядро дол­жно его рас­парсить, что­бы понять, на какой порт он при­шел и какому при­ложе­нию его дос­тавить. Отправ­ляя слу­чай­но сге­нери­рован­ные TCP-пакеты, мы можем фаз­зить сетевую под­систе­му с внеш­ней сто­роны.

Воз­ника­ет воп­рос: как дос­тавлять в ядро дан­ные со сто­роны внеш­них интерфей­сов? Сис­колы мы прос­то зва­ли из бинар­ника, а если мы хотим общать­ся с ядром по USB, то такой под­ход не прой­дет.

Дос­тавлять дан­ные мож­но через реаль­ное железо: нап­ример, отправ­лять сетевые пакеты по сетево­му кабелю или исполь­зовать Facedancer для USB. Но такой под­ход пло­хо мас­шта­биру­ется: хочет­ся иметь воз­можность фаз­зить внут­ри вир­туал­ки.

Здесь есть два решения.

Пер­вое — это написать свой драй­вер, который вот­кнет­ся в нуж­ное мес­то внут­ри ядра и дос­тавит туда наши дан­ные. А самому драй­веру дан­ные мы будем переда­вать через сис­колы. Для некото­рых интерфей­сов такие драй­веры уже есть в ядре.

Нап­ример, сеть я фаз­зил через TUN/TAP. Этот интерфейс поз­воля­ет отправ­лять в ядро сетевые пакеты так, что пакет про­ходит через те же самые пути пар­синга, как если бы он при­шел извне. В свою оче­редь, для фаз­зинга USB мне приш­лось написать свой драй­вер.

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

Та­кой под­ход при­меня­ется в фаз­зере vUSBf. В нем исполь­зовали QEMU и про­токол usbredir, который поз­воля­ет с хос­та под­клю­чать USB-устрой­ства внутрь вир­туал­ки.

За пределами API-aware-фаззинга

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

При­мер: clone и  sigaction. Да, они тоже при­нима­ют аргу­мен­ты, тоже могут вер­нуть резуль­тат, но при этом они порож­дают еще один поток исполне­ния. clone соз­дает новый про­цесс, а sigaction поз­воля­ет нас­тро­ить обра­бот­чик сиг­нала, которо­му передас­тся управле­ние, ког­да этот сиг­нал при­дет.

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

О сложных подсистемах

Есть еще под­систе­мы eBPF и KVM. В качес­тве вво­дов вмес­то прос­тых струк­тур они при­нима­ют пос­ледова­тель­ность исполня­емых инс­трук­ций. Сге­нери­ровать кор­рек­тную цепоч­ку инс­трук­ций — это гораз­до более слож­ная задача, чем сге­нери­ровать кор­рек­тную струк­туру. Для фаз­зинга таких под­систем нуж­но раз­рабаты­вать спе­циаль­ные фаз­зеры. Нав­роде фаз­зера JavaScript-интер­пре­тато­ров fuzzilli.

Структурирование внешних вводов

Пред­ста­вим, что мы фаз­зим ядро Linux со сто­роны сети. Может показать­ся, что фаз­зинг сетевых пакетов — это та же генера­ция и отправ­ка обыч­ных струк­тур. Но на самом деле сеть работа­ет как API, толь­ко с внеш­ней сто­роны.

При­мер: пусть мы фаз­зим TCP и у нас на хос­те есть сокет, с которым мы хотим уста­новить соеди­нение извне. Казалось бы, мы посыла­ем SYN, хост отве­чает SYN/ACK, мы посыла­ем ACK — все, соеди­нение уста­нов­лено. Но в получен­ном нами пакете SYN/ACK содер­жится но­мер под­твержде­ния, который мы дол­жны вста­вить в пакет ACK. В каком‑то смыс­ле это воз­врат зна­чения из ядра, но с внеш­ней сто­роны.

То есть внеш­нее вза­имо­дей­ствие с сетью — это пос­ледова­тель­ность вызовов (отпра­вок пакетов) и исполь­зование их воз­вра­щаемых зна­чений (номеров под­твержде­ния) в сле­дующих вызовах. Получа­ем, что сеть работа­ет как API и для нее при­мени­мы идеи API-aware-фаз­зинга.

Про USB

USB — необыч­ный про­токол: там все обще­ние ини­циирует­ся хос­том. Поэто­му даже если мы наш­ли спо­соб под­клю­чать USB-устрой­ства извне, то мы не можем прос­то так посылать дан­ные на хост. Вмес­то это­го нуж­но дож­дать­ся зап­роса от хос­та и на этот зап­рос отве­тить. При этом мы не всег­да зна­ем, какой зап­рос при­дет сле­дующим. Фаз­зер USB дол­жен учи­тывать эту осо­бен­ность.

Помимо KCOV

Как еще мож­но собирать пок­рытие кода, кро­ме как с помощью KCOV?

Во‑пер­вых, мож­но исполь­зовать эму­лято­ры. Пред­ставьте, что вир­туал­ка эму­лиру­ет ядро инс­трук­ция за инс­трук­цией. Мы можем внед­рить­ся в цикл эму­ляции и собирать отту­да адре­са инс­трук­ций. Этот под­ход хорош тем, что, в отли­чие от KCOV, тут не нуж­ны исходни­ки ядра. Как следс­твие, этот спо­соб мож­но исполь­зовать для зак­рытых модулей, которые дос­тупны в виде бинар­ников. Так дела­ют фаз­зеры TriforceAFL и UnicoreFuzz.

Еще один спо­соб собирать пок­рытие — исполь­зовать аппа­рат­ные фичи про­цес­сора. Нап­ример, kAFL исполь­зует Intel PT.

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

Сборка релевантного покрытия

Для coverage-guided-фаз­зинга нам нуж­но собирать пок­рытие с кода под­систе­мы, которую мы фаз­зим.

Сбор­ка пок­рытия из текуще­го потока, которую мы обсужда­ли до сих пор, работа­ет для этой цели не всег­да: под­систе­ма может обра­баты­вать вво­ды в дру­гих кон­тек­стах. Нап­ример, некото­рые сис­колы соз­дают новый поток в ядре и обра­баты­вают ввод там. В слу­чае того же USB пакеты обра­баты­вают­ся в гло­баль­ных потоках, которые стар­туют при заг­рузке ядра и никак к юзер­спей­су не при­вяза­ны.

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

За пределами сбора покрытия кода

Нап­равлять про­цесс фаз­зинга мож­но не толь­ко с помощью пок­рытия кода.

Нап­ример, мож­но отсле­живать сос­тояние ядра: монито­рить учас­тки памяти или сле­дить за изме­нени­ем сос­тояний внут­ренних объ­ектов. И добав­лять в кор­пус вво­ды, которые вво­дят объ­екты в ядре в новые сос­тояния.

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

Сборка корпуса вводов

Еще один спо­соб генера­ции вво­дов — сде­лать это на осно­ве дей­ствий реаль­ных прог­рамм. Реаль­ные прог­раммы уже вза­имо­дей­ству­ют с ядром нет­риви­аль­ным обра­зом и про­ника­ют глу­боко внутрь кода. Сге­нери­ровать такое же вза­имо­дей­ствие с нуля может быть невоз­можно даже для очень умно­го фаз­зера.

Я видел такой под­ход в про­екте Moonshine: авто­ры запус­кали сис­темные ути­литы под strace, собира­ли с них лог и исполь­зовали получен­ную пос­ледова­тель­ность сис­колов как ввод для фаз­зинга с помощью syzkaller.

Ловим больше багов

Су­щес­тву­ющие динами­чес­кие детек­торы неидеаль­ны и могут не замечать некото­рые ошиб­ки. Как находить такие ошиб­ки? Улуч­шать детек­торы.

Мож­но, к при­меру, взять KASAN (напом­ню, он ищет пов­режде­ния памяти) и до­бавить анно­тации для какого‑нибудь нового алло­като­ра. По умол­чанию KASAN под­держи­вает стан­дар­тные алло­като­ры ядра, такие как slab и page_alloc. Но некото­рые драй­веры выделя­ют здо­ровен­ный кусок памяти и потом самос­тоятель­но его нареза­ют на бло­ки помель­че (при­вет, Android!). KASAN в таком слу­чае не смо­жет най­ти перепол­нение из одно­го бло­ка в дру­гой. Нуж­но добав­лять анно­тации вруч­ную.

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

Мож­но делать свои баг‑детек­торы с нуля. Самый прос­той спо­соб — добавить в исходни­ки ядра ассерты. Если мы зна­ем, что в опре­делен­ном мес­те всег­да дол­жно выпол­нять­ся опре­делен­ное усло­вие, — добав­ляем BUG_ON и начина­ем фаз­зить. Если BUG_ON сра­ботал — баг най­ден. А мы сде­лали эле­мен­тарный детек­тор логичес­кой ошиб­ки. Такие детек­торы осо­бен­но инте­рес­ны в кон­тек­сте фаз­зинга BPF, потому что ошиб­ка в BPF обыч­но не при­водит к пов­режде­нию памяти и оста­ется незаме­чен­ной.

Итого

Да­вайте под­ведем ито­ги.

Гло­баль­но под­ходов к фаз­зингу ядра Linux три:

  1. Ис­поль­зовать юзер­спей­сный фаз­зер. Либо берете фаз­зер типа AFL или libFuzzer и его переде­лыва­ете, что­бы он звал сис­колы вмес­то фун­кций юзер­спей­сной прог­раммы. Либо вытас­кива­ете ядер­ный код в юзер­спейс и фаз­зите его там. Эти спо­собы прек­расно работа­ют для под­систем, обра­баты­вающих струк­туры, потому что в основном юзер­спей­сные фаз­зеры ори­енти­рова­ны на мутацию мас­сива бай­тов. При­меры: фаз­зинг фай­ловых сис­тем и Netlink. Для coverage-guided-фаз­зинга вам при­дет­ся под­клю­чить сбор­ку пок­рытия с ядра к алго­рит­му фаз­зера.
  2. Ис­поль­зовать syzkaller. Он иде­аль­но под­ходит для API-aware-фаз­зинга. Для опи­сания сис­колов и их воз­вра­щаемых зна­чений и аргу­мен­тов он исполь­зует спе­циаль­ный язык — syzlang.
  3. На­писать свой фаз­зер с нуля. Это отличный спо­соб ра­зоб­рать­ся, как работа­ет фаз­зинг изнутри. А еще с помощью это­го под­хода мож­но фаз­зить под­систе­мы с не­обыч­ными ин­терфей­сами.

Советы по syzkaller

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

  • Не исполь­зуйте syzkaller на стан­дар­тном ядре со стан­дар­тным кон­фигом — ничего не най­дете. Мно­го людей фаз­зят ядро руками и с помощью syzkaller. Кро­ме того, есть syzbot, который фаз­зит ядро в обла­ке. Луч­ше сде­лайте что‑нибудь новое: напишите новые опи­сания сис­колов или возь­мите нес­тандар­тный кон­фиг ядра.
  • Syzkaller мож­но улуч­шать и рас­ширять. Ког­да я делал фаз­зинг USB, я сде­лал его поверх syzkaller, написав допол­нитель­ный модуль.
  • Syzkaller мож­но исполь­зовать как фрей­мворк. Нап­ример, взять часть кода для пар­синга лога ядра. Syzkaller уме­ет рас­позна­вать сот­ню раз­ных типов оши­бок, и эту часть мож­но пере­исполь­зовать в сво­ем фаз­зере. Или мож­но взять код, который управля­ет вир­туаль­ными машина­ми, что­бы не писать его самому.

Как понять, что ваш фаз­зер работа­ет хорошо? Оче­вид­но, что если он находит новые баги, то все отлично. Но вот что делать, если не находит?

Про­веряйте пок­рытие кода. Фаз­зите кон­крет­ную под­систе­му? Про­верьте, что ваш фаз­зер дотяги­вает­ся до всех ее инте­рес­ных час­тей.
До­бавьте искусс­твен­ные баги в под­систе­му, которую фаз­зите. Нап­ример, добавьте ассертов и про­верьте, что фаз­зер до них дотяги­вает­ся. Этот совет отчасти пов­торя­ет пре­дыду­щий, но он работа­ет, даже если ваш фаз­зер не собира­ет пок­рытие кода.

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

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

И еще пара советов:

Пи­шите фаз­зер на осно­ве кода, а не докумен­тации. Докумен­тация может быть неточ­на. Источни­ком исти­ны всег­да будет код. Я на это натол­кнул­ся, ког­да делал фаз­зер USB: ядро обра­баты­вало дру­гое под­мно­жес­тво про­токо­лов, чем опи­сан­ное в докумен­тации.

В пер­вую оче­редь делайте фаз­зер умным, а уже потом делайте его быс­трым. «Умный» озна­чает генери­ровать более точ­ные вво­ды, луч­ше собирать пок­рытие или что‑нибудь еще в таком роде, а «быс­трый» — иметь боль­ше исполне­ний в секун­ду.

Выводы

Соз­дание фаз­зеров — инже­нер­ная работа. И осно­вана она на инже­нер­ных уме­ниях: про­екти­рова­нии, прог­рамми­рова­нии, тес­тирова­нии, дебаг­гинге и бен­чмар­кинге.

От­сюда два вывода. Пер­вый: что­бы написать прос­той фаз­зер — дос­таточ­но прос­то уметь прог­рамми­ровать. Вто­рой: что­бы написать кру­той фаз­зер — нуж­но быть хорошим инже­нером. При­чина, по которой syzkaller име­ет такой успех, — в него было вло­жено мно­го инже­нер­ного опы­та и вре­мени.

На­деюсь, я ско­ро уви­жу новый необыч­ный фаз­зер, который напишите имен­но вы!

Еще по теме: Как взломать программу в формате MSI

Дима (Kozhuh)

Эксперт в кибербезопасности. Работал в ведущих компаниях занимающихся аналитикой компьютерных угроз. Анонсы новых статей в Телеграме.

Добавить комментарий