The ELF Virus Writing HOWTO: Introduction
Prev |  | Next

# 6. Scanners (i)

| One can search the brain with a microscope and not find the mind, and can search the stars with a telescope and not find God.
| J. Gustav White

After finding an exploitable peculiarity you need to verify its existence in a typical population of target executables. Some peculiarities can be used only once, i.e. filling the segment gap on i386 removes the gap. This makes the following scripts kind of dual-use technology.

## 6.1. Driver scripts

Scanner scripts read a list of file names from `stdin`, one file name per line. Human readable output is written to `stdout`, with the convention that clean targets create no output. Since the scripts just parse the output of another tool performance is generally bad. Use of file(1) as a fast file-type is the best we can do to lower noise and duration to acceptable regions.

The following driver script are configurable. First arguments is the output file. Second argument is the file name of the scanner. Third argument is required postfix of target file names. A quoted empty string (`""`) accepts all files in target directories. The other typical value is `"_infected"` since this postfix is used by all examples in this document. All other arguments are directory names passed on to find(1).

Source: src/scanner/driver.sh

    #!/bin/sh
    dst=$1; shift
    scanner=$1; shift
    postfix=$1; shift

    flags='-p'
    [ `basename ${scanner}` = 'entry_point.pl' ] && flags='-fh'

    . ./config-${HOSTNAME}.sh

    type="ELF ${TEVWH_ELF_ADDR_SIZE}-bit ${TEVWH_BYTE_ORDER}SB executable"

    # On Solaris 'find' has no -print0 and 'xargs' has neither -0 nor -r
    ${TEVWH_PATH_FIND} "$@" -type f -name "*${postfix}" \
    | ${TEVWH_PATH_XARGS} ${TEVWH_PATH_FILE} \
    | ${TEVWH_PATH_SED} -ne "s/:[[:space:]]*${type}.*//p" \
    | ${TEVWH_PATH_TEE} ${dst}.files \
    | ${TEVWH_PATH_XARGS} ${TEVWH_PATH_OBJDUMP} ${flags} \
    | ${scanner} 2>&1 \
    | ${TEVWH_PATH_SED} "s#^${TEVWH_TMP}/##" \
    | ${TEVWH_PATH_TEE} ${dst}.full \
    | ${TEVWH_PATH_TAIL} \
    > ${dst}

## 6.2. Scan segment padding

The input is expected to be the output of objdump `-p` (for multiple files). Input is parsed for lines starting with the word `LOAD`. The end of the segment is calculated and stored for the next line. The distance from the start of a `LOAD` segment to the end of the previous one is than compared with two values. See Segment padding infection (i) for an overview of `_SC_PAGESIZE` and `Alignment`. Actual values for a specific platform are at Variables & packages.

Output is at Segment padding infection. Its meaning is ambigious. We can positively tell whether the gap exists or not. But we cannot say whether the gap never existed or is indeed occupied by an infection. On platforms other than i386 alignment is larger than page size. It is possible that a small infection taking just one page affects a single target more than once and still leaves a gap larger than the segment alignment. These cases go undetected by this script.

Anyway, `det_page` is the number of files detected to have a gap smaller equal one page (no further infection possible). `det_align` is the number of files where the gap is smaller than segment alignment (probably infected). Obviously these criteria overlap.

Source: src/scanner/dist.pl

    #!/usr/bin/perl -w
    use strict;

    my $tmp = $ENV{'TEVWH_TMP'} || die "TEVWH_TMP undefined.";
    my $pagesize = $ENV{'TEVWH_PAGESIZE'} || die "TEVWH_PAGESIZE undefined.";
    $pagesize = hex($pagesize);
    my $align = $ENV{'TEVWH_ELF_ALIGN'} || die "TEVWH_ELF_ALIGN undefined.";
    $align = hex($align);

    my $min = 0xFFFFFFFF; my $max = 0; my $det_page = 0; my $det_align = 0;
    my $nr_files = 0; my $filename;
    while(<>)
    {
      if (m#^(/[^:\s]+):#) { $nr_files++; $filename = $1; next; }
      if (m#^$tmp/([^:\s]+):#) { $nr_files++; $filename = $1; next; }
      if (m#^Program Header:#)
      {
        my $nrLoad = 0; my $end = 0;
        while(<>)
        {
          last if (m#^\s*$#);
          next if (!m/^\s*LOAD\s+/);

          $nrLoad++;
          my @number = split /\s+/;
          my $addr = hex($number[5]);

          $_ = <>;
          @number = split /\s+/, $_;
          my $filesiz = hex($number[2]);

          if ($end != 0)
          {
    	my $dist = $addr - $end;
    	my $print = 0;
    	if ($dist <= $pagesize) { $det_page++; $print = 1; }
    	if ($dist < $align) { $det_align++; $print = 1; }
    	printf "%-52s addr=%08x dist=%08x\n", $filename, $addr, $dist
    	if ($print);
    	$max = $dist if ($dist > $max);
    	$min = $dist if ($dist < $min);
          }
          $end = $addr + $filesiz;
        }
        printf("%-52s has %d LOAD segments.\n", $filename, $nrLoad)
        if ($nrLoad != 2);
      }
    }
    printf "files=%04d; det_page=%04d; det_align=%04d; min=0x%08x; max=0x%08x\n",
      $nr_files, $det_page, $det_align, $min, $max;

## 6.3. Scan entry point

For each file the start of section `.text` should equal the entry point. See Sections for background and output.

Source: src/scanner/entry_point.pl

    #!/usr/bin/perl -w
    use strict;

    my $tmp = $ENV{'TEVWH_TMP'} || die "TEVWH_TMP undefined.";

    my $min = 0xFFFFFFFF; my $max = 0; my $detected = 0;
    my $nr_files = 0; my $filename; my $entry_point;
    while(<>)
    {
      if (m#^(/[^:\s]+):#) { $nr_files++; $filename = $1; next; }
      if (m#^$tmp/([^:\s]+):#) { $nr_files++; $filename = $1; next; }
      if (m#start address 0x([0-9A-Fa-f]+)#) { $entry_point = hex($1); next; }
      if (m#^Idx Name#)
      {
        if (!defined($entry_point))
        {
          printf "%-64s has no entry point.\n", $filename;
          next;
        }
        my $start_of_text;
        while(<>)
        {
          if (m/^\s*\d+\s+.text\s+[0-9A-Fa-f]+\s+([0-9A-Fa-f]+)/)
          {
            $start_of_text = hex($1);
            last;
          }
        }
        if ($entry_point != $start_of_text)
        {
          $detected++;
          printf "%-64s ep=0x%08x sot=0x%08x\n",
            $filename, $entry_point, $start_of_text;
        }
      }
    }
    printf "files=%04d; detected=%04d\n", $nr_files, $detected;

Prev | Home | Next
Remote shell trojan (RST) (i) |  | GNU Free Documentation License