Урок 1 Введение в Си и начало reverse engineering.
/*Это
моя первая программа
на С. Я создаю исходные
код. Далее использую компилятор, который
мой исходный код
превращает в объектный
код. После идет
подключение компоновщика, который
компонует исходный код,
библиотечный код и
код запуска. Только
после этого получается исполняемый код.
Из ООП
мы знаем, что
переменная - это коробочка,
куда мы складываем типы данных. Функция
преобразует наши типы
входных данных, и
на выходе мы
получаем модифицированные переменные.
Компьютерная программа
выражена в числовом
коде - машинный код.
Все что хранится
в компьютере, хранится
в виде чисел.
*/
Введем понятие ПЕРЕМЕННАЯ
Понять,
что такое переменные, поможет простая метафора. Думайте о них как о небольших
(или больших) спичечных коробках! Именно как о спичечных коробках, которые вы
раскрасили и на которых написали некие имена.
Переменные
могут содержать не только строки, но и числа. Если вернуться к аналогии со
спичечным коробком, сохранение в переменной count числа 17 будет эквивалентно
помещению, скажем, 17 бусин в коробок, на котором написано слово count:
Чтобы
узнать о содержимом коробка, его нужно просто открыть и посчитать бусины.
Массивы
можно представить в виде нескольких склеенных вместе спичечных коробков.
//БУФЕР
- ПРОМЕЖУТОЧНАЯ ОБЛАСТЬ ХРАНЕНИЯ
//Это простая программа
#include <stdio.h>
int main(void)
{
int num;
num = 1;
printf("Я простой");
printf("компьютер. \n"); printf("Моя любимая цифра %d, так как она первая.n", num);
return 0;
}
Пояснения
#include
<stdio.h> //Начало программы.
Включить другой файл
/*Эта строка требует от компилятора включить информацию,
хранящуюся в файле stdio.h, который является стандартной частью всех пакетов
компилятора языка С. Этот файл обеспечивает поддержку ввода с клавиатуры и
отображения вывода.
include -
Директива включить файлы - способ совместного использования информации, который
применяется во многих программах.
#include -
директива прeпроцессора в С. В общем случае компиляторы языка С выполняют некоторую подготовительную работу над исходным
кодом перед компиляцией - это называется предварительная обработка.
Файл stdio.h поставляется
как часть всех пакетов компиляторов С. Он содержит информацию о функциях ввода
и вывода, таких как printf(), и предназначен для использования компилятором,
это заголовочный файл стандартного ввода-вывода.
Описание функции printf() требует использования файла
stdio.h. Это относится к библиотечным функциям.
stdio.h -
заголовочный файл
# - символ
обозначает, что эта строка должна обрабатываться препроцессором до передачи ее
компилятору. */
int main(void) //имя функции (объявление функции с
именем main
/*Программа на С состоит из одной или большего числа функций
- базовых модулей программ на С. Рассматриваемая программа состоит из одной
функции с именем main.
Круглые скобки показывают, что main() есть имя функции,
int - указывает
на то, что функция main() возвращает целое число,
void - говорит о
том, что функция main() не принимает никаких аргументов.
Программа на языке С всегда начинается с выполнения функции
main().
() -
идентификация функции - т.е. main() -
скобки идентифицируют main() как
функцию.
Функции - это базовые модули программы на языке С.
int - это
возвращаемый тип функции main(). Это значит, что тип значения, который может
вернуть функция main(), является целочисленным.
В круглых скобках, которые следуют за именем функции,
находится информация, передаваемая функциями. В данном примере ничего не
передается, поэтому в скобках содержится слово void.
{
.....
} -
фигурные скобки определяют границы функции main().
*/
{} //тела функции. Оператор
определения функции
int num; //ОПЕРАТОР ОБЪЯВЛЕНИЯ назначает
переменной имя и определяет тип данных, которые будут храниться в этой
переменной.
/*этот оператор объявляет переменную с именем num и
уведомляет, что переменная num имеет тип int (целое число). Также можно сказать, что где-то в функции
имеется переменная по имени num. И переменная имеет целочисленный тип.
В данном примере объявлены два объекта ->
num - переменная
в теле функции
int - объявляет
num как целое число, т.е. число без десятичной точки или без дробной части. (int - это тип данных). Также является
ключевым словом.
; точка с запятой
в конце строки показывает, что данная строка является оператором
num - является
идентификатором, имя переменной, функции, логического объекта
Объявление переменной соединяет конкретный идентификатор с
конкретной ячейкой в памяти компьютера и при этом устанавливает тип информации
или тип данных, которые будут храниться в этой ячейки.
В роли КОНСТАНТ выступают целые числа.
*/
num = 1; //ОПЕРАТОР ПРИСВАИВАНИЯ - устанавливает
значение переменной или область хранения.
/* Оператор num = 1; присваивает значение 1 переменной с
именем num. Присвоить значение 1 переменной num.
int num; -
резервирует в памяти компьютера пространство для хранения переменной num, а
строка с оператором присваивания записывает значение в эту ячейку. Позже можно
присвоить переменной num другое значение. Вот почему num называется переменной.
num - ИМЯ ПЕРЕМЕННОЙ
1-> КОНСТАНТА
*/
АРГУМЕНТ - ЭЛЕМЕНТЫ ИНФОРМАЦИИ, ПЕРЕДАВАЕМОЙ ФУНКЦИИ.
ФУНКЦИИ printf() и
scanf() не ограничены конкретным количеством аргументов.
Аргументы отделяются друг от друга запятыми.
printf("Я
простой"); //ОПЕРАТОР
ВЫЗОВА ФУНКЦИИ - запускает на выполнение функцию с
указанным именем
/*Первый оператор, использующий функции printf() отображает
на экране фразу "Я простой", оставляя курсор в той же строке.
Используя здесь функцию printf() является частью стандартной библиотеки С. Она
носит название функции, а использование функции в программе называется вызовом функции.
Программа выполняется последовательно. Когда доходит до
строки printf("Я простой"); управление передается указанной функции
printf(). Как только функция выполнит свою задачу, управление возвращается в
исходную, т.е. в вызывающую функцию, в данном примере - main().
printf() - функция с именем printf(). Информация в
круглых скобках - это информация, передаваемая из функции main() в функцию printf().
Эта строка передает функции printf() фразу "Я
простой" - эта информация называется аргументом, т.е. аргумент функции.
Что делает Функция printf() с этим аргументом? Просматривает
все что заключено в двойные кавычки
"" и выводит этот текст на экран.
*/
printf("компьютер.
\n"); //ОПЕРАТОР ВЫЗОВА ФУНКЦИИ
- запускает на выполнение функцию с указанным
именем
/*Следующий вызов функции printf() приписывает слово
компьютер в конец напечатанной предыдущей фразы. \n - это код, указывающий компьютеру начать новую строку, т.е.
переместить курсор в начало следующей строки*/
printf("Моя
любимая цифра %d, так как она первая \n", num);
/*Последнее использование функции printf() приводит к печати
значения переменной num(которое равно 1), вставленной во фразу, заключенную в
кавычки. Код %d указывает
компьютеру, где и в какой форме печатать значение num */
return 0; //ОПЕРАТОР
ВОЗВРАТА
printf() -
ФУНКЦИЯ ВЫВОДА
scanf() - ФУНКЦИЯ ВВОДА
printf() - функция вывода, применяется для вывода фраз и
значений переменных
scanf() - функция ввода - воспринимает ввод аналогично
getchar() но при помощи спецификаторов преобразования, преобразует символьный
ввод в числовые значения
getchar() - функция ввода аналогична scanf() только
интерпретирует каждый байт как символьный код
putchar() - функция вывода аналогично printf()
Теперь, после ознакомления с конкретным примером, вы готовы
к изучению нескольких общих правил написания программ на языке С. Программа
состоит из совокупности одной или нескольких функций, одна из которых
обязательно должна быть названа main (). Написание функции состоит из заголовка
и тела функции. Заголовок содержит операторы препроцессора, такие как #include
, а также имя функции. Вы можете распознать имя функции по круглым скобкам,
внутри которых может быть пусто. Тело функции заключено в фигурные скобки ( { }
) и состоит из некоторой последовательности операторов, при этом каждый
оператор завершается точкой с запятой.
МНОЖЕСТВО ФУНКЦИЙ
Продемонстрируем возможность внедрения в программу наряду с
функцией main() вашей собственной функции.
Пример
#include <stdio.h> //КОМАНДА
ПРЕПРОЦЕССОРА - подключение файла стандартной библиотеки С
stdio.h содержит библиотеку функций ввода-вывода
void jolly(void); //Объявления
ПРОТОТИП ФУНКЦИИ с именем jolly()
void deny(void); //Объявления
ПРОТОТИП ФУНКЦИИ с именем deny()
int main(void) //ИМЯ
ФУНКЦИИ С АРГУМЕНТАМИ. Имя функции - main(). Функция которой
всегда присутствует в программе на С.
//int
- ф-ция возвращает целое число, void - ф-ция не принимает аргументов
{
printf ("He
is funny good guy. \n"); //ОПЕРАТОР
ВЫЗОВА ФУНКЦИИ с именем printf() функция
ВЫВОД ДАННЫХ НА ЭКРАН.
jolly(); //ОПЕРАТОР
ВЫЗОВА ФУНКЦИИ с именем jolly() - это ПРОТОТИП
ФУНКЦИИ
printf("He is funny good guy. \n");
deny(); //ОПЕРАТОР
ВЫЗОВА ФУНКЦИИ с именем deny() - это ПРОТОТИП
ФУНКЦИИ
return 0; //ОПЕРАТОР
ВОЗВРАТА
}
void jolly(void) //начало
определения функции с именем jolly, функция не принимает
аргументов и нечего не возвращает
{
printf("He
is funny good guy. \n"); //ОПЕРАТОР
ВЫЗОВА ФУНКЦИИ с именем printf() - функция
ВЫВОД ДАННЫХ НА ЭКРАН. Имеет один аргумент.
АРГУМЕНТ - ЭЛЕМЕНТЫ ИНФОРМАЦИИ, ПЕРЕДАВАЕМОЙ
ФУНКЦИИ.
}
void deny(void) //начало
определения функции с именем deny
{
printf("Nobody
say another \n"); //ОПЕРАТОР
ВЫЗОВА ФУНКЦИИ с именем printf() - функция
ВЫВОД ДАННЫХ НА ЭКРАН.
//\n
код указывающий начать новую строку
}
Функция
butler () трижды появляется в рассматриваемой программе .
В
первый раз она появляется в виде прототипа,
передающего компилятору информацию о функциях, которые будут использованы в данной
программе.
Во
второй раз она появляется в main () в форме вызова функции.
Третий
раз, в данной программе представлено определение функции, которое является
исходным кодом самой функции. Рассмотрим по очереди каждое из этих трех
появлений.
Прототип - это
форма объявления, которая уведомляет компилятор о том, что вы используете
конкретную функцию. Он также определяет свойства этой функции. Например, первое
ключевое слово void в прототипе функции butler() указывает на то, что butler() не имеет возвращаемого значения .
(В общем случае функция может возвратить значение в
вызывающую функцию для последующего его использования, но butler() этого не
делает.)
Второе void, то, что в указано в функции butler (void) ,
означает, что функция butler() не принимает аргументов.
Поэтому, когда достигается место в main(), в котором
вызывается butler(), выполняется проверка корректности использования функции
butler(). Обратите внимание на тот факт, что ключевое слово void
употребляется в смысле " пусто", а не в смысле
"неправильно".
Исходный код
//Это простая программа
#include <stdio.h>
int main(void)
{
int num;
num = 1;
printf("I am simple");
printf("computer. \n"); printf("My love digit is %d, as it is first", num);
return 0;
}
Откроем Code
Block
Далее создадим Новый проект - > Create New Project ->
Console Application -> Выбираем Application на С -> Далее даем
название проекту и место хранения -> Выбираем компилятор -> Finish -> Получаем
заготовку, где можем писать свой код -> Пишем свой код. В нашем случае - наш
пример
Далее Build
and Run.
Мы получаем рабочее консольное приложение.
Теперь в картинках.
1) Открываем Code Blocks
2) Выбираем образ программы. В нашем случае Console Application
3) Выбираем язык на котором будем писать приложение. В нашем
случае С.
4) Даем название и путь хранения
5) Выбираем компилятор и нажимаем Finish
6) Получаем макет, где мы можем вставлять свой код
7) Пишем свой код и нажимаем Build and Run
8) Получаем
консольное приложение
Анатомия программы на
Си
Далее
мы посмотрим нашу программу изнутри с помощью средств обратной разработки. Это
поможет нам лучше понять анатомию программирования и работы ОС,
Классически
мы используем:
- Hex Workshop Hex Editor - 16-ричный редактор
- PeiD - Один из самых популярных анализаторов исполняемых
файлов. Хорошо определяет многие упаковщики и протекторы
- LordPe - Инструмент для системных программистов
которые нуждаются в ручном редактирование исполняемых файлов (Portable
Executable).
- Ollydbg - 32-битный отладчик уровня ассемблера для
операционных систем Windows, предназначенный для анализа и модификации
откомпилированных исполняемых файлов и библиотек, работающих в режиме
пользователя (ring-3).
- IDA Pro Disassembler (англ. Interactive DisAssembler) — интерактивный
дизассемблер, который широко используется для реверс-инжиниринга.
Бит (русское обозначение: бит;
международное: bit; от англ. binary digit — двоичное число; также
игра слов: англ. bit — кусочек, частица)
— единица измерения количества
информации.
Целое, целочисленный тип данных (англ.
Integer), в информатике — один из простейших и самых распространённых типов
данных в языках программирования. Служит для представления целых чисел.
Множество
чисел этого типа представляет собой конечное подмножество бесконечного
множества целых чисел, ограниченное максимальным и минимальным значениями.
В
программировании различают беззнаковые целые числа и целые числа со знаком.
Знак числа обычно кодируется старшим битом машинного слова. Традиционно, если
старший бит равен 1, то число считается отрицательным, только, если оно не
определено как беззнаковое.
Количество
чисел в машинном изображении множества целых чисел зависит от длины машинного
слова, обычно выражаемой в битах. Например, при длине машинного слова 1 байт
(8 бит) диапазон представимых целых чисел со знаком от -128 до 127. В
беззнаковом формате байтовое представление числа будет от 0 до 255 (28 - 1).
Если используется 32-разрядное машинное слово, то целое со знаком будет
представлять значения от −2 147 483 648 (-231) до 2 147 483 647 (231−1); всего
1 0000 000016 (4 294 967 29610) возможных значений.
1 байт = 8 бит;
2 байт = 16 бит:
4 байт = 32 бит;
8 байт = 64 бит и т.д.
Многие
языки программирования предлагают выбор между короткими (англ. short), длинными
(англ. long) и целыми стандартной длины. Длина стандартного целого типа, как
правило, совпадает с размером машинного слова на целевой платформе. Для
16-разрядных операционных систем — этот тип (int) составляет 2 байта и
совпадает с типом short int (можно использовать как short, опуская слово int),
для 32-разрядных операционных систем он будет равен 4 байтам и совпадает с
длинным целым long int (можно использовать как long, опуская слово int), и в
этом случае будет составлять 4 байта. Короткое целое short int, для
16-разрядных операционных систем, 32-разрядных операционных систем, и для
большинства 64-разрядных операционных систем составляет — 2 байта. Также в
некоторых языках может использоваться тип данных двойное длинное long long,
который составляет 8 байт.
Анализ программы example.exe - 32-битное консольное приложение
1. Hex Workshop Hex Editor - 16-ричный редактор
Загрузим нашу программу в Hex Workshop Hex Editor
Теперь мы видим внутренности программы
Здесь мы видим сигнатуру файла. Давайте разбираться дальше.
Магическое число, или сигнатура —
целочисленная константа, используемая для однозначной идентификации ресурса или
данных. Такое число само по себе не несёт никакого смысла и может вызвать
недоумение, встретившись в коде программы без соответствующего контекста или
комментария, при этом попытка изменить его на другое, даже близкое по значению,
может привести к абсолютно непредсказуемым последствиям. По этой причине подобные
числа были иронично названы магическими. В настоящее время это название прочно
закрепилось как термин.
Portable Executable (PE, «переносимый
исполняемый») — формат исполняемых файлов, объектного кода и динамических
библиотек, используемый в 32- и 64-разрядных версиях операционной системы
Microsoft Windows. Формат PE представляет собой структуру данных, содержащую
всю информацию, необходимую PE-загрузчику для отображения файла в память.
Исполняемый код включает в себя ссылки для связывания динамически загружаемых
библиотек, таблицы экспорта и импорта API функций, данные для управления
ресурсами и данные локальной памяти потока (TLS).
MZ — стандартный формат 16-битных исполняемых файлов с
расширением .EXE для DOS. Назван так по сигнатуре
— ASCII-символам MZ (4D 5A) в первых двух байтах. Эта сигнатура — инициалы Марка
Збиковски, одного из создателей MS-DOS.
Формат
был разработан как замена устаревшему формату .COM. Исполняемые файлы MZ включают
метаданные, могут иметь размер больше 64 Кбайт и использовать несколько
сегментов памяти различного типа (кода, данных и стека), точка входа в
программу также может быть в любом месте (в файлах .COM выполнение команд
всегда начинается непосредственно с начала файла). Метод загрузки исполняемого файла определяется по сигнатуре: при её
наличии обрабатывается MZ-заголовок, при отсутствии файл запускается как .COM —
независимо от расширения файла (например, в последних версиях MS-DOS
интерпретатор командной строки COMMAND.COM на самом деле является EXE-файлом).
Исполняемые
файлы более поздних форматов для Windows начинаются с MZ-заглушки. Обычно
заглушка, добавляемая компиляторами, выводит сообщение наподобие «This program
cannot be run in DOS mode» («Эту программу невозможно запустить в режиме DOS»).
Технические детали PE Portable Executable
(PE, «переносимый исполняемый»)
- сигнатура
-
структура
-
таблица импорта
-
таблица экспорта
-
таблица перемещений
Сигнатура
Первые
2 байта PE файла содержат сигнатуру 0x4D 0x5A — «MZ» (как наследник
MZ-формата). Далее двойное слово по смещению 0x3C содержит адрес PE-заголовка.
Последний начинается с сигнатуры 0x50 0x45 — «PE».
Структура
Файл
PE состоит из нескольких заголовков и секций, которые указывают динамическому
компоновщику, как отображать файл в память. Исполняемый образ состоит
из нескольких различных областей (секций), каждая из которых требует различных
прав доступа к памяти; таким образом, начало каждой секции должно быть
выровнено по границе страницы. Например, обычно секция
.text, которая содержит код программы,
отображена как исполняемая/доступная только для чтения, а секция
.data, содержащая глобальные переменные,
отображена как неисполняемая/доступная для чтения и записи.
.text: Code
.data: Initialized data
.bss: Uninitialized data
.rdata: Const/read-only (and
initialized) data
.edata: Export descriptors
.idata: Import descriptors
The
DATA Section
data
section - используется для объявление инициализированных данных или констант
Эти
данные не изменяются в процессе выполнение. Можно объявлять переменные,
константы,
имена
файлов, размер буфера и т.д.
The
BSS Section
bss
section - используется для объявления переменных.
The
TEXT Section
text
section - используется для хранения кода.
global_start
- говорит ядру где начинает выполнятся программа
Таблица импорта
Одна
из известных секций — таблица адресов импорта (IAT — Import Address Table), которая используется в качестве
таблицы поиска, когда приложение вызывает функцию из другого модуля. Это может
быть сделано и в форме импорта по порядковому номеру функции (ordinal), и
импорта по её имени. Поскольку скомпилированной программе неизвестно
расположение библиотек, от которых она зависит, то требуется производить
косвенный переход всякий раз, когда происходит вызов API-функции. Когда
динамический компоновщик загружает модули и объединяет их, он записывает
действительные адреса в область IAT так, чтобы они указали на ячейки памяти
соответствующих библиотечных функций.
Таблица экспорта
Таблица
адресов экспорта (EAT — Export Address
Table) нужна для того, чтобы один модуль (обычно это динамически
загружаемая библиотека) мог указать другим модулям, какие функции они могут из
него импортировать, и по каким адресам последние расположены.
Таблица перемещений
Файлы
PE не содержат позиционно-независимого кода. Вместо этого они скомпилированы
для предпочтительного базового адреса, и все адреса, генерируемые
компилятором/компоновщиком, заранее фиксированы. Если PE-файл не может быть
загружен по своему предпочтительному адресу (потому что он уже занят чем-то
ещё), операционная система будет перебазировать его. Это включает в себя
перевычисление каждого абсолютного адреса и изменение кода для того, чтобы
использовать новые значения. Загрузчик делает это, сравнивая предпочтительный и
фактический адреса загрузки, и вычисляя значение разности. Тогда для получения
нового адреса ячейки памяти эта разность складывается с предпочтительным
адресом. Базовые адреса перемещений хранятся в списке и при необходимости
добавляются к существующей ячейке памяти.
Теперь
используем PeiD - Один из самых популярных
анализаторов исполняемых файлов.
Откроем
наш файл example.exe в программе PeiD.
Откроем
EP Section .text -> секция
кода
Здесь мы
видим
.text: Code
.data: Initialized data
.rdata: Const/read-only (and
initialized) data
.bss: Uninitialized data
.edata: Export descriptors
.idata: Import descriptors
Откроем PE Details
Таблица
адресов импорта (IAT — Import Address
Table), которая используется в качестве таблицы поиска, когда приложение
вызывает функцию из другого модуля.
Таблица
адресов экспорта (EAT — Export Address
Table) нужна для того, чтобы один модуль (обычно это динамически
загружаемая библиотека) мог указать другим модулям, какие функции они могут из
него импортировать, и по каким адресам последние расположены.
TLSTable - данные для управления
ресурсами и данные локальной памяти потока (TLS).
FLS - file
location calculator
Для
проверки упакован файл или нет используем дополнительные функции PeiD
Теперь рассмотрим
инструмент LordPe
LordPe - Инструмент для системных программистов
которые нуждаются в ручном редактирование исполняемых файлов (Portable
Executable).
Здесь
мы видим аналогично PeiD разделы
Таблица
адресов импорта (IAT — Import Address
Table), которая используется в качестве таблицы поиска, когда приложение
вызывает функцию из другого модуля.
Таблица
адресов экспорта (EAT — Export Address
Table) нужна для того, чтобы один модуль (обычно это динамически
загружаемая библиотека) мог указать другим модулям, какие функции они могут из
него импортировать, и по каким адресам последние расположены.
TLSTable - данные для управления
ресурсами и данные локальной памяти потока (TLS).
FLS - file location calculator
Здесь
мы видим байты
Сигнатура
Первые
2 байта PE файла содержат сигнатуру 0x4D 0x5A — «MZ» (как наследник
MZ-формата). Далее двойное слово по смещению 0x3C содержит адрес PE-заголовка.
Последний начинается с сигнатуры 0x50 0x45 — «PE».
Как
сделать файл записывающимся - writable
Идем
в секцию Subsystem
-> .text -> открываем
секцию .text и ставим галочку writable
Используем - Ollydbg - 32-битный отладчик уровня ассемблера для
операционных систем Windows, предназначенный для анализа и модификации
откомпилированных исполняемых файлов и библиотек, работающих в режиме
пользователя (ring-3).
Используя
Ollydbg отладчик мы видим наш код.
РЕГИСТРЫ
EAX, EBX, ECX, EDX, ESI, EDI, EBP (32-разрядные регистры)называются регистрами
общего
назначения и могут свободно участвовать в любых математических операциях или
опе-
рациях
обращения к памяти.
- IDA Pro Disassembler (англ. Interactive DisAssembler) — интерактивный дизассемблер,
который широко используется для реверс-инжиниринга
(int argc, char **argv) - АРУМЕНТЫ
КОМАНДНОЙ СТРОКИ - формальные аргументы
.text:00401369 ; =============== S U
B R O U T I N E ===================
.text:00401369
.text:00401369 ; Attributes:
bp-based frame
.text:00401369
.text:00401369 ; int __cdecl
main(int argc, const char **argv, const char **envp)
.text:00401369 public _main
.text:00401369 _main proc near ; CODE XREF:
___mingw_CRTStartup+F8p
.text:00401369
.text:00401369 Format = dword ptr -20h
.text:00401369 var_1C = dword ptr -1Ch
.text:00401369 var_4 = dword ptr -4
.text:00401369 argc = dword ptr 8
.text:00401369 argv = dword ptr 0Ch
.text:00401369 envp = dword ptr 10h
.text:00401369
.text:00401369 push ebp
.text:0040136A mov ebp, esp
.text:0040136C and
esp, 0FFFFFFF0h
.text:0040136F sub esp, 20h
.text:00401372 call ___main
.text:00401377 mov [esp+20h+Format], offset
Format ; "How many dogs you have?\n"
.text:0040137E call _printf
.text:00401383 lea eax, [esp+20h+var_4]
.text:00401387 mov [esp+20h+var_1C], eax
.text:0040138B mov [esp+20h+Format], offset
aD ; "%d"
.text:00401392 call _scanf
.text:00401397 mov eax, [esp+20h+var_4]
.text:0040139B mov [esp+20h+var_1C], eax
.text:0040139F mov [esp+20h+Format], offset
aSoYouHaveDDogs ; "So, you have %d dogs!\n "
.text:004013A6 call _printf
.text:004013AB call _getchar
.text:004013B0 call _getchar
.text:004013B5 mov eax, 0
.text:004013BA leave
.text:004013BB retn
.text:004013BB _main endp
.text:004013BB
.text:004013BB
; --------------------------------------------------
Теперь
попробуем упаковать файл. Программа для упаковки файлов upx307w. Упаковывают
файл в целях защиты от реверса и обеспечения безопасности кода. После
упаковки мы увидим, что код станет сложно анализировать. Используя
командную строку введем следующую команду.
Теперь
попробуем упаковать файл. Программа для упаковки файлов upx307w.
Упаковывают
файл в целях защиты от реверса и обеспечения безопасности кода.
После
упаковки мы увидим, что код станет сложно анализировать.
Используя
командную строку введем следующую команду
C:\Users\Lisa-Alisa>C:\Users\Lisa-Alisa\Documents\upx307w\upx.exe
C:\Users\Lisa-
Alisa\Desktop\example.exe
Ultimate Packer for
eXecutables
Copyright (C) 1996 -
2010
UPX 3.07w Markus Oberhumer, Laszlo Molnar &
John Reiser Sep 08th 2010
File size Ratio Format Name
-------------------- ------ -----------
-----------
71233 -> 45121 63.34%
win32/pe example.exe
Packed 1 file.
Теперь
наш файл упакован.
Посмотрим
теперь как изменился файл с помощью PeiD. Мы видим, что программа упакована UPX упаковщиком.
Здесь
мы тоже видим, что наш код запакован и обфусцирован и не читаем.
РЕЗЮМЕ
Программа
на языке С состоит из одного или большего числа функций С. Каждая программа на
С должна содержать функцию с именем main ( ) , поскольку именно эта функция
вызывается в момент запуска программы. Простая функция состоит из заголовка, за
которым следует открывающая фигурная скобка, далее следуют операторы,
образующие тело функции, за которой следует завершающая или закрывающая
фигурная скобка.
Каждый
оператор языка С является инструкцией компьютеру и обязательно заканчивается
точкой с запятой. Оператор объявления назначает переменной имя и определяет тип
данных, которые будет храниться в этой переменной. Имя переменной может служить
примером идентификатора. Оператор присваивания устанавливает значение
переменной или используя более общий термин, выделяет ей пространство в памяти.
Вызов функции запускает на выполнение функцию, имя которой указано в
обращении.
По выполнении вызванной функции программа переходит к выполнению оператора,
следующего за вызовом функции.
Функция
p r i n t f ( ) может использоваться для печати фраз и значений переменных.
Синтаксис языка - это набор правил, определяющий способ , посредством которого
допустимые операторы в этом языке группируются в единую последовательность.
Семантика
оператора есть его смысловое значение. Компилятор позволяет вам обнаруживать
синтаксические ошибки, однако синтаксические ошибки проявляются в программе
лишь после того, как эта программа будет откомпилирована . Обнаружение
семантических ошибок предусматривает мониторинг состояния программы, то есть,
значений всех переменных на каждом шаге выполнения программы. И, наконец,
ключевые слова образуют словарь языка С .