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.

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
--- | --- | --- | ---
`ARCH` | `i386` | `ASM_COMMENT` | `;`
`ASM_FLAVOR` | `set disassembly-flavor intel` | `ASM_RETURN` | `\(ret\|hlt\)`
`ASM_STYLE` | `intel` | `BYTE_ORDER` | `L`
`CFLAGS` | `-O1 -I out/i386-redhat7.3-linux -D NDEBUG` | `ELF_ADDR_SIZE` | `32`
`ELF_ALIGN` | `0x1000` | `ELF_BASE` | `0x8048000`
`ELF_EHDR` | `Elf32_Ehdr` | `ELF_MAGIC` | `0x8048001`
`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`
`PAGESIZE` | `0x1000` | `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`.

Table 2. Variables prefixed with `TEVWH_PATH_`

Variable name | Value on this platform | Variable name | Value on this platform
--- | --- | --- | ---
`BC` | `/usr/bin/bc` | `CC` | `/usr/bin/gcc`
`CHMOD` | `/bin/chmod` | `COL` | `/usr/bin/col`
`CSH` | `/bin/tcsh` | `CUT` | `/usr/bin/cut`
`DD` | `/bin/dd` | `DU` | `/usr/bin/du`
`ECHO` | `/bin/echo` | `EXPAND` | `/usr/bin/expand`
`FILE` | `/usr/bin/file` | `FIND` | `/usr/bin/find`
`GDB` | `/usr/bin/gdb` | `GREP` | `/bin/grep`
`HEAD` | `/usr/bin/head` | `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/sh`
`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`
`WHICH` | `/usr/bin/which` | `XARGS` | `/usr/bin/xargs`
`XXD` | `/usr/bin/xxd` |  |

## 1.1. 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/sh
    /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)
    23926	/var/lib/rpm

The query is straightforward:

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

    #!/bin/sh
    /bin/rpm -qf $( /usr/bin/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/sh
    /bin/rpm -q -f $( /usr/bin/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.2. 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/sh
    /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 prefers a plain ``section``.

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

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

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

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

## 1.3. 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/sh
    /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/printcap
    ..?..... 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/sh
    /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