Merge remote-tracking branch 'origin/master' into debugger

This commit is contained in:
Dan
2021-01-19 14:17:49 -05:00
51 changed files with 2909 additions and 827 deletions
@@ -450,6 +450,18 @@
<P><U>Started By:</U> Importing or adding to a program, Auto Analyze command</P>
</BLOCKQUOTE>
<H3><A name="Format_String_Analyzer"></A>Format String Analyzer</H3>
<BLOCKQUOTE>
<P>This analyzer detects variadic function calls in the bodies of each function that intersect
the current selection. It then parses their format string arguments to infer the correct function
call signatures. Currently, this analyzer only supports printf, scanf, and their variants (e.g., snprintf, fscanf).
If the current selection is emtpy, it searches through every function within the binary. Once
the signatures are inferred, they are overridden.</P>
<P><U>Started By:</U> Importing or adding to a program, Auto Analyze command</P>
</BLOCKQUOTE>
<H3><A name="Image"></A>Image Analyzer</H3>
@@ -364,7 +364,7 @@
<H3>Intel Hex Options<A name="Options_Intel_Hex"/></H3>
<BLOCKQUOTE>
<H4>Bass Address</H4>
<H4>Base Address</H4>
<BLOCKQUOTE>
<P>This field is used to specify the start address in memory for where to load the
@@ -395,7 +395,7 @@
<H3>Motorola Hex Options<A name="Options_Motorola_Hex"/></H3>
<BLOCKQUOTE>
<H4>Bass Address</H4>
<H4>Base Address</H4>
<BLOCKQUOTE>
<P>This field is used to specify the start address in memory for where to load the
@@ -27,7 +27,7 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
@@ -67,7 +67,7 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
try {
dtm = service.openDataTypeArchive(archiveName);
if (dtm == null) {
Msg.showError(this, null, "Failed to Apply Data Types",
log.appendMsg("Apply Data Archives",
"Failed to locate data type archive: " + archiveName);
}
else {
@@ -75,8 +75,18 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
}
}
catch (Exception e) {
if (mgr.debugOn) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
Throwable cause = e.getCause();
if (cause instanceof VersionException) {
log.appendMsg("Apply Data Archives",
"Unable to open archive " + archiveName + ": " + cause.toString());
}
else {
String msg = e.getMessage();
if (msg == null) {
msg = e.toString();
}
log.appendMsg("Apply Data Archives",
"Unexpected Error opening archive " + archiveName + ": " + msg);
}
}
}
@@ -438,9 +438,7 @@ public class DataTypeManagerHandler {
addArchive(archive);
}
if (isUserAction && (archive instanceof FileArchive)) {
if (file != null) {
userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath()));
}
userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath()));
}
return archive;
}
@@ -1251,8 +1249,8 @@ public class DataTypeManagerHandler {
public Set<String> getPossibleEquateNames(long value) {
Set<String> equateNames = new HashSet<>();
for (int i = 0; i < openArchives.size(); i++) {
DataTypeManager dtMgr = openArchives.get(i).getDataTypeManager();
for (Archive element : openArchives) {
DataTypeManager dtMgr = element.getDataTypeManager();
dtMgr.findEnumValueNames(value, equateNames);
}
return equateNames;
@@ -74,11 +74,20 @@ public class BinaryReader {
*/
public BinaryReader clone(long newIndex) {
BinaryReader clone = new BinaryReader(provider, isLittleEndian());
clone.converter = converter;
clone.currentIndex = newIndex;
return clone;
}
/**
* Returns an independent clone of this reader positioned at the same index.
*
* @return a independent clone of this reader positioned at the same index
*/
@Override
public BinaryReader clone() {
return clone(currentIndex);
}
/**
* Returns true if this reader will extract values in little endian,
* otherwise in big endian.
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,33 +18,34 @@ package ghidra.app.util.bin.format.dwarf.line;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
public class FileEntry {
private String fileName;
private LEB128 directoryIndex;
private LEB128 lastModifiedTime;
private LEB128 fileLengthInBytes;
private long directoryIndex;
private long lastModifiedTime;
private long fileLengthInBytes;
FileEntry(BinaryReader reader) throws IOException {
fileName = reader.readNextAsciiString();
if (fileName.length() == 0) {
return;
}
directoryIndex = new LEB128(reader, false);
lastModifiedTime = new LEB128(reader, false);
fileLengthInBytes = new LEB128(reader, false);
directoryIndex = LEB128.readAsLong(reader, false);
lastModifiedTime = LEB128.readAsLong(reader, false);
fileLengthInBytes = LEB128.readAsLong(reader, false);
}
public String getFileName() {
return fileName;
}
public LEB128 getDirectoryIndex() {
public long getDirectoryIndex() {
return directoryIndex;
}
public LEB128 getLastModifiedTime() {
public long getLastModifiedTime() {
return lastModifiedTime;
}
public LEB128 getFileLengthInBytes() {
public long getFileLengthInBytes() {
return fileLengthInBytes;
}
@@ -1,48 +0,0 @@
/* ###
* 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.
*/
package ghidra.app.util.bin.format.dwarf.line;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
class LEB128 {
private long value;
LEB128(BinaryReader reader, boolean isSigned) throws IOException {
if (isSigned) {
throw new UnsupportedOperationException();
}
int shift = 0;
while (true) {
int nextByte = reader.readNextByte() & 0xff;
value |= ((nextByte & 0x7f) << shift);
if ((nextByte & 0x80) == 0) {
break;
}
shift += 7;
}
}
long getValue() {
return value;
}
@Override
public String toString() {
return "0x" + Long.toHexString(value);
}
}
@@ -16,6 +16,7 @@
package ghidra.app.util.bin.format.dwarf.line;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import java.io.IOException;
@@ -92,7 +93,7 @@ public final class StatementProgramInstructions {
}
private void executeExtended(int opcode) throws IOException {
LEB128 length = new LEB128(reader, false);
long length = LEB128.readAsLong(reader, false);
long oldIndex = reader.getPointerIndex();
int extendedOpcode = reader.readNextByte();
@@ -110,7 +111,7 @@ public final class StatementProgramInstructions {
throw new UnsupportedOperationException();
}
if (oldIndex + length.getValue() != reader.getPointerIndex()) {
if (oldIndex + length != reader.getPointerIndex()) {
throw new IllegalStateException("Index values do not match!");
}
}
@@ -122,23 +123,23 @@ public final class StatementProgramInstructions {
break;
}
case DW_LNS_advance_pc: {
LEB128 value = new LEB128(reader, false);
machine.address += (value.getValue() * prologue.getMinimumInstructionLength());
long value = LEB128.readAsLong(reader, false);
machine.address += (value * prologue.getMinimumInstructionLength());
break;
}
case DW_LNS_advance_line: {
LEB128 value = new LEB128(reader, false);
machine.line += value.getValue();
long value = LEB128.readAsLong(reader, false);
machine.line += value;
break;
}
case DW_LNS_set_file: {
LEB128 value = new LEB128(reader, false);
machine.file = (int) value.getValue();
long value = LEB128.readAsLong(reader, false);
machine.file = (int) value;
break;
}
case DW_LNS_set_column: {
LEB128 value = new LEB128(reader, false);
machine.column = (int) value.getValue();
long value = LEB128.readAsLong(reader, false);
machine.column = (int) value;
break;
}
case DW_LNS_negate_statement: {
@@ -168,10 +168,10 @@ public class StatementProgramPrologue {
* @param directoryIndex the directory index
* @return the directory or current directory
*/
public String getDirectoryByIndex(LEB128 directoryIndex) {
if (directoryIndex.getValue() == 0) {
public String getDirectoryByIndex(long directoryIndex) {
if (directoryIndex == 0) {
return ".";
}
return includeDirectories.get((int)directoryIndex.getValue() - 1);
return includeDirectories.get((int)directoryIndex - 1);
}
}
@@ -43,11 +43,11 @@ public class DWARFAbbreviation
TaskMonitor monitor)
throws IOException, CancelledException {
int ac = LEB128.decode32u(reader);
int ac = LEB128.readAsUInt32(reader);
if (ac == 0) {
return null;
}
int tag = LEB128.decode32u(reader);
int tag = LEB128.readAsUInt32(reader);
DWARFChildren hasChildren = DWARFChildren.find((int) reader.readNextByte());
// Read each attribute specification until attribute and its value is 0
@@ -38,8 +38,8 @@ public class DWARFAttributeSpecification {
* @throws IOException
*/
public static DWARFAttributeSpecification read(BinaryReader reader) throws IOException {
int attribute = LEB128.decode32u(reader);
DWARFForm attributeForm = DWARFForm.find(LEB128.decode32u(reader));
int attribute = LEB128.readAsUInt32(reader);
DWARFForm attributeForm = DWARFForm.find(LEB128.readAsUInt32(reader));
return attribute != 0 && attributeForm != DWARFForm.NULL
? new DWARFAttributeSpecification(attribute, attributeForm) : null;
@@ -201,9 +201,9 @@ public class DWARFLine {
// This entry exists only if the length of the string is more than 0
if (this.name.length() > 0) {
this.directory_index = LEB128.decode(reader, false);
this.modification_time = LEB128.decode(reader, false);
this.length = LEB128.decode(reader, false);
this.directory_index = LEB128.readAsLong(reader, false);
this.modification_time = LEB128.readAsLong(reader, false);
this.length = LEB128.readAsLong(reader, false);
}
}
@@ -53,7 +53,7 @@ public class DebugInfoEntry {
public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit unit,
DWARFAttributeFactory attributeFactory) throws IOException {
long offset = reader.getPointerIndex();
int abbreviationCode = LEB128.decode32u(reader);
int abbreviationCode = LEB128.readAsUInt32(reader);
// Check for terminator DIE
if (abbreviationCode == 0) {
@@ -18,122 +18,195 @@ package ghidra.app.util.bin.format.dwarf4;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.util.NumberUtil;
public final class LEB128 {
import ghidra.app.util.bin.ByteArrayProvider;
/**
* Decode a LEB128 signed number and return it as a java 32 bit int.
* <p>
* If the value of the number can not fit in the int type, an {@link IOException} will
* be thrown.
*
* @param reader
* @return
* @throws IOException
*/
public static int decode32s(BinaryReader reader) throws IOException {
long tmp = decode(reader, true);
if (tmp < Integer.MIN_VALUE || tmp > Integer.MAX_VALUE) {
throw new IOException(
"LEB128 value out of range for java 32 bit signed int: " + Long.toString(tmp));
}
/**
* Class to hold result of reading a LEB128 value, along with size and position metadata.
* <p>
* Note: If a LEB128 value that would result in a native value longer than 64bits is attempted to
* be read, an {@link IOException} will be thrown, and the stream's position will be left at the last read byte.
* <p>
* If this was a valid (but overly large) LEB128, the caller's stream will be left still pointing to LEB data.
* <p>
*/
public class LEB128 {
return (int) tmp;
private final long offset;
private final long value;
private final int byteLength;
private LEB128(long offset, long value, int byteLength) {
this.offset = offset;
this.value = value;
this.byteLength = byteLength;
}
/**
* Decode a LEB128 unsigned number and return it as a java 32 bit int.
* <p>
* If the value of the number can not fit in the positive range of the int type,
* an {@link IOException} will be thrown.
*
* @param reader
* @return
* @throws IOException
* Returns the value as an unsigned int32. If the actual value
* is outside the positive range of a java int (ie. 0.. {@link Integer#MAX_VALUE}),
* an exception is thrown.
*
* @return int in the range of 0 to {@link Integer#MAX_VALUE}
* @throws IOException if value is outside range
*/
public static int decode32u(BinaryReader reader) throws IOException {
long tmp = decode(reader, false);
// NOTE: will only be lt 0 if tmp's value was larger than what fits in signed long and it wrapped.
if (tmp < 0 || tmp > Integer.MAX_VALUE) {
throw new IOException("LEB128 value out of range for java 32 bit unsigned int: " +
Long.toUnsignedString(tmp));
}
return (int) tmp;
public int asUInt32() throws IOException {
ensureInt32u(value);
return (int) value;
}
/**
* Decodes a LEB128 number using a binary reader and stores it in a long.
* <p>
* Large unsigned integers that use 64 bits will be returned in java's native
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
* <p>
* Large integers that use more than 64 bits will cause an IOException to be thrown.
* <p>
* @param reader the binary reader
* @param isSigned true if the value is signed
* @throws IOException if an I/O error occurs
* Returns the value as an signed int32. If the actual value
* is outside the range of a java int (ie. {@link Integer#MIN_VALUE}.. {@link Integer#MAX_VALUE}),
* an exception is thrown.
*
* @return int in the range of {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}
* @throws IOException if value is outside range
*/
public static long decode(BinaryReader reader, boolean isSigned) throws IOException {
int nextByte = 0;
int shift = 0;
long value = 0;
boolean overflow = false;
while (true) {
nextByte = reader.readNextUnsignedByte();
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
// if the value being read is more than 64 bits long mark it as overflow.
// keep reading the rest of the number so the caller is not left in the
// middle of the LEB128 number's guts.
overflow = true;
}
// must cast to long before shifting otherwise shift values greater than 32 cause problems
value |= ((long) (nextByte & 0x7F)) << shift;
shift += 7;
if ((nextByte & 0x80) == 0) {
break;
}
}
if (overflow) {
throw new IOException(
"Unsupported LEB128 value, too large to fit in 64bit java long variable");
}
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
value |= -(1 << shift);
}
public int asInt32() throws IOException {
ensureInt32s(value);
return (int) value;
}
/**
* Returns the value as a 64bit primitive long. Interpreting the signed-ness of the
* value will depend on the way the value was read (ie. if {@link #readSignedValue(BinaryReader)}
* vs. {@link #readUnsignedValue(BinaryReader)} was used).
*
* @return long value.
*/
public long asLong() {
return value;
}
/**
* Decodes a LEB128 number using a byte array and stores it in a long.
* This function cannot read numbers larger than Long.MAX_VALUE.
* @param bytes the bytes representing the LEB128 number
* @param isSigned true if the value is signed
* @throws IOException
* Returns the offset of the LEB128 value in the stream it was read from.
*
* @return stream offset of the LEB128 value
*/
public static long decode(byte[] bytes, boolean isSigned) throws IOException {
return decode(bytes, 0, isSigned);
public long getOffset() {
return offset;
}
/**
* Decodes a LEB128 number using an offset into a byte array and stores it in a long.
* This function cannot read numbers larger than Long.MAX_VALUE.
* @param bytes the bytes representing the LEB128 number
* @param offset offset into the byte array
* @param isSigned true if the value is signed
* @throws IOException
* Returns the number of bytes that were used to store the LEB128 value in the stream
* it was read from.
*
* @return number of bytes used to store the read LEB128 value
*/
public static long decode(byte[] bytes, int offset, boolean isSigned) throws IOException {
public int getLength() {
return byteLength;
}
@Override
public String toString() {
return String.format("LEB128: value: %d, offset: %d, byteLength: %d", value, offset,
byteLength);
}
/**
* Reads a LEB128 value from the BinaryReader and returns a {@link LEB128} instance
* that contains the value along with size and position metadata.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param reader {@link BinaryReader} to read bytes from
* @param isSigned true if the value is signed
* @return a {@link LEB128} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128 readValue(BinaryReader reader, boolean isSigned) throws IOException {
long offset = reader.getPointerIndex();
long value = LEB128.readAsLong(reader, isSigned);
int size = (int) (reader.getPointerIndex() - offset);
return new LEB128(offset, value, size);
}
/**
* Reads an unsigned LEB128 value from the BinaryReader and returns a {@link LEB128} instance
* that contains the value along with size and position metadata.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param reader {@link BinaryReader} to read bytes from
* @return a {@link LEB128} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128 readUnsignedValue(BinaryReader reader) throws IOException {
return readValue(reader, false);
}
/**
* Reads an signed LEB128 value from the BinaryReader and returns a {@link LEB128} instance
* that contains the value along with size and position metadata.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param reader {@link BinaryReader} to read bytes from
* @return a {@link LEB128} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128 readSignedValue(BinaryReader reader) throws IOException {
return readValue(reader, true);
}
/**
* Reads a LEB128 signed number from the BinaryReader and returns it as a java 32 bit int.
* <p>
* If the value of the number can not fit in the int type, an {@link IOException} will
* be thrown.
*
* @param reader {@link BinaryReader} to read bytes from
* @return signed int32 value
* @throws IOException if error reading bytes or value is outside the
* range of a signed int32
*/
public static int readAsInt32(BinaryReader reader) throws IOException {
long tmp = readAsLong(reader, true);
ensureInt32s(tmp);
return (int) tmp;
}
/**
* Reads a LEB128 unsigned number from the BinaryReader and returns it as a java 32 bit int.
* <p>
* If the value of the number can not fit in the positive range of the int type,
* an {@link IOException} will be thrown.
*
* @param reader {@link BinaryReader} to read bytes from
* @return unsigned int32 value 0..Integer.MAX_VALUE
* @throws IOException if error reading bytes or value is outside the
* positive range of a java 32 bit int (ie. 0..Integer.MAX_VALUE)
*/
public static int readAsUInt32(BinaryReader reader) throws IOException {
long tmp = readAsLong(reader, false);
ensureInt32u(tmp);
return (int) tmp;
}
/**
* Reads a LEB128 number from the BinaryReader and returns it as a java 64 bit long int.
* <p>
* Large unsigned integers that use all 64 bits are be returned in a java native
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
* <p>
* Large integers that use more than 64 bits will cause an IOException to be thrown.
* <p>
* @param reader {@link BinaryReader} to read bytes from
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static long readAsLong(BinaryReader reader, boolean isSigned) throws IOException {
int nextByte = 0;
int shift = 0;
long value = 0;
for (int i = offset; i < bytes.length; i++) {
nextByte = bytes[i] & NumberUtil.UNSIGNED_BYTE_MASK;
while (true) {
nextByte = reader.readNextUnsignedByte();
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
throw new IOException(
"Unsupported LEB128 value, too large to fit in 64bit java long variable");
@@ -141,7 +214,6 @@ public final class LEB128 {
// must cast to long before shifting otherwise shift values greater than 32 cause problems
value |= ((long) (nextByte & 0x7F)) << shift;
shift += 7;
if ((nextByte & 0x80) == 0) {
@@ -149,11 +221,62 @@ public final class LEB128 {
}
}
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
long tmp1 = (1L << shift);
long tmp2 = -tmp1;
value |= tmp2;
// 0x40 is the new 'high' sign bit since 0x80 is the continuation flag
value |= (-1L << shift);
}
return value;
}
/**
* Decodes a LEB128 number from a byte array and returns it as a long.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param bytes the bytes representing the LEB128 number
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if error reading bytes or value is outside the
* range of a java 64 bit int
*/
public static long decode(byte[] bytes, boolean isSigned) throws IOException {
return decode(bytes, 0, isSigned);
}
/**
* Decodes a LEB128 number from a byte array and returns it as a long.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param bytes the bytes representing the LEB128 number
* @param offset offset in byte array of where to start reading bytes
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if error reading bytes or value is outside the
* range of a java 64 bit int
*/
public static long decode(byte[] bytes, int offset, boolean isSigned) throws IOException {
ByteArrayProvider bap = new ByteArrayProvider(bytes);
BinaryReader br = new BinaryReader(bap, true);
br.setPointerIndex(offset);
return readAsLong(br, isSigned);
}
private static void ensureInt32u(long value) throws IOException {
if (value < 0 || value > Integer.MAX_VALUE) {
throw new IOException("LEB128 value out of range for java 32 bit unsigned int: " +
Long.toUnsignedString(value));
}
}
private static void ensureInt32s(long value) throws IOException {
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
throw new IOException(
"LEB128 value out of range for java 32 bit signed int: " +
Long.toString(value));
}
}
}
@@ -73,7 +73,7 @@ public class DWARFAttributeFactory {
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
}
case DW_FORM_ref_udata: {
long uoffset = LEB128.decode(reader, false);
long uoffset = LEB128.readAsLong(reader, false);
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
}
@@ -103,7 +103,7 @@ public class DWARFAttributeFactory {
return new DWARFBlobAttribute(reader.readNextByteArray(length));
}
case DW_FORM_block: {
int length = LEB128.decode32u(reader);
int length = LEB128.readAsUInt32(reader);
if (length < 0 || length > MAX_BLOCK4_SIZE) {
throw new IOException("Invalid/bad dw_form_block size: " + length);
}
@@ -121,12 +121,12 @@ public class DWARFAttributeFactory {
case DW_FORM_data8:
return new DWARFNumericAttribute(reader.readNextLong());
case DW_FORM_sdata:
return new DWARFNumericAttribute(LEB128.decode(reader, true));
return new DWARFNumericAttribute(LEB128.readAsLong(reader, true));
case DW_FORM_udata:
return new DWARFNumericAttribute(LEB128.decode(reader, false));
return new DWARFNumericAttribute(LEB128.readAsLong(reader, false));
case DW_FORM_exprloc: {
int length = LEB128.decode32u(reader);
int length = LEB128.readAsUInt32(reader);
if (length < 0 || length > MAX_BLOCK4_SIZE) {
throw new IOException("Invalid/bad dw_form_exprloc size: " + length);
}
@@ -157,7 +157,7 @@ public class DWARFAttributeFactory {
// Indirect Form
case DW_FORM_indirect:
DWARFForm formValue = DWARFForm.find(LEB128.decode32u(reader));
DWARFForm formValue = DWARFForm.find(LEB128.readAsUInt32(reader));
DWARFAttributeValue value = read(reader, unit, formValue);
return new DWARFIndirectAttribute(value, formValue);
@@ -137,9 +137,9 @@ public class DWARFExpression {
case U_LONG:
return reader.readNextLong(); /* & there is no mask for ulong */
case S_LEB128:
return LEB128.decode(reader, true);
return LEB128.readAsLong(reader, true);
case U_LEB128:
return LEB128.decode(reader, false);
return LEB128.readAsLong(reader, false);
case SIZED_BLOB:
throw new IOException("Can't read SIZED_BLOB as a Long value");
case DWARF_INT:
@@ -92,7 +92,7 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
static LEB128Info parse(BinaryReader reader, boolean signed) throws IOException {
long nextPos = reader.getPointerIndex();
long value = LEB128.decode(reader, signed);
long value = LEB128.readAsLong(reader, signed);
long pos = reader.getPointerIndex();
int size = (int) (pos - nextPos);
return new LEB128Info((int) nextPos, value, size);
@@ -132,7 +132,7 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
// NOTE: assumes 2-GByte MemBuffer limit
int offset = (int) reader.getPointerIndex();
long groupSize = LEB128.decode(reader, true);
long groupSize = LEB128.readAsLong(reader, true);
if (groupSize > remainingRelocations) {
Msg.debug(this, "Group relocation count " + groupSize +
" exceeded total count " + remainingRelocations);
@@ -205,20 +205,20 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
try {
int relocationIndex = 0;
long remainingRelocations = LEB128.decode(reader, true);
long offset = LEB128.decode(reader, true);
long remainingRelocations = LEB128.readAsLong(reader, true);
long offset = LEB128.readAsLong(reader, true);
long addend = 0;
while (remainingRelocations > 0) {
long groupSize = LEB128.decode(reader, true);
long groupSize = LEB128.readAsLong(reader, true);
if (groupSize > remainingRelocations) {
Msg.warn(this, "Group relocation count " + groupSize +
" exceeded total count " + remainingRelocations);
break;
}
long groupFlags = LEB128.decode(reader, true);
long groupFlags = LEB128.readAsLong(reader, true);
boolean groupedByInfo =
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
boolean groupedByDelta = (groupFlags &
@@ -228,22 +228,22 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
boolean groupHasAddend =
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
long groupOffsetDelta = groupedByDelta ? LEB128.decode(reader, true) : 0;
long groupRInfo = groupedByInfo ? LEB128.decode(reader, true) : 0;
long groupOffsetDelta = groupedByDelta ? LEB128.readAsLong(reader, true) : 0;
long groupRInfo = groupedByInfo ? LEB128.readAsLong(reader, true) : 0;
if (groupedByAddend && groupHasAddend) {
addend += LEB128.decode(reader, true);
addend += LEB128.readAsLong(reader, true);
}
for (int i = 0; i < groupSize; i++) {
offset += groupedByDelta ? groupOffsetDelta : LEB128.decode(reader, true);
offset += groupedByDelta ? groupOffsetDelta : LEB128.readAsLong(reader, true);
long info = groupedByInfo ? groupRInfo : LEB128.decode(reader, true);
long info = groupedByInfo ? groupRInfo : LEB128.readAsLong(reader, true);
long rAddend = 0;
if (groupHasAddend) {
if (!groupedByAddend) {
addend += LEB128.decode(reader, true);
addend += LEB128.readAsLong(reader, true);
}
rAddend = addend;
}
@@ -139,10 +139,6 @@ public class ElfSectionHeaderType {
public final String description;
public ElfSectionHeaderType(int value, String name, String description) {
if (value < 0) {
throw new IllegalArgumentException(
"ElfProgramHeaderType value out of range: 0x" + Long.toHexString(value));
}
this.value = value;
this.name = name;
this.description = description;
@@ -0,0 +1,91 @@
/* ###
* 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.
*/
package ghidra.app.util.bin.format.elf;
import static ghidra.app.util.bin.StructConverter.*;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.DuplicateNameException;
/**
* Factory data type that marks up a Gnu Build-Id record from a
* ELF .note.gnu.build-id section
*/
public class GnuBuildIdSection extends FactoryStructureDataType {
private static final int MAX_SANE_STR_LENS = 1024;
private long sectionSize;
/**
* Creates a new GnuBuildIdDataType instance.
*
* @param dtm the {@link DataTypeManager} for the program
* @param sectionSize the size of the section (for bounds checking, assumes this
* is the only record in the section)
*/
public GnuBuildIdSection(DataTypeManager dtm, long sectionSize) {
super("Gnu_BuildId", dtm);
this.sectionSize = sectionSize;
}
@Override
public DataType clone(DataTypeManager dtm) {
if (dtm == dataMgr) {
return this;
}
return new GnuBuildIdSection(dtm, sectionSize);
}
@Override
protected void populateDynamicStructure(MemBuffer buf, Structure es) {
try {
long nameLen = buf.getUnsignedInt(0);
long descLen = buf.getUnsignedInt(4);
if (nameLen > MAX_SANE_STR_LENS || descLen > MAX_SANE_STR_LENS ||
nameLen + descLen + 12 /* sizeof int fields */ > sectionSize) {
return;
}
es.add(DWORD, "namesz", "Length of name field");
es.add(DWORD, "descsz", "Length of description field");
es.add(DWORD, "type", "Vendor specific type");
if (nameLen > 0) {
es.add(StringDataType.dataType, (int) nameLen, "name", "Build-id vendor name");
}
if (descLen > 0) {
es.add(new ArrayDataType(BYTE, (int) descLen, BYTE.getLength(), dataMgr),
"description", "Build-id value");
}
}
catch (MemoryAccessException e) {
// ignore and drop thru with partial defined structure type
}
}
@Override
protected Structure setCategoryPath(Structure struct, MemBuffer buf) {
try {
struct.setCategoryPath(new CategoryPath("/ELF"));
}
catch (DuplicateNameException e) {
// ignore - will not happen
}
return struct;
}
}
@@ -0,0 +1,75 @@
/* ###
* 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.
*/
package ghidra.app.util.bin.format.elf;
import static ghidra.app.util.bin.StructConverter.*;
import ghidra.docking.settings.SettingsImpl;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
* Factory data type that marks up a ELF .gnu_debuglink section.
*/
public class GnuDebugLinkSection extends FactoryStructureDataType {
private long sectionSize;
/**
* Creates a new GnuDebugLinkDataType instance.
*
* @param dtm the program's {@link DataTypeManager}
* @param sectionSize the size of the section (for bounds checking)
*/
public GnuDebugLinkSection(DataTypeManager dtm, long sectionSize) {
super("Gnu_DebugLink", dtm);
this.sectionSize = sectionSize;
}
@Override
public DataType clone(DataTypeManager dtm) {
if (dtm == dataMgr) {
return this;
}
return new GnuDebugLinkSection(dtm, sectionSize);
}
@Override
protected void populateDynamicStructure(MemBuffer buf, Structure es) {
StringDataInstance filenameStr = StringDataInstance.getStringDataInstance(
StringDataType.dataType, buf, SettingsImpl.NO_SETTINGS, -1);
int filenameLen = filenameStr.getStringLength();
if (filenameLen <= 0 || filenameLen + 4 /* crc field */ > sectionSize) {
return;
}
filenameLen = (int) NumericUtilities.getUnsignedAlignedValue(filenameLen, 4);
es.add(StringDataType.dataType, filenameLen, "filename", "Debug file name");
es.add(DWORD, "crc", null);
}
@Override
protected Structure setCategoryPath(Structure struct, MemBuffer buf) {
try {
struct.setCategoryPath(new CategoryPath("/ELF"));
}
catch (DuplicateNameException e) {
// ignore - will not happen
}
return struct;
}
}
@@ -104,8 +104,8 @@ public class FunctionDataTypeHTMLRepresentation extends HTMLDataTypeRepresentati
String name = var.getName();
DataType locatableType = getLocatableDataType(dataType);
lines.add(new VariableTextLine(HTMLUtilities.friendlyEncodeHTML(displayName), name,
locatableType));
lines.add(new VariableTextLine(HTMLUtilities.friendlyEncodeHTML(displayName),
HTMLUtilities.friendlyEncodeHTML(name), locatableType));
}
return lines;
@@ -114,7 +114,7 @@ public class FunctionDataTypeHTMLRepresentation extends HTMLDataTypeRepresentati
private static String buildHTMLText(TextLine returnType, TextLine functionName,
List<ValidatableLine> arguments, TextLine varArgs, TextLine voidArgs) {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
String returnTypeText = returnType.getText();
returnTypeText = wrapStringInColor(returnTypeText, returnType.getTextColor());
@@ -167,6 +167,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
markupHashTable(monitor);
markupGnuHashTable(monitor);
markupGnuBuildId(monitor);
markupGnuDebugLink(monitor);
processGNU(monitor);
processGNU_readOnly(monitor);
@@ -749,7 +751,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
Address baseAddress = relocationSpace.getTruncatedAddress(baseWordOffset, true);
// long relocationOffset = baseWordOffset + reloc.getOffset();
// r_offset is defined to be a byte offset (assume byte size is 1)
Address relocAddr = context != null ? context.getRelocationAddress(baseAddress, reloc.getOffset()) : baseAddress.addWrap(reloc.getOffset());;
Address relocAddr = context != null ? context.getRelocationAddress(baseAddress, reloc.getOffset()) : baseAddress.addWrap(reloc.getOffset());
// Address relocAddr = relocationSpace.getTruncatedAddress(relocationOffset, true);
long[] values = new long[] { reloc.getSymbolIndex() };
@@ -867,7 +869,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
Structure phStructDt = (Structure) elf.getProgramHeaders()[0].toDataType();
phStructDt = (Structure) phStructDt.clone(program.getDataTypeManager());
phStructDt = phStructDt.clone(program.getDataTypeManager());
Array arrayDt = new ArrayDataType(phStructDt, headerCount, size);
@@ -925,7 +927,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
Structure shStructDt = (Structure) elf.getSections()[0].toDataType();
shStructDt = (Structure) shStructDt.clone(program.getDataTypeManager());
shStructDt = shStructDt.clone(program.getDataTypeManager());
Array arrayDt = new ArrayDataType(shStructDt, elf.e_shnum(), elf.e_shentsize());
@@ -1899,6 +1901,38 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
// return new AddressRangeImpl(alignedAddress, freeRange.getMaxAddress());
// }
private void markupGnuBuildId(TaskMonitor monitor) {
ElfSectionHeader sh = elf.getSection(".note.gnu.build-id");
Address addr = findLoadAddress(sh, 0);
if (addr == null) {
return;
}
try {
listing.createData(addr,
new GnuBuildIdSection(program.getDataTypeManager(), sh.getSize()));
}
catch (Exception e) {
log("Failed to properly markup Gnu Build-Id at " + addr + ": " + getMessage(e));
}
}
private void markupGnuDebugLink(TaskMonitor monitor) {
ElfSectionHeader sh = elf.getSection(".gnu_debuglink");
Address addr = findLoadAddress(sh, 0);
if (addr == null) {
return;
}
try {
listing.createData(addr,
new GnuDebugLinkSection(program.getDataTypeManager(), sh.getSize()));
}
catch (Exception e) {
log("Failed to properly markup Gnu DebugLink at " + addr + ": " + getMessage(e));
}
}
private void markupHashTable(TaskMonitor monitor) {
ElfDynamicTable dynamicTable = elf.getDynamicTable();
@@ -15,154 +15,199 @@
*/
package ghidra.app.util.bin.format.dwarf4;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteArrayProvider;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteArrayProvider;
import ghidra.util.NumericUtilities;
public class LEB128Test {
private static final boolean BR_IS_LITTLE_ENDIAN = true;
private BinaryReader br(byte... bytes) {
private BinaryReader br(int... intBytes) {
byte[] bytes = new byte[intBytes.length];
for (int i = 0; i < intBytes.length; i++) {
bytes[i] = (byte) intBytes[i];
}
return new BinaryReader(new ByteArrayProvider(bytes), BR_IS_LITTLE_ENDIAN);
}
/**
* Test reading the largest unsigned LEB128 int that we can handle (64 used bits).
* <p>
*
* @throws IOException
*/
@Test
public void testMax64bitUnsigned() throws IOException {
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01 };
static class TestEntry {
long expectedValue;
byte[] bytes;
long value = LEB128.decode(bytes, false);
// -1 signed long == MAX unsigned
Assert.assertEquals(-1, value);
TestEntry(long expectedValue, byte[] bytes) {
this.expectedValue = expectedValue;
this.bytes = bytes;
}
}
/**
* Test reading a number that is larger than 32 bits to ensure that all the shifting
* done in the LEB128 reader doesn't have a 32bit fault.
* @throws IOException
*/
@Test
public void test36bitUnsigned() throws IOException {
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0x01 };
private TestEntry te(long expectedValue, int... intBytes) {
byte[] bytes = new byte[intBytes.length];
for (int i = 0; i < intBytes.length; i++) {
bytes[i] = (byte) intBytes[i];
}
return new TestEntry(expectedValue, bytes);
}
long value = LEB128.decode(bytes, false);
Assert.assertEquals(0xfffffffffL, value);
private List<TestEntry> unsignedTestEntries = List.of(
// misc
te(0L, 0x80, 0x80, 0x80, 0x80, 0x80, 0x0), // Tests reading a zero value that is encoded in non-optimal way.
te(-1L, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01), // -1 == MAX unsigned long
te(0xf_ffff_ffffL, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01), // more than 32 bits to test shifting > 32bits
// 1 byte
te(1L, 0x01),
te(63L, 0x3f),
te(64L, 0x40),
// 1 byte to 2 byte transition
te(125L, 0x7d),
te(126L, 0x7e),
te(127L, 0x7f),
te(128L, 0x80, 0x01),
te(129L, 0x81, 0x01),
te(130L, 0x82, 0x01),
te(131L, 0x83, 0x01),
te(254L, 0xfe, 0x01),
te(255L, 0xff, 0x01),
te(256L, 0x80, 0x02),
te(257L, 0x81, 0x02),
// 2 byte to 3 byte transition
te(16382L, 0xfe, 0x7f),
te(16383L, 0xff, 0x7f),
te(16384L, 0x80, 0x80, 0x01),
te(16385L, 0x81, 0x80, 0x01),
// 3 byte to 4 byte transition
te(2097151L, 0xff, 0xff, 0x7f),
te(2097152L, 0x80, 0x80, 0x80, 0x01),
te(2097153L, 0x81, 0x80, 0x80, 0x01),
// 4 byte to 5 byte transition
te(268435455L, 0xff, 0xff, 0xff, 0x7f),
te(268435456L, 0x80, 0x80, 0x80, 0x80, 0x01),
te(268435457L, 0x81, 0x80, 0x80, 0x80, 0x01),
// 5 byte to 6 byte transition
te(34359738367L, 0xff, 0xff, 0xff, 0xff, 0x7f),
te(34359738368L, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01),
te(34359738369L, 0x81, 0x80, 0x80, 0x80, 0x80, 0x01)
//
);
private List<TestEntry> signedTestEntries = List.of(
// misc
te(-2130303778817L, 0xff, 0xff, 0xff, 0xff, 0xff, 0x41),
// 1 byte positive stuff
te(0L, 0x00),
te(1L, 0x01),
// 1 byte to 2 byte transition (positive)
te(63L, 0x3f),
te(64L, 0xc0, 0x00),
te(65L, 0xc1, 0x00),
te(66L, 0xc2, 0x00),
te(126L, 0xfe, 0x00),
te(127L, 0xff, 0x00),
te(128L, 0x80, 0x01),
te(129L, 0x81, 0x01),
te(254L, 0xfe, 0x01),
te(255L, 0xff, 0x01),
te(256L, 0x80, 0x02),
te(257L, 0x81, 0x02),
// 2 byte to 3 byte transition
te(8190L, 0xfe, 0x3f),
te(8191L, 0xff, 0x3f),
te(8192L, 0x80, 0xc0, 0x00),
te(8193L, 0x81, 0xc0, 0x00),
// 1 byte negative stuff
te(-1L, 0x7f),
te(-2L, 0x7e),
te(-3L, 0x7d),
te(-4L, 0x7c),
te(-5L, 0x7b),
te(-6L, 0x7a),
// 1 byte to 2 byte transition (negative)
te(-64L, 0x40),
te(-65L, 0xbf, 0x7f),
te(-127, 0x81, 0x7f),
te(-128, 0x80, 0x7f),
te(-129, 0xff, 0x7e),
// 2 byte to 3 byte transition (negative)
te(-8191L, 0x81, 0x40),
te(-8192L, 0x80, 0x40),
te(-8193L, 0xff, 0xbf, 0x7f),
te(-8194L, 0xfe, 0xbf, 0x7f)
);
@Test
public void testUnsignedTestEntries() throws IOException {
testTestEntries(unsignedTestEntries, false, "Unsigned TestEntry");
}
@Test
public void testSignedNeg2() throws IOException {
byte[] bytes = new byte[] { (byte) 0x7e };
long value = LEB128.decode(bytes, true);
Assert.assertEquals(-2, value);
public void testSignedTestEntries() throws IOException {
testTestEntries(signedTestEntries, true, "Signed TestEntry");
}
@Test
public void testSignedNeg127() throws IOException {
byte[] bytes = new byte[] { (byte) 0x81, (byte) 0x7f };
long value = LEB128.decode(bytes, true);
Assert.assertEquals(-127, value);
public void testTestEntries(List<TestEntry> testEntries, boolean signed, String name)
throws IOException {
for (int i = 0; i < testEntries.size(); i++) {
TestEntry te = testEntries.get(i);
BinaryReader br =
new BinaryReader(new ByteArrayProvider(te.bytes), BR_IS_LITTLE_ENDIAN);
long actualValue = LEB128.readAsLong(br, signed);
assertEquals(String.format(
"%s[%d] failed: leb128(%s) != %d. Expected=%d / %x, actual=%d / %x",
name, i, NumericUtilities.convertBytesToString(te.bytes), te.expectedValue,
te.expectedValue, te.expectedValue, actualValue, actualValue), te.expectedValue,
actualValue);
assertEquals(String.format("%s[%d] failed: left-over bytes: %d", name, i,
te.bytes.length - br.getPointerIndex()), te.bytes.length, br.getPointerIndex());
}
}
@Test
public void testSignedNeg128() throws IOException {
byte[] bytes = new byte[] { (byte) 0x80, (byte) 0x7f };
long value = LEB128.decode(bytes, true);
Assert.assertEquals(-128, value);
}
@Test
public void testSignedNeg129() throws IOException {
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0x7e };
long value = LEB128.decode(bytes, true);
Assert.assertEquals(-129, value);
}
@Test
public void testSigned129() throws IOException {
byte[] bytes = new byte[] { (byte) 0x81, (byte) 0x1 };
long value = LEB128.decode(bytes, true);
Assert.assertEquals(129, value);
}
/**
* Tests reading an zero value that is encoded in non-optimal way.
* @throws IOException
*/
@Test
public void testAltZeroEncoded() throws IOException {
byte[] bytes = new byte[] { (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
(byte) 0x0 };
long value = LEB128.decode(bytes, false);
Assert.assertEquals(0, value);
}
/**
* Test a 36bit signed negative value.
*
* @throws IOException
*/
@Test
public void test36bitSignedNeg() throws IOException {
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0x41 };
long value = LEB128.decode(bytes, true);
Assert.assertEquals(-2130303778817L, value);
}
/**
* Test reading a unsigned LEB128 that is just 1 bit too large for a java 64 bit long int.
* @throws IOException
*/
@Test
@Test(expected = IOException.class)
public void testToolargeUnsigned() throws IOException {
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x02 };
// Test reading a unsigned LEB128 that is just 1 bit too large for a java 64 bit long int.
try {
long value = LEB128.decode(bytes, false);
Assert.fail(
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
value);
}
catch (IOException ioe) {
// good
}
BinaryReader br = br(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02);
long value = LEB128.readAsLong(br, false);
Assert.fail(
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
value);
}
/**
* Test that the BinaryReader stream is correctly positioned after the LEB128 bytes after
* reading a LEB128 that is too large for a java 64 bit long int.
*
* @throws IOException
*/
@Test
public void testToolargeBinaryReaderStreamPosition() throws IOException {
BinaryReader br = br((byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0x02, (byte) 0x1, (byte) 0x2);
public void testTooLargeValueBinaryReaderStreamPosition() {
// Test that the BinaryReader stream is 'correctly' positioned after the LEB128 bytes after
// reading a LEB128 that is too large for a java 64 bit long int.
BinaryReader br =
br(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x1, 0x2);
try {
long value = LEB128.decode(br, false);
long value = LEB128.readAsLong(br, false);
Assert.fail(
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
Long.toUnsignedString(value));
@@ -171,38 +216,23 @@ public class LEB128Test {
// good
}
Assert.assertEquals(1, br.readNextByte() & 0xFF);
Assert.assertEquals(2, br.readNextByte() & 0xFF);
Assert.assertEquals(10, br.getPointerIndex());
}
/**
* Test uint32 max
*
* @throws IOException
*/
@Test
public void testUint32Max() throws IOException {
int value =
LEB128.decode32u(br((byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07));
int value = LEB128.readAsUInt32(br(0xff, 0xff, 0xff, 0xff, 0x07));
Assert.assertEquals(Integer.MAX_VALUE, value);
}
/**
* Test uint32 max overflow with 0xff_ff_ff_ff
*
* @throws IOException
*/
@Test
@Test(expected = IOException.class)
public void testUint32Overflow() throws IOException {
try {
int value = LEB128.decode32u(
br((byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f));
Assert.fail(
"Should not be able to read a LEB128 that is larger than what can fit in java 32 bit int: " +
Integer.toUnsignedString(value));
}
catch (IOException ioe) {
// good
}
// Test uint32 max overflow with 0xff_ff_ff_ff
int value = LEB128.readAsUInt32(br(0xff, 0xff, 0xff, 0xff, 0x0f));
Assert.fail(
"Should not be able to read a LEB128 that is larger than what can fit in java 32 bit int: " +
Integer.toUnsignedString(value));
}
}
+1
View File
@@ -546,6 +546,7 @@ model {
}
}
else if (b.toolChain in Clang) {
b.cppCompiler.args "-std=c++11"
b.cppCompiler.args "-Wall"
b.cppCompiler.args "-O2" // for DEBUG, comment this line out
// b.cppCompiler.args "-g" // for DEBUG, uncomment this line
@@ -181,6 +181,20 @@ typedef char int1;
typedef uint8 uintp;
#endif
#if defined (__APPLE_CC__) && defined (__aarch64__)
#define HOST_ENDIAN 0
typedef unsigned int uintm;
typedef int intm;
typedef unsigned long uint8;
typedef long int8;
typedef unsigned int uint4;
typedef int int4;
typedef unsigned short uint2;
typedef short int2;
typedef unsigned char uint1;
typedef char int1;
typedef uint8 uintp;
#endif
#if defined(_WINDOWS)
#pragma warning (disable:4312)
@@ -0,0 +1,63 @@
/* ###
* 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.
*/
package ghidra.app.plugin.core.string.variadic;
/**
* This class represents a single argument of a variadic function
*/
public class FormatArgument {
private String lengthModifier;
private String conversionSpecifier;
/**
* Constructor for a FormatArg
*
* @param lengthModifier length modifier of a format argument
* @param conversionSpec conversion specifier of a format argument
*/
public FormatArgument(String lengthModifier, String conversionSpec) {
this.lengthModifier = lengthModifier;
this.conversionSpecifier = conversionSpec;
}
/**
* lenghtModifier getter
*
* @return lengthModifier
*/
public String getLengthModifier() {
return this.lengthModifier;
}
/**
* convertionSpec getter
*
* @return conversionSpecifier
*/
public String getConversionSpecifier() {
return this.conversionSpecifier;
}
/**
* Converts FormatArg to String
*
* @return FormatArgument as String
*/
public String toString() {
return String.format("[%s, %s]", this.lengthModifier, this.conversionSpecifier);
}
}
@@ -0,0 +1,392 @@
/* ###
* 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.
*/
package ghidra.app.plugin.core.string.variadic;
import java.util.*;
import org.apache.commons.collections4.IteratorUtils;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.parallel.*;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.pcode.PcodeOpAST;
import ghidra.program.util.DefinedDataIterator;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class FormatStringAnalyzer extends AbstractAnalyzer {
// Array of substrings of variadic function names that are searched for
private static final String[] VARIADIC_SUBSTRINGS = { "printf", "scanf" };
private static final String NAME = "Variadic Function Signature Override";
private static final String DESCRIPTION =
"Detects variadic function calls in the bodies of each function that intersect the" +
"current selection and parses their format string arguments to infer the correct " +
"signatures. Currently, this analyzer only supports printf, scanf, and thier variants " +
"(e.g., snprintf, fscanf). If the current selection is empty, it searches through " +
"every function. Once the correct signatures are inferred, they are overridden.";
private final static boolean OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED = false;
private final static String OPTION_NAME_CREATE_BOOKMARKS = "Create Analysis Bookmarks";
private static final String OPTION_DESCRIPTION_CREATE_BOOKMARKS =
"Select this check box if you want this analyzer to create analysis bookmarks " +
"when items of interest are created/identified by the analyzer.";
private boolean createBookmarksEnabled = OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED;
// Any function name containing this substring is determined to be an input type function
private static final String INPUT_FUNCTION_SUBSTRING = "scanf";
private Program currentProgram = null;
private FormatStringParser parser;
public FormatStringAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.FUNCTION_SIGNATURES_ANALYZER);
setSupportsOneTimeAnalysis();
setPriority(AnalysisPriority.LOW_PRIORITY);
setDefaultEnablement(false);
setPrototype();
}
@Override
public boolean canAnalyze(Program program) {
return true;
}
private synchronized FormatStringParser getParser() {
if (parser == null) {
parser = new FormatStringParser(currentProgram);
}
return parser;
}
private synchronized void disposeParser() {
parser = null;
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) {
this.currentProgram = program;
try {
run(set, monitor);
}
catch (CancelledException e) {
// User cancelled analysis
}
finally {
disposeParser();
}
return true;
}
private void run(AddressSetView selection, TaskMonitor monitor)
throws CancelledException {
DefinedDataIterator dataIterator = DefinedDataIterator.definedStrings(currentProgram);
Map<Address, Data> stringsByAddress = new HashMap<>();
for (Data data : dataIterator) {
String s = data.getDefaultValueRepresentation();
if (s.contains("%")) {
stringsByAddress.put(data.getAddress(), data);
}
monitor.checkCanceled();
}
FunctionIterator functionIterator = currentProgram.getListing().getFunctions(true);
FunctionIterator externalIterator = currentProgram.getListing().getExternalFunctions();
Iterator<Function> programFunctionIterator = IteratorUtils.chainedIterator(functionIterator,externalIterator);
Map<String, List<DataType>> namesToParameters = new HashMap<>();
Map<String, DataType> namesToReturn = new HashMap<>();
Set<Function> toDecompile = new HashSet<>();
Set<String> variadicFunctionNames = new HashSet<>();
// Find variadic function names and their parameter data types
for (Function function : IteratorUtils.asIterable(programFunctionIterator)) {
String name = function.getName().strip();
if (usesVariadicFormatString(function)) {
for (String variadicSubstring : VARIADIC_SUBSTRINGS) {
if (name.contains(variadicSubstring)) {
variadicFunctionNames.add(name);
namesToParameters.put(name, getParameters(function));
namesToReturn.put(name, function.getReturnType());
break;
}
}
}
monitor.checkCanceled();
}
Iterator<Function> functionsToSearchIterator = selection != null
? currentProgram.getFunctionManager()
.getFunctionsOverlapping(selection)
: currentProgram.getFunctionManager().getFunctionsNoStubs(true);
// Find functions that call variadic functions
while (functionsToSearchIterator.hasNext()) {
Function function = functionsToSearchIterator.next();
Set<Function> calledFunctions = function.getCalledFunctions(monitor);
for (Function calledFunction : calledFunctions) {
// If this function calls a variadic function, add it to functions to decompile
if (namesToParameters.containsKey(calledFunction.getName())) {
toDecompile.add(function);
break;
}
}
monitor.checkCanceled();
}
decompile(currentProgram, monitor, stringsByAddress, variadicFunctionNames,
namesToParameters,
namesToReturn,
toDecompile);
}
private void decompile(Program program, TaskMonitor monitor,
Map<Address, Data> stringsByAddress,
Set<String> variadicFunctionNames,
Map<String, List<DataType>> namesToParameters, Map<String, DataType> namesToReturn,
Set<Function> toDecompile) {
DecompilerCallback<Void> callback = initDecompilerCallback(program, stringsByAddress,
variadicFunctionNames, namesToParameters, namesToReturn);
if (toDecompile.isEmpty()) {
Msg.info(this, "No functions detected that make variadic function calls with " +
"format strings containing format specifiers");
return;
}
try {
ParallelDecompiler.decompileFunctions(callback, toDecompile, monitor);
}
catch (Exception e) {
Msg.error(this, "Error: could not decompile functions with ParallelDecompiler", e);
}
finally {
callback.dispose();
}
}
private DecompilerCallback<Void> initDecompilerCallback(Program program,
Map<Address, Data> stringsByAddress,
Set<String> variadicFuncNames, Map<String, List<DataType>> namesToParameters,
Map<String, DataType> namesToReturn) {
return new DecompilerCallback<>(program,
new VariadicSignatureDecompileConfigurer()) {
@Override
public Void process(DecompileResults results, TaskMonitor tMonitor) throws Exception {
if (results == null) {
return null;
}
Function function = results.getFunction();
PcodeFunctionParser pcodeParser = new PcodeFunctionParser(program);
if (results.getHighFunction() == null ||
results.getHighFunction().getPcodeOps() == null) {
return null;
}
Iterator<PcodeOpAST> pcodeOpASTIterator = results.getHighFunction().getPcodeOps();
List<PcodeOpAST> pcodeOpASTs = new ArrayList<>();
if ((results.getHighFunction() != null) && pcodeOpASTIterator != null) {
while (pcodeOpASTIterator.hasNext()) {
PcodeOpAST pcodeAST = pcodeOpASTIterator.next();
pcodeOpASTs.add(pcodeAST);
}
}
List<FunctionCallData> functionCallDataList = pcodeParser.parseFunctionForCallData(
pcodeOpASTs, stringsByAddress, variadicFuncNames);
if (functionCallDataList != null && functionCallDataList.size() > 0) {
overrideCallList(program, function, functionCallDataList, namesToParameters,
namesToReturn);
}
tMonitor.checkCanceled();
return null;
}
};
}
private List<DataType> getParameters(Function function) {
// NOTE: Currently only considers variadic functions with format string
// arguments.
List<DataType> dataTypes = new ArrayList<>();
for (ParameterDefinition pd : function.getSignature().getArguments()) {
dataTypes.add(pd.getDataType());
}
return dataTypes;
}
private boolean usesVariadicFormatString(Function function) {
int paramCount = function.getParameterCount();
return function.hasVarArgs() && paramCount > 0 &&
isCharPointer(function.getParameters()[paramCount - 1].getDataType());
}
private boolean isCharPointer(DataType dataType) {
if (dataType instanceof TypeDef) {
dataType = ((TypeDef) dataType).getBaseDataType();
}
if (!(dataType instanceof Pointer)) {
return false;
}
DataType dt = ((Pointer) dataType).getDataType();
return dt instanceof CharDataType || dt instanceof WideCharDataType ||
dt instanceof WideChar16DataType || dt instanceof WideChar32DataType;
}
private class VariadicSignatureDecompileConfigurer implements DecompileConfigurer {
// DecompInterface allows for control of decompilation processes
@Override
public void configure(DecompInterface decompiler) {
decompiler.toggleCCode(true); // Produce C code
decompiler.toggleSyntaxTree(true); // Produce syntax tree
decompiler.openProgram(currentProgram);
decompiler.setSimplificationStyle("normalize");
DecompileOptions options = new DecompileOptions();
options.grabFromProgram(currentProgram);
decompiler.setOptions(options);
}
}
private ParameterDefinition[] parseParameters(Function function,
Address address,
String callFunctionName, String formatString,
Map<String, List<DataType>> namesToParameters) {
Program functionProgram = function.getProgram();
FormatStringParser parser = getParser();
// DataTypes of arguments are treated differently when the variadic function
// looks like scanf since it takes in inputs. We need this information
// so that the correct DataType arguments are generated
boolean isOutputType = !callFunctionName.contains(INPUT_FUNCTION_SUBSTRING);
List<FormatArgument> formatArguments =
parser.convertToFormatArgumentList(formatString, isOutputType);
DataType[] dataTypes = isOutputType ? parser.convertToOutputDataTypes(formatArguments)
: parser.convertToInputDataTypes(formatArguments);
if (dataTypes == null) {
currentProgram.getBookmarkManager()
.setBookmark(address, BookmarkType.ANALYSIS, "Unrecognized format string",
"Format string could not be parsed: " + formatString);
return null;
}
ParameterDefinition[] paramDefs =
createParameters(callFunctionName, dataTypes, functionProgram, namesToParameters);
return paramDefs;
}
private ParameterDefinition[] createParameters(String callFunctionName, DataType[] dataTypes,
Program program, Map<String, List<DataType>> namesToParameters) {
List<DataType> initialFunctionParameters = namesToParameters.get(callFunctionName);
int numberOfParameters = initialFunctionParameters.size() + dataTypes.length;
if (numberOfParameters == 0) {
return null; // Invalid function
}
ParameterDefinition[] parameterDefinitions = new ParameterDefinition[numberOfParameters];
for (int i = 0; i < numberOfParameters; i++) {
if (i < initialFunctionParameters.size()) {
parameterDefinitions[i] =
new ParameterDefinitionImpl("param" + i, initialFunctionParameters.get(i), "");
}
else {
parameterDefinitions[i] = new ParameterDefinitionImpl("param" + i,
dataTypes[i - initialFunctionParameters.size()], "");
}
}
return parameterDefinitions;
}
private FunctionSignature initSignature(Function function, Address address,
String callFunctionName, String formatString,
Map<String, List<DataType>> namesToParameters, Map<String, DataType> namesToReturn) {
ParameterDefinition[] parameterDefinitions =
parseParameters(function, address, callFunctionName, formatString, namesToParameters);
if (parameterDefinitions == null || parameterDefinitions.length == 0) {
return null;
}
FunctionDefinitionDataType signature = new FunctionDefinitionDataType(callFunctionName);
signature.setArguments(parameterDefinitions);
signature.setReturnType(namesToReturn.get(callFunctionName));
return signature;
}
private void overrideCallList(Program program, Function function,
List<FunctionCallData> functionCallDataList,
Map<String, List<DataType>> namesToParameters, Map<String, DataType> namesToReturn) {
if (function == null || functionCallDataList == null) {
return;
}
for (FunctionCallData data : functionCallDataList) {
overrideFunctionCall(program, function, data.getAddressOfCall(), data.getCallFuncName(),
data.getFormatString(), namesToParameters, namesToReturn);
}
}
private void overrideFunctionCall(Program program, Function function, Address address,
String callFunctionName, String formatString,
Map<String, List<DataType>> namesToParameters,
Map<String, DataType> namesToReturn) {
if (formatString == null) {
return;
}
FunctionSignature functionSignature = initSignature(function, address, callFunctionName,
formatString, namesToParameters, namesToReturn);
if (functionSignature == null || function == null || address == null) {
return;
}
try {
if (createBookmarksEnabled) {
BookmarkManager bookmark = program.getBookmarkManager();
bookmark.setBookmark(address, BookmarkType.ANALYSIS,
"Function Signature Override",
"Override for call to function " + callFunctionName);
}
HighFunctionDBUtil.writeOverride(function, address, functionSignature);
}
catch (InvalidInputException e) {
Msg.error(this, "Error: invalid input given to writeOverride()", e);
}
}
@Override
public boolean removed(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
return false;
}
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_NAME_CREATE_BOOKMARKS, createBookmarksEnabled, null,
OPTION_DESCRIPTION_CREATE_BOOKMARKS);
}
@Override
public void optionsChanged(Options options, Program program) {
createBookmarksEnabled =
options.getBoolean(OPTION_NAME_CREATE_BOOKMARKS, createBookmarksEnabled);
}
}
@@ -0,0 +1,68 @@
/* ###
* 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.
*/
package ghidra.app.plugin.core.string.variadic;
import ghidra.program.model.address.*;
/**
* Class for encapsulating a variadic function call
*/
public class FunctionCallData {
private Address addressOfCall;
private String callFunctionName;
private String formatString;
/**
* Constructore for FuncCallData
*
* @param addressOfCall Address of function call
* @param callFunctionName variadic function name
* @param formatString format String
*/
public FunctionCallData(Address addressOfCall, String callFunctionName, String formatString) {
this.addressOfCall = addressOfCall;
this.callFunctionName = callFunctionName;
this.formatString = formatString;
}
/**
* addressOfCall getter
*
* @return addressOfCall
*/
public Address getAddressOfCall() {
return this.addressOfCall;
}
/**
* callFunctionName getter
*
* @return callFunctionName
*/
public String getCallFuncName() {
return this.callFunctionName;
}
/**
* formatString getter
*
* @return formatString
*/
public String getFormatString() {
return this.formatString;
}
}
@@ -0,0 +1,184 @@
/* ###
* 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.
*/
package ghidra.app.plugin.core.string.variadic;
import java.util.*;
import ghidra.docking.settings.SettingsImpl;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.StringDataInstance;
import ghidra.program.model.data.StringDataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBufferImpl;
import ghidra.program.model.pcode.PcodeOpAST;
import ghidra.program.model.pcode.Varnode;
/**
* Class for parsing functions' Pcode representations and finding variadic
* functions being called
*
*/
public class PcodeFunctionParser {
// All values within the range [32, 126] are ascii readable
private static final int READABLE_ASCII_LOWER_BOUND = 32;
private static final int READABLE_ASCII_UPPER_BOUND = 126;
// How many bytes to read from a memory address when initial format
// String cannot be found. This normally only happens for short format
// Strings with lengths less than 5
private static final int BUFFER_LENGTH = 20;
private static final String CALL_INSTRUCTION = "CALL";
private Program program;
public PcodeFunctionParser(Program program) {
this.program = program;
}
/**
* Takes pcode ops of a function and parses them to determine whether there are
* any calls to variadic functions that use format Strings.
*
* @param pcodeOps List of PcodeOpAST for a function
* @param addressToCandidateData map of Addresses to format String data
* @param variadicFunctionNames Set of variadic functions to look for
* @return List of variadic functions that the current function calls
*/
public List<FunctionCallData> parseFunctionForCallData(List<PcodeOpAST> pcodeOps,
Map<Address, Data> addressToCandidateData, Set<String> variadicFunctionNames) {
if (pcodeOps == null || addressToCandidateData == null || variadicFunctionNames == null ||
this.program == null) {
return null;
}
List<FunctionCallData> functionCallDataList = new ArrayList<>();
for (PcodeOpAST ast : pcodeOps) {
Varnode firstNode = ast.getInput(0);
if (firstNode == null) {
continue;
}
if (ast.getMnemonic().contentEquals(CALL_INSTRUCTION)) {
FunctionManager functionManager = this.program.getFunctionManager();
Function function = functionManager.getFunctionAt(firstNode.getAddress());
if (function == null) {
return null;
}
String functionName = function.getName();
if (variadicFunctionNames.contains(functionName)) {
Varnode[] inputs = ast.getInputs();
if (inputs.length > 0) {
boolean hasDefinedFormatString = searchForVariadicCallData(ast,
addressToCandidateData, functionCallDataList, functionName);
if (!hasDefinedFormatString) {
searchForHiddenFormatStrings(ast, functionCallDataList, functionName);
}
}
}
}
}
return functionCallDataList;
}
private boolean searchForVariadicCallData(PcodeOpAST ast,
Map<Address, Data> addressToCandidateData, List<FunctionCallData> functionCallDataList,
String functionName) {
boolean hasDefinedFormatString = false;
Varnode[] inputs = ast.getInputs();
for (int i = 1; i < inputs.length; i++) {
Varnode v = inputs[i];
Data data = null;
Address ramSpaceAddress = convertAddressToRamSpace(v.getAddress());
if (addressToCandidateData.containsKey(ramSpaceAddress)) {
data = addressToCandidateData.get(ramSpaceAddress);
functionCallDataList.add(new FunctionCallData(ast.getSeqnum().getTarget(),
functionName, data.getDefaultValueRepresentation()));
hasDefinedFormatString = true;
}
}
return hasDefinedFormatString;
}
// If addrToCandidateData doesn't have format String data for this call
// and we are calling a variadic function, parse the String to determine
// whether it's a format String.
private void searchForHiddenFormatStrings(PcodeOpAST ast,
List<FunctionCallData> functionCallDataList, String functionName) {
Varnode[] inputs = ast.getInputs();
// Initialize i = 1 to skip first input
for (int i = 1; i < inputs.length; ++i) {
Varnode v = inputs[i];
String formatStringCandidate = findFormatString(v.getAddress());
if (formatStringCandidate == null) {
continue;
}
if (formatStringCandidate.contains("%")) {
functionCallDataList.add(new FunctionCallData(ast.getSeqnum().getTarget(),
functionName, formatStringCandidate));
}
break;
}
}
private Address convertAddressToRamSpace(Address address) {
String addressString = address.toString(false);
return this.program.getAddressFactory().getAddress(addressString);
}
/**
* Looks at bytes at given address and converts to format String
*
* @param address Address of format String
* @return format String
*/
private String findFormatString(Address address) {
if (!address.getAddressSpace().isConstantSpace()) {
return null;
}
// Old address associated with constant space which doesn't work
Address ramSpaceAddress = convertAddressToRamSpace(address);
MemoryBufferImpl memoryBuffer =
new MemoryBufferImpl(this.program.getMemory(), ramSpaceAddress);
SettingsImpl settings = new SettingsImpl();
StringDataInstance stringDataInstance = StringDataInstance
.getStringDataInstance(new StringDataType(), memoryBuffer, settings, BUFFER_LENGTH);
String stringValue = stringDataInstance.getStringValue();
if (stringValue == null) {
return null;
}
String formatStringCandidate = "";
for (int i = 0; i < stringValue.length(); i++) {
if (!isAsciiReadable(stringValue.charAt(i))) {
break;
}
formatStringCandidate += stringValue.charAt(i);
}
return formatStringCandidate;
}
private boolean isAsciiReadable(char c) {
return c >= READABLE_ASCII_LOWER_BOUND && c <= READABLE_ASCII_UPPER_BOUND;
}
}
@@ -0,0 +1,332 @@
/* ###
* 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.
*/
package ghidra.app.plugin.core.string.variadic;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.data.*;
public class FormatStringParserTest extends AbstractGenericTest {
private ProgramBuilder builder;
private ProgramDB program;
@Before
public void setUp() throws Exception {
builder = new ProgramBuilder("FormatStringParserTest", ProgramBuilder._TOY, this);
assertNotNull(builder);
program = builder.getProgram();
assertNotNull(program);
}
// Determines whether null is properly returned for
// invalid format Strings. Each String is invalid due to
// either (1) invalid conversion specifier, (2) invalid
// length modifier, or (3) placeholder incorrectly used
@Test
public void testInvalidFormatString() {
runFormatTest("%r", null, true); // r is not a conversion specifier
runFormatTest("%%%lw", null, true); // w is not a conversion specifier
runFormatTest("%#0*.*ld", null, false); // scanf doesn't use flags or period
runFormatTest("%d::%%%ld%z", null, true); // z is not a conversion specifier
runFormatTest("thisisatest%%%#**u", null, true); // two consecutive astericks
runFormatTest("%#0'*rd", null, true); // r is not length modifier
runFormatTest("%%%#'*md", null, true); // m is not length modifier
runFormatTest("%*.**d", null, true); // two consecutive astericks
runFormatTest("%lD", null, true); // D is not a conversion specifier
runFormatTest("%-0+**d", null, false); // scanf doesn't use flags, two consecutive astericks
runFormatTest("%-0+*.*d", null, false); // scanf doesn't use flags or period
runFormatTest("%2.3d", null, false); // scanf doesn't use period
runFormatTest("%*1$d %d\n", null, true); // If one placeholder specifies parameter, the others must too
runFormatTest("%2$d %d\n", null, true); // If one placeholder specifies parameter, the others must too
}
// Tests format strings for scanf which have expected types of pointers instead
// of standard format strings
@Test
public void testScanfFormatString() {
DataType[] expectedTypes1 =
{ program.getDataTypeManager().getPointer(new IntegerDataType()) };
runFormatTest("%d", expectedTypes1, false);
DataType[] expectedTypes2 =
{ program.getDataTypeManager().getPointer(new IntegerDataType()),
program.getDataTypeManager().getPointer(new ShortDataType()) };
runFormatTest("%d%hi", expectedTypes2, false);
DataType[] expectedTypes3 =
{ program.getDataTypeManager().getPointer(new PointerDataType(DataType.VOID)),
program.getDataTypeManager().getPointer(new CharDataType()) };
runFormatTest("%p%*d%s", expectedTypes3, false);
DataType[] expectedTypes4 =
{ program.getDataTypeManager().getPointer(new LongDoubleDataType()),
program.getDataTypeManager().getPointer(new CharDataType()),
program.getDataTypeManager().getPointer(new PointerDataType(DataType.VOID)) };
runFormatTest("!:%12La%*d+=%2s%3p%*20d", expectedTypes4, false);
}
// Tests format strings that are more complex, containing less commonly
// used format patterns and more '%' characters
@Test
public void testComplexFormatString() {
DataType[] expectedTypes1 =
{ program.getDataTypeManager().getPointer(new IntegerDataType()), };
runFormatTest("#12%n\nd2", expectedTypes1, true);
DataType[] expectedTypes2 =
{ program.getDataTypeManager().getPointer(new CharDataType()), new LongDataType() };
runFormatTest("#thisisatest%+-4.12s%#.1lin\nd2", expectedTypes2, true);
DataType[] expectedTypes3 =
{ new PointerDataType(DataType.VOID), new LongDoubleDataType(),
new UnsignedCharDataType() };
runFormatTest("%01.3pp%%%#1.2Lg%%%%%hhXxn2", expectedTypes3, true);
DataType[] expectedTypes4 = { new IntegerDataType(), new IntegerDataType(),
new UnsignedCharDataType(), new IntegerDataType(), new LongDoubleDataType() };
runFormatTest("%0#+-*.*hhX%%%.*La", expectedTypes4, true);
DataType[] expectedTypes5 = { new IntegerDataType(),
program.getDataTypeManager().getPointer(new IntegerDataType()), new IntegerDataType(),
program.getDataTypeManager().getPointer(new WideCharDataType()), new IntegerDataType(),
new LongDoubleDataType() };
runFormatTest("%.*n%*C%%%%%.*LE", expectedTypes5, true);
}
// Tests format strings that use astericks to add another int
// argument to determine field width or precision
@Test
public void testAsterickFormatString() {
DataType[] expectedTypes1 = { new IntegerDataType(), new IntegerDataType() };
runFormatTest("%*d", expectedTypes1, true);
DataType[] expectedTypes2 = { new IntegerDataType(), new LongDataType() };
runFormatTest("%.*ld", expectedTypes2, true);
DataType[] expectedTypes3 =
{ new IntegerDataType(), new IntegerDataType(), new IntegerDataType() };
runFormatTest("%*.*d", expectedTypes3, true);
DataType[] expectedTypes4 =
{ new IntegerDataType(), new IntegerDataType(), new IntegerDataType() };
runFormatTest("*%%%+-*.*d", expectedTypes4, true);
}
// Test simple format strings with different length modifiers
@Test
public void testLengthModifierFormatString() {
DataType[] expectedTypes1 =
{ new LongDataType(), new PointerDataType(LongDataType.dataType) };
runFormatTest("%ld %ln", expectedTypes1, true);
DataType[] expectedTypes2 =
{ new ShortDataType(), new CharDataType(), new PointerDataType(ShortDataType.dataType),
new PointerDataType(CharDataType.dataType) };
runFormatTest("%hd %hhi %hn %hhn", expectedTypes2, true);
DataType[] expectedTypes3 = { new UnsignedShortDataType(), new UnsignedCharDataType() };
runFormatTest("%hx %hhu", expectedTypes3, true);
DataType[] expectedTypes4 =
{ new UnsignedLongDataType(), new LongLongDataType(), new UnsignedLongLongDataType(),
new PointerDataType(LongLongDataType.dataType) };
runFormatTest("%lX %lld %llx %lln", expectedTypes4, true);
DataType[] expectedTypes5 =
{ new LongDoubleDataType(), new LongLongDataType(), new UnsignedLongLongDataType(),
new UnsignedShortDataType(), new UnsignedCharDataType() };
runFormatTest("%LE %lli %llX %hu %hhX", expectedTypes5, true);
}
// Test simple format strings with different special length modifiers
// using generated default typedefs
@Test
public void testSpecialLengthModifierFormatStringDefault() {
DataType[] expectedTypes1 =
{ new TypedefDataType("size_t", UnsignedLongDataType.dataType) };
runFormatTest("%zd", expectedTypes1, true);
DataType[] expectedTypes2 =
{ new TypedefDataType("size_t", UnsignedLongDataType.dataType) };
runFormatTest("%zu", expectedTypes2, true);
DataType[] expectedTypes3 = { new TypedefDataType("ptrdiff_t", LongDataType.dataType) };
runFormatTest("%td", expectedTypes3, true);
DataType[] expectedTypes4 =
{ new TypedefDataType("size_t", UnsignedLongDataType.dataType) };
runFormatTest("%tu", expectedTypes4, true);
DataType[] expectedTypes5 = { new TypedefDataType("intmax_t", LongLongDataType.dataType) };
runFormatTest("%jd", expectedTypes5, true);
DataType[] expectedTypes6 =
{ new TypedefDataType("uintmax_t", UnsignedLongLongDataType.dataType) };
runFormatTest("%ju", expectedTypes6, true);
DataType[] expectedTypes7 =
{ new PointerDataType(new TypedefDataType("intmax_t", LongLongDataType.dataType)) };
runFormatTest("%jn", expectedTypes7, true);
}
// Test simple format strings with different special length modifiers
// using predefined typedefs
@Test
public void testSpecialLengthModifierFormatStringPredefined() {
int txId = program.startTransaction("Add TypeDefs");
try {
ProgramDataTypeManager dtm = program.getDataTypeManager();
DataType sizetDt =
dtm.resolve(new TypedefDataType("size_t", UnsignedLongLongDataType.dataType), null);
DataType ptrdiftDt =
dtm.resolve(new TypedefDataType("ptrdiff_t", LongLongDataType.dataType), null);
DataType intmaxtDt =
dtm.resolve(new TypedefDataType("intmax_t", LongDataType.dataType), null);
DataType uintmaxtDt =
dtm.resolve(new TypedefDataType("uintmax_t", UnsignedLongDataType.dataType), null);
DataType[] expectedTypes1 = { sizetDt };
runFormatTest("%zd", expectedTypes1, true);
DataType[] expectedTypes2 = { sizetDt };
runFormatTest("%zu", expectedTypes2, true);
DataType[] expectedTypes3 = { ptrdiftDt };
runFormatTest("%td", expectedTypes3, true);
DataType[] expectedTypes4 = { sizetDt };
runFormatTest("%tu", expectedTypes4, true);
DataType[] expectedTypes5 = { intmaxtDt };
runFormatTest("%jd", expectedTypes5, true);
DataType[] expectedTypes6 = { uintmaxtDt };
runFormatTest("%ju", expectedTypes6, true);
DataType[] expectedTypes7 = { new PointerDataType(intmaxtDt) };
runFormatTest("%jn", expectedTypes7, true);
}
finally {
program.endTransaction(txId, true);
}
}
// Test simple format Strings with different conversion specifiers
@Test
public void testConversionSpecFormatString() {
DataType[] expectedTypes1 = { new IntegerDataType() };
runFormatTest("%d", expectedTypes1, true);
DataType[] expectedTypes2 =
{ new IntegerDataType(), new IntegerDataType(), new UnsignedIntegerDataType(),
program.getDataTypeManager().getPointer(new CharDataType()) };
runFormatTest("%i %i %x %s", expectedTypes2, true);
DataType[] expectedTypes3 = { new IntegerDataType(), new IntegerDataType(),
program.getDataTypeManager().getPointer(new CharDataType()) };
runFormatTest("%d %d %s", expectedTypes3, true);
DataType[] expectedTypes4 = { new DoubleDataType(), new DoubleDataType(),
new DoubleDataType(), new DoubleDataType(), new UnsignedCharDataType() };
runFormatTest("%e %f %E %G %c", expectedTypes4, true);
DataType[] expectedTypes5 = { new UnsignedIntegerDataType(), new UnsignedIntegerDataType(),
new UnsignedIntegerDataType(), new DoubleDataType(), new DoubleDataType() };
runFormatTest("%u %x %X %e %g", expectedTypes5, true);
DataType[] expectedTypes6 = { new IntegerDataType() };
runFormatTest("%.d", expectedTypes6, true);
}
// Format Strings with field widths indicated by the sequence "*m$"
// where m is an integer that determines the position in the argument
// list of an integer argument
@Test
public void testFormatParameters() {
DataType[] expectedTypes1 = { new IntegerDataType() };
runFormatTest("%1$d", expectedTypes1, true);
DataType[] expectedTypes2 = { new IntegerDataType(), new IntegerDataType() };
runFormatTest("%1$*2$d", expectedTypes2, true);
DataType[] expectedTypes3 = { new IntegerDataType(), new IntegerDataType() };
runFormatTest("%1$.*2$d", expectedTypes3, true);
DataType[] expectedTypes4 = { new IntegerDataType(), new IntegerDataType(),
new IntegerDataType(), new IntegerDataType() };
runFormatTest("%1$d:%2$.*3$d:%4$.*3$d\n", expectedTypes4, true);
DataType[] expectedTypes5 =
{ new UnsignedIntegerDataType(), new UnsignedIntegerDataType() };
runFormatTest("%2$d %2$#x; %1$d %1$#x", expectedTypes5, true);
DataType[] expectedTypes6 =
{ new UnsignedIntegerDataType(), new IntegerDataType(), new IntegerDataType() };
runFormatTest("%2$+#*3$d:%2$#x;0-:'.~%1$0*2$d:!2%1$#x", expectedTypes6, true);
DataType[] expectedTypes7 =
{ new UnsignedLongLongDataType(), new DoubleDataType(), new IntegerDataType() };
runFormatTest("%2$+#*3$f:*;`2!%1$#qu", expectedTypes7, true);
}
private void runFormatTest(String testString, DataType[] expected, boolean runOutputAnalyzer) {
FormatStringParser parser = new FormatStringParser(program);
List<FormatArgument> formatArguments =
parser.convertToFormatArgumentList(testString, runOutputAnalyzer);
DataType[] dataTypes = runOutputAnalyzer ? parser.convertToOutputDataTypes(formatArguments)
: parser.convertToInputDataTypes(formatArguments);
assertEquivalent(dataTypes, expected);
}
private void assertEquivalent(DataType[] actual, DataType[] expected) {
if (expected == null) {
assertNull(actual);
return;
}
assertNotNull("Expected args were not produced", actual);
assertNotNull("Unexpected args were produced", expected);
assertEquals("Expected arg count differs from actual", actual.length, expected.length);
for (int i = 0; i < actual.length; i++) {
assertNotNull("Unexpected null arg returned", actual[i]);
if (!actual[i].isEquivalent(expected[i])) {
fail("Expected: " + expected[i] + ", Actual: " + actual[i]);
}
}
}
}
@@ -15,18 +15,14 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class AnnotationElement implements StructConverter {
private int nameIndex;
@@ -34,10 +30,9 @@ public class AnnotationElement implements StructConverter {
private EncodedValue value;
public AnnotationElement( BinaryReader reader ) throws IOException {
nameIndex = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
nameIndexLength = Leb128.unsignedLeb128Size( nameIndex );
reader.setPointerIndex( reader.getPointerIndex( ) + nameIndexLength );
LEB128 leb128 = LEB128.readUnsignedValue(reader);
nameIndex = leb128.asUInt32();
nameIndexLength = leb128.getLength();
value = new EncodedValue( reader );
}
@@ -15,15 +15,15 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class ClassDataItem implements StructConverter {
private int staticFieldsSize;
@@ -36,27 +36,27 @@ public class ClassDataItem implements StructConverter {
private int directMethodsSizeLength;// in bytes
private int virtualMethodsSizeLength;// in bytes
private List< EncodedField > staticFields = new ArrayList< EncodedField >( );
private List< EncodedField > instancesFields = new ArrayList< EncodedField >( );
private List< EncodedMethod > directMethods = new ArrayList< EncodedMethod >( );
private List< EncodedMethod > virtualMethods = new ArrayList< EncodedMethod >( );
private List< EncodedField > staticFields = new ArrayList< >( );
private List< EncodedField > instancesFields = new ArrayList< >( );
private List< EncodedMethod > directMethods = new ArrayList< >( );
private List< EncodedMethod > virtualMethods = new ArrayList< >( );
public ClassDataItem( BinaryReader reader ) throws IOException {
staticFieldsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
staticFieldsSizeLength = Leb128.unsignedLeb128Size( staticFieldsSize );
reader.readNextByteArray( staticFieldsSizeLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
staticFieldsSize = leb128.asUInt32();
staticFieldsSizeLength = leb128.getLength();
instanceFieldsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
instanceFieldsSizeLength = Leb128.unsignedLeb128Size( instanceFieldsSize );
reader.readNextByteArray( instanceFieldsSizeLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
instanceFieldsSize = leb128.asUInt32();
instanceFieldsSizeLength = leb128.getLength();
directMethodsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
directMethodsSizeLength = Leb128.unsignedLeb128Size( directMethodsSize );
reader.readNextByteArray( directMethodsSizeLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
directMethodsSize = leb128.asUInt32();
directMethodsSizeLength = leb128.getLength();
virtualMethodsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
virtualMethodsSizeLength = Leb128.unsignedLeb128Size( virtualMethodsSize );
reader.readNextByteArray( virtualMethodsSizeLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
virtualMethodsSize = leb128.asUInt32();
virtualMethodsSizeLength = leb128.getLength();
for ( int i = 0 ; i < staticFieldsSize ; ++i ) {
staticFields.add( new EncodedField( reader ) );
@@ -15,18 +15,14 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class DebugInfoItem implements StructConverter {
private int lineStart;
@@ -38,30 +34,25 @@ public class DebugInfoItem implements StructConverter {
private byte [] stateMachineOpcodes;
public DebugInfoItem( BinaryReader reader ) throws IOException {
lineStart = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
lineStartLength = Leb128.unsignedLeb128Size( lineStart );
reader.readNextByteArray( lineStartLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
lineStart = leb128.asUInt32();
lineStartLength = leb128.getLength();
parametersSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
parametersSizeLength = Leb128.unsignedLeb128Size( parametersSize );
reader.readNextByteArray( parametersSizeLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
parametersSize = leb128.asUInt32();
parametersSizeLength = leb128.getLength();
parameterNames = new int[ parametersSize ];
parameterNamesLengths = new int[ parametersSize ];
for ( int i = 0 ; i < parametersSize ; ++i ) {
int value = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int valueLength = Leb128.unsignedLeb128Size( value );
reader.readNextByteArray( valueLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
parameterNames[ i ] = value - 1;// uleb128p1
parameterNamesLengths[ i ] = valueLength;
parameterNames[i] = leb128.asUInt32() - 1;// uleb128p1
parameterNamesLengths[i] = leb128.getLength();
}
long startIndex = reader.getPointerIndex( );
int count = DebugInfoStateMachineReader.computeLength( reader );
reader.setPointerIndex( startIndex );
int count = DebugInfoStateMachineReader.computeLength( reader.clone() );
stateMachineOpcodes = reader.readNextByteArray( count );
}
@@ -15,102 +15,64 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.dex.util.Leb128;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
class DebugInfoStateMachineReader {
private static final int MAX_SIZE = 0x10000; // 64k
static int computeLength( BinaryReader reader ) throws IOException {
int length = 0;
long start = reader.getPointerIndex();
while ( true ) {
if ( length > 0x10000 ) {//don't loop forever!
return 0;
}
while (reader.getPointerIndex() - start < MAX_SIZE) {
byte opcode = reader.readNextByte( );
++length;
switch( opcode ) {
case DebugStateMachineOpCodes.DBG_END_SEQUENCE: {
return length;//done!
return (int) (reader.getPointerIndex() - start);//done!
}
case DebugStateMachineOpCodes.DBG_ADVANCE_PC: {
int advance = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int advanceLength = Leb128.unsignedLeb128Size( advance );
reader.setPointerIndex( reader.getPointerIndex( ) + advanceLength );
length += advanceLength;
LEB128.readAsUInt32(reader);
break;
}
case DebugStateMachineOpCodes.DBG_ADVANCE_LINE: {
int advance = Leb128.readSignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int advanceLength = Leb128.signedLeb128Size( advance );
reader.setPointerIndex( reader.getPointerIndex( ) + advanceLength );
length += advanceLength;
LEB128.readAsUInt32(reader);
break;
}
case DebugStateMachineOpCodes.DBG_START_LOCAL: {
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int registerLength = Leb128.unsignedLeb128Size( register );
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
length += registerLength;
int register = LEB128.readAsUInt32(reader);
//TODO uleb128p1
int name = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int nameLength = Leb128.unsignedLeb128Size( name );
reader.setPointerIndex( reader.getPointerIndex( ) + nameLength );
length += nameLength;
int name = LEB128.readAsUInt32(reader);
//TODO uleb128p1
int type = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int typeLength = Leb128.unsignedLeb128Size( type );
reader.setPointerIndex( reader.getPointerIndex( ) + typeLength );
length += typeLength;
int type = LEB128.readAsUInt32(reader);
break;
}
case DebugStateMachineOpCodes.DBG_START_LOCAL_EXTENDED: {
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int registerLength = Leb128.unsignedLeb128Size( register );
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
length += registerLength;
int register = LEB128.readAsUInt32(reader);
//TODO uleb128p1
int name = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int nameLength = Leb128.unsignedLeb128Size( name );
reader.setPointerIndex( reader.getPointerIndex( ) + nameLength );
length += nameLength;
int name = LEB128.readAsUInt32(reader);
//TODO uleb128p1
int type = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int typeLength = Leb128.unsignedLeb128Size( type );
reader.setPointerIndex( reader.getPointerIndex( ) + typeLength );
length += typeLength;
int type = LEB128.readAsUInt32(reader);
//TODO uleb128p1
int signature = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int signatureLength = Leb128.unsignedLeb128Size( signature );
reader.setPointerIndex( reader.getPointerIndex( ) + signatureLength );
length += signatureLength;
int signature = LEB128.readAsUInt32(reader);
break;
}
case DebugStateMachineOpCodes.DBG_END_LOCAL: {
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int registerLength = Leb128.unsignedLeb128Size( register );
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
length += registerLength;
int register = LEB128.readAsUInt32(reader);
break;
}
case DebugStateMachineOpCodes.DBG_RESTART_LOCAL: {
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int registerLength = Leb128.unsignedLeb128Size( register );
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
length += registerLength;
int register = LEB128.readAsUInt32(reader);
break;
}
case DebugStateMachineOpCodes.DBG_SET_PROLOGUE_END: {
@@ -121,10 +83,7 @@ class DebugInfoStateMachineReader {
}
case DebugStateMachineOpCodes.DBG_SET_FILE: {
//TODO uleb128p1
int name = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
int nameLength = Leb128.unsignedLeb128Size( name );
reader.setPointerIndex( reader.getPointerIndex( ) + nameLength );
length += nameLength;
int name = LEB128.readAsUInt32(reader);
break;
}
default: {
@@ -132,5 +91,7 @@ class DebugInfoStateMachineReader {
}
}
}
return 0;
}
}
@@ -15,31 +15,31 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class EncodedAnnotation implements StructConverter {
private int typeIndex;
private int typeIndexLength;// in bytes
private int size;
private int sizeLength;// in bytes
private List< AnnotationElement > elements = new ArrayList< AnnotationElement >( );
private List< AnnotationElement > elements = new ArrayList< >( );
public EncodedAnnotation( BinaryReader reader ) throws IOException {
typeIndex = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
typeIndexLength = Leb128.unsignedLeb128Size( typeIndex );
reader.readNextByteArray( typeIndexLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
typeIndex = leb128.asUInt32();
typeIndexLength = leb128.getLength();
size = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
sizeLength = Leb128.unsignedLeb128Size( size );
reader.readNextByteArray( sizeLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
size = leb128.asUInt32();
sizeLength = leb128.getLength();
for ( int i = 0 ; i < size ; ++i ) {
elements.add( new AnnotationElement( reader ) );
@@ -21,7 +21,7 @@ import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -33,18 +33,16 @@ public class EncodedArray implements StructConverter {
private byte [] values;
public EncodedArray( BinaryReader reader ) throws IOException {
size = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
sizeLength = Leb128.unsignedLeb128Size( size );
reader.readNextByteArray( sizeLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
size = leb128.asUInt32();
sizeLength = leb128.getLength();
long oldIndex = reader.getPointerIndex( );
List< EncodedValue > valuesList = new ArrayList< EncodedValue >( );
BinaryReader evReader = reader.clone();
List< EncodedValue > valuesList = new ArrayList< >( );
for ( int i = 0 ; i < size ; ++i ) {
valuesList.add( new EncodedValue( reader ) );
valuesList.add(new EncodedValue(evReader));
}
int nBytes = (int) ( reader.getPointerIndex() - oldIndex );
reader.setPointerIndex(oldIndex);
int nBytes = (int) (evReader.getPointerIndex() - reader.getPointerIndex());
values = reader.readNextByteArray(nBytes); // Re-read the encoded values as a byte array
}
@@ -15,41 +15,37 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class EncodedCatchHandler implements StructConverter {
private int size;
private int sizeLength;// in bytes
private List< EncodedTypeAddressPair > handlers = new ArrayList< EncodedTypeAddressPair >( );
private List< EncodedTypeAddressPair > handlers = new ArrayList< >( );
private int catchAllAddress;
private int catchAllAddressLength;
public EncodedCatchHandler( BinaryReader reader ) throws IOException {
size = Leb128.readSignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
sizeLength = Leb128.signedLeb128Size( size );
reader.readNextByteArray( sizeLength );// consume leb...
LEB128 leb128 = LEB128.readSignedValue(reader);
size = leb128.asInt32();
sizeLength = leb128.getLength();
for ( int i = 0 ; i < Math.abs( size ) ; ++i ) {
handlers.add( new EncodedTypeAddressPair( reader ) );
}
if ( size <= 0 ) {// This element is only present if size is non-positive.
catchAllAddress = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
catchAllAddressLength = Leb128.unsignedLeb128Size( catchAllAddress );
reader.readNextByteArray( catchAllAddressLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
catchAllAddress = leb128.asUInt32();
catchAllAddressLength = leb128.getLength();
}
}
@@ -15,30 +15,26 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class EncodedCatchHandlerList implements StructConverter {
private int size;
private int sizeLength;// in bytes
private List< EncodedCatchHandler > handlers = new ArrayList< EncodedCatchHandler >( );
private List< EncodedCatchHandler > handlers = new ArrayList< >( );
public EncodedCatchHandlerList( BinaryReader reader ) throws IOException {
size = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
sizeLength = Leb128.unsignedLeb128Size( size );
reader.readNextByteArray( sizeLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
size = leb128.asUInt32();
sizeLength = leb128.getLength();
for ( int i = 0 ; i < size ; ++i ) {
handlers.add( new EncodedCatchHandler( reader ) );
@@ -15,18 +15,14 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class EncodedField implements StructConverter {
private long _fileOffset;
@@ -38,15 +34,15 @@ public class EncodedField implements StructConverter {
private int accessFlagsLength;// in bytes
public EncodedField( BinaryReader reader ) throws IOException {
_fileOffset = reader.getPointerIndex( );
fieldIndexDifference = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
fieldIndexDifferenceLength = Leb128.unsignedLeb128Size( fieldIndexDifference );
reader.readNextByteArray( fieldIndexDifferenceLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
_fileOffset = leb128.getOffset();
fieldIndexDifference = leb128.asUInt32();
fieldIndexDifferenceLength = leb128.getLength();
accessFlags = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
accessFlagsLength = Leb128.unsignedLeb128Size( accessFlags );
reader.readNextByteArray( accessFlagsLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
accessFlags = leb128.asUInt32();
accessFlagsLength = leb128.getLength();
}
public long getFileOffset( ) {
@@ -15,14 +15,14 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class EncodedMethod implements StructConverter {
private long _fileOffset;
@@ -39,29 +39,22 @@ public class EncodedMethod implements StructConverter {
private CodeItem codeItem;
public EncodedMethod( BinaryReader reader ) throws IOException {
_fileOffset = reader.getPointerIndex( );
methodIndexDifference = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
methodIndexDifferenceLength = Leb128.unsignedLeb128Size( methodIndexDifference );
reader.readNextByteArray( methodIndexDifferenceLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
_fileOffset = leb128.getOffset();
methodIndexDifference = leb128.asUInt32();
methodIndexDifferenceLength = leb128.getLength();
accessFlags = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
accessFlagsLength = Leb128.unsignedLeb128Size( accessFlags );
reader.readNextByteArray( accessFlagsLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
accessFlags = leb128.asUInt32();
accessFlagsLength = leb128.getLength();
codeOffset = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
codeOffsetLength = Leb128.unsignedLeb128Size( codeOffset );
reader.readNextByteArray( codeOffsetLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
codeOffset = leb128.asUInt32();
codeOffsetLength = leb128.getLength();
if ( codeOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
try {
reader.setPointerIndex( codeOffset );
codeItem = new CodeItem( reader );
}
finally {
reader.setPointerIndex( oldIndex );
}
codeItem = new CodeItem( reader.clone(codeOffset) );
}
}
@@ -15,18 +15,14 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class EncodedTypeAddressPair implements StructConverter {
private int typeIndex;
@@ -36,13 +32,13 @@ public class EncodedTypeAddressPair implements StructConverter {
private int addressLength;// in bytes
public EncodedTypeAddressPair( BinaryReader reader ) throws IOException {
typeIndex = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
typeIndexLength = Leb128.unsignedLeb128Size( typeIndex );
reader.readNextByteArray( typeIndexLength );// consume leb...
LEB128 leb128 = LEB128.readUnsignedValue(reader);
typeIndex = leb128.asUInt32();
typeIndexLength = leb128.getLength();
address = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
addressLength = Leb128.unsignedLeb128Size( address );
reader.readNextByteArray( addressLength );// consume leb...
leb128 = LEB128.readUnsignedValue(reader);
address = leb128.asUInt32();
addressLength = leb128.getLength();
}
public int getTypeIndex( ) {
@@ -15,52 +15,41 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class StringDataItem implements StructConverter {
private static final int MAX_STRING_LEN = 0x200000; // 2Mb'ish
private int stringLength;
private int lebLength;
private int actualLength;
private String string;
public StringDataItem( StringIDItem stringItem, BinaryReader reader ) throws IOException {
long oldIndex = reader.getPointerIndex( );
try {
reader.setPointerIndex( stringItem.getStringDataOffset( ) );
stringLength = Leb128.readUnsignedLeb128( reader.readByteArray( stringItem.getStringDataOffset( ), 5 ) );
public StringDataItem(StringIDItem stringItem, BinaryReader reader) throws IOException {
reader = reader.clone(stringItem.getStringDataOffset());
LEB128 leb128 = LEB128.readUnsignedValue(reader);
stringLength = leb128.asUInt32();
lebLength = leb128.getLength();
long nullTermIndex =
getIndexOfByteValue(reader, reader.getPointerIndex(), MAX_STRING_LEN, (byte) 0);
actualLength = (int) (nullTermIndex - reader.getPointerIndex() + 1);
byte[] stringBytes = reader.readNextByteArray(actualLength);
lebLength = Leb128.unsignedLeb128Size( stringLength );
ByteArrayInputStream in = new ByteArrayInputStream(stringBytes);
reader.readNextByteArray( lebLength );// consume leb...
char[] out = new char[stringLength];
actualLength = computeActualLength( reader );
byte [] stringBytes = reader.readNextByteArray( actualLength );
ByteArrayInputStream in = new ByteArrayInputStream( stringBytes );
char [] out = new char[ stringLength ];
string = ModifiedUTF8.decode( in, out );
}
finally {
reader.setPointerIndex( oldIndex );
}
string = ModifiedUTF8.decode(in, out);
}
public String getString( ) {
public String getString() {
return string;
}
@@ -73,15 +62,18 @@ public class StringDataItem implements StructConverter {
return structure;
}
private int computeActualLength( BinaryReader reader ) throws IOException {
int count = 0;
while ( count < 0x200000 ) {// don't run forever!
if ( reader.readByte( reader.getPointerIndex( ) + count ) == 0x0 ) {
break;
private static long getIndexOfByteValue(BinaryReader reader, long startIndex, int maxLen,
byte byteValueToFind) throws IOException {
long maxIndex = startIndex + maxLen;
long currentIndex = startIndex;
while (currentIndex < maxIndex) {
byte b = reader.readByte(currentIndex);
if (b == byteValueToFind) {
return currentIndex;
}
++count;
currentIndex++;
}
return count + 1;
return currentIndex;
}
}
@@ -1,202 +0,0 @@
/* ###
* IP: Apache License 2.0
*/
/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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.
*/
package ghidra.file.formats.android.dex.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import com.googlecode.d2j.DexException;
import ghidra.util.NumericUtilities;
/**
* Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3 section 7.6.
*/
public final class Leb128 {
private Leb128() {
}
/**
* Gets the number of bytes in the unsigned LEB128 encoding of the given value.
*
* @param value
* the value in question
* @return its write size, in bytes
*/
public static int unsignedLeb128Size( int value ) {
// TODO: This could be much cleverer.
int remaining = value >> 7;
int count = 0;
while ( remaining != 0 ) {
remaining >>= 7;
count++;
}
return count + 1;
}
/**
* Gets the number of bytes in the signed LEB128 encoding of the given value.
*
* @param value
* the value in question
* @return its write size, in bytes
*/
public static int signedLeb128Size( int value ) {
// TODO: This could be much cleverer.
int remaining = value >> 7;
int count = 0;
boolean hasMore = true;
int end = ( ( value & Integer.MIN_VALUE ) == 0 ) ? 0 : -1;
while ( hasMore ) {
hasMore = ( remaining != end ) || ( ( remaining & 1 ) != ( ( value >> 6 ) & 1 ) );
value = remaining;
remaining >>= 7;
count++;
}
return count;
// ByteArrayOutputStream out = new ByteArrayOutputStream( );
// int remaining = value >>> 7;
//
// while ( remaining != 0 ) {
// out.write( ( byte ) ( ( value & 0x7f ) | 0x80 ) );
// value = remaining;
// remaining >>>= 7;
// }
//
// out.write( ( byte ) ( value & 0x7f ) );
//
// return out.toByteArray( ).length;
}
public static int readSignedLeb128( byte [] bytes ) {
return readSignedLeb128( new ByteArrayInputStream( bytes ) );
}
/**
* Reads an signed integer from {@code in}.
*/
public static int readSignedLeb128( ByteArrayInputStream in ) {
int result = 0;
int cur;
int count = 0;
int signBits = -1;
do {
cur = in.read( ) & 0xff;
result |= ( cur & 0x7f ) << ( count * 7 );
signBits <<= 7;
count++;
}
while ( ( ( cur & 0x80 ) == 0x80 ) && count < 5 );
if ( ( cur & 0x80 ) == 0x80 ) {
throw new DexException( "invalid LEB128 sequence" );
}
// Sign extend if appropriate
if ( ( ( signBits >> 1 ) & result ) != 0 ) {
result |= signBits;
}
return result;
}
public static int readUnsignedLeb128( byte [] bytes ) {
return readUnsignedLeb128( new ByteArrayInputStream( bytes ) );
}
/**
* Reads an unsigned integer from {@code in}.
*/
public static int readUnsignedLeb128( ByteArrayInputStream in ) {
int result = 0;
int cur;
int count = 0;
do {
cur = in.read( ) & 0xff;
result |= ( cur & 0x7f ) << ( count * 7 );
count++;
}
while ( ( ( cur & 0x80 ) == 0x80 ) && count < 5 );
if ( ( cur & 0x80 ) == 0x80 ) {
throw new DexException( "invalid LEB128 sequence" );
}
return result;
}
/**
* Writes {@code value} as an unsigned integer to {@code out}, starting at {@code offset}. Returns the number of bytes written.
*/
public static void writeUnsignedLeb128( ByteArrayOutputStream out, int value ) {
int remaining = value >>> 7;
while ( remaining != 0 ) {
out.write( ( byte ) ( ( value & 0x7f ) | 0x80 ) );
value = remaining;
remaining >>>= 7;
}
out.write( ( byte ) ( value & 0x7f ) );
}
/**
* Writes {@code value} as a signed integer to {@code out}, starting at {@code offset}. Returns the number of bytes written.
*/
public static void writeSignedLeb128( ByteArrayOutputStream out, int value ) {
int remaining = value >> 7;
boolean hasMore = true;
int end = ( ( value & Integer.MIN_VALUE ) == 0 ) ? 0 : -1;
while ( hasMore ) {
hasMore = ( remaining != end ) || ( ( remaining & 1 ) != ( ( value >> 6 ) & 1 ) );
out.write( ( byte ) ( ( value & 0x7f ) | ( hasMore ? 0x80 : 0 ) ) );
value = remaining;
remaining >>= 7;
}
}
public static void main( String [] args ) {
//ByteArrayOutputStream out = new ByteArrayOutputStream( );
//writeSignedLeb128( out, -12345 );
//System.out.println( "array length: " + out.toByteArray( ).length );
//System.out.println( "actual length: " + signedLeb128Size( -12345 ) );
//System.out.println( readSignedLeb128( out.toByteArray( ) ) );
System.out.println( readUnsignedLeb128( NumericUtilities.convertStringToBytes("807f" ) ) );
}
}
@@ -1,5 +1,6 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +52,7 @@ public class RttiModelTest extends AbstractRttiTest {
setupRtti4_32(builder, 0x01001340L, 0, 0, 0, "0x01005364", "0x0100137c");
Address address = builder.addr(0x01001340L);
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
"No vf table pointer is defined for this TypeDescriptor model.");
"TypeDescriptor data type at 01005364 doesn't point to a vfTable address in a loaded and initialized memory block.");
}
@Test
@@ -62,7 +63,7 @@ public class RttiModelTest extends AbstractRttiTest {
setupRtti0_32(builder, 0x01001364, "0x01007700", "0x0", "stuff");
Address address = builder.addr(0x01001340L);
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
"No vf table pointer is defined for this TypeDescriptor model.");
"TypeDescriptor data type at 01001364 doesn't point to a vfTable address in a loaded and initialized memory block.");
}
@Test
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +30,13 @@ public class DockingCheckBoxMenuItem extends JCheckBoxMenuItem {
@Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
return true; // we will take care of the action ourselves
// TODO this note doesn't really make sense. I think this idea is outdated. Leaving this
// here for a bit, in case there is something we missed. This code is also in
// DockingMenuItem.
// return true; // we will take care of the action ourselves
// Our KeyBindingOverrideKeyEventDispatcher processes actions for us, so there is no
// need to have the menu item do it
return false;
}
}
@@ -33,7 +33,8 @@ public class DockingMenuItem extends JMenuItem implements GComponent {
@Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
// TODO this note doesn't really make sense. I think this idea is outdated. Leaving this
// here for a bit, in case there is something we missed
// here for a bit, in case there is something we missed. This code is also in
// DockingCheckboxMenuItemUI.
// return true; // we will take care of the action ourselves
// Our KeyBindingOverrideKeyEventDispatcher processes actions for us, so there is no
@@ -2724,6 +2724,7 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public Set<Function> getCallingFunctions(TaskMonitor monitor) {
monitor = TaskMonitor.dummyIfNull(monitor);
Set<Function> set = new HashSet<>();
ReferenceIterator iter = program.getReferenceManager().getReferencesTo(getEntryPoint());
while (iter.hasNext()) {
@@ -2742,6 +2743,7 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public Set<Function> getCalledFunctions(TaskMonitor monitor) {
monitor = TaskMonitor.dummyIfNull(monitor);
Set<Function> set = new HashSet<>();
Set<Reference> references = getReferencesFromBody(monitor);
for (Reference reference : references) {
@@ -32,6 +32,16 @@ public interface TaskMonitor {
public static final TaskMonitor DUMMY = new StubTaskMonitor();
/**
* Returns the given task monitor if it is not {@code null}. Otherwise, a {@link #DUMMY}
* monitor is returned.
* @param tm the monitor to check for {@code null}
* @return a non-null task monitor
*/
public static TaskMonitor dummyIfNull(TaskMonitor tm) {
return tm == null ? DUMMY : tm;
}
/** A value to indicate that this monitor has no progress value set */
public static final int NO_PROGRESS_VALUE = -1;

Some files were not shown because too many files have changed in this diff Show More