┌──┌─┐┌──
──┘├─┘──┘ Presents
┐  ┌┐┐┌─┤ VMag, Issue 1, 1 June 1997
└─┘┘ ┘└─┘ ──────────────────────────

Ассемблерные трюки на основе анализа вирусов семейства Nutcracker.

        Вирусы  данного  семейства  являются  весьма  сложными  и
изощренными. По информации Е.В.Касперского  (автора  антивирусной
программы  AVP)  вирусы  подсемейства   Nutcracker.AB2   являются
вторыми по сложности в мире среди вирусов для PC,  и  третьими  -
среди  вирусов  вообще.  "Табель  о  рангах"  выглядит  следующим
образом (источник тот же):


1. Интернетовский червяк Роберта Т. Морриса (размер  около  60K).
   Написан студентном Корнеллского университета [США].

2. Полиморфические файловые вирусы  Zhengxi  (размер  более  7K).
   Создан  в России.  Автор, Д#### П##### FidoNet: 2:5030/###.##.

3. Полиморфические  файлово-загрузочные   вирусы  Nutcracker.AB2.
   Автор, предположительно, брестчанин.

4. Вирусы серии SSR. Автор - член SGWW по кличке Stainless Rat.

................


АССЕМБЛЕРНЫЕ ТРЮКИ ПО УМЕНЬШЕНИЮ ДЛИНЫ ВЫПОЛНЯЕМОГО КОДА.
---------------------------------------------------------

        Авторами вирусов всегда предпринимались и предпринимаются
попытки уменьшения выполняемого кода вируса  при  сохранении  или
увеличении его функциональных возможностей. Для этого применяются
различные изощренные алгоритмы функционирования, замена "длинных"
инструкции  аналогичными  более  короткими  и   т.д.   Рассмотрим
некоторые из этих приемов.

R  - регистр общего назначения.
RW - регистр общего назначения размером в слово.
RB - регистр общего назначения размером в байт.


а)   MOV AX,RW (длина 2 байта) - может быть заменена  на  команду
     XCHG AX,RW (длина 1 байт), если содержимое р-ра RW  для  нас
     некритично.

б)   ADD RW,2 (длина 3/4 байта) - на INC RW/INC RW (2 байта).

в)   INC  SI/INC  SI  либо  ADD  SI,2  на  команду  LODSW,   если
     содержимое р-ра AX некритично и Direction Flag(DF)=0.

г)   INC DI/INC DI - на команду SCASW (при DF=0).

д)   Все команды JMP NEAR по-возможности заменяются на JMP SHORT,
     т.к. первая имеет размер 3 байта, вторая  -  только  2  (для
     этого юзайте TASM с ключиком /M).

е)   Команды CMP R,0 (3 или 4 байта, кроме CMP AL,0 - 2 байта)  -
     на команды OR R,R; AND R,R; TEST R,R (2 байта).

ж)   Использование там, где это  возможно  знакового  флага  Sign
     Flag(SF).  Например,  команда:  TEST  BX,8000h  может   быть
     заменена на эквивалентную: TEST BX,BX, по ее окончании SF=0,
     если знаковый  бит  сброшен  или  SF=1,  если  знаковый  бит
     установлен. То же относится и команде DEC.

з)   Использование там,  где  это  возможно  команд  SAHF/LAHF  и
     PUSHF/POPF,  дабы  заново   не   произвидить   повторяющиеся
     "длинные" проверки.

и)   Использование замены сегмента в строковых  операциях.  Таким
     образом можно заменить используемый процессором по умолчанию
     регистр DS (но не ES!). И не надо никакой корректировки  DS.
     Например: REP MOVS BYTE PTR ES:[DI],CS:[SI]

к)   Используйте там, где это возможно содержимое флага  четности
     Parity Flag (PF). Это особенно полезно при работе с  битами.
     Например:  (пусть RW=3)
                TEST RW,RW лучше, чем TEST RW,3  (-1 байт)
                JNP  xxx              JZ   xxx

л)   Используйте,  неявно  устанавливаемые  процессором  значения
     регистров после выполнения некоторых команд. Например  после
     команд  LOOP([N]Z)  и  REP(N)E   MOV(LOD,SCA,STO,CMP)S(W,B),
     возможно  (в  зависимости  от  применяемого  вами   условия)
     значение р-ра CX будет равно 0.
     Пример: REP MOVSB
             INC CX    ;CX=1 (1 байт), вместо MOV CX,1 (3 байта).

м)   Там,  где  это  возможно,  меняйте  адресацию  с  прямой  на
     косвенную, но  только  в  случае,  когда  смещение  лежит  в
     диапазоне от (-80h)  до  (7Fh).
     Пример: (Пусть  DI=5)
             MOV BX,[DI+5] лучше, чем MOV BX,[000A] (-1 байт)
     (правда, в команде MOV это не касается  регистра  AX/AL,  но
     хуже от этого не будет ;-). Команда MOV BX,[DI] и вовсе дает
     выигрыш в 2 байта.

н)   Для обнуления регистра пользуйтесь командами XOR RW,RW;  SUB
     RW,RW (2 байта), а не MOV RW,0 (3/4 байта).

о)   Используйте JCXZ там, где это возможно.

п)   Вместо команд CMP RW,1 пользуйтесь командой DEC RW.
     Пример: DEC CX   лучше, чем CMP CX,1  (-1 байт).
             JNZ xxx             JNZ xxx
             ....                ....
        xxx: INC CX

р)   Используйте  то,  что  после  команд  AND/OR/XOR/TEST   флаг
     переноса или Carry Flag (CF) всегда равен 0.

с)   Там, где это возможно и  приемлимо  заменяйте  команды  CALL
     NEAR или INT xx, на команду INT 3. Это позволит вам  создать
     проблемы под большинством отладчиков и с экономит  2  или  1
     байт на каждой заме- не (только  не  забйдьте  устанавливать
     вектор  INT   3   на   нужный   адрес,   а   перед   выходом
     восстанавливать исходный ;-).

т)   Изменяйте напрямую код своей прграммы там, где это  возможно
     (только особенно не увлекайтесь и помните о конвейере, а  то
     ведь еще далеко не все работают на пентиумах ;-). Этот метод
     можно использовать для изменения адресов  переходов  по  JMP
     SHORT (или NEAR), CALL, команд условного перехода,  а  также
     для изменения значения непосредственно в команде, либо самой
     команды.
     Например: LODSB
               MOV  DATA1,AL
               .......
               CMP  CL,0
       DATA1   =    BYTE PTR $-1

     Либо:     MOV  OFST,ON   ;включить команду JMP SHORT
               .......
               jmp  short $+2
        OFST   =    BYTE PTR $-1
               .......
        ON     EQU  $-OFST-1  
               .......  ;теперь переход будет осуществляться сюда

у)   Используйте там, где это  возможно  команды
        CMP AX, ?
        MOV RW, ?
        CMP AL, ?
        MOV RB, ?
     вместо команд JMP SHORT. Как это делается?
     Пример: DEC  AX
             JNZ  xxx
             MOV  BL,4
             CMP  AX,0
             ORG  $-2   ;сдвигаем все на 2 байта вверх  (нолик  в
        xxx: MOV  BL,8  ;CMP убирается) 2  байта  (с  кодом  этой
                        ;команды будет сравниваться AX). Т.е. эта
                        ;команда будет пропущена
             ......
     (Экономия в 1 байт налицо ;-).

ф)   Используйте команду LEA.
     Например: LEA CX,[DI+1Ah] лучше, чем  MOV CX,DI  (-2 байта)
               ........                    ADD CX,1Ah
                                           ..........

x)   Вместо долгих и нудных SHL(R)'ей используйте SHL(R) R,CL или
     вовсе команды ROL(R).
     Пример: SHL AL,1  ;AL*64   лучше  MOV CL,6   или  ROR AL,1
             SHL AL,1                  SHL AL,CL       ROR AL,1
             SHL AL,1                  ........        ........
             SHL AL,1
             SHL AL,1
             SHL AL,1
             .......

ц)   Вместо команд MOV RW,0 (3 байта) используйте XOR RW,RW;  SUB
     RW,RW (2 байта).


        В  статье  Nut_AB2.Txt  представлен  анализ   конкретного
представителя вирусов семейства Nutcracker - Nutcracker.AB2.6982.

        Успешного программирования!

        Nice // SPS06.
        ■ 2:454/7.64@FidoNet