C program to replace malloc, calloc and relloc using LD_PRELOAD i.e during the runtime of a process

LD_PRELOAD Theory

All about LD_PRELOAD

Normally the Linux dynamic loader ld-linux (see ld-linux(8) man page) finds and loads the shared libraries needed by a program, prepare the program to run, and then run it. The shared libraries (shared objects) are loaded in whatever order the loader needs them in order to resolve symbols.
LD_PRELOAD is an optional environmental variable containing one or more paths to shared libraries, or shared objects, that the loader will load before any other shared library including the C runtime library (libc.so) This is called preloading a library.
Preloading a library means that its functions will be used before others of the same name in later libraries. This enables library functions to be intercepted and replaced (overwritten.) As a result program behavior can be non-invasively modified, i.e. a recompile is not necessary.
For example, you could write a library which implements alternative malloc and free functionality. By preloading the new library using LD_PRELOAD the new malloc and free functions will be used rather than the corresponding standard libc functions.
Shared library paths, if there is more than one, may be separated by either colons (preferred) or spaces. Entries in LD_PRELOAD containing ’/’are treated as path names, whereas entries not containing ’/’are searched for as usual. Obviously this only affects dynamically linked – not statically linked – applications.
To avoid this mechanism being using as an attack vector for suid/sgid executable binaries, the loader ignores LD_PRELOAD if ruid != euid. For such binaries, only libraries in standard paths that are also suid/sgid will be preloaded.
Some users use LD_PRELOAD to specify libraries in nonstandard locations, but the LD_LIBRARY_PATH environmental variable is a better solution
Note:
Shared libraries specified in /etc/ld.so.preload are loaded before libraries specified by LD_PRELOAD
The libc library checks for the existing of /etc/ld.so.preload (see elf/rtld.c) and, if found, loads the listed shared libraries just as setting the environment variable LD_PRELOAD would do. The advantage of using /etc/ld.so.preload is that these shared libraries are implicitly trusted and hence the ruid != euid test does not apply. Thus the loader will load the shared objects listed in /etc/ld.so.preload even for suid/sgid executable binaries.

Danger of using dlsym with rtld_next

C program


Directory Structure

First create a project Folder "preload_alloc_library".

Create the following sub-directories and files.
1. 'bin'   ---> Directory 
2. 'src'   ---> Directory
3. Makefile  ---> File
4. exe.sh      ---->  Bash executable shell script 

Note: Just run the executable script for compilation and output generation.


Inside 'src' folder, create 2 files: 

1. preload_library.c  
2. test.c



Neelkanth_221$ cat Makefile

SRCS_DIR     := src
BUILD_DIR    := bin
CURRENT_DIR  := $(shell pwd)

PRELOAD_LIB  := $(BUILD_DIR)/libpreload.so

FINAL_BINARY := $(BUILD_DIR)/test

CC=gcc
CFLAGS=-Wall -g


all:  compile_source

compile_source:  compile_libs
        @echo '###############################################'
        @echo 'compile the test program'
        @echo '###############################################'
        $(CC) $(CFLAGS) $(SRCS_DIR)/test.c -o $(FINAL_BINARY)

compile_libs:

        $(CC) -fPIC -shared $(SRCS_DIR)/preload_library.c -o $(PRELOAD_LIB) -ldl


clean:
        rm -rf bin/*

Neelkanth_221$ cat preload_library.c

/************************************************************************************
 * This is the pre-load library, which is replace all alloc glic libray functions,
 * with new definitions on runtime.
 ************************************************************************************/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

/*********************************************************************
 *                      FUNCTION DECLARATIONS                        *
 *********************************************************************/
/* Function pointers to hold the value of the glibc functions */

/* This needs to be in the c file.. global declaration.  */
static void* (*real_malloc)(size_t size) = NULL;
static void  (*real_free)(void *ptr) = NULL;
static void* (*real_calloc)(size_t nmemb, size_t size) = NULL;
static void*  (*real_realloc)(void *ptr, size_t size) = NULL;

static void* func_malloc(size_t c);
static void* func_calloc(size_t nmemb, size_t size);
static void* func_realloc(void *ptr, size_t size);
static void func_free(void *ptr);


/************************************************************************
  FREE
 *************************************************************************/
void org_free(void *ptr){
    printf ("GLIBC Free called\n");
    /* call the real glibc function and return the result */
    real_free =  dlsym(RTLD_NEXT, "free");
    if (real_free != NULL){
        real_free(ptr);
    }
    return;

}

static void func_free(void* p) {
    org_free(p);
    return;
}

void free(void* p) {
    printf("LD preload FREE is done here.\n");
    func_free(p);
    return;
}

/************************************************************************
  MALLOC
 ***********************************************************************/
/* Function pointers to hold the value of the glibc functions */
static void *org_malloc (size_t c)
{
    printf ("GLIBC malloc called with %zu\n", c);
    /* call the real glibc function and return the result */
    real_malloc =  dlsym(RTLD_NEXT, "malloc");
    if (real_malloc != NULL){
        return real_malloc(c);
    }

    return NULL;
}


static void* func_malloc(size_t c)
{
    char *temp = NULL;
    temp = (char *)org_malloc(c);

    return temp;
}

void *malloc (size_t c)
{
    printf ("LD_PRELOAD malloc called with %zu\n", c);
    return func_malloc(c);
}

/************************************************************************
  CALLOC
 ***********************************************************************/
static void *org_calloc (size_t nmemb, size_t size)
{
    printf ("GLIBC calloc called with %zu\n", nmemb*size);
    /* call the real glibc function and return the result */
    real_calloc =  dlsym(RTLD_NEXT, "calloc");
    if (real_calloc != NULL)
    {
        return real_calloc(nmemb, size);
    }

    return NULL;
}


static void* func_calloc(size_t nmemb, size_t size)
{
    char *temp = NULL;
    temp = (char *)org_calloc(nmemb, size);

    return temp;

}

void *calloc(size_t nmemb, size_t size)
{
    printf ("LD_PRELOAD calloc called with %zu\n", nmemb*size);
    return func_calloc(nmemb, size);
}

/************************************************************************
  RE-ALLOC
 ***********************************************************************/
static void *org_realloc(void *ptr, size_t size)
{
    printf ("GLIBC re-alloc called with %zu\n", size);
    /* call the real glibc function and return the result */
    real_realloc =  dlsym(RTLD_NEXT, "realloc");
    if (real_realloc != NULL)
    {
        return real_realloc(ptr, size);
    }

    return NULL;
}


static void* func_realloc(void *ptr, size_t size)
{

    char *temp = NULL;
    temp = (char *)org_realloc(ptr, size);

    return temp;

}

void *realloc(void *ptr, size_t size)
{
    printf ("LD_PRELOAD realloc\n");
    return func_realloc(ptr, size);
}


Neelkanth_221$ cat test.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{

    printf("##########################  MALLOC ######################################\n");
    char *ptr = NULL;
    ptr = (char *)malloc(10 * sizeof(char));
    printf("###################################################################\n\n");

    char *ptr1;
    printf("######################## STRDUP  ###########################################\n");
    ptr1 = strdup("roger federer is the greatest of all times");
    printf("###################################################################\n\n");


    char *ptr2 = NULL;
    printf("####################### CALLOC ############################################\n");
    ptr2 = (char *)calloc(10 , sizeof(char));
    printf("###################################################################\n\n");


    char *ptr4;
    printf("######################## STRDUP  ###########################################\n");
    ptr4 = strdup("asdfkljhasdlfjkhasdlfjkhsadflh");
    printf("###################################################################\n\n");

    char *ptr3 = NULL;
    printf("####################### REALLOC #####################################\n");
    ptr3 = (char *)realloc(ptr2, 20*sizeof(char));
    printf("###################################################################\n\n");


    free(ptr);
    free(ptr1);
    free(ptr3);

    return 0;
}

Neelkanth_221$ cat exe.sh

#!/bin/bash

make clean
make
LD_PRELOAD=/home/labuser/Desktop/wrap-syscall-master/bin/libpreload.so ./bin/test


Output: Run the script

./exe.sh

rm -rf bin/*
gcc -fPIC -shared src/preload_library.c -o bin/libpreload.so -ldl
###############################################
compile the test program
###############################################
gcc -Wall -g src/test.c -o bin/test
src/test.c: In function ‘main’:
src/test.c:28:11: warning: variable ‘ptr4’ set but not used [-Wunused-but-set-variable]
     char *ptr4;
           ^
##########################  MALLOC ######################################
LD_PRELOAD malloc called with 10
GLIBC malloc called with 10
###################################################################

######################## STRDUP  ###########################################
LD_PRELOAD malloc called with 43
GLIBC malloc called with 43
###################################################################

####################### CALLOC ############################################
LD_PRELOAD calloc called with 10
GLIBC calloc called with 10
###################################################################

######################## STRDUP  ###########################################
LD_PRELOAD malloc called with 31
GLIBC malloc called with 31
###################################################################

####################### REALLOC #####################################
LD_PRELOAD realloc
GLIBC re-alloc called with 20
###################################################################

LD preload FREE is done here.
GLIBC Free called
LD preload FREE is done here.
GLIBC Free called
LD preload FREE is done here.
GLIBC Free called