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


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

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

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

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

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

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

        mov     bx,0000h                ; 3 байта
        xor     bx,bx                   ; 2 байта
        sub     bx,bx                   ; 2 байта

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

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

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

¤ Сравнения:

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

        cmp     ax,bx                   ; 2 байта
        xor     ax,bx                   ; 2 байта

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

        cmp     ax,0666h                ; 3 байта
        xor     ax,0666h                ; 2 байта

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

        cmp     ax,0000h                ; 3 байта
        or      ax,ax                   ; 2 байта

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

        cmp     bx,0666h                ; 4 байта
        cmp     ax,0666h                ; 3 байта

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

        mov     bx,ax                   ; 2 байта
        xchg    ax,bx                   ; 1 байт

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

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

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

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

        les     di,ds:[si]              ; 3 байта

        movsb                           ; Если нам нужен байт (1 байт)
        movsw                           ; Если нам нужно слово (1 байт)
        movsd                           ; Если нам нужно двойное слово (2
                                        ; байта) 386+

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

        mov     ax,ds:[si]              ; 2 байта

        lodsb                           ; Если нам нужен байт (1 байт)
        lodsw                           ; Если нам нужно слово (1 байт)
        lodsd                           ; Если нам нужно двойное слово (2
                                        ; байта) 386+

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

        les     di,al                   ; Нельзя это сделать!
        les     di,ax                   ; Нельзя это сделать!

        stosb                           ; Если нужен байт (1 байт)
        stosw                           ; Если нужно слово (1 байт)
        stosd                           ; Если нужно двойное слово (2 байта)
                                        ; 386+

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

        cmp     ds:[si],es:[di]         ; Не может быть два переопределения
                                        ; сегмента!

        cmpsb                           ; Если нужен байт (1 байт)
        cmpsw                           ; Если нужно слово (1 байт)
        cmpsd                           ; Если нужно двойное слово (2 байта)
                                        ; 386+

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

        cmp     ax,es:[di]              ; 3 байта

        scasb                           ; Если нужен байт (1 байт)
        scasw                           ; Если нам нужно слово (1 байт)
        scasd                           ; Если нам нужно двойное слово (2 байта)
                                        ; 386+

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

        mov     ah,06h                  ; 2 байта
        mov     al,66h                  ; 2 байта (вместе 4 байта)

        mov     ax,0666h                ; 3 байта

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

        inc     al                      ; 2 байта
        inc     ax                      ; 1 байт

        dec     al                      ; 2 байта
        dec     ax                      ; 1 байт

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

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

        mov     es,ds                   ; Это сделать нельзя!

        mov     ax,ds                   ; 2 байта
        mov     es,ax                   ; 2 байта (вместе 4 байта)

        push    ds                      ; 1 байт
        pop     es                      ; 1 байт (в сумме 2 байта)

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

        mov     ax,ds:[bp]              ; 4 байта
        mov     ax,ds:[si]              ; 3 байта

¤ Процедуры:

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

 X = [rout. size - (CALL size + RET size)] * number of calls - rout. size

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

 fpend: mov     ax,4202h                ; 3 bytes
 fpmov: xor     cx,cx                   ; 2 bytes
        cwd                             ; 1 byte
        int     21h                     ; 2 bytes
        ret                             ; 1 byte

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

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

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

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

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

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

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

[C] Billy Belcebu, пер. Aquila

 

Источник wasm.ru /03.09.2002/

Поделиться в соц сетях

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Есть идеи, замечания, предложения? Воспользуйтесь формой Обратная связь или отправьте сообщение по адресу replay@sciencestory.ru
© 2017 Истории науки. Информация на сайте опубликована в ознакомительных целях может иметь ограничение 18+