10 Eylül 2020 Perşembe

GNU Runtime - Main'den Önce Çağrılabilecek Metodlar

Giriş
Main'den önce bir sürü metod çağrılır. Açıklaması şöyle.
Main is usually a programmer-defined entry point, while entry is defined by the compiler, it's doing many other operations such as libc initializations, heap allocation, and so on, and eventually, call the user-defined main entry point. You can see main as a callback function that defined by the user and eventually called by entry.
_start metodu
main'den daha önce çağrılır. Açıklaması şöyle.
With the GNU C library, on x86-64, execution starts at the _start entry point, which calls __libc_start_main to set things up, and the latter ends up calling main. But before calling main, it calls a number of other functions, which causes various pieces of data to be written to the stack. The stack’s contents aren’t cleared in between function calls, so when you get into main, your stack contains leftovers from the previous function calls.
shell veya gui'den yapılan execve() çağrısı ile tetiklenen loader önce argc, argv, envp değişkenlerini stack'a doldurur. Daha sonra uygulamamın _start() metodunu çağırır. Şeklen şöyledir.
// Compile it with gcc -nostartfiles

void _start(){
  int ret = my_main();
  exit(ret); 
} 
int my_main() 
{
  puts("This is a program without main!\n");
  return 0; 
}
__libc_start_main metodu
Örnek
Açıklaması şöyle. Burada amaç dynamic loader bazı metodları çalıştırmadan araya girmek.
A trick you can use on all platforms is to LD_PRELOAD a small library which overides __libc_start_main() and does a pointless system call like write(-1, "IT_STARTS_HERE", 14) before calling the original __libc_start_main().
Şöyle yaparız
#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
int __libc_start_main(
  int (*main)(int,char**,char**), int ac, char **av,
  int (*init)(int,char**,char**), void (*fini)(void),
  void (*rtld_fini)(void), void *stack_end)
{
  typeof(__libc_start_main) *next = dlsym(RTLD_NEXT, "__libc_start_main");
  write(-1, "IT_STARTS_HERE", 14); errno = 0;
  return next(main, ac, av, init, fini, rtld_fini, stack_end);
}
__attribute__((constructor) işaretli metodlar
Gcc ile __attribute__((constructor) olarak işaretli bir metod main'den önce veya sonra çağrılabilir.
Yukarıdaki şekilde constructors1...n ile gösterilen kutucukta bu çağrıların main'den önce olduğu görülebilir.

Main'den önce çağırma örneği:
#include <stdio.h>

void beforeMain (void) __attribute__((constructor));

void beforeMain (void)
{
  printf ("\nThis is before main\n");
}

int main ()
{
 printf ("\nThis is my main \n");
 return 0;
}
Main'den  sonra çağırma örneği
static void before_main(void) __attribute__((constructor));
static void after_main(void) __attribute__((destructor));

static void before_main(void)
{
    /* This is run before main() */
}

static void after_main(void)
{
    /* This is run after main() returns (or exit() is called) */
}
.init_array
Main'den önce argc vve argv parametrelerine erişebiliriz. Şöyle yapalır.
#include <stdio.h>

static int printargs(int argc, char** argv, char** env) {
  puts("In printargs:");
  for (int i = 0; i < argc; ++i)
  printf("  Arg %d (%p) '%s'\n", i, (void*)argv[i], argv[i]);
  return 0;
}

/* Put the function into the init_array */
__attribute__((section(".init_array"))) static void *ctr = &printargs;
Bu kodu bir .so dosyasında kullanırsak sonucu şöyle görürüz.
$ gcc -o printargs.so -std=c11 -shared -fpic printargs.c
$ LD_PRELOAD=./printargs.so /bin/echo Hello, world.
In printargs:
  Arg 0 (0x7ffc7617102f) '/bin/echo'
  Arg 1 (0x7ffc76171039) 'Hello,'
  Arg 2 (0x7ffc76171040) 'world.'
Hello, world.

Hiç yorum yok:

Yorum Gönder