mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
Use InjectPayload for segment ops
This commit is contained in:
parent
06de0d46a0
commit
4c3289f09f
7 changed files with 93 additions and 132 deletions
|
@ -124,31 +124,6 @@ SegmentOp::SegmentOp(Architecture *g,const string &nm,int4 ind)
|
||||||
constresolve.space = (AddrSpace *)0;
|
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,
|
bool SegmentOp::unify(Funcdata &data,PcodeOp *op,
|
||||||
vector<Varnode *> &bindlist) const
|
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->getIn(0)->getOffset() != useropindex) return false;
|
||||||
if (op->numInput() != 3) return false;
|
if (op->numInput() != 3) return false;
|
||||||
innervn = op->getIn(1);
|
innervn = op->getIn(1);
|
||||||
if (basepresent) {
|
if (baseinsize != 0) {
|
||||||
basevn = op->getIn(1);
|
basevn = op->getIn(1);
|
||||||
innervn = op->getIn(2);
|
innervn = op->getIn(2);
|
||||||
if (basevn->isConstant())
|
if (basevn->isConstant())
|
||||||
|
@ -181,40 +156,23 @@ bool SegmentOp::unify(Funcdata &data,PcodeOp *op,
|
||||||
uintb SegmentOp::execute(const vector<uintb> &input) const
|
uintb SegmentOp::execute(const vector<uintb> &input) const
|
||||||
|
|
||||||
{
|
{
|
||||||
uintb base,inner;
|
ExecutablePcode *pcodeScript = (ExecutablePcode *)glb->pcodeinjectlib->getPayload(injectId);
|
||||||
|
return pcodeScript->evaluate(input);
|
||||||
if (basepresent)
|
|
||||||
base = executeSide(basefollow,input[1]);
|
|
||||||
else
|
|
||||||
base = 0;
|
|
||||||
|
|
||||||
inner = executeSide(innerfollow,input[0]);
|
|
||||||
return (base+inner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SegmentOp::restoreXml(const Element *el)
|
void SegmentOp::restoreXml(const Element *el)
|
||||||
|
|
||||||
{
|
{
|
||||||
spc = glb->getSpaceByName(el->getAttributeValue("space"));
|
spc = glb->getSpaceByName(el->getAttributeValue("space"));
|
||||||
|
injectId = -1;
|
||||||
baseinsize = 0;
|
baseinsize = 0;
|
||||||
innerinsize = 0;
|
innerinsize = 0;
|
||||||
bool userdefined = false;
|
bool userdefined = false;
|
||||||
forcesegment = true;
|
|
||||||
supportsfarpointer = false;
|
supportsfarpointer = false;
|
||||||
name = "segment"; // Default name, might be overridden by userop attribute
|
name = "segment"; // Default name, might be overridden by userop attribute
|
||||||
for(int4 i=0;i<el->getNumAttributes();++i) {
|
for(int4 i=0;i<el->getNumAttributes();++i) {
|
||||||
const string &nm(el->getAttributeName(i));
|
const string &nm(el->getAttributeName(i));
|
||||||
if (nm == "space") continue;
|
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")
|
else if (nm == "farpointer")
|
||||||
supportsfarpointer = true;
|
supportsfarpointer = true;
|
||||||
else if (nm == "userop") { // Based on existing sleigh op
|
else if (nm == "userop") { // Based on existing sleigh op
|
||||||
|
@ -227,28 +185,17 @@ void SegmentOp::restoreXml(const Element *el)
|
||||||
throw LowlevelError("Redefining userop "+name);
|
throw LowlevelError("Redefining userop "+name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (nm == "force")
|
|
||||||
forcesegment = xml_readbool(el->getAttributeValue(i));
|
|
||||||
else
|
else
|
||||||
throw LowlevelError("Bad segmentop tag attribute: "+nm);
|
throw LowlevelError("Bad segmentop tag attribute: "+nm);
|
||||||
}
|
}
|
||||||
if (!userdefined)
|
if (!userdefined)
|
||||||
throw LowlevelError("Missing userop attribute in segmentop tag");
|
throw LowlevelError("Missing userop attribute in segmentop tag");
|
||||||
basepresent = (baseinsize != 0);
|
|
||||||
|
|
||||||
const List &list(el->getChildren());
|
const List &list(el->getChildren());
|
||||||
List::const_iterator iter;
|
List::const_iterator iter;
|
||||||
for(iter=list.begin();iter!=list.end();++iter) {
|
for(iter=list.begin();iter!=list.end();++iter) {
|
||||||
const Element *subel = *iter;
|
const Element *subel = *iter;
|
||||||
if (subel->getName()=="baseop") {
|
if (subel->getName()=="constresolve") {
|
||||||
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") {
|
|
||||||
int4 sz;
|
int4 sz;
|
||||||
const List &sublist(subel->getChildren());
|
const List &sublist(subel->getChildren());
|
||||||
if (!sublist.empty()) {
|
if (!sublist.empty()) {
|
||||||
|
@ -260,9 +207,28 @@ void SegmentOp::restoreXml(const Element *el)
|
||||||
constresolve.size = sz;
|
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
|
else
|
||||||
throw LowlevelError("Bad segment pattern tag: "+subel->getName());
|
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
|
/// \param g is the Architecture owning this set of jump assist scripts
|
||||||
|
|
|
@ -198,24 +198,19 @@ struct OpFollow {
|
||||||
/// The core routine that looks for the term-tree is unify().
|
/// The core routine that looks for the term-tree is unify().
|
||||||
class SegmentOp : public TermPatternOp {
|
class SegmentOp : public TermPatternOp {
|
||||||
AddrSpace *spc; ///< The physical address space into which a segmented pointer points
|
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 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
|
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
|
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
|
VarnodeData constresolve; ///< How to resolve constant near pointers
|
||||||
static uintb executeSide(const vector<OpFollow> &follow,uintb input);
|
|
||||||
public:
|
public:
|
||||||
SegmentOp(Architecture *g,const string &nm,int4 ind); ///< Constructor
|
SegmentOp(Architecture *g,const string &nm,int4 ind); ///< Constructor
|
||||||
AddrSpace *getSpace(void) const { return spc; } ///< Get the address space being pointed to
|
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 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 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
|
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
|
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 bool unify(Funcdata &data,PcodeOp *op,vector<Varnode *> &bindlist) const;
|
||||||
virtual uintb execute(const vector<uintb> &input) const;
|
virtual uintb execute(const vector<uintb> &input) const;
|
||||||
virtual void restoreXml(const Element *el);
|
virtual void restoreXml(const Element *el);
|
||||||
|
|
|
@ -227,51 +227,19 @@
|
||||||
</optional>
|
</optional>
|
||||||
</define>
|
</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">
|
<define name="segmentop_type">
|
||||||
<element name="segmentop">
|
<element name="segmentop">
|
||||||
<attribute name="space"/>
|
<attribute name="space"/>
|
||||||
<optional> <attribute name="userop"/> </optional>
|
<optional> <attribute name="userop"/> </optional>
|
||||||
<optional> <attribute name="baseinsize"/> </optional>
|
|
||||||
<optional> <attribute name="innerinsize"/> </optional>
|
|
||||||
<optional> <attribute name="farpointer"/> </optional>
|
<optional> <attribute name="farpointer"/> </optional>
|
||||||
|
<element name="pcode">
|
||||||
|
<ref name="pcode_type"/>
|
||||||
|
</element>
|
||||||
<optional>
|
<optional>
|
||||||
<attribute name="force">
|
<element name="constresolve">
|
||||||
<ref name="boolean_type"/>
|
<ref name="varnode_tags_type"/>
|
||||||
</attribute>
|
</element>
|
||||||
</optional>
|
</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>
|
</element>
|
||||||
</define>
|
</define>
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
private void read(XmlPullParser parser) {
|
||||||
Set<String> registerDataSet = new HashSet<>();
|
Set<String> registerDataSet = new HashSet<>();
|
||||||
|
|
||||||
|
@ -775,9 +798,7 @@ public class SleighLanguage implements Language {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (element.getName().equals("segmentop")) {
|
else if (element.getName().equals("segmentop")) {
|
||||||
while (parser.peek().isStart()) {
|
parseSegmentOp(element, parser);
|
||||||
parser.discardSubTree();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// get rid of the end tag of whatever we started with at the top of the while
|
// get rid of the end tag of whatever we started with at the top of the while
|
||||||
parser.end(element);
|
parser.end(element);
|
||||||
|
|
|
@ -163,29 +163,30 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
|
language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
|
||||||
if (classname == null) {
|
if (classname == null) {
|
||||||
pcodeInject = new PcodeInjectLibrary(language); // This is the default implementation
|
pcodeInject = new PcodeInjectLibrary(language); // This is the default implementation
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
try {
|
else {
|
||||||
Class<?> c = Class.forName(classname);
|
try {
|
||||||
if (!PcodeInjectLibrary.class.isAssignableFrom(c)) {
|
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,
|
Msg.error(this,
|
||||||
"Language " + language.getLanguageID() + " does not specify a valid " +
|
"Language " + language.getLanguageID() + " does not specify a valid " +
|
||||||
GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
|
GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
|
||||||
throw new RuntimeException(classname + " does not implement interface " +
|
throw new RuntimeException("Failed to instantiate " + classname + " for language " +
|
||||||
PcodeInjectLibrary.class.getName());
|
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();
|
List<InjectPayloadSleigh> additionalInject = language.getAdditionalInject();
|
||||||
if (additionalInject != null) {
|
if (additionalInject != null) {
|
||||||
|
|
|
@ -7,10 +7,15 @@
|
||||||
</properties>
|
</properties>
|
||||||
<programcounter register="EIP"/>
|
<programcounter register="EIP"/>
|
||||||
<segmented_address space="ram" type="real" />
|
<segmented_address space="ram" type="real" />
|
||||||
<segmentop space="ram" userop="segment" baseinsize="2" innerinsize="2" farpointer="yes">
|
<segmentop space="ram" userop="segment" farpointer="yes">
|
||||||
<baseop code="INT_ZEXT"/>
|
<pcode>
|
||||||
<baseop code="INT_LEFT" value="4"/>
|
<input name="inner" size="2"/>
|
||||||
<innerop code="INT_ZEXT"/>
|
<input name="base" size="2"/>
|
||||||
|
<output name="res" size="4"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
res = (zext(base) << 4) + zext(inner);
|
||||||
|
]]></body>
|
||||||
|
</pcode>
|
||||||
<constresolve>
|
<constresolve>
|
||||||
<register name="DS"/>
|
<register name="DS"/>
|
||||||
</constresolve>
|
</constresolve>
|
||||||
|
|
|
@ -8,10 +8,15 @@
|
||||||
</properties>
|
</properties>
|
||||||
<programcounter register="EIP"/>
|
<programcounter register="EIP"/>
|
||||||
<segmented_address space="ram" type="protected"/>
|
<segmented_address space="ram" type="protected"/>
|
||||||
<segmentop space="ram" userop="segment" baseinsize="2" innerinsize="2" farpointer="yes">
|
<segmentop space="ram" userop="segment" farpointer="yes">
|
||||||
<baseop code="INT_ZEXT"/>
|
<pcode>
|
||||||
<baseop code="INT_LEFT" value="16"/>
|
<input name="inner" size="2"/>
|
||||||
<innerop code="INT_ZEXT"/>
|
<input name="base" size="2"/>
|
||||||
|
<output name="res" size="4"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
res = (zext(base) << 16) + zext(inner);
|
||||||
|
]]></body>
|
||||||
|
</pcode>
|
||||||
<constresolve>
|
<constresolve>
|
||||||
<register name="DS"/>
|
<register name="DS"/>
|
||||||
</constresolve>
|
</constresolve>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue