Hook – Block IP address access:
1. Demand
- No process (real process) is allowed to access the websites in the blacklist;
- Use logs (in the form of process name + time + access object + result) to record intercepted access records and other successful access records;
- Check the blacklist regularly for changes.
2. Demand analysis
1. Goal:
– Implement a network access filter that allows or denies network connections and accepts requests based on a list of IP addresses in a blacklist.
2. Main functions:
- Blacklist management: The program maintains a blacklist of IP addresses, which is used to store IP addresses that are prohibited from access. - Connection filtering: Block connection attempts to IP addresses in the blacklist. - Access records: Record successful and intercepted network access events, including timestamp, process ID, operation type, IP address and port information. - Dynamic blacklist: Supports dynamically updating the blacklist, reading IP addresses from external files (blacklist.txt), and regularly checking for updates. - Based on dynamic linking: Use `dlopen` and `dlsym` to dynamically link the `connect` and `accept` functions in the libc.so.6 system library in order to intercept network connections.
3. Code structure:
- In the
main
function, the program initializes the blacklist, obtains the handle to the system library, and dynamically links theconnect
andaccept
functions. - The
connect
function is used to intercept network connections, check whether the IP address of the connection request is in the blacklist, record access events, and then decide whether to allow the connection based on the results. (Only ipv4 addresses are supported for now) - The
accept
function is similarly used to intercept accept requests and perform the same operations.
4. Blacklist management:
- The IP address list of the blacklist is represented by the
blacklist
array in the program, and new IP addresses can be loaded from the external fileblacklist.txt
when the program is running. - The blacklist file is checked every hour and if there are new IP addresses, the program will load them and update the blacklist.
5. Access records:
- Successful connections and intercepted access events will be recorded to the `ip_access_log.txt` log file, including timestamp, process ID, operation type, IP address and port information.
6. Dynamic link:
- The program uses `dlopen` and `dlsym` to dynamically link the `connect` and `accept` functions in the system library so that it can intercept the calls of these functions and add custom filtering logic.
7. Exception handling:
- If the blacklist file cannot be read, the program will output an error message.
8. Test address:
- Some IP addresses for testing are provided, including local machine addresses, Baidu, Google’s public DNS servers, and Cloudflare’s public DNS servers.
3. Code deployment
1. Make sure there is a blacklist file in the current directory: blacklist.txt 2. Make sure there is a log file in the current directory: ip_access_log.txt 3. Query several IP addresses available for testing for command line testing: telnet x.x.x.x Provided here: Local address: 127.0.0.1 Baidu: 220.181.38.149 Google’s public DNS servers: 8.8.8.8 Cloudflare's public DNS servers: 1.1.1.1 4. Use the following command to compile the ip.c file into a dynamic link library gcc ip.c -fpic -shared -ldl -o ip.so 5. Use the following command to inject the ip.so dynamic library into the current directory export LD_PRELOAD=./ip.so 6. Test telnet 127.0.0.1 telnet 220.181.38.149 telnet 8.8.8.8 telnet 1.1.1.1
4. Code
#define _GNU_SOURCE // Enable GNU extended features #include <stdio.h> #include <sys/types.h> #include <dlfcn.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <time.h> /* IP addresses available for testing: Local address: 127.0.0.1 Baidu: 220.181.38.149 Google’s public DNS servers: 8.8.8.8 Cloudflare's public DNS servers: 1.1.1.1 */ // Define a blacklist, including IP addresses that are prohibited from access const char *blacklist[] = { "127.0.0.1"}; //The number of IP addresses in the blacklist static int numBlacklist = sizeof(blacklist) / sizeof(blacklist[0]); //Record the time of the last blacklist check static time_t lastBlacklistCheckTime = 0; //Define the path to the blacklist file static const char *blacklistFile = "blacklist.txt"; //Record access records to the log file void logAccess(const char *action, int sockfd, const char *ip, int port); // Check if the blacklist has changed void checkBlacklistChanges(); // Determine whether the accessed network is a filtered IP, and if so, return early (for the IP address where the client initiates the connection) typedef int (*new_socket)(int, const struct sockaddr *, socklen_t); typedef int (*new_accept)(int, struct sockaddr *, socklen_t *); // (for the IP address where the client initiated the connection) intercept the connection request int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { void *handle = NULL; new_socket old_connect = NULL; // Save as old_connect for call // Get the handle of libc.so.6 handle = dlopen("libc.so.6", RTLD_LAZY); //Return the loading address of the open function in libc.so.6 old_connect = (new_socket)dlsym(handle, "connect"); char ip[128]; memset(ip, 0, sizeof(ip)); // ip address int port = -1; // port number // If the incoming address is an IPv4 address if (AF_INET == addr->sa_family) { // Force the incoming address to an IPv4 address structure struct sockaddr_in *sa4 = (struct sockaddr_in *)addr; // Use the inet_ntop function to convert the IPv4 address to human-readable string form (IP address) inet_ntop(AF_INET, (void *)(struct sockaddr *) & sa4->sin_addr, ip, sizeof(ip)); //Extract the port number and convert it from network byte order to host byte order port = ntohs(sa4->sin_port); //Print IP address and port number printf("\ AF_INET IP===%s:%d\ ", ip, port); } checkBlacklistChanges(); //Record access information to the log file logAccess("connect", sockfd, ip, port); for (int i = 0; i < numBlacklist; + + i) { if (0 == strcmp(ip, blacklist[i])) { printf("\ ===%s netfilter...connect failed!\ ", ip); //Record intercepted access information to the log file logAccess("connect (Blocked)", sockfd, ip, port); return -1; } } printf("\ PID:%d, socket:%d, %s Successfully connected!\ ", getpid(), sockfd, ip); //Record successful access information to the log file logAccess("connect (Success)", sockfd, ip, port); return old_connect(sockfd, addr, addrlen); } // Monitor and control connection requests initiated through sockets (for the IP address of the server initiating the connection), intercept and accept requests int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { void *handle = NULL; new_accept old_accept = NULL; // Save old_accept for call // Get the handle of libc.so.6 handle = dlopen("libc.so.6", RTLD_LAZY); // Return the loading address of the accept function in libc.so.6 old_accept = (new_accept)dlsym(handle, "accept"); char ip[128]; memset(ip, 0, sizeof(ip)); // IP address int clientfd = -1; // client socketfd if (AF_INET == addr->sa_family) { // Force the incoming address to an IPv4 address structure struct sockaddr_in *sa4 = (struct sockaddr_in *)addr; // Use the inet_ntoa function to convert the IPv4 address to an IP address string and copy it to the ip variable strcpy(ip, inet_ntoa(sa4->sin_addr)); } checkBlacklistChanges(); //Record access information to the log file logAccess("accept", sockfd, ip, -1); for (int i = 0; i < numBlacklist; + + i) { if (0 == strcmp(ip, blacklist[i])) { printf("\ PID:%d ===%s=== netfilter...accept failed!\ ", getpid(), ip); //Record intercepted access information to the log file logAccess("accept (Blocked)", sockfd, ip, -1); return -1; } } // Release and call the original accept function to accept the client's connection request clientfd = old_accept(sockfd, addr, addrlen); printf("\ PID:%d, clientfd:%d, %s Successfully accepted!\ ", getpid(), clientfd, ip); //Record successful access information to the log file logAccess("accept (Success)", sockfd, ip, -1); return clientfd; } // Record the access results to the log file void logAccess(const char *action, int sockfd, const char *ip, int port) { time_t now; struct tm *tm_info; char timestamp[20]; time( & amp;now); // Get the current time tm_info = localtime( & amp;now); // Convert the current time to the local time structure strftime(timestamp, 20, "%Y-%m-%d %H:%M:%S", tm_info); // Format time as a string (year-month-day hour: minute: second) char logEntry[256]; // Store log entries snprintf(logEntry, sizeof(logEntry), "[%s] [PID:%d] [%s] [IP:%s] [Port:%d]\ ", timestamp, getpid(), action, ip, port); //Open the log file and append information to the end of the file FILE *logFile = fopen("ip_access_log.txt", "a"); if (logFile != NULL) { fputs(logEntry, logFile); fclose(logFile); } } // Check if the blacklist has changed void checkBlacklistChanges() { time_t current_time; time(& amp;current_time); // The time interval for checking blacklist changes, here is set to 1 hour if (current_time - lastBlacklistCheckTime >= 3600) { FILE *file = fopen(blacklistFile, "r"); if(file) { //Read the contents of the blacklist file char line[256]; int numBlacklistCount = 0; // Track the number of IP addresses in the blacklist while (fgets(line, sizeof(line), file)) { //Remove the newline character at the end of the line line[strcspn(line, "\ ")] = '\0'; // Add the new IP address to the blacklist blacklist[numBlacklistCount] = strdup(line); numBlacklistCount + + ; } numBlacklist = numBlacklistCount; fclose(file); //Update the number of IP addresses in the blacklist for (int i = numBlacklistCount; i < sizeof(blacklist) / sizeof(blacklist[0]); + + i) { blacklist[i] = NULL; } lastBlacklistCheckTime = current_time; // Update the last check time } } else { perror("Error reading blacklist file"); } }