Путеводитель по написанию вирусов: 12. Оптимизация

Дата публикации 3 сен 2002

Путеводитель по написанию вирусов: 12. Оптимизация — Архив WASM.RU

Есть два вида оптимизации: структурная и локальная. В этой маленькой главе я затрону оба эти вида. Hо, во-первых, вы должны понять одну вещь: никогда не оптимизируйте ваш код, пока ваш код не будет полностью работать. Если вы начнете оптимизировать код, который не работает, вы наворотите еще больше ошибок.

Структурная оптимизация

Это самая эффективная и самая сложная для воплощения и понимания. Этот вид оптимизации можно легко понять, используя бумагу для зарисовки алгоритма вашего вируса. Здесь у нас нет, поэтому давайте представим, что вы, вернее ваш вирус, сначала открываете файл только для чтения, закрываете, открываете снова для чтения/записи и закрываете снова. Все это - потеря байтов. При структурной оптимизации вы должны хорошо продумать, что должен делать ваш вирус и что нет, дабы не тратить место попусту. Решение будет отличаться в зависимости от конкретной ситуации.

Локальная оптимизация

Это самый простой вид, хотя и с его помощью можно сэкономить множество байтов. Метод состоит в изменении некоторых линий на другие, которые делают то же самое, но занимают меньшее количество байт.

¤ Очищение регистров:

Код (Text):
  1.  
  2.         mov     bx,0000h                ; 3 байта
  3.         xor     bx,bx                   ; 2 байта
  4.         sub     bx,bx                   ; 2 байта

Hикогда не используйте первый способ, а используйте второй или третий. Один регистр (DX) можно очищать еще одним способом. Давайте посмотрим:

Код (Text):
  1.  
  2.         mov     dx,0000h                ; 3 байта
  3.         xor     dx,dx                   ; 2 байта
  4.         sub     dx,dx                   ; 2 байта
  5.         cwd                             ; Конвертируем слово в двойное слово
  6.                                         ; (1 байт)

CWD будет работать, только если содержимое AX меньше, чем 8000h. Есть еще одни путь очистить AH за один байт: если AL < 80h, вы можете использовать инструкцию CBW.

¤ Сравнения:

Для сравнения, как известно, существует специальная инструкция. Для сравнения двух регистров вы можете использовать два способа:

Код (Text):
  1.  
  2.         cmp     ax,bx                   ; 2 байта
  3.         xor     ax,bx                   ; 2 байта

Hо XOR мы можем использовать только, если мы хотим узнать, равны ли значения. Тем не менее, мы можем использовать XOR вместо CMP, чтобы сохранить байт, если сравниваем регистр с числом:

Код (Text):
  1.  
  2.         cmp     ax,0666h                ; 3 байта
  3.         xor     ax,0666h                ; 2 байта

Но в силу природы инструкции XOR мы не можем использовать ее для того, чтобы узнать, пусть ли регистр. Здесь нас спасет OR...

Код (Text):
  1.  
  2.         cmp     ax,0000h                ; 3 байта
  3.         or      ax,ax                   ; 2 байта
¤ Оптимизированный регистр - AX:

Вы можете использовать его для сравнений:

Код (Text):
  1.  
  2.         cmp     bx,0666h                ; 4 байта
  3.         cmp     ax,0666h                ; 3 байта

И вы можете перемещать содержимое AX в другой регистр оптимизированным образом:

Код (Text):
  1.  
  2.         mov     bx,ax                   ; 2 байта
  3.         xchg    ax,bx                   ; 1 байт

Вы можете это делать, если значения AX и BX для вас не важны. Это инструкция будет полезна для открытия файла, потому что файловый хэндл лучше держать в BX.

¤ Строковые операции:

Каждая из строковых команд (MOVS, STOS, SCAS...) - это оптимизированный способ выполнять определенные действия. Давайте посмотрим, для каких целей они могут пригодиться:

- MOVS: перемещение из позиции DS:[SI] в ES:[DI]

Код (Text):
  1.  
  2.         les     di,ds:[si]              ; 3 байта
  3.  
  4.         movsb                           ; Если нам нужен байт (1 байт)
  5.         movsw                           ; Если нам нужно слово (1 байт)
  6.         movsd                           ; Если нам нужно двойное слово (2
  7.                                         ; байта) 386+

- LODS: помещает в приемник значение в позиции DS:[SI]

Код (Text):
  1.  
  2.         mov     ax,ds:[si]              ; 2 байта
  3.  
  4.         lodsb                           ; Если нам нужен байт (1 байт)
  5.         lodsw                           ; Если нам нужно слово (1 байт)
  6.         lodsd                           ; Если нам нужно двойное слово (2
  7.                                         ; байта) 386+

- STOS: помещает в приемник значение позиции ES:[DI]

Код (Text):
  1.  
  2.         les     di,al                   ; Нельзя это сделать!
  3.         les     di,ax                   ; Нельзя это сделать!
  4.  
  5.         stosb                           ; Если нужен байт (1 байт)
  6.         stosw                           ; Если нужно слово (1 байт)
  7.         stosd                           ; Если нужно двойное слово (2 байта)
  8.                                         ; 386+

- CMPS: сравнивает значение в DS:[SI] со значением в ES:[DI]

Код (Text):
  1.  
  2.         cmp     ds:[si],es:[di]         ; Не может быть два переопределения
  3.                                         ; сегмента!
  4.  
  5.         cmpsb                           ; Если нужен байт (1 байт)
  6.         cmpsw                           ; Если нужно слово (1 байт)
  7.         cmpsd                           ; Если нужно двойное слово (2 байта)
  8.                                         ; 386+

- SCAS: сравнивает значение приемника с ES:[DI]

Код (Text):
  1.  
  2.         cmp     ax,es:[di]              ; 3 байта
  3.  
  4.         scasb                           ; Если нужен байт (1 байт)
  5.         scasw                           ; Если нам нужно слово (1 байт)
  6.         scasd                           ; Если нам нужно двойное слово (2 байта)
  7.                                         ; 386+
¤ 16-ти битные регистры:

Обычно использование 16-ти битных регистров в плане оптимизации лучше, чем использование 8-ми битных. Давайте посмотрим пример с инструкцией MOVЖ

Код (Text):
  1.  
  2.         mov     ah,06h                  ; 2 байта
  3.         mov     al,66h                  ; 2 байта (вместе 4 байта)
  4.  
  5.         mov     ax,0666h                ; 3 байта

Также увеличение/понижение значения 16-ти битного регистра тоже более выгодно:

Код (Text):
  1.  
  2.         inc     al                      ; 2 байта
  3.         inc     ax                      ; 1 байт
  4.  
  5.         dec     al                      ; 2 байта
  6.         dec     ax                      ; 1 байт

¤ Базы и сегменты:

Перемещение из одного сегмента в другой нельзя сделать напрямую, поэтому мы должны сделать это одним из следующих способов:

Код (Text):
  1.  
  2.         mov     es,ds                   ; Это сделать нельзя!
  3.  
  4.         mov     ax,ds                   ; 2 байта
  5.         mov     es,ax                   ; 2 байта (вместе 4 байта)
  6.  
  7.         push    ds                      ; 1 байт
  8.         pop     es                      ; 1 байт (в сумме 2 байта)

Использование DI/SI более выгодно, чем использование BP.

Код (Text):
  1.  
  2.         mov     ax,ds:[bp]              ; 4 байта
  3.         mov     ax,ds:[si]              ; 3 байта

¤ Процедуры:

Если вы используете какой-то код много раз, подумайте о создании процедуры. Это может оптимизировать ваш код. Тем не менее, неправильное использование процедур может привести к обратному результату: размер кода возрастет. Поэтому если вы хотите знать, поможет ли вам создание процедуры, используйте эту маленькую формулу:

Код (Text):
  1.  
  2.  X = [rout. size - (CALL size + RET size)] * number of calls - rout. size

CALL size + RET size означают 4 байта. X будем количеством байт, которое мы сэкономим. Давайте посмотрим на типичный пример, перемещение файлового указателя:

Код (Text):
  1.  
  2.  fpend: mov     ax,4202h                ; 3 bytes
  3.  fpmov: xor     cx,cx                   ; 2 bytes
  4.         cwd                             ; 1 byte
  5.         int     21h                     ; 2 bytes
  6.         ret                             ; 1 byte

У нас есть 8 байт + размер CALL... 11 байт. Давайте посмотрим, соптимизирует ли это наш код:

Код (Text):
  1.  
  2.  X = [ 7 - ( 3 + 1 ) ] * 3 - 7
  3.  X = 2 байта сэкономлено

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

- Используйте SFT. В этой структуре множество полезной информации, и вы можете манипулировать ими безо всяких проблем.

- Пусть ваш компилятор делает по крайней мере 3 прохода, чтобы удалить все ненужные NOP'ы и другой отстой.

- Используйте стек.

- Также во многих случаях выгодно использовать инструкцию LEA. © Billy Belcebu, пер. Aquila


0 869
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532