Newer
Older
#include "interfaces/STARSTypes.h"
#include "interfaces/STARSIDATypes.h"
#include "interfaces/SMPDBInterface.h"
#include "interfaces/abstract/all.h"
#include "base/SMPBasicBlock.h"
#include "base/SMPFunction.h"
#include "base/SMPProgram.h"
Clark Coleman
committed
#define STARS_USE_EHP_LIB 1
#if STARS_USE_EHP_LIB
#include <ehp.hpp> // SMPStaticAnalyzer/libehp/include/ehp.hpp
clc5q
committed
#include <pro.h>
#include <fpro.h>
#include <bytes.hpp>
#include <kernwin.hpp>
#include <xref.hpp>
clc5q
committed
Clark Coleman
committed
#include <funcs.hpp>
Clark Coleman
committed
#include <segment.hpp> // for is_spec_ea()
Clark Coleman
committed
Clark Coleman
committed
#if (IDA_SDK_VERSION < 700)
#include <area.hpp>
#else
#include <range.hpp>
#endif
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#include <tryblks.hpp>
#pragma GCC diagnostic pop
#if STARS_USE_EHP_LIB
using namespace EHP;
#endif
int STARS_IDA_Interface_t::STARS_fprintf(FILE *fp, const char *format, ...) {
va_list va;
va_start(va, format);
int code = qvfprintf(fp, format, va);
va_end(va);
return code;
}
int STARS_IDA_Interface_t::STARS_fscanf(FILE *fp, const char *format, ...) {
va_list va;
va_start(va, format);
int code = qvfscanf(fp, format, va);
va_end(va);
return code;
}
long STARS_IDA_Interface_t::STARS_ftell(FILE *fp) {
return (long) qftell(fp);
}
char * STARS_IDA_Interface_t::STARS_fgets(char *buffer, int buflen, FILE *fp) {
return qfgets(buffer, buflen, fp);
}
int STARS_IDA_Interface_t::STARS_fseek(FILE *fp, long offset, int whence) {
return qfseek(fp, offset, whence);
}
int STARS_IDA_Interface_t::STARS_msg(const char *format, ...) {
va_list va;
va_start(va, format);
int nbytes = vmsg(format, va);
va_end(va);
return nbytes;
}
int STARS_IDA_Interface_t::STARS_snprintf(char *buffer, std::size_t n, const char *format, ...) {
va_list va;
va_start(va, format);
int code = qvsnprintf(buffer, n, format, va);
va_end(va);
return code;
}
// The choices that IDA makes for deciding which parent function of a
// TAIL chunk is the primary owner of the tail can be counterintuitive.
// A function entry can both fall into and jump to a tail chunk that
// is contiguous with it, yet the "owner" might be a function that is
// far below it in the executable address space. This function will
// change the ownership to a more sensible arrangement.
void STARS_IDA_Interface_t::AuditTailChunkOwnership(void)
{
char FuncName[MAXSTR];
// Iterate through all chunks in the program.
std::size_t NumChunks = get_fchunk_qty();
for (std::size_t ChunkIndex = 0; ChunkIndex < NumChunks; ++ChunkIndex) {
#if (IDA_SDK_VERSION < 700)
STARS_ea_t ChunkStartAddr = ChunkInfo->startEA;
#else
STARS_ea_t ChunkStartAddr = ChunkInfo->start_ea;
#endif
if (is_func_tail(ChunkInfo)) {
// For each TAIL chunk, find all the parent chunks. Find the last
// parent chunk with an address less than the TAIL chunk address.
STARS_ea_t BestCandidate = 0;
func_parent_iterator_t FuncParent(ChunkInfo);
#if SMP_DEBUG_CHUNKS
#endif
for (bool ok = FuncParent.first(); ok; ok = FuncParent.next()) {
STARS_ea_t parent = FuncParent.parent();
#if SMP_DEBUG_CHUNKS
SMP_msg(" parent: %x ", parent);
#endif
if ((parent > BestCandidate) && (parent < ChunkStartAddr)) {
}
#if SMP_DEBUG_CHUNKS
SMP_msg("\n");
#endif
// Make the best parent chunk the owner of the TAIL chunk if it is
// not already the owner.
if (ChunkInfo->owner != BestCandidate) {
if (0 < BestCandidate) {
if (set_tail_owner(ChunkInfo, BestCandidate)) {
func_t *FuncInfo = ::get_func(BestCandidate);
SMP_msg("Set %llx as new owner of tail %llx\n",
(uint64_t) BestCandidate, (uint64_t) ChunkStartAddr);
// Reanalyze the parent function (and all its
// tail chunks) now that the structure has changed.
reanalyze_function(FuncInfo);
}
else {
SMP_msg("set_tail_owner failed for tail %llx and parent %llx\n",
(uint64_t) ChunkStartAddr, (uint64_t) BestCandidate);
}
}
else {
func_t *FuncInfo = ::get_func(ChunkInfo->owner);
#if SMP_DEBUG_CHUNKS
::get_func_name(FuncInfo->startEA, FuncName, sizeof(FuncName) - 1);
SMP_msg("No good parent candidate before tail at %llx\n",
(uint64_t) ChunkStartAddr);
SMP_msg("Current parent is %x: %s\n", FuncInfo->startEA, FuncName);
#endif
// Find out if a function entry chunk that comes before the
// tail is a better candidate for the owner (i.e. it falls
// through to the tail, or jumps to it).
BestCandidate = 0;
#if SMP_DEBUG_CHUNKS
SMP_msg("Finding parent func candidates for %x:", ChunkInfo->startEA);
#endif
SMP_bounds_t CurrFunc;
for (std::size_t FuncIndex = 0; FuncIndex < global_STARS_program->GetFuncBoundsSize(); ++FuncIndex) {
CurrFunc = global_STARS_program->GetFuncBounds(FuncIndex);
STARS_ea_t FuncStartAddr = CurrFunc.startEA;
if ((FuncStartAddr < ChunkStartAddr)
&& (FuncStartAddr > BestCandidate)) {
BestCandidate = FuncStartAddr;
SMP_msg(" candidate: %llx tail: %llx", (uint64_t) BestCandidate,
(uint64_t) ChunkStartAddr);
SMP_msg(" not a candidate: %llx tail: %llx best: %llx\n",
(uint64_t) FuncStartAddr, (uint64_t) ChunkStartAddr, (uint64_t) BestCandidate);
} // end for (std::size_t FuncIndex = 0; ...)
if (0 >= BestCandidate) { // highly unlikely
SMP_msg("No good func entry parent candidate.\n");
}
else {
FuncInfo = ::get_func(BestCandidate);
#if SMP_DEBUG_CHUNKS
::get_func_name(FuncStartAddr, FuncName, sizeof(FuncName) - 1);
SMP_msg("Best func entry parent candidate: %s at %llx",
FuncName, (uint64_t) BestCandidate);
if (FuncInfo->endEA == ChunkInfo->startEA)
SMP_msg(" Function endEA == tail chunk startEA");
SMP_msg("\n");
#endif
}
}
} // end if (ChunkInfo->owner != BestCandidate)
#if SMP_DEBUG_CHUNKS
else {
SMP_msg("Already best parent for %x is %x\n", ChunkInfo->startEA,
ChunkInfo->owner);
}
#endif
} // end if (is_func_tail(ChunkInfo))
} // end for (std::size_t ChunkIndex = 0; ...)
return;
} // end of AuditTailChunkOwnership()
// Find the span of contiguous code that is not contained within any
// function, starting at StartAddr, which should already be an example
// of an instruction address that is outside of a function.
static STARS_ea_t FindNewFuncLimit(STARS_ea_t StartAddr)
{
STARS_ea_t LimitAddr = StartAddr;
STARS_Segment_t *seg = SMP_getseg(StartAddr);
if (NULL == seg)
return LimitAddr;
STARS_ea_t SegLimit = seg->get_endEA();
for (STARS_ea_t addr = SMP_get_item_end(StartAddr); addr < SegLimit; addr = SMP_get_item_end(addr)) {
if (SMP_isCode(InstrFlags) && SMP_isHead(InstrFlags)) {
LimitAddr = addr;
func_t *FuncInfo = get_func(addr);
if (NULL != FuncInfo)
break; // ran into an existing function
}
else // Not a code head; time to stop.
break;
}
return LimitAddr;
} // end of FindNewFuncLimit()
bool STARS_IDA_Interface_t::STARS_getenv(const char *varname, char varvalue[STARS_MAXSTR]) const {
Clark Coleman
committed
qstring IDAstring(varvalue);
bool VarExists = ::qgetenv(varname, &IDAstring);
if (VarExists) {
const char *IDAinternal = IDAstring.c_str();
assert(nullptr != IDAinternal);
(void) ::qstrncpy(varvalue, IDAinternal, STARS_MAXSTR-1);
Clark Coleman
committed
}
return VarExists;
};
bool STARS_IDA_Interface_t::InstHasNoCodeXrefs(STARS_InstructionID_t InstID) const {
SMP_xref_t CurrXrefs;
STARS_ea_t InstAddr = InstID.GetIDWithinFile();
bool FoundCodeXref = false;
for (bool ok = CurrXrefs.SMP_first_to(InstAddr, XREF_ALL); ok; ok = CurrXrefs.SMP_next_to()) {
STARS_ea_t FromAddr = CurrXrefs.GetFrom();
if ((FromAddr != 0) && (CurrXrefs.GetIscode())) {
FoundCodeXref = true;
break;
}
}
return (!FoundCodeXref);
} // end of STARS_IDA_Interface_t::InstHasNoCodeXrefs()
bool STARS_IDA_Interface_t::IsInstJumpTarget(STARS_InstructionID_t InstID) {
// Determine whether the instruction is a jump target by looking
// at its cross references and seeing if it has "TO" code xrefs.
bool InstIsJumpTarget = false;
SMP_xref_t xrefs;
// TODO: Why XREF_FAR? What about near jumps?
for (bool ok = xrefs.SMP_first_to(InstID.GetIDWithinFile(), XREF_FAR); ok; ok = xrefs.SMP_next_to()) {
if ((xrefs.GetFrom() != 0) && (xrefs.GetIscode())) {
InstIsJumpTarget = true;
break;
}
}
return InstIsJumpTarget;
}
STARS_InstructionID_t STARS_IDA_Interface_t::FindFirstCallTarget(STARS_InstructionID_t CallInstID) {
SMP_xref_t xrefs;
STARS_ea_t CallTarget = STARS_BADADDR;
STARS_ea_t CallInstAddr = CallInstID.GetIDWithinFile();
for (bool ok = xrefs.SMP_first_from(CallInstAddr, XREF_ALL); ok; ok = xrefs.SMP_next_from()) {
if ((xrefs.GetTo() != 0) && (xrefs.GetIscode())) {
// Found a code target, with its address in xrefs.to
// Exclude the ordinary fall-through control flow type, fl_F
if (xrefs.GetIscode() && (xrefs.GetType() == fl_F)) {
// A call instruction will have two targets: the fall through to the
// next instruction, and the called function. We want to find
// the called function.
continue;
}
// We found a target, not the fall-through.
CallTarget = xrefs.GetTo();
Clark Coleman
committed
if (::is_spec_ea(CallTarget)) {
// Call to external func, most likely. Signify with special address.
CallTarget = STARS_EXTERNAL_FUNC_ADDR;
}
else {
SMP_msg("Found indirect call target %llx at %llx\n", (uint64_t)CallTarget, (uint64_t)CallInstAddr);
}
break;
}
} // end for all code xrefs
if (STARS_BADADDR == CallTarget)
SMP_msg("WARNING: Did not find indirect call target at %lx\n", (unsigned long) CallInstAddr);
return STARS_InstructionID_t(CallTarget);
} // end of STARS_IDA_Interface_t::FindFirstCallTarget()
// Audit the IDA database with respect to branches and calls. They should
// each have valid code targets (not data or unknown bytes) and the code
// cross references should reflect the linkage.
void STARS_IDA_Interface_t::AuditCodeTargets(void)
{
// Cover all the code that IDA has grouped into functions by iterating
// through all function chunks in the program.
std::size_t NumChunks = get_fchunk_qty();
for (std::size_t ChunkIndex = 0; ChunkIndex < NumChunks; ++ChunkIndex) {
func_t *ChunkInfo = getn_fchunk((int) ChunkIndex);
char FuncName[MAXSTR];
#if (IDA_SDK_VERSION < 700)
STARS_ea_t ChunkStartAddr = ChunkInfo->startEA;
STARS_ea_t ChunkEndAddr = ChunkInfo->endEA;
#else
STARS_ea_t ChunkStartAddr = ChunkInfo->start_ea;
STARS_ea_t ChunkEndAddr = ChunkInfo->end_ea;
#endif
get_func_name(ChunkStartAddr, FuncName, sizeof(FuncName) - 1);
// First, see if any calls to this function (if this chunk is
// an entry point) are not coming from within functions.
if (is_func_entry(ChunkInfo)) {
SMP_xref_t xb;
for (bool ok = xb.SMP_first_to(addr, XREF_ALL); ok; ok = xb.SMP_next_to()) {
uchar XrefType = xb.GetType() & XREF_MASK;
if (xb.GetIscode()) {
if ((XrefType == fl_U) || (XrefType == fl_USobsolete)) {
SMP_msg("AUDIT: Bad xref type: %llx %s\n", (uint64_t) addr, FuncName);
}
#if SMP_DEBUG_FIXUP_IDB
else if ((XrefType == fl_JF) || (XrefType == fl_JN)) {
SMP_msg("Jump to func: %llx %s from: %llx\n",
(uint64_t) addr, FuncName, (uint64_t) xb.GetFrom());
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
}
#endif
else if (XrefType == fl_F) {
SMP_msg("AUDIT: Fall through to func: %lx %s from: %lx\n",
(unsigned long) addr, FuncName, (unsigned long) xb.GetFrom());
}
else if ((XrefType == fl_CF) || (XrefType == fl_CN)) {
// Far call or Near call
func_t *CallingFunc = ::get_func(xb.GetFrom());
if (NULL == CallingFunc) {
;
#if SMP_DEBUG_FIXUP_IDB
SMP_msg("Call to %x Func %s from %x not in function.\n",
addr, FuncName, xb.GetFrom());
#endif
}
}
} // end if (xb.GetIscode())
else { // DATA xref
if (XrefType == dr_O) {
#if SMP_DEBUG_FIXUP_IDB
SMP_msg("Data xref to %x Func %s from %x\n",
addr, FuncName, xb.GetFrom());
#endif
}
else {
SMP_msg("AUDIT: Strange data xref %d to %lx Func %s from %lx\n",
XrefType, (unsigned long) addr, FuncName, (unsigned long) xb.GetFrom());
}
}
} // end for (bool ok = xb.SMP_first_to(); ...)
} // end if (is_func_entry(ChunkInfo))
// Next, see if any call or branch in this chunk references
// a target address that is not in a function. If so, and the
// callee address code looks like a function prologue, then
// create a function for the contiguous code starting at that
// address and ask IDA to analyze it and store it in the
// IDA database. If it is a branch target, not a call target,
// create a new TAIL chunk for the current parent functions.
for (STARS_ea_t addr = ChunkStartAddr; addr < ChunkEndAddr;
addr = SMP_get_item_end(addr)) {
flags_t InstrFlags = SMP_getFlags(addr);
if (SMP_isCode(InstrFlags) && SMP_isHead(InstrFlags)) {
SMPInstr CurrInst(addr);
CurrInst.Analyze();
if ((CALL|JUMP|COND_BRANCH) & CurrInst.GetDataFlowType()) {
SMP_xref_t xb;
for (bool ok = xb.SMP_first_from(addr, XREF_FAR); ok; ok = xb.SMP_next_from()) {
if (xb.GetIscode()) {
STARS_ea_t FirstAddr = xb.GetTo();
func_t *FuncInfo = ::get_func(FirstAddr);
if (NULL == FuncInfo) {
// Found call to addr that is not in a func.
// Find limits of contiguous code starting at FirstAddr.
STARS_ea_t LastAddr = FindNewFuncLimit(FirstAddr);
if (CALL == CurrInst.GetDataFlowType())
SMP_msg("AUDIT: Found new func from %lx to %lx\n",
(unsigned long) FirstAddr, (unsigned long) LastAddr);
else
SMP_msg("AUDIT: Found new chunk from %lx to %lx\n",
(unsigned long) FirstAddr, (unsigned long) LastAddr);
}
}
}
}
}
}
} // end for (std::size_t ChunkIndex = 0; ... )
Clark Coleman
committed
// Detect IDA Pro func boundary problems, if code segment; true if problems found
bool STARS_IDA_Interface_t::AuditFunctionBoundaries(const STARS_ea_t startEA, const STARS_ea_t endEA) const {
bool ProblemFound = false;
// First, use the tryblks.hpp interface from IDA Pro to parse the EH_FRAME
Clark Coleman
committed
// section in the ELF binary, or equivalent section in other binaries.
//
// Algorithm:
// 1. Call get_tryblks() for the address range of current seg, passed in as args.
// 2. for each tryblk_t in the qvector returned:
// if the kind field is TB_CPP, examine the parent struct rangevec_t, which holds try blocks.
// 3. for each range_t in the rangevec_t, extract the start_ea and end_ea fields
// from each element and note a problem if start_ea and (end_ea - 1) are not in the
// same func.
// Step 1.
tryblks_t *TryBlockVec = new tryblks_t();
range_t FuncRange(startEA, endEA);
std::size_t NumTryBlks = ::get_tryblks(TryBlockVec, FuncRange);
// Step 2.
for (std::size_t TryIndex = 0; TryIndex < NumTryBlks; ++TryIndex) {
tryblk_t CurrTryBlk = TryBlockVec->at(TryIndex);
if (CurrTryBlk.is_cpp()) { // C++ try/catch type
// Step 3.
for (std::size_t TryIndex2 = 0; TryIndex2 < CurrTryBlk.size(); ++TryIndex2) {
range_t CurrTryBlk2 = CurrTryBlk.at(TryIndex2);
// !!!!****!!!! TryIndex3 not used; review data structures
for (std::size_t TryIndex3 = 0; TryIndex3 < CurrTryBlk2.size(); ++TryIndex3) {
Clark Coleman
committed
STARS_ea_t CurrStartEA = CurrTryBlk2.start_ea;
STARS_ea_t CurrEndEA = CurrTryBlk2.end_ea;
#if 1
SMP_msg("INFO: EHSEG: Try block from %llx to %llx \n",
(uint64_t) CurrStartEA, (uint64_t) (CurrEndEA - 1));
#endif
// See if start and end of try block are in the same func.
STARS_Function_t *StartFunc = SMP_get_func(CurrStartEA);
STARS_Function_t *EndFunc = SMP_get_func(CurrEndEA - 1);
if (StartFunc != EndFunc) {
ProblemFound = true;
SMP_msg("ERROR: FUNCBOUNDS: Try block from %llx to %llx spans functions\n",
(uint64_t) CurrStartEA, (uint64_t) (CurrEndEA - 1));
}
} // end for TryIndex3
} // end for TryIndex2
Clark Coleman
committed
}
} // end for TryIndex
Clark Coleman
committed
delete TryBlockVec;
return ProblemFound;
} // end of STARS_IDA_Interface_t::AuditFunctionBoundaries()
// Detect IDA Pro func boundary problems using EH_FRAME FDE info; true if problems found
Clark Coleman
committed
bool STARS_IDA_Interface_t::AuditEHFunctionBoundaries(void) {
bool ProblemFound = false;
#if STARS_USE_EHP_LIB
#ifdef __X64__ // stub out for 32-bit plugins; libehp is 64 bits
Clark Coleman
committed
if (64 > global_STARS_program->GetSTARS_ISA_Bitwidth())
return ProblemFound; // stub out for 32-bit binaries, also
// Use the FDEs (Frame Descriptor Entries) from the eh_frame section
// to perform the same algorithm as above: an FDE should contain only one func.
const string ExeFileName = global_STARS_program->GetRootFileName();

Jason Hiser
committed
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
try
{
auto EHParser = EHP::EHFrameParser_t::factory(ExeFileName);
const auto FDEvecptr = EHParser->getFDEs();
for (const auto FDEveciter : *FDEvecptr) {
uint64_t startAddr = FDEveciter->getStartAddress();
uint64_t endAddr = FDEveciter->getEndAddress();
// See if start and end of FDE landing pad are in the same IDA Pro func.
STARS_ea_t CurrStartEA = (STARS_ea_t) startAddr;
STARS_ea_t CurrEndEA = (STARS_ea_t) endAddr;
func_t *StartFunc = ::get_func(CurrStartEA);
func_t *EndFunc = ::get_func(CurrEndEA - 1);
if (StartFunc != EndFunc) {
STARS_Segment_t *FuncSeg = this->getseg(CurrStartEA);
assert(nullptr != FuncSeg);
char SegName[STARS_MAXSTR];
STARS_ssize_t SegNameLen = FuncSeg->GetSegmentName(SegName, STARS_MAXSTR - 1);
assert(0 < SegNameLen);
const bool PLTflag = (nullptr != strstr(SegName, "plt"));
const bool DYNflag = (nullptr != strstr(SegName, "dyn"));
if (!(PLTflag || DYNflag)) {
ProblemFound = true;
SMP_msg("INFO: FUNCBOUNDS: FDE range from %llx to %llx spans functions in segment %s\n",
(uint64_t) CurrStartEA, (uint64_t) (CurrEndEA - 1), SegName);
bool success = this->RedefineIDAFuncBounds(StartFunc, EndFunc, CurrStartEA, CurrEndEA);
if (success)
SMP_msg("INFO: Redefined IDA FuncBounds successfully.\n");
else
SMP_msg("ERROR: Failed to redefine IDA FuncBounds.\n");
}
Clark Coleman
committed
}
Clark Coleman
committed
else if (nullptr == StartFunc) {
SMP_msg("INFO: FUNCBOUNDS: FDE range from %llx to %llx is orphan code.\n",
(uint64_t) CurrStartEA, (uint64_t) (CurrEndEA - 1));
ProblemFound = true;
}

Jason Hiser
committed
} // end for (const auto FDEveciter : *FDEvecptr)
}
catch(const std::exception& e)
{
const auto msg = string("WARN: Unhandled exception when processing EH frame: ")+e.what();
SMP_msg(msg.c_str());
}
#endif // __X64__
#endif // STARS_USE_EHP_LIB
return ProblemFound;
} // end of STARS_IDA_Interface_t::AuditEHFunctionBoundaries()
// Patch IDA Pro database.
bool STARS_IDA_Interface_t::STARS_patch_byte(STARS_ea_t InstAddr, uint32_t ByteValue) {
Clark Coleman
committed
return ::patch_byte(InstAddr, ByteValue);
}
Clark Coleman
committed
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
// From StartAddr to LimitAddr - 1, make IDA Pro re-analyze so that one function contains all addrs.
// StartFunc is current IDA Pro func for StartAddr, EndFunc is current IDA Pro func for LimitAddr - 1.
// A nullptr value for either one indicates orphaned code, outside of any function.
bool STARS_IDA_Interface_t::RedefineIDAFuncBounds(func_t *StartFunc, func_t *EndFunc, const STARS_ea_t StartAddr, const STARS_ea_t LimitAddr) {
bool success = false;
const bool NullStartFunc = (nullptr == StartFunc);
const bool NullEndFunc = (nullptr == EndFunc);
const STARS_ea_t EndFuncStartAddr = NullEndFunc ? STARS_BADADDR : EndFunc->start_ea;
const STARS_ea_t StartFuncEndAddr = NullStartFunc ? STARS_BADADDR : StartFunc->end_ea;
// Case 1: Orphaned code at StartAddr, make EndFunc start at StartAddr.
if (NullStartFunc && (!NullEndFunc)) {
if (this->IsNopInstSequence(StartAddr, EndFuncStartAddr)) {
SMP_msg("INFO: FUNCBOUNDS: Earlier orphaned code at %llx all nops, ignoring.\n", (uint64_t)StartAddr);
}
else {
int ReturnCode = ::set_func_start(LimitAddr - 1, StartAddr);
success = (ReturnCode == MOVE_FUNC_OK);
if (success) {
::reanalyze_function(EndFunc);
SMP_msg("INFO: FUNCBOUNDS: Success: Earlier orphaned code at %llx merged into func after it.\n",
(uint64_t)StartAddr);
}
else {
SMP_msg("ERROR: FUNCBOUNDS: IDA set_func_start() returned code %d\n", ReturnCode);
}
}
}
else if (!NullStartFunc && NullEndFunc) {
// Case 2: Orphaned code after the first function; extend StartFunc to EndAddr.
if (this->IsNopInstSequence(StartFuncEndAddr, LimitAddr)) {
SMP_msg("INFO: FUNCBOUNDS: Later orphaned code at %llx all nops, ignoring.\n", (uint64_t)StartFuncEndAddr);
}
else {
success = ::set_func_end(StartAddr, LimitAddr);
if (success) {
::reanalyze_function(StartFunc);
SMP_msg("INFO: FUNCBOUNDS: Success: Later orphaned code up to %llx merged into func before it.\n",
(uint64_t)(LimitAddr - 1));
}
else {
SMP_msg("ERROR: FUNCBOUNDS: IDA set_func_end() returned false\n");
}
}
}
else if (!NullStartFunc && (!NullEndFunc)) {
// Two funcs. If the two funcs occupy exactly the range specified by the FDE, then combine
// them into one function.
if ((EndFunc->end_ea == LimitAddr) && (StartFunc->end_ea == EndFuncStartAddr) && (StartAddr == StartFunc->start_ea)) {
// Delete the second function and expand the first function to take its space, then
// reanalyze the new function.
success = ::del_func(EndFuncStartAddr);
if (success) {
success = ::set_func_end(StartAddr, LimitAddr);
if (success) {
::reanalyze_function(StartFunc);
SMP_msg("INFO: FUNCBOUNDS: Success: Later func at %llx merged into func before it starting at %llx.\n",
(uint64_t) EndFuncStartAddr, (uint64_t) StartAddr);
}
else {
SMP_msg("ERROR: FUNCBOUNDS: IDA set_func_end() returned false in two-function case.\n");
// Undo the damage caused by deleting the EndFunc.
const bool success2 = ::add_func(EndFuncStartAddr, LimitAddr);
if (!success2) {
SMP_msg("ERROR: FUNCBOUNDS: add_func() failed on deleted func from %llx to %llx\n",
(uint64_t) EndFuncStartAddr, (uint64_t) LimitAddr);
}
SMP_msg("INFO: FUNCBOUNDS: Damage undone successfully.\n");
}
}
else {
SMP_msg("ERROR: FUNCBOUNDS: del_func() failed on address %llx\n.", (uint64_t) EndFunc->start_ea);
}
}
else {
SMP_msg("INFO: FUNCBOUNDS: RedefineIDAFuncBounds() was passed two separate funcs not filling the FDE range, did nothing.\n");
}
}
else {
SMP_msg("ERROR: FUNCBOUNDS: RedefineIDAFuncBounds() was passed no funcs.\n");
}
// Other cases are too complex for now.
return success;
} // end of STARS_IDA_Interface_t::RedefineIDAFuncBounds()
// From StartAddr to LimitAddr - 1, do we have nothing but no-op instructions?
bool STARS_IDA_Interface_t::IsNopInstSequence(const STARS_ea_t StartAddr, const STARS_ea_t LimitAddr) {
bool NopSequence = true;
for (STARS_ea_t addr = StartAddr; addr < LimitAddr; addr = SMP_get_item_end(addr)) {
flags_t InstrFlags = SMP_getFlags(addr);
if (SMP_isHead(InstrFlags) && SMP_isCode(InstrFlags)) {
SMPInstr TempInst(addr);
TempInst.Analyze();
int InstrLen = (int) TempInst.GetSize();
if ((0 >= InstrLen) || (!TempInst.IsNop())) {
NopSequence = false;
break;
}
}
}
return NopSequence;
} // end of STARS_IDA_Interface_t::IsNopInstSequence()