/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "inject_sleigh.hh" #include "pcodeparse.hh" #include "architecture.hh" InjectContextSleigh::~InjectContextSleigh(void) { if (pos != (ParserContext *)0) delete pos; } InjectPayloadSleigh::~InjectPayloadSleigh(void) { if (tpl != (ConstructTpl *)0) delete tpl; } InjectPayloadSleigh::InjectPayloadSleigh(const string &src,const string &nm,int4 tp) : InjectPayload(nm,tp) { source = src; tpl = (ConstructTpl *)0; paramshift = 0; } void InjectPayloadSleigh::inject(InjectContext &context,PcodeEmit &emit) const { InjectContextSleigh &con((InjectContextSleigh &)context); con.cacher.clear(); con.pos->setAddr(con.baseaddr); con.pos->setNaddr(con.nextaddr); con.pos->setCalladdr(con.calladdr); ParserWalkerChange walker(con.pos); con.pos->deallocateState(walker); setupParameters(con,walker,inputlist,output,source); // delayslot and crossbuild directives are not allowed in snippets, so we don't need the DisassemblyCache // and we don't need a unique allocation mask SleighBuilder builder(&walker,(DisassemblyCache *)0,&con.cacher,con.glb->getConstantSpace(),con.glb->getUniqueSpace(),0); builder.build(tpl,-1); con.cacher.resolveRelatives(); con.cacher.emit(con.baseaddr,&emit); } void InjectPayloadSleigh::restoreXml(const Element *el) { InjectPayload::restoreXml(el); const List &list(el->getChildren()); List::const_iterator iter; for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter; if (subel->getName() == "body") { parsestring = subel->getContent(); } } if (parsestring.size() == 0 && (!dynamic)) throw LowlevelError("Missing subtag in : "+getSource()); } void InjectPayloadSleigh::printTemplate(ostream &s) const { tpl->saveXml(s,-1); } void InjectPayloadSleigh::checkParameterRestrictions(InjectContextSleigh &con, const vector &inputlist, const vector &output, const string &source) { // Verify that the storage locations passed in -con- match the restrictions set for this payload if (inputlist.size() != con.inputlist.size()) throw LowlevelError("Injection parameter list has different number of parameters than p-code operation: "+source); for(int4 i=0;i &inputlist, const vector &output, const string &source) { // Set-up operands in the parser state so that they pick up storage locations in InjectContext checkParameterRestrictions(con,inputlist,output,source); ParserContext *pos = walker.getParserContext(); for(int4 i=0;iallocateOperand(inputlist[i].getIndex(),walker); VarnodeData &data( con.inputlist[i] ); FixedHandle &hand(walker.getParentHandle()); hand.space = data.space; hand.offset_offset = data.offset; hand.size = data.size; hand.offset_space = (AddrSpace *)0; walker.popOperand(); } for(int4 i=0;iallocateOperand(output[i].getIndex(),walker); VarnodeData &data( con.output[i] ); FixedHandle &hand(walker.getParentHandle()); hand.space = data.space; hand.offset_offset = data.offset; hand.size = data.size; hand.offset_space = (AddrSpace *)0; walker.popOperand(); } } InjectPayloadCallfixup::InjectPayloadCallfixup(const string &sourceName) : InjectPayloadSleigh(sourceName,"unknown",CALLFIXUP_TYPE) { } void InjectPayloadCallfixup::restoreXml(const Element *el) { const List &list(el->getChildren()); List::const_iterator iter; name = el->getAttributeValue("name"); bool pcodeSubtag = false; for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter; if (subel->getName() == "pcode") { InjectPayloadSleigh::restoreXml(subel); pcodeSubtag = true; } else if (subel->getName() == "target") targetSymbolNames.push_back(subel->getAttributeValue("name")); } if (!pcodeSubtag) throw LowlevelError(" is missing subtag: "+name); } InjectPayloadCallother::InjectPayloadCallother(const string &sourceName) : InjectPayloadSleigh(sourceName,"unknown",CALLOTHERFIXUP_TYPE) { } void InjectPayloadCallother::restoreXml(const Element *el) { const List &list(el->getChildren()); List::const_iterator iter; name = el->getAttributeValue("targetop"); iter = list.begin(); if ((iter == list.end()) || ((*iter)->getName() != "pcode")) throw LowlevelError(" does not contain a tag"); InjectPayloadSleigh::restoreXml(*iter); } ExecutablePcodeSleigh::ExecutablePcodeSleigh(Architecture *g,const string &src,const string &nm) : ExecutablePcode(g,src,nm) { tpl = (ConstructTpl *)0; } ExecutablePcodeSleigh::~ExecutablePcodeSleigh(void) { if (tpl != (ConstructTpl *)0) delete tpl; } void ExecutablePcodeSleigh::inject(InjectContext &context,PcodeEmit &emit) const { InjectContextSleigh &con((InjectContextSleigh &)context); con.cacher.clear(); con.pos->setAddr(con.baseaddr); con.pos->setNaddr(con.nextaddr); con.pos->setCalladdr(con.calladdr); ParserWalkerChange walker(con.pos); con.pos->deallocateState(walker); InjectPayloadSleigh::setupParameters(con,walker,inputlist,output,getSource()); // delayslot and crossbuild directives are not allowed in snippets, so we don't need the DisassemblyCache // and we don't need a unique allocation mask SleighBuilder builder(&walker,(DisassemblyCache *)0,&con.cacher,con.glb->getConstantSpace(),con.glb->getUniqueSpace(),0); builder.build(tpl,-1); con.cacher.resolveRelatives(); con.cacher.emit(con.baseaddr,&emit); } void ExecutablePcodeSleigh::restoreXml(const Element *el) { InjectPayload::restoreXml(el); const List &list(el->getChildren()); List::const_iterator iter; bool hasbody = false; for (iter = list.begin(); iter != list.end(); ++iter) { const Element *subel = *iter; if (subel->getName() == "body") { hasbody = true; parsestring = subel->getContent(); } } if (!hasbody) throw LowlevelError("Missing subtag in : " + getSource()); } void ExecutablePcodeSleigh::printTemplate(ostream &s) const { tpl->saveXml(s,-1); } InjectPayloadDynamic::~InjectPayloadDynamic(void) { map::iterator iter; for(iter=addrMap.begin();iter!=addrMap.end();++iter) delete (*iter).second; } void InjectPayloadDynamic::restoreEntry(const Element *el) { const List &list(el->getChildren()); List::const_iterator iter; iter = list.begin(); Address addr = Address::restoreXml(*iter,glb); ++iter; istringstream s((*iter)->getContent()); try { Document *doc = xml_tree(s); map::iterator iter = addrMap.find(addr); if (iter != addrMap.end()) delete (*iter).second; // Delete any preexisting document addrMap[addr] = doc; } catch(XmlError &err) { throw LowlevelError("Error in dynamic payload XML"); } } void InjectPayloadDynamic::inject(InjectContext &context,PcodeEmit &emit) const { map::const_iterator eiter = addrMap.find(context.baseaddr); if (eiter == addrMap.end()) throw LowlevelError("Missing dynamic inject"); const Element *el = (*eiter).second->getRoot(); const List &list(el->getChildren()); List::const_iterator iter; for(iter=list.begin();iter!=list.end();++iter) emit.restoreXmlOp(*iter,glb->translate); } PcodeInjectLibrarySleigh::PcodeInjectLibrarySleigh(Architecture *g) : PcodeInjectLibrary(g,g->translate->getUniqueStart(Translate::INJECT)) { slgh = (const SleighBase *)g->translate; contextCache.glb = g; } int4 PcodeInjectLibrarySleigh::registerDynamicInject(InjectPayload *payload) { int4 id = injection.size(); injection.push_back(payload); return id; } /// \brief Force a payload to be dynamic for debug purposes /// /// Debug information may include inject information for payloads that aren't dynamic. /// We substitute a dynamic payload so that analysis uses the debug info to inject, rather /// than the hard-coded payload information. /// \param injectid is the id of the payload to treat dynamic /// \return the new dynamic payload object InjectPayloadDynamic *PcodeInjectLibrarySleigh::forceDebugDynamic(int4 injectid) { InjectPayload *oldPayload = injection[injectid]; InjectPayloadDynamic *newPayload = new InjectPayloadDynamic(glb,oldPayload->getName(),oldPayload->getType()); delete oldPayload; injection[injectid] = newPayload; return newPayload; } void PcodeInjectLibrarySleigh::parseInject(InjectPayload *payload) { if (payload->isDynamic()) return; if (slgh == (const SleighBase *)0) { // Make sure we have the sleigh AddrSpaceManager slgh = (const SleighBase *)glb->translate; if (slgh == (const SleighBase *)0) throw LowlevelError("Registering pcode snippet before language is instantiated"); } if (contextCache.pos == (ParserContext *)0) { // Make sure we have a context contextCache.pos = new ParserContext((ContextCache *)0); contextCache.pos->initialize(8,8,slgh->getConstantSpace()); } PcodeSnippet compiler(slgh); // compiler.clear(); // Not necessary unless we reuse for(int4 i=0;isizeInput();++i) { InjectParameter ¶m( payload->getInput(i) ); compiler.addOperand(param.getName(),param.getIndex()); } for(int4 i=0;isizeOutput();++i) { InjectParameter ¶m( payload->getOutput(i) ); compiler.addOperand(param.getName(),param.getIndex()); } if (payload->getType() == InjectPayload::EXECUTABLEPCODE_TYPE) { compiler.setUniqueBase(0x2000); // Don't need to deconflict with anything other injects ExecutablePcodeSleigh *sleighpayload = (ExecutablePcodeSleigh *)payload; istringstream s(sleighpayload->parsestring); if (!compiler.parseStream(s)) throw LowlevelError(payload->getSource() + ": Unable to compile pcode: "+compiler.getErrorMessage()); sleighpayload->tpl = compiler.releaseResult(); sleighpayload->parsestring = ""; // No longer need the memory } else { compiler.setUniqueBase(tempbase); InjectPayloadSleigh *sleighpayload = (InjectPayloadSleigh *)payload; istringstream s(sleighpayload->parsestring); if (!compiler.parseStream(s)) throw LowlevelError(payload->getSource() + ": Unable to compile pcode: "+compiler.getErrorMessage()); tempbase = compiler.getUniqueBase(); sleighpayload->tpl = compiler.releaseResult(); sleighpayload->parsestring = ""; // No longer need the memory } } int4 PcodeInjectLibrarySleigh::allocateInject(const string &sourceName,const string &name,int4 type) { int4 injectid = injection.size(); if (type == InjectPayload::CALLFIXUP_TYPE) injection.push_back(new InjectPayloadCallfixup(sourceName)); else if (type == InjectPayload::CALLOTHERFIXUP_TYPE) injection.push_back(new InjectPayloadCallother(sourceName)); else if (type == InjectPayload::EXECUTABLEPCODE_TYPE) injection.push_back(new ExecutablePcodeSleigh(glb,sourceName,name)); else injection.push_back(new InjectPayloadSleigh(sourceName,name,type)); return injectid; } void PcodeInjectLibrarySleigh::registerInject(int4 injectid) { InjectPayload *payload = injection[injectid]; if (payload->isDynamic()) { InjectPayload *sub = new InjectPayloadDynamic(glb,payload->getName(),payload->getType()); delete payload; payload = sub; injection[injectid] = payload; } switch(payload->getType()) { case InjectPayload::CALLFIXUP_TYPE: registerCallFixup(payload->getName(), injectid); parseInject(payload); break; case InjectPayload::CALLOTHERFIXUP_TYPE: registerCallOtherFixup(payload->getName(), injectid); parseInject(payload); break; case InjectPayload::CALLMECHANISM_TYPE: registerCallMechanism(payload->getName(), injectid); parseInject(payload); break; case InjectPayload::EXECUTABLEPCODE_TYPE: registerExeScript(payload->getName(), injectid); parseInject(payload); break; default: throw LowlevelError("Unknown p-code inject type"); } } void PcodeInjectLibrarySleigh::restoreDebug(const Element *el) { const List &list(el->getChildren()); List::const_iterator iter; for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter; const string &name( subel->getAttributeValue("name") ); istringstream s( subel->getAttributeValue("type") ); int4 type = -1; s.unsetf(ios::dec | ios::hex | ios::oct); s >> type; int4 id = getPayloadId(type,name); InjectPayloadDynamic *payload = dynamic_cast(getPayload(id)); if (payload == (InjectPayloadDynamic *)0) { payload = forceDebugDynamic(id); } payload->restoreEntry(subel); } } const vector &PcodeInjectLibrarySleigh::getBehaviors(void) { if (inst.empty()) glb->collectBehaviors(inst); return inst; } int4 PcodeInjectLibrarySleigh::manualCallFixup(const string &name,const string &snippetstring) { string sourceName = "(manual callfixup name=\"" + name + "\")"; int4 injectid = allocateInject(sourceName, name, InjectPayload::CALLFIXUP_TYPE); InjectPayloadSleigh *payload = (InjectPayloadSleigh *)getPayload(injectid); payload->parsestring = snippetstring; registerInject(injectid); return injectid; } int4 PcodeInjectLibrarySleigh::manualCallOtherFixup(const string &name,const string &outname, const vector &inname,const string &snippet) { string sourceName = "inputlist.push_back(InjectParameter(inname[i],0)); if (outname.size() != 0) payload->output.push_back(InjectParameter(outname,0)); payload->orderParameters(); payload->parsestring = snippet; registerInject(injectid); return injectid; }