Segments

The ELF Virus Writing HOWTO: sparc-sunos5.9
Prev |  | Next

# 5. Segments

Well, my terminal's locked up, and I ain't got any Mail,
And I can't recall the last time that my program didn't fail;
I've got stacks in my structs, I've got arrays in my queues,
I've got the : Segmentation violation -- Core dumped blues.

ELF provides two parallel views of a file's contents. The linking view is defined by the section header table, an array of `Elf32_Shdr`. The execution view is defined by the program header table, an array of `Elf32_Phdr`.

Theoretically the ELF specification is quite liberal. Position, contents and order of sections and segments are not restricted. But in real life an operating system is to used to just one program loader, one linker and few compilers. This makes the work of virus writers easier. We can reverse engineer the de-facto standard, a tiny subset of what the ELF standard allows. On a typical system only a minority of programs violates this subset, so ignoring them does not lower chances of survival.

All headers necessary to execute a program are stuffed into the first page (0x1000 bytes on sparc), probably to simplify the design of an OS' executable format detector. Dynamically linked executables require a few program headers more than static executables, but that's about all the variation there is. In any case the program header of the code segments is followed by the program header of the data segment. Static executables have the code segment at index 0, dynamically linked executables at index 2.

Let's get a bit more serious and examine the assembly program from The language of evil. A standalone executable built from assembler source is probably the most trivial example we can find.

## 5.1. objdump -fp

Command: pre/sparc-sunos5.9/segments/objdump.sh

    #!/usr/xpg4/bin/sh
    cd tmp/sparc-sunos5.9/evil_magic
    /usr/xpg4/bin/ls -Ll att
    /opt/sfw/bin/gobjdump -fp att

Output: out/sparc-sunos5.9/segments/objdump

    -rwxr-xr-x   1 alba     alba        1416 Feb 15  2003 att

    att:     file format elf32-sparc
    architecture: sparc, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x0000000000010250

    Program Header:
        PHDR off    0x0000000000000034 vaddr 0x0000000000010034 paddr 0x0000000000000000 align 2**0
             filesz 0x00000000000000a0 memsz 0x00000000000000a0 flags r-x
      INTERP off    0x00000000000000d4 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**0
             filesz 0x0000000000000011 memsz 0x0000000000000000 flags r--
        LOAD off    0x0000000000000000 vaddr 0x0000000000010000 paddr 0x0000000000000000 align 2**16
             filesz 0x0000000000000274 memsz 0x0000000000000274 flags r-x
        LOAD off    0x0000000000000274 vaddr 0x0000000000020274 paddr 0x0000000000000000 align 2**16
             filesz 0x0000000000000094 memsz 0x0000000000000094 flags rwx
     DYNAMIC off    0x0000000000000278 vaddr 0x0000000000020278 paddr 0x0000000000000000 align 2**0
             filesz 0x0000000000000090 memsz 0x0000000000000000 flags rwx

    Dynamic Section:
      NEEDED      libucb.so.1
      NEEDED      libresolv.so.2
      NEEDED      libsocket.so.1
      NEEDED      libnsl.so.1
      NEEDED      libelf.so.1
      RUNPATH     /usr/ucblib
      RPATH       /usr/ucblib
      HASH        0x100e8
      STRTAB      0x101ac
      STRSZ       0xa2
      SYMTAB      0x1012c
      SYMENT      0x10
      CHECKSUM    0x419b
      DEBUG       0x0
      FEATURE     0x1
      FLAGS       0x0
      FLAGS_1     0x0

## 5.2. readelf -l

`objdump`'s output is butt-ugly. On to `readelf`. The line starting with "There are 5 program headers" shows the value of `e_phnum` and `e_phoff`.

Command: pre/sparc-sunos5.9/segments/readelf.sh

    #!/usr/xpg4/bin/sh
    cd tmp/sparc-sunos5.9/evil_magic
    /usr/xpg4/bin/ls -Ll att
    /opt/sfw/bin/greadelf -l att

Output: out/sparc-sunos5.9/segments/readelf

    -rwxr-xr-x   1 alba     alba        1416 Feb 15  2003 att

    Elf file type is EXEC (Executable file)
    Entry point 0x10250
    There are 5 program headers, starting at offset 52

    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x00010034 0x00000000 0x000a0 0x000a0 R E 0
      INTERP         0x0000d4 0x00000000 0x00000000 0x00011 0x00000 R   0
          [Requesting program interpreter: /usr/lib/ld.so.1]
      LOAD           0x000000 0x00010000 0x00000000 0x00274 0x00274 R E 0x10000
      LOAD           0x000274 0x00020274 0x00000000 0x00094 0x00094 RWE 0x10000
      DYNAMIC        0x000278 0x00020278 0x00000000 0x00090 0x00000 RWE 0

     Section to Segment mapping:
      Segment Sections...
       00
       01
       02     .interp .hash .dynsym .dynstr .text
       03     .got .dynamic
       04

## 5.3. elfdump

And just to complete the confusion a look on the native Solaris tool.

Command: pre/sparc-sunos5.9/segments/elfdump.sh

    #!/usr/xpg4/bin/sh
    cd tmp/sparc-sunos5.9/evil_magic
    /usr/xpg4/bin/ls -Ll att
    /usr/ccs/bin/elfdump -ep att

Output: out/sparc-sunos5.9/segments/elfdump

    -rwxr-xr-x   1 alba     alba        1416 Feb 15  2003 att

    ELF Header
      ei_magic:   { 0x7f, E, L, F }
      ei_class:   ELFCLASS32          ei_data:      ELFDATA2MSB
      e_machine:  EM_SPARC            e_version:    EV_CURRENT
      e_type:     ET_EXEC
      e_flags:                     0
      e_entry:               0x10250  e_ehsize:     52  e_shstrndx:   10
      e_shoff:                 0x3d0  e_shentsize:  40  e_shnum:      11
      e_phoff:                  0x34  e_phentsize:  32  e_phnum:       5

    Program Header[0]:
        p_vaddr:      0x10034         p_flags:    [ PF_X  PF_R ]
        p_paddr:      0               p_type:     [ PT_PHDR ]
        p_filesz:     0xa0            p_memsz:    0xa0
        p_offset:     0x34            p_align:    0

    Program Header[1]:
        p_vaddr:      0               p_flags:    [ PF_R ]
        p_paddr:      0               p_type:     [ PT_INTERP ]
        p_filesz:     0x11            p_memsz:    0
        p_offset:     0xd4            p_align:    0

    Program Header[2]:
        p_vaddr:      0x10000         p_flags:    [ PF_X  PF_R ]
        p_paddr:      0               p_type:     [ PT_LOAD ]
        p_filesz:     0x274           p_memsz:    0x274
        p_offset:     0               p_align:    0x10000

    Program Header[3]:
        p_vaddr:      0x20274         p_flags:    [ PF_X  PF_W  PF_R ]
        p_paddr:      0               p_type:     [ PT_LOAD ]
        p_filesz:     0x94            p_memsz:    0x94
        p_offset:     0x274           p_align:    0x10000

    Program Header[4]:
        p_vaddr:      0x20278         p_flags:    [ PF_X  PF_W  PF_R ]
        p_paddr:      0               p_type:     [ PT_DYNAMIC ]
        p_filesz:     0x90            p_memsz:    0
        p_offset:     0x278           p_align:    0

## 5.4. Observations

Nice to see the entry point (0x10250) again. Program layout is a simplified variation of Sort of an answer. The value of `FileSiz` includes ELF header and program header.

> Overhead:

> `overhead = Entry point - VirtAddr =`

> `0x10250 - 0x10000 = 0x250 = 592 bytes`

> Effective code size:

> `code size = FileSiz - overhead =`

> `0x274 - 0x250 = 0x24 = 36 bytes`

This matches with the disassembly listing. However, the ratio of file size to effective code deserves the title "Bloat", with capital B. Only 3 percent of the file actually do something useful!

> Bloat factor:

> `code size / file size = 36 / 1416 = 0.025`

## 5.5. Segments of /usr/bin/csh

Anyway, we see that even for trivial examples the code is surrounded by lots of other stuff. Let's zoom in on our target. [1].

Command: pre/sparc-sunos5.9/segments/sh/readelf.sh

    #!/usr/xpg4/bin/sh
    shell=$( /usr/xpg4/bin/sed 1q \
    	out/sparc-sunos5.9/scanner/segment_padding/infect )
    [ -x "${shell}" ] || exit 1
    /usr/xpg4/bin/ls -Ll ${shell}
    /opt/sfw/bin/greadelf -l ${shell}

Output: out/sparc-sunos5.9/segments/sh/readelf

    -r-xr-xr-x   2 root     bin       159332 Apr  7  2002 /usr/bin/csh

    Elf file type is EXEC (Executable file)
    Entry point 0x17f0c
    There are 6 program headers, starting at offset 52

    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x00010034 0x00000000 0x000c0 0x000c0 R E 0
      INTERP         0x000e38 0x00000000 0x00000000 0x00011 0x00000 R   0
          [Requesting program interpreter: /usr/lib/ld.so.1]
      LOAD           0x000000 0x00010000 0x00000000 0x234a4 0x234a4 R E 0x10000
      LOAD           0x024000 0x00044000 0x00000000 0x028e0 0x06238 RWE 0x10000
      DYNAMIC        0x0245b4 0x000445b4 0x00000000 0x00100 0x00000 RWE 0
      LOOS+ffffffb   0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0

     Section to Segment mapping:
      Segment Sections...
       00
       01
       02     .SUNW_syminfo .interp .hash .dynsym .dynstr .SUNW_version .rela.ex_shared .rela.cpp_finidata .rela.data .rela.bss .rela.plt .text .init .fini .exception_ranges .rodata .rodata1
       03     .got .plt .dynamic .ex_shared .cpp_finidata .data .data1 .bss
       04
       05

Looks intimidating. But then the ELF specification says that only segments of type "LOAD" are considered for execution. Since the flags of the first one include "execute" but not "write" it must be the code segment. The other one has the "write" flags set, so it must be the data segment. There is one possible deviation: On sparc-sunos most executables built by Sun feature a data segment with "execute" flag.

`MemSiz` (0x6238) is larger than `FileSiz` (0x28e0) in the data segment. Just like with mmap(2) excessive bytes are defined to be initialized with 0. The linker takes advantages of that by grouping all variables that should be initialized to zero at the end. Note that the last section of segment 3 (counting starts with 0) is called `.bss`, the traditional name for this kind of area.

The mapping for segment 2 looks even more complex. But I would guess that `.rodata` means "read-only data" and `.text` contains productive code, as opposed to the administrative stuff in the other sections.

Some executables of Red Hat 8.0 have an additional program header of type `GNU_EH_FRAME`.

## 5.6. Self modifying code

Previous examples in The language of evil used an `__attribute__` clause to put the code into section `.text`. Without that it would end up in section `.rodata`. Both are members of the code segment which is executable in it its entireness; in this regard that would make no difference. But what about putting the code a write enabled data segment? These settings can probably be changed by mprotect(2). [1] But what are the default settings?

Our minimal example is position independent and can be moved around freely. We need a slight modification to be able to call it like a function, though.

Output = Source: out/sparc-sunos5.9/evil_magic/func.inc

    const unsigned char in_code[]
    __attribute__ (( aligned(8), section(".text") )) =
    {
      0x82,0x10,0x20,0x04,           /* 0: mov 4, %g1                    */
      0x90,0x10,0x20,0x01,           /* 4: mov 1, %o0                    */
      0x13,0x00,0x00,0x40,           /* 8: sethi %hi(0x10000), %o1       */
      0x92,0x12,0x60,0x01,           /* c: or %o1, 1, %o159              */
      0x94,0x10,0x20,0x03,           /* 10: mov 3, %o2                   */
      0x91,0xd0,0x20,0x08,           /* 14: ta 8                         */
      0x81,0xc3,0xe0,0x08,           /* 18: retl                         */
      0x01,0x00,0x00,0x00            /* 1c: nop                          */
    }; /* 32 bytes (0x20) */

The program using this piece is platform independent. You will find it at Self modifying code (i). Below is the output. The

Output: out/sparc-sunos5.9/evil_magic/self_modify

       10730 is code ... ELF sigill=0
       20ba0 is data ... ELF sigill=0
       20d18 is heap ... ELF sigill=0
    effffb80 is stack ... ELF sigill=0

### Notes

[1] | The matter is actually quite complex. Theo de Raadt himself describes the problems they had with making OpenBSD more secure at http://marc.theaimsgroup.com/?l=openbsd-tech&m=104391783312978&w=2

Prev | Home | Next
The language of evil |  | Sections