Newer
Older
clc5q
committed
if (DebugAddress) SMP_msg(" data byte: %x\n", DisasmAddr);
clc5q
committed
DisasmAddr = get_item_end(DisasmAddr);
}
else if (isCode(DataFlags)) {
// How could this ever happen?
clc5q
committed
if (DebugAddress) SMP_msg(" isCode: %x\n", 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).
clc5q
committed
if (DebugAddress) SMP_msg(" Not isData: %x\n", DisasmAddr);
clc5q
committed
return true;
}
clc5q
committed
if (DebugAddress) SMP_msg(" new DisasmAddr: %x\n", 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: %d new index: %d\n",
clc5q
committed
AreaSize, ShadowDisasmIndex, DisasmIndex);
clc5q
committed
SMP_msg(" exiting FindDataToConvert()\n");
SMP_msg("\n");
clc5q
committed
1029
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
1055
}
} // 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();
clc5q
committed
SMP_msg("Address %x is code in IDB but not in external disassembler: %s\n",
clc5q
committed
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
CurrAddr, TempInstr.GetDisasm());
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.
int InstrLen;
// 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)) {
clc5q
committed
SMP_msg("Sync problem in FixCodeID: %x\n", CurrDisasmAddr);
clc5q
committed
}
else {
#if IDA_SDK_VERSION < 600
InstrLen = ua_code(CurrDisasmAddr);
#else
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
}
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
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)) {
func_t *CurrFunc = get_func(addr);
if (NULL == CurrFunc) {
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. **!!**
// If instruction is still not included in a code chunk,
// emit annotations for it in isolation.
CurrInst.EmitAnnotations(true, false, true, AnnotFile, InfoAnnotFile);
}
}
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) {
clc5q
committed
SMP_msg("ERROR: tag_remove failed at addr %x \n", 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);
clc5q
committed
SMP_msg("ERROR: generate_disasm_line failed at addr %x \n", 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) {
char disasm[MAXSTR];
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) {
clc5q
committed
SMP_msg("DEBUG: Orphaned code addr %x found.\n", addr);
}
if ((!FirstRun) && (pairib.second)) {
clc5q
committed
SMP_msg("SERIOUS WARNING: Newly orphaned code at %x \n", 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)) {
clc5q
committed
SMP_msg("Bad xref type: %x %s\n", 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
clc5q
committed
SMP_msg("Fall through to func: %x %s from: %x\n",
addr, FuncName, 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) {
clc5q
committed
SMP_msg("Data xref to %x Func %s from %x\n",
addr, FuncName, xb.GetFrom());
clc5q
committed
SMP_msg("Strange data xref %d to %x Func %s from %x\n",
XrefType, addr, FuncName, xb.GetFrom());
clc5q
committed
} // 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 (ea_t addr = ChunkInfo->startEA; addr < ChunkInfo->endEA;
addr = get_item_end(addr)) {
flags_t InstrFlags = getFlags(addr);
if (isCode(InstrFlags) && isHead(InstrFlags)) {
SMPInstr CurrInst(addr);
CurrInst.Analyze();
if ((CALL|JUMP|COND_BRANCH) & CurrInst.GetDataFlowType()) {
clc5q
committed
SMP_xref_t xb;
for (bool ok = xb.SMP_first_from(addr, XREF_FAR); ok; ok = xb.SMP_next_from()) {
if (xb.GetIscode()) {
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.
clc5q
committed
ea_t LastAddr = FindNewFuncLimit(FirstAddr);
if (CALL == CurrInst.GetDataFlowType())
clc5q
committed
SMP_msg("Found new func from %x to %x\n",
FirstAddr, LastAddr);
else
clc5q
committed
SMP_msg("Found new chunk from %x to %x\n",
FirstAddr, LastAddr);
}
}
}
}
}
}
} // end for (size_t ChunkIndex = 0; ... )
return;
} // end of AuditCodeTargets()
// 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.
ea_t FindNewFuncLimit(ea_t StartAddr) {
ea_t LimitAddr = StartAddr;
clc5q
committed
segment_t *seg = SMP_getseg(StartAddr);
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
if (NULL == seg)
return LimitAddr;
ea_t SegLimit = seg->endEA;
for (ea_t addr = get_item_end(StartAddr); addr < SegLimit; addr = get_item_end(addr)) {
flags_t InstrFlags = getFlags(addr);
if (isCode(InstrFlags) && 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()
void SpecialDebugOutput(void) {
char disasm[MAXSTR];
vector<ea_t> ProblemAddrs;
ProblemAddrs.push_back(0x8066d08);
bool IDAsuccess;
int InstLen;
ssize_t StringLen;
clc5q
committed
insn_t LocalCmd;
ulong LocalFeatures;
for (size_t index = 0; index < ProblemAddrs.size(); ++index) {
ea_t addr = ProblemAddrs[index];
flags_t InstrFlags = getFlags(addr);
if (isCode(InstrFlags) && isHead(InstrFlags)) {
clc5q
committed
IDAsuccess = SMPGetCmd(addr, LocalCmd, LocalFeatures);
InstLen = (int) LocalCmd.size;
if ((IDAsuccess) && (0 < InstLen)) {
IDAsuccess = generate_disasm_line(addr, disasm, sizeof(disasm) - 1);
if (IDAsuccess) {
StringLen = tag_remove(disasm, disasm, 0);
if (-1 < StringLen)
clc5q
committed
SMP_msg("Problem addr %x : %s\n", addr, disasm);
clc5q
committed
SMP_msg("ERROR: tag_remove failed at addr %x \n", addr);
clc5q
committed
SMP_msg("ERROR: generate_disasm_line failed at addr %x \n", addr);
clc5q
committed
SMP_msg("ERROR: decode_insn failed at addr %x \n", addr);
}
}
return;
} // end of SpecialDebugOutput()
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
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
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
// Convert a call type string from the policy file, such as "FILECALLS", to the
// corresponding ZST_SysCallType, such as ZST_FILE_CALL.
ZST_SysCallType ConvertStringToCallType(char *Str2) {
ZST_SysCallType ReturnVal;
if (0 == strcmp("PRIVILEGECALLS", Str2)) {
ReturnVal = ZST_HIGHPRIVILEGE_CALL;
}
else if (0 == strcmp("FILECALLS", Str2)) {
ReturnVal = ZST_FILE_CALL;
}
else if (0 == strcmp("NETWORKCALLS", Str2)) {
ReturnVal = ZST_NETWORK_CALL;
}
else {
ReturnVal = ZST_UNMONITORED_CALL;
}
return ReturnVal;
} // end of ConvertStringToCallType()
// Convert a policy string from the policy file, such as "DISALLOW", to
// the corresponding ZST_Policy value, such as ZST_DISALLOW.
ZST_Policy ConvertStringToPolicy(char *Str3) {
ZST_Policy ReturnVal;
if (0 == strcmp("DISALLOW", Str3)) {
ReturnVal = ZST_DISALLOW;
}
else if (0 == strcmp("WHITELIST", Str3)) {
ReturnVal = ZST_WHITELIST;
}
else if (0 == strcmp("BLACKLIST", Str3)) {
ReturnVal = ZST_BLACKLIST;
}
else { // error handling precedes calls to this function
ReturnVal = ZST_ALLOWALL;
}
return ReturnVal;
} // end of ConvertStringToPolicy()
// Given a function name, return its Zephyr Security Toolkit call type.
ZST_SysCallType GetCallTypeFromFuncName(string SysCallName) {
ZST_SysCallType ReturnVal;
map<string, ZST_SysCallType>::iterator FindIter = ZST_FuncTypeMap.find(SysCallName);
if (FindIter == ZST_FuncTypeMap.end()) { // not found; might not even be system call
ReturnVal = ZST_UNMONITORED_CALL;
}
else {
ReturnVal = FindIter->second;
}
return ReturnVal;
} // end of GetCallTypeFromFuncName()
// Get the user-specified security policy for the given call type.
ZST_Policy GetPolicyFromCallType(ZST_SysCallType CallType) {
ZST_Policy ReturnVal;
map<ZST_SysCallType, ZST_Policy>::iterator FindIter = ZST_TypePolicyMap.find(CallType);
if (FindIter == ZST_TypePolicyMap.end()) {
// Policy not found; default to ALLOW_ALL
ReturnVal = ZST_ALLOWALL;
}
else {
ReturnVal = FindIter->second;
}
return ReturnVal;
} // end of GetPolicyFromCallType()
// Given a call type and called function name, is it on the location whitelist
// for that call type?
// NOTE: HANDLE CASE IN WHICH WHITELISTED LOCATION IS A PREFIX, TERMINATING in a slash.
bool IsLocationWhitelisted(ZST_SysCallType CallType, string LocationName) {
set<string>::iterator FindIter;
bool ReturnVal;
if (CallType == ZST_FILE_CALL) {
FindIter = ZST_FileLocWhitelist.find(LocationName);
ReturnVal = (FindIter != ZST_FileLocWhitelist.end());
}
else if (CallType == ZST_NETWORK_CALL) {
FindIter = ZST_NetworkLocWhitelist.find(LocationName);
ReturnVal = (FindIter != ZST_NetworkLocWhitelist.end());
}
else { // should not be here
ReturnVal = false;
}
return ReturnVal;
} // end of IsLocationWhitelisted()
// Given a call type and called function name, is it on the location blacklist
// for that call type?
// NOTE: HANDLE CASE IN WHICH BLACKLISTED LOCATION IS A PREFIX, TERMINATING in a slash.
bool IsLocationBlacklisted(ZST_SysCallType CallType, string LocationName) {
set<string>::iterator FindIter;
bool ReturnVal;
if (CallType == ZST_FILE_CALL) {
FindIter = ZST_FileLocBlacklist.find(LocationName);
ReturnVal = (FindIter != ZST_FileLocBlacklist.end());
}
else if (CallType == ZST_NETWORK_CALL) {
FindIter = ZST_NetworkLocBlacklist.find(LocationName);
ReturnVal = (FindIter != ZST_NetworkLocBlacklist.end());
}
else { // should not be here
ReturnVal = false;
}
return ReturnVal;
}
// These two constants should agree with their counterparts in ZST-policy.c.
#define ZST_MAX_FILE_NAME_LEN 1024
#define ZST_MAX_CALL_NAME_LEN 64
// Read the foo.exe.policy file to initialize our security policies for system calls.
void ZST_InitPolicies(const char *PolicyFileName) {
clc5q
committed
FILE *PolicyFile = SMP_fopen(PolicyFileName, "r");
char Str1[ZST_MAX_CALL_NAME_LEN], Str2[ZST_MAX_CALL_NAME_LEN], Str3[ZST_MAX_FILE_NAME_LEN];
if (NULL != PolicyFile) {
clc5q
committed
while (!SMP_feof(PolicyFile)) {
int ItemsRead = qfscanf(PolicyFile, "%63s %63s %1023s", Str1, Str2, Str3);
if (3 != ItemsRead) {
clc5q
committed
SMP_msg("ERROR: Line in %s had %d items instead of the required 3; line ignored.\n", PolicyFileName, ItemsRead);
}
else {
string FirstStr(Str1), SecondStr(Str2), ThirdStr(Str3);
pair<set<string>::iterator, bool> SetInsertResult;
if (0 == strcmp(Str1, "SECURITYPOLICY")) {
ZST_SysCallType TempCallType = ConvertStringToCallType(Str2);
ZST_Policy TempPolicy = ConvertStringToPolicy(Str3);
pair<map<ZST_SysCallType, ZST_Policy>::iterator, bool> InsertResult;
pair<ZST_SysCallType, ZST_Policy> TempPair(TempCallType, TempPolicy);
InsertResult = ZST_TypePolicyMap.insert(TempPair);
if (!(InsertResult.second)) {
clc5q
committed
SMP_msg("ERROR: Could not insert security policy %s for %s. Possible duplicate or conflicting policies.\n",
Str3, Str2);
}
}
else if (0 == strcmp(Str1, "FILELOCATION")) {
if (0 == strcmp(Str2, "WHITELIST")) {
SetInsertResult = ZST_FileLocWhitelist.insert(ThirdStr);
if (!(SetInsertResult.second)) {
clc5q
committed
SMP_msg("WARNING: Duplicate file whitelist location %s ignored.\n", Str3);
}
}
else if (0 == strcmp(Str2, "BLACKLIST")) {
SetInsertResult = ZST_FileLocBlacklist.insert(ThirdStr);
if (!(SetInsertResult.second)) {
clc5q
committed
SMP_msg("WARNING: Duplicate file blacklist location %s ignored.\n", Str3);
}
}
else {
clc5q
committed
SMP_msg("ERROR: Unknown second field value in policy line: %s %s %s ; ignored\n", Str1, Str2, Str3);
}
}
else if (0 == strcmp(Str1, "NETWORKLOCATION")) {
if (0 == strcmp(Str2, "WHITELIST")) {
SetInsertResult = ZST_NetworkLocWhitelist.insert(ThirdStr);
if (!(SetInsertResult.second)) {
clc5q
committed
SMP_msg("WARNING: Duplicate network whitelist location %s ignored.\n", Str3);
}
}
else if (0 == strcmp(Str2, "BLACKLIST")) {
SetInsertResult = ZST_NetworkLocBlacklist.insert(ThirdStr);
if (!(SetInsertResult.second)) {
clc5q
committed
SMP_msg("WARNING: Duplicate network blacklist location %s ignored.\n", Str3);
}
}
else {
clc5q
committed
SMP_msg("ERROR: Unknown second field value in policy line: %s %s %s ; ignored\n", Str1, Str2, Str3);
}
}
else {
clc5q
committed
SMP_msg("ERROR: Unknown first field value in policy line: %s %s %s ; ignored\n", Str1, Str2, Str3);
}
}
}
clc5q
committed
if (0 == SMP_fclose(PolicyFile)) {
SMP_msg("Policy file %s successfully closed; all policies recorded.\n", PolicyFileName);
}
else {
clc5q
committed
SMP_msg("ERROR: fclose failed on policy file %s. However, policies should be in effect.\n", PolicyFileName);
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
}
// Now, initialize the system call name maps.
pair<map<string, ZST_SysCallType>::iterator, bool> FuncInsertResult;
// Do all the high privilege calls first.
string SysFuncName("putenv");
pair<string, ZST_SysCallType> FuncNamePolicyPair(SysFuncName, ZST_HIGHPRIVILEGE_CALL);
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setenv");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setegid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("seteuid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setgid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setpgid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setregid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setreuid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("setuid");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("execl");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("execv");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("execle");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("execve");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("execlp");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("execvp");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("system");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
// Now do all the file operation calls.
FuncNamePolicyPair.second = ZST_FILE_CALL;
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("chdir");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("chmod");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("chown");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("creat");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("creat64");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("fopen");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("freopen");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("open");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("open64");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("mknod");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);
assert(FuncInsertResult.second);
FuncNamePolicyPair.first.clear();
FuncNamePolicyPair.first.append("remove");
FuncInsertResult = ZST_FuncTypeMap.insert(FuncNamePolicyPair);