- •Основы исследование безопасности программного обеспечения
- •Оглавление
- •Обращение к читателю
- •Введение
- •С чего начать
- •Инструменты исследователя
- •Установка исследуемой программы
- •Типовая защита «Запрос пароля» или «Запрос ввода регистрационного номера»
- •Исследование типовой защиты в отладчике OllyDbg
- •Исследование типовой защиты в дизассемблере ida Pro
- •Типовая защита «Запрос пароля» или «Запрос ввода регистрационного номера» с шифрованием сообщения
- •Технологии защиты программного кода от исследования и отладки
- •Как не надо проектировать защиту программ
- •Как обмануть отладчик и дизассемблер
- •Приложение 1. Ассемблер в Windows
- •О стеке
- •Команды передачи управления
- •Литература для дополнительного изучения
- •Приложение 2. Отладчик OllyDbg
- •Откуда скачать программу
- •Установка и настройка программы
- •Дизассемблированный код (листинг)
- •Регистры
- •Наиболее полезные клавиши в OllyDbg
- •Подключение плагинов в OllyDbg
- •Встроенный язык ida Pro
- •Плагины ida Pro
- •Литература для дополнительного изучения
- •Задание для самостоятельного выполнения
- •Список используемой литературы
Как обмануть отладчик и дизассемблер
Что может помешать дизассемблеру4:
-
непонятные вызовы функций, например, call [ebp+022h];
-
зашифрованные куски кода.
Что может помешать отладчику:
-
изменение работы программы в случае ее выполнения в отладчике;
-
определение отладчика до запуска программы с последующим завершением. Функция IsDebuggerPresent вернет в регистре EAX значение 0FFFFFFFFh, если в системе нет отладчика – такую проверку можно осуществить несколько раз.
Использование виртуальной машины. В саму программу внедряются специальные команды, которые могут обрабатываться только специальным интерпретатором – виртуальной машиной. Отладчик и дизассемблер не знают специализированных команд.
Приложение 1. Ассемблер в Windows
Создадим новый консольный проект в Visual Studio 2010.
Рис. Создание консольного приложения в Visual Studio 2010
Рис. Выбор параметров консольного приложения в Visual Studio 2010
Рис. Создание нового файла в проекте
Рис. Создание файла на языке С++
Листинг 1:
#include <stdio.h>
int a;
void main()
{
a=10;
__asm {
add a,10
sub a,2
}
printf("%d ",a);
}
Запускаем программу на выполнение.
Рис. Результат выполнения программы
Основная нагрузка при работе компьютера ложится на два устройства: процессор и память. Процессор выполняет команды, хранящиеся в памяти. В памяти также хранятся данные. Между процессором и памятью происходит непрерывный обмен информацией. Процессор имеет свою небольшую память, состоящую из регистров. Команда процессора, использующая находящиеся в регистрах данные, выполняется много быстрее аналогичных команд над данными в памяти, поэтому часто данные помещают в регистры. Результат команды можно поместить обратно в память.
Для процессора вся память представляет собой последовательность однобайтовых ячеек, каждая из которых имеет свой адрес.
Листинг 2:
#include <stdio.h>
#include <Windows.h>
//пересылка данных
//переменные
DWORD a,b,c,d,e,f,g,h;
void main()
{
f=200;
__asm {
//непосредственная передача
mov a,100
mov eax,100
mov b,eax
//использование косвенной адресации
lea ebx,c
mov dword ptr [ebx],eax
//использование стека
//содержимое eax оказывается в e, число 100 в d
push eax
push 100
pop d
pop e
//команда xchg (обмен содержимым операндов),
//содержимое f переносится в g
xchg eax,f
xchg g,eax
//сложение с предварительным обменом операндов
mov ebx,50
mov eax,20
xadd eax,ebx
mov dword ptr h,ebx
//теперь сумма в eax, а число 20 в h
}
printf("%u %u %u %u %u %u %u %u\n", a,b,c,d,e,f,g,h);
}
Рис. Результат работы программы
Рассмотрим Листинг 2. Заголовочный файл windows.h содержит определение типов переменных, в частности, там содержится определение типа DWORD, которое сводится просто к unsigned int.
В ассемблерных командах мы используем переменные, определенные средствами языка С, т.к. встроенный в С ассемблер не позволяет осуществлять резервирование памяти. Адресация памяти с помощью переменных – прямая адресация.
MOV является командой пересылки данных, с помощью этой команды можно обмениваться данными между регистрами, непосредственно передавать данные в регистр или ячейку памяти.
В программе используется косвенная адресация. Если адрес ячейки памяти содержится в регистре, например, в EDX, то, для того чтобы послать туда число, нужно записать MOV BYTE PTR [EDX],100. BYTE PTR означает, что в операции участвует однобайтовая ячейка памяти (WORD PTR – слово, DWORD PTR – для двойного слова).
Команда PUSH помещает в стек четырехбайтовое число, команда POP достает число оттуда. Стек построен по принципу «первым пришел – последним ушел», т.е. в переменной e окажется число из EAX, а в d – число 100.
Команда LEA используется, чтобы получить адрес переменной.
Листинг 3:
#include <stdio.h>
#include <Windows.h>
BYTE ar[6] = {1,12,128,50,200,10};
BYTE a,b,c,d;
WORD e;
DWORD f;
void main()
{
__asm {
mov al,byte ptr ar
mov a,al
lea ebx,ar
mov al,byte ptr [ebx]
mov b,al
mov al,byte ptr [ebx]+3
mov c,al
mov edx,1
mov al,byte ptr [ebx][edx]+2
mov d,al
mov ax,word ptr [ebx]+2
mov e,ax
mov eax,dword ptr [ebx]+2
mov f,eax
};
printf("%u %u %u %u %u %u\n",a,b,c,d,e,f);
}
Рис. Результат работы программы
Листинг 3 посвящен способам адресации, пример построен на косвенной адресации: [EBX]+2, т.е. результирующий адрес получается путем сложения адреса, хранящегося в регистре EBX, и числа 2.
В случае [EBX][EDX]+2 (или [EBX + EDX + 2]) результирующий адрес получается путем сложения содержимого регистра EBX, регистра EDX и числа 2. Это называется адресацией по базе с индексированием и со сдвигом.