12.07.2015 Views

SELF: a Transparent Security Extension for ELF Binaries

SELF: a Transparent Security Extension for ELF Binaries

SELF: a Transparent Security Extension for ELF Binaries

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

¡¡¡¡¡¡¡¡¡ConsumerSideProducerSideProducerSourceCodeSourceCodeRegularCompiler<strong>S<strong>ELF</strong></strong>CompilerOrdinaryBinaryBinaryAnalyzer<strong>S<strong>ELF</strong></strong>Binary<strong>S<strong>ELF</strong></strong>BinaryBinaryTrans<strong>for</strong>merTrans<strong>for</strong>medBinaryConsumerFigure 3: Model <strong>for</strong> the distribution of <strong>S<strong>ELF</strong></strong> binaries.is not available at the consumer end. In this case, binary analysistechniques are used to generate the <strong>S<strong>ELF</strong></strong> section. There are threeprimary reasons <strong>for</strong> the dual-path approach:It allows externally supplied and pre-existing legacy binariesto be supported to as great a degree as possible.It allows <strong>for</strong> complete support of <strong>S<strong>ELF</strong></strong>-compiled binaries.The <strong>S<strong>ELF</strong></strong>-analysis and generation is decoupled from the binarytrans<strong>for</strong>mation, resulting in better per<strong>for</strong>mance in caseswhere programs are trans<strong>for</strong>med multiple times (e.g., as isthe case with address obfuscation, discussed in Section 5).The rest of this section describes these two paths in greater detail.4.1 Compile-time <strong>S<strong>ELF</strong></strong> generationGenerating <strong>S<strong>ELF</strong></strong> from within a compiler is a straight<strong>for</strong>ward process,as most of the in<strong>for</strong>mation required can be gleaned directlyfrom the compiler’s internal symbol tables. Also required will bea .rel.self section, which will contain the relocation entriesused by the linker to update the .self section when the programlayout is finalized. A good implementation strategy <strong>for</strong> adding a<strong>S<strong>ELF</strong></strong> generation option to a typical compiler is to modify the codeused to generate debugging in<strong>for</strong>mation, since there is much overlapbetween the debugging in<strong>for</strong>mation and <strong>S<strong>ELF</strong></strong>. The .self sectioncontents can be viewed as a copy of the debugging in<strong>for</strong>mationwith unneeded in<strong>for</strong>mation removed, such as variable namesand types, and extra in<strong>for</strong>mation added, such as in<strong>for</strong>mation aboutpointers embedded within machine instructions.4.2 Post-compilation <strong>S<strong>ELF</strong></strong> generationPost-compilation <strong>S<strong>ELF</strong></strong> generation poses a much greater challenge,since the binary file must be analyzed. In particular, the analysismust identify the following:Data embedded within the code segmentPointer values (within both the data and code segments)The size of each data object4.2.1 Identifying data within the code segmentData embedded within the code segment is identified by startingwith a set of known instructions (i.e., the set of known entry points),analyzing each known instruction to find its set of successors, andrepeating this process until the transitive closure is reached. Asmentioned earlier, on the x86 architecture this analysis is actuallysomewhat difficult, primarily due to indirect calls and jumps, theuse of runtime-generated code on the stack, and variable-length instructions.One tool which already does a fairly good job of distinguishingcode from data is LEEL [46], although there is still some room <strong>for</strong>improvement. In particular, in cases where some branches and/orentry points are uncertain, the set of feasible disassemblies can becomputed (i.e., those disassemblies which don’t branch out of theaddress space or collide with a known data value). If more thanone disassembly is feasible, then each byte can be marked based onhow it is utilized in each disassembly, according to the followingrules:A byte is definitely code if it can be executed under everyfeasible disassembly.A byte is definitely data if it cannot be executed under everyfeasible disassembly.A byte is indeterminate (either data or code) if exactly oneof the above two conditions does not hold.This approach yields the most in<strong>for</strong>mation possible without makingany assumptions about the behavior of the code generator, given thedifficulties inherent in disassembling x86 machine code.4.2.2 Identifying pointer valuesIdentifying pointer values within the code and data segments isdone by flow analysis. Instructions which dereference pointer valuesare traced backwards to discover the origin of the pointer value.This process can essentially be viewed as a type inference problem.Values which are loaded from code or text segment and thenused as pointers along every execution path can be inferred to definitelybe pointers; and similarly values which are used solely asdata along every execution path can be marked as definitely data.Values which propagate only through static memory and registersare easy to correctly type using this approach; values which propagatethrough the stack are slightly harder; and values which propagatethrough the heap are rather difficult. The end result of theanalysis is that every data value is typed as being either a definitepointer, definite scalar, or indeterminate/both.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!