▄▄                   ▄
▀▀▀ STEALTH GROUP WW █ Mail:   BOX 15, 125080 MOSCOW   ████████ █████████ █▀▀█
▀██ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ █ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▐▀▀▀   ┌─┐┬ ┬┌─┤┬ ┬ ╥ ┬┐┌ █▄▄█
 ▐█ █▀▄ █▀▀ ▄▀▀ ▄▀▀ ▄█▄ ▄▀▀ █▀█   ▌ █ ▄▀█ █ ▄▀▀ █▄▄    ├┬┘│ │└─┐├─┤   │└┤ ▄  █
  █ █ █ █▀  █▀  █    █  █▀  █ █   █ █ █ █ █ █   █      ┴└─└─┘└─┘┴ ┴   ┴ ┴ ▀▀▀▀
  █ ▐ ▐ ▐   ▐▄▄ ▐▄▄  ▐  ▐▄▄ ▐▄▀    ▀█ ▀▄█ ▐ ▐▄▄ ▐▄▄▄   ████  WINTER ' 96  ████
  ▐ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄   ███████████████████████

		   О П Т И М И З А Ц И Я   К О Д А
 		     Руковдство по ассемблированю

Автор:   DARKMAN / VLAD
Перевод и дополнения : LovinGOD / SGWW

Представьте, что ваш вирус создан и после долгого процесса отладки наконец
работает.  Но ваше произведение программерского искусства длиннее, чем хотелось
бы. Ниже приведены советы, как убрать из кода лишние десятки байт. Следуя им,
вы сделаете свой код чище и красивее.
		Незванный соавтор LovinGOD - хуже и летально ! :)
P.S. В процессе перевода я добавил несколько советов, используемых мной при
программировании и тех, что я почерпнул из некоторых вирусов.

-------------------------------------------------
Используйте AL/AX вместо других регистров.
-------------------------------------------------
  
  Операции с регистрами AL/AX в некоторых случаях занимают на байт меньше,
чем те же операции с другими регистрами.

Сравните инструкцию

	     cmp     bx,1234h             ; Сравнить BX с 1234h (4 байта)

и нструкцию

	     cmp     ax,1234h             ; Сравнить AX с 1234h (3 байта)

  Естественно, если AX/AL содержит какие-то важные данные - не испортите их!  
Код с использованием регистров AX/AL вместо других будет выглядеть короче,
даже если вам придется его PUSH/POP для сохранения.

---------------------------------------
Испоьзуйте DS вместо других сегментов
---------------------------------------
  Занесение слова из памяти в AX может выглядеть так:

	     mov     ax,es:[si]           ; Занести ES:[SI] в AX (3 байта)

  Или так:

	     mov     ax,ds:[si]           ; Занести DS:[SI] в AX (2 байта)

----------------
Очистить регистр
----------------
  Обычный код для очистки регистра AX

	     mov     ax,00h              ; (3 байта)

Самый лучший способ очистить регистр :

	     sub     ax,ax               ; (2 байта)

Или способ не хуже:

	     xor     ax,ax               ; (2 байт)

---------------------
Очистить регистр DX
---------------------

Обычно:

	     mov     dx,00h              ; (3 байт)

Оптимизированно: (см. то же с регистром AX)

	     xor     dx,dx               ; (2 байт)

Самый короткий способ:

	     cwd                         ; (1 байт) - преобразовать слово в
					 ; двойное слово. Однако,  этот код
					 ; будет иметь смысл только, если
					 ; AX содержит положительное число
					 ; (меньше 8000h). Иначе DX будет 
					 ; равно минус 1 (0FFFFh)

---------------------------
Проверка регистра на 0
---------------------------
  Стандартная инструкция:

	     cmp     ax,00h              ; AX = 0? (3 байт)

  Это можно сделать более коротко:

	     or      ax,ax               ; AX = 0? (2 байт)
					 ; jz Go_if_Zero
----------------------------------------------------
Использование 16-битного регистра вместо двух 8-битных
----------------------------------------------------
  Занести два байта в 16-битный регистр можно так:

	     mov     ah,12h              ; занести 12h в AH (2 байт)
	     mov     al,34h              ; занести 34h в AL (2 байт)

  Так будет короче:

	     mov     ax,1234h             ; занести 1234h to AX (3 байт)

  Пример:
	Необходимо вывести символ на экран (f=0E,int10h,al=буква для вывода)
	Вместо:
	     mov ah,0eh
	     mov al,'!'
	Пишем:
	     mov ax,0E00h+'!'

---------------------------------------------------------
Занести AL/AX в другой регист или наоборот
---------------------------------------------------------
  Стандартно пишут:

	     mov     bx,ax               ; занести AX в BX (2 байт)

Более оптимизированный код:

	     xchg    ax,bx               ; обменять AX с BX (1 байт)

  Необходимо учитывать, не испортим ли мы исходный регистр - нужно ли его
значение нам в дальнейшем.

-------------------------------
Используйте DI/SI в качестве базового регистра вместо BP
-------------------------------

	     mov     ax,ds:[bp]           ; занести DS:[BP] в AX (3 байт)

На один байт короче будет код:

	     mov     ax,ds:[si]           ; занести DS:[SI] в AX (2 байт)

  Вы добьетесь оптимизации, даже если делать PUSH/POP для сохранения регистров
DS/SI.

--------------------------------------------------------
Используйте CMPS, LODS, MOVS, SCAS, STOS и REP 
--------------------------------------------------------
  Занесение значения из памяти в AX может быть сделано так:

	     mov     ax,ds:[si]           ; занести DS:[SI] в AX (2 байт)

  На один байт короче выглядит инструкция

	     lodsw                        ; Загрузить в AX содержимое DS:[DI]
					  ; (1 байт)
  Помните о флаге Direction (направление) и о том, что после операции
	SI=SI+1 ( DirectionFLag=0)
	SI=SI-1 ( DF=1 )
	     
---------------------------
Занести сегментный регистр в сегментный регистр:
---------------------------
  Вы не можете занести напрямую один сегментный регистр в другой :	

	     mov     ds,cs               ; Нет такой комманды

Поэтому вы должны использовать какой-нибудь регистр для транзита:

	     mov     ax,cs               ; занести CS в AX (2 байт)
	     mov     ds,ax               ; занести AX в DS (2 байт)

  Проще сделать это с помощью стека (в 2 раза короче):

	     push    cs                  ; Занести CS в стек          (1 байт)
	     pop     ds                  ; Занести в DS из стека (CS) (1 байт)

-----------------------------------------------
Используйте SHL/SHR вместо DIV/MUL
-----------------------------------------------
  Умножить AL на 2 можно так:

	     mov     bh,02h              ; занести 02h в BH (2 байт)
	     mul     bh                  ; Умножить AL на BL (2 байт)
					 ; Результат - в AX

  В 2 раза короче будет сдвиг регистра на одну степень двойки:

	     shl     al,01h              ; Multiply AL with 02h (2 байт)

  Это может быть использовано только для деления (умножения) налопопам.
  Для умножения/деления (SHL/SHR) более чем на 2 (4,8,16,32,...) необходимо
  число бит сдвига занести в регистр, к примеру, CL.
	
	    mov cl,6			 ; 2^6=64	(2 байта)
	    shl al,cl			 ;		(2 байта)

----------------------------------------------------------------
Используйте непосредственно код вместо ассемблерных инструкций:
----------------------------------------------------------------
  Межсегментный CALL можно сделать так:

	     call    dword ptr address   ; Межсегментный переход  (3 байта)
address      dd      ?                   ; Адрес для перехода     (4 байта)

  Лучше занести адрес перехода непосредственно на место операнда комманды

callfar      db      9ah                 ; Hex-код CALL FAR       (1 байт)
address      dd      ?                   ; Адрес процедуры        (4 байт)

  Если вам нужно сохранить значение регистра не в стеке, а потом занести
 значение обратно, то вместо

	     mov ax_save,ax		; (3 байта)
	     ...
	     mov ax,ax_save		; (3 байта)
	     ...
	     ax_save dw ?		; (2 байта)

 делайте вместе с нами так:

	     mov ax_save,ax		; (3 байта)
	     ...
	     db  0B8h			; код "MOV AX,значение"	 (1 байт)
     ax_save dw  ?			; хранилище для AX, оно же операнд
					; комманды "MOV AX,значение")

  !!! Помните о конвейере (prefetch - предвыборка комманд) !!!
  При выполнении кода процессор считывает в себя сразу 16 байт кода.
  Если в нем не содержится переходов, то этот участок кода выполняется
  независимо от того, какие изменения произойдут с ним в памяти в это время.
  На этом основан антитрассировочный прием:

  	            mov fool_debugger,ax	; меняем код следующей комманды
  fool_debugger:    след. комманда		;

  Процессор заглатывает сразу две и даже более комманд, и изменение комманды
  после метки fool_debugger не повлияет на выполнение программы.
  (Поменяйте комманду <след.комманда> обратно после выполнения этого кода)
  Если вы хотите, чтобы процессор выполнил именно измененную комманду,
  то необходимо очистить конвейер коммандой JMP :
		
		   mov changed_var,ax
		   JMP SHORT $+2		; прыжок на месте
   changed_var:    что-нибудь			; выполнится комманда,
						; занесенная сюда из AX
----------------------
Используйте процедуры
----------------------
  Вместо кусков повторяющегося кода лучше использовать процедуры.
Эффективность использования прцедуры вычисляется по формуле:

Сэкономленных_байт = ((Размер_процедуры - 3) * Число_вызовов -1) - Размер_процедуры
		
  Число 3 - число байт, затраченных на инструкцию CALL при вызове процедуры,
       -1 - добавленная в процедуру инструкция RET.

--------------------------
Делайте процедуры гибкими
--------------------------

Movefptrend  proc    near             ; переместить указатель файла в EOF
	     mov     al,02h           ;  "        "      "   "   "
Movefileptr  proc    near             ; занести указатель файла в конец/начало
	     cwd                      ; Очистить DX (см.выше)
Movefpointer proc    near             ; передвинуть указатель файла
	     xor     dx,dx            ; в начало
	     mov     ah,42h           ; 
	     int     21h              ; 
	     ret                      ; 
	     endp
	     endp
	     endp

 Вы можете вызвать Movefptrend для перемещения указателя файла в конец и
 вызвать Movefileptr для перемещения в начало (чуть дальше комманды MOV AL,02)

----------------------------------
Полнее используйте инфо в DTA
----------------------------------
  DTA (Disk Transfer Area) используется функциями DOS 4eh и 4fh.
  Содержание DTA:

                ----------------------------------------
		Смещение Размер  Содержание
                ----------------------------------------
		  00     Byte    Имя диска
		 01-0B   байт    Шаблон для поиска
		 0C-14   байт    Зарезервировано 
		  15     Byte    Файл: атрибуты
		 16-17   Word          время создания
		 18-19   Word          дата создания
		 1A-1D   DWord         размер 
		 1E-3A                 имя+расширение+0
                ----------------------------------------

- Для изменения времени создания файла используйте DTA вместо f57h int21h
- Если вам необходимо заразить только ОДИН файл, поменяйте в DTA букву диска
  на несуществующий, это вызовет ошибку при продолжении поиска (вместо
  специальных проверок во время цикла поиска)

Оптимизации вы достигнете ТОЛЬКО если вы уже используете DTA.
Специальное нахождение DTA для вышеописанных целей займет лишнее место.

------------------------------------
И напоследок еще несколько советов:
------------------------------------
- Уберите все лишние NOP
- старайтесь использовать JMP SHORT (0EBh) вместо JMP NEAR (0E9h) где это
  возможно
- Не вычисляйте в программе то, что может быть вычислено при ассемблировании
  к примеру:	
	mov ax,ds:[si][offset somewhere - offset start]
  вместо
	add si,offset somewhere - offset start
	mov ax,ds:[si]

- Используйте LEA вместо MOV OFFSET. (?! LovinGOD)
- Используйте стек для хранения временных переменных, будьте осторожны!
- Используйте CBW для очистки AH, если AL меньше 80h.
- Используйте DEC/INC вместо ADD/SUB register,01h.

	add si,2      (3 байта)
   или
	inc si	      (1 байт)	
	inc si        (1 байт)

- При использовании DEC/INC лучше использовать 16-битные регистры вместо
  8-битных.

- Используйте LES/LDS для загрузки регистровых пар DS:reg и ES:reg

	lds ax,dword ptr ds:[my_pair]	; (4 байта)
  вместо 8-байтного
	mov ax,word ptr my_pair
	mov ds,word ptr my_pair+2	

- для обмена памяти с регистром используйте XCHG 

- занесение непосредственного значения в стек

	Два функционально одинаковых участка кода:

	
	mov  ax,1111h			push 1111h
	push ax

  !!! для процессора 8086 после ассемблирования комманда "PUSH 1111h"
      будет заменена на:

	push ax
	push bp
	mov  bp,sp
	mov  ss:[bp+2],1111h	; значение AX заменяется на 1111h
	pop  bp

  Трехбайтовая комманда "PUSH непосредств_значение" будет сгенерирована
  только для 286+ :
	Добавьте в начале файла ".286" 

; ─────────────────────────────────────────────────────────────────────────
; ──────────────── Remember Where You Saw This Phile First ────────────────
; ───────[=] ARRESTED DEVELOPMENT +31.77.3547477 The Netherlands [=]───────
; ─────────────────────────────────────────────────────────────────────────
