SysCalls



  • Hallo liebe Community,

    Also beschäftige mich nun eine Weile lang mit Kernel Programmierung. Was ist mein Ziel?
    Ich will sys_calls umleiten, so dass z.b. wenn ein sys_read, sys_write etc. gemacht wird, eine Ausgabe wie "Befehl geloggt" ausgegeben wird.

    Da die SysCallTable seit dem 2.6.x Kernel ja nicht mehr exportiert wird, habe ich hier verschiedene Ansätze.

    1. Über ein Codestück, welches den Memory durchläuft und nach der Sys_Call_Table sucht.
    2. Über System.map
    3. Über /proc/kallsyms

    cat /proc/kallsyms | grep sys_call_table
    c0509940 R sys_call_table

    Mein Modul nimmt nun die Adresse der SysCallTable als Parameter entgegen:

    //CNYK irc.linux.org #kernel
    
    #include <linux/string.h>
    
    #include <linux/smp_lock.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/file.h>
    #include <linux/fs.h>
    #include <linux/sched.h>
    #include <linux/syscalls.h>
    #include <linux/time.h>
    #include <asm/unistd.h>
    #include <linux/version.h>
    #include <linux/errno.h>
    #include <linux/dcache.h>
    #include <linux/mm.h>
    #include <asm/uaccess.h>
    #include <asm/string.h>
    #define __KERNEL_SYSCALLS__
    #include <linux/dirent.h>
    #include <linux/fcntl.h>
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("bli bla blu");
    
    unsigned long sctable_addr;
    module_param(sctable_addr, ulong, S_IRUGO); 
    
    asmlinkage int (*original_sys_exit)(int);
    
    asmlinkage int our_fake_exit_function(int error_code)
    {
            printk("HEY! sys_exit called with error_code=%d\n",error_code);
    
            /*call the original sys_exit*/
           // return original_sys_exit(error_code);
    	return 1;
    }
    
    /* main function entry point */
    static int __init readlog_init(void) 
    {
    	void **sctable = (void **)sctable_addr;
            if (sctable == NULL)
                    return -EINVAL;
    
    	printk(KERN_INFO "%p\n", sctable[__NR_write]);
    	printk(KERN_INFO "\n%p\n", sctable[0]);
    
    	printk(KERN_INFO "SysCallTable is located at: %p\n", sctable);
    	printk(KERN_INFO "SysCallWrite is located at: %p\n", sctable[__NR_write]);
    	printk(KERN_INFO "SysCallRead is located at: %p\n", sctable[__NR_read]);
    	printk(KERN_INFO "SysCallOpen is located at: %p\n", sctable[__NR_open]);
    	printk(KERN_INFO "SysCallFork is located at: %p\n", sctable[__NR_fork]);
    
    	/*store reference to the original sys_exit*/
            original_sys_exit=sctable[__NR_exit];
    	printk(KERN_INFO "Exit: %p\n", sctable[__NR_exit]);
          	sctable[__NR_exit]=our_fake_exit_function;
    
    	printk(KERN_INFO "UNTIL HERE\n");
    
       	return 0;
    }
    
    static void __exit readlog_exit(void)
    {
    /* You MUST restore sctable[__NR_exit] or the system will crash soon */
    	printk(KERN_ALERT "\nModule unloaded\n");
    
    	return;
    }
    
    module_init(readlog_init);
    module_exit(readlog_exit);
    

    Nun, was passiert?

    sudo insmod hello9.ko sctable_addr=0xc0509940
    Segmentation fault

    Mal sehen:

    dmesg

    [33077.095488] c0144a60
    [33077.095491] SysCallTable is located at: c0509940
    [33077.095496] SysCallWrite is located at: c01bdbb0
    [33077.095500] SysCallRead is located at: c01bdda0
    [33077.095504] SysCallOpen is located at: c01bb790
    [33077.095508] SysCallFork is located at: c01023f0
    [33077.095512] Exit: c013dbc0
    [33077.095528] BUG: unable to handle kernel paging request at c0509944
    [33077.095536] IP: [<e0bbc0b8>] readlog_init+0xb8/0xd2 [hello9]
    [33077.095547] *pde = 1d49d163 *pte = 00509161
    [33077.095556] Oops: 0003 [#1] SMP
    [33077.095563] last sysfs file: /sys/devices/pci0000:00/0000:00:1e.0/0000:06:04.0/rf_kill
    [33077.095571] Dumping ftrace buffer:
    [33077.095577] (ftrace buffer empty)
    [33077.095581] Modules linked in: hello9(+) usb_storage isofs udf crc_itu_t arc4 ecb ieee80211_crypt_wep binfmt_misc ppdev bridge stp bnep input_polldev sbp2 lp parport joydev pcmcia snd_hda_intel snd_pcm_oss snd_mixer_oss snd_pcm snd_seq_dummy snd_seq_oss snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq snd_timer snd_seq_device tifm_sd yenta_socket rsrc_nonstatic pcspkr snd ipw2200 iTCO_wdt iTCO_vendor_support psmouse tifm_7xx1 pcmcia_core soundcore serio_raw video tifm_core snd_page_alloc ieee80211 ieee80211_crypt sony_laptop output intel_agp agpgart e100 mii ohci1394 ieee1394 fbcon tileblit font bitblit softcursor
    [33077.095681]
    [33077.095687] Pid: 21190, comm: insmod Not tainted (2.6.28-11-generic #42-Ubuntu) VGN-FS115M
    [33077.095694] EIP: 0060:[<e0bbc0b8>] EFLAGS: 00010282 CPU: 0
    [33077.095701] EIP is at readlog_init+0xb8/0xd2 [hello9]
    [33077.095707] EAX: 00000021 EBX: c0509940 ECX: ffffffff EDX: 00c50000
    [33077.095713] ESI: e0bbc000 EDI: 00000000 EBP: c5e49e24 ESP: c5e49e18
    [33077.095718] DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
    [33077.095724] Process insmod (pid: 21190, ti=c5e48000 task=ddb1bed0 task.ti=c5e48000)
    [33077.095729] Stack:
    [33077.095733] e0bb7089 c013dbc0 00000000 c5e49f8c c010111e c5e49e50 c020c015 c020b7e3
    [33077.095746] c5e49e50 c020b85f c7bdeba0 c5e49e5c 00000002 c10f3ce0 c06d3380 00002400
    [33077.095761] c5e49e80 00000206 00000110 00000001 00000000 00000206 00000000 00000286
    [33077.095776] Call Trace:
    [33077.095781] [<c013dbc0>] ? sys_exit+0x0/0x20
    [33077.095793] [<c010111e>] ? _stext+0x2e/0x170
    [33077.095801] [<c020c015>] ? sysfs_addrm_finish+0x15/0xf0
    [33077.095811] [<c020b7e3>] ? sysfs_add_one+0x13/0x50
    [33077.095819] [<c020b85f>] ? sysfs_addrm_start+0x3f/0xa0
    [33077.095827] [<c01a908c>] ? __vunmap+0x9c/0xe0
    [33077.095837] [<c01a908c>] ? __vunmap+0x9c/0xe0
    [33077.095851] [<c01a9121>] ? vfree+0x21/0x30
    [33077.095859] [<c0163f3a>] ? load_module+0x103a/0x1040
    [33077.095884] [<c0163fc8>] ? sys_init_module+0x88/0x1b0
    [33077.095892] [<c01bb5da>] ? sys_close+0x7a/0xc0
    [33077.095900] [<c0103f6b>] ? sysenter_do_call+0x12/0x2f
    [33077.095908] [<c0500000>] ? relay_hotcpu_callback+0x6d/0xbd
    [33077.095919] Code: c7 04 24 38 71 bb e0 89 44 24 04 e8 11 4a 94 df 8b 43 04 a3 04 97 bb e0 8b 43 04 c7 04 24 89 70 bb e0 89 44 24 04 e8 f6 49 94 df <c7> 43 04 00 70 bb e0 c7 04 24 96 70 bb e0 e8 e3 49 94 df 31 c0
    [33077.095996] EIP: [<e0bbc0b8>] readlog_init+0xb8/0xd2 [hello9] SS:ESP 0068:c5e49e18
    [33077.096009] ---[ end trace 20b22d0045744765 ]---

    Wie man also sieht bricht er mit einer Segmentation Fault genau an dieser Zeile ab:

    sctable[__NR_exit]=our_fake_exit_function;
    

    Arbeite nun seit Wochen daran und finde keine Lösung.
    Ist das ne WriteProtection?
    Was läuft falsch?

    Hoffe jemand kann mir unter die Arme greifen, schonmal danke!



  • Kann es vielleicht sein, dass die Zeile:

    module_param(sctable_addr, ulong, S_IRUGO);
    

    ein ulong in dezimal, und nicht in hexadezimal erwartet? Also 3226507584 statt c0509940 ? Oder würde sonst schon insmod meckern bevor er das Modul einfügt? (Bin kein Kernel-Hacker, war nur ne Idee.)



  • Hallo,

    Danke für deine Anregung :=)
    Hab das wirklich übersehen.

    Habs gerade korrigiert, jedoch leider mit dem selben Ergebnis? 😞



  • Spuckt:

    printk(KERN_INFO "SysCallTable is located at: %p\n", sctable);
    

    jetzt auch die dezimale Schreibweise aus? Deine erste dmesg Ausgabe scheint mir eher einen String auszugeben statt der Adresse des Pointers (sollte normalerweise bei hexadezimal mit 0x anfangen).



  • Also er spuckt genau das selbe aus:

    [ 205.723575] SysCallTable is located at: c0509940
    [ 205.723580] SysCallWrite is located at: c01bdbb0
    [ 205.723584] SysCallRead is located at: c01bdda0
    [ 205.723588] SysCallOpen is located at: c01bb790
    [ 205.723592] SysCallFork is located at: c01023f0

    Scheint eher an dem Überschreiben zu liegen, laut dmesg:

    [ 205.723612] BUG: unable to handle kernel paging request at c0509944

    Grüße



  • Das kann nicht funktionieren, wenn der Kernel die Adresse der sys_call_table weiterhin frei zugänglich machen würde, dann hätte man auch einfach das Symbol weiterhin exportieren können.

    Aber lies dir das hier mal durch http://www.kernelhacking.com/rodrigo/docs/StMichael/kernel-land-rootkits.pdf ob das in aktuellen Kernels (noch) geht kann ich dir nicht sagen, aber auf den ersten Blick scheint das ein guter Ansatzpunkt zu sein, evt. musst du es noch ein wenig modifizieren.



  • Ok, also du kannst die sys_call_table wie folgt bekommen:

    void *get_system_call(void);
    void *get_sys_call_table(void *system_call);
    
    /* Und so bekommt du die sys_call_table: */
    void* sys_call_table = get_sys_call_table( get_sytem_call() );
    
    /* Code von http://www.enye-sec.org */
    
    void *get_system_call(void)
    {
    unsigned char idtr[6];
    unsigned long base;
    struct idt_descriptor desc;
    
    asm ("sidt %0" : "=m" (idtr));
    base = *((unsigned long *) &idtr[2]);
    memcpy(&desc, (void *) (base + (0x80*8)), sizeof(desc));
    
    return((void *) ((desc.off_high << 16) + desc.off_low)); 
    
    }
    
    void *get_sys_call_table(void *system_call)
    {
    unsigned char *p;
    unsigned long s_c_t;
    
    p = (unsigned char *) system_call;
    
    while (!((*p == 0xff) && (*(p+1) == 0x14) && (*(p+2) == 0x85)))
    	p++;
    
    dire_call = (unsigned long) p;
    
    p += 3;
    s_c_t = *((unsigned long *) p);
    
    p += 4;
    after_call = (unsigned long) p;
    
    /* cli */
    while (*p != 0xfa)
    	p++;
    
    dire_exit = (unsigned long) p;
    
    return((void *) s_c_t);
    
    }
    

    Das Verfahren ist das aus dem PDF beschriebene, wenn du die Präsentation allerdings durchgelesen hast wirst du wissen, dass das Verfahren sehr leicht geblockt werden kann.



  • @Tippgeber: Die Adresse der Tabelle hat er doch schon und die stimmt ja auch.

    Genau so wie bestimmte Bereiche aus /dev/mem geblockt werden, wird dann hier auch der Speicherbereich geschützt sein (wie Tippgeber ja bereits gesagt hat). Unter einem älteren Kernel (aus Ubuntu 8.04 , wenn ich mich richtig erinnere) hat das ganze bei mir aber noch funktioniert. (BTW: Welche Distribution benutzt du?)

    @joboyjo: Kannst du den Kernel auch anpassen wie du willst oder muss dein Modul für jeden Kernel passen? Beim Kernel-Build lässt sich auch konfigurieren, dass er sys_call_table weiterhin an Module exportieren soll.



  • ich frag mich nur zu was das ganze gut sein soll vorhandene kernelfunktionen zu überschreiben. (evtl für einen rootkit?)



  • @tippgeber.

    Das ist kompletter Schwachsinn, sry:

    1. Hatte ich das selbe Script wie du, nur selber programmiert und leicht abgeändert. => Gibt genau die selbe Adresse zur SysCallTable aus

    2. /proc/kallsyms wird bei jedem reboot neu geschrieben und dort werden sämtliche symbole ausgelagert, damit auch die sys_call_adresse.

    @devkid
    Habe das aktuelle Ubuntu 9.04 kernel version
    2.6.28.11-generic

    das modul sollte eigentlich für sämtliche 2.6.x kernel funktionieren.



  • joboyy schrieb:

    @devkid
    Habe das aktuelle Ubuntu 9.04 kernel version
    2.6.28.11-generic

    Ich auch, und da funktioniert es bei mir auch nicht. Ich hab jetzt leider keinen andere Distro/Kernel installiert zum Testen.

    joboyy schrieb:

    das modul sollte eigentlich für sämtliche 2.6.x kernel funktionieren.

    Naja, source-kompatibel würde es bleiben, der Kernel müsste nur speziell konfiguriert sein...



  • joboyy schrieb:

    @tippgeber.

    Das ist kompletter Schwachsinn, sry:

    1. Hatte ich das selbe Script wie du, nur selber programmiert und leicht abgeändert. => Gibt genau die selbe Adresse zur SysCallTable aus

    2. /proc/kallsyms wird bei jedem reboot neu geschrieben und dort werden sämtliche symbole ausgelagert, damit auch die sys_call_adresse.

    Stimmt. Versuch mal bevor du auf die sys_call_table schreibst ihr die Schreibberechtigung zu geben, scheinbar ist die Seite ab 2.6.24 read-only.
    Das geht mit "set_memory_rw( sctable, 1);", diese Funktion ist in arch/x86/mm/pageattr.c zu finden.



  • @tippgeber
    Danke, ich dachte schon an eine WriteProtection.
    Werde mir das jetzt mal genauer ansehen und die Ergebnisse hier posten 🙂

    Grüße



  • /home/si/Desktop/kernel/hello11.c:51: Fehler: Implizite Deklaration der Funktion »set_memory_rw«

    ...
    Er scheint die Funktion nicht zu kennen. Die Includes sind:

    #include <linux/string.h>
    #include <linux/smp_lock.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/file.h>
    #include <linux/fs.h>
    #include <linux/sched.h>
    #include <linux/syscalls.h>
    #include <linux/time.h>
    #include <asm/unistd.h>
    #include <linux/version.h>
    #include <linux/errno.h>
    #include <linux/dcache.h>
    #include <linux/mm.h>
    #include <asm/uaccess.h>
    #include <asm/string.h>
    #define __KERNEL_SYSCALLS__
    #include <linux/dirent.h>
    #include <linux/fcntl.h>
    


  • Du musst halt die Funktionsdeklaration angeben: int set_memory_rw(unsigned long addr, int numpages);

    Aber du kannst dir die Mühe sparen, denn es funktioniert nicht, habe es eben getestet.

    Wenn du einfach nur auf deinem lokalen System das machen willst, dann kannst du auch ganz einfach im Kernel Sourcecode die Schreibberechtigung hinzufügen. Dummerweise finde ich gerade die entsprechende Datei nicht mehr 😞



  • Es muss doch ne Möglichkeit geben auf die syscalls schreiben zu können?



  • jojoboyj schrieb:

    Es muss doch ne Möglichkeit geben auf die syscalls schreiben zu können?

    Wenn es so einfach wäre, dann wäre die Sicherheit des Kernels enorm reduziert. Wenn du auf die Erstellung als Modul verzichten würdest wäre es auch gar kein Ding, dann hättest du ganz legitim Zugriff darauf, oder wenn du die Seite der sys_call_table beschreibbar machst. Aber in jedem Fall musst du den Kernel selbst patchen. Was für deine Übungszwecke doch in Ordnung sein sollte, oder nicht :)?



  • Wie schon oben erwähnt sollte es für jeden 2.6.x Kernel funktionieren.
    Und ich kann nicht anfangen auf privat System bzw. manchmal produktiv System immer erst den Kernel zu patchen. Das sollte eigentlich übers Modul laufen...



  • jojoboyj schrieb:

    Wie schon oben erwähnt sollte es für jeden 2.6.x Kernel funktionieren.
    Und ich kann nicht anfangen auf privat System bzw. manchmal produktiv System immer erst den Kernel zu patchen. Das sollte eigentlich übers Modul laufen...

    Auf Produktivsystemen musst du aber auch davon ausgehen, dass dort entsprechend geschützte Kernel laufen die Checksummen der sys_call_table anlegen und regelmäßig überprüfen (siehe das von mir verlinkte Paper). Und wie du siehst ändert sich hier ständig etwas, so sind verfahren aus dem 2.6.24er Kernel schon jetzt nicht mehr anwendbar.



  • Also gibt es im Moment wirklich keine Möglichkeit?


Anmelden zum Antworten