Académique Documents
Professionnel Documents
Culture Documents
Problem Statement
Given a memory trace, what information does the trace gives us about the underlying data structures?
Road map
Investigation of previous approaches Realization that they kind of suck Enlightenment phase how can we improve
Introduction
Binary Instrumentation
Introduction
Makes reverse engineering happier Saves time Computers got fast enough to trace every single memory access
Why not?
Introduction
Dynamic analysis
Howard: Dynamic Excavator for Reverse Engineering Data Structures Rewards: DDE, Dynamic Data Structure Excavation WYSINWYX: What You See Is Not What You eXecute
Static analysis
Type Sinks
A type sink is a function, syscall or instruction that we know which types it is taking System calls and standard libraries are the more verbose
For instance:
ssize_t read(int fd, void *buf, size_t count); Leaks four types: ssize_t, int, void *, size_t Also we can extract semantics
Type Sinks
CMPS, INS, LODS, MOVS, OUTS, SCAS, STOS FADD, FDIV, FMUL, and so on. JG / JL Signed Integers JA / JB Unsigned Integers Data dereferences leak half a type
Jumps
Memory dereferences
We just know the dereferenced address is a pointer We know that the dereferenced address contains a pointer to a function.
Things to recognize:
How?
Identifying Pointers
Just see what instructions dereference memory The dereferenced argument must be a valid pointer
Otherwise the program would crash We cannot yet know the type of the pointer If we are lucky enough, and by lucky I mean that we have sufficient code coverage, we will identify the type of the pointer.
Problem
Absolute correctness
Hint I don't Even if we could automatically identify a fraction of the types correctly, that saves us work. Inconsistent typing is detected by humans We cannot get back what is not there
Identifying Structures
This depends heavily on the compiler and the optimization level. Often, access patterns will be similar. Let A be a base pointer *(A + 0) is the first field *(A + 8) is the second field And so on
Example
Identifying Structures
We can obtain this from a memory trace What if A was not a structure
But
Let A be an array *(A + 0) is the first element *(A + 8) is the second element And we are screwed There is no base pointer
Identifying Structures
There is no way we can decide, with certainty, whether a pointer points to a structure or an array
We have to make unsound assumptions Rely on compiler specific constructs Heuristics And why not a bit of magic Still, less work than reversing manually
Identifying Structures
Example:
Example:
Access field 2 which is an integer Then access field 3 which is a short integer Etc.
Memory accesses
2 3 5
4 7
Identifying Structures
Initializing a structure with memset Copying a structure with memcpy If we have more than one access pattern, favor the more irregular
Identifying Arrays
Identifying Arrays
Let A be a pointer We are on a loop A is dereferenced at loop cycle one. B is generated also at loop cycle one. Next iteration B is dereferenced. A is likely an array pointer
Identifying Arrays
If all the accesses are of the same size we have a hint that we are dealing with an array.
We need to further educate ourselves We need to have stronger assumptions that we can rely on. What about address reutilization
We need to tag every address with a TAG to differentiate two identical addresses accessed in different times
Not really We need to make our analysis a little bit more specific
Why?
It leads to good things from an vulnerability research point malloc like functions give us the size of the chunks Hook allocation routines and tag the returned memory with a unique id Hook also deallocation routines to keep track of valid memory chunks
Each object method needs to somehow reference its underlying object. Objects of a given class share a set of common characteristics Or at least those object with shared state information We are dealing with structures of know size Now the whole address space is reduced to a fraction of its size
Just analyze the .heap Hook the allocation routine The block is alive Hook the free routine The block is dead
Visual Studio: will set the 'ecx' register to the 'this' pointer GCC 32 bits: pushes as the first method argument the object GCC 64 bits: 'rsi' is set to the 'this' pointer
So we mark every tracked heap chunk that is on ecx, rsi or the first argument of a function as a possible object The object must be used inside the potential method
Which are better than most Anti-Virus heuristics :P The dynamic nature of a trace makes us rely on code coverage. Sometimes the this pointer remains spuriously in 'ecx'
We know its size We know where they are being used Detect fields Detect relationships with other types
Inheritance Composition
We already have all heap memory accesses in our trace If the memory access is to one of our interesting objects we save the access offset and size Since we only track interesting objects the analysis is much quicker We can implement the algorithms used by Howard/Rewards
Detecting methods
If true
Mark the chunk as interesting Save the access offset for future usage
Mark the function as interesting Does this function get called again with the same conditions?
That is, the same function gets called with a chunk of the same size as the 'this' parameter
With all the collected traces we can obtain quite a lot of information
Class Hierarchy Virtual Function Tables Types! Bonus (not really related with type inference)
Useful to help IDA Pro to discover more functions For each write to an interesting chunk
Types
Pun intended
That is chunks that were passed as the 'this' argument Set the composite type size to the size of the chunk
If 'this' does not point to the first byte of the chunk, get the offset
Repeat the process with all the methods that used the chunk and subdivide the composite type
Chunk
TypeB Chunk
Offset = C
In this case there are two types, we recognize this because there were two methods called with 'this' pointing at the same memory chunk but at a different offset.
Add the current function to a list of methods For each write to the interesting chunk
Add a field at the offset of the write Mark the field with the corresponding basic type according to the write size
For each chunk that was received as the 'this' argument build a map from the method address to a list of all the types created. This will be later used build relationships between types and subsequent merging of identical types
Cheat by first using the type constraints collected on the FAP phase They have the same size
Equal types with differing sizes will be addressed in the third pass That is, at offset O there is a type T of size S in both types How many?
Let N be the number of methods in Type1 Let M be the number of methods in Type2 Let S be the number of shared methods SimilarityIndex(N,M,S) = (S / (N+M)) * 100 If SimilarityIndex > SimilarityThreshold then they are similar
There are types that share methods and fields but they differ in size What is going on?
len(Type2) > len(Type1) most of the times This is the case of for example strings in some browsers
Inheritance / Composition
Inheritance / Composition
ClassA Field1 Field2 Field3 Field4 ClassB ClassA Field1 Field2 Field3 Field4 Field1 Field2 Field3
Inheritance / Composition
Two classes of different size use the same method The bigger one is likely the child class The smallest one is likely the parent class This heuristic can fail
Failure will generate an extra type but the relationships between the types will still be interesting and can be detected by a human once the information is imported into IDA Pro
Hard example :)
Example string class that will contain metadata and contents on the same chunk of memory Other recurring complex examples are hash tables
Increasing accuracy
Increasing accuracy
The more code coverage, the more accuracy The smart way
We can tweak Klee (requires source code) We can code our version of SAGE
??? Profit
Fuzz the application like a 15 year old Gather a set of input files (if possible) and calculate the set of files that gets the maximum coverage
Static Analysis
We have collected a fair amount of information, how can we propagate this information?
Propagating the type information into basic blocks not executed on the trace Or we can be lazy and let HexRays decompiler to do it for us :)
A spurious function calls can happen when a non method function is called on a method The function call can receive the 'this' pointer of the previous method call We avoid this case by ruling out all the function calls that do not behave as thiscall
Given a function get its CFG Obtain a DAG (direct acyclic graph) Do a topological sort Assume ECX is a 'this' pointer
Add it to a list of 'this' aliases If instruction kills any of the 'this' aliases
If the alias list is empty return not thiscall Add the new alias to the list
If the instruction accesses memory using one of the aliases of 'this' then the function is likely 'thiscall'
This can fail too Generally it gives a correct answer in 90% of the analyzed function
In practice this information allows us to detect spurious functions detected as methods of a class
References
Thanks to
Juliano Rizzo Nicolas Waisman Pablo Sole Sean Heelan Topo Muiz