2006/10/05

Funktiokutsun korvaaminen kirjastosta

Kun ohjelma käynnistetään Linuxilla niin ld-linux.so(8) lataa ohjelman tarvitsemat kirjastot ja suorittaa ohjelman. Kirjastoja etsitään seuraavista paikoista.
  • LD_LIBRARY_PATH
  • /etc/ld.so.cache
  • /usr/lib
  • /lib
Oman kirjaston saa kuitenkin latautumaan lisäämällä sen LD_PRELOAD-ympäristömuuttujaan. Muuttuja sisältää välilyönnein erotellun listan kirjastoista, jotka ladataan ennen muita, mahdollistaen kirjastojen tai yksittäisten funktioiden korvaamisen.

Kirjastossa ei tarvitse muuta kuin määritellä funktio oikealla nimellä. Esimerkkikirjasto korvaa getpid-kutsun funktiolla, joka palauttaa aina 123.
#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void)
{
return 123;
}
Kirjaston voi kääntää komennolla gcc -shared -Wl,-soname,getpid_replacement.so.1 -o getpid_replacement.so getpid_replacement.c -Wall

Testiohjelma, joka kutsuu getpidiä.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main(void)
{
printf("getpid() = %d\n", getpid());
return 0;
}
Nyt kun ohjelman ajaa se voi tulostaa "getpid() = 7570". Seuraavaksi pitää asettaa ympäristömuuttujat kuntoon ja ajaa ohjelma korvatulla getpidillä.
  • $ export LD_PRELOAD=getpid_replacement.so
  • $ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
  • $ ./a.out
  • getpid() = 123
Tätä tekniikkaa voidaan myös käyttää väärin esimerkiksi korvaamalla read- tai scanf-funktiot (käyttäjä syöttää salasanan) ja lisäämällä export-rivit tiedostoon ~/.bashrc, jolloin korvaava kirjasto ladataan kaikille ohjelmille.

Tässä vielä versio, joka tulostaa prosessin oikean pidin ja palauttaa väärän.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

inline int get_real_pid(void)
{
asm("mov $20, %%eax\n"
"int $0x80\n" : :);
}

pid_t getpid(void)
{
printf("Original pid:%d\n", get_real_pid());
return 123;
}

Ei kommentteja: