Newer
Older
clc5q
committed
index = (min + max) / 2;
}
// IDAProLocs[index] contains addr.
vector<ea_t>::iterator RemovalIterator = IDAProLocs.begin();
RemovalIterator += index;
RemovalIterator = IDAProLocs.erase(RemovalIterator);
return;
} // end of RemoveIDACodeAddr()
// Driver for all other fixing functions. Upon its return, the IDA
// database (IDB file) should be fixed up as much as we can fix it.
void FixupIDB(void) {
FindCodeAddresses();
#if SMP_DEBUG_FIXUP_IDB
SpecialDebugOutput();
#endif
AuditCodeTargets();
FindDataInCode();
AuditTailChunkOwnership();
if (DisasmLocs.size() > 0) {
FixCodeIdentification();
int fixes = FixupNewCodeChunks();
#if SMP_DEBUG_FIXUP_IDB
#endif
}
DisasmLocs.clear();
IDAProLocs.clear();
clc5q
committed
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
} // end of FixupIDB()
// Find and print all data head addresses in code segments.
// If an isolated code instruction is found in the midst of a run
// of data bytes and has no code xrefs jumping to it, it is not
// reachable as code and is undoubtedly a mixup by IDA. Possibly
// the whole data region will be converted to code later, in which
// case the isolated code is not necessarily properly aligned and
// parsed at its present address, so we are glad to convert it into
// data anyway so that FindDataToConvert() will succeed on it later.
// Data to code conversion, and isolated code detection, are inhibited
// by IDA identifying several consecutive instructions in the midst
// of a data region, with the code addresses not agreeing with the
// external disassembler's code addresses. We will convert these
// misaligned instructions to data as we detect them. We will also
// convert unexplored bytes (isUnknown(flags) == true) into data if
// they are in the midst of a data sequence.
#define MIN_DATARUN_LEN 24 // #bytes on either side of "isolated" code
void FindDataInCode(void) {
size_t DataRunLen = 0; // How many data bytes in a row have we seen?
bool IsolatedCodeTrigger = false; // Have seen data, then isolated code
// Now looking for data
ea_t IsolatedCodeAddr;
int IsolatedCodeLen;
int InstrLen;
clc5q
committed
bool InstOK;
insn_t LocalCmd;
ulong LocalFeatures;
clc5q
committed
clc5q
committed
for (int SegIndex = 0; SegIndex < SMP_get_segm_qty(); ++SegIndex) {
segment_t *seg = SMP_getnseg(SegIndex);
clc5q
committed
for (segment_t *seg = SMP_get_first_seg(); NULL != seg; seg = SMP_get_next_seg(RecentAddr)) {
clc5q
committed
if (SEG_CODE != seg->type)
continue;
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
char SegName[MAXSTR];
clc5q
committed
ssize_t SegNameSize = SMP_get_segm_name(seg, SegName, sizeof(SegName) - 1);
SMP_msg("Non-code addresses for code segment %s from %x to %x\n",
clc5q
committed
SegName, seg->startEA, seg->endEA);
#endif
for (ea_t addr = seg->startEA; addr < seg->endEA; addr = get_item_end(addr)) {
flags_t AddrFlags = getFlags(addr);
if (isHead(AddrFlags)) {
if (isData(AddrFlags)) {
DataRunLen += get_item_size(addr);
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("Data: %x\n", addr);
clc5q
committed
#endif
if (MIN_DATARUN_LEN <= DataRunLen) {
if (IsolatedCodeTrigger) {
// Saw data, then one isolated code, then data
do_unknown_range(IsolatedCodeAddr, IsolatedCodeLen, DOUNK_SIMPLE);
RemoveIDACodeAddr(IsolatedCodeAddr);
if (do_data_ex(IsolatedCodeAddr, byteflag(),
IsolatedCodeLen, BADNODE)) {
SMP_msg("Converted isolated code to data: %lx\n",
(unsigned long) IsolatedCodeAddr);
clc5q
committed
}
else {
SMP_msg("Failed to convert isolated code to data: %lx len: %x\n",
(unsigned long) IsolatedCodeAddr, IsolatedCodeLen);
clc5q
committed
}
IsolatedCodeTrigger = false;
} // end if (IsolatedCodeTrigger)
} // end if (MIN_DATARUN_LEN <= DataRunLen)
} // end if (isData(AddrFlags)
else if (isUnknown(AddrFlags)) {
// Just in case; unknown usually means not head or tail
// If in a data run, convert to data.
InstrLen = get_item_size(addr);
clc5q
committed
SMP_msg("Unknown: %x len: %x\n", addr, InstrLen);
clc5q
committed
if (0 < DataRunLen) {
if (do_data_ex(addr, byteflag(), InstrLen, BADNODE)) {
clc5q
committed
SMP_msg("Converted unknown to data at %x len: %x\n", addr, InstrLen);
clc5q
committed
DataRunLen += InstrLen;
}
else {
clc5q
committed
SMP_msg("Failed to convert unknown to data at %x len: %x\n", addr, InstrLen);
clc5q
committed
DataRunLen = 0;
IsolatedCodeTrigger = false;
}
}
}
else if (isCode(AddrFlags)) { // must be true
if (MIN_DATARUN_LEN <= DataRunLen) {
clc5q
committed
SMP_msg("DataRunLen: %d at %x\n", DataRunLen, addr);
clc5q
committed
InstOK = SMPGetCmd(addr, LocalCmd, LocalFeatures);
assert(InstOK);
InstrLen = (int) LocalCmd.size;
// We don't check the returned InstrLen for validity because IsCodeMisaligned()
// will check for validity immediately below.
clc5q
committed
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("Calling IsCodeMisaligned: len %d\n", InstrLen);
clc5q
committed
#endif
if (IsCodeMisaligned(addr)) {
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("Code was misaligned.\n");
clc5q
committed
#endif
do_unknown_range(addr, InstrLen, DOUNK_SIMPLE);
RemoveIDACodeAddr(addr);
if (do_data_ex(addr, byteflag(), InstrLen, BADNODE)) {
clc5q
committed
SMP_msg("Converted misaligned code to data at %x : len: %x\n",
clc5q
committed
addr, InstrLen);
clc5q
committed
// Step back so data gets processed.
DataRunLen += get_item_size(addr);
continue; // skip reset of DataRunLen
}
else {
clc5q
committed
SMP_msg("Misaligned code left as unknown at %x : len: %x\n",
clc5q
committed
addr, InstrLen);
clc5q
committed
IsolatedCodeTrigger = false;
}
} // end if (IsCodeMisaligned() ...)
else if (!hasRef(AddrFlags)) {
// No references at all --> isolated code.
IsolatedCodeTrigger = true;
IsolatedCodeAddr = addr;
IsolatedCodeLen = InstrLen;
}
else {
clc5q
committed
SMP_xref_t xb;
bool ok = xb.SMP_first_to(addr, XREF_ALL);
clc5q
committed
if (!ok) {
// No code xrefs to this target addr.
IsolatedCodeTrigger = true;
IsolatedCodeAddr = addr;
IsolatedCodeLen = InstrLen;
}
}
} // end if (MIN_DATARUN_LEN <= DataRunLen)
else if (IsolatedCodeTrigger) {
// Two instructions in a row does not fit the pattern.
IsolatedCodeTrigger = false;
}
DataRunLen = 0;
} // end if (isData) ... else if (isUnknown) ... else isCode
} // end if (isHead)
else if (isUnknown(AddrFlags)) {
// If in a data run, convert to data.
InstrLen = get_item_size(addr);
clc5q
committed
SMP_msg("Unknown: %x len: %x\n", addr, InstrLen);
clc5q
committed
if (0 < DataRunLen) {
if (do_data_ex(addr, byteflag(), InstrLen, BADNODE)) {
clc5q
committed
SMP_msg("Converted unknown to data at %x len: %x\n", addr, InstrLen);
clc5q
committed
DataRunLen += InstrLen;
}
else {
clc5q
committed
SMP_msg("Failed to convert unknown to data at %x len: %x\n", addr, InstrLen);
clc5q
committed
DataRunLen = 0;
IsolatedCodeTrigger = false;
}
}
}
} // end for (ea_t addr = seg->startEA; ...)
clc5q
committed
return;
} // end of FindDataInCode()
// 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 AuditTailChunkOwnership(void) {
char FuncName[MAXSTR];
// Iterate through all chunks in the program.
size_t NumChunks = get_fchunk_qty();
for (size_t ChunkIndex = 0; ChunkIndex < NumChunks; ++ChunkIndex) {
clc5q
committed
func_t *ChunkInfo = getn_fchunk((int) ChunkIndex);
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.
ea_t BestCandidate = 0;
func_parent_iterator_t FuncParent(ChunkInfo);
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg("Tail chunk: %x ", ChunkInfo->startEA);
clc5q
committed
#endif
for (bool ok = FuncParent.first(); ok; ok = FuncParent.next()) {
ea_t parent = FuncParent.parent();
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg(" parent: %x ", parent);
clc5q
committed
#endif
if ((parent > BestCandidate) && (parent < ChunkInfo->startEA))
BestCandidate = parent;
}
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg("\n");
clc5q
committed
#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 %lx as new owner of tail %lx\n",
(unsigned long) BestCandidate, (unsigned long) ChunkInfo->startEA);
clc5q
committed
// 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 %lx and parent %lx\n",
(unsigned long) ChunkInfo->startEA, (unsigned long) BestCandidate);
clc5q
committed
}
}
else {
func_t *FuncInfo = get_func(ChunkInfo->owner);
get_func_name(FuncInfo->startEA, FuncName, sizeof(FuncName) - 1);
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg("No good parent candidate before tail at %x\n",
clc5q
committed
ChunkInfo->startEA);
clc5q
committed
SMP_msg("Current parent is %x: %s\n", FuncInfo->startEA, FuncName);
clc5q
committed
#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
clc5q
committed
SMP_msg("Finding parent func candidates for %x:", ChunkInfo->startEA);
clc5q
committed
#endif
SMP_bounds_t CurrFunc;
for (size_t FuncIndex = 0; FuncIndex < FuncBounds.size(); ++FuncIndex) {
CurrFunc = FuncBounds[FuncIndex];
if ((CurrFunc.startEA < ChunkInfo->startEA)
&& (CurrFunc.startEA > BestCandidate)) {
BestCandidate = CurrFunc.startEA;
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg(" candidate: %x tail: %x", BestCandidate,
clc5q
committed
ChunkInfo->startEA);
#endif
}
else {
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg(" not a candidate: %x tail: %x best: %x\n",
clc5q
committed
CurrFunc.startEA, ChunkInfo->startEA, BestCandidate);
#endif
break;
}
} // end for (size_t FuncIndex = 0; ...)
if (0 >= BestCandidate) { // highly unlikely
clc5q
committed
SMP_msg("No good func entry parent candidate.\n");
clc5q
committed
}
else {
FuncInfo = get_func(BestCandidate);
get_func_name(FuncInfo->startEA, FuncName, sizeof(FuncName) - 1);
#if SMP_DEBUG_CHUNKS
clc5q
committed
SMP_msg("Best func entry parent candidate: %s at %x",
clc5q
committed
FuncName, BestCandidate);
if (FuncInfo->endEA == ChunkInfo->startEA)
clc5q
committed
SMP_msg(" Function endEA == tail chunk startEA");
SMP_msg("\n");
clc5q
committed
#endif
}
}
} // end if (ChunkInfo->owner != BestCandidate)
#if SMP_DEBUG_CHUNKS
else {
clc5q
committed
SMP_msg("Already best parent for %x is %x\n", ChunkInfo->startEA,
clc5q
committed
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
ChunkInfo->owner);
}
#endif
} // end if (is_func_tail(ChunkInfo))
} // end for (size_t ChunkIndex = 0; ...)
return;
} // end of AuditTailChunkOwnership()
// If the addresses signified from DisasmIndex to IDAProIndex are
// all considered data and do NOT follow a return instruction,
// return false and update AreaSize to reflect the area to be
// converted.
// Return value: true -> skip to IDAProIndex; false -> convert AreaSize bytes.
bool FindDataToConvert(size_t IDAProIndex, size_t DisasmIndex, int &AreaSize) {
ea_t PrevIDAAddr;
ea_t NextIDAAddr;
size_t ShadowDisasmIndex = DisasmIndex - 1;
ea_t DisasmAddr = DisasmLocs[ShadowDisasmIndex];
bool CannotConvert = false; // return value
bool DebugAddress = false;
#if SMP_DEBUG_FIXUP_IDB
DebugAddress = (DisasmAddr == 0x806c19a);
#endif
if (DebugAddress) {
clc5q
committed
SMP_msg("IDAProIndex: %zu DisasmIndex: %zu\n", IDAProIndex, DisasmIndex);
SMP_msg("IDA locs size %zu Disasm locs size %zu\n", IDAProLocs.size(),
clc5q
committed
DisasmLocs.size());
}
if (IDAProIndex >= IDAProLocs.size()) {
// Have already processed the last IDA address.
clc5q
committed
if (DebugAddress) SMP_msg(" Already done with IDAProLocs.\n");
clc5q
committed
return true;
}
else if (DisasmIndex >= DisasmLocs.size()) {
// Strange. Last Disasm address is only one to convert, and
// IDA still has addresses after that?
clc5q
committed
if (DebugAddress) SMP_msg(" Already done with DisasmLocs.\n");
clc5q
committed
return true;
}
else if (IDAProIndex < 2) {
// We have Disasm addrs before the very first IDA addr. We
// don't trust this boundary case.
clc5q
committed
if (DebugAddress) SMP_msg(" Boundary case with IDAProLocs.\n");
clc5q
committed
return true;
}
NextIDAAddr = IDAProLocs[IDAProIndex - 1];
PrevIDAAddr = IDAProLocs[IDAProIndex - 2];
if (DebugAddress) SMP_msg(" PrevIDAAddr: %lx NextIDAAddr: %lx\n", (unsigned long) PrevIDAAddr, (unsigned long) NextIDAAddr);
clc5q
committed
// See if previous IDA address was a return.
flags_t PrevFlags = getFlags(PrevIDAAddr);
if (!isCode(PrevFlags) || !isHead(PrevFlags)) {
SMP_msg("PrevIDAAddr %lx not isCode or not isHead.\n", (unsigned long) PrevIDAAddr);
clc5q
committed
return true;
}
SMPInstr PrevInstr(PrevIDAAddr);
PrevInstr.Analyze();
clc5q
committed
if (DebugAddress) SMP_msg("Finished PrevInstr.Analyze()\n");
clc5q
committed
if (PrevInstr.MDIsReturnInstr()) {
// Right after a return come no-ops and 2-byte no-ops
// that are just for alignment. IDA does not seem to be
// happy when we convert all those to code.
clc5q
committed
if (DebugAddress) SMP_msg(" Data followed a return instruction.\n");
clc5q
committed
return true;
}
// Now, see if the area from DisasmAddr to NextIDAAddr is all data
// according to IDA.
while (DisasmAddr < NextIDAAddr) {
flags_t DataFlags = getFlags(DisasmAddr);
if (isTail(DataFlags)) {
if (DebugAddress) SMP_msg(" tail byte: %lx\n", (unsigned long) DisasmAddr);
clc5q
committed
DisasmAddr = get_item_end(DisasmAddr);
}
else if (isData(DataFlags)) {
if (DebugAddress) SMP_msg(" data byte: %lx\n", (unsigned long) DisasmAddr);
clc5q
committed
DisasmAddr = get_item_end(DisasmAddr);
}
else if (isCode(DataFlags)) {
// How could this ever happen?
if (DebugAddress) SMP_msg(" isCode: %lx\n", (unsigned long) DisasmAddr);
clc5q
committed
return true;
}
else { // must be isUnknown()
// Very conservative here; only want to convert when the whole
// region is data, because that is a symptom of IDA missing
// a piece of code within a function (usually a piece of code
// that is only reachable via an indirect jump).
if (DebugAddress) SMP_msg(" Not isData: %lx\n", (unsigned long) DisasmAddr);
clc5q
committed
return true;
}
if (DebugAddress) SMP_msg(" new DisasmAddr: %lx\n", (unsigned long) DisasmAddr);
clc5q
committed
} // end while (DisasmAddr < NextIDAAddr)
clc5q
committed
if (DebugAddress) SMP_msg(" loop exit CannotConvert: %d\n", CannotConvert);
clc5q
committed
if (!CannotConvert) {
// Success.
DisasmAddr = DisasmLocs[ShadowDisasmIndex];
AreaSize = NextIDAAddr - DisasmAddr;
if (DebugAddress) {
clc5q
committed
SMP_msg(" Success! AreaSize: %x Old index: %zu new index: %zu\n",
clc5q
committed
AreaSize, ShadowDisasmIndex, DisasmIndex);
clc5q
committed
SMP_msg(" exiting FindDataToConvert()\n");
SMP_msg("\n");
clc5q
committed
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
}
} // end if (!CannotConvert)
return CannotConvert;
} // end of FindDataToConvert()
// Does a converted code region look like a function prologue? If so,
// we should not include it in the previous function.
bool IsFunctionPrologue(ea_t StartAddr, ea_t EndAddr) {
return false; // **!!** TODO
} // end of IsFunctionPrologue()
// Patch program bytes that could not be converted from
// data to code, if it can be determined that the bytes represent code
// that IDA has a hard time with.
// Currently limited to finding "call near ptr 0" instructions, which
// often are found in optimized glibc code because gcc was able to
// determine that a function pointer was zero and did constant propagation,
// but unfortunately was not able to determine that the code was unreachable.
// IDA will not succeed in ua_code() for "call 0", but there is no danger
// of a working program ever executing this code. Replacing the call with
// no-ops permits us to continue converting a contiguous range of data to
// code, and permits IDA to reanalyze the function later.
// Returns true if program bytes were patched.
bool MDPatchUnconvertedBytes(ea_t CurrDisasmAddr) {
flags_t AddrFlags = getFlags(CurrDisasmAddr);
if (isData(AddrFlags) || isTail(AddrFlags)) {
// Bytes should have been converted to unknown already.
clc5q
committed
SMP_msg("Cannot patch data bytes or tail bytes at %x\n", CurrDisasmAddr);
clc5q
committed
return false;
}
SMPInstr PatchInstr(CurrDisasmAddr);
PatchInstr.Analyze();
int InstrLen = PatchInstr.GetCmd().size;
if (0 >= InstrLen) {
clc5q
committed
SMP_msg("decode_insn() failed on patch location %x\n", CurrDisasmAddr);
clc5q
committed
return false;
}
else {
if (PatchInstr.GetCmd().itype != NN_call) {
clc5q
committed
SMP_msg("Cannot patch non-call instruction at %x\n", CurrDisasmAddr);
clc5q
committed
return false;
}
PatchInstr.PrintOperands();
op_t CallDest = PatchInstr.GetFirstUse()->GetOp();
clc5q
committed
if ((o_near != CallDest.type) || (0 != CallDest.addr)) {
clc5q
committed
SMP_msg("Cannot patch call unless it is call near ptr 0 at %x",
clc5q
committed
CurrDisasmAddr);
clc5q
committed
return false;
}
ea_t PatchAddr = CurrDisasmAddr;
for (int i = 0; i < InstrLen; ++i) {
bool ok = patch_byte(PatchAddr, 0x90); // x86 no-op
if (!ok) {
clc5q
committed
SMP_msg("patch_byte() failed at %x\n", PatchAddr);
clc5q
committed
return false;
}
++PatchAddr;
}
clc5q
committed
SMP_msg("Patched %d bytes successfully at %x\n", InstrLen, CurrDisasmAddr);
#if IDA_SDK_VERSION < 600
InstrLen = ua_code(CurrDisasmAddr);
#else
InstrLen = create_insn(CurrDisasmAddr);
clc5q
committed
if (0 >= InstrLen) {
clc5q
committed
SMP_msg(" ... but ua_code() still failed!\n");
clc5q
committed
return false;
}
} // end if (0 >= InstrLen) ... else ...
return true;
} // end of MDPatchUnconvertedBytes()
// Use the lists of code addresses identified by IDA Pro (in IDAProLocs)
clc5q
committed
// and an external disassembler (in DisasmLocs). Compare the lists and
// try to convert addresses to code that are found in DisasmLocs but
// not in IDAProLocs. Emit warnings when IDAProLocs has a code address
// not found in DisasmLocs.
void FixCodeIdentification(void) {
size_t DisasmIndex = 0;
ea_t CurrDisasmAddr = DisasmLocs[DisasmIndex++];
size_t IDAProIndex = 0;
ea_t CurrAddr = IDAProLocs[IDAProIndex++];
while (DisasmIndex <= DisasmLocs.size()) {
// If the current address is less than the current
// external disasm address, we have the rare case in
// which IDA Pro has identified an address as code
// but the external disasm has not. Emit a warning
// message and go on to the next IDA address.
if (CurrAddr < CurrDisasmAddr) {
SMPInstr TempInstr(CurrAddr);
TempInstr.Analyze();
SMP_msg("AUDIT: Address %lx is code in IDB but not in external disassembler: %s\n",
(unsigned long) CurrAddr, TempInstr.GetDisasm());
clc5q
committed
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
if (IDAProIndex < IDAProLocs.size())
CurrAddr = IDAProLocs[IDAProIndex++];
else {
// Last IDA addr; might still process Disasm addrs
// after loop exit.
break;
}
}
else if (CurrAddr == CurrDisasmAddr) {
// If equal, no problem, we are moving through the
// code addresses in lockstep. Grab the next address
// from each source.
if (DisasmIndex < DisasmLocs.size()) {
CurrDisasmAddr = DisasmLocs[DisasmIndex++];
}
else {
++DisasmIndex; // cause loop exit; skip cleanup loop
}
if (IDAProIndex < IDAProLocs.size())
CurrAddr = IDAProLocs[IDAProIndex++];
else {
// Last IDA addr; might still process Disasm addrs
// after loop exit in cleanup loop.
break;
}
}
else {
// We must have CurrAddr > CurrDisasmAddr. That means
// IDA has jumped over some code addresses in
// DisasmLocs. We need to try to convert addresses
// to code until we can reach the current addr.
// For now, we will address only the case in which IDA
// has identified addresses as data bytes, and the
// external disassembler(e.g. objdump) has identified
// the same addresses as code. We only want to deal with
// contiguous areas of data-to-code conversion that do NOT
// follow a return statement.
int AreaSize = 0;
ea_t AreaStart = CurrDisasmAddr;
ea_t AreaEnd;
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("CurrDisasmAddr: %x CurrAddr: %x\n", CurrDisasmAddr, CurrAddr);
clc5q
committed
#endif
bool SkipArea = FindDataToConvert(IDAProIndex, DisasmIndex, AreaSize);
if (SkipArea) {
// Skip over the extra external disasm addresses.
while (CurrDisasmAddr < CurrAddr)
CurrDisasmAddr = DisasmLocs[DisasmIndex++];
}
else {
// Convert the overlooked code region to unexplored.
AreaEnd = CurrDisasmAddr + AreaSize;
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("Found data to convert: %x to %x\n", AreaStart, AreaEnd);
clc5q
committed
#endif
do_unknown_range(AreaStart, AreaSize, DOUNK_SIMPLE);
SMP_bounds_t ConvertRegion;
ConvertRegion.startEA = AreaStart;
ConvertRegion.endEA = AreaEnd;
FixupRegion CurrRegion(ConvertRegion);
CodeReanalyzeList.push_back(CurrRegion);
clc5q
committed
bool AllConverted = true;
bool AllNops = true;
clc5q
committed
do {
flags_t InstrFlags = getFlags(CurrDisasmAddr);
if (!isUnknown(InstrFlags)) {
SMP_msg("Sync problem in FixCodeID: %lx\n", (unsigned long) CurrDisasmAddr);
clc5q
committed
}
else {
int InstrLen = ua_code(CurrDisasmAddr);
int InstrLen = create_insn(CurrDisasmAddr);
clc5q
committed
if (InstrLen > 0) { // Successfully converted to code
SMPInstr NewInstr(CurrDisasmAddr);
NewInstr.Analyze();
if (!NewInstr.IsNop())
AllNops = false;
clc5q
committed
#if SMP_DEBUG_FIXUP_IDB
#if 0
clc5q
committed
SMP_msg("FixCodeID success at %x: len: %d %s\n", CurrDisasmAddr,
clc5q
committed
InstrLen, NewInstr.GetDisasm());
#endif
clc5q
committed
#endif
}
else {
if (MDPatchUnconvertedBytes(CurrDisasmAddr)) {
clc5q
committed
SMP_msg(" Patched bytes at %x\n", CurrDisasmAddr);
clc5q
committed
}
else {
CurrRegion.FixupInstrs.push_back(CurrDisasmAddr);
clc5q
committed
AllConverted = false;
clc5q
committed
SMP_msg("FixCodeID failure at %x\n", CurrDisasmAddr);
clc5q
committed
}
}
} // end if (isCode(InstrFlags) ... else ...
if (DisasmIndex < DisasmLocs.size()) {
CurrDisasmAddr = DisasmLocs[DisasmIndex++];
}
else {
// cause loops to exit
CurrDisasmAddr = CurrAddr;
++DisasmIndex; // skip cleanup loop
}
} while (CurrDisasmAddr < CurrAddr);
if (AllConverted && AllNops) {
// We want to convert the region back to unexplored bytes
// and take it off the work list. Regions that are all nops
// create data flow analysis problems sometimes. The region
// is often unreachable code and produces a basic block with
// no predecessors within a function. This often happens when
// an optimizing compiler uses nops as padding to align jump
// targets on cache line bounaries. With no fall through into
// the nops, they are unreachable and should be left as unknown.
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("FixCodeID nops region from %x to %x\n", CurrRegion.GetStart(),
CurrRegion.GetEnd());
do_unknown_range(CurrRegion.GetStart(),
CurrRegion.GetEnd() - CurrRegion.GetStart(), DOUNK_SIMPLE);
CodeReanalyzeList.pop_back();
}
clc5q
committed
} // end if (SkipArea) ... else ...
} // end if (addr < CurrDisasmAddr) .. else if ... else ...
} // end while (DisasmIndex <= DisasmLocs.size()
#if 0 // Make this code use FindDataToConvert() **!!**
// Cleanup loop:
// If there are still Disasm addrs to process, try to turn them
// into code in the IDB.
while (DisasmIndex <= DisasmLocs.size()) {
flags_t InstrFlags = getFlags(CurrDisasmAddr);
if (isCode(InstrFlags)) {
clc5q
committed
SMP_msg("Sync problem in FixCodeID: %x\n", CurrDisasmAddr);
clc5q
committed
}
else {
// Clear bytes to unexplored.
clc5q
committed
segment_t *seg = SMP_getseg(CurrDisasmAddr);
clc5q
committed
if (SEG_CODE == seg->type) {
do_unknown_range(CurrDisasmAddr, seg->endEA - CurrDisasmAddr, DOUNK_SIMPLE);
}
else {
// Might be safest to just discontinue processing
// if we wander into a non-code segment.
// DisasmLocs should not have an entire code segment
// that IDA Pro missed.
break;
}
int InstrLen = ua_code(CurrDisasmAddr);
if (InstrLen > 0) { // Successfully converted to code
SMPInstr NewInstr(CurrDisasmAddr);
NewInstr.Analyze();
clc5q
committed
SMP_msg("FixCodeID success at %x: %s\n", CurrDisasmAddr,
clc5q
committed
NewInstr.GetDisasm());
}
else {
clc5q
committed
SMP_msg("FixCodeID failure at %x\n", CurrDisasmAddr);
clc5q
committed
}
} // end if (isCode(InstrFlags) ... else ...
if (DisasmIndex < DisasmLocs.size()) {
CurrDisasmAddr = DisasmLocs[DisasmIndex++];
}
else {
++DisasmIndex; // cause loop to exit
}
} // end while (DisasmIndex <= DisasmLocs.size()
#endif
return;
} // end of FixCodeIdentification()
// Analyze instructions that could not be analyzed earlier and were placed on the CodeReanalyzeList.
// Earlier failures are usually because the instruction branches to an address that has not
// yet been converted from data to code, so ua_code() failed. Now that all data to code
// conversions have completed, ua_code() should succeed.
// Return the number of instructions successfully analyzed.
int FixupNewCodeChunks(void) {
list<FixupRegion>::iterator CurrRegion;
int changes = 0;
for (CurrRegion = CodeReanalyzeList.begin(); CurrRegion != CodeReanalyzeList.end(); ++CurrRegion) {
bool AllConverted = true;
bool AllNops = true;
bool NoFixups = (0 == CurrRegion->FixupInstrs.size());
if (NoFixups) {
CurrRegion->SetStart(BADADDR); // mark for removal
continue; // skip to next region
}
list<ea_t>::iterator CurrInstr;
for (CurrInstr = CurrRegion->FixupInstrs.begin(); CurrInstr != CurrRegion->FixupInstrs.end(); ++CurrInstr) {
#if IDA_SDK_VERSION < 600
int InstrLen = ua_code(*CurrInstr);
#else
int InstrLen = create_insn(*CurrInstr);
if (InstrLen > 0) { // Successfully converted to code
SMPInstr NewInstr(*CurrInstr);
NewInstr.Analyze();
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("FixupNewCodeChunks success at %x: len: %d\n", *CurrInstr, InstrLen);
#endif
if (!NewInstr.IsNop()) {
AllNops = false;
*CurrInstr = BADADDR; // mark for removal
}
}
else {
AllConverted = false;
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("FixupNewCodeChunks failure at %x\n", *CurrInstr);
#endif
}
} // end for all instrs in CurrRegion
if (AllConverted && !AllNops) {
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("FixupNewCodeChunks success for region from %x to %x\n",
CurrRegion->GetStart(), CurrRegion->GetEnd());
#endif
CurrRegion->SetStart(BADADDR); // mark for removal
}
else if (AllConverted && AllNops) {
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg("FixupNewCodeChunks re-converting nops region from %x to %x\n",
CurrRegion->GetStart(), CurrRegion->GetEnd());
#endif
do_unknown_range(CurrRegion->GetStart(),
CurrRegion->GetEnd() - CurrRegion->GetStart(), DOUNK_SIMPLE);
CurrRegion->SetStart(BADADDR); // mark for removal
}
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
else {
// Remove only the instructions that were fixed up.
CurrInstr = CurrRegion->FixupInstrs.begin();
while (CurrInstr != CurrRegion->FixupInstrs.end()) {
if (BADADDR == *CurrInstr) {
CurrInstr = CurrRegion->FixupInstrs.erase(CurrInstr);
}
else {
++CurrInstr;
}
}
}
} // end for all regions in the CodeReanalyzeList
// Remove completed regions from the CodeReanalyzeList
CurrRegion = CodeReanalyzeList.begin();
while (CurrRegion != CodeReanalyzeList.end()) {
if (BADADDR == CurrRegion->GetStart())
CurrRegion = CodeReanalyzeList.erase(CurrRegion);
else
++CurrRegion;
}
#if 0
if (AllConverted) {
if (IsFunctionPrologue(AreaStart, AreaEnd)) {
// Create a new function entry chunk here.
// **!!** TODO
;
}
else {
// Extend the previous chunk to include the
// converted code.
ea_t PrevIDAAddr = IDAProLocs[IDAProIndex - 2];
func_t *PrevChunk = get_fchunk(PrevIDAAddr);
#if SMP_DEBUG_FIXUP_IDB
clc5q
committed
SMP_msg(" addr in chunk to extend: %x\n", PrevIDAAddr);
SMP_msg(" func_t pointer for chunk: %x\n", PrevChunk);
#endif
#if 0 // temporary for debugging
if (is_func_entry(PrevChunk)) {
// Extend the func entry to contain the new code.
if (func_setend(PrevIDAAddr, AreaEnd)) {
clc5q
committed
SMP_msg("Func extended to include code from %x to %x\n",
AreaStart, AreaEnd);
FuncReanalyzeList.push_back(PrevIDAAddr);
}
else {
clc5q
committed
SMP_msg("Failed to extend func from %x to %x\n",
AreaStart, AreaEnd);
}
}
else { // tail
// See if this works for function tails, also.
// Extend the func entry to contain the new code.
if (func_setend(PrevIDAAddr, AreaEnd)) {
clc5q
committed
SMP_msg("Tail extended to include code from %x to %x\n",
AreaStart, AreaEnd);
func_t *TailOwner = get_func(PrevChunk->owner);
FuncReanalyzeList.push_back(PrevIDAAddr);
}
else {
clc5q
committed
SMP_msg("Failed to extend tail from %x to %x\n",
AreaStart, AreaEnd);
}
} // end if (is_func_entry()) ... else ...
#endif
} // end if (IsFunctionPrologue()) ... else ...
} // end if (AllConverted)
else {
clc5q
committed
SMP_msg("not AllConverted; cannot include new code in previous chunk.\n");
}
#endif
return changes;
} // end of FixupNewCodeChunnks()
// Audit the IDA code database by looking at all instructions in the
// code segment and printing all those that are not contained in a
// function. Emit the context-free annotations that we are able to
// emit on a per-instruction basis.
void FindOrphanedCode(segment_t *CurrSeg, FILE *AnnotFile, FILE *InfoAnnotFile) {
char disasm[MAXSTR];
for (ea_t addr = CurrSeg->startEA; addr < CurrSeg->endEA;
addr = get_item_end(addr)) {
flags_t InstrFlags = getFlags(addr);
clc5q
committed
if (isTail(InstrFlags))
continue;
if (isHead(InstrFlags) && isCode(InstrFlags)) {
ea_t FirstFuncAddr;
if (!(CurrProg->IsInstAddrStillInFunction(addr, FirstFuncAddr))) {
SMPInstr CurrInst(addr);
CurrInst.Analyze();
clc5q
committed
SMP_msg("Orphan code at %x : %s\n", addr, CurrInst.GetDisasm());
// TODO: If there are code xrefs to the orphan code,
// see what kind. If a CALL, and orphan code looks
// like a prologue, make a function. If a JUMP of
// some kind, then make a function chunk and make
// it a tail of all functions that jump to it. **!!**
clc5q
committed
// Do machine-dependent fixes for DEF and USE lists.
// The fixes can help produce better annotations.
CurrInst.MDFixupDefUseLists();
// If instruction is still not included in a code chunk,
// emit annotations for it in isolation.
CurrInst.EmitAnnotations(true, false, true, AnnotFile, InfoAnnotFile, CurrProg);
// If instruction is an indirect branch, emit an XREF
// annotation for each of its targets.
SMPitype CurrDataFlow = CurrInst.GetDataFlowType();
if ((CurrDataFlow == INDIR_JUMP) || (CurrDataFlow == INDIR_CALL)) {
SMP_xref_t xrefs;
for (bool ok = xrefs.SMP_first_from(addr, XREF_ALL); ok; ok = xrefs.SMP_next_from()) {
if (xrefs.GetTo() != 0) {
if (xrefs.GetIscode() && (xrefs.GetType() != fl_F)) {
// Found a code target, with its address in xrefs.to
PrintCodeToCodeXref(addr, xrefs.GetTo(), CurrInst.GetSize());
}
}
}
}
}
}
else if (isUnknown(InstrFlags)) {
clc5q
committed
SMP_msg("Unanalyzed byte at %x\n", addr);
// Can IDA analyze this to be code?
int InstrLen;
#if IDA_SDK_VERSION < 600
InstrLen = ua_code(addr);
#else
InstrLen = create_insn(addr);
#endif
bool IDAsuccess = generate_disasm_line(addr, disasm, sizeof(disasm) - 1);
if (IDAsuccess) {
// Remove interactive color-coding tags.
ssize_t StringLen = tag_remove(disasm, disasm, 0);
if (-1 >= StringLen) {
SMP_msg("ERROR: tag_remove failed at addr %lx \n", (unsigned long) addr);
clc5q
committed
SMP_msg("Successfully analyzed! %s\n", disasm);
SMPInstr UnknownInstr(addr);
UnknownInstr.Analyze();
// TODO: Get new code into a chunk. **!!**
// If instruction is still not included in a code chunk,
// emit annotations for it in isolation.
UnknownInstr.EmitAnnotations(true, false, true, AnnotFile, InfoAnnotFile, CurrProg);
SMP_msg("ERROR: generate_disasm_line failed at addr %lx \n", (unsigned long) addr);
}
}
} // end for (ea_t addr = CurrSeg->startEA; ...)
} // end of FindOrphanedCode()
// Version of FindOrphanedCode that does not emit annotations but can be used
// to determine at what point in time code becomes orphaned.
void Debug_FindOrphanedCode(segment_t *CurrSeg, bool FirstRun) {
ea_t DebugAddr = 0x8050db0;
for (ea_t addr = CurrSeg->startEA; addr < CurrSeg->endEA;
addr = get_item_end(addr)) {
flags_t InstrFlags = getFlags(addr);
if (isHead(InstrFlags) && isCode(InstrFlags)) {
func_t *CurrFunc = get_func(addr);
if (NULL == CurrFunc) { // Code not in a func; orphaned
pair<set<ea_t>::iterator, bool> pairib;
pairib = CodeOrphans.insert(addr);
if (DebugAddr == addr) {
SMP_msg("DEBUG: Orphaned code addr %lx found.\n", (unsigned long) addr);
}
if ((!FirstRun) && (pairib.second)) {
SMP_msg("SERIOUS WARNING: Newly orphaned code at %lx \n", (unsigned long) addr);
}
}
}
} // end for (ea_t addr = CurrSeg->startEA; ...)
} // end of Debug_FindOrphanedCode()
// 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 AuditCodeTargets(void) {
// Cover all the code that IDA has grouped into functions by iterating
// through all function chunks in the program.
size_t NumChunks = get_fchunk_qty();
for (size_t ChunkIndex = 0; ChunkIndex < NumChunks; ++ChunkIndex) {
func_t *ChunkInfo = getn_fchunk((int) ChunkIndex);
char FuncName[MAXSTR];
get_func_name(ChunkInfo->startEA, 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)) {
clc5q
committed
SMP_xref_t xb;
ea_t addr = ChunkInfo->startEA;
clc5q
committed
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: %lx %s\n", (unsigned long) addr, FuncName);
clc5q
committed
#if SMP_DEBUG_FIXUP_IDB
else if ((XrefType == fl_JF) || (XrefType == fl_JN)) {
clc5q
committed
SMP_msg("Jump to func: %x %s from: %x\n",
addr, FuncName, xb.GetFrom());
clc5q
committed
#endif
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
clc5q
committed
func_t *CallingFunc = get_func(xb.GetFrom());
clc5q
committed
SMP_msg("Call to %x Func %s from %x not in function.\n",
addr, FuncName, xb.GetFrom());
clc5q
committed
} // end if (xb.GetIscode())
else { // DATA xref
if (XrefType == dr_O) {