mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
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:
parent
2ce191d865
commit
2c9b771d13
7 changed files with 409 additions and 20 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue