Magic revealed

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

# 3. Magic revealed

| It looks worse than you can imagine! I can imagine some pretty bad things! That's why I said *worse*!
| Terry Pratchett, Moving Pictures

The fancy output format of The address of main was chosen for a reason. It is valid input for `/bin/sh`. Let's check out whether the program from The magic of the Elf has `main` at the same offset.

## 3.1. objdump -d

The output of `objdump` includes function labels. Filtering the complete disassembly can yield the desired code without prior knowledge of the function address. But since we already have the value we use `--start-address` for symmetry with `ndisasm`. That option accepts only numeric values, not symbol names.

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

    #!/usr/xpg4/bin/sh
    . out/sparc-sunos5.9/magic_elf/addr_of_main
    /opt/sfw/bin/gobjdump -d  \
    	--start-address=0x${addr_main_x} \
    	tmp/sparc-sunos5.9/magic_elf/magic_elf \
    2>&1 | pre/sparc-sunos5.9/magic_elf/objdump_format.pl \
    	-start_address=${addr_main_x}

The filter to pretty up this disassembly is at objdump_format.pl (i)

Output: out/sparc-sunos5.9/magic_elf/objdump.asm

       105c0:   9d e3 bf 90         save    %sp, -112, %sp
       105c4:   90 10 20 01         mov     1, %o0
       105c8:   13 00 00 40         sethi   %hi(0x10000), %o1
       105cc:   92 12 60 01         or      %o1, 1, %o159 ! 10001 <_START_+0x1>
       105d0:   40 00 40 4d         call    20704 <_PROCEDURE_LINKAGE_TABLE_+0x6c>
       105d4:   94 10 20 03         mov     3, %o2
       105d8:   81 c7 e0 08         ret
       105dc:   91 e8 20 00         restore %g0, 0, %o0

This looks like a real `main`. So both programs indeed have `main` at the same offset. Unfortunately a brief look through `/bin` proves this to be pure chance. And instead of a real system call for write(2) we see something strange. It resolves to a location in a shared library. But what function in what library?

## 3.2. GDB to the rescue

Command: pre/sparc-sunos5.9/magic_elf/gdb_core.sh

    #!/usr/xpg4/bin/sh
    /opt/sfw/bin/gdb ${1} -q <<EOF 2>&1

    	disassemble ${2}
    EOF

The filter to pretty up this disassembly is at gdb_format.pl (i)

Command: pre/sparc-sunos5.9/magic_elf/gdb.sh

    #!/usr/xpg4/bin/sh
    file=${1:-tmp/sparc-sunos5.9/magic_elf/magic_elf}
    func=${2:-main}

    /usr/bin/echo "[func=${func}]"
    pre/sparc-sunos5.9/magic_elf/gdb_core.sh ${file} ${func} \
    | pre/sparc-sunos5.9/magic_elf/gdb_format.pl

Output: out/sparc-sunos5.9/magic_elf/gdb

    [func=main]
    0x105c0 <main>:               save          %sp, -112, %sp
    0x105c4 <main+4>:             mov           1, %o0
    0x105c8 <main+8>:             sethi         %hi(0x10000), %o1
    0x105cc <main+12>:            or            %o1, 1, %o1         ! 0x10001
    0x105d0 <main+16>:            call          0x20704 <write>
    0x105d4 <main+20>:            mov           3, %o2
    0x105d8 <main+24>:            ret
    0x105dc <main+28>:            restore       %g0, 0, %o0

Looks better. We need a way to retrieve the function name, `write`, from this output. Then we can feed `gdb` this argument for disassembly.

Command: pre/sparc-sunos5.9/evil_magic/first_gdb_func.sed

    #!/usr/xpg4/bin/sed -nf
    /.*<\(.*\)>$/ {
    	s//\1/
    	p
    	q
    }

Command: pre/sparc-sunos5.9/evil_magic/gdb_write.sh

    #!/usr/xpg4/bin/sh
    file=${1:-tmp/sparc-sunos5.9/magic_elf/magic_elf}
    func=$( pre/sparc-sunos5.9/evil_magic/first_gdb_func.sed \
    	< out/sparc-sunos5.9/magic_elf/gdb )

    /usr/bin/echo "[func=${func}]"
    pre/sparc-sunos5.9/magic_elf/gdb_core.sh ${file} ${func} \
    | pre/sparc-sunos5.9/magic_elf/gdb_format.pl

Output: out/sparc-sunos5.9/evil_magic/write.gdb

    [func=write]
    0x20704 <write>:              sethi         %hi(0x1b000), %g1
    0x20708 <write+4>:            b,a           0x20698 <_PROCEDURE_LINKAGE_TABLE_>
    0x2070c <write+8>:            nop
    0x20710 <write+12>:           nop

Oops. Shared libraries don't share their secrets with everyone.

## 3.3. In doubt use force

We can now search for a fine manual explaining how to debug shared libraries. Or just compile the bugger static.

Command: pre/sparc-sunos5.9/magic_elf/cc_static.sh

    #!/usr/xpg4/bin/sh
    /opt/sfw/bin/gcc -static \
    	-Wall -O1 -I . -I out/sparc-sunos5.9 -D NDEBUG \
    	-o tmp/sparc-sunos5.9/magic_elf/magic_elf_static \
    	pre/sparc-sunos5.9/magic_elf/magic_elf.c \
    && /usr/xpg4/bin/ls -l tmp/sparc-sunos5.9/magic_elf/ \
    && tmp/sparc-sunos5.9/magic_elf/magic_elf_static

Output: out/sparc-sunos5.9/magic_elf/magic_elf_static

    total 760
    -rwxr-xr-x   1 alba     alba        6180 Feb 15  2003 magic_elf
    -rwxr-xr-x   1 alba     alba      373344 Feb 15  2003 magic_elf_static
    ELF

Seems we found an easy way to fill up the hard disk. Anyway, what has gdb(1) to say about it?

Output: out/sparc-sunos5.9/evil_magic/static_main.gdb

    [func=main]
    0x1020c <main>:               save          %sp, -112, %sp
    0x10210 <main+4>:             mov           1, %o0
    0x10214 <main+8>:             sethi         %hi(0x10000), %o1
    0x10218 <main+12>:            or            %o1, 1, %o1         ! 0x10001
    0x1021c <main+16>:            call          0x10b38 <write>
    0x10220 <main+20>:            mov           3, %o2
    0x10224 <main+24>:            ret
    0x10228 <main+28>:            restore       %g0, 0, %o0

The function was called `write` before, it is called `write` now. Let's look what is behind the name.

Source: pre/sparc-sunos5.9/evil_magic/static_write.sh

    #!/usr/xpg4/bin/sh
    file=${1:-tmp/sparc-sunos5.9/magic_elf/magic_elf_static}
    func=$( pre/sparc-sunos5.9/evil_magic/first_gdb_func.sed \
    	< out/sparc-sunos5.9/evil_magic/static_main.gdb )

    /usr/bin/echo "[func=${func}]"
    pre/sparc-sunos5.9/magic_elf/gdb_core.sh ${file} ${func} \
    | pre/sparc-sunos5.9/magic_elf/gdb_format.pl

Output: out/sparc-sunos5.9/evil_magic/static_write.gdb

    [func=write]
    0x10b38 <write>:              save          %sp, -96, %sp
    0x10b3c <write+4>:            sethi         %hi(0x56000), %o0
    0x10b40 <write+8>:            ld            [ %o0 + 0xc4 ], %o0 ! 0x560c4 <ti_jmp_table+284>
    0x10b44 <write+12>:           jmp           %o0
    0x10b48 <write+16>:           restore

## 3.4. Write your name

Above disassembly is not guaranteed to work. The names of symbols imported by libraries differ from one platform to the other, and from one compiler to the other. A more rational approach is to search the listing of all symbols for similar names and identical addresses.

Command: pre/sparc-sunos5.9/evil_magic/nm.sh

    #!/usr/xpg4/bin/sh
    # -p produces same output format on SunOS and GNU
    /usr/xpg4/bin/nm -p tmp/sparc-sunos5.9/magic_elf/magic_elf_static \
    | /usr/xpg4/bin/grep '[^[:alnum:]]write\>' \
    | /usr/xpg4/bin/sort

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

    0000000000 f write.s
    0000068408 T write
    0000093760 T _libc_write
    0000093760 T _write

I suspect there is actually order behind the chaos. The symbol `_write`, with a varying number of leading underscores, seems to be "the real thing" on all platforms. The aliases for the value, `0x93760`, differ a lot.

Command: pre/sparc-sunos5.9/evil_magic/gdb_nm.sh

    #!/usr/xpg4/bin/sh
    file=${1:-tmp/sparc-sunos5.9/magic_elf/magic_elf_static}

    # \< and \> don't work on i386-freebsd4.7
    func=$( /usr/xpg4/bin/nm -p ${file} \
    	| /usr/xpg4/bin/sed -ne '/.*[tTwW] \(__*write\)/ {
    		s//\1/
    		p
    		q
    	}' )

    /usr/bin/echo "[func=${func}]"
    pre/sparc-sunos5.9/magic_elf/gdb_core.sh ${file} ${func} \
    | pre/sparc-sunos5.9/magic_elf/gdb_format.pl

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

    [func=_write]
    0x16e40 <_write>:             st            %o0, [ %sp + 0x44 ]
    0x16e44 <_write+4>:           mov           4, %g1
    0x16e48 <_write+8>:           ta            8
    0x16e4c <_write+12>:          bcc           0x16e64 <_write+36>
    0x16e50 <_write+16>:          cmp           %o0, 0x5b
    0x16e54 <_write+20>:          be,a          0x16e44 <_write+4>
    0x16e58 <_write+24>:          ld            [ %sp + 0x44 ], %o0
    0x16e5c <_write+28>:          b             0x16e6c <_cerror>
    0x16e60 <_write+32>:          nop
    0x16e64 <_write+36>:          retl
    0x16e68 <_write+40>:          nop

intro(2) describes the surroundings of system calls and contains a list of available man-pages. An additional look into directory `/usr/share/man/sman2` can't hurt, though. A general purpose interface to system calls is described by syscall(2). Anyway, the statement `mov 4,%g1` corresponds to the value of `SYS_write` in `/usr/include/sys/syscall.h`. Note that Linux uses `ta 0x10` while Solaris uses `ta 8`. There are other differences, but they are beyond the scope of this document.

Prev | Home | Next
The magic of the Elf |  | The language of evil