Czasami może zaistnieć sytuacja, w której chcielibysmy uruchomić program na jakiejś maszynie, który chodziłby jak najdłużej i nie rzucał się w oczy (backdoor?;)... Zwykły user może pobawić się trochę z adminem - przypominać to będzie trochę walkę Dawida z Goliatem, jednak w każdej chwili Goliat może zadać śmiertelny cios i po zabawie... Można to mu oczywiście nieco utrudnić... Przpyatrzmy się więc następującemu programowi:
---hello.c---
#include <stdio.h>
#include <unistd.h>
int main() {
    /* tu się zaczyna nasz program */
    printf("hello, world...\n");
    if(fork()) exit(0);
    while(1);
}
---hello.c---
Program po uruchomieniu wita się, przechodzi w tło i czeka w nieskończonej pętli... Kompilujemy go gcc hello.c -o hello, następnie uruchamiamy ./hello. Administrator widzi, że program podejrzanie długo [przypuscmy;] chodzi, wydaje więc polecenie killall hello, kill [pid] czy jeszcze na inny sposób uścmierca nasz bezbronny programik... Ale zaraz, domyślnie kill, killall i inne takie wysyłają sygnał TERM, który możemy przecież przechwycić (możemy przechwycić wszystkie sygnały oprócz KILL(9) i STOP(19)), tym samym nasz program można troszkę uodpornić... Pomocne nam będą następujące funkcje: sigaction - zmienia akcję wykonywaną po otrzymaniu danego sygnału; sigprocmask - zmienia listę aktualnie blokowanych sygnałów; sigfillset - zapisuje w podanej zmiennej pełną listę sygnałow; sigdelset - kasuje dany sygnał z listy; Możemy więc przystąpić do pisania drugiej wersji programu:
---hello2.c---
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int main() {
    struct sigaction act;
    sigset_t set;
    void catchsig(int sig) {
        if(fork()) exit(0);
    }

    sigfillset(&set); /* tworzymy pełną listę sygnałów w zmiennej set */
    sigdelset(&set, SIGINT); /* usuwamy z niej te trzy sygnały, */
    sigdelset(&set, SIGQUIT);   /* gdyż będą nam potrzebne */
    sigdelset(&set, SIGTERM);         /* później :) */
    sigprocmask(SIG_SETMASK, &set, NULL); /* ustawiamy blokadę na sygnały
                                             znajduące się w zmiennej set */
    act.sa_handler=catchsig;
    sigaction(SIGINT, &act, NULL); /* teraz po otrzymaniu któregoś z tych */
    sigaction(SIGQUIT, &act, NULL); /* sygnałow zostanie wykonana funkcja */
    sigaction(SIGTERM, &act, NULL);    /* catchsig czyli fork i exit*/

    /* tu się zaczyna nasz program... */
    printf("hello, world...\n");
    if(fork()) exit(0);
    while(1);
}
---hello2.c---
Skompilujmy program, uruchommy go, napiszmy ps, zabijmy proces, napiszmy ps, zabijmy proces, napiszmy ps itd... Widzimy, że (o ile nie zostanie wysłany sygnał KILL) program po każdej próbie zabicia 'odradza sie' na nowo. O to nam właśnie chodziło... Jednak program cały czas widznieje na liście procesów pod tą samą nazwą... Trzeba to zmienić - napiszemy więc funkcje, która będzie zmieniała nazwę procesu (argv[0]) po próbie zabicia go na losowo wybraną z podanej tablicy. Powstanie tak program hello3.c :)
---hello3.c---
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

char *fakenames[]={
    "ps", "ls", "mail", "sh", "bash", "less", "man bash", "NULL"
};

char *givenewname(char **names) {
    int i, n, m;

    for(i=0;names[i]!=NULL;i++) ;
    m=(int)time((time_t)NULL);
    srand(m);
    n=random()%(i-1);
    return(names[n]);
}

int main(int argc, char **argv) {
    struct sigaction act;
    sigset_t set;
    void catchsig(int sig) {
        int i;

        for(i=0;i<argc;i++)
            bzero(argv[i], strlen(argv[i]));
        strcpy(argv[0], givenewname(fakenames));
        if(fork()) exit(0);
    }

    sigfillset(&set); /* tworzymy pełną listę sygnałów w zmiennej set */
[...i dalej już tak samo jak w hello2.c...]
---hello3.c---
Git - teraz po każdej próbie zabicia prorgam uruchamia się jako nowy proces pod inną nazwą... Jednak nie do końca. Gdy administrator wyda polecenie ps auxc wyświetli się 'true command name' czyli prawdziwa nazwa, również link exe w /proc/[pid]/ będzie wskazywał na rzeczywisty program. Jedynym ominięciem tego jest skopiowanie pliku gdzieś, następnie uruchomienie go z nowej lokalizacji, a potem skasowanie lub przywrócenie poprzedniej wersji. Spójrzmy więc na hello4.c - ostatni listing:
---hello4.c---
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

char *fakenames[]={
    "/tmp/ps", "/tmp/ls", "/tmp/mail", "/tmp/sh", "/tmp/bash", "/tmp/less",
    "/tmp/man", "tmp/ps", "NULL"
};

int getrealname(char *name, pid_t pid) {
    char exe[512];
    sprintf(exe, "/proc/%d/exe", pid);
    return (readlink(exe, name, 512));
}

char *givenewname(char **names) {
    int i, n, m;

    for(i=0;names[i]!=NULL;i++) ;
    m=(int)time((time_t)NULL);
    srand(m);
    n=random()%(i-1);
    return (names[n]);
}

int main(int argc, char **argv) {
    struct sigaction act;
    sigset_t set;
    static char realname[512];
    int a;

    void catchsig(int sig) {
        int ok=0;
        char *newpatch, *shortname, tmppatch[64];

        newpatch=givenewname(fakenames);
        shortname=strrchr(newpatch, '/');
        shortname++;
        sprintf(tmppatch, "%s.orygin", newpatch);

        if(!access(newpatch, F_OK) && access(newpatch, W_OK)) ok=-1;
        else {
            rename(newpatch, tmppatch);
            link(realname, newpatch);
        }

        if(!fork()) {
                printf("child starting %s\n", newpatch);
                execl((!ok)?newpatch:realname, shortname, realname, NULL);
        }
        else {
            sleep(1);
            if(ok) exit(0);
            unlink(newpatch);
            rename(tmppatch, newpatch);
            exit(0);
        }
    }

    chdir("/");

    if(argc>1) {
        if(strlen(argv[1])>512) {
           printf("buffer overflow test?\n");
           exit(1);
        }
        strcpy(realname, argv[1]);
    }
    else getrealname(realname, getpid());

    for(a=1; a<argc; a++)
        bzero(argv[a], strlen(argv[a]));

    sigfillset(&set); /* tworzymy pełną listę sygnałów w zmiennej set */
    sigdelset(&set, SIGINT); /* usuwamy z niej te trzy sygnały, */
    sigdelset(&set, SIGQUIT);   /* gdyż będą nam potrzebne */
    sigdelset(&set, SIGTERM);         /* później :) */
    sigprocmask(SIG_SETMASK, &set, NULL); /* ustawiamy blokadę na sygnały
                                            znajduące się w zmiennej set */
    act.sa_handler=catchsig;
    sigaction(SIGINT, &act, NULL); /* teraz po otrzymaniu któregoś z tych */
    sigaction(SIGQUIT, &act, NULL); /* sygnałow zostanie wykonana funkcja */
    sigaction(SIGTERM, &act, NULL);    /* catchsig czyli fork i exit*/

    /* tu się zaczyna nasz program... */
    printf("hello, world...\n");
    if(fork()) exit(0);
    while(1);
}
---hello4.c---
Jak to działa? Program po otrzymaniu jakiegoś sygnału wybiera losowa jakąś nazwę (z fakenames), następnie jeżeli taki plik już istnieje zmienia jego nazwę, kopiuje się pod nową nazwę, uruchamia się i na końcu zmienia zmienioną nazwę starego programu spowrotem :)... Można jeszcze kombinować np. uruchamiając program na nowo co 10 sekund, ale to już zależy tylko od inwencji twórczej programisty...