┌──┌─┐┌──
──┘├─┘──┘ 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