Вирусы и другой вредоносный код

       

Стартовый код


В девяностых годах двадцатого века, когда вирусы создавались преимущество на ассемблере и писались преимущественно профессионалами, а коммерческие программисты в своем подавляющем большинстве полностью отказались от ассемблера и перешли на языки высокого уровня, для разработчиков антивирусов наступили золотые дни, ибо распознать зараженный файл зачастую удавалось с одного взгляда. Действительно, любая нормально откомпилированная программа начинается с так называемого стартового кода (Start-Up code), который легко отождествить визуально (как правило, он начинается с вызова функций GetVersion, GetModuleHandleA и т. д.), а дизассемблер IDA и вовсе идентифицирует стартовый код по обширной библиотеке сигнатур, выдавая тип и версию компилятора. Ассемблерные же программы стартового кода лишены и потому, когда ассемблерный вирус внедряется в программу, написанную на языке высокого уровня, стартовый код отодвигается как бы "вглубь" файла , демаскируя тем самым факт своего заражения.

Сегодня, когда ассемблерные вирусы становятся музейной редкостью, такой способ отождествления мало-помалу перестает работать, однако до полного списывания в утиль дело еще далеко.

Строго говоря, никаких формальных признаков "нормального" start-up'a не существует, поэтому всяк разработчик волен реализовывать его по своему. Однако редкий разработчик цепляет к программе свой собственный start-up и все чаще для этих целей используется библиотечный стартовый код, поставляемый вместе с компилятором. Несмотря на то что даже в рамках одного-единственного компилятора существует множество разновидностей стартового кода, все они легко узнаваемы и факт отсутствия стартового кода надежно обнаруживается даже самыми начинающими из исследователей!

Приблизительная структура типичного стартового кода такова: сначала идет пролог, затем настройка обработчика структурных исключений (для Си++ программ), обнаруживающая себя по обращению к сегментному регистру FS. Далее следует вызов функций GetVersion (GetVersionEx), GetModuleHandleA и GetStartupInfoA.
Подробнее об идентификации стартового кода можно прочитать в книге "Фундаментальные основы хакерства" Криса Касперски или в "Hacker Disassembling Uncovered" его же.

Здесь же мы не можем позволить себе подробно останавливаться на этом обширном вопросе и просто сравним start-up код нормальной программы с кодом вируса Win2K.Inta.1676



.text:00401670       start proc   near
.text:00401670 push ebp
.text:00401671 mov ebp, esp
.text:00401673 push 0FFFFFFFFh
.text:00401675 push offset stru_420218
.text:0040167A push offset __except_handler3
.text:0040167F mov eax, large fs:0
.text:00401685 push eax
.text:00401686 mov large fs:0, esp
.text:00401696 call ds:GetVersion ; Get current version number of Windows
.text:004016EC push 0
.text:004016EE call __heap_init
.text:00401704 mov [ebp+var_4], 0
.text:0040170B call __ioinit
.text:00401710 call ds:GetCommandLineA
.text:00401716 mov dword_424F44, eax
.text:0040171B call ___crtGetEnvironmentStringsA
.text:00401720 mov dword_4235C0, eax
.text:00401725 call __setargv
.text:0040172A call __setenvp
.text:0040172F call __cinit
.text:00401754 call _main
.text:00401763 call _exit
Листинг 2 так выглядит нормальный start-up от MicrosoftVisualC++ 6.0…

.text:00011000     start     proc   near
.text:00011000 mov eax, [esp+arg_0]
.text:00011004 lea edx, loc_11129
.text:0001100A mov [eax+34h], edx
.text:0001100D lea edx, dword_117A0
.text:00011013 lea eax, aHh             ;   "HH"
.text:00011019 mov [edx+8], eax
.text:0001101C mov [eax+4], aSystemrootSyst
.text:0001101C               ;   "\\SystemRoot\\system32\\drivers\\inf.sys"
.text:00011023 push 1200h
.text:00011028 push 0
.text:0001102D call ExAllocatePool
.text:00011032 or eax, eax
.text:00011034 jnz short loc_1103E
.text:00011036 mov eax, 0C0000001h
.text:0001103B retn 8
<


Листинг 3 … а так выглядят окрестности точки входа вируса Win2K.Inta.1676

Смотрите! В то время как "хорошая" программа лениво опрашивает текущую версию операционной системы и иже с ней, зловредный вирус сломя голову несется в объятья драйвера inf.sys. Правильные программы так себя не ведут, и коварность вирусных планов разоблачается с первого взгляда!

Разумеется, отсутствие стартового кода еще не есть свидетельство вируса! Быть может, исследуемый файл был упакован или разработчик применил нестандартный компилятор или набор библиотек. Ну, с упаковкой мы уже разобрались, а с идентификацией компилятора поможет справиться IDA и наш личный опыт (замечание: текущие версии IDA PRO определяют версию компилятора непосредственно по стартовому коду: если же он отсутствует или был изменен, механизм распознавания дезактивируется, и поиском подходящей библиотеки сигнатур нам приходится заниматься вручную, через меню File -> Load file -> FLIRT signature file). И если обнаружиться, что нормальный start-up у файла все-таки есть, но выполнение программы начинается не с него, – шансы на присутствие вируса значительно возрастут!

Троянские программы, в большинстве своем написанные на языках высокого уровня, имеют вполне стандартный Start-Up и потому на такую наживку не обнаруживаются. Взять например того же Kilez'a, стартовый код которого выглядит так:

.text:00408458     start       proc   near
.text:00408458 push ebp          ;      sub_408458
.text:00408459 mov ebp, esp
.text:0040845B push 0FFFFFFFFh
.text:0040845D push offset stru_40D240
.text:00408462 push offset __except_handler3
.text:00408467 mov eax, large fs:0
.text:0040846D push eax
.text:0040846E mov large fs:0, esp
.text:0040847B mov [ebp+var_18], esp
.text:0040847E call ds:GetVersion      ;      Get current version number of Windows
.text:004084AF xor esi, esi
.text:004084B1 push esi
.text:004084B2 call __heap_init
.text:004084C4 mov [ebp+var_4], esi
.text:004084C7 call __ioinit
.text:004084CC call ds:GetCommandLineA
.text:004084D2 mov dword_494E68, eax
.text:004084D7 call ___crtGetEnvironmentStringsA
.text:004084DC mov dword_493920, eax
.text:004084E1 call __setargv
.text:004084E6 call __setenvp
.text:004084EB call __cinit
.text:004084F0 mov [ebp+StartupInfo.dwFlags], esi
.text:004084F3 lea eax, [ebp+StartupInfo]
.text:004084F6 push eax        ;      lpStartupInfo
.text:004084F7 call ds:GetStartupInfoA
.text:004084FD call __wincmdln
Листинг 4 червь I-Worm.Kilez.h имеет стандартный стартовый код

Даже "невооруженным" глазом видно, что стартовый код червя идентичен стартовому коду Microsoft Visual C++ 6.0, что и не удивительно, так именно на нем червь и написан.


Содержание раздела