mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
256 lines
14 KiB
C++
256 lines
14 KiB
C++
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/// \file varmap.hh
|
|
/// \brief Classes for keeping track of local variables and reconstructing stack layout
|
|
|
|
#ifndef __VARMAP_HH__
|
|
#define __VARMAP_HH__
|
|
|
|
#include "database.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
extern AttributeId ATTRIB_LOCK; ///< Marshaling attribute "lock"
|
|
extern AttributeId ATTRIB_MAIN; ///< Marshaling attribute "main"
|
|
|
|
extern ElementId ELEM_LOCALDB; ///< Marshaling element \<localdb>
|
|
|
|
/// \brief A symbol name recommendation with its associated storage location
|
|
///
|
|
/// The name is associated with a static Address and use point in the code. Symbols
|
|
/// present at the end of function decompilation without a name can acquire \b this name
|
|
/// if their storage matches.
|
|
class NameRecommend {
|
|
Address addr; ///< The starting address of the storage location
|
|
Address useaddr; ///< The code address at the point of use
|
|
int4 size; ///< An optional/recommended size for the variable being stored
|
|
string name; ///< The local symbol name recommendation
|
|
uint8 symbolId; ///< Id associated with the original Symbol
|
|
public:
|
|
NameRecommend(const Address &ad,const Address &use,int4 sz,const string &nm,uint8 id) :
|
|
addr(ad), useaddr(use), size(sz), name(nm), symbolId(id) {} ///< Constructor
|
|
const Address &getAddr(void) const { return addr; } ///< Get the storage address
|
|
const Address &getUseAddr(void) const { return useaddr; } ///< Get the use point address
|
|
int4 getSize(void) const { return size; } ///< Get the optional size
|
|
string getName(void) const { return name; } ///< Get the recommended name
|
|
uint8 getSymbolId(void) const { return symbolId; } ///< Get the original Symbol id
|
|
};
|
|
|
|
/// \brief A name recommendation for a particular dynamic storage location
|
|
///
|
|
/// A recommendation for a symbol name whose storage is dynamic. The storage
|
|
/// is identified using the DynamicHash mechanism and may or may not exist.
|
|
class DynamicRecommend {
|
|
Address usePoint; ///< Use point of the Symbol
|
|
uint8 hash; ///< Hash encoding the Symbols environment
|
|
string name; ///< The local symbol name recommendation
|
|
uint8 symbolId; ///< Id associated with the original Symbol
|
|
public:
|
|
DynamicRecommend(const Address &addr,uint8 h,const string &nm,uint8 id) :
|
|
usePoint(addr), hash(h), name(nm), symbolId(id) {} ///< Constructor
|
|
const Address &getAddress(void) const { return usePoint; } ///< Get the use point address
|
|
uint8 getHash(void) const { return hash; } ///< Get the dynamic hash
|
|
string getName(void) const { return name; } ///< Get the recommended name
|
|
uint8 getSymbolId(void) const { return symbolId; } ///< Get the original Symbol id
|
|
};
|
|
|
|
/// \brief Data-type for a storage location when there is no Symbol (yet)
|
|
///
|
|
/// Allow a data-type to be fed into a specific storage location. Currently
|
|
/// this only applies to input Varnodes.
|
|
class TypeRecommend {
|
|
Address addr; ///< Storage address of the Varnode
|
|
Datatype *dataType; ///< Data-type to assign to the Varnode
|
|
public:
|
|
TypeRecommend(const Address &ad,Datatype *dt) :
|
|
addr(ad), dataType(dt) {} ///< Constructor
|
|
const Address &getAddress(void) const { return addr; } ///< Get the storage address
|
|
Datatype *getType(void) const { return dataType; } ///< Get the data-type
|
|
};
|
|
|
|
/// \brief Partial data-type information mapped to a specific range of bytes
|
|
///
|
|
/// This object gives a hint about the data-type for a sequence of bytes
|
|
/// starting at a specific address offset (typically on the stack). It describes
|
|
/// where the data-type starts, what data-type it might be, and how far it extends
|
|
/// from the start point (possibly as an array).
|
|
class RangeHint {
|
|
friend class MapState;
|
|
friend class ScopeLocal;
|
|
public:
|
|
/// \brief The basic categorization of the range
|
|
enum RangeType {
|
|
fixed = 0, ///< A data-type with a fixed size
|
|
open = 1, ///< An array with a (possibly unknown) number of elements
|
|
endpoint = 2 ///< An (artificial) boundary to the range of bytes getting analyzed
|
|
};
|
|
private:
|
|
uintb start; ///< Starting offset of \b this range of bytes
|
|
int4 size; ///< Number of bytes in a single element of this range
|
|
intb sstart; ///< A signed version of the starting offset
|
|
Datatype *type; ///< Putative data-type for a single element of this range
|
|
uint4 flags; ///< Additional boolean properties of this range
|
|
RangeType rangeType; ///< The type of range
|
|
int4 highind; ///< Minimum upper bound on the array index (if \b this is \e open)
|
|
public:
|
|
RangeHint(void) {} ///< Uninitialized constructor
|
|
RangeHint(uintb st,int4 sz,intb sst,Datatype *ct,uint4 fl,RangeType rt,int4 hi) {
|
|
start=st; size=sz; sstart=sst; type=ct; flags=fl; rangeType = rt; highind=hi; } ///< Initialized constructor
|
|
bool reconcile(const RangeHint *b) const;
|
|
bool contain(const RangeHint *b) const;
|
|
bool preferred(const RangeHint *b,bool reconcile) const;
|
|
bool absorb(RangeHint *b); ///< Try to absorb the other RangeHint into \b this
|
|
bool merge(RangeHint *b,AddrSpace *space,TypeFactory *typeFactory); ///< Try to form the union of \b this with another RangeHint
|
|
int4 compare(const RangeHint &op2) const; ///< Order \b this with another RangeHint
|
|
static bool compareRanges(const RangeHint *a,const RangeHint *b) { return (a->compare(*b) < 0); } ///< Compare two RangeHint pointers
|
|
};
|
|
|
|
class ProtoModel;
|
|
class LoadGuard;
|
|
|
|
/// \brief A light-weight class for analyzing pointers and aliasing on the stack
|
|
///
|
|
/// The gather() method looks for pointer references into a specific AddressSpace
|
|
/// (usually the stack). Then hasLocalAlias() checks if a specific Varnode within
|
|
/// the AddressSpace is (possibly) aliased by one of the gathered pointer references.
|
|
class AliasChecker {
|
|
public:
|
|
/// \brief A helper class holding a Varnode pointer reference and a possible index added to it
|
|
struct AddBase {
|
|
Varnode *base; ///< The Varnode holding the base pointer
|
|
Varnode *index; ///< The index value or NULL
|
|
AddBase(Varnode *b,Varnode *i) { base=b; index=i; } ///< Constructor
|
|
};
|
|
private:
|
|
const Funcdata *fd; ///< Function being searched for aliases
|
|
AddrSpace *space; ///< AddressSpace in which to search
|
|
mutable vector<AddBase> addBase; ///< Collection of pointers into the AddressSpace
|
|
mutable vector<uintb> alias; ///< List of aliased addresses (as offsets)
|
|
mutable bool calculated; ///< Have aliases been calculated
|
|
uintb localExtreme; ///< Largest possible offset for a local variable
|
|
uintb localBoundary; ///< Boundary offset separating locals and parameters
|
|
mutable uintb aliasBoundary; ///< Shallowest alias
|
|
int4 direction; ///< 1=stack grows negative, -1=positive
|
|
void deriveBoundaries(const FuncProto &proto); ///< Set up basic boundaries for the stack layout
|
|
void gatherInternal(void) const; ///< Run through Varnodes looking for pointers into the stack
|
|
public:
|
|
AliasChecker() { fd = (const Funcdata *)0; space = (AddrSpace *)0; calculated=false; } ///< Constructor
|
|
void gather(const Funcdata *f,AddrSpace *spc,bool defer); ///< Gather Varnodes that point on the stack
|
|
bool hasLocalAlias(Varnode *vn) const; ///< Return \b true if it looks like the given Varnode is aliased by a pointer
|
|
void sortAlias(void) const; ///< Sort the alias starting offsets
|
|
const vector<AddBase> &getAddBase(void) const { return addBase; } ///< Get the collection of pointer Varnodes
|
|
const vector<uintb> &getAlias(void) const { return alias; } ///< Get the list of alias starting offsets
|
|
static void gatherAdditiveBase(Varnode *startvn,vector<AddBase> &addbase);
|
|
static uintb gatherOffset(Varnode *vn);
|
|
};
|
|
|
|
/// \brief A container for hints about the data-type layout of an address space
|
|
///
|
|
/// A collection of data-type hints for the address space (as RangeHint objects) can
|
|
/// be collected from Varnodes, HighVariables or other sources, using the
|
|
/// gatherVarnodes(), gatherHighs(), and gatherOpen() methods. This class can then sort
|
|
/// and iterate through the RangeHint objects.
|
|
class MapState {
|
|
AddrSpace *spaceid; ///< The address space being analyzed
|
|
RangeList range; ///< The subset of ranges, within the whole address space to analyze
|
|
vector<RangeHint *> maplist; ///< The list of collected RangeHints
|
|
vector<RangeHint *>::iterator iter; ///< The current iterator into the RangeHints
|
|
Datatype *defaultType; ///< The default data-type to use for RangeHints
|
|
AliasChecker checker; ///< A collection of pointer Varnodes into our address space
|
|
void addGuard(const LoadGuard &guard,OpCode opc,TypeFactory *typeFactory); ///< Add LoadGuard record as a hint to the collection
|
|
void addRange(uintb st,Datatype *ct,uint4 fl,RangeHint::RangeType rt,int4 hi); ///< Add a hint to the collection
|
|
void reconcileDatatypes(void); ///< Decide on data-type for RangeHints at the same address
|
|
public:
|
|
#ifdef OPACTION_DEBUG
|
|
mutable bool debugon;
|
|
mutable Architecture *glb;
|
|
void turnOnDebug(Architecture *g) const { debugon = true; glb=g; }
|
|
void turnOffDebug(void) const { debugon = false; }
|
|
#endif
|
|
MapState(AddrSpace *spc,const RangeList &rn,const RangeList &pm,Datatype *dt); ///< Constructor
|
|
~MapState(void); ///< Destructor
|
|
bool initialize(void); ///< Initialize the hint collection for iteration
|
|
void sortAlias(void) { checker.sortAlias(); } ///< Sort the alias starting offsets
|
|
const vector<uintb> &getAlias(void) { return checker.getAlias(); } ///< Get the list of alias starting offsets
|
|
void gatherSymbols(const EntryMap *rangemap); ///< Add Symbol information as hints to the collection
|
|
void gatherVarnodes(const Funcdata &fd); ///< Add stack Varnodes as hints to the collection
|
|
void gatherHighs(const Funcdata &fd); ///< Add HighVariables as hints to the collection
|
|
void gatherOpen(const Funcdata &fd); ///< Add pointer references as hints to the collection
|
|
RangeHint *next(void) { return *iter; } ///< Get the current RangeHint in the collection
|
|
bool getNext(void) { ++iter; if (iter==maplist.end()) return false; return true; } ///< Advance the iterator, return \b true if another hint is available
|
|
};
|
|
|
|
/// \brief A Symbol scope for \e local variables of a particular function.
|
|
///
|
|
/// This acts like any other variable Scope, but is associated with a specific function
|
|
/// and the address space where the function maps its local variables and parameters, typically
|
|
/// the \e stack space. This object in addition to managing the local Symbols, builds up information
|
|
/// about the \e stack address space: what portions of it are used for mapped local variables, what
|
|
/// portions are used for temporary storage (not mapped), and what portion is for parameters.
|
|
class ScopeLocal : public ScopeInternal {
|
|
AddrSpace *space; ///< Address space containing the local stack
|
|
list<NameRecommend> nameRecommend; ///< Symbol name recommendations for specific addresses
|
|
list<DynamicRecommend> dynRecommend; ///< Symbol name recommendations for dynamic locations
|
|
list<TypeRecommend> typeRecommend; ///< Data-types for specific storage locations
|
|
uintb minParamOffset; ///< Minimum offset of parameter passed (to a called function) on the stack
|
|
uintb maxParamOffset; ///< Maximum offset of parameter passed (to a called function) on the stack
|
|
bool stackGrowsNegative; ///< Marked \b true if the stack is considered to \e grow towards smaller offsets
|
|
bool rangeLocked; ///< True if the subset of addresses \e mapped to \b this scope has been locked
|
|
bool adjustFit(RangeHint &a) const; ///< Make the given RangeHint fit in the current Symbol map
|
|
void createEntry(const RangeHint &a); ///< Create a Symbol entry corresponding to the given (fitted) RangeHint
|
|
bool restructure(MapState &state); ///< Merge hints into a formal Symbol layout of the address space
|
|
void markUnaliased(const vector<uintb> &alias); ///< Mark all local symbols for which there are no aliases
|
|
void fakeInputSymbols(void); ///< Make sure all stack inputs have an associated Symbol
|
|
void addRecommendName(Symbol *sym); ///< Convert the given symbol to a name recommendation
|
|
void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack
|
|
void annotateRawStackPtr(void); ///< Generate placeholder PTRSUB off of stack pointer
|
|
public:
|
|
ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor
|
|
virtual ~ScopeLocal(void) {} ///< Destructor
|
|
|
|
AddrSpace *getSpaceId(void) const { return space; } ///< Get the associated (stack) address space
|
|
|
|
/// \brief Is this a storage location for \e unaffected registers
|
|
///
|
|
/// \param vn is the Varnode storing an \e unaffected register
|
|
/// \return \b true is the Varnode can be used as unaffected storage
|
|
bool isUnaffectedStorage(Varnode *vn) const { return (vn->getSpace() == space); }
|
|
|
|
bool isUnmappedUnaliased(Varnode *vn) const; ///< Check if a given unmapped Varnode should be treated as unaliased.
|
|
|
|
void markNotMapped(AddrSpace *spc,uintb first,int4 sz,bool param); ///< Mark a specific address range is not mapped
|
|
|
|
// Routines that are specific to one address space
|
|
virtual void encode(Encoder &encoder) const;
|
|
virtual void decode(Decoder &decoder);
|
|
virtual void decodeWrappingAttributes(Decoder &decoder);
|
|
virtual string buildVariableName(const Address &addr,
|
|
const Address &pc,
|
|
Datatype *ct,
|
|
int4 &index,uint4 flags) const;
|
|
void resetLocalWindow(void); ///< Reset the set of addresses that are considered mapped by the scope to the default
|
|
void restructureVarnode(bool aliasyes); ///< Layout mapped symbols based on Varnode information
|
|
void restructureHigh(void); ///< Layout mapped symbols based on HighVariable information
|
|
SymbolEntry *remapSymbol(Symbol *sym,const Address &addr,const Address &usepoint);
|
|
SymbolEntry *remapSymbolDynamic(Symbol *sym,uint8 hash,const Address &usepoint);
|
|
void recoverNameRecommendationsForSymbols(void);
|
|
void applyTypeRecommendations(void); ///< Try to apply recommended data-type information
|
|
bool hasTypeRecommendations(void) const { return !typeRecommend.empty(); } ///< Are there data-type recommendations
|
|
void addTypeRecommendation(const Address &addr,Datatype *dt); ///< Add a new data-type recommendation
|
|
};
|
|
|
|
} // End namespace ghidra
|
|
#endif
|