Muitas pessoas utilizam, utilizaram ou vão utilizar a variável LD_PRELOAD, e por sua vez nem sempre sabem para que ela serve! Caso este seja o seu problema, problema este não mais será!! :P

O que acontece basicamente e que o linker dinâmico do Linux (assim como em tantos outros sistemas operacionais) utiliza diversas formas, alguma delas sendo através de variáveis de ambiente para controlar seu comportamento. Sendo que neste caso a variável LD_PRELOAD informa ao linker dinâmico que carregue as bibliotecas listadas nela antes de carregar quaisquer outras bibliotecas necessárias, enquando LD_LIBRARY_PATH especifica um caminho alternativo para usar ao procurar bibliotecas que serão carregadas.

Partindo deste principio podemos fazer com que um programa a ser executado seja “hijacked” por outro programa, ou seja. Podemos fazer por exemplo que a função hehe() previamente chamada pelo programa “A” tenha seu comportamento alterado sem precisar fazer quaisquer alteração no programa “A”. Um pouco complexo? talvez! Mais vamos por a mão na massa! hands on!

Digamos que você tem o programa “main” conforme o código de exemplo abaixo, perceba que o código e super simples. apenas declaro um ponteiro de caracteres, aloco memória e em seguida copio uma sequência de caracteres para o ponteiro previamente alocado. Simples, certo?

1) Abaixo código de exemplo de nosso “main.c” ou clique aqui para download.

/*
 *  Filename: hijack_main.c
 *  Created: Wed Jun  9 22:11:12 BRT 2010
 *  Author: Jorge Pereira <jpereiran@gmail.com>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int 
main (int argc, 
      char* argv[])
{
  char* nome = (char*)malloc (100);

  strcpy (nome, "Jorge Pereira");
  printf ("NOME: %s\n", nome);

  free (nome);
  return 0;
}

# Vamos compilar e executar o nosso exemplo “main.c”

$ gcc -Wall -o main main.c
$ ./main
NOME: Jorge Pereira
$

Até aqui tudo bem, porém imagine você em uma determinada situação em que precisa saber quantos bytes está sendo alocado por um determinado programa? e você por alguns instantes imagina sobre a possibilidade de poder fazer algum tipo de “overload” de uma determinada função na qual você conhece sua assinatura. (Digamos, você sabe a assinatura do método, quantidade e tipos dos parâmetros, …).

Pois bem, neste exemplo que irei demonstrar será para sobrecarregar todas as chamadas feitas pelo meu programa “main” às funções malloc() e free() e em seguida exibir uma mensagem no caso do malloc() imprimindo seu parâmetro que e o tamanho de bytes alocados, e na função free() exibindo os ponteiros que foram liberados.

Neste caso, iremos criar uma biblioteca chamada “libhijack_hehe.so” que será carregada através da variável mágica LD_PRELOAD em parceria com nosso querido linker dinâmico.

2) Abaixo código de exemplo de “hijack_hehe.c“, ou clique aqui para download.

/*
 *  Filename: hijack_hehe.c
 *  Created: Wed Jun  9 22:11:12 BRT 2010
 *  Author: Jorge Pereira <jpereiran@gmail.com>
 */
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <dlfcn.h>

#define HIJACK_DEBUG(fmt, ...) \
	fprintf(stderr, " ** DEBUG: %s:%d %s(): "fmt"\n", \
	__FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)

static void* (*hack_malloc)(size_t size) = NULL;
static void  (*hack_free)(void *p) = NULL;

static void* rest_malloc = NULL;

void*
malloc (size_t size)
{
  if (hack_malloc == NULL) 
  {
    hack_malloc = (void *(*)(size_t)) dlsym (RTLD_NEXT, "malloc");
    rest_malloc = NULL;
  }

  if (rest_malloc == NULL) 
  {
    rest_malloc = hack_malloc (size);
    HIJACK_DEBUG ("Alocando (%d) bytes, chunck(%p)", size, (void*)rest_malloc);
    return rest_malloc;
  }

  hack_malloc = NULL;
  return rest_malloc; 
}

void
free (void *p)
{
	HIJACK_DEBUG ("Desalocando (%p)", p);

  if (hack_free == NULL) 
  {
    hack_free = (void (*)(void *)) dlsym(RTLD_NEXT, "free");
  }

  hack_free (p);
}

Agora vamos compilar, executar e analisar o comportamento.

$ gcc -Wall -shared -ldl -o libhijack_hehe.so hijack_hehe.c
$ LD_PRELOAD=./libhijack_hehe.so ./main
 ** DEBUG: hijack_hehe.c:35 malloc(): Alocando (100) bytes, chunck(0x9273008)
NOME: Jorge Pereira
 ** DEBUG: hijack_hehe.c:46 free(): Desalocando (0x9273008)
$

Percebeu algo diferente na execução com a LD_PRELOAD passando como parâmetro a nossa libhijack_hehe.so? pois bem, todas as chamadas às funções malloc() e free() foram sobrecarregadas e passaram a se comportar conforme as versões que escrevi em hijack_hehe.c. Caso tenha ficado curioso, e so re-escrever tais exemplos com outras funções que você deseja sobrecarregar e ver o comportamento. Lembrando que basta utilizar a criatividade e perceberá na quantidade de coisas que podem ser feita com tal técnica.

Exemplo: Nas funções que fazem checagem com strcmp(), uso da crypt(), … entre outras.

Referências

  • man 8 ld.so

Autor: Jorge Pereira
Data: Wed Jun 9 23:42:26 BRT 2010