Use InjectPayload for segment ops

This commit is contained in:
caheckman 2019-08-29 14:17:02 -04:00
parent 06de0d46a0
commit 4c3289f09f
7 changed files with 93 additions and 132 deletions

View File

@ -124,31 +124,6 @@ SegmentOp::SegmentOp(Architecture *g,const string &nm,int4 ind)
constresolve.space = (AddrSpace *)0;
}
/// \brief Execute a stream of operations (OpFollow) given a starting input value
///
/// Each operation is performed in turn, with output from the previous becoming
/// input of the next. The final output is returned.
/// \param follow is the ordered set of operations to perform
/// \param input is the constant input for the first operation
/// \return the final constant output
uintb SegmentOp::executeSide(const vector<OpFollow> &follow,uintb input)
{
for(int4 i=0;i<follow.size();++i) {
switch(follow[i].opc) {
case CPUI_INT_AND:
input &= follow[i].val;
break;
case CPUI_INT_LEFT:
input <<= follow[i].val;
break;
default:
break;
}
}
return input;
}
bool SegmentOp::unify(Funcdata &data,PcodeOp *op,
vector<Varnode *> &bindlist) const
{
@ -163,7 +138,7 @@ bool SegmentOp::unify(Funcdata &data,PcodeOp *op,
if (op->getIn(0)->getOffset() != useropindex) return false;
if (op->numInput() != 3) return false;
innervn = op->getIn(1);
if (basepresent) {
if (baseinsize != 0) {
basevn = op->getIn(1);
innervn = op->getIn(2);
if (basevn->isConstant())
@ -181,40 +156,23 @@ bool SegmentOp::unify(Funcdata &data,PcodeOp *op,
uintb SegmentOp::execute(const vector<uintb> &input) const
{
uintb base,inner;
if (basepresent)
base = executeSide(basefollow,input[1]);
else
base = 0;
inner = executeSide(innerfollow,input[0]);
return (base+inner);
ExecutablePcode *pcodeScript = (ExecutablePcode *)glb->pcodeinjectlib->getPayload(injectId);
return pcodeScript->evaluate(input);
}
void SegmentOp::restoreXml(const Element *el)
{
spc = glb->getSpaceByName(el->getAttributeValue("space"));
injectId = -1;
baseinsize = 0;
innerinsize = 0;
bool userdefined = false;
forcesegment = true;
supportsfarpointer = false;
name = "segment"; // Default name, might be overridden by userop attribute
for(int4 i=0;i<el->getNumAttributes();++i) {
const string &nm(el->getAttributeName(i));
if (nm == "space") continue;
else if (nm == "baseinsize") {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> baseinsize;
}
else if (nm == "innerinsize") {
istringstream s1(el->getAttributeValue(i));
s1.unsetf(ios::dec | ios::hex | ios::oct);
s1 >> innerinsize;
}
else if (nm == "farpointer")
supportsfarpointer = true;
else if (nm == "userop") { // Based on existing sleigh op
@ -227,28 +185,17 @@ void SegmentOp::restoreXml(const Element *el)
throw LowlevelError("Redefining userop "+name);
}
}
else if (nm == "force")
forcesegment = xml_readbool(el->getAttributeValue(i));
else
throw LowlevelError("Bad segmentop tag attribute: "+nm);
}
if (!userdefined)
throw LowlevelError("Missing userop attribute in segmentop tag");
basepresent = (baseinsize != 0);
const List &list(el->getChildren());
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) {
const Element *subel = *iter;
if (subel->getName()=="baseop") {
basefollow.push_back(OpFollow());
basefollow.back().restoreXml(subel);
}
else if (subel->getName()=="innerop") {
innerfollow.push_back(OpFollow());
innerfollow.back().restoreXml(subel);
}
else if (subel->getName()=="constresolve") {
if (subel->getName()=="constresolve") {
int4 sz;
const List &sublist(subel->getChildren());
if (!sublist.empty()) {
@ -260,9 +207,28 @@ void SegmentOp::restoreXml(const Element *el)
constresolve.size = sz;
}
}
else if (subel->getName() == "pcode") {
string nm = name + "_pcode";
string source = "cspec";
injectId = glb->pcodeinjectlib->restoreXmlInject(source, nm, InjectPayload::EXECUTABLEPCODE_TYPE, subel);
}
else
throw LowlevelError("Bad segment pattern tag: "+subel->getName());
}
if (injectId < 0)
throw LowlevelError("Missing <execute> child in <segmentop> tag");
InjectPayload *payload = glb->pcodeinjectlib->getPayload(injectId);
if (payload->sizeOutput() != 1)
throw LowlevelError("<execute> child of <segmentop> tag must declare one <output>");
if (payload->sizeInput() == 1) {
innerinsize = payload->getInput(0).getSize();
}
else if (payload->sizeInput() == 2) {
baseinsize = payload->getInput(0).getSize();
innerinsize = payload->getInput(1).getSize();
}
else
throw LowlevelError("<execute> child of <segmentop> tag must declare one or two <input> tags");
}
/// \param g is the Architecture owning this set of jump assist scripts

View File

@ -198,24 +198,19 @@ struct OpFollow {
/// The core routine that looks for the term-tree is unify().
class SegmentOp : public TermPatternOp {
AddrSpace *spc; ///< The physical address space into which a segmented pointer points
int4 injectId; ///< Id of InjectPayload that emulates \b this operation
int4 baseinsize; ///< The size in bytes of the \e base or \e segment value
int4 innerinsize; ///< The size in bytes of the \e near pointer value
bool basepresent; ///< Is \b true is a base value must be present in the raw p-code
bool forcesegment; ///< Is \b true if an exception is thrown when a segment op can't be unified
bool supportsfarpointer; ///< Is \b true if the joined pair base:near acts as a \b far pointer
vector<OpFollow> basefollow; ///< Sequence of operations performed on the \b base value
vector<OpFollow> innerfollow; ///< Sequence of operations performed on the \b near value
VarnodeData constresolve; ///< How to resolve constant near pointers
static uintb executeSide(const vector<OpFollow> &follow,uintb input);
public:
SegmentOp(Architecture *g,const string &nm,int4 ind); ///< Constructor
AddrSpace *getSpace(void) const { return spc; } ///< Get the address space being pointed to
bool hasFarPointerSupport(void) const { return supportsfarpointer; } ///< Return \b true, if \b this op supports far pointers
bool isForced(void) const { return forcesegment; } ///< Return \b true if exceptions are thrown for bad unification
int4 getBaseSize(void) const { return baseinsize; } ///< Get size in bytes of the base/segment value
int4 getInnerSize(void) const { return innerinsize; } ///< Get size in bytes of the near value
const VarnodeData &getResolve(void) const { return constresolve; } ///< Get the default register for resolving indirect segments
virtual int4 getNumVariableTerms(void) const { if (basepresent) return 2; return 1; }
virtual int4 getNumVariableTerms(void) const { if (baseinsize!=0) return 2; return 1; }
virtual bool unify(Funcdata &data,PcodeOp *op,vector<Varnode *> &bindlist) const;
virtual uintb execute(const vector<uintb> &input) const;
virtual void restoreXml(const Element *el);

View File

@ -227,51 +227,19 @@
</optional>
</define>
<define name="segment_op_type">
<attribute name="code">
<choice>
<value type="string">INT_ZEXT</value>
<value type="string">INT_LEFT</value>
<value type="string">INT_AND</value>
</choice>
</attribute>
<optional>
<attribute name="value"/>
</optional>
<optional>
<attribute name="slot"/>
</optional>
</define>
<define name="segmentop_type">
<element name="segmentop">
<attribute name="space"/>
<optional> <attribute name="userop"/> </optional>
<optional> <attribute name="baseinsize"/> </optional>
<optional> <attribute name="innerinsize"/> </optional>
<optional> <attribute name="farpointer"/> </optional>
<element name="pcode">
<ref name="pcode_type"/>
</element>
<optional>
<attribute name="force">
<ref name="boolean_type"/>
</attribute>
<element name="constresolve">
<ref name="varnode_tags_type"/>
</element>
</optional>
<interleave>
<zeroOrMore>
<element name="baseop">
<ref name="segment_op_type"/>
</element>
</zeroOrMore>
<zeroOrMore>
<element name="innerop">
<ref name="segment_op_type"/>
</element>
</zeroOrMore>
<optional>
<element name="constresolve">
<ref name="varnode_tags_type"/>
</element>
</optional>
</interleave>
</element>
</define>

View File

@ -599,6 +599,29 @@ public class SleighLanguage implements Language {
}
}
private void parseSegmentOp(XmlElement el, XmlPullParser parser) {
String name = el.getAttribute("userop");
if (name == null) {
name = "segment";
}
name = name + "_pcode";
String source = "pspec: " + getLanguageID().getIdAsString();
if (parser.peek().isStart()) {
if (parser.peek().getName().equals("pcode")) {
InjectPayloadSleigh payload =
new InjectPayloadSleigh(name, InjectPayload.EXECUTABLEPCODE_TYPE, source);
if (additionalInject == null) {
additionalInject = new ArrayList<>();
}
payload.restoreXml(parser);
additionalInject.add(payload);
}
}
while (parser.peek().isStart()) {
parser.discardSubTree();
}
}
private void read(XmlPullParser parser) {
Set<String> registerDataSet = new HashSet<>();
@ -775,9 +798,7 @@ public class SleighLanguage implements Language {
}
}
else if (element.getName().equals("segmentop")) {
while (parser.peek().isStart()) {
parser.discardSubTree();
}
parseSegmentOp(element, parser);
}
// get rid of the end tag of whatever we started with at the top of the while
parser.end(element);

View File

@ -163,29 +163,30 @@ public class BasicCompilerSpec implements CompilerSpec {
language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
if (classname == null) {
pcodeInject = new PcodeInjectLibrary(language); // This is the default implementation
return;
}
try {
Class<?> c = Class.forName(classname);
if (!PcodeInjectLibrary.class.isAssignableFrom(c)) {
else {
try {
Class<?> c = Class.forName(classname);
if (!PcodeInjectLibrary.class.isAssignableFrom(c)) {
Msg.error(this,
"Language " + language.getLanguageID() + " does not specify a valid " +
GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
throw new RuntimeException(classname + " does not implement interface " +
PcodeInjectLibrary.class.getName());
}
Class<? extends PcodeInjectLibrary> injectLibraryClass =
(Class<? extends PcodeInjectLibrary>) c;
Constructor<? extends PcodeInjectLibrary> constructor =
injectLibraryClass.getConstructor(SleighLanguage.class);
pcodeInject = constructor.newInstance(language);
}
catch (Exception e) {
Msg.error(this,
"Language " + language.getLanguageID() + " does not specify a valid " +
GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
throw new RuntimeException(classname + " does not implement interface " +
PcodeInjectLibrary.class.getName());
throw new RuntimeException("Failed to instantiate " + classname + " for language " +
language.getLanguageID(), e);
}
Class<? extends PcodeInjectLibrary> injectLibraryClass =
(Class<? extends PcodeInjectLibrary>) c;
Constructor<? extends PcodeInjectLibrary> constructor =
injectLibraryClass.getConstructor(SleighLanguage.class);
pcodeInject = constructor.newInstance(language);
}
catch (Exception e) {
Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " +
GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
throw new RuntimeException(
"Failed to instantiate " + classname + " for language " + language.getLanguageID(),
e);
}
List<InjectPayloadSleigh> additionalInject = language.getAdditionalInject();
if (additionalInject != null) {

View File

@ -7,10 +7,15 @@
</properties>
<programcounter register="EIP"/>
<segmented_address space="ram" type="real" />
<segmentop space="ram" userop="segment" baseinsize="2" innerinsize="2" farpointer="yes">
<baseop code="INT_ZEXT"/>
<baseop code="INT_LEFT" value="4"/>
<innerop code="INT_ZEXT"/>
<segmentop space="ram" userop="segment" farpointer="yes">
<pcode>
<input name="inner" size="2"/>
<input name="base" size="2"/>
<output name="res" size="4"/>
<body><![CDATA[
res = (zext(base) << 4) + zext(inner);
]]></body>
</pcode>
<constresolve>
<register name="DS"/>
</constresolve>

View File

@ -8,10 +8,15 @@
</properties>
<programcounter register="EIP"/>
<segmented_address space="ram" type="protected"/>
<segmentop space="ram" userop="segment" baseinsize="2" innerinsize="2" farpointer="yes">
<baseop code="INT_ZEXT"/>
<baseop code="INT_LEFT" value="16"/>
<innerop code="INT_ZEXT"/>
<segmentop space="ram" userop="segment" farpointer="yes">
<pcode>
<input name="inner" size="2"/>
<input name="base" size="2"/>
<output name="res" size="4"/>
<body><![CDATA[
res = (zext(base) << 16) + zext(inner);
]]></body>
</pcode>
<constresolve>
<register name="DS"/>
</constresolve>