Recent Work – evolving ELF files

Table of Contents

Motivation

  1. apply our technique to a wider range of programs
    • anything compiling to ELF files
    • remove need for source code
  2. levels of representation
    • C level (compiler and linker)
    • at the ASM level (linker)
    • binary level (no linker, but respect instruction boundaries)
    • at the byte level (just manipulate raw bytes)

Troubles

Too many Libraries!

libelf
nice tutorial, poor community, not robust
elfsh
targets virus writers, hyperbole and error prone
libbfd
poor documentation, strong community, doesn't support my use case

Finally just wrote my own using the ELF standard. Much easier.

Demo – read a "hello world" elf executable

To evolve variants we need to edit the code living in .text.

Taking a simple C file

main(){puts("hello world");}

We can load it up and check the magic number.

(elf-p "hello")

Then read it into an elf data structure.

(setf *elf* (read-elf "hello"))

And look at the top-level elf header.

(show-it (header *elf*))

Demo – view the layout in memory and on disk

An ELF file has two internal tables, and two instantiations.

tableinstantiation
section tablebinary executable on disk
program tablememory image during execution

looking at the program table

(mapcar #'show-it (program-table *elf*))

looking at the layout of the binary file both on disk

(show-file-layout *elf*)

and in memory

(show-memory-layout *elf*)

Demo – Change the .text section

We can investigate the binary contents of the .text section.

(let ((instrs (subseq (data (named-section *elf* ".text")) 38 42)))
  (mapcar (lambda (el) (format nil "0x~x" el))
          (coerce instrs 'list)))

We can also update the value of this section

(setf (aref (data (named-section *elf* ".text")) 40) #xc3)

And when we write the altered file out to disk

(write-elf *elf* "hello2")

The resulting file still works

chmod +x hello2
./hello2
echo $?

Mutation Operators

I've implemented the following mutation operators on this framework.

  • Delete
    (defmethod rm ((asm asm))
      (let ((rm-point (place asm)))
        (setf (aref (genome asm) rm-point) nop)
        (setf (ops asm) (cons (cons :rm rm-point) (ops asm)))))
    
  • Swap
    (defmethod swap ((asm asm))
      (let* ((a (place asm)) (a-instr (aref (genome asm) a))
             (b (place asm)) (b-instr (aref (genome asm) b)))
        (setf (aref (genome asm) a) b-instr)
        (setf (aref (genome asm) b) a-instr)
        (setf (ops asm) (cons (cons :swap (list a b)) (ops asm)))))
    
  • Insert
    (defmethod insert ((asm asm))
      (let ((point (place asm)) closest-nop)
        ;; remove the nop closest to the inserted instruction
        (loop for n from 1 upto (max (- (length (genome asm)) point) point)
           until closest-nop
           do (let ((forward (+ point n)) (backward (- point n)))
                (cond
                  ((and (> backward 0)
                        (equal (aref (genome asm) backward) nop))
                   (setf closest-nop backward))
                  ((and (< forward (length (genome asm)))
                        (equal (aref (genome asm) forward) nop))
                   (setf closest-nop forward)))))
        (setf (genome asm)
              (let ((low  (min closest-nop point))
                    (high (max closest-nop point))
                    (copy (list (aref (genome asm) (place asm)))))
                (concatenate 'vector
                             (subseq (genome asm) 0 low)
                             (when (equal low point) copy)
                             (subseq (genome asm) low high)
                             (when (equal high point) copy)
                             (subseq (genome asm) (1+ high)))))
        (setf (ops asm) (cons (cons :insert (list point closest-nop)) (ops asm)))))
    

Initial Mutational Robustness Results

Which result in the following mutational robustness over the simple hello world program.

oppassed 1 testpassed 0 testsscript-errortimeout-error
:RM53004700
:SWAP30506932
:INSERT1000000