kernel mode sockets part 1 (the dirty way)

by nks · august 29 2008 · one comment

Version 0.3 – 23.01.2009

Kernel mode sockets sind hoch-interessant. Im ersten teil befasse ich mich mit einer einfachen Implementierung eines ziemlich simplen kernel mode sockets, das ist nicht der Weg wie man es machen sollte allerdings hilft es erstmal dem grundsätzlichem Verständnis von lkm – also kernel modulen unter linux.

Es ist natürlich trotzdem möglich diesen Code in einem rootkit zu verwenden etc. aber ich finde das dieser Code nichts in einem produktiven Umfeld zu suchen hat, man öffnet keine Dateien aus derm kernel mode.

Wir beginnen mit den Basics eines lkm.

Zu erst das klassische “Hello world.”:

/*
*  lkm_hello_world.c
*/
#include <linux/module.h>
#include <linux/kernel.h>
 
int init_module(void)
{
printk(KERN_INFO "Hello world.\n");
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world.\n");
 
}

Zum kompilieren des Moduls legen wir nun ein Makefile an:

obj-m += lkm_hello_world.o
 
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
 
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Vielleicht hat der ein oder andere versucht die sys/socket.h (etc.) einzubinden, aber das geht nicht. man sollte das auch nicht machen da das user-mode includes sind. Also machen wir weiter, wir erstellen wir einen simplen (dirty) socket im kernel mode. Ich hab mich dazu entschieden das wir den socket über den syscall “socketcall()” erstellen. Um syscalls aus dem kernel aufrufen zu können gab es bis kernel 2.6.19 die syscall macros (für alle mit einem neuerem kernel habe ich einen kleinen header mit den macros hoch geladen), allerdings kann man eigtl keine syscalls vom kernel aus machen, damit wir das können müssen wir aus dem kernel-space Adressraum in den user space Adressraum, das geschieht hier per set_fs(). und schon kann man userspace calls ausführen.

/*
*  lkm_dirty_socket.c - nks
*/
#include "syscall_macros.h" /* auskommentieren wenn die kernel version
unter 2.6.19/18 ist! */
 
#include <linux/module.h>
#include <linux/kernel.h>
 
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
 
#include <linux/init.h>
#include <linux/syscalls.h>
 
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <linux/unistd.h>
 
int errno; /* needed by socketcall() */
 
/*
int socketcall(int call, unsigned long *args);
wie wir sehen koennen werden der funktion socketcall() 2 call uebergeben,
also benutzen wir das _syscall2-makro:
*/
static inline _syscall2(int, socketcall, int, call, unsigned long *, args);
int init_module(void)
{
        unsigned long arg[3];
        int socket;
        mm_segment_t old_fs;
        struct sockaddr_in addr;
        struct sockaddr_in saddr;
 
        printk(KERN_INFO "#sixserv/sixserv.org presents:\n");
        printk(KERN_INFO "simple kernel mode socket - nks\n");
 
        old_fs = get_fs();
 
        /* die argumente fuer socketcall vorbereiten*/
        arg[0] = PF_INET;
        arg[1] = SOCK_STREAM;
        arg[2] = 0;
 
        set_fs(KERNEL_DS);
        if ((socket = socketcall(1, arg)) == -1) // SYS_SOCKET = 1
        {
                printk(KERN_INFO "-- Kernel Mode Socket ERROR...\n");
                printk(KERN_INFO "-- ERRNO: %d..\n",errno);
                set_fs(old_fs);
                return 0;
        }
        else
        {
                printk(KERN_INFO "++ Kernel Mode Socket is up ...\n");
                printk(KERN_INFO "++ ERRNO: %d..\n",errno);
        }
 
        return 0;
}
 
void cleanup_module(void)
{
        printk(KERN_INFO "Goodbye world.\n");
}

nun muessen wir die Makefile an das neue modul anpassen

obj-m += lkm_dirty_socket.o
 
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
 
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Nun können wir mit einem

# make
#insmod lkm_dirty_socket

das Modul bauen und laden, mit dmesg sollte man dann die Meldungen sehen ob es geklappt hat sieht man wenn der folgende text in der debug Ausgabe erscheint:

++ Kernel Mode Socket is up …

Aber nur ein socket alleine ist ja bekanntlich ziemlich langweilig :D
Also verbinden wir uns mit einem http Server. Hierfür müssen wir noch ein paar Anpassungen an unserem bestehendem Code tätigen:

/*
*  lkm_dirty_socket.c - nks
*/
#include "syscall_macros.h" /* auskommentieren wenn die kernel version unter 2.6.19/18 ist! */
 
#include <linux/module.h>
#include <linux/kernel.h>
 
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
 
#include <linux/init.h>
#include <linux/syscalls.h>
 
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <linux/unistd.h>
int errno; /* needed by socketcall() */
 
/*
int socketcall(int call, unsigned long *args);
wie wir sehen koennen werden der funktion socketcall() 2 parameter uebergeben,
also benutzen wir das _syscall2-makro:
*/
static inline _syscall2(int, socketcall, int, call, unsigned long *, args);
int init_module(void)
{
        unsigned long arg[3];
        int socket;
        mm_segment_t old_fs;
        struct sockaddr_in addr;
        struct sockaddr_in saddr;
        unsigned long arg1[3];
        unsigned long args[4];
        char buffer[1024];
 
        printk(KERN_INFO "#sixserv/sixserv.org presents:\n");
        printk(KERN_INFO "simple kernel mode socket - nks\n");
 
        old_fs = get_fs();
 
        arg[0] = PF_INET;
        arg[1] = SOCK_STREAM;
        arg[2] = 0;
 
        set_fs(KERNEL_DS);
        if ((socket = socketcall(1, arg)) == -1) // SYS_SOCKET = 1
        {
                printk(KERN_INFO "-- Kernel Mode Socket ERROR...\n");
                printk(KERN_INFO "-- ERRNO: %d..\n",errno);
                set_fs(old_fs);
                return 0;
        }
        else
        {
                printk(KERN_INFO "++ Kernel Mode Socket is up ...\n");
                printk(KERN_INFO "++ ERRNO: %d..\n",errno);
        }
 
        saddr.sin_addr.s_addr = inet_addr("79.140.33.153");
        saddr.sin_port = htons(80);
        saddr.sin_family = AF_INET;
        /* argumente fuer connect():
            int connect(int sockfd, struct sockaddr *serv_addr, int addrlen );
        */
        arg1[0] = socket;
        arg1[1] =  (unsigned long)&saddr;
        arg1[2] =  (unsigned long)sizeof(saddr);
 
         if ((socketcall(SYS_CONNECT, arg1)) == -1)
        {
                printk(KERN_INFO "-- Kernel Mode Socket ERROR...\n");
                printk(KERN_INFO "-- ERRNO: %d..\n",errno);
                set_fs(old_fs);
                return 0;
        }
        else
        {
                printk(KERN_INFO "++ Kernel Mode Socket is up an connected...\n");
                printk(KERN_INFO "++ ERRNO: %d..\n",errno);
        }
 
        /* argumente fuer send():
            send(int s, const void *buf, size_t len, int flags);
        */
        args[0] = socket;
        args[1] = "GET / HTTP/1.0\r\n\r\n";
        args[2] = strlen("GET / HTTP/1.0\r\n\r\n");
        args[3] = 0;
 
         if ((socketcall(SYS_SEND, args)) == -1)
        {
                printk(KERN_INFO "-- Kernel Mode Socket ERROR...\n");
                printk(KERN_INFO "-- ERRNO: %d..\n",errno);
                set_fs(old_fs);
                return 0;
        }
        else
        {
                printk(KERN_INFO "++ Kernel Mode Socket is sending stuff...\n");
                printk(KERN_INFO "++ ERRNO: %d..\n",errno);
 
        }
 
        /* argumente fuer recv():
            int recv(int s, void *buf, size_t len, int flags);
        */
        args[0] = socket;
        args[1] = (unsigned long) buffer;
        args[2] = 1024;
        args[3] = 0;
 
        if ((socketcall(SYS_RECV, args)) == -1)
        {
                printk(KERN_INFO "-- Kernel Mode Socket ERROR...\n");
                printk(KERN_INFO "++ ERRNO: %d..\n",errno);
                set_fs(old_fs);
                return 0;
        }
        else
        {
                printk(KERN_INFO "++ Kernel Mode Socket is recieving stuff...\n");
                printk(KERN_INFO "++ ERRNO: %d..\n",errno);
                printk(KERN_INFO "++ Recieved: %s..\n",buffer);
 
        }
 
       set_fs(old_fs);
       return 0;
}
 
void cleanup_module(void)
{
        printk(KERN_INFO "Goodbye world.\n");
}
 
unsigned int inet_addr(char *str)
{
        int a,b,c,d;
        char arr[4];
        sscanf(str,"%d.%d.%d.%d",&a,&b,&c,&d);
        arr[0] = a; arr[1] = b; arr[2] = c; arr[3] = d;
        return *(unsigned int*)arr;
}

Wieder ist eine neue Funktion hinzugekommen inet_addr, aber viel interessanter sind die neuen socketcalls, beim lesen kann man anhand des call-Arguments gut sehen welche socket-Funktion nun aufgerufen wird. zur Übergabe der Parameter verwenden wir ein char array bzw zwei da recv und send jeweils 4 Argumente haben.

Die syscall macros hab ich für euch hochgeladen

quellen:

Bei fragen etc. kommt doch einfach ins irc (#nullserv/#sixserv im freenode)

nks

ps:
benutzt öfters mal google-alternativen.. :D   http://cuil.com oder http://metager2.de

pps:
chillig, und wie ich finde sehr geil: http://www.myspace.com/17thboulevard
bin gespannt auf das album…

Tagged: , , , ,

One Comment

Subscribe to comments with RSS or TrackBack to 'kernel mode sockets part 1 (the dirty way)'.

  1. [...] public links >> syscalls kernel mode sockets part 1 (the dirty way) First saved by gvanden | 1 days ago Problem #27 Due tomorrow First saved by sergeya | 13 days [...]

Leave a Reply