In diesem Artikel wird hauptsächlich die Systemfunktionunter Linux kurz analysiert. Interessierte Freunde können sich auf die
einfache Analyse beziehen Der relevante Inhalt von Die Systemfunktion unter Linux lautet wie folgt:int libc_system (const char *line) { if (line == NULL) /* Check that we have a command processor available. It might not be available after a chroot(), for example. */ return do_system ("exit 0") == 0; return do_system (line); } weak_alias (libc_system, system)
static int do_system (const char *line) { int status, save; pid_t pid; struct sigaction sa; #ifndef _LIBC_REENTRANT struct sigaction intr, quit; #endif sigset_t omask; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigemptyset (&sa.sa_mask); DO_LOCK (); if (ADD_REF () == 0) { if (sigaction (SIGINT, &sa, &intr) < 0) { (void) SUB_REF (); goto out; } if (sigaction (SIGQUIT, &sa, &quit) < 0) { save = errno; (void) SUB_REF (); goto out_restore_sigint; } } DO_UNLOCK (); /* We reuse the bitmap in the 'sa' structure. */ sigaddset (&sa.sa_mask, SIGCHLD); save = errno; if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) { #ifndef _LIBC if (errno == ENOSYS) set_errno (save); else #endif { DO_LOCK (); if (SUB_REF () == 0) { save = errno; (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); out_restore_sigint: (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); set_errno (save); } out: DO_UNLOCK (); return -1; } } #ifdef CLEANUP_HANDLER CLEANUP_HANDLER; #endif #ifdef FORK pid = FORK (); #else pid = fork (); #endif if (pid == (pid_t) 0) { /* Child side. */ const char *new_argv[4]; new_argv[0] = SHELL_NAME; new_argv[1] = "-c"; new_argv[2] = line; new_argv[3] = NULL; /* Restore the signals. */ (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); INIT_LOCK (); /* Exec the shell. */ (void) execve (SHELL_PATH, (char *const *) new_argv, environ); _exit (127); } else if (pid < (pid_t) 0) /* The fork failed. */ status = -1; else /* Parent side. */ { /* Note the system() is a cancellation point. But since we call waitpid() which itself is a cancellation point we do not have to do anything here. */ if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) status = -1; } #ifdef CLEANUP_HANDLER CLEANUP_RESET; #endif save = errno; DO_LOCK (); if ((SUB_REF () == 0 && (sigaction (SIGINT, &intr, (struct sigaction *) NULL) | sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0) || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0) { #ifndef _LIBC /* glibc cannot be used on systems without waitpid. */ if (errno == ENOSYS) set_errno (save); else #endif status = -1; } DO_UNLOCK (); return status; } do_system
#ifdef FORK pid = FORK (); #else pid = fork (); #endif if (pid == (pid_t) 0) { /* Child side. */ const char *new_argv[4]; new_argv[0] = SHELL_NAME; new_argv[1] = "-c"; new_argv[2] = line; new_argv[3] = NULL; /* Restore the signals. */ (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); INIT_LOCK (); /* Exec the shell. */ (void) execve (SHELL_PATH, (char *const *) new_argv, environ); _exit (127); } else if (pid < (pid_t) 0) /* The fork failed. */ status = -1; else /* Parent side. */ { /* Note the system() is a cancellation point. But since we call waitpid() which itself is a cancellation point we do not have to do anything here. */ if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) status = -1; }
Tatsächlich generiert es einen Unterprozess und ruft
/bin/sh -c „command“ auf
#define SHELL_PATH "/bin/sh" /* Path of the shell. */ #define SHELL_NAME "sh" /* Name to give it. */
Das Folgende ist eigentlich der Grund und der Schwerpunkt meiner Forschung zur Systemfunktion: In der PWN-Frage von CTF schlägt der Aufruf der Systemfunktion durch Stapelüberlauf manchmal fehl dass die Umgebungsvariable überschrieben wird, aber ich war immer verwirrt. Ich habe es heute eingehend studiert und es endlich herausgefunden.
Die hier für die Systemfunktion erforderlichen Umgebungsvariablen werden in der globalen Variablenumgebung gespeichert. Was ist also der Inhalt dieser Variablen? environ ist in glibc/csu/libc-start.c definiert. Schauen wir uns ein paar Schlüsselaussagen an.libc_start_main ist die von _start aufgerufene Funktion, die einige Initialisierungsarbeiten zu Beginn des Programms erfordert. Wenn Sie diese Begriffe nicht verstehen, können Sie diesen Artikel lesen. Schauen Sie sich als Nächstes die Funktion LIBC_START_MAIN an.
Wir können sehen, dass der Wert von environ in Zeile 19 definiert ist, ohne SHARED zu definieren. Bevor das Startprogramm LIBC_START_MAIN aufruft, speichert es zunächst die Umgebungsvariablen und die# define LIBC_START_MAIN libc_start_main
in argv (eigentlich wird sie auf dem Stapel gespeichert) und speichert dann nacheinander die Adressen jeder Zeichenfolge in der Umgebungsvariablen und Jede Zeichenfolge in argv. Die Adresse der Elementzeichenfolge und argc werden auf den Stapel verschoben, daher muss sich das Umgebungsvariable
ArraySTATIC int LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv, #ifdef LIBC_START_MAIN_AUXVEC_ARG ElfW(auxv_t) *auxvec, #endif typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end) { /* Result of the 'main' function. */ int result; libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up; #ifndef SHARED char **ev = &argv[argc + 1]; environ = ev; /* Store the lowest stack address. This is done in ld.so if this is the code for the DSO. */ libc_stack_end = stack_end; ...... /* Nothing fancy, just call the function. */ result = main (argc, argv, environ MAIN_AUXVEC_PARAM); #endif exit (result); }
Wenn also die Länge des Stapelüberlaufs zu groß ist und der Überlaufinhalt den wichtigen Inhalt in der Adresse in der Umgebung abdeckt, schlägt der Aufruf der Systemfunktion fehl. Wie weit die spezifische Umgebungsvariable von der Überlaufadresse entfernt ist, kann durch eine Unterbrechung in _start überprüft werden.
Das obige ist der detaillierte Inhalt vonEinführung in die Analyse der Systemfunktion unter Linux. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!