Статья Скрипты на Python как инструмент для анализа ПО

Всем привет!
В последнее время увлёкся написанием скриптов на Python и решил объединить это увлечение с интересом к ИБ. У меня возникла идея создать скрипт для анализа программного обеспечения и написать на эту тему статью для конкурса.

P.S. Я понимаю, что большинство постояльцев форума — профессионалы с огромным опытом, и моя статья может показаться слишком простой по сравнению с другими. Однако я все же решился опубликовать её, поскольку кого-то она может заинтересовать.


Введение.
Вредоносное ПО (или malware) представляет собой серьёзную угрозу для безопасности компьютерных систем. Анализ вредоносного ПО необходим для понимания его поведения, методов заражения и способов защиты. Python, благодаря своей простоте и богатому набору библиотек, становится всё более популярным инструментом для анализа вредоносного ПО. В этой статье я расскажу о методах анализа ПО с использованием Python. В качестве практического примера мы проведем анализ простого вредоносного скрипта на Python.

Создание демонстрационного скрипта.​

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

Python:
import os

def create_file():
    print("Creating malicious file...")
    with open("malicious_file.txt", "w") as file:
        file.write("This is a malicious file.\n")
        file.write("Your system has been compromised.\n")
    print("File created.")

def read_file():
    print("Reading malicious file...")
    if os.path.exists("malicious_file.txt"):
        with open("malicious_file.txt", "r") as file:
            content = file.read()
            print("File content:\n" + content)

def delete_file():
    print("Deleting malicious file...")
    if os.path.exists("malicious_file.txt"):
        os.remove("malicious_file.txt")
    print("File deleted.")

if __name__ == "__main__":
    create_file()
    read_file()
    delete_file()

Преобразуем скрипт в исполняемый (exe) файл.

Bash:
pyinstaller --onefile malicious_script.py

Краткое описание методов анализа.

Анализ вредоносного ПО можно разделить на два основных типа: статический и динамический анализ.
  1. Статический анализ включает изучение исходного кода или бинарного файла без его выполнения. Это позволяет выявить структуру программы, используемые библиотеки, вредоносные функции и т.д.
  2. Динамический анализ предполагает выполнение программы в контролируемой среде для наблюдения за её поведением. Это помогает выявить изменения в системе, сетевую активность, взаимодействие с файловой системой и т.д.

Статический анализ.

Чтобы написать скрипт для статического анализа, воспользуемся библиотекой pefile для анализа PE-файлов.

Python:
import pefile

def analyze_pe(filename):
    pe = pefile.PE(filename)

    print("DOS Header:")
    print(pe.DOS_HEADER)

    print("\nPE Sections:")
    for section in pe.sections:
        print(section.Name.decode().strip(), hex(section.VirtualAddress), hex(section.Misc_VirtualSize), section.SizeOfRawData)

    print("\nImported DLLs and functions:")
    for entry in pe.DIRECTORY_ENTRY_IMPORT:
        print(entry.dll.decode())
        for imp in entry.imports:
            print('\t', imp.name.decode() if imp.name else None)

if __name__ == "__main__":
    filename = 'dist/simple_malware.exe'
    analyze_pe(filename)

Python:
def analyze_pe(filename):
    pe = pefile.PE(filename)
Эта функция принимает в качестве аргумента имя файла и загружает его как PE-файл

Python:
print("DOS Header:")
print(pe.DOS_HEADER)
Эта часть скрипта выводит на экран заголовок DOS, который является первым компонентом PE-файла. Заголовок DOS содержит информацию, которая помогает понять, как файл загружается и выполняется.

Python:
print("\nPE Sections:")
for section in pe.sections:
    print(section.Name.decode().strip(), hex(section.VirtualAddress), hex(section.Misc_VirtualSize), section.SizeOfRawData)
Эта часть скрипта выводит информацию о секциях PE-файла. Каждая секция содержит различный тип данных, например, код или ресурсы. Скрипт выводит имя секции, виртуальный адрес, виртуальный размер и размер данных на диске.

Python:
print("\nImported DLLs and functions:")
for entry in pe.DIRECTORY_ENTRY_IMPORT:
    print(entry.dll.decode())
    for imp in entry.imports:
        print('\t', imp.name.decode() if imp.name else None)
Эта часть скрипта выводит информацию о DLL-файлах и функциях, которые импортируются PE-файлом. Она перечисляет все DLL, которые файл использует, и функции, импортируемые из этих DLL. Это важно для понимания, какие внешние библиотеки и функции требуются для выполнения файла.

Результаты статического анализа:

analyze1.png
analyze2.png
analyze3.png

Bash:
DOS Header:
[IMAGE_DOS_HEADER]
0x0        0x0   e_magic:                       0x5A4D
0x2        0x2   e_cblp:                        0x90
0x4        0x4   e_cp:                          0x3
0x6        0x6   e_crlc:                        0x0
0x8        0x8   e_cparhdr:                     0x4
0xA        0xA   e_minalloc:                    0x0
0xC        0xC   e_maxalloc:                    0xFFFF
0xE        0xE   e_ss:                          0x0
0x10       0x10  e_sp:                          0xB8
0x12       0x12  e_csum:                        0x0
0x14       0x14  e_ip:                          0x0
0x16       0x16  e_cs:                          0x0
0x18       0x18  e_lfarlc:                      0x40
0x1A       0x1A  e_ovno:                        0x0
0x1C       0x1C  e_res:
0x24       0x24  e_oemid:                       0x0
0x26       0x26  e_oeminfo:                     0x0
0x28       0x28  e_res2:
0x3C       0x3C  e_lfanew:                      0x100


PE Sections:
.text    0x1000 0x2b150 176640
.rdata   0x2d000 0x12c26 77312
.data    0x40000 0x33b8 3584
.pdata   0x44000 0x2364 9216
_RDATA   0x47000 0x1f4 512
.rsrc    0x48000 0xef8c 61440
.reloc   0x57000 0x75c 2048


Imported DLLs and functions:
USER32.dll
         GetWindowThreadProcessId
         ShowWindow
KERNEL32.dll
         CreateFileW
         GetFinalPathNameByHandleW
         CloseHandle
         GetModuleFileNameW
         CreateSymbolicLinkW
         GetProcAddress
         GetCommandLineW
         GetEnvironmentVariableW
         SetEnvironmentVariableW
         ExpandEnvironmentStringsW
         CreateDirectoryW
         GetTempPathW
         WaitForSingleObject
         Sleep
         SetDllDirectoryW
         CreateProcessW
         GetStartupInfoW
         FreeLibrary
         LoadLibraryExW
         SetConsoleCtrlHandler
         FindClose
         FindFirstFileExW
         GetCurrentProcess
         GetCurrentProcessId
         LocalFree
         FormatMessageW
         MultiByteToWideChar
         WideCharToMultiByte
         GetConsoleWindow
         HeapSize
         GetLastError
         WriteConsoleW
         SetEndOfFile
         GetExitCodeProcess
         TlsGetValue
         RtlCaptureContext
         RtlLookupFunctionEntry
         RtlVirtualUnwind
         UnhandledExceptionFilter
         SetUnhandledExceptionFilter
         TerminateProcess
         IsProcessorFeaturePresent
         QueryPerformanceCounter
         GetCurrentThreadId
         GetSystemTimeAsFileTime
         InitializeSListHead
         IsDebuggerPresent
         GetModuleHandleW
         RtlUnwindEx
         SetLastError
         EnterCriticalSection
         LeaveCriticalSection
         DeleteCriticalSection
         InitializeCriticalSectionAndSpinCount
         TlsAlloc
         TlsSetValue
         TlsFree
         EncodePointer
         RaiseException
         RtlPcToFileHeader
         GetCommandLineA
         GetDriveTypeW
         GetFileInformationByHandle
         GetFileType
         PeekNamedPipe
         SystemTimeToTzSpecificLocalTime
         FileTimeToSystemTime
         GetFullPathNameW
         RemoveDirectoryW
         FindNextFileW
         SetStdHandle
         DeleteFileW
         ReadFile
         GetStdHandle
         WriteFile
         ExitProcess
         GetModuleHandleExW
         HeapFree
         GetConsoleMode
         ReadConsoleW
         SetFilePointerEx
         GetConsoleOutputCP
         GetFileSizeEx
         HeapAlloc
         FlsAlloc
         FlsGetValue
         FlsSetValue
         FlsFree
         CompareStringW
         LCMapStringW
         GetCurrentDirectoryW
         FlushFileBuffers
         HeapReAlloc
         GetFileAttributesExW
         GetStringTypeW
         IsValidCodePage
         GetACP
         GetOEMCP
         GetCPInfo
         GetEnvironmentStringsW
         FreeEnvironmentStringsW
         GetProcessHeap
         GetTimeZoneInformation
ADVAPI32.dll
         ConvertSidToStringSidW
         GetTokenInformation
         OpenProcessToken
         ConvertStringSecurityDescriptorToSecurityDescriptorW

Подозрительные функции:

  • CreateFileW и WriteFile: Используются для создания и записи в файлы. Вредоносное ПО может использовать эти функции для записи своих компонентов на диск.
  • CreateProcessW: Используется для создания новых процессов. Вредоносное ПО может использовать эту функцию для запуска дополнительных вредоносных компонентов.
  • GetProcAddress и LoadLibraryExW: Эти функции позволяют динамически загружать библиотеки и получать адреса функций, что может использоваться для обхода защитных механизмов.
  • VirtualAlloc и WriteProcessMemory: Используются для выделения памяти и записи в память других процессов, что может быть признаком внедрения кода (инъекции).
  • IsDebuggerPresent: Используется для проверки наличия отладчика, что часто используется вредоносным ПО для избегания анализа.
  • OpenProcessToken и AdjustTokenPrivileges: Используются для управления токенами безопасности процессов, что может указывать на попытки повышения привилегий.
С полным перечнем функций Windows API можно ознакомиться , а пока двигаемся дальше.

Динамический анализ.

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

Python:
import subprocess

def run_and_monitor(filename):
    proc = subprocess.Popen([filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        stdout, stderr = proc.communicate(timeout=10)
        print("STDOUT:\n", stdout.decode('utf-8', errors='ignore'))
        print("STDERR:\n", stderr.decode('utf-8', errors='ignore'))
    except subprocess.TimeoutExpired:
        proc.kill()
        print("Execution timed out")

if __name__ == "__main__":
    filename = 'dist/malicious_script.exe'
    run_and_monitor(filename)

Python:
def run_and_monitor(filename):
    proc = subprocess.Popen([filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Эта функция принимает имя файла (filename) и запускает его как новый процесс с помощью subprocess.Popen. Параметры stdout=subprocess.PIPE и stderr=subprocess.PIPE указывают, что нужно захватывать стандартный вывод и поток ошибок процесса.

Python:
try:
    stdout, stderr = proc.communicate(timeout=10)
    print("STDOUT:\n", stdout.decode('utf-8', errors='ignore'))
    print("STDERR:\n", stderr.decode('utf-8', errors='ignore'))
except subprocess.TimeoutExpired:
    proc.kill()
    print("Execution timed out")
  • proc.communicate(timeout=10): Эта строка ждет до 10 секунд, пока процесс завершится, и захватывает его стандартный вывод и поток ошибок. Если процесс не завершится за 10 секунд, выдаст ошибку TimeoutExpired.
  • print("STDOUT:\n", stdout.decode('utf-8', errors='ignore')): Выводит стандартный вывод процесса, декодируя его из байтов в строку.
  • print("STDERR:\n", stderr.decode('utf-8', errors='ignore')): Выводит поток ошибок процесса, декодируя его из байтов в строку.
  • proc.kill(): Если процесс не завершился в течение 10 секунд, он будет принудительно завершен.

Результаты динамического анализа:

monitor.png

Bash:
STDOUT:
 Creating malicious file...
File created.
Reading malicious file...
File content:
This is a malicious file.
Your system has been compromised.


Deleting malicious file...
File deleted.


STDERR:

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

Заключение.​

В этой статье я поделился своим опытом создания и анализа вредоносного ПО на Python. Мы рассмотрели процесс написания вредоносного скрипта, его преобразования в исполняемый файл, а также методы статического и динамического анализа. Функционал скриптов для анализа можно разносторонне расширять (добавить функции мониторинга системных ресурсов, создать список из подозрительных функций и т.п.) Надеюсь, эта статья будет кому-то интересна, а так-же поможет лучше понять принципы работы и анализа ПО.
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!