В девяностых годах двадцатого века, когда вирусы создавались преимущество на ассемблере и писались преимущественно профессионалами, а коммерческие программисты в своем подавляющем большинстве полностью отказались от ассемблера и перешли на языки высокого уровня, для разработчиков антивирусов наступили золотые дни, ибо распознать зараженный файл зачастую удавалось с одного взгляда. Действительно, любая нормально откомпилированная программа начинается с так называемого стартового кода (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 |
| .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 |
| .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 |