static void listInstructions(const InstructionProvider::Ptr &insns, const MemoryMap::Ptr &map, const FunctionByAddress &code1, FunctionByAddress &code2) { std::ostream &out = std::cout; static const size_t insnWidth = 110; rose_addr_t va1 = code1.hull().least(); rose_addr_t va2 = code2.hull().least(); rose_addr_t va = std::min(va1, va2); rose_addr_t expectedVa = va; AsmUnparser unparser; while (va<=code1.hull().greatest() || va<=code2.hull().greatest()) { // Address and contents if (va != expectedVa) out <<"\n"; // visual cue that addresses are not sequential here std::ostringstream ss; size_t size; if (!map->at(va).require(MemoryMap::EXECUTABLE).exists()) { ss <<StringUtility::addrToString(va) <<": " <<(map->at(va).exists() ? "not executable" : "not mapped"); size = 1; } else if (SgAsmInstruction *insn = (*insns)[va]) { unparser.unparse(ss, insn); size = insn->get_size(); } else { ss <<StringUtility::addrToString(va) <<": bad instruction"; size = 1; } std::vector<std::string> lines = StringUtility::split('\n', ss.str()); while (lines.size()>0 && lines[lines.size()-1]=="") lines.pop_back(); for (size_t i=0; i<lines.size(); ++i) { if (i+1 < lines.size()) { out <<lines[i] <<"\n"; } else { out <<std::setw(insnWidth) <<std::left <<lines[i]; } } // Functions owning Sawyer::Optional<rose_addr_t> f1 = code1.getOptional(va); Sawyer::Optional<rose_addr_t> f2 = code2.getOptional(va); out <<"\t" <<std::setw(10) <<std::left <<(f1 ? StringUtility::addrToString(*f1) : std::string("none")); out <<"\t" <<std::setw(10) <<std::left <<(f2 ? StringUtility::addrToString(*f2) : std::string("none")); out <<" " <<(f1.isEqual(f2) ? "" : "<---") <<"\n"; // Advance address pointer rose_addr_t next = va + size; expectedVa = next; FunctionByAddress::ConstIntervalIterator i1 = code1.upperBound(va); if (i1!=code1.nodes().end() && i1->least() < next) next = i1->least(); FunctionByAddress::ConstIntervalIterator i2 = code2.upperBound(va); if (i2!=code2.nodes().end() && i2->least() < next) next = i2->least(); if (!map->atOrAfter(next).next().assignTo(va)) break; } }
virtual bool operator()(bool enabled, const Args &args) { if (enabled && args.insn->get_address()==trigger_va) { args.thread->get_process()->get_simulator()->deactivate(); Sawyer::Message::Stream m = args.thread->tracing(TRACE_MISC); m <<"disassembly triggered; disassembling now...\n"; SgAsmBlock *gblk = args.thread->get_process()->disassemble(false); // full disassembly with partitioning AsmUnparser unparser; unparser.set_organization(org); unparser.unparse(std::cout, gblk); throw this; // to terminate specimen } return enabled; }
int main(int argc, char *argv[]) { /* Process the command-line; remove the block address and pass the remainder to ROSE. */ ROSE_ASSERT(argc>=2); char *rest; rose_addr_t removal_addr = strtoull(argv[1], &rest, 0); if (!*rest) { for (int i=2; i<=argc; i++) /*include null ptr at end of list*/ argv[i-1]=argv[i]; --argc; } else { std::cerr <<"warning: no basic block will be removed" <<std::endl; } SgProject *project = frontend(argc, argv); ROSE_ASSERT(project!=NULL); /* Remove the specified basic block */ struct T1: public SgSimpleProcessing { rose_addr_t removal_addr; T1(rose_addr_t a): removal_addr(a) {} void visit(SgNode *node) { SgAsmBlock *bb = isSgAsmBlock(node); SgAsmFunction *func = bb ? SageInterface::getEnclosingNode<SgAsmFunction>(bb) : NULL; if (func && bb->get_address()==removal_addr) { SgAsmStatementPtrList::iterator found = std::find(func->get_statementList().begin(), func->get_statementList().end(), bb); ROSE_ASSERT(found!=func->get_statementList().end()); func->get_statementList().erase(found); std::cout <<"removed basic block " <<StringUtility::addrToString(removal_addr) <<std::endl; // throw 1 /* found the one-and-only block, so we can abandon the traversal */ } } }; T1(removal_addr).traverse(project, preorder); /* Unparse the file using assembled data from above */ std::vector<SgAsmInterpretation*> interps = SageInterface::querySubTree<SgAsmInterpretation>(project, V_SgAsmInterpretation); ROSE_ASSERT(!interps.empty()); for (size_t i=0; i<interps.size(); ++i) { std::cout <<"\n\n\n==================== Interpretation Listing ====================\n\n"; interps[i]->get_map()->dump(stdout); AsmUnparser unparser; unparser.set_organization(AsmUnparser::ORGANIZED_BY_ADDRESS); unparser.unparse(std::cout, interps[i]); assemble_all(interps[i]); } backend(project); }
int main(int argc, char *argv[]) { // This paragraph initializes the ROSE library, generates the man page for this tool, does command-line parsing for quite a // few switches including "--help", loads various specimen resources (ELF/PE, running process, raw memory dumps, etc), // disassembles, and partitions. We could have called Engine::frontend() and done it all in one function call, but then we // wouldn't have a Partitioner2::Partitioner object that we need below. std::string purpose = "demonstrate inter-function disassembly"; std::string description = "Disassembles and partitions the specimen(s), then tries to disassemble things between the functions."; P2::Engine engine; std::vector<std::string> specimens = engine.parseCommandLine(argc, argv, purpose, description).unreachedArgs(); P2::Partitioner partitioner = engine.partition(specimens); // The partitioner's address usage map (AUM) describes what part of memory has been disassembled as instructions or // data. We're interested in the unused parts between the lowest and highest disassembled addresses, so we loop over those // parts. The hull() is the entire used interval -- lowest to highest addresses used regardless of the unused areas in the // middle. An AddressInterval evaluated in boolean context returns false if it's empty. rose_addr_t va = partitioner.aum().hull().least(); while (AddressInterval unused = partitioner.aum().nextUnused(va)) { // Is the unused area beyond the last thing compiled? We're only interested in the stuff between functions. This // check also means that unused.greatest()+1 will not overflow, which simplifies later code. Overflows are easy to // trigger when the specimen's word size is the same as ROSE's word size. if (unused.least() > partitioner.aum().hull().greatest()) break; // The unused address might be in the middle of some very large unmapped area of memory, or perhaps in an area that // doesn't have execute permission (the partitioner will only disassemble at addresses that we've marked as // executable). A naive implementation would just increment to the next address and try again, but that could take a // very long time. This "if" statement will give us the next executable address that falls within the unused interval // if possible. The address is assigned to "va" if possible. if (!engine.memoryMap().within(unused).require(MemoryMap::EXECUTABLE).next().assignTo(va)) { va = unused.greatest() + 1; // won't overflow because of check above continue; } // "va" now points to an executable address that the partitioner doesn't know about yet. ASSERT_require(engine.memoryMap().at(va).require(MemoryMap::EXECUTABLE).exists()); ASSERT_forbid(partitioner.aum().instructionExists(va)); std::cout <<"unused address " <<StringUtility::addrToString(va) <<"\n"; // Cause the partitioner to discover (disassemble) one basic block. This doesn't add the basic block to the // partitioner or change the partitioner in any way. If the BB isn't something we want to keep then just forget about // it and garbage collection will reclaim the memory. P2::BasicBlock::Ptr bb = partitioner.discoverBasicBlock(va); if (!isGoodBasicBlock(bb)) { ++va; continue; } std::cout <<" disassembled " <<bb->printableName() <<"\n"; // Inform the partitioner that we wish to keep this BB. partitioner.attachBasicBlock(bb); // This BB was not reachable by any previous CFG edge, therefore it doesn't belong to any function. In order for it to // show up in the eventual AST we need to add it to some function (the ROSE AST has a requirement that every basic // block belongs to a function, although the partitioner can easily cope with the other case). The easiest way in this // situation is to just create a new function whose entry block is this BB. Creating a function doesn't modify the // partitioner in any way, so we need to also attach the function to the partitioner. P2::Function::Ptr function = P2::Function::instance(va, SgAsmFunction::FUNC_USERDEF); function->insertBasicBlock(va); // allowed only before attaching function to partitioner partitioner.attachOrMergeFunction(function); // This basic block might be the first block of a whole bunch that are connected by as yet undiscovered CFG edges. We // can recursively discover and attach all those blocks with one Engine method. There are also Partitioner methods to // do similar things, but they're lower level. engine.runPartitionerRecursive(partitioner); } // We've probably added a bunch more functions and basic blocks to the partitioner, but we haven't yet assigned the basic // blocks discovered by Engine::runPartitionerRecursive to any functions. We might also need to assign function labels // from ELF/PE information, re-run some analysis, etc., so do that now. engine.runPartitionerFinal(partitioner); // Most ROSE analysis is performed on an abstract syntax tree, so generate one. If the specime is an ELF or PE container // then the returned global block will also be attached somewhere below a SgProject node, otherwise the returned global // block is the root of the AST and there is no project (e.g., like when the specimen is a raw memory dump). SgAsmBlock *gblock = P2::Modules::buildAst(partitioner, engine.interpretation()); // Generate an assembly listing. These unparser properties are all optional, but they result in more informative assembly // listings. AsmUnparser unparser; unparser.set_registers(partitioner.instructionProvider().registerDictionary()); unparser.add_control_flow_graph(ControlFlow().build_block_cfg_from_ast<ControlFlow::BlockGraph>(gblock)); unparser.staticDataDisassembler.init(engine.disassembler()); unparser.unparse(std::cout, gblock); }