Better Disassembly Through Computable Trust About Me: Chris Parich Sophomore, University of Louisiana RITA August 2nd, 2010

Download Report

Transcript Better Disassembly Through Computable Trust About Me: Chris Parich Sophomore, University of Louisiana RITA August 2nd, 2010

Better Disassembly Through
Computable Trust
About Me:
Chris Parich
Sophomore, University of Louisiana
RITA
August 2nd, 2010
How Important is Correct
Disassembly?
Disassembly Affects:

Code Obfuscation Algorithms

Code De-Obfuscation Algorithms

Code Watermarking (Stenography)

Decompiling Techniques
Or more generally
Everything
Correct disassembly affects all aspects of program
analysis, reverse engineering, and binary
modification.
Why do you say “Correct”?
Most program analysis techniques are created
with the assumption that the entire binary is
perfectly disassembled
Where is this assumption safe?

This works well for interpreted languages like
Java, Python, and any other programmatic bytecode compiled language.


Java intentionally compiles to a verbose format. All its
type information and instruction information are
clearly visible with static analysis
This works well-enough for RISC architectures
like MIPS, ARM, PPC, and SPARC

RISC uses a single-size instruction format to help
enforce simplicity
And guess where it fails!
On > 80% of the processors we run.
And even more of the personal desktop market.
For most of our application market.
Issues in x86

Variable instruction length




Instructions can range from 1 to 15 bytes
The average instruction length in most programs is
~2.5 bytes long
Most of the bulk of an average program is taken by 5+
byte instructions
High instruction density



Starting at an arbitrary offset in arbitrary data is highly
likely to produce a valid x86 instruction.
Starting at the very next offset is just as likely to
produce another valid x86 instruction
Extremely easy to confuse code with data
How to correctly disassemble x86

Required knowledge

The specific OS targeted



The specific processor model targeted
The language being used


Optimizations for size and speed
The list of all subroutine addresses


GCC, MSVC, Borland, Intel, etc.
The transformations being applied


C, C++, D, Fortran, Iron Python, etc.
The compiler being used to produce the binary


Win32/64, Linux 32/64, OSX, BSD, Solaris, etc.
Or at least all independent subroutines
The entry point of the program (Where it starts).
What information is available?

The guaranteed-to-be-there information consists
of:




Targeted OS, Architecture
Executable memory pages and
Entry Point
The might-be-there-in-well-behaved-code
information appends:


List of independent subroutines and
Library and function import tables
What to do?

A collection of heuristic data are needed, both
from the target application, and x86 applications in
general.



Instruction likelihood, or, the chances that a
disassembled instruction is actually valid
Instruction “except-ability”, or, the chances a
disassembled instruction would cause a software or
hardware exception.
Instruction “overlap ability”, or, how easily can another
instruction be disassembled from the same offset range.
On Instruction Likelihood
x86 Instruction Usage

For a popular 3D
Rendering Engine:



Moves, Calls, Adds,
Floating Operations
Push, Cmp, Jmp, Pop
less used
For Glib 2.24.0


Mov, Call, Jmp, Test
Important
Arithmetic less
necessary
mov l
call
addl
leal
f lds
subl
pushl
ret
f stps
cmpl
jmp
leav e
je
mov zbl
popl
movl
c all
leal
je
testl
popl
addl
pushl
jmp
c mpl
jne
ret
subl
xorl
On Instruction Exception

Instructions can cause exceptions in several ways:

A hardware exception




A software exception




A malformed instruction or
A bug in the processor or
Trying to execute when a no-execute bit is set
An instruction that the OS deems illegal
Accessing out of bounds of a programs allocation
Out of allocatable memory
An application exception

Internally defined by the application for invalid input
On Instruction Overlap




Bytes read from one offset for a range will
translate into a specific collection of instructions.
Bytes read from an immediately proceeding offset
and for the same range will translate into a likely
different collection of instructions.
x86 is known for having the ability to overlap
different control flows together.
Work is being done to find how possible this
capability is to exploit or even just implement.
A Means of Batch Disassembly

There are two different kinds of disassemblers:

A linear-sweep disassembler



A recursive-traversal disassembler


IDA Pro
Linear-sweep


OBJDUMP
Most Debuggers
Will disassemble more code, but not necessarily
correctly
Recursive-Traversal

Will disassemble code more correctly, but only that
which it can reach.
Hybrid Disassembly


Some disassemblers attempt to hybridize the
disassembler, and make it a multi-pass analyzer.
These disassemblers, in published
implementations, are usually based on machinelearning techniques.


Use neural networks or some other type of classifying
mechanism to determine both is-a and is-not-a valid
instruction.
Usage is extremely slow and manual, since the
machine must be trained on a crafted dataset.
Batch Disassembly via Modified
Recursive Traversal

Acknowledge the multi-pass paradigm and
embrace it.


Make only provably true assumptions, for any
available architecture.




Disassemble from multiple offsets independently
Only the entry point must be a valid instruction.
Calls (or equivalent) do not have to return to the next
offset.
Conditional Jumps either do or do not occur.
Finally, heuristically score instructions on
likelihood of being valid.
Trusted Disassembly



Using the assumption that only the first instruction
must occur, we can create a trust mechanism in
produced disassembly.
Trust only flows from a caller to a callee. No
conditional flow can be trusted.
The first executed block is trustworthy.



Unconditional direct jumps are as trustworthy as the
block they are part of. Where they go is as well.
Calls are as trustworthy as the block they come from.
Where they go is as well. Where they claim to return is
not.
Returns are as trustworthy as the block they come
from. If they are not trusted, they might not occur.
Trusted Disassembly


Unconditional indirect Jumps as trustworthy as the
block they come from. If the location is
discoverable with the given trustworthy
disassembly, then the location is as trustworthy as
its caller.
Conditional Jumps are NOT trustworthy. They
give valid locations to disassemble, but not
trustworthy locations.
Trusted Disassembly

If a trusted block directs to a known untrusted
block in a trustworthy manner, then that untrusted
block must be trustworthy.
Multiple Passes

Pass 1


Pass 2



The zero-offset of an executable section is often data,
but is just as likely valid instructions as any other byte
in the section.
Any exported function from the Program's load header
is a valid location for disassembly.
Any discovered call of an imported function is a valid
location to disassemble
None of these locations are trustworthy. Malware
can easily mess up these addresses to make
disassembly difficult.
The Trusted Pass

Pass 3




Disassemble from the Entry Point.
Only the entry point is a trustworthy valid block.
All other trusted code must be discovered from here.
Pass 4


Recurse through all discovered blocks, letting the
discovered trust disseminate until no further “changes”
are noted.
This stage also scores discovered instructions
heuristically.
Purpose



Trusted disassembly is applicable to Machine
Learning mechanisms and Reverse Engineering /
Program Analysis.
The analyzer now has knowledge about what is
undoubtedly going to occur in the program.
This allows for faster and better analysis to build
on top of this method.
Questions?
Questions?
o/ <(Pick me!)