The ELF Virus Writing HOWTO: i386-redhat7.3-linux
Prev |  | Next

# 1. Variables & packages

| Once it hits the fan, the only rational choice is to sweep it up, package it, and sell it as fertilizer.
| anonymous

This document tries to cover multiple platforms through conditional compilation. There is a `configure.pl` that determines the host type and sets up a `config.sh` containing environment variable definitions. There also are equivalent `config.csh`, `config.h`, `config.mak`, `config.sed`, and `config.xml`. The `Makefile` then uses individual sub-directories for each platform. The name of these directories (and some other platform specific values) is retrieved through environment variables. The directory structure is not without meaning.

- `src/` is for source code, i.e. text files written and maintained by humans.

- `pre/i386-redhat7.3-linux/` is directory `src/` pre-processed with `config.sed`. In `pre/` all program names are absolute. Magic numbers and platform specific constants are verbatim.

- `tmp/i386-redhat7.3-linux/` is the only place to hold binaries, i.e. executables and `.o` files.

- `out/i386-redhat7.3-linux/` is for the output of executables, hex dumps, disassembly listings, text processing.

## 1.1. Variables prefixed with `TEVWH_`

The files in `src/` are obfuscated with obscene amounts of variable references like `${TEVWH_ELF_BASE}` or even `${TEVWH_PATH_LS}`. I admit that using variables instead of plain program names makes shell scripts harder to read. But this is necessary to maintain a minimum level of reproducibility on SunOS. Anyway, directory `pre/` makes that `8048000` and `/bin/ls`, so you will probably never encounter this syntax nightmare. An almost complete list of used variables is given below.

Table 1. Variables prefixed with `TEVWH_`

Variable name | Value on this platform | Variable name | Value on this platform
--- | --- | --- | ---
`AFLAGS` | `-I . -D _ASM` | `ARCH` | `i386`
`ASM` | `i386_Linux_intel` | `ASM_COMMENT` | `;`
`ASM_FLAVOR` | `set disassembly-flavor intel` | `ASM_RETURN` | `(ret|hlt)`
`ASM_STYLE` | `intel` | `BYTE_ORDER` | `L`
`CFLAGS` | `-Wall -O1 -I . -I out/i386-redhat7.3-linux -D NDEBUG` | `ELF_ADDR` | `Elf32_Addr`
`ELF_ADDR_SIZE` | `32` | `ELF_ALIGN` | `1000`
`ELF_BASE` | `8048000` | `ELF_EHDR` | `Elf32_Ehdr`
`ELF_MAGIC` | `8048001` | `ELF_OFF` | `Elf32_Off`
`ELF_PAGE_SIZE` | `1000` | `ELF_PHDR` | `Elf32_Phdr`
`ELF_SHDR` | `Elf32_Shdr` | `HOSTTYPE` | `Linux/i386`
`OS_CODE` | `i386-redhat7.3-linux` | `OS_NAME` | `Red Hat Linux release 7.3 (Valhalla)`
`OS_PKG_SYS` | `rpm` | `OS_VENDOR` | `redhat`
`OS_VERSION` | `7.3` | `OUT` | `out/i386-redhat7.3-linux`
`OUT_XML` | `out/i386-redhat7.3-linux/xml` | `PRE` | `pre/i386-redhat7.3-linux`
`PROC_EXE` | `/proc/self/exe` | `PROC_MEM` | `/proc/self/mem`
`TMP` | `tmp/i386-redhat7.3-linux` | `UNAME` | `Linux`

Note that hexadecimal shell variables actually miss the leading `0x` to simplify calculations with `bc`. These values are also available to C code through corresponding `#define` statements after `#include <config.h>`. Values are not quoted, but hexadecimal values are correctly prefixed by `0x`.

## 1.2. Variables prefixed with `TEVWH_PATH_`

Table 2. Variables prefixed with `TEVWH_PATH_`

Variable name | Value on this platform | Variable name | Value on this platform
--- | --- | --- | ---
`BASH` | `/bin/bash` | `BC` | `/usr/bin/bc`
`CAT` | `/bin/cat` | `CC` | `/usr/bin/gcc`
`CHMOD` | `/bin/chmod` | `CSH` | `/bin/tcsh`
`CUT` | `/usr/bin/cut` | `DD` | `/bin/dd`
`DISTID` | `/etc/redhat-release` | `DU` | `/usr/bin/du`
`ECHO` | `/bin/echo` | `EXPAND` | `/usr/bin/expand`
`FILE` | `/usr/bin/file` | `FIND` | `/usr/bin/find`
`FMT` | `/usr/bin/fmt` | `GDB` | `/usr/bin/gdb`
`GREP` | `/bin/grep` | `HEXDUMP` | `/usr/bin/hexdump`
`KILL` | `/bin/kill` | `LD` | `/usr/bin/ld`
`LDD` | `/usr/bin/ldd` | `LS` | `/bin/ls`
`MAKE` | `/usr/bin/make` | `MAN` | `/usr/bin/man`
`MT` | `/bin/mt` | `NASM` | `/usr/bin/nasm`
`NDISASM` | `/usr/bin/ndisasm` | `NICE` | `/bin/nice`
`NM` | `/usr/bin/nm` | `OBJDUMP` | `/usr/bin/objdump`
`OD` | `/usr/bin/od` | `PERL` | `/usr/bin/perl`
`READELF` | `/usr/bin/readelf` | `RPM` | `/bin/rpm`
`SED` | `/bin/sed` | `SH` | `/bin/bash`
`SORT` | `/bin/sort` | `STRACE` | `/usr/bin/strace`
`STRINGS` | `/usr/bin/strings` | `STRIP` | `/usr/bin/strip`
`TAIL` | `/usr/bin/tail` | `TEE` | `/usr/bin/tee`
`TR` | `/usr/bin/tr` | `UNIQ` | `/usr/bin/uniq`
`WC` | `/usr/bin/wc` | `XARGS` | `/usr/bin/xargs`
`XXD` | `/usr/bin/xxd` |  |

## 1.3. The name of the X

Command: src/packages/uname.sh

    #!/bin/sh
    uname -mprs
    echo "[${HOSTTYPE}]"
    echo "[${VENDOR}]"
    echo "[${OSTYPE}]"
    echo "[${MACHTYPE}]"

Output: out/i386-redhat7.3-linux/packages/uname

    Linux 2.4.18-19.7.x i686 unknown
    [i386-linux]
    [intel]
    [linux]
    [i386]

While most Linux distributions ship with slightly modified kernels, no vendor has ever dared to mess with the values returned by uname(2). Instead the tradition of distribution dependent text files in directory `/etc` was established.

Command: pre/i386-redhat7.3-linux/packages/distid.sh

    #!/bin/bash
    # We need this script to copy the id-file into directory out/.
    # I use many machines to test examples, but only one to render the document.

    /bin/cat /etc/redhat-release

Output: out/i386-redhat7.3-linux/packages/distid

    Red Hat Linux release 7.3 (Valhalla)

## 1.4. The owner of files

One of the lesser known features of package management is self-reflection. How do we determine the package owning a file if we have the canonical path name?

Red Hat Linux release 7.3 (Valhalla) uses `rpm` for package management. It maintains an extensive database in `/var/lib/rpm/` in binary form. Query operations are quite fast, but disk space usage is rather high.

Source: pre/i386-redhat7.3-linux/packages/rpm/du.sh

    #!/bin/bash
    /usr/bin/file /var/lib/rpm/*
    /usr/bin/du -s /var/lib/rpm

Output: out/i386-redhat7.3-linux/packages/du

    /var/lib/rpm/Basenames:      Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Conflictname:   Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Dirnames:       Berkeley DB (Btree, version 8, native byte-order)
    /var/lib/rpm/Filemd5s:       Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Group:          Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Installtid:     Berkeley DB (Btree, version 8, native byte-order)
    /var/lib/rpm/Name:           Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Packages:       Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Providename:    Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Provideversion: Berkeley DB (Btree, version 8, native byte-order)
    /var/lib/rpm/Requirename:    Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Requireversion: Berkeley DB (Btree, version 8, native byte-order)
    /var/lib/rpm/Sha1header:     Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Sigmd5:         Berkeley DB (Hash, version 7, native byte-order)
    /var/lib/rpm/Triggername:    Berkeley DB (Hash, version 7, native byte-order)
    16180	/var/lib/rpm

The query is straightforward:

Source: pre/i386-redhat7.3-linux/packages/rpm/simple.sh

    #!/bin/bash
    /bin/rpm -qf $( which perl )

Output: out/i386-redhat7.3-linux/packages/simple

    perl-5.6.1-34.99.6

Output format is customizable. A list of available tag names is output by rpm --querytags.

Source: pre/i386-redhat7.3-linux/packages/rpm/custom.sh

    #!/bin/bash
    /bin/rpm -q -f $( which perl ) \
    --qf 'name=%{name}\nversion=%{version}\nrelease=%{release}\n'

Output: out/i386-redhat7.3-linux/packages/rpm/custom

    name=perl
    version=5.6.1
    release=34.99.6

## 1.5. The source of man-pages

Option `-a` of `man` returns all matching entries, not just the lowest section. This behavior is identical between platforms.

Command: pre/i386-redhat7.3-linux/packages/man-all/Linux.sh

    #!/bin/bash
    /usr/bin/man -a -w kill

Output: out/i386-redhat7.3-linux/packages/man-all

    /usr/share/man/man1/kill.1.gz
    /usr/share/man/man2/kill.2.gz

Requesting a specific section requires option `-s `section`` on SunOS, while Linux and FreeBSD prefer a plain ``section``.

Command: pre/i386-redhat7.3-linux/packages/man-section/Linux.sh

    #!/bin/bash
    /usr/bin/man -w 2 kill

Output: out/i386-redhat7.3-linux/packages/man-section

    /usr/share/man/man2/kill.2.gz

## 1.6. Verifying installed packages

This chapter is not about checking the integrity of package files. See Intrusion detection systems for a general introduction.

rpm(1) lets you verify everything or complete packages. A package can be specified indirectly through a file owned by it, though.

Command: pre/i386-redhat7.3-linux/packages/rpm/verify.sh

    #!/bin/bash
    /bin/rpm -qf /etc/profile
    /bin/rpm --verify bash
    /bin/echo status=$?
    /bin/rpm --verify -f /etc/profile
    /bin/echo status=$?

Output: out/i386-redhat7.3-linux/packages/verify

    setup-2.5.12-1
    status=0
    S.5....T c /etc/csh.cshrc
    S.5....T c /etc/csh.login
    ..?..... c /etc/securetty
    status=1

A quick fix to lower the noise is to ignore all files flagged as `" c "`. Take the following as inspiration for a cron-based script.

Command: pre/i386-redhat7.3-linux/packages/rpm/verify-all.sh

    #!/bin/bash
    /bin/nice -n 19 /bin/rpm --verify --all \
    | /bin/grep -v '........ c'

With option `-p `package_file`` you can verify against the checksums included in a package file, e.g. on the installation CD.

Prev | Home | Next
The ELF Virus Writing HOWTO |  | The magic of the Elf