ZF

             "Заражение (или вакцинация) EXE-файлов"
                            by DrMAD

                     Милостивые государи!


    В вирусологии   (и   не  только)  давно  известны  способы
внедрения своих исполнимых  фрагментов  в  код  ЕХЕ-программы.
Метод   с   приписыванием   фрагмента   к  концу  ЕХЕ-файла  и
модифицированием нескольких полей в его заголовке  (обычно это
ReloCS,  ExeIP,  PageCnt и PartPag, иногда еще ReloSS и ExeSP)
даже получил наименование "стандартного".
    Между тем,    способ   имеет   ряд   крупных   недостатков
(вспомните,  например,  проблему  "оверлейных"   файлов).   Не
случайно     все    "прогрессивное    человечество"    (авторы
пристыковочных защит от НСК,  вакцин, упаковщиков и пр.) давно
отказалось  от "стандартного" метода.  Ими активно и с успехом
используется  способ   собственного   загрузчика,   включающий
саостоятельное  размещение кода программы в памяти,  настройку
перемещаемых адресов и  пр.  Разумеется,  это  сложней,  но  в
несколько раз надежней!
    Что же требуется,  чтобы выполнить ряд функций  командного
процессора и самостоятельно загрузить ЕХЕ-программу? Обратимся
за справочной информацией к нестареющему TechHelp:

Смещ.Длина Содержимое
**** ***** ***************************************************
      +-------+
 +0  2|4Dh 5aH|"подпись" файла .EXE ('MZ')
      +---+---+
 +2  2|PartPag|длина неполной последней страницы
      +---+---+
 +4  2|PageCnt|длина образа в 512-байтниках, включая заголовок
      +---+---+
 +6  2|ReloCnt|число элементов в таблице перемещения
      +---+---+
 +8  2|HdrSize|длина заголовка в 16-байтовых параграфах
      +---+---+
+0aH 2|MinMem |минимум требуемой памяти за концом программы
      +---+---+
+0cH 2|MaxMem |максимум требуемой памяти за концом программы
      +---+---+
+0eH 2|ReloSS |сегментное смещениестека (для установки SS)
      +---+---+
+10H 2|ExeSP  |значение регистра SP при запуске
      +---+---+
+12H 2|ChkSum |К/сумма (отрицательная сумма всех слов в файле)
      +---+---+
+14H 2|ExeIP  |значение регистра IP при запуске
      +---+---+
+16H 2|ReloCS |сегментное смещение кода (для установки CS)
      +---+---+
+18H 2|TablOff|смещение в файле 1-го элемента перемещения
      +---+---+
+1aH 2|Overlay|номер оверлея (0 для главного модуля)
      +---+---+
1cH   размер форматированной порции заголовка EXE
        +-------+-------+ - -  Таблица перемещения. Начало
+ ? 4*? | смещ.  сегмент|   по смещению [EXE+18H]. Имеет
        +---+---+---+---+ - -  [EXE+6] 4-байтовых элемента.
+ ? ?   пропуск до границы параграфа
+ ? ?   начало образа программы

    Поскольку EXE-файл может быть загружен  в  любой  сегмент,
все абсолютные сегментные ссылки (FAR CALL, далекие указатели,
ссылки типа MOV AX,data_seg) должны быть приведены  к  адресам
памяти,   соответствующим   загрузке.   Ниже  приведены  шаги,
используемые  программой  загрузки  DOS  (функция  4bH  )  при
загрузке файла EXE:

    1. создать PSP посредством функции DOS 26H

    2. прочитать  1cH  байт  файла EXE (форматированную порцию
заголовка EXE) в локальную область памяти

    3. определить размер модуля = ((PageCnt*512)-(HdrSize*16))
- PartPag

    4. определить  файловое  смещение  загружаемого  модуля  =
(HdrSize * 16)

    5. выбрать  сегментный  адрес,  START_SEG,  для   загрузки
(обычно PSP + 10H)

    6. считать модуль в область памяти,  начинающуюся с адреса
START_SEG:0000

    7. LSEEK  (уст.  указатель  файла)   на   начало   таблицы
перемещения (TablOff)

8.  для каждого элемента перемещения (ReloCnt):
    a. считать элемент как два 16-битовых  слова (I_OFF,I_SEG)
    b. вычислить       RELO_SEG=(START_SEG+I_SEG)       (адрес
перемещаемой ссылки)
    c. извлечь   слово  по  адресу  RELO_SEG:I_OFF  (прочитать
текущее значение)
    d. прибавить  START_SEG  к этому слову (выполнить привязку
сегмента)
    e. поместить    результат    обратно    по    его   адресу
(RELO_SEG:I_OFF)

    9. Распределить память для  программы  согласно  MaxMem  и
MinMem

10. Инициализировать регистры и запустить программу:
    a. ES = DS = PSP
    b. AX  =  отражает  корректность  идентификаторов дисков в
командной строке
    c. SS = START_SEG+ReloSS, SP = ExeSP
    d. CS = START_SEG+ReloCS,  IP = ExeIP (команды:  PUSH seg;
PUSH offset; RETF)

    Замечание: Последние  добавления  в  формат EXE,  особенно
версии   EXE-файлов   "CodeView"   и    "Windows",    содержат
дополнительную информацию,  включенную в выполнимый файл.  Эти
добавления не отражены в этой версии TECH Help!.
**************************************************************

    Как видим, все достаточно просто и понятно. Посмотрим, как
это выглядит на практике.
    С давних  пор на моем винчестере поселилась утилита e2com,
созданная Сергеем  Евтушенко  (Elisoft  Computer  Group).  Она
предназначена  для  "преобразования"  небольших EXE-программ в
СОМ-формат.  Достаточно  долгое  время  я  считал,  что  автор
использовал  какие-то  крайне сложные преобразования исходного
кода...  пока не обратил  внимание,  что  в  результате  длина
преобразованной  программы  увеличивается  всего  на несколько
сотен  байт  (208  байтов).  Любопытство  побудило  "заразить"
маленькую простую программку и дизассемблировать ее.
    Выяснилось, что  вся  операция  "заражения-преобразования"
включает всего одно простое действие:

    РАЗМЕЩЕНИЕ 208-БАЙТНОГО КОДА ЗАГРУЗЧИКА В НАЧАЛЕ ПРОГРАММЫ
И ПРИПИСЫВАНИЕ К НЕМУ СОВЕРШЕННО НЕИЗМЕНЯЕМОГО  ФАЙЛА ИСХОДНОЙ
ПРОГРАММЫ. И ВСЕ !!!

    Разумеется, "заражению" могут подвергнуться лишь небольшие
EXE-программы.  Есть  и  другие  недостатки.  Зато   все   они
компенсируются   двумя   огромными   достоинствами:  ПРОСТОТОЙ
ОПЕРАЦИИ "ЗАРАЖЕНИЯ" и "ТОЛЕРАНТНОСТЬЮ" К ОВЕРЛЕЙНОСТИ.

    Короче, вот  Вам  дизассемблированный  и  комментированный
листинг   загрузочного   кода  программы  e2com  (может  быть,
кто-нибудь когда-нибудь уже делал подобное,  но лично  мне  об
этом неизвестно):

;##########################################################################
;##					                                 ##
;##				PROBA	                                 ##
;##					                                 ##
;## Disassembled & commented by Constantin E. Climentieff,  Samara 1997  ##
;##					                                 ##
;##########################################################################

; The following equates show data references outside the range of the program.

 = 0002			data_1e		equ	2			; (9059:0002=0)
 = 00EF			data_2e		equ	0EFh			; (9059:00EF=0)

seg_a		segment	byte public
		assume	cs:seg_a, ds:seg_a


		org	100h

proba		proc	far
;--------------------------------------------------------------------------------------------------------------------				
; Сейчас в памяти имеет место быть следующая конфигурация:
; +---------------------------------+
; | PSP загрузчика                  |
; +---------------------------------+
; | Код загрузчика                  |
; +---------------------------------+
; | Заголовок EXE-программы         |
; +---------------------------------+
; | Relocation Table EXE-программы  |
; +---------------------------------+
; | Код и данные EXE-программы      |
; +---------------------------------+
;-----------------------------------------------------------------------------------------------------------------------------
     0100			start:
     0100 ъBE 01D0				mov	si,offset data_6	; Адресуемся на начало EXE-программы.
     0103  8B 04				mov	ax,[si]                 ; Берем первое ее слово, оно д.б. 'MZ'
     0105  3D 5A4D				cmp	ax,5A4Dh                ; Это была EXE-программа?
     0108  74 01				je	loc_3			; Если да, то все ОК - продолжаем

     010A			loc_ret_2:					; Отваливаем
     010A ъCB					retf				;
     010B			loc_3:						;
;-----------------------------------------------------------------------------------------------------------------------------
     010B  8B C6				mov	ax,si                   ; По известному смещению начала
     010D  B1 04				mov	cl,4                    ;  сохраненной EXE-программы
     010F  D3 E8				shr	ax,cl			;  и текущему сегменту вычисляем сегмент
     0111  8C DB				mov	bx,ds                   ;  местоположения сохраненной
     0113  03 C3				add	ax,bx                   ;  EXE-программы.
;-----------------------------------------------------------------------------------------------------------------------------
     0115  03 44 08				add	ax,[si+8]               ; Прибавляем к нему размер заголовочной части
     0118  A3 01CC				mov	data_4,ax		;  EXE-программы и ховаем в data_4,
                                                                                ;  теперь там у нас - сегмент кода EXE-программы.
;-----------------------------------------------------------------------------------------------------------------------------
     011B  8C C8				mov	ax,cs                   ; По текущему сегменту PSP вычисляем
     011D  05 0010				add	ax,10h                  ;  сегмент кода загрузчика и
     0120  A3 01CE				mov	data_5,ax		;  ховаем в data_5.
;-----------------------------------------------------------------------------------------------------------------------------
     0123  8B 44 10				mov	ax,[si+10h]             ; По известным значениям ReloSS и ExeSP
     0126  B1 04				mov	cl,4                    ;  из заголовка EXE-программы вычисляем
     0128  D3 E8				shr	ax,cl			;  положение стека EXE-программы.
     012A  03 06 01CE				add	ax,data_5		;
     012E  03 44 0E				add	ax,[si+0Eh]             ;
;-----------------------------------------------------------------------------------------------------------------------------
     0131 ъBB 0002				mov	bx,data_1e		; bx := [PSP:2], т.е. сегмент конца
     0134  8B 1F				mov	bx,[bx]                 ;  выделенного блока памяти.
     0136  3B D8				cmp	bx,ax                   ; Стек умещается в выделенном блоке?
     0138  73 02				jae	loc_4			; Да - продолжаем.
     013A  EB CE				jmp	short loc_ret_2		; Нет - отваливаем, вероятно, не хватит памяти.
     013C			loc_4:						;
     013C  A1 01CE				mov	ax,data_5		; ax := сегмент кода загрузчика.
     013F  8B DE				mov	bx,si                   ; bx := смещение заголовка EXE-программы.
     0141  03 5C 18				add	bx,[si+18h]             ; bx + длина заголовочной части, получаем адрес кода.
     0144  8B 4C 06				mov	cx,[si+6]               ; cx := количество элементов в Relocation Table.
     0147  E3 13				jcxz	loc_6			; Если Relocation Table пуста, то пропускаем.
;------------------------------------------------------------------------------------------------------------------------------
     0149			locloop_5:					; Здесь Relocation Table не пуста, ее адрес в BX.
     0149  8B 57 02				mov	dx,[bx+2]               ; Берем I_SEG.
     014C  03 16 01CC				add	dx,data_4		; Прибавляем сегмент EXE-кода.
     0150  8E C2				mov	es,dx                   ; Это будет сегмент модифицируемого слова.
     0152  8B 3F				mov	di,[bx]                 ; Берем I_OFS - это его смещение.
     0154  26: 01 05				add	es:[di],ax              ; Настраиваем ссылку, прибавляя сегмент загрузчика.
     0157  83 C3 04				add	bx,4                    ; Смещаемся на следующий элемент Relocation Table.
     015A  E2 ED				loop	locloop_5		; И продолжаем в цикле настройку перемещаемых ссылок.
;-------------------------------------------------------------------------------------------------------------------------------
     015C			loc_6:						;  xref 9059:0147
     015C  56					push	si                      ; (7)
     015D  0E					push	cs                      ;
     015E  07					pop	es                      ;
     015F ъBF 00EF				mov	di,data_2e		; Адрес хвоста PSP
     0162 ъBE 01A5				mov	si,1A5h			; Адрес кода завершения.
     0165  B9 0011				mov	cx,11h                  ; Длина кода завершения.
     0168  F3/ A4				rep	movsb			; Копируем код завершения в редко используемый хвост PSP.
     016A  5E					pop	si
     016B  A1 01CE				mov	ax,data_5		; Сегмент кода загрузчика.
     016E  03 44 16				add	ax,[si+16h]             ; Прибавляем ReloCS, получаем сегмент точки входа и
     0171  50					push	ax                      ;  помещаем в стек.  (6)
     0172  8B 44 14				mov	ax,[si+14h]             ; Берем ExeIP - смещение точки входа и
     0175  50					push	ax                      ;  помещаем в стек.  (5)
     0176  1E					push	ds                      ; (4)
     0177  1E					push	ds                      ; (3)
     0178  A1 01CE				mov	ax,data_5		; Сегмент кода загрузчика.
     017B  8E C0				mov	es,ax                   ; es := сегмент кода загрузчика (PSP+10h).
     017D  33 FF				xor	di,di			; di := 0
     017F  03 44 0E				add	ax,[si+0Eh]             ; Смещаем сегмент стека на длину загрузчика.
     0182  50					push	ax                      ; Сохраняем новое значение ReloSS в стеке. (2)
     0183  8B 44 10				mov	ax,[si+10h]             ; Берем ExeSp
     0186  50					push	ax                      ;  и сохраняем в стеке.  (1)
     0187  8B 5C 04				mov	bx,[si+4]               ; Размер файла в 512-байтниках.
     018A  8B 44 08				mov	ax,[si+8]               ; Размер заголовочной части в 16-байтниках.
     018D  B1 05				mov	cl,5
     018F  D3 E8				shr	ax,cl			; Длина заголовка/32 = она же в целых 512-байтниках.
     0191  2B D8				sub	bx,ax                   ; Вычисляем полезную длину EXE-программы в 512-байтниках.
     0193  B1 08				mov	cl,8                    ;
     0195  D3 E3				shl	bx,cl			; Длина*512 = длина в байтах!
     0197  8B CB				mov	cx,bx                   ; Регистр cx должен содержать длину кода для movsw!
     0199  A1 01CC				mov	ax,data_4		;
     019C  8E D8				mov	ds,ax                   ; ds := сегмент EXE-кода
     019E  33 F6				xor	si,si			; si := 0
     01A0 ъBB 00EF		;*		mov	bx,offset loc_1		; Переходим на копию кода завершения, которая теперь в PSP.
     01A0  BB EF 00				db	0BBh,0EFh, 00h
     01A3  FF E3				jmp	bx			;*Register jump
; -----------------------------------------------------------------------------------------------------------------------------
; Код завершения, копируется в другое место, потом на него передается управление.
; Сейчас ds:si указывает на EXE-код, es:di - на код загрузчика, сразу после PSP.
     01A5  F3/ A5				rep	movsw			; Копируем код EXE-программы наверх, затирая загрузчик.
     01A7  58					pop	ax                      ; ExeSP  (1)
     01A8  5B					pop	bx                      ; ReloSS (2)
     01A9  07					pop	es                      ; Старый ds (3)
     01AA  1F					pop	ds                      ; Старый ds (4)
     01AB  59					pop	cx                      ; ExeIP  (5)
     01AC  5A					pop	dx                      ; ReloCS (6)
     01AD  FA					cli				;
     01AE  8E D3				mov	ss,bx                   ; Настраиваем
     01B0  8B E0				mov	sp,ax                   ; положение стека по (1),(2)
     01B2  FB					sti				;
     01B3  52					push	dx                      ; Повторно помещаем в стек
     01B4  51					push	cx                      ; точку входа.
     01B5  CB					retf				; И переходим на нее командой retf !
                                                                                ; Обратите внимание, в стеке осталось слово (7), т.е. 0 !
                                                                                ; А ax,bx,cx,dx остались не совсем корректные. K
;--------------------------------------------------------------------------------------------------------------------------------------
; Это всего лишь авторский копирайт
     01B6  28 43 29 20 43 6F	copyright	db	'(C) Copyright Elisoft.'
     01BC  70 79 72 69 67 68
     01C2  74 20 45 6C 69 73
     01C8  6F 66 74 2E
;--------------------------------------------------------------------------------------------------------------------------------------
     01CC  0000			data_4		dw	0			;  xref 9059:0118, 014C, 0199
     01CE  0000			data_5		dw	0			;  xref 9059:0120, 012A, 013C, 016B
										;            0178
;--------------------------------------------------------------------------------------------------------------------------------------
; А отсюда начинается пристыкованное к концу содержимое файла EXE-программы										
     01D0  4D			data_6		db	4Dh			;  xref 9059:0100
     01D1  5A 40 00 02 00 01			db	 5Ah, 40h, 00h, 02h, 00h, 01h
     01D7  00 20 00 00 00 FF			db	 00h, 20h, 00h, 00h, 00h,0FFh
     01DD  FF 00 00 20 00 91			db	0FFh, 00h, 00h, 20h, 00h, 91h
     01E3  29 00 00 03 00 1E			db	 29h, 00h, 00h, 03h, 00h, 1Eh
     01E9  00 00 00 01 00 01			db	 00h, 00h, 00h, 01h, 00h, 01h
     01EF  00 03				db	 00h, 03h
     01F1  01DF[00]				db	479 dup (0)
     03D0  2A 2A 2A 2A 2A 2A			db	'********************************'
     03D6  2A 2A 2A 2A 2A 2A
     03DC  2A 2A 2A 2A 2A 2A
     03E2  2A 2A 2A 2A 2A 2A
     03E8  2A 2A 2A 2A 2A 2A
     03EE  2A 2A
     03F0  48 65 6C 6C 6F 21			db	'Hello!$'
     03F6  24
     03F7  0009[00]				db	9 dup (0)
     0400  B8 02 00 8E D8 BA			db	0B8h, 02h, 00h, 8Eh,0D8h,0BAh
     0406  00 00 B4 09 CD 21			db	 00h, 00h,0B4h, 09h,0CDh, 21h
     040C  B4 4C CD				db	0B4h, 4Ch,0CDh
     040F  21					db	21h

proba		endp

seg_a		ends

		end	start

#################### CROSS REFERENCE - KEY ENTRY POINTS ###################

    seg:off    type	   label
   ---- ----   ----   --------------------------------
   9059:0100   far    start

 ################## Interrupt Usage Synopsis ##################

        No Interrupts used.

 ################## I/O Port Usage Synopsis  ##################

        No I/O ports used.




(C) NF, 1998-2004