Files
sigmaker-ida/idasdk75/include/frame.hpp
2021-06-05 21:10:25 +03:00

578 lines
19 KiB
C++

/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2020 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _FRAME_HPP
#define _FRAME_HPP
#include <idp.hpp>
/*! \file frame.hpp
\brief Routines to manipulate function stack frames, stack
variables, register variables and local labels.
The frame is represented as a structure:
<pre>
+------------------------------------------------+
| function arguments |
+------------------------------------------------+
| return address (isn't stored in func_t) |
+------------------------------------------------+
| saved registers (SI, DI, etc - func_t::frregs) |
+------------------------------------------------+ <- typical BP
| | |
| | | func_t::fpd
| | |
| | <- real BP
| local variables (func_t::frsize) |
| |
| |
+------------------------------------------------+ <- SP
</pre>
To access the structure of a function frame, use:
- get_struc() (use func_t::frame as structure ID)
- get_frame(const func_t *pfn)
- get_frame(ea_t ea)
*/
class struc_t;
class member_t;
class op_t;
// We need to trace value of SP register. For this we introduce
// an array of SP register change points.
// SP register change point
//
// NOTE: To manipulate/modify stack points, please use the specialized
// functions provided below in this file (stack pointer change points)
struct stkpnt_t
{
ea_t ea; // linear address
sval_t spd; // here we keep a cumulative difference from [BP-frsize]
DECLARE_COMPARISONS(stkpnt_t)
{
if ( ea < r.ea )
return -1;
if ( ea > r.ea )
return 1;
return 0;
}
};
DECLARE_TYPE_AS_MOVABLE(stkpnt_t);
// we declare a struct to be able to forward declare it in other files
struct stkpnts_t : public qvector<stkpnt_t>
{
DECLARE_COMPARISONS(stkpnts_t) { return compare_containers(*this, r); }
};
//--------------------------------------------------------------------------
// F R A M E M A N I P U L A T I O N
//--------------------------------------------------------------------------
/// Add function frame.
/// \param pfn pointer to function structure
/// \param frsize size of function local variables
/// \param frregs size of saved registers
/// \param argsize size of function arguments range which will be purged upon return.
/// this parameter is used for __stdcall and __pascal calling conventions.
/// for other calling conventions please pass 0.
/// \retval 1 ok
/// \retval 0 failed (no function, frame already exists)
idaman bool ida_export add_frame(
func_t *pfn,
sval_t frsize,
ushort frregs,
asize_t argsize);
/// Delete a function frame.
/// \param pfn pointer to function structure
/// \return success
idaman bool ida_export del_frame(func_t *pfn);
/// Set size of function frame.
/// Note: The returned size may not include all stack arguments. It does so
/// only for __stdcall and __fastcall calling conventions. To get the entire
/// frame size for all cases use get_struc_size(get_frame(pfn)).
/// \param pfn pointer to function structure
/// \param frsize size of function local variables
/// \param frregs size of saved registers
/// \param argsize size of function arguments that will be purged
/// from the stack upon return
/// \return success
idaman bool ida_export set_frame_size(
func_t *pfn,
asize_t frsize,
ushort frregs,
asize_t argsize);
/// Get full size of a function frame.
/// This function takes into account size of local variables + size of
/// saved registers + size of return address + number of purged bytes.
/// The purged bytes correspond to the arguments of the functions with
/// __stdcall and __fastcall calling conventions.
/// \param pfn pointer to function structure, may be NULL
/// \return size of frame in bytes or zero
idaman asize_t ida_export get_frame_size(const func_t *pfn);
/// Get size of function return address.
/// \param pfn pointer to function structure, can't be NULL
idaman int ida_export get_frame_retsize(const func_t *pfn);
/// Parts of a frame
enum frame_part_t
{
FPC_ARGS,
FPC_RETADDR,
FPC_SAVREGS,
FPC_LVARS,
};
/// Get offsets of the frame part in the frame.
/// \param range pointer to the output buffer with the frame part
/// start/end(exclusive) offsets, can't be NULL
/// \param pfn pointer to function structure, can't be NULL
/// \param part frame part
idaman void ida_export get_frame_part(range_t *range, const func_t *pfn, frame_part_t part);
/// Get starting address of arguments section
inline ea_t frame_off_args(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_ARGS);
return range.start_ea;
}
/// Get starting address of return address section
inline ea_t frame_off_retaddr(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_RETADDR);
return range.start_ea;
}
/// Get starting address of saved registers section
inline ea_t frame_off_savregs(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_SAVREGS);
return range.start_ea;
}
/// Get start address of local variables section
inline ea_t frame_off_lvars(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_LVARS);
return range.start_ea;
}
/// Does the given offset lie within the arguments section?
inline bool processor_t::is_funcarg_off(const func_t *pfn, uval_t frameoff) const
{
range_t args;
get_frame_part(&args, pfn, FPC_ARGS);
return stkup()
? frameoff < args.end_ea
: frameoff >= args.start_ea;
}
/// Does the given offset lie within the local variables section?
inline sval_t processor_t::lvar_off(const func_t *pfn, uval_t frameoff) const
{
range_t lvars;
get_frame_part(&lvars, pfn, FPC_LVARS);
return stkup()
? frameoff - lvars.start_ea
: lvars.end_ea - frameoff;
}
/// Get pointer to function frame.
/// \param pfn pointer to function structure
idaman struc_t *ida_export get_frame(const func_t *pfn);
/// Get pointer to function frame.
/// \param ea any address in the function
inline struc_t *get_frame(ea_t ea) { return get_frame(get_func(ea)); }
/// Convert struct offsets into fp-relative offsets.
/// This function converts the offsets inside the struc_t object
/// into the frame pointer offsets (for example, EBP-relative).
inline sval_t soff_to_fpoff(func_t *pfn, uval_t soff)
{
return soff - pfn->frsize + pfn->fpd;
}
/// Update frame pointer delta.
/// \param pfn pointer to function structure
/// \param fpd new fpd value.
/// cannot be bigger than the local variable range size.
/// \return success
idaman bool ida_export update_fpd(func_t *pfn, asize_t fpd);
/// Set the number of purged bytes for a function or data item (funcptr).
/// This function will update the database and plan to reanalyze items
/// referencing the specified address. It works only for processors
/// with #PR_PURGING bit in 16 and 32 bit modes.
/// \param ea address of the function of item
/// \param nbytes number of purged bytes
/// \param override_old_value may overwrite old information about purged bytes
/// \return success
idaman bool ida_export set_purged(ea_t ea, int nbytes, bool override_old_value);
/// Get function by its frame id.
/// \warning this function works only with databases created by IDA > 5.6
/// \param frame_id id of the function frame
/// \return start address of the function or #BADADDR
idaman ea_t ida_export get_func_by_frame(tid_t frame_id);
//--------------------------------------------------------------------------
// S T A C K V A R I A B L E S
//--------------------------------------------------------------------------
/// Get pointer to stack variable.
/// \param actval actual value used to fetch stack variable
/// this pointer may point to 'v'
/// \param insn the instruction
/// \param x reference to instruction operand
/// \param v immediate value in the operand (usually x.addr)
/// \return NULL or ptr to stack variable
idaman member_t *ida_export get_stkvar(
sval_t *actval,
const insn_t &insn,
const op_t &x,
sval_t v);
/// Automatically add stack variable if doesn't exist.
/// Processor modules should use insn_t::create_stkvar().
/// \param insn the instruction
/// \param x reference to instruction operand
/// \param v immediate value in the operand (usually x.addr)
/// \param flags \ref STKVAR_1
/// \return success
idaman bool ida_export add_stkvar(const insn_t &insn, const op_t &x, sval_t v, int flags);
/// \defgroup STKVAR_1 Add stkvar flags
/// Passed as 'flags' parameter to add_stkvar()
//@{
#define STKVAR_VALID_SIZE 0x0001 ///< x.dtyp contains correct variable type
///< (for insns like 'lea' this bit must be off)
///< in general, dr_O references do not allow
///< to determine the variable size
//@}
/// Define/redefine a stack variable.
/// \param pfn pointer to function
/// \param name variable name, NULL means autogenerate a name
/// \param off offset of the stack variable in the frame.
/// negative values denote local variables, positive - function arguments.
/// \param flags variable type flags (byte_flag() for a byte variable, for example)
/// \param ti additional type information (like offsets, structs, etc)
/// \param nbytes number of bytes occupied by the variable
/// \return success
idaman bool ida_export define_stkvar(
func_t *pfn,
const char *name,
sval_t off,
flags_t flags,
const opinfo_t *ti,
asize_t nbytes);
/// Build automatic stack variable name.
/// \param buf pointer to buffer
/// \param pfn pointer to function (can't be NULL!)
/// \param v value of variable offset
/// \return length of stack variable name or -1
idaman ssize_t ida_export build_stkvar_name(
qstring *buf,
const func_t *pfn,
sval_t v);
/// Calculate offset of stack variable in the frame structure.
/// \param pfn pointer to function (can't be NULL!)
/// \param insn the instruction
/// \param n number of operand: (0..#UA_MAXOP-1)
/// -1 if error, return #BADADDR
/// \return #BADADDR if some error (issue a warning if stack frame is bad)
idaman ea_t ida_export calc_stkvar_struc_offset(
func_t *pfn,
const insn_t &insn,
int n);
/// Find and delete wrong frame info.
/// Namely, we delete:
/// - unreferenced stack variable definitions
/// - references to dead stack variables (i.e. operands displayed in red)
/// these operands will be untyped and most likely displayed in hex.
/// We also plan to reanalyze instruction with the stack frame references
/// \param pfn pointer to the function
/// \param should_reanalyze callback to determine which instructions to reanalyze
/// \return number of deleted definitions
idaman int ida_export delete_wrong_frame_info(
func_t *pfn,
bool idaapi should_reanalyze(const insn_t &insn));
//--------------------------------------------------------------------------
// R E G I S T E R V A R I A B L E S
//--------------------------------------------------------------------------
/// \defgroup regvar Register variables
/// Definition of ::regvar_t and related functions
//@{
/// A register variable allows the user to rename a general processor register
/// to a meaningful name.
/// IDA doesn't check whether the target assembler supports the register renaming.
/// All register definitions will appear at the beginning of the function.
struct regvar_t : public range_t
{
char *canon; ///< canonical register name (case-insensitive)
char *user; ///< user-defined register name
char *cmt; ///< comment to appear near definition
};
DECLARE_TYPE_AS_MOVABLE(regvar_t);
/// Define a register variable.
/// \param pfn function in which the definition will be created
/// \param ea1,ea2 range of addresses within the function where the definition
/// will be used
/// \param canon name of a general register
/// \param user user-defined name for the register
/// \param cmt comment for the definition
/// \return \ref REGVAR_ERROR_
idaman int ida_export add_regvar(
func_t *pfn,
ea_t ea1,
ea_t ea2,
const char *canon,
const char *user,
const char *cmt);
/// \defgroup REGVAR_ERROR_ Register variable error codes
/// Return values for functions in described in \ref regvar
//@{
#define REGVAR_ERROR_OK 0 ///< all ok
#define REGVAR_ERROR_ARG (-1) ///< function arguments are bad
#define REGVAR_ERROR_RANGE (-2) ///< the definition range is bad
#define REGVAR_ERROR_NAME (-3) ///< the provided name(s) can't be accepted
//@}
/// Find a register variable definition (powerful version).
/// One of 'canon' and 'user' should be NULL.
/// If both 'canon' and 'user' are NULL it returns the first regvar
/// definition in the range.
/// \param pfn function in question
/// \param ea1,ea2 range of addresses to search.
/// ea1==BADADDR means the entire function
/// \param canon name of a general register
/// \param user user-defined name for the register
/// \return NULL-not found, otherwise ptr to regvar_t
idaman regvar_t *ida_export find_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon, const char *user);
/// Find a register variable definition.
/// \param pfn function in question
/// \param ea current address
/// \param canon name of a general register
/// \return NULL-not found, otherwise ptr to regvar_t
inline regvar_t *find_regvar(func_t *pfn, ea_t ea, const char *canon)
{
return find_regvar(pfn, ea, ea+1, canon, NULL);
}
/// Is there a register variable definition?
/// \param pfn function in question
/// \param ea current address
inline bool has_regvar(func_t *pfn, ea_t ea)
{
return find_regvar(pfn, ea, ea+1, NULL, NULL) != NULL;
}
/// Rename a register variable.
/// \param pfn function in question
/// \param v variable to rename
/// \param user new user-defined name for the register
/// \return \ref REGVAR_ERROR_
idaman int ida_export rename_regvar(func_t *pfn, regvar_t *v, const char *user);
/// Set comment for a register variable.
/// \param pfn function in question
/// \param v variable to rename
/// \param cmt new comment
/// \return \ref REGVAR_ERROR_
idaman int ida_export set_regvar_cmt(func_t *pfn, regvar_t *v, const char *cmt);
/// Delete a register variable definition.
/// \param pfn function in question
/// \param ea1,ea2 range of addresses within the function where the definition holds
/// \param canon name of a general register
/// \return \ref REGVAR_ERROR_
idaman int ida_export del_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon);
//@} regvar
//--------------------------------------------------------------------------
// S P R E G I S T E R C H A N G E P O I N T S
//--------------------------------------------------------------------------
/// Add automatic SP register change point.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address where SP changes.
/// usually this is the end of the instruction which
/// modifies the stack pointer (\cmd{ea}+\cmd{size})
/// \param delta difference between old and new values of SP
/// \return success
idaman bool ida_export add_auto_stkpnt(func_t *pfn, ea_t ea, sval_t delta);
/// Add user-defined SP register change point.
/// \param ea linear address where SP changes
/// \param delta difference between old and new values of SP
/// \return success
idaman bool ida_export add_user_stkpnt(ea_t ea, sval_t delta);
/// Delete SP register change point.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address
/// \return success
idaman bool ida_export del_stkpnt(func_t *pfn, ea_t ea);
/// Get difference between the initial and current values of ESP.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address of an instruction
/// \return 0 or the difference, usually a negative number.
/// returns the sp-diff before executing the instruction.
idaman sval_t ida_export get_spd(func_t *pfn, ea_t ea);
/// Get effective difference between the initial and current values of ESP.
/// This function returns the sp-diff used by the instruction.
/// The difference between get_spd() and get_effective_spd() is present only
/// for instructions like "pop [esp+N]": they modify sp and use the modified value.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address
/// \return 0 or the difference, usually a negative number
idaman sval_t ida_export get_effective_spd(func_t *pfn, ea_t ea);
/// Get modification of SP made at the specified location
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address
/// \return 0 if the specified location doesn't contain a SP change point.
/// otherwise return delta of SP modification.
idaman sval_t ida_export get_sp_delta(func_t *pfn, ea_t ea);
/// Recalculate SP delta for an instruction that stops execution.
/// The next instruction is not reached from the current instruction.
/// We need to recalculate SP for the next instruction.
///
/// This function will create a new automatic SP register change
/// point if necessary. It should be called from the emulator (emu.cpp)
/// when auto_state == ::AU_USED if the current instruction doesn't pass
/// the execution flow to the next instruction.
/// \param cur_ea linear address of the current instruction
/// \retval 1 new stkpnt is added
/// \retval 0 nothing is changed
idaman bool ida_export recalc_spd(ea_t cur_ea);
/// An xref to an argument or variable located in a function's stack frame
struct xreflist_entry_t
{
ea_t ea; ///< Location of the insn referencing the stack frame member
uchar opnum; ///< Number of the operand of that instruction
uchar type; ///< The type of xref (::cref_t & ::dref_t)
DECLARE_COMPARISONS(xreflist_entry_t)
{
int code = ::compare(ea, r.ea);
if ( code == 0 )
{
code = ::compare(type, r.type);
if ( code == 0 )
code = ::compare(opnum, r.opnum);
}
return code;
}
};
DECLARE_TYPE_AS_MOVABLE(xreflist_entry_t);
typedef qvector<xreflist_entry_t> xreflist_t; ///< vector of xrefs to variables in a function's stack frame
/// Fill 'out' with a list of all the xrefs made from function 'pfn', to
/// the argument or variable 'mptr' in 'pfn's stack frame.
/// \param out the list of xrefs to fill.
/// \param pfn the function to scan.
/// \param mptr the argument/variable in pfn's stack frame.
idaman void ida_export build_stkvar_xrefs(xreflist_t *out, func_t *pfn, const member_t *mptr);
#ifndef NO_OBSOLETE_FUNCS
idaman DEPRECATED ea_t ida_export get_min_spd_ea(func_t *pfn);
idaman DEPRECATED int ida_export delete_unreferenced_stkvars(func_t *pfn);
idaman DEPRECATED int ida_export delete_wrong_stkvar_ops(func_t *pfn);
#endif
#endif // _FRAME_HPP