GT-3198 remove usage of java.beans.XMLDecoder/ XMLEncoder #1090.

Java's built-in XMLEncoder / XMLDecoder is too flexible and allows the
XML data being decoded to execute arbitrary commands and other
non-niceness to happen.

Replace FunctionBitPatternExplorerPlugin's usage with manual
serializing.
This commit is contained in:
dev747368 2019-10-01 14:02:18 -04:00
parent 2ce191d865
commit 2c9b771d13
7 changed files with 409 additions and 20 deletions

View file

@ -16,7 +16,6 @@
//This script dumps information about byte and instructions in neighborhoods around function starts
//and returns to an XML file
//@category FunctionStartPatterns
import java.beans.XMLEncoder;
import java.io.*;
import java.util.List;
@ -118,10 +117,7 @@ public class DumpFunctionPatternInfoScript extends GhidraScript {
File savedFile = new File(saveDir.getAbsolutePath() + File.separator +
currentProgram.getDomainFile().getPathname().replaceAll("/", "_") + "_" +
currentProgram.getExecutableMD5() + "_funcInfo.xml");
try (XMLEncoder xmlEncoder =
new XMLEncoder(new BufferedOutputStream(new FileOutputStream(savedFile)))) {
xmlEncoder.writeObject(funcPatternList);
}
funcPatternList.toXmlFile(savedFile);
Msg.info(this,
"Programs analyzed: " + programsAnalyzed + "; total functions: " + totalFuncs);
}

View file

@ -17,10 +17,15 @@ package ghidra.bitpatterns.info;
import java.math.BigInteger;
import org.jdom.Element;
/**
* class for representing the values a specific context register assumes within a function body.
*/
public class ContextRegisterInfo {
static final String XML_ELEMENT_NAME = "ContextRegisterInfo";
String contextRegister;//the context register
String value;//the value it assumes (needed because a BigInteger will not serialize to xml)
BigInteger valueAsBigInteger;//the value it assumes
@ -133,4 +138,36 @@ public class ContextRegisterInfo {
hashCode = 31 * hashCode + value.hashCode();
return hashCode;
}
/**
* Creates a {@link ContextRegisterInfo} object using data in the supplied XML node.
*
* @param ele xml Element
* @return new {@link ContextRegisterInfo} object, never null
*/
public static ContextRegisterInfo fromXml(Element ele) {
String contextRegister = ele.getAttributeValue("contextRegister");
String value = ele.getAttributeValue("value");
ContextRegisterInfo result = new ContextRegisterInfo();
result.setContextRegister(contextRegister);
result.setValue(value);
return result;
}
/**
* Converts this object into XML
*
* @return new jdom Element
*/
public Element toXml() {
Element e = new Element(XML_ELEMENT_NAME);
e.setAttribute("contextRegister", contextRegister);
e.setAttribute("value", value);
return e;
}
}

View file

@ -15,9 +15,16 @@
*/
package ghidra.bitpatterns.info;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.jdom.*;
import org.jdom.input.SAXBuilder;
import ghidra.util.Msg;
import ghidra.util.xml.XmlUtilities;
/**
* An object of this class stores all the function bit pattern information for an executable.
* It records the number of bytes and instructions for each category (first, pre, and return), as
@ -27,6 +34,8 @@ import java.util.List;
public class FileBitPatternInfo {
static final String XML_ELEMENT_NAME = "FileBitPatternInfo";
private int numFirstBytes = 0;
private int numFirstInstructions = 0;
private int numPreBytes = 0;
@ -195,4 +204,110 @@ public class FileBitPatternInfo {
public void setNumReturnInstructions(int numReturnInstructions) {
this.numReturnInstructions = numReturnInstructions;
}
/**
* Converts this object into XML
*
* @return new jdom {@link Element}
*/
public Element toXml() {
Element result = new Element(XML_ELEMENT_NAME);
XmlUtilities.setStringAttr(result, "ghidraURL", ghidraURL);
XmlUtilities.setStringAttr(result, "languageID", languageID);
XmlUtilities.setIntAttr(result, "numFirstBytes", numFirstBytes);
XmlUtilities.setIntAttr(result, "numFirstInstructions", numFirstInstructions);
XmlUtilities.setIntAttr(result, "numPreBytes", numPreBytes);
XmlUtilities.setIntAttr(result, "numPreInstructions", numPreInstructions);
XmlUtilities.setIntAttr(result, "numReturnBytes", numReturnBytes);
XmlUtilities.setIntAttr(result, "numReturnInstructions", numReturnInstructions);
Element funcBitPatternInfoListEle = new Element("funcBitPatternInfoList");
for (FunctionBitPatternInfo fbpi : funcBitPatternInfo) {
funcBitPatternInfoListEle.addContent(fbpi.toXml());
}
result.addContent(funcBitPatternInfoListEle);
return result;
}
/**
* Creates a {@link FileBitPatternInfo} instance from XML.
*
* @param e XML element to convert
* @return new {@link FileBitPatternInfo}, never null
* @throws IOException if file IO error or xml data problem
*/
public static FileBitPatternInfo fromXml(Element e) throws IOException {
String ghidraURL = e.getAttributeValue("ghidraURL");
String languageID = e.getAttributeValue("languageID");
int numFirstBytes =
XmlUtilities.parseInt(XmlUtilities.requireStringAttr(e, "numFirstBytes"));
int numFirstInstructions =
XmlUtilities.parseInt(XmlUtilities.requireStringAttr(e, "numFirstInstructions"));
int numPreBytes = XmlUtilities.parseInt(XmlUtilities.requireStringAttr(e, "numPreBytes"));
int numPreInstructions =
XmlUtilities.parseInt(XmlUtilities.requireStringAttr(e, "numPreInstructions"));
int numReturnBytes =
XmlUtilities.parseInt(XmlUtilities.requireStringAttr(e, "numReturnBytes"));
int numReturnInstructions =
XmlUtilities.parseInt(XmlUtilities.requireStringAttr(e, "numReturnInstructions"));
List<FunctionBitPatternInfo> funcBitPatternInfoList = new ArrayList<>();
Element funcBitPatternInfoListEle = e.getChild("funcBitPatternInfoList");
if (funcBitPatternInfoListEle != null) {
for (Element childElement : XmlUtilities.getChildren(funcBitPatternInfoListEle,
FunctionBitPatternInfo.XML_ELEMENT_NAME)) {
funcBitPatternInfoList.add(FunctionBitPatternInfo.fromXml(childElement));
}
}
FileBitPatternInfo result = new FileBitPatternInfo();
result.setFuncBitPatternInfo(funcBitPatternInfoList);
result.setGhidraURL(ghidraURL);
result.setLanguageID(languageID);
result.setNumFirstBytes(numFirstBytes);
result.setNumFirstInstructions(numFirstInstructions);
result.setNumPreBytes(numPreBytes);
result.setNumPreInstructions(numPreInstructions);
result.setNumReturnBytes(numReturnBytes);
result.setNumReturnInstructions(numReturnInstructions);
return result;
}
/**
* Converts this object to XML and writes it to the specified file.
*
* @param destFile name of xml file to create
* @throws IOException if file io error
*/
public void toXmlFile(File destFile) throws IOException {
Element rootEle = toXml();
Document doc = new Document(rootEle);
XmlUtilities.writeDocToFile(doc, destFile);
}
/**
* Creates a {@link FileBitPatternInfo} instance from a XML file.
*
* @param inputFile name of xml file to read
* @return new {@link FileBitPatternInfo} instance, never null
* @throws IOException if file io error or xml data format problem
*/
public static FileBitPatternInfo fromXmlFile(File inputFile) throws IOException {
SAXBuilder sax = XmlUtilities.createSecureSAXBuilder(false, false);
try (InputStream fis = new FileInputStream(inputFile)) {
Document doc = sax.build(fis);
Element rootElem = doc.getRootElement();
return fromXml(rootElem);
}
catch (JDOMException | IOException e) {
Msg.error(FileBitPatternInfo.class, "Bad file bit pattern file " + inputFile, e);
throw new IOException("Failed to read file bit pattern " + inputFile, e);
}
}
}

View file

@ -16,7 +16,6 @@
package ghidra.bitpatterns.info;
import java.awt.Component;
import java.beans.XMLDecoder;
import java.io.*;
import java.util.*;
@ -184,23 +183,19 @@ public class FileBitPatternInfoReader {
numFiles++;
FileBitPatternInfo fileInfo = null;
try (XMLDecoder xmlDecoder = new XMLDecoder(new FileInputStream(dataFile))) {
fileInfo = (FileBitPatternInfo) xmlDecoder.readObject();
}
catch (ArrayIndexOutOfBoundsException e) {
// Probably wrong type of XML file...skip
try {
fileInfo = FileBitPatternInfo.fromXmlFile(dataFile);
}
catch (IOException e) {
Msg.error(this, "IOException", e);
}
if (fileInfo == null) {
Msg.info(this, "null FileBitPatternInfo for " + dataFile);
Msg.error(this, "Error reading FileBitPatternInfo file " + dataFile, e);
return;
}
if (fileInfo.getFuncBitPatternInfo() == null) {
Msg.info(this, "fList.getFuncBitPatternInfoList null for " + dataFile);
return;
}
if (params == null) {
//TODO: this will set the params to the params of the first valid file
//these should agree with the parameters for all of the files

View file

@ -18,6 +18,8 @@ package ghidra.bitpatterns.info;
import java.math.BigInteger;
import java.util.*;
import org.jdom.Element;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Register;
@ -26,15 +28,17 @@ import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.RefType;
import ghidra.util.Msg;
import ghidra.util.xml.XmlUtilities;
/**
* This class represents information about small neighborhoods around the start and returns of a
* single function
*/
//@XmlRootElement
public class FunctionBitPatternInfo {
static final String XML_ELEMENT_NAME = "FunctionBitPatternInfo";
private InstructionSequence firstInst;
private InstructionSequence preInst;
private List<InstructionSequence> returnInst;
@ -59,7 +63,7 @@ public class FunctionBitPatternInfo {
}
/**
* No-arg constructor for use by JAXB when restoring from XML
* No-arg constructor
*/
public FunctionBitPatternInfo() {
returnBytes = new ArrayList<String>();
@ -494,4 +498,99 @@ public class FunctionBitPatternInfo {
this.contextRegisters = contextRegisters;
}
/**
* Converts a XML element into a FunctionBitPatternInfo object.
*
* @param e xml {@link Element} to convert
* @return new {@link FunctionBitPatternInfo} object, never null
*/
public static FunctionBitPatternInfo fromXml(Element e) {
String preBytes = e.getAttributeValue("preBytes");
String firstBytes = e.getAttributeValue("firstBytes");
String address = e.getAttributeValue("address");
List<String> returnBytes = new ArrayList<>();
Element returnBytesListEle = e.getChild("returnBytesList");
if (returnBytesListEle != null) {
for (Element rbEle : XmlUtilities.getChildren(returnBytesListEle, "returnBytes")) {
returnBytes.add(rbEle.getAttributeValue("value"));
}
}
InstructionSequence firstInst = InstructionSequence.fromXml(e.getChild("firstInst"));
InstructionSequence preInst = InstructionSequence.fromXml(e.getChild("preInst"));
List<InstructionSequence> returnInst = new ArrayList<>();
Element returnInstListEle = e.getChild("returnInstList");
if (returnInstListEle != null) {
for (Element isEle : XmlUtilities.getChildren(returnInstListEle,
InstructionSequence.XML_ELEMENT_NAME)) {
returnInst.add(InstructionSequence.fromXml(isEle));
}
}
List<ContextRegisterInfo> contextRegisters = new ArrayList<>();
Element contextRegistersListEle = e.getChild("contextRegistersList");
if ( contextRegistersListEle != null ) {
for (Element criElement : XmlUtilities.getChildren(contextRegistersListEle,
ContextRegisterInfo.XML_ELEMENT_NAME)) {
contextRegisters.add(ContextRegisterInfo.fromXml(criElement));
}
}
FunctionBitPatternInfo result = new FunctionBitPatternInfo();
result.setPreBytes(preBytes);
result.setFirstBytes(firstBytes);
result.setAddress(address);
result.setReturnBytes(returnBytes);
result.setFirstInst(firstInst);
result.setPreInst(preInst);
result.setReturnInst(returnInst);
result.setContextRegisters(contextRegisters);
return result;
}
/**
* Converts this object instance into XML.
*
* @return new jdom Element populated with all the datas
*/
public Element toXml() {
Element result = new Element(XML_ELEMENT_NAME);
XmlUtilities.setStringAttr(result, "preBytes", preBytes);
XmlUtilities.setStringAttr(result, "firstBytes", firstBytes);
XmlUtilities.setStringAttr(result, "address", address);
Element returnBytesListEle = new Element("returnBytesList");
result.addContent(returnBytesListEle);
for (String s : returnBytes) {
Element rbNode = new Element("returnBytes");
XmlUtilities.setStringAttr(rbNode, "value", s);
returnBytesListEle.addContent(rbNode);
}
if (firstInst != null) {
result.addContent(firstInst.toXml("firstInst"));
}
if (preInst != null) {
result.addContent(preInst.toXml("preInst"));
}
if (returnInst != null) {
Element returnInstListEle = new Element("returnInstList");
result.addContent(returnInstListEle);
for (InstructionSequence is : returnInst) {
returnInstListEle.addContent(is.toXml());
}
}
if (contextRegisters != null) {
Element contextRegistersListEle = new Element("contextRegistersList");
result.addContent(contextRegistersListEle);
for (ContextRegisterInfo cri : contextRegisters) {
contextRegistersListEle.addContent(cri.toXml());
}
}
return result;
}
}

View file

@ -17,6 +17,10 @@ package ghidra.bitpatterns.info;
import java.util.*;
import org.jdom.Element;
import ghidra.util.xml.XmlUtilities;
/**
* An object in this class stores a sequence of instructions along with the sizes and operands of each.
* These sequences come from function starts, function returns, or immediately before function starts.
@ -24,12 +28,14 @@ import java.util.*;
public class InstructionSequence {
final static String XML_ELEMENT_NAME = "InstructionSequence";
private String[] instructions;
private Integer[] sizes;
private String[] commaSeparatedOperands;
/**
* Default no-arg constructor for use by JAXB
* Default no-arg constructor
*/
public InstructionSequence() {
}
@ -241,8 +247,9 @@ public class InstructionSequence {
continue;
}
for (int i = 0, numSeqs = currentSeqs.size(); i < numSeqs; ++i) {
if (currentSeqs.get(i).getInstructions()[0] != null &&
currentBytes.get(i).getBytes() != null) {
if (currentSeqs.get(i)
.getInstructions()[0] != null && currentBytes.get(i)
.getBytes() != null) {
instSeqs.add(currentSeqs.get(i));
}
}
@ -254,4 +261,107 @@ public class InstructionSequence {
}
return instSeqs;
}
/**
* Convert this object into a XML node, using {@link #XML_ELEMENT_NAME} as the name for the node.
*
* @return new XML element
*/
public Element toXml() {
return toXml(XML_ELEMENT_NAME);
}
/**
* Convert this object into a XML node, using the specified name for the node.
*
* @param elementName name for the new XML node
* @return new XML element
*/
public Element toXml(String elementName) {
Element result = new Element(elementName);
Element instructionsListEle = new Element("instructions");
result.addContent(instructionsListEle);
if (instructions != null) {
for (String s : instructions) {
Element x = new Element("instruction");
instructionsListEle.addContent(x);
if (s != null) {
x.setAttribute("value", s);
}
}
}
Element sizesListEle = new Element("sizes");
result.addContent(sizesListEle);
if (sizes != null) {
for (Integer s : sizes) {
Element x = new Element("size");
sizesListEle.addContent(x);
if (s != null) {
XmlUtilities.setIntAttr(x, "value", s);
}
}
}
Element csoListEle = new Element("commaSeparatedOperands");
result.addContent(csoListEle);
if (commaSeparatedOperands != null) {
for (String s : commaSeparatedOperands) {
Element x = new Element("operands");
csoListEle.addContent(x);
if (s != null) {
x.setAttribute("value", s);
}
}
}
return result;
}
/**
* Creates an {@link InstructionSequence} instance from a XML node.
*
* @param element jdom Element to read, null ok
* @return new {@link InstructionSequence} or null if element was null
*/
public static InstructionSequence fromXml(Element element) {
if (element == null) {
return null;
}
List<String> instructionsList = new ArrayList<>();
Element instructionsListEle = element.getChild("instructions");
if (instructionsListEle != null) {
for (Element instEle : XmlUtilities.getChildren(instructionsListEle, "instruction")) {
String val = instEle.getAttributeValue("value");
instructionsList.add(val);
}
}
List<Integer> sizesList = new ArrayList<>();
Element sizesListEle = element.getChild("sizes");
if (sizesListEle != null) {
for (Element sizeEle : XmlUtilities.getChildren(sizesListEle, "size")) {
String val = sizeEle.getAttributeValue("value");
sizesList.add(val != null ? XmlUtilities.parseInt(val) : null);
}
}
List<String> csoList = new ArrayList<>();
Element csoListEle = element.getChild("commaSeparatedOperands");
if (csoListEle != null) {
for (Element csoEle : XmlUtilities.getChildren(csoListEle, "operands")) {
String val = csoEle.getAttributeValue("value");
csoList.add(val);
}
}
InstructionSequence result = new InstructionSequence();
result.setInstructions(instructionsList.toArray(new String[instructionsList.size()]));
result.setCommaSeparatedOperands(csoList.toArray(new String[csoList.size()]));
result.setSizes(sizesList.toArray(new Integer[sizesList.size()]));
return result;
}
}

View file

@ -17,6 +17,7 @@ package ghidra.util.xml;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -32,6 +33,7 @@ import org.xml.sax.*;
import generic.jar.ResourceFile;
import ghidra.util.Msg;
import ghidra.util.NumericUtilities;
import util.CollectionUtils;
/**
* A set of utility methods for working with XML.
@ -603,6 +605,41 @@ public class XmlUtilities {
return value;
}
/**
* Sets a string attribute on the specified element.
*
* @param ele JDom element
* @param attrName name of attribute
* @param attrValue value of attribute, null ok
*/
public static void setStringAttr(Element ele, String attrName, String attrValue) {
if (attrValue != null) {
ele.setAttribute(attrName, attrValue);
}
}
/**
* Sets an integer attribute on the specified element.
*
* @param ele JDom element
* @param attrName name of attribute
* @param attrValue value of attribute
*/
public static void setIntAttr(Element ele, String attrName, int attrValue) {
ele.setAttribute(attrName, Integer.toString(attrValue));
}
/**
* Type-safe way of getting a list of {@link Element}s from JDom.
*
* @param ele the parent element
* @param childName the name of the children elements to return
* @return List<Element> of elements
*/
public static List<Element> getChildren(Element ele, String childName) {
return CollectionUtils.asList(ele.getChildren(childName), Element.class);
}
/**
* Tests a string for characters that would cause a problem if added to an
* xml attribute or element.