• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Реверс-инжиниринг для всех

yashechka

Генератор контента.Фанат Ильфака и Рикардо Нарвахи
Эксперт
Регистрация
24.11.2012
Сообщения
2 344
Реакции
3 563
Подождите, что такое реверс-инжиниринг?

Википедия определяет его как:

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

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

Screenshot_56.png


Этот исчерпывающий туториал по реверс-инжинирингу охватывает архитектуры x86, x64, а также 32-разрядные ARM и 64-разрядные архитектуры.
Если вы новичок, желающий научиться реверсить, или вы просто кто-то кто хочет пересмотреть некоторые концепции, вы попали в нужное место.
Как новичок, эти туториалы пронесут вас от нуля к основам реверс-инжиниринга - навыку, которым должен владеть каждый в сфере кибербезопасности.
Если вы здесь, чтобы освежить некоторые концепции, вы можете удобно использовать боковую панель, чтобы просмотреть разделы.

Вы можете получить весь туториал в формате PDF или MOBI. Все эти версии электронных книг будут обновляться автоматически по мере добавления новых туториалов.

Часть 1: Задачи

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

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

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

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

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

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

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

В следующем уроке мы обсудим методы анализа вредоносных программ.
 
Часть 2: Методы

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

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

В динамическом анализе используются дизассемблеры и отладчики для анализа двоичных файлов вредоносных программ. Самый популярный инструмент на рынке сегодня называется IDA, который представляет собой многоплатформенный, многопроцессорный дизассемблер и отладчик. Сегодня на рынке есть и другие инструменты дизассемблера/отладчика, такие как Hopper Disassembler, OllyDbg и многие другие.

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

Реверс инжиниринг - это гораздо больше, чем просто анализ вредоносных программ. В конце нашей серии в нашем учебном пособии будет использоваться IDA, поскольку мы создадим реальный сценарий, в котором генеральный директор ABC Biochemicals попросит вас тайно попытаться этически взломать программное обеспечение его компании, которое управляет пуленепробиваемой дверью в очень чувствительной биохимической лаборатории, чтобы проверить, насколько хорошо программное обеспечение работает против реальных угроз. Проект будет очень простым, однако он в конечном итоге продемонстрирует мощь языка ассемблера и то, как его можно использовать для реверс инжиниринга и, в конечном итоге, предоставить решения о том, как лучше спроектировать код, чтобы сделать его более безопасным.

В следующем уроке мы обсудим различные типы вредоносных программ.
 
Часть 3: Типы вредоносных программ

Чтобы просмотреть полное содержание всех уроков, нажмите ниже ссылку, так как в ней дается краткое описание каждого урока в дополнение к темам, которые он охватывает. https://github.com/mytechnotalent/Reverse-Engineering-Tutorial

Вредоносное ПО делится на несколько категорий, о которых я кратко коснусь ниже.

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

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

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

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

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

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

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

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

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

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

В следующем уроке мы начнем наше долгое путешествие в язык ассемблера x86. Чтобы по-настоящему понять самые основы реверс инжиниринга и вредоносных программ, нам нужно в течение следующих нескольких месяцев глубоко погрузиться в ядро и построить свой путь.
 
Часть 4: Введение в ассемблер x86

Дамы и господа, мальчики и девочки, дети всех возрастов! Мы собираемся отправиться в путешествие, которое навсегда изменит вашу жизнь!

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

Первый вопрос, на который мы должны ответить, - что такое язык ассемблера x86, ответ на который представляет собой семейство обратно совместимых языков ассемблера, обеспечивающих совместимость с микропроцессорами серии Intel 8000. Языки ассемблера x86 используются для создания объектного кода для вышеупомянутой серии процессоров. Он использует мнемонику для представления инструкций, которые может выполнять ЦП.

Язык ассемблера микропроцессора x86 работает совместно с различными операционными системами. Мы сосредоточимся на языке ассемблера Linux, использующем синтаксис Intel, в дополнение к изучению того, как программировать на C, на котором мы будем дизассемблировать исходный код и анализировать соответствующую сборку.

Язык ассемблера x86 имеет два варианта синтаксиса. Синтаксис AT&T доминировал в мире Unix, так как ОС была разработана в AT&T Bell Labs. Напротив, синтаксис Intel изначально использовался для документации платформы x86 и преобладал в средах MS-DOS и Windows.

Для наших целей, когда мы в конечном итоге дизассемблируем или отлаживаем программное обеспечение, будь то в среде Linux или Windows, мы в значительной степени увидим синтаксис Intel. Это важно независимо от того, изучаем ли мы двоичный файл Windows в формате PE или двоичный файл Linux в формате ELF. Подробнее об этом позже в этом руководстве.

Основное различие между ними заключается в синтаксисе AT&T: источник находится перед пунктом назначения, а в синтаксисе Intel назначение идет перед источником. Мы обсудим это более подробно позже в уроке.

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

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

Linux также имеет открытый исходный код и существует множество версий. Мы сосредоточимся на Ubuntu в наших демонстрациях, которую можно получить бесплатно. В отличие от этого, операционная система Windows принадлежит и контролируется одной компанией, Microsoft, которой все обновления, исправления безопасности и служебные исправления поступают непосредственно от нее, а в Linux миллионы профессионалов предоставляют то же самое абсолютно бесплатно!

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

В следующем уроке мы обсудим двоичную систему счисления. Возьмите чашку кофе, которая вам понадобится!
 
Часть 5: двоичная система счисления

Двоичные числа - это то, что определяет ядро компьютера. Бит в компьютере либо включен, либо выключен. К биту либо подведено электричество, либо его нет. Мы углубимся в это в будущих уроках.

Озадаченные и сбитые с толку, что нам делать дальше?

Не бойтесь! Здесь есть двоичная система счисления! Важно понимать, что в двоичном формате каждый столбец имеет значение, в два раза превышающее значение столбца справа от него, и в основе только две цифры, которые равны 0 и 1.

В десятичной системе счисления с основанием 10, скажем, у нас есть число 15, которое означает (1 x 10) + (5 x 1) = 15, поэтому 5 - это число, умноженное на 1, а 1 - это число, умноженное на 10.

Бинарные файлы работают аналогичным образом, но теперь мы говорим о основании 2. Это же число в двоичном формате равно - 1111. Проиллюстрируем:

1520526607781.jpg


Двоичные числа важны, потому что их использование вместо десятичной системы упрощает проектирование компьютеров и связанных с ними технологий. Самое простое определение двоичной системы счисления - это система нумерации, в которой используются только две цифры, как мы упоминали выше, для представления чисел, необходимых для компьютерной архитектуры, а не с использованием цифр от 1 до 9 плюс 0 для их представления.

В следующем уроке мы обсудим шестнадцатеричную систему счисления. Отсюда становится только интереснее!
 
Часть 6: Шестнадцатеричная система счисления

Теперь, когда мы мастера двоичного кода, пришло время заняться системой нумерации систем счисления!

Мы узнали в двоичном формате, что каждое число представляет собой бит. Если объединить 8 бит, мы получим байт. Далее байт может быть разделен на 4 старших бита и 4 младших бита. Комбинация 4-х битов - полубайт. Поскольку 4 бита дают вам возможный диапазон от 0 до 15, с системой счисления с основанием 16 легче работать. Имейте в виду, когда мы говорим, что основание 16, мы начинаем с 0, и поэтому 0-15 - это 16 различных чисел.

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

Шестнадцатеричная система похожа на любую другую систему счисления, за исключением шестнадцатеричной, каждый столбец имеет значение, в 16 раз превышающее значение столбца справа от него. Самое интересное в шестнадцатеричном формате заключается в том, что у нас есть не только 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, но и A, B, C, D, E и F и, следовательно, 16 различных символов.

Давайте посмотрим на простую таблицу, чтобы увидеть, как шестнадцатеричные числа сравниваются с десятичными.
1520241886257.jpg



Хорошо, я вижу, как дым выходит из ушей, но все в порядке! В десятичной системе все учитывается в степени 10.

Возьмем число 42 и рассмотрим его в десятичном виде:

2 х 10 ^ 0 = 2

4 х 10 ^ 1 = 40

Помните, что 10 в степени 0 равно 1, а 10 в степени 1 равно 10, поэтому 2 + 40 = 42.

Возьмите кофе, вот и самое интересное!

Если мы понимаем, что десятичная дробь - это система счисления с основанием 10, мы можем создать простую формулу, где b представляет собой основание. В этом случае b = 10.

(2 * b ^ 0) + (4 * b ^ 1)

(2 * 10 ^ 0) + (4 * 10 ^ 1) = 42

В двоичном формате число 42 в десятичной системе является двоичным 0010 1010 следующим образом:

0 х 2 ^ 0 = 0

1 х 2 ^ 1 = 2

0 х 2 ^ 2 = 0

1 х 2 ^ 3 = 8

0 х 2 ^ 4 = 0

1 х 2 ^ 5 = 32

0 х 2 ^ 6 = 0

0 х 2 ^ 7 = 0

0 + 2 + 0 + 8 + 0 + 32 + 0 + 0 = 42

В шестнадцатеричной системе все рассматривается в степени 16. Следовательно, 42 в десятичной системе счисления равно 2А в шестнадцатеричной системе счисления:

10 * 16 ^ 0 = 10

2 * 16 ^ 1 = 32

10 + 32 = 42 десятичное => 2 шестнадцатеричное

Это то же самое, что сказать:

10 * 1 = 10

2 * 16 = 32

10 + 32 = 42 десятичное => 2 шестнадцатеричное

Имейте в виду, что десятичное число 10 равно шестнадцатеричному числу A, а десятичное число 2 равно 2 шестнадцатеричному числу. В нашей формуле выше, когда мы имеем дело с A, B, C, D, E или F, нам нужно преобразовать их в их десятичный эквивалент.

Возьмем еще один пример в шестнадцатеричной системе F5.

5 х 16 ^ 0 = 5

15 х 16 ^ 1 = 240

5 + 240 = 245 в десятичной системе => F5 в шестнадцатеричной системе

Давайте посмотрим на таблицу из двоичного в шестнадцатеричный:

1520145784508.jpg


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

Давайте посмотрим на это с другой стороны. Давайте поработаем еще с шестнадцатеричными числами и преобразуем их в десятичные:

1520236890634.jpg


Давайте сделаем преобразование F1CD :

D --- 13 х 1 = 13

C --- 12 х 16 = 192

1 --- 1 х 256 = 256

F --- 15 x 4096 = 61 440

13 + 192 + 256 + 61 440 = 61 901

Сложение в шестнадцатеричном формате работает следующим образом. С этого момента все числа в шестнадцатеричном формате будут иметь символ "h" рядом с числом:

1520242000178.jpg


Другой пример:

1520243446596.jpg


Последний пример сложения выглядит так:

*
123.jpg


Теперь мы сосредоточимся на вычитании:

1234.jpg


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

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

В следующем уроке мы обсудим свитчи, транзисторы и память.
 
Часть 7: Транзисторы и память

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

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

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

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

Давайте вернемся к основам компьютеров, так что поехали!

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

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

Давайте возьмем пример с 3 контактами. Когда на контакт 1 подается электрическое напряжение, ток течет между контактами 2 и 3. Когда напряжение снимается с первого контакта, ток перестает течь между контактами 2 и 3.

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

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

В следующем уроке мы обсудим байты и слова.
 
Часть 8 - Байты, слова, двойные слова и т. Д.

Память измеряется в байтах. Байт равен 8 битам. Два байта называются словом, а два слова называются двойным словом, которое составляет четыре байта (32 бита), а учетверенное слово - восемь байтов (64 бита).

Байт составляет 8 бит и имеет степень 2 ^ 8, что составляет 256. Число двоичных чисел размером 8 бит - одно из 256 значений, начиная с 0 и заканчивая 255.

Каждый байт памяти компьютера имеет свой уникальный адрес. Давайте рассмотрим дизассемблированные инструкции для простого приложения hello world в Linux, установив точку останова на функцию main. Мы будем использовать отладчик GDB:

1.jpg


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

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

2.jpg


Мы видим ячейку памяти 0xffffd040, которая, конечно, находится в шестнадцатеричном формате. Мы также видим значение в регистре ESP - 0xf7fac3dc, которое также находится в шестнадцатеричном формате.

Важно понимать, что 0xffffd040 - это 4 байта и двойное слово. Как мы узнали в Части 6: каждая шестнадцатеричная цифра имеет длину 4 бита, иначе называемую полубайтом. В 0xffffd040 давайте посмотрим на самую правую цифру 0. В этом примере 0 (шестнадцатеричный) имеет длину 4 бита. Если мы посмотрим на 40 (в шестнадцатеричном формате), мы увидим, что это длина байта или 8 бит. Если мы посмотрим на d040, у нас есть два байта или слово. Наконец, ffffd040 - это двойное слово длиной 4 байта, что составляет 32 бита. 0x в начале адреса просто означает, что это шестнадцатеричное значение.

Компьютерная программа - это не что иное, как машинные инструкции, хранящиеся в памяти. 32-битный ЦП извлекает двойное слово из адреса памяти. Двойное слово - это 4 байта подряд, которые считываются из памяти и загружаются в ЦП. Как только ЦП завершает выполнение инструкции, ЦП извлекает следующую машинную инструкцию в памяти из указателя инструкции.

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

В нашем следующем руководстве мы обсудим основы архитектуры x86.
 
Часть 9: базовая архитектура x86

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

Базовая архитектура состоит из ЦП, памяти и устройств ввода/вывода, которые являются устройствами ввода/вывода, которые все соединены системной шиной, как подробно описано ниже.

3.jpg


ЦП состоит из 4 частей:

1) Блок управления - извлекает и декодирует инструкции из ЦП, а затем сохраняет и извлекает их из памяти.

2) Блок выполнения - где происходит выполнение инструкций выборки и извлечения.

3) Регистры - ячейки внутренней памяти ЦП которые используются для временного хранилища данных.

4) Флаги - указывают события, когда происходит выполнение.

4.jpg


Мы обсудим 32-битный x86, поэтому 32-битный процессор сначала выбирает двойное слово (4 байта или 32 бита) из определенного адреса в памяти, а затем считывает его из памяти и загружает в ЦП. В этот момент ЦП смотрит на двоичную комбинацию битов в двойном слове и начинает выполнение процедуры, которую ему указывает полученная машинная инструкция.

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

Мы сразу видим, что если мы контролируем поток EIP, мы можем изменить программу, чтобы она выполняла то, для чего она НЕ предназначалась. Это популярный метод работы вредоносных программ.

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

В нашем следующем руководстве мы углубимся в архитектуру IA-32 с обсуждением регистров общего назначения.
 
Часть 10: Регистры общего назначения

Регистры общего назначения используются для временного хранения данных по мере их обработки процессором. Регистры со временем претерпели значительные изменения и продолжают расти. Для наших целей мы сосредоточимся на 32-битной архитектуре x86.

Каждая новая версия регистров общего назначения создается для обеспечения обратной совместимости с предыдущими процессорами. Это означает, что код, использующий 8-битные регистры на чипах 8080, будет по-прежнему работать на сегодняшнем 64-битном чипсете.

Регистры общего назначения могут использоваться для хранения данных любого типа, некоторые из которых получили специальное использование в программах. Давайте рассмотрим 8 регистров общего назначения в архитектуре IA-32.

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

EBX: Базовый регистр. Указатель на данные в сегменте DS. Используется для хранения базового адреса программы.

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

EDX: регистр общего назначения. Дополнительно используется для операций ввода-вывода. Вдобавок расширяет EAX до 64-бит.

ESI: регистр источника. Указатель на данные в сегменте, на который указывает регистр DS. Используется как адрес смещения в операциях со строками и массивами. Он содержит адрес, откуда читать данные.

EDI: регистр назначения. Указатель на данные (или место назначения) в сегменте, на который указывает регистр ES. Используется как адрес смещения в операциях со строками и массивами. Он содержит подразумеваемый адрес записи для всех строковых операций.

EBP: базовый указатель. Указатель на данные в стеке (в сегменте SS). Он указывает на нижнюю часть текущего кадра стека. Он используется для ссылки на локальные переменные.

ESP: указатель стека (в сегменте SS). Он указывает на верхнюю часть текущего кадра стека. Он используется для ссылки на локальные переменные.

Имейте в виду, что каждый из вышеуказанных регистров имеет длину 32 бита или 4 байта. На каждый из двух младших байтов регистров EAX, EBX, ECX и EDX можно ссылаться через AX, а затем делить их по именам AH, BH, CH и DH для старших байтов и AL, BL, CL и DL для младших байтов, которые имеют размер 1 байт каждый.

Кроме того, на ESI, EDI, EBP и ESP можно ссылаться по их 16-битному эквиваленту, то есть SI, DI, BP, SP.

Это может немного сбить с толку тех, кто не изучал компьютер , однако позвольте мне проиллюстрировать это в таблице ниже:

5.jpg


EAX будет иметь AX в качестве 16-битного сегмента, и тогда вы можете дополнительно разделить AX на AL для младших 8 бит и AH для старших 8 бит. То же самое верно и для EBX, ECX и EDX. EBX будет иметь BX в качестве своего 16-битного сегмента, а затем вы можете дополнительно разделить BX на BL для младших 8 бит и BH для старших 8 бит. В ECX будет CX в качестве 16-битного сегмента, и тогда вы можете дополнительно разделить CX на CL для младших 8 бит и BH для старших 8 бит. EDX будет иметь DX в качестве 16-битного сегмента, а затем вы можете дополнительно разделить DX на DL для младших 8 бит и DH для старших 8 бит.

ESI, EDI, EBP и ESP можно разбить на 16-битные сегменты следующим образом:

6.jpg


ESI будет иметь SI в качестве 16-битного сегмента, EDI будет иметь DI в качестве 16-битного сегмента, EBP будет иметь BP в качестве 16-битного сегмента, а ESP будет иметь SP в качестве 16-битного сегмента.

В нашем следующем руководстве мы продолжим обсуждение архитектуры IA-32 с регистрами сегмента.
 
Часть 11: Сегментные регистры

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

Имеется шесть сегментных регистров, а именно:

CS: Регистр сегментов кода хранит базовое расположение раздела кода (раздел .text), который используется для доступа к данным.

DS: Регистр сегмента данных хранит местоположение по умолчанию для переменных (раздел .data), которое используется для доступа к данным.

ES: Дополнительный сегментный регистр, который используется во время строковых операций.

SS: Регистр сегмента стека хранит базовое местоположение сегмента стека и используется при неявном использовании указателя стека или при явном использовании указателя базы.

FS: Дополнительный сегментный регистр.

GS: Дополнительный сегментный регистр.

Каждый сегментный регистр является 16-битным и содержит указатель на начало специфичного для памяти сегмента. Регистр CS содержит указатель на сегмент кода в памяти. Сегмент кода - это место, где коды инструкций хранятся в памяти. Процессор извлекает коды команд из памяти на основе значения регистра CS и значения смещения, содержащегося в регистре указателя команд (EIP). Имейте в виду, что никакая программа не может явно загружать или изменять регистр CS. Процессор присваивает свои значения, поскольку программе назначается область памяти.

Регистры сегментов DS, ES, FS и GS используются для указания на сегменты данных. Каждый из четырех отдельных сегментов данных помогает программе разделять элементы данных, чтобы гарантировать, что они не перекрываются. Программа загружает регистры сегментов данных с соответствующим значением указателя для сегментов, а затем обращается к отдельным ячейкам памяти, используя значение смещения.

Регистр сегмента стека (SS) используется для указания на сегмент стека. Стек содержит значения данных, передаваемые функциям и процедурам в программе.

Сегментные регистры считаются частью операционной системы и почти во всех случаях не могут быть ни прочитаны, ни изменены напрямую. При работе в плоской модели защищенного режима (32-разрядная архитектура x86) ваша программа запускается и получает адресное пространство размером 4 ГБ, к которому любой 32-разрядный регистр потенциально может адресовать любой из четырех миллиардов ячеек памяти, за исключением определенных защищенных областей операционной системой. Физическая память может быть больше 4 ГБ, однако 32-разрядный регистр может отображать только 4 294 967 296 различных ячеек. Если у вас более 4 ГБ памяти на вашем компьютере, ОС должна организовать область 4 ГБ в памяти, и ваши программы будут ограничены этой новой областью. Эта задача выполняется сегментными регистрами, и операционная система постоянно контролирует это.

В нашем следующем руководстве мы продолжим обсуждение архитектуры IA-32 с регистром указателя инструкций.
 
Регистр указателя команд, называемый регистром EIP, - это просто самый важный регистр, с которым вы будете иметь дело при любом реверс инжиниринге. EIP отслеживает код следующей инструкции для выполнения. EIP указывает на следующую инструкцию для выполнения. Если вы измените этот указатель, чтобы перейти в другую область кода, вы получите полный контроль над этой программой.

Давайте забежим вперед и углубимся в код. Вот пример простого приложения hello world на C, который мы рассмотрим более подробно позже в нашей серии руководств. Для наших целей сегодня мы увидим чистую МОЩНОСТЬ языка ассемблера и особенно регистр EIP и то, что мы можем сделать, чтобы полностью взломать управление программой.

1.jpg


Не беспокойтесь, если вы не понимаете, что он делает или его функции. Здесь следует обратить внимание на то, что у нас есть функция unreachableFunction, которая никогда не вызывается основной функцией. Как вы увидите, если мы сможем управлять регистром EIP, мы сможем взломать эту программу для выполнения этого кода!

2.jpg


Мы просто скомпилировали код для работы с набором инструкций IA32 и запустили его. Как вы можете видеть, нет никакого вызова unreachableFunction любого типа, так как она недоступна в нормальных условиях, поскольку вы можете видеть "Hello World!" выводимое при выполнении.

3.jpg


Мы дизассемблировали программу с помощью GDB Debugger. Мы установили точку останова для основной функции и запустили программу. Знак => показывает, куда указывает EIP, когда мы переходим к следующей инструкции. Если мы будем следовать нормальному потоку программы, "Hello World!" напечатается на консоли и программа завершится.

4.jpg


Если мы снова запустим программу и проверим, куда указывает EIP, мы увидим:

5.jpg


Мы видим, что EIP указывает на main+17 или адрес 0x680cec83.

Давайте исследуем unreachableFunction, посмотрим, где она начинается в памяти, и запишем этот адрес.

6.jpg


Следующим шагом является установка EIP на адрес 0x0804843b, чтобы мы перехватили поток программы для запуска unreachableFunction.

7.jpg


Теперь, когда мы взломали контроль над EIP, давайте продолжим и посмотрим, как мы перехватили работу работающей программы в наших интересах!

8.jpg


Тада! Мы взломали программу!

Итак, у вас возникает вопрос: почему вы показали мне это, когда я понятия не имею, что это такое? Важно понимать, что когда мы делаем такое длинное руководство, как это, нам иногда следует с нетерпением ждать, чтобы увидеть, почему мы предпринимаем так много шагов, чтобы изучить основы, прежде чем погрузиться в него. Однако важно показать вам, что если вы останетесь с туториалом, ваша тяжелая работа окупится, поскольку мы узнаем, как взломать любую работающую программу, чтобы заставить ее делать все, что мы хотим, в дополнение к проактивному разрушению вредоносной программы, чтобы мы могли не только отключить её, но и отследить до потенциального IP-адреса.

В нашем следующем руководстве мы продолжим обсуждение архитектуры IA-32 с регистрами управления.
 
Часть 13: Регистры управления

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

CR0: Системный флаг, управляющий режимом работы и различными состояниями процессора.

CR1: (В настоящее время не реализован)

CR2: Информация о неисправности страницы памяти.

CR3: Информация о каталоге страниц памяти.

CR4: Флаги, которые включают плюшки процессора и указывают функциональные возможности процессора.

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

Если требуется изменение значения флага регистра управления, это изменение может быть выполнено в данных в регистре GP, и регистр переместится в CR. Системные программисты низкого уровня обычно изменяют значения в регистрах управления. Обычные прикладные программы обычно не изменяют записи в регистре управления, однако они могут запрашивать значения флагов, чтобы определить возможности микросхемы хост-процессора, на которой в настоящее время выполняется программа.

В следующем уроке мы продолжим обсуждение архитектуры IA-32 темой о флагах.
 
Часть 14: Флаги

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

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

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

Мы имеем дело с 32-битным ассемблером, для которого существует один 32-битный регистр, содержащий группу статусных, управляющих и системных флагов. Этот регистр называется регистром EFLAGS, поскольку он содержит 32 бита информации, которые отображаются для представления определенных флагов информации.

Есть три типа флагов: флаги состояния, флаги управления и системные флаги.

Флаги состояния следующие:

CF: флаг переноса

PF: флаг четности

AF: вспомогательный флаг переноса

ZF: флаг нуля

SF: флаг знака

OF: флаг переполнения

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

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

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

Флаг нуля устанавливается, если результат операции равен нулю.

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

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

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

Системные флаги используются для управления операциями на уровне ОС, которые НИКОГДА не должны изменяться какой-либо соответствующей программой или приложением.

TF: Флаг ловушки

IF: Флаг разрешения прерывания

IOPL: флаг уровня привилегий ввода-вывода

NT: флаг вложенной задачи

RF: флаг резюме

VM: флаг режима виртуального-8086

AC: флаг проверки выравнивания

VIF: виртуальный флаг прерывания

VIP: флаг ожидания виртуального прерывания

ID: идентификационный флаг

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

Флаг разрешения прерывания контролирует реакцию процессора на сигналы, полученные от внешних источников.

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

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

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

Флаг VM указывает, что процессор работает в виртуальном режиме 8086 вместо защищенного или реального режима.

Флаг проверки выравнивания используется вместе с битом AM в регистре управления CR0, чтобы включить проверку выравнивания ссылок на память.

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

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

Флаг ID указывает, поддерживает ли процессор инструкцию CPUID.

В следующем уроке мы обсудим стек.
 
Часть 15: Стек

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

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

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

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

1.jpg


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

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

В приведенном ниже примере addMe указатель стека (ESP) при проверке в памяти на точке останова на основной функции показывает 0xffffd050. Когда программа вызывает функцию addMe из main, ESP теперь имеет значение 0xffffd030, что НИЖЕ в памяти. Поэтому стек растет ВНИЗ, несмотря на то, что на диаграмме он направлен вверх. Просто имейте в виду, что когда стрелки внизу указывают вверх, они фактически указывают на более низкие адреса памяти.

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

Предел стека - это наименьший допустимый адрес стека. Если указатель стека становится меньше этого, происходит переполнение стека, которое может повредить программу, позволяя злоумышленнику получить контроль над системой. Вредоносное ПО пытается воспользоваться переполнением стека. В последнее время в современные ОС встроены средства защиты, которые пытаются предотвратить это.

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

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

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

Давайте посмотрим на программу C, которую мы создали в уроке 12, и посмотрим, как выглядит основная функция:

2.jpg


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

Когда мы запустим эту программу, стек будет выглядеть так:

3.jpg


Мы видим фрейм стека для int main (void) выше. Это также называется записью активации. Кадр стека существует всякий раз, когда функция запущена, но еще не завершена. Например, внутри тела int main (void) есть вызов int addMe (int a, int b), который принимает два аргумента a и b. В int main (void) должен быть код языка ассемблера, чтобы поместить аргументы для int addMe (int a, int b) в стек. Давайте рассмотрим код.

4.jpg


Когда мы скомпилируем и запустим эту программу, мы увидим, что значение 5 будет напечатано следующим образом:

5.jpg


Очень просто, int main (void) сначала вызывает int addMe (int a, int b) и помещает в стек следующим образом:

6.jpg


Вы можете видеть, что, поместив аргументы в стек, фрейм стека для int main (void) увеличился в размере. Мы также зарезервировали место для возвращаемого значения, которое вычисляется int addMe (int a, int b), и когда функция возвращается, возвращаемое значение в int main (void) восстанавливается, и выполнение продолжается в int main (void) до тех пор, пока оно не завершится.

После того, как мы получим инструкции для int addMe (int a, int b), функции могут потребоваться локальные переменные, поэтому функции необходимо добавить некоторое пространство в стеке, которое будет выглядеть так:

7.jpg


int addMe (int a, int b) может получить доступ к аргументам, переданным ему из int main (void), потому что код в int main (void) размещает аргументы так же, как этого ожидает int addMe (int a, int b).

FP - указатель кадра и указывает на место, где указатель стека находился непосредственно перед тем, как int addMe (int a, int b) переместил указатель стека или SP для собственных локальных переменных int addMe (int a, int b).

Использование указателя кадра необходимо, когда функция, вероятно, несколько раз переместит указатель стека в ходе выполнения функции. Идея состоит в том, чтобы фиксировать указатель фрейма на время стека int addMe (int a, int b). Тем временем указатель стека может изменять значения.

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

Как только пришло время выйти из int addMe (int a, int b), указатель стека устанавливается в том месте, где находится указатель кадра, который выталкивается из кадра стека int addMe (int a, int b).

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

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

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

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

В следующем уроке мы обсудим кучу.
 
Часть 16: Куча

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

8.jpg


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

Чтобы выделить память в куче, вы должны использовать malloc() или calloc(), которые являются встроенными функциями C. После того, как вы выделили память в куче, вы несете ответственность за ее освобождение, используя free(), чтобы освободить эту память, когда она вам больше не понадобится.

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

В отличие от стека, куча не имеет ограничений по размеру переменного размера. Единственное, что может ограничить кучу, - это физические ограничения вашего компьютера. Память кучи немного медленнее для чтения и записи, потому что вам нужно использовать указатели для доступа к памяти в куче. Когда мы углубимся в нашу серию руководств по C, мы продемонстрируем это.

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

Если вам нужно выделить большой блок памяти для чего-то вроде структуры или большого массива, и вам нужно сохранить эту переменную в течение длительного времени программы, к которой должен быть доступен глобальный доступ, тогда вы должны выбрать кучу для этой цели . Если вам нужны такие переменные, как массивы и структуры, которые могут динамически изменять размер, например массивы, которые могут увеличиваться или уменьшаться по мере необходимости, вам, вероятно, потребуется выделить их в куче и использовать функции динамического распределения памяти, такие как malloc(), calloc() , realloc() и free() для управления этой памятью вручную.

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

Я с нетерпением жду встречи со всеми вами на следующей неделе, когда мы рассмотрим подробное пошаговое руководство по установке Linux на ваш компьютер с помощью БЕСПЛАТНОГО программного инструмента Virtual Box.
 
Часть 17 - Как установить Linux

Если у вас не установлен Linux на компьютере, я бы предложил установить Virtual Box, бесплатную виртуальную среду с открытым исходным кодом, которую вы можете установить на свой существующий компьютер, чтобы иметь версию Linux, с которой вы можете программировать. Ниже приведена ссылка для загрузки и установки Virtual Box, так как есть версии для Windows и Mac.

https://www.virtualbox.org/wiki/Downloads

10.jpg


Кроме того, вам понадобится копия Linux, с которой я буду работать. Ниже приведена ссылка для загрузки файла .iso, в который вы будете устанавливать после установки Virtual Box.

http://www.ubuntu.com/download/desktop

11.jpg


После загрузки вышеуказанного .iso перейдите в каталог загрузки и сначала запустите VirtualBox-5.0.24-108355-Win.exe или любую другую версию VirtualBox, которая доступна в настоящее время. Если у вас Mac, вы скачиваете файл .dmg. Просто дважды щелкните файл, чтобы запустить его.

После установки VirtualBox-5.0.24-108355-Win.exe или файла Mac .dmg вы увидите этот экран:

12.jpg


Нажмите кнопку "Создать", которая находится в верхнем левом углу экрана, поскольку это большой синий кружок в виде шестеренки.

13.jpg


В поле имени выше введите Ubuntu и нажмите кнопку "Далее".

14.jpg


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

15.jpg


Затем нажмите "Создать".

16.jpg


Затем нажмите "Далее".

17.jpg


Затем нажмите "Далее".

18.jpg


Переместите ползунок до 16,00 ГБ, а не до 8,00 ГБ, как показано выше, затем нажмите "Создать".

19.jpg


Следующим шагом является нажатие на зеленую кнопку запуска.

20.jpg


Следующим шагом является щелчок по желтой папке над кнопкой отмены.

21.jpg


Далее нужно щелкнуть файл .iso, который должен находиться в вашем каталоге загрузки, и нажать кнопку "Открыть".

22.jpg


Далее будем нажимать кнопку "Пуск".

23.jpg


Следующим шагом нужно начать установку и нажать "Установить Ubuntu".

24.jpg


Следующим шагом является установка флажка для загрузки обновлений при установке Ubuntu и нажатие кнопки "Продолжить".

25.jpg


Следующим шагом нужно выбрать "Стереть диск", установить Ubuntu и нажать "Установить сейчас".

26.jpg


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

27.jpg


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

28.jpg


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

29.jpg


На этом этапе установка операционной системы займет некоторое время. Когда процесс будет завершен, нажмите "Перезапустить сейчас". Если окно блокируется, нажмите "Выключить устройство", затем нажмите "Закрыть" ,или "Далее".

30.jpg


На этом этапе нажмите зеленую кнопку запуска.

31.jpg


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

32.jpg


Поздравляю! У вас рабочая версия Linux!

33.jpg


Щелкните значок в верхнем левом углу, введите "Терминал" и дважды щелкните первый значок "Терминал" с символом > _ в окне.

34.jpg


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

35.jpg


В окне терминала введите cd Desktop и нажмите Enter. Затем введите mkdir и нажмите ввод. Первая команда перемещает вас в каталог Desktop, а команда mkdir создает на рабочем столе папку под названием Code, чтобы у нас было место для хранения наших программ, которые мы создаем.

36.jpg


Важно, чтобы ваша версия Linux была актуальной. Каждый раз, когда вы входите в систему, вы должны вводить следующие команды. Сначала sudo apt-get update и нажмите ввод.

37.jpg


Затем вы должны ввести sudo apt-get upgrade и нажать Enter.

38.jpg


Чтобы работать с 32-разрядным ассемблером, нам необходимо установить пакет gcc Multilib, чтобы мы могли скомпилировать 32-разрядные версии кода C. Введите sudo apt-get install gcc-Multilib и нажмите ввод.

39.jpg


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

Это был очень длинный туториал, однако он был необходим для получения рабочей копии Linux, чтобы мы могли продолжить наше обучение.

Я с нетерпением жду встречи с вами на следующей неделе, когда мы узнаем, как использовать текстовый редактор vim, чтобы начать кодить!
 
Часть 18 - текстовый редактор vim

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

Для начала откройте свой терминал и введите:

40.jpg


Это откроет текстовый редактор vi. Первое, что вам нужно напечатать, это букву "i", чтобы установить редактор в режим вставки, чтобы вы могли начать печатать.

41.jpg


Закончив ввод, нажмите клавишу "esc", введите ": wq" и нажмите клавишу ВВОД.

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

В первой строке указан номер набора, что означает, что мы хотели бы, чтобы в каждом файле отображались номера строк, поскольку это важно для отладки кода. Операторы set smartindent, set tabstop, set shiftwidth и set expandtab устанавливают правила для правильного форматирования кода и допускают 4 пробела на отступ табуляции, что поможет нашему коду выглядеть чистым.

Вам нужно знать несколько команд. Имейте в виду, что для перехода в командный режим, а не в режим вставки, вы должны нажать клавишу "esc".

Ниже приведены наиболее распространенные команды:

j или стрелка вниз [переместите курсор на одну строку вниз]

k или стрелка вверх [переместите курсор на одну строку вверх]

h или стрелка влево [переместите курсор на один символ влево]

l или стрелка вправо [переместите курсор на один символ вправо]

0 [переместите курсор в начало текущей строки]

$ [переместить курсор в конец текущей строки]

b [переместить курсор в начало предыдущего слова]

dd [удаляет строку, на которой находится курсор]

D [удаляет от позиции курсора до конца строки]

yy [копирует текущую строку]

p [помещает скопированный текст после курсора]

u [отменить последнее изменение в файле]

: w [сохранить файл]

: wq [сохранить файл и выйти из текстового редактора]

: q! [выйти из текстового редактора и не сохранять никаких изменений]

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

Теперь, когда у нас настроен vi, давайте установим vim, который имеет улучшенную функциональность. Просто введите:

42.jpg


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

Я с нетерпением жду встречи с вами, когда мы поговорим о том, почему так важно изучать язык ассемблера.
 
Часть 19 - Зачем изучать ассемблер

Зачем изучать язык ассемблера? Java - это самый востребованный язык программирования, и он сразу же даст мне работу, так почему, черт возьми, я должен тратить свое чертово время на изучение этой архаичной чуши типа языка ассемблера?

Так много людей задают мне этот вопрос, и это правда, Java является ГОРЯЧИМ блюдом и пользуется наибольшим спросом, и нет ничего плохого в изучении Java, однако угрозы, с которыми общество сталкивается больше всего в этом мире, прежде всего, это угроза кибербезопасности. С учетом сказанного, Java предлагает отличный карьерный путь, и я рекомендую вам изучить его, однако Java - не единственная игра в городе.

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

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

Когда мы исследуем вредоносное ПО, мы получаем только скомпилированный двоичный файл. Единственное, что мы можем сделать с скомпилированным двоичным кодом, - это разбить его, инструкция за инструкцией, на языке ассемблера, поскольку ВСЕ в конечном итоге сводится к языку ассемблера.

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

Понимание языка ассемблера позволяет открыть отладчик в запущенном процессе. Каждая запущенная программа имеет PID, числовое значение которого обозначает запущенную программу. Если мы откроем запущенный процесс или любую вредоносную программу с помощью профессионального инструмента или инструмента с открытым исходным кодом, такого как GDB, мы сможем ТОЧНО увидеть, что происходит, а затем взять указатель инструкции EIP, чтобы перейти туда, где он нам нужен, чтобы получить ПОЛНЫЙ контроль над поток программы.

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

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

Предыдущие 18 уроков этой серии туториалов познакомили вас с основами оборудования x86. Как я уже говорил в предыдущих туториалах, мы сосредоточимся на отладке 32-разрядного кода, поскольку большинство вредоносных программ будет пытаться повлиять на как можно больше систем, и несмотря на наличие 64-разрядных вредоносных программ, 32-разрядные вредоносные программы значительно более разрушительны и опасны.

Я с нетерпением жду встречи с вами в следующем модуле, когда мы изучим основы работы с кодом инструкций.
 
Часть 20 - Обработка кода инструкций

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

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

Указатель данных помогает процессору отслеживать, где начинается область данных в памяти, то есть стек. Когда новые элементы данных помещаются в стек, указатель стека перемещается в памяти вниз, а при считывании данных из стека указатель стека перемещается в памяти вверх. Прочтите Часть 15 - Стек, если вы не понимаете эту концепцию.

Указатель инструкций используется, чтобы помочь ЦП отслеживать, какие коды инструкций уже были обработаны и какой код должен быть обработан следующим. Если вы не понимаете эту концепцию, ознакомьтесь с частью 12 - Регистрация указателя инструкций.

Каждый код инструкции должен включать в себя код операции, который определяет основную функцию или задачу, которую должен выполнять ЦП, коды операций имеют длину от 1 до 3 байтов и однозначно определяют выполняемую функцию.

Для начала рассмотрим простую программу на языке C под названием test.c.

1.jpg


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

Скомпилируем и запустим эту программу.

2.jpg


Давайте воспользуемся инструментом objdump и найдем в нем основную функцию.

3.jpg


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

4.jpg


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

Чтобы не усложнять задачу, давайте рассмотрим адрес памяти 80483de, где мы видим коды операций b8 00 00 00 00. Мы видим, что код операции b8 соответствует инструкции mov eax, 0x0 справа. Следующая серия 00 00 00 00 представляет 4 байта значения 0. Мы видим mov eax, 0x0, поэтому значение 0 перемещается в eax, представляя приведенный выше код. Имейте в виду, что платформа IA-32 использует то, что мы называем little-endian нотацией, что означает, что байты с меньшим значением появляются первыми по порядку при чтении справа налево.

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

mov eax, 0x1

В этом сценарии соответствующий код операции будет:

b8 01 00 00 00

Если вы запутались, ничего страшного. Помните метод прямого порядка байтов? Имейте в виду, что eax имеет ширину 32 бита, следовательно, это 4 байта (8 бит = 1 байт). Значения перечислены в обратном порядке, поэтому мы видим приведенное выше представление.

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


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх