Файл: Изучение методов детектирования ВПО, методов противодействия детектированию ВПО и создание программы для анализа эффективности антивирусов, применяющих те или иные подходы к обнаружению угроз.pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 30.06.2023

Просмотров: 82

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

pe_base::import_rebuilder_settings settings;

//Original import address table нам не нужна (пояснения ниже)

settings.build_original_iat(false);

//Будем переписывать IAT именно по тому адресу,

//которому указали (load_library_address_rva)

settings.save_iat_and_original_iat_rvas(true, true);

//Расположим импорты прямо за концом упакованных данных

settings.set_offset_from_section_start(added_section.get_raw_data().size());

//Пересоберем импорты

image.rebuild_imports(imports, added_section, settings);

}

{

//Новая секция

pe_base::section unpacker_section;

//Имя - exsite

unpacker_section.set_name("exsite");

//Доступна на чтение и исполнение

unpacker_section.readable(true).executable(true);

{

//Получаем ссылку на данные секции распаковщика

std::string& unpacker_section_data = unpacker_section.get_raw_data();

//Записываем туда код распаковщика

//Этот код хранится в авто файле

//unpacker.h, который мы подключили в main.cpp

unpacker_section_data = std::string(reinterpret_cast<const char*>(unpacker_data), sizeof(unpacker_data));

//Записываем по нужным смещениям адрес

//загрузки образа

*reinterpret_cast<DWORD*>(&unpacker_section_data[original_image_base_offset]) = image.get_image_base_32();

//и виртуальный адрес самой первой секции упакованного файла,

//в которой лежат данные для распаковки и информация о них

//В самом начале это секции, как вы помните, лежит

//структура packed_file_info

*reinterpret_cast<DWORD*>(&unpacker_section_data[rva_of_first_section_offset]) = image.get_image_sections().at(0).get_virtual_address();

}

//Добавляем и эту секцию

const pe_base::section& unpacker_added_section = image.add_section(unpacker_section);

//Выставляем новую точку входа - теперь она указывает

//на распаковщик, на самое его начало

image.set_ep(image.rva_from_section_offset(unpacker_added_section, 0));

}

//Удалим все часто используемые директории

image.remove_directory(IMAGE_DIRECTORY_ENTRY_BASERELOC);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_EXPORT);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_IAT);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_RESOURCE);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_SECURITY);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_TLS);

image.remove_directory(IMAGE_DIRECTORY_ENTRY_DEBUG);

//Урезаем таблицу директорий, удаляя все нулевые

//Урезаем не полностью, а минимум до 12 элементов, так как в оригинальном

//файле могут присутствовать первые 12 и использоваться

image.strip_data_directories(16 - 4);

//Удаляем стаб из заголовка, если какой-то был

image.strip_stub_overlay();

//Создаем новый PE-файл

//Вычислим имя переданного нам файла без директории

std::string base_file_name(argv[1]);

std::string dir_name;

std::string::size_type slash_pos;

if((slash_pos = base_file_name.find_last_of("/\\")) !=

std::string::npos)

{

dir_name = base_file_name.substr(0, slash_pos + 1); //Директория исходного файла

base_file_name = base_file_name.substr(slash_pos + 1); //Имя исходного файла


}

base_file_name = dir_name + "packed_" + base_file_name;

std::ofstream new_pe_file(base_file_name.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);

if(!new_pe_file)

{

//Если не удалось создать файл - выведем ошибку

std::cout << "Cannot create " << base_file_name << std::endl;

return -1;

}

//Пересобираем PE-образ

//Урезаем DOS-заголовок, накладывая на него NT-заголовки

//(за это отвечает второй параметр true)

//Не пересчитываем SizeOfHeaders - за это отвечает третий параметр

image.rebuild_pe(new_pe_file, true, false);

std::cout << "Packed image was saved to " << base_file_name << std::endl;

}

catch(const pe_exception& e)

{

//Если по какой-то причине открыть его не удалось

std::cout << e.what() << std::endl;

return -1;

}

return 0;

}

Unpacker: unpacker.cpp

//Подключаем файл со структурами из проекта упаковщика

#include "../simple_pe_packer/structs.h"

//Алгоритм распаковки

#include "lzo_conf.h"

/* decompression */

LZO_EXTERN(int)

lzo1z_decompress ( const lzo_bytep src, lzo_uint src_len,

lzo_bytep dst, lzo_uintp dst_len,

lzo_voidp wrkmem /* NOT USED */ );

//Создадим функцию без пролога и эпилога

extern "C" void __declspec(naked) unpacker_main()

{

//Пролог вручную

__asm

{

push ebp;

mov ebp, esp;

sub esp, 256;

}

//Адрес загрузки образа

unsigned int original_image_base;

//Относительный адрес первой секции,

//в которую упаковщик кладет информацию для

//распаковщика и сами упакованные данные

unsigned int rva_of_first_section;

//Эти инструкции нужны только для того, чтобы

//заменить в сборщике распаковщика адреса на реальные__asm

{

mov original_image_base, 0x11111111;

mov rva_of_first_section, 0x22222222;

}

//Получаем указатель на структуру с информацией,

//которую для нас приготовил упаковщик

const packed_file_info* info;

//Она находится в самом начале

//первой секции упакованного файла

info = reinterpret_cast<const packed_file_info*>(original_image_base + rva_of_first_section);

//Два тайпдефа прототипов функций LoadLibraryA и GetProcAddress

typedef HMODULE (__stdcall* load_library_a_func)(const char* library_name);

typedef INT_PTR (__stdcall* get_proc_address_func)(HMODULE dll, const char* func_name);

//Считаем их адреса из структуры packed_file_info

//Их нам туда подложил загрузчик

load_library_a_func load_library_a;

get_proc_address_func get_proc_address;

load_library_a = reinterpret_cast<load_library_a_func>(info->load_library_a);

get_proc_address = reinterpret_cast<get_proc_address_func>(info->get_proc_address);

//Создаем буфер на стеке

char buf[32];

//kernel32.dll

*reinterpret_cast<DWORD*>(&buf[0]) = 'nrek';

*reinterpret_cast<DWORD*>(&buf[4]) = '23le';

*reinterpret_cast<DWORD*>(&buf[8]) = 'lld.';

*reinterpret_cast<DWORD*>(&buf[12]) = 0;

//Загружаем библиотеку kernel32.dll

HMODULE kernel32_dll;

kernel32_dll = load_library_a(buf);

//Тайпдеф прототипа функции VirtualAlloc

typedef LPVOID (__stdcall* virtual_alloc_func)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);

//Тайпдеф прототипа функции VirtualProtect


typedef LPVOID (__stdcall* virtual_protect_func)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);

//Тайпдеф прототипа функции VirtualFree

typedef LPVOID (__stdcall* virtual_free_func)(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);

//VirtualAlloc

*reinterpret_cast<DWORD*>(&buf[0]) = 'triV';

*reinterpret_cast<DWORD*>(&buf[4]) = 'Alau';

*reinterpret_cast<DWORD*>(&buf[8]) = 'coll';

*reinterpret_cast<DWORD*>(&buf[12]) = 0;

//Получаем адрес функции VirtualAlloc

virtual_alloc_func virtual_alloc;

virtual_alloc = reinterpret_cast<virtual_alloc_func>(get_proc_address(kernel32_dll, buf));

//VirtualProtect

*reinterpret_cast<DWORD*>(&buf[0]) = 'triV';

*reinterpret_cast<DWORD*>(&buf[4]) = 'Plau';

*reinterpret_cast<DWORD*>(&buf[8]) = 'etor';

*reinterpret_cast<DWORD*>(&buf[12]) = 'tc';

//Получаем адрес функции VirtualProtect

virtual_protect_func virtual_protect;

virtual_protect = reinterpret_cast<virtual_protect_func>(get_proc_address(kernel32_dll, buf));

//VirtualFree

*reinterpret_cast<DWORD*>(&buf[0]) = 'triV';

*reinterpret_cast<DWORD*>(&buf[4]) = 'Flau';

*reinterpret_cast<DWORD*>(&buf[8]) = 'eer';

//Получаем адрес функции VirtualFree

virtual_free_func virtual_free;

virtual_free = reinterpret_cast<virtual_free_func>(get_proc_address(kernel32_dll, buf));

//Относительный виртуальный адрес директории импорта

DWORD original_import_directory_rva;

//Виртуальный размер директории импорта

DWORD original_import_directory_size;

//Оригинальная точка входа

DWORD original_entry_point;

//Общий размер всех секций файла

DWORD total_virtual_size_of_sections;

//Количество секций в оригинальном файле

BYTE number_of_sections;

//Копируем эти значения из структуры packed_file_info,

//которую для нас записал упаковщик

original_import_directory_rva = info->original_import_directory_rva;

original_import_directory_size = info->original_import_directory_size;

original_entry_point = info->original_entry_point;

total_virtual_size_of_sections = info->total_virtual_size_of_sections;

number_of_sections = info->number_of_sections;

//Указатель на память, в которую

//мы запишем распакованные данные

LPVOID unpacked_mem;

//Выделяем память

unpacked_mem = virtual_alloc(

0,

info->size_of_unpacked_data,

MEM_COMMIT,

PAGE_READWRITE);

//Выходной размер распакованных данных

lzo_uint out_len;

out_len = 0;

//Производим распаковку алгоритмом LZO

lzo1z_decompress(

reinterpret_cast<const unsigned char*>(reinterpret_cast<DWORD>(info) + sizeof(packed_file_info)),

info->size_of_packed_data,

reinterpret_cast<unsigned char*>(unpacked_mem),

&out_len,

0);

//Указатель на DOS-заголовок файла

const IMAGE_DOS_HEADER* dos_header;

//Указатель на файловый заголовок

IMAGE_FILE_HEADER* file_header;

//Виртуальный адрес начала заголовков секций

DWORD offset_to_section_headers;

//Просчитываем этот адрес

dos_header = reinterpret_cast<const IMAGE_DOS_HEADER*>(original_image_base);

file_header = reinterpret_cast<IMAGE_FILE_HEADER*>(original_image_base + dos_header->e_lfanew + sizeof(DWORD));

//Вот по такой формуле

offset_to_section_headers = original_image_base + dos_header->e_lfanew + file_header->SizeOfOptionalHeader

+ sizeof(IMAGE_FILE_HEADER) + sizeof(DWORD) /* Signature */;

//Обнулим всю память первой секции


//эта область соответствует области памяти, которую

//в оригинальном файле занимают все секции

memset(

reinterpret_cast<void*>(original_image_base + rva_of_first_section),

0,

total_virtual_size_of_sections - rva_of_first_section);

//Изменим атрибуты блока памяти, в котором

//расположены заголовки PE-файла и секций

//Нам необходим доступ на запись

DWORD old_protect;

virtual_protect(reinterpret_cast<LPVOID>(offset_to_section_headers),

number_of_sections * sizeof(IMAGE_SECTION_HEADER),

PAGE_READWRITE, &old_protect);

//Теперь изменим количество секций

//в заголовке PE-файла на оригинальное

file_header->NumberOfSections = number_of_sections;

//Виртуальный адрес структуры заголовка секции

DWORD current_section_structure_pos;

current_section_structure_pos = offset_to_section_headers;

//Перечислим все секции

for(int i = 0; i != number_of_sections; ++i)

{

//Создаем структуру заголовка секции

IMAGE_SECTION_HEADER section_header;

//Обнуляем структуру

memset(&section_header, 0, sizeof(section_header));

//Заполняем важные поля:

//Характеристики

section_header.Characteristics = (reinterpret_cast<packed_section*>(unpacked_mem) + i)->characteristics;

//Смещение файловых данных

section_header.PointerToRawData = (reinterpret_cast<packed_section*>(unpacked_mem) + i)->pointer_to_raw_data;

//Размер файловых данных

section_header.SizeOfRawData = (reinterpret_cast<packed_section*>(unpacked_mem) + i)->size_of_raw_data;

//Относительный виртуальный адрес секции

section_header.VirtualAddress = (reinterpret_cast<packed_section*>(unpacked_mem) + i)->virtual_address;

//Виртуальный размер секции

section_header.Misc.VirtualSize = (reinterpret_cast<packed_section*>(unpacked_mem) + i)->virtual_size;

//Копируем оригинальное имя секции

memcpy(section_header.Name, (reinterpret_cast<packed_section*>(unpacked_mem) + i)->name, sizeof(section_header.Name));

//Копируем заполненный заголовок

//в память, где находятся заголовки секций

memcpy(reinterpret_cast<void*>(current_section_structure_pos), &section_header, sizeof(section_header));

//Перемещаем указатель на следующий заголовок секции

current_section_structure_pos += sizeof(section_header);

}

//Указатель на сырые данные секции

//Необходим для восстановления сжатых данных секций

//и расположения их по нужным местам

DWORD current_raw_data_ptr;

current_raw_data_ptr = 0;

//Восстановим указатель на заголовки секций

current_section_structure_pos = offset_to_section_headers;

//Снова перечисляем все секции

for(int i = 0; i != number_of_sections; ++i)

{

//Заголовок секции, который мы только что сами записали

const IMAGE_SECTION_HEADER* section_header = reinterpret_cast<const IMAGE_SECTION_HEADER*>(current_section_structure_pos);

//Копируем данные секции в то место памяти,

//где они должны располагаться

memcpy(reinterpret_cast<void*>(original_image_base + section_header->VirtualAddress),

reinterpret_cast<char*>(unpacked_mem) + number_of_sections * sizeof(packed_section) + current_raw_data_ptr,

section_header->SizeOfRawData);

//Перемещаем указатель на данные секции

//в распакованном блоке данных


current_raw_data_ptr += section_header->SizeOfRawData;

//Переходим к следующему заголовку секции

current_section_structure_pos += sizeof(IMAGE_SECTION_HEADER);

import_dir = reinterpret_cast<IMAGE_DATA_DIRECTORY*>(offset_to_directories + sizeof(IMAGE_DATA_DIRECTORY) * IMAGE_DIRECTORY_ENTRY_IMPORT);

//Записываем значения размера и виртуального адреса в соответствующие поля

import_dir->Size = original_import_directory_size;

import_dir->VirtualAddress = original_import_directory_rva;

//Если у файла имеются импорты

if(original_import_directory_rva)

{

//Виртуальный адрес первого дескриптора

IMAGE_IMPORT_DESCRIPTOR* descr;

descr = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(original_import_directory_rva + original_image_base);

//Перечисляем все дескрипторы

//Последний - нулевой

while(descr->Name)

{

//Загружаем необходимую DLL

HMODULE dll;

dll = load_library_a(reinterpret_cast<char*>(descr->Name + original_image_base));

//Указатели на таблицу адресов и lookup-таблицу

DWORD* lookup, *address;

//Учтем, что lookup-таблицы может и не быть

lookup = reinterpret_cast<DWORD*>(original_image_base + (descr->OriginalFirstThunk ? descr->OriginalFirstThunk : descr->FirstThunk));

address = reinterpret_cast<DWORD*>(descr->FirstThunk + original_image_base);

//Перечисляем все импорты в дескрипторе

while(true)

{

//До первого нулевого элемента в лукап-таблице

DWORD lookup_value = *lookup;

if(!lookup_value)

break;

//Проверим, импортируется ли функция по ординалу

if(IMAGE_SNAP_BY_ORDINAL32(lookup_value))

*address = static_cast<DWORD>(get_proc_address(dll, reinterpret_cast<const char*>(lookup_value & ~IMAGE_ORDINAL_FLAG32)));

else

*address = static_cast<DWORD>(get_proc_address(dll, reinterpret_cast<const char*>(lookup_value + original_image_base + sizeof(WORD))));

//Переходим к следующему элементу

++lookup;

++address;

}

//Переходим к следующему дескриптору

++descr;

}

}

//Вернем атрибуты памяти заголовков, как было изначально

virtual_protect(reinterpret_cast<LPVOID>(offset_to_section_headers), number_of_sections * sizeof(IMAGE_SECTION_HEADER), old_protect, &old_protect);

//Эпилог вручную

_asm

{

//Переходим на оригинальную точку входа

mov eax, original_entry_point;

add eax, original_image_base;

leave;

//Вот так

jmp eax;

}

}

Converter: main.cpp

#include <pe_32_64.h> //Директивы для линкования с собранной библиотекой PE

#ifndef _M_X64

#ifdef _DEBUG

#pragma comment(lib, "../../Debug/pe_lib.lib")

#else

#pragma comment(lib, "../../Release/pe_lib.lib")

#endif

#else

#ifdef _DEBUG

#pragma comment(lib, "../../x64/Debug/pe_lib.lib")

#else

#pragma comment(lib, "../../x64/Release/pe_lib.lib")

#endif

#endif

int main(int argc, char* argv[])

{

//Подсказка по использованию

if(argc != 3)

{

std::cout << "Usage: unpacker_converter.exe unpacker.exe output.h" << std::endl;

return 0;

}

//Открываем файл

//Открываем файл unpacker.exe - его имя

//и путь к нему хранятся в массиве argv по индексу 1

std::ifstream file(argv[1], std::ios::in | std::ios::binary);

if(!file)

{

//Если открыть файл не удалось - сообщим и выйдем с ошибкой

std::cout << "Cannot open " << argv[1] << std::endl;