mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 18:26:53 +08:00
Merge remote-tracking branch 'origin/master' into debugger
This commit is contained in:
@@ -450,6 +450,18 @@
|
|||||||
|
|
||||||
<P><U>Started By:</U> Importing or adding to a program, Auto Analyze command</P>
|
<P><U>Started By:</U> Importing or adding to a program, Auto Analyze command</P>
|
||||||
</BLOCKQUOTE>
|
</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>
|
<H3><A name="Image"></A>Image Analyzer</H3>
|
||||||
|
|
||||||
|
|||||||
@@ -364,7 +364,7 @@
|
|||||||
<H3>Intel Hex Options<A name="Options_Intel_Hex"/></H3>
|
<H3>Intel Hex Options<A name="Options_Intel_Hex"/></H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<H4>Bass Address</H4>
|
<H4>Base Address</H4>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>This field is used to specify the start address in memory for where to load the
|
<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>
|
<H3>Motorola Hex Options<A name="Options_Motorola_Hex"/></H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<H4>Bass Address</H4>
|
<H4>Base Address</H4>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>This field is used to specify the start address in memory for where to load the
|
<P>This field is used to specify the start address in memory for where to load the
|
||||||
|
|||||||
+14
-4
@@ -27,7 +27,7 @@ import ghidra.program.model.address.AddressSetView;
|
|||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
||||||
@@ -67,7 +67,7 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
|||||||
try {
|
try {
|
||||||
dtm = service.openDataTypeArchive(archiveName);
|
dtm = service.openDataTypeArchive(archiveName);
|
||||||
if (dtm == null) {
|
if (dtm == null) {
|
||||||
Msg.showError(this, null, "Failed to Apply Data Types",
|
log.appendMsg("Apply Data Archives",
|
||||||
"Failed to locate data type archive: " + archiveName);
|
"Failed to locate data type archive: " + archiveName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -75,8 +75,18 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
if (mgr.debugOn) {
|
Throwable cause = e.getCause();
|
||||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -438,9 +438,7 @@ public class DataTypeManagerHandler {
|
|||||||
addArchive(archive);
|
addArchive(archive);
|
||||||
}
|
}
|
||||||
if (isUserAction && (archive instanceof FileArchive)) {
|
if (isUserAction && (archive instanceof FileArchive)) {
|
||||||
if (file != null) {
|
userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath()));
|
||||||
userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return archive;
|
return archive;
|
||||||
}
|
}
|
||||||
@@ -1251,8 +1249,8 @@ public class DataTypeManagerHandler {
|
|||||||
|
|
||||||
public Set<String> getPossibleEquateNames(long value) {
|
public Set<String> getPossibleEquateNames(long value) {
|
||||||
Set<String> equateNames = new HashSet<>();
|
Set<String> equateNames = new HashSet<>();
|
||||||
for (int i = 0; i < openArchives.size(); i++) {
|
for (Archive element : openArchives) {
|
||||||
DataTypeManager dtMgr = openArchives.get(i).getDataTypeManager();
|
DataTypeManager dtMgr = element.getDataTypeManager();
|
||||||
dtMgr.findEnumValueNames(value, equateNames);
|
dtMgr.findEnumValueNames(value, equateNames);
|
||||||
}
|
}
|
||||||
return equateNames;
|
return equateNames;
|
||||||
|
|||||||
@@ -74,11 +74,20 @@ public class BinaryReader {
|
|||||||
*/
|
*/
|
||||||
public BinaryReader clone(long newIndex) {
|
public BinaryReader clone(long newIndex) {
|
||||||
BinaryReader clone = new BinaryReader(provider, isLittleEndian());
|
BinaryReader clone = new BinaryReader(provider, isLittleEndian());
|
||||||
clone.converter = converter;
|
|
||||||
clone.currentIndex = newIndex;
|
clone.currentIndex = newIndex;
|
||||||
return clone;
|
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,
|
* Returns true if this reader will extract values in little endian,
|
||||||
* otherwise in big endian.
|
* otherwise in big endian.
|
||||||
|
|||||||
+10
-10
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.LEB128;
|
||||||
|
|
||||||
public class FileEntry {
|
public class FileEntry {
|
||||||
private String fileName;
|
private String fileName;
|
||||||
private LEB128 directoryIndex;
|
private long directoryIndex;
|
||||||
private LEB128 lastModifiedTime;
|
private long lastModifiedTime;
|
||||||
private LEB128 fileLengthInBytes;
|
private long fileLengthInBytes;
|
||||||
|
|
||||||
FileEntry(BinaryReader reader) throws IOException {
|
FileEntry(BinaryReader reader) throws IOException {
|
||||||
fileName = reader.readNextAsciiString();
|
fileName = reader.readNextAsciiString();
|
||||||
if (fileName.length() == 0) {
|
if (fileName.length() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
directoryIndex = new LEB128(reader, false);
|
directoryIndex = LEB128.readAsLong(reader, false);
|
||||||
lastModifiedTime = new LEB128(reader, false);
|
lastModifiedTime = LEB128.readAsLong(reader, false);
|
||||||
fileLengthInBytes = new LEB128(reader, false);
|
fileLengthInBytes = LEB128.readAsLong(reader, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFileName() {
|
public String getFileName() {
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
public LEB128 getDirectoryIndex() {
|
public long getDirectoryIndex() {
|
||||||
return directoryIndex;
|
return directoryIndex;
|
||||||
}
|
}
|
||||||
public LEB128 getLastModifiedTime() {
|
public long getLastModifiedTime() {
|
||||||
return lastModifiedTime;
|
return lastModifiedTime;
|
||||||
}
|
}
|
||||||
public LEB128 getFileLengthInBytes() {
|
public long getFileLengthInBytes() {
|
||||||
return fileLengthInBytes;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+11
-10
@@ -16,6 +16,7 @@
|
|||||||
package ghidra.app.util.bin.format.dwarf.line;
|
package ghidra.app.util.bin.format.dwarf.line;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.LEB128;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ public final class StatementProgramInstructions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void executeExtended(int opcode) throws IOException {
|
private void executeExtended(int opcode) throws IOException {
|
||||||
LEB128 length = new LEB128(reader, false);
|
long length = LEB128.readAsLong(reader, false);
|
||||||
|
|
||||||
long oldIndex = reader.getPointerIndex();
|
long oldIndex = reader.getPointerIndex();
|
||||||
int extendedOpcode = reader.readNextByte();
|
int extendedOpcode = reader.readNextByte();
|
||||||
@@ -110,7 +111,7 @@ public final class StatementProgramInstructions {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldIndex + length.getValue() != reader.getPointerIndex()) {
|
if (oldIndex + length != reader.getPointerIndex()) {
|
||||||
throw new IllegalStateException("Index values do not match!");
|
throw new IllegalStateException("Index values do not match!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,23 +123,23 @@ public final class StatementProgramInstructions {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LNS_advance_pc: {
|
case DW_LNS_advance_pc: {
|
||||||
LEB128 value = new LEB128(reader, false);
|
long value = LEB128.readAsLong(reader, false);
|
||||||
machine.address += (value.getValue() * prologue.getMinimumInstructionLength());
|
machine.address += (value * prologue.getMinimumInstructionLength());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LNS_advance_line: {
|
case DW_LNS_advance_line: {
|
||||||
LEB128 value = new LEB128(reader, false);
|
long value = LEB128.readAsLong(reader, false);
|
||||||
machine.line += value.getValue();
|
machine.line += value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LNS_set_file: {
|
case DW_LNS_set_file: {
|
||||||
LEB128 value = new LEB128(reader, false);
|
long value = LEB128.readAsLong(reader, false);
|
||||||
machine.file = (int) value.getValue();
|
machine.file = (int) value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LNS_set_column: {
|
case DW_LNS_set_column: {
|
||||||
LEB128 value = new LEB128(reader, false);
|
long value = LEB128.readAsLong(reader, false);
|
||||||
machine.column = (int) value.getValue();
|
machine.column = (int) value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LNS_negate_statement: {
|
case DW_LNS_negate_statement: {
|
||||||
|
|||||||
+3
-3
@@ -168,10 +168,10 @@ public class StatementProgramPrologue {
|
|||||||
* @param directoryIndex the directory index
|
* @param directoryIndex the directory index
|
||||||
* @return the directory or current directory
|
* @return the directory or current directory
|
||||||
*/
|
*/
|
||||||
public String getDirectoryByIndex(LEB128 directoryIndex) {
|
public String getDirectoryByIndex(long directoryIndex) {
|
||||||
if (directoryIndex.getValue() == 0) {
|
if (directoryIndex == 0) {
|
||||||
return ".";
|
return ".";
|
||||||
}
|
}
|
||||||
return includeDirectories.get((int)directoryIndex.getValue() - 1);
|
return includeDirectories.get((int)directoryIndex - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -43,11 +43,11 @@ public class DWARFAbbreviation
|
|||||||
TaskMonitor monitor)
|
TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
int ac = LEB128.decode32u(reader);
|
int ac = LEB128.readAsUInt32(reader);
|
||||||
if (ac == 0) {
|
if (ac == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int tag = LEB128.decode32u(reader);
|
int tag = LEB128.readAsUInt32(reader);
|
||||||
DWARFChildren hasChildren = DWARFChildren.find((int) reader.readNextByte());
|
DWARFChildren hasChildren = DWARFChildren.find((int) reader.readNextByte());
|
||||||
|
|
||||||
// Read each attribute specification until attribute and its value is 0
|
// Read each attribute specification until attribute and its value is 0
|
||||||
|
|||||||
+2
-2
@@ -38,8 +38,8 @@ public class DWARFAttributeSpecification {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static DWARFAttributeSpecification read(BinaryReader reader) throws IOException {
|
public static DWARFAttributeSpecification read(BinaryReader reader) throws IOException {
|
||||||
int attribute = LEB128.decode32u(reader);
|
int attribute = LEB128.readAsUInt32(reader);
|
||||||
DWARFForm attributeForm = DWARFForm.find(LEB128.decode32u(reader));
|
DWARFForm attributeForm = DWARFForm.find(LEB128.readAsUInt32(reader));
|
||||||
|
|
||||||
return attribute != 0 && attributeForm != DWARFForm.NULL
|
return attribute != 0 && attributeForm != DWARFForm.NULL
|
||||||
? new DWARFAttributeSpecification(attribute, attributeForm) : 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
|
// This entry exists only if the length of the string is more than 0
|
||||||
if (this.name.length() > 0) {
|
if (this.name.length() > 0) {
|
||||||
this.directory_index = LEB128.decode(reader, false);
|
this.directory_index = LEB128.readAsLong(reader, false);
|
||||||
this.modification_time = LEB128.decode(reader, false);
|
this.modification_time = LEB128.readAsLong(reader, false);
|
||||||
this.length = LEB128.decode(reader, false);
|
this.length = LEB128.readAsLong(reader, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ public class DebugInfoEntry {
|
|||||||
public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit unit,
|
public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit unit,
|
||||||
DWARFAttributeFactory attributeFactory) throws IOException {
|
DWARFAttributeFactory attributeFactory) throws IOException {
|
||||||
long offset = reader.getPointerIndex();
|
long offset = reader.getPointerIndex();
|
||||||
int abbreviationCode = LEB128.decode32u(reader);
|
int abbreviationCode = LEB128.readAsUInt32(reader);
|
||||||
|
|
||||||
// Check for terminator DIE
|
// Check for terminator DIE
|
||||||
if (abbreviationCode == 0) {
|
if (abbreviationCode == 0) {
|
||||||
|
|||||||
@@ -18,122 +18,195 @@ package ghidra.app.util.bin.format.dwarf4;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.util.NumberUtil;
|
import ghidra.app.util.bin.ByteArrayProvider;
|
||||||
public final class LEB128 {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a LEB128 signed number and return it as a java 32 bit int.
|
* Class to hold result of reading a LEB128 value, along with size and position metadata.
|
||||||
* <p>
|
* <p>
|
||||||
* If the value of the number can not fit in the int type, an {@link IOException} will
|
* Note: If a LEB128 value that would result in a native value longer than 64bits is attempted to
|
||||||
* be thrown.
|
* be read, an {@link IOException} will be thrown, and the stream's position will be left at the last read byte.
|
||||||
*
|
* <p>
|
||||||
* @param reader
|
* If this was a valid (but overly large) LEB128, the caller's stream will be left still pointing to LEB data.
|
||||||
* @return
|
* <p>
|
||||||
* @throws IOException
|
*/
|
||||||
*/
|
public class LEB128 {
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
* Returns the value as an unsigned int32. If the actual value
|
||||||
* <p>
|
* is outside the positive range of a java int (ie. 0.. {@link Integer#MAX_VALUE}),
|
||||||
* If the value of the number can not fit in the positive range of the int type,
|
* an exception is thrown.
|
||||||
* an {@link IOException} will be thrown.
|
|
||||||
*
|
*
|
||||||
* @param reader
|
* @return int in the range of 0 to {@link Integer#MAX_VALUE}
|
||||||
* @return
|
* @throws IOException if value is outside range
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static int decode32u(BinaryReader reader) throws IOException {
|
public int asUInt32() throws IOException {
|
||||||
long tmp = decode(reader, false);
|
ensureInt32u(value);
|
||||||
|
return (int) value;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a LEB128 number using a binary reader and stores it in a long.
|
* Returns the value as an signed int32. If the actual value
|
||||||
* <p>
|
* is outside the range of a java int (ie. {@link Integer#MIN_VALUE}.. {@link Integer#MAX_VALUE}),
|
||||||
* Large unsigned integers that use 64 bits will be returned in java's native
|
* an exception is thrown.
|
||||||
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
|
*
|
||||||
* <p>
|
* @return int in the range of {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}
|
||||||
* Large integers that use more than 64 bits will cause an IOException to be thrown.
|
* @throws IOException if value is outside range
|
||||||
* <p>
|
|
||||||
* @param reader the binary reader
|
|
||||||
* @param isSigned true if the value is signed
|
|
||||||
* @throws IOException if an I/O error occurs
|
|
||||||
*/
|
*/
|
||||||
public static long decode(BinaryReader reader, boolean isSigned) throws IOException {
|
public int asInt32() throws IOException {
|
||||||
int nextByte = 0;
|
ensureInt32s(value);
|
||||||
int shift = 0;
|
return (int) value;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a LEB128 number using a byte array and stores it in a long.
|
* Returns the offset of the LEB128 value in the stream it was read from.
|
||||||
* This function cannot read numbers larger than Long.MAX_VALUE.
|
*
|
||||||
* @param bytes the bytes representing the LEB128 number
|
* @return stream offset of the LEB128 value
|
||||||
* @param isSigned true if the value is signed
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static long decode(byte[] bytes, boolean isSigned) throws IOException {
|
public long getOffset() {
|
||||||
return decode(bytes, 0, isSigned);
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a LEB128 number using an offset into a byte array and stores it in a long.
|
* Returns the number of bytes that were used to store the LEB128 value in the stream
|
||||||
* This function cannot read numbers larger than Long.MAX_VALUE.
|
* it was read from.
|
||||||
* @param bytes the bytes representing the LEB128 number
|
*
|
||||||
* @param offset offset into the byte array
|
* @return number of bytes used to store the read LEB128 value
|
||||||
* @param isSigned true if the value is signed
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
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 nextByte = 0;
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
long value = 0;
|
long value = 0;
|
||||||
for (int i = offset; i < bytes.length; i++) {
|
while (true) {
|
||||||
nextByte = bytes[i] & NumberUtil.UNSIGNED_BYTE_MASK;
|
nextByte = reader.readNextUnsignedByte();
|
||||||
|
|
||||||
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
|
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
"Unsupported LEB128 value, too large to fit in 64bit java long variable");
|
"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
|
// must cast to long before shifting otherwise shift values greater than 32 cause problems
|
||||||
value |= ((long) (nextByte & 0x7F)) << shift;
|
value |= ((long) (nextByte & 0x7F)) << shift;
|
||||||
|
|
||||||
shift += 7;
|
shift += 7;
|
||||||
|
|
||||||
if ((nextByte & 0x80) == 0) {
|
if ((nextByte & 0x80) == 0) {
|
||||||
@@ -149,11 +221,62 @@ public final class LEB128 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
|
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
|
||||||
long tmp1 = (1L << shift);
|
// 0x40 is the new 'high' sign bit since 0x80 is the continuation flag
|
||||||
long tmp2 = -tmp1;
|
value |= (-1L << shift);
|
||||||
value |= tmp2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -73,7 +73,7 @@ public class DWARFAttributeFactory {
|
|||||||
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
|
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
|
||||||
}
|
}
|
||||||
case DW_FORM_ref_udata: {
|
case DW_FORM_ref_udata: {
|
||||||
long uoffset = LEB128.decode(reader, false);
|
long uoffset = LEB128.readAsLong(reader, false);
|
||||||
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
|
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ public class DWARFAttributeFactory {
|
|||||||
return new DWARFBlobAttribute(reader.readNextByteArray(length));
|
return new DWARFBlobAttribute(reader.readNextByteArray(length));
|
||||||
}
|
}
|
||||||
case DW_FORM_block: {
|
case DW_FORM_block: {
|
||||||
int length = LEB128.decode32u(reader);
|
int length = LEB128.readAsUInt32(reader);
|
||||||
if (length < 0 || length > MAX_BLOCK4_SIZE) {
|
if (length < 0 || length > MAX_BLOCK4_SIZE) {
|
||||||
throw new IOException("Invalid/bad dw_form_block size: " + length);
|
throw new IOException("Invalid/bad dw_form_block size: " + length);
|
||||||
}
|
}
|
||||||
@@ -121,12 +121,12 @@ public class DWARFAttributeFactory {
|
|||||||
case DW_FORM_data8:
|
case DW_FORM_data8:
|
||||||
return new DWARFNumericAttribute(reader.readNextLong());
|
return new DWARFNumericAttribute(reader.readNextLong());
|
||||||
case DW_FORM_sdata:
|
case DW_FORM_sdata:
|
||||||
return new DWARFNumericAttribute(LEB128.decode(reader, true));
|
return new DWARFNumericAttribute(LEB128.readAsLong(reader, true));
|
||||||
case DW_FORM_udata:
|
case DW_FORM_udata:
|
||||||
return new DWARFNumericAttribute(LEB128.decode(reader, false));
|
return new DWARFNumericAttribute(LEB128.readAsLong(reader, false));
|
||||||
|
|
||||||
case DW_FORM_exprloc: {
|
case DW_FORM_exprloc: {
|
||||||
int length = LEB128.decode32u(reader);
|
int length = LEB128.readAsUInt32(reader);
|
||||||
if (length < 0 || length > MAX_BLOCK4_SIZE) {
|
if (length < 0 || length > MAX_BLOCK4_SIZE) {
|
||||||
throw new IOException("Invalid/bad dw_form_exprloc size: " + length);
|
throw new IOException("Invalid/bad dw_form_exprloc size: " + length);
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ public class DWARFAttributeFactory {
|
|||||||
|
|
||||||
// Indirect Form
|
// Indirect Form
|
||||||
case DW_FORM_indirect:
|
case DW_FORM_indirect:
|
||||||
DWARFForm formValue = DWARFForm.find(LEB128.decode32u(reader));
|
DWARFForm formValue = DWARFForm.find(LEB128.readAsUInt32(reader));
|
||||||
DWARFAttributeValue value = read(reader, unit, formValue);
|
DWARFAttributeValue value = read(reader, unit, formValue);
|
||||||
|
|
||||||
return new DWARFIndirectAttribute(value, formValue);
|
return new DWARFIndirectAttribute(value, formValue);
|
||||||
|
|||||||
+2
-2
@@ -137,9 +137,9 @@ public class DWARFExpression {
|
|||||||
case U_LONG:
|
case U_LONG:
|
||||||
return reader.readNextLong(); /* & there is no mask for ulong */
|
return reader.readNextLong(); /* & there is no mask for ulong */
|
||||||
case S_LEB128:
|
case S_LEB128:
|
||||||
return LEB128.decode(reader, true);
|
return LEB128.readAsLong(reader, true);
|
||||||
case U_LEB128:
|
case U_LEB128:
|
||||||
return LEB128.decode(reader, false);
|
return LEB128.readAsLong(reader, false);
|
||||||
case SIZED_BLOB:
|
case SIZED_BLOB:
|
||||||
throw new IOException("Can't read SIZED_BLOB as a Long value");
|
throw new IOException("Can't read SIZED_BLOB as a Long value");
|
||||||
case DWARF_INT:
|
case DWARF_INT:
|
||||||
|
|||||||
+2
-2
@@ -92,7 +92,7 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
|
|||||||
|
|
||||||
static LEB128Info parse(BinaryReader reader, boolean signed) throws IOException {
|
static LEB128Info parse(BinaryReader reader, boolean signed) throws IOException {
|
||||||
long nextPos = reader.getPointerIndex();
|
long nextPos = reader.getPointerIndex();
|
||||||
long value = LEB128.decode(reader, signed);
|
long value = LEB128.readAsLong(reader, signed);
|
||||||
long pos = reader.getPointerIndex();
|
long pos = reader.getPointerIndex();
|
||||||
int size = (int) (pos - nextPos);
|
int size = (int) (pos - nextPos);
|
||||||
return new LEB128Info((int) nextPos, value, size);
|
return new LEB128Info((int) nextPos, value, size);
|
||||||
@@ -132,7 +132,7 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
|
|||||||
// NOTE: assumes 2-GByte MemBuffer limit
|
// NOTE: assumes 2-GByte MemBuffer limit
|
||||||
int offset = (int) reader.getPointerIndex();
|
int offset = (int) reader.getPointerIndex();
|
||||||
|
|
||||||
long groupSize = LEB128.decode(reader, true);
|
long groupSize = LEB128.readAsLong(reader, true);
|
||||||
if (groupSize > remainingRelocations) {
|
if (groupSize > remainingRelocations) {
|
||||||
Msg.debug(this, "Group relocation count " + groupSize +
|
Msg.debug(this, "Group relocation count " + groupSize +
|
||||||
" exceeded total count " + remainingRelocations);
|
" exceeded total count " + remainingRelocations);
|
||||||
|
|||||||
+10
-10
@@ -205,20 +205,20 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
int relocationIndex = 0;
|
int relocationIndex = 0;
|
||||||
long remainingRelocations = LEB128.decode(reader, true);
|
long remainingRelocations = LEB128.readAsLong(reader, true);
|
||||||
long offset = LEB128.decode(reader, true);
|
long offset = LEB128.readAsLong(reader, true);
|
||||||
long addend = 0;
|
long addend = 0;
|
||||||
|
|
||||||
while (remainingRelocations > 0) {
|
while (remainingRelocations > 0) {
|
||||||
|
|
||||||
long groupSize = LEB128.decode(reader, true);
|
long groupSize = LEB128.readAsLong(reader, true);
|
||||||
if (groupSize > remainingRelocations) {
|
if (groupSize > remainingRelocations) {
|
||||||
Msg.warn(this, "Group relocation count " + groupSize +
|
Msg.warn(this, "Group relocation count " + groupSize +
|
||||||
" exceeded total count " + remainingRelocations);
|
" exceeded total count " + remainingRelocations);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
long groupFlags = LEB128.decode(reader, true);
|
long groupFlags = LEB128.readAsLong(reader, true);
|
||||||
boolean groupedByInfo =
|
boolean groupedByInfo =
|
||||||
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
|
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
|
||||||
boolean groupedByDelta = (groupFlags &
|
boolean groupedByDelta = (groupFlags &
|
||||||
@@ -228,22 +228,22 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
|||||||
boolean groupHasAddend =
|
boolean groupHasAddend =
|
||||||
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
|
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
|
||||||
|
|
||||||
long groupOffsetDelta = groupedByDelta ? LEB128.decode(reader, true) : 0;
|
long groupOffsetDelta = groupedByDelta ? LEB128.readAsLong(reader, true) : 0;
|
||||||
long groupRInfo = groupedByInfo ? LEB128.decode(reader, true) : 0;
|
long groupRInfo = groupedByInfo ? LEB128.readAsLong(reader, true) : 0;
|
||||||
|
|
||||||
if (groupedByAddend && groupHasAddend) {
|
if (groupedByAddend && groupHasAddend) {
|
||||||
addend += LEB128.decode(reader, true);
|
addend += LEB128.readAsLong(reader, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < groupSize; i++) {
|
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;
|
long rAddend = 0;
|
||||||
if (groupHasAddend) {
|
if (groupHasAddend) {
|
||||||
if (!groupedByAddend) {
|
if (!groupedByAddend) {
|
||||||
addend += LEB128.decode(reader, true);
|
addend += LEB128.readAsLong(reader, true);
|
||||||
}
|
}
|
||||||
rAddend = addend;
|
rAddend = addend;
|
||||||
}
|
}
|
||||||
|
|||||||
-4
@@ -139,10 +139,6 @@ public class ElfSectionHeaderType {
|
|||||||
public final String description;
|
public final String description;
|
||||||
|
|
||||||
public ElfSectionHeaderType(int value, String name, 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.value = value;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|||||||
+91
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+75
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+3
-3
@@ -104,8 +104,8 @@ public class FunctionDataTypeHTMLRepresentation extends HTMLDataTypeRepresentati
|
|||||||
String name = var.getName();
|
String name = var.getName();
|
||||||
|
|
||||||
DataType locatableType = getLocatableDataType(dataType);
|
DataType locatableType = getLocatableDataType(dataType);
|
||||||
lines.add(new VariableTextLine(HTMLUtilities.friendlyEncodeHTML(displayName), name,
|
lines.add(new VariableTextLine(HTMLUtilities.friendlyEncodeHTML(displayName),
|
||||||
locatableType));
|
HTMLUtilities.friendlyEncodeHTML(name), locatableType));
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
@@ -114,7 +114,7 @@ public class FunctionDataTypeHTMLRepresentation extends HTMLDataTypeRepresentati
|
|||||||
private static String buildHTMLText(TextLine returnType, TextLine functionName,
|
private static String buildHTMLText(TextLine returnType, TextLine functionName,
|
||||||
List<ValidatableLine> arguments, TextLine varArgs, TextLine voidArgs) {
|
List<ValidatableLine> arguments, TextLine varArgs, TextLine voidArgs) {
|
||||||
|
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
String returnTypeText = returnType.getText();
|
String returnTypeText = returnType.getText();
|
||||||
returnTypeText = wrapStringInColor(returnTypeText, returnType.getTextColor());
|
returnTypeText = wrapStringInColor(returnTypeText, returnType.getTextColor());
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||||||
|
|
||||||
markupHashTable(monitor);
|
markupHashTable(monitor);
|
||||||
markupGnuHashTable(monitor);
|
markupGnuHashTable(monitor);
|
||||||
|
markupGnuBuildId(monitor);
|
||||||
|
markupGnuDebugLink(monitor);
|
||||||
|
|
||||||
processGNU(monitor);
|
processGNU(monitor);
|
||||||
processGNU_readOnly(monitor);
|
processGNU_readOnly(monitor);
|
||||||
@@ -749,7 +751,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||||||
Address baseAddress = relocationSpace.getTruncatedAddress(baseWordOffset, true);
|
Address baseAddress = relocationSpace.getTruncatedAddress(baseWordOffset, true);
|
||||||
// long relocationOffset = baseWordOffset + reloc.getOffset();
|
// long relocationOffset = baseWordOffset + reloc.getOffset();
|
||||||
// r_offset is defined to be a byte offset (assume byte size is 1)
|
// 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);
|
// Address relocAddr = relocationSpace.getTruncatedAddress(relocationOffset, true);
|
||||||
|
|
||||||
long[] values = new long[] { reloc.getSymbolIndex() };
|
long[] values = new long[] { reloc.getSymbolIndex() };
|
||||||
@@ -867,7 +869,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Structure phStructDt = (Structure) elf.getProgramHeaders()[0].toDataType();
|
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);
|
Array arrayDt = new ArrayDataType(phStructDt, headerCount, size);
|
||||||
|
|
||||||
@@ -925,7 +927,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Structure shStructDt = (Structure) elf.getSections()[0].toDataType();
|
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());
|
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());
|
// 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) {
|
private void markupHashTable(TaskMonitor monitor) {
|
||||||
|
|
||||||
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
||||||
|
|||||||
+172
-142
@@ -15,154 +15,199 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf4;
|
package ghidra.app.util.bin.format.dwarf4;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import static org.junit.Assert.*;
|
||||||
import ghidra.app.util.bin.ByteArrayProvider;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.ByteArrayProvider;
|
||||||
|
import ghidra.util.NumericUtilities;
|
||||||
|
|
||||||
public class LEB128Test {
|
public class LEB128Test {
|
||||||
|
|
||||||
private static final boolean BR_IS_LITTLE_ENDIAN = true;
|
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);
|
return new BinaryReader(new ByteArrayProvider(bytes), BR_IS_LITTLE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static class TestEntry {
|
||||||
* Test reading the largest unsigned LEB128 int that we can handle (64 used bits).
|
long expectedValue;
|
||||||
* <p>
|
byte[] bytes;
|
||||||
*
|
|
||||||
* @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 };
|
|
||||||
|
|
||||||
long value = LEB128.decode(bytes, false);
|
TestEntry(long expectedValue, byte[] bytes) {
|
||||||
|
this.expectedValue = expectedValue;
|
||||||
// -1 signed long == MAX unsigned
|
this.bytes = bytes;
|
||||||
Assert.assertEquals(-1, value);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private TestEntry te(long expectedValue, int... intBytes) {
|
||||||
* Test reading a number that is larger than 32 bits to ensure that all the shifting
|
byte[] bytes = new byte[intBytes.length];
|
||||||
* done in the LEB128 reader doesn't have a 32bit fault.
|
for (int i = 0; i < intBytes.length; i++) {
|
||||||
* @throws IOException
|
bytes[i] = (byte) intBytes[i];
|
||||||
*/
|
}
|
||||||
@Test
|
return new TestEntry(expectedValue, bytes);
|
||||||
public void test36bitUnsigned() throws IOException {
|
}
|
||||||
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
|
|
||||||
(byte) 0x01 };
|
|
||||||
|
|
||||||
long value = LEB128.decode(bytes, false);
|
private List<TestEntry> unsignedTestEntries = List.of(
|
||||||
Assert.assertEquals(0xfffffffffL, value);
|
// 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
|
@Test
|
||||||
public void testSignedNeg2() throws IOException {
|
public void testSignedTestEntries() throws IOException {
|
||||||
byte[] bytes = new byte[] { (byte) 0x7e };
|
testTestEntries(signedTestEntries, true, "Signed TestEntry");
|
||||||
|
|
||||||
long value = LEB128.decode(bytes, true);
|
|
||||||
Assert.assertEquals(-2, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
public void testTestEntries(List<TestEntry> testEntries, boolean signed, String name)
|
||||||
public void testSignedNeg127() throws IOException {
|
throws IOException {
|
||||||
byte[] bytes = new byte[] { (byte) 0x81, (byte) 0x7f };
|
for (int i = 0; i < testEntries.size(); i++) {
|
||||||
|
TestEntry te = testEntries.get(i);
|
||||||
long value = LEB128.decode(bytes, true);
|
BinaryReader br =
|
||||||
Assert.assertEquals(-127, value);
|
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
|
@Test(expected = IOException.class)
|
||||||
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
|
|
||||||
public void testToolargeUnsigned() throws IOException {
|
public void testToolargeUnsigned() throws IOException {
|
||||||
byte[] bytes = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
|
// Test reading a unsigned LEB128 that is just 1 bit too large for a java 64 bit long int.
|
||||||
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x02 };
|
|
||||||
|
|
||||||
try {
|
BinaryReader br = br(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02);
|
||||||
long value = LEB128.decode(bytes, false);
|
|
||||||
Assert.fail(
|
long value = LEB128.readAsLong(br, false);
|
||||||
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
|
Assert.fail(
|
||||||
value);
|
"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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
@Test
|
||||||
public void testToolargeBinaryReaderStreamPosition() throws IOException {
|
public void testTooLargeValueBinaryReaderStreamPosition() {
|
||||||
BinaryReader br = br((byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
|
// Test that the BinaryReader stream is 'correctly' positioned after the LEB128 bytes after
|
||||||
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
|
// reading a LEB128 that is too large for a java 64 bit long int.
|
||||||
(byte) 0x02, (byte) 0x1, (byte) 0x2);
|
|
||||||
|
BinaryReader br =
|
||||||
|
br(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x1, 0x2);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long value = LEB128.decode(br, false);
|
long value = LEB128.readAsLong(br, false);
|
||||||
Assert.fail(
|
Assert.fail(
|
||||||
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
|
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
|
||||||
Long.toUnsignedString(value));
|
Long.toUnsignedString(value));
|
||||||
@@ -171,38 +216,23 @@ public class LEB128Test {
|
|||||||
// good
|
// good
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertEquals(1, br.readNextByte() & 0xFF);
|
Assert.assertEquals(10, br.getPointerIndex());
|
||||||
Assert.assertEquals(2, br.readNextByte() & 0xFF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test uint32 max
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testUint32Max() throws IOException {
|
public void testUint32Max() throws IOException {
|
||||||
int value =
|
int value = LEB128.readAsUInt32(br(0xff, 0xff, 0xff, 0xff, 0x07));
|
||||||
LEB128.decode32u(br((byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07));
|
|
||||||
Assert.assertEquals(Integer.MAX_VALUE, value);
|
Assert.assertEquals(Integer.MAX_VALUE, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Test(expected = IOException.class)
|
||||||
* Test uint32 max overflow with 0xff_ff_ff_ff
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testUint32Overflow() throws IOException {
|
public void testUint32Overflow() throws IOException {
|
||||||
try {
|
|
||||||
int value = LEB128.decode32u(
|
// Test uint32 max overflow with 0xff_ff_ff_ff
|
||||||
br((byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f));
|
|
||||||
Assert.fail(
|
int value = LEB128.readAsUInt32(br(0xff, 0xff, 0xff, 0xff, 0x0f));
|
||||||
"Should not be able to read a LEB128 that is larger than what can fit in java 32 bit int: " +
|
Assert.fail(
|
||||||
Integer.toUnsignedString(value));
|
"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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -546,6 +546,7 @@ model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (b.toolChain in Clang) {
|
else if (b.toolChain in Clang) {
|
||||||
|
b.cppCompiler.args "-std=c++11"
|
||||||
b.cppCompiler.args "-Wall"
|
b.cppCompiler.args "-Wall"
|
||||||
b.cppCompiler.args "-O2" // for DEBUG, comment this line out
|
b.cppCompiler.args "-O2" // for DEBUG, comment this line out
|
||||||
// b.cppCompiler.args "-g" // for DEBUG, uncomment this line
|
// b.cppCompiler.args "-g" // for DEBUG, uncomment this line
|
||||||
|
|||||||
@@ -181,6 +181,20 @@ typedef char int1;
|
|||||||
typedef uint8 uintp;
|
typedef uint8 uintp;
|
||||||
#endif
|
#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)
|
#if defined(_WINDOWS)
|
||||||
#pragma warning (disable:4312)
|
#pragma warning (disable:4312)
|
||||||
|
|||||||
+63
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+392
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+961
File diff suppressed because it is too large
Load Diff
+68
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+184
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+332
@@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+7
-12
@@ -15,18 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
package ghidra.file.formats.android.dex.format;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
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.ArrayDataType;
|
import ghidra.program.model.data.*;
|
||||||
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 ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class AnnotationElement implements StructConverter {
|
public class AnnotationElement implements StructConverter {
|
||||||
|
|
||||||
private int nameIndex;
|
private int nameIndex;
|
||||||
@@ -34,10 +30,9 @@ public class AnnotationElement implements StructConverter {
|
|||||||
private EncodedValue value;
|
private EncodedValue value;
|
||||||
|
|
||||||
public AnnotationElement( BinaryReader reader ) throws IOException {
|
public AnnotationElement( BinaryReader reader ) throws IOException {
|
||||||
nameIndex = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
|
nameIndex = leb128.asUInt32();
|
||||||
nameIndexLength = Leb128.unsignedLeb128Size( nameIndex );
|
nameIndexLength = leb128.getLength();
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + nameIndexLength );
|
|
||||||
|
|
||||||
value = new EncodedValue( reader );
|
value = new EncodedValue( reader );
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-22
@@ -15,15 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
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.io.IOException;
|
||||||
import java.util.*;
|
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 {
|
public class ClassDataItem implements StructConverter {
|
||||||
|
|
||||||
private int staticFieldsSize;
|
private int staticFieldsSize;
|
||||||
@@ -36,27 +36,27 @@ public class ClassDataItem implements StructConverter {
|
|||||||
private int directMethodsSizeLength;// in bytes
|
private int directMethodsSizeLength;// in bytes
|
||||||
private int virtualMethodsSizeLength;// in bytes
|
private int virtualMethodsSizeLength;// in bytes
|
||||||
|
|
||||||
private List< EncodedField > staticFields = new ArrayList< EncodedField >( );
|
private List< EncodedField > staticFields = new ArrayList< >( );
|
||||||
private List< EncodedField > instancesFields = new ArrayList< EncodedField >( );
|
private List< EncodedField > instancesFields = new ArrayList< >( );
|
||||||
private List< EncodedMethod > directMethods = new ArrayList< EncodedMethod >( );
|
private List< EncodedMethod > directMethods = new ArrayList< >( );
|
||||||
private List< EncodedMethod > virtualMethods = new ArrayList< EncodedMethod >( );
|
private List< EncodedMethod > virtualMethods = new ArrayList< >( );
|
||||||
|
|
||||||
public ClassDataItem( BinaryReader reader ) throws IOException {
|
public ClassDataItem( BinaryReader reader ) throws IOException {
|
||||||
staticFieldsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
staticFieldsSizeLength = Leb128.unsignedLeb128Size( staticFieldsSize );
|
staticFieldsSize = leb128.asUInt32();
|
||||||
reader.readNextByteArray( staticFieldsSizeLength );// consume leb...
|
staticFieldsSizeLength = leb128.getLength();
|
||||||
|
|
||||||
instanceFieldsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
instanceFieldsSizeLength = Leb128.unsignedLeb128Size( instanceFieldsSize );
|
instanceFieldsSize = leb128.asUInt32();
|
||||||
reader.readNextByteArray( instanceFieldsSizeLength );// consume leb...
|
instanceFieldsSizeLength = leb128.getLength();
|
||||||
|
|
||||||
directMethodsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
directMethodsSizeLength = Leb128.unsignedLeb128Size( directMethodsSize );
|
directMethodsSize = leb128.asUInt32();
|
||||||
reader.readNextByteArray( directMethodsSizeLength );// consume leb...
|
directMethodsSizeLength = leb128.getLength();
|
||||||
|
|
||||||
virtualMethodsSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
virtualMethodsSizeLength = Leb128.unsignedLeb128Size( virtualMethodsSize );
|
virtualMethodsSize = leb128.asUInt32();
|
||||||
reader.readNextByteArray( virtualMethodsSizeLength );// consume leb...
|
virtualMethodsSizeLength = leb128.getLength();
|
||||||
|
|
||||||
for ( int i = 0 ; i < staticFieldsSize ; ++i ) {
|
for ( int i = 0 ; i < staticFieldsSize ; ++i ) {
|
||||||
staticFields.add( new EncodedField( reader ) );
|
staticFields.add( new EncodedField( reader ) );
|
||||||
|
|||||||
+14
-23
@@ -15,18 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
package ghidra.file.formats.android.dex.format;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
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.ArrayDataType;
|
import ghidra.program.model.data.*;
|
||||||
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 ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class DebugInfoItem implements StructConverter {
|
public class DebugInfoItem implements StructConverter {
|
||||||
|
|
||||||
private int lineStart;
|
private int lineStart;
|
||||||
@@ -38,30 +34,25 @@ public class DebugInfoItem implements StructConverter {
|
|||||||
private byte [] stateMachineOpcodes;
|
private byte [] stateMachineOpcodes;
|
||||||
|
|
||||||
public DebugInfoItem( BinaryReader reader ) throws IOException {
|
public DebugInfoItem( BinaryReader reader ) throws IOException {
|
||||||
lineStart = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
lineStartLength = Leb128.unsignedLeb128Size( lineStart );
|
lineStart = leb128.asUInt32();
|
||||||
reader.readNextByteArray( lineStartLength );// consume leb...
|
lineStartLength = leb128.getLength();
|
||||||
|
|
||||||
parametersSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
parametersSizeLength = Leb128.unsignedLeb128Size( parametersSize );
|
parametersSize = leb128.asUInt32();
|
||||||
reader.readNextByteArray( parametersSizeLength );// consume leb...
|
parametersSizeLength = leb128.getLength();
|
||||||
|
|
||||||
parameterNames = new int[ parametersSize ];
|
parameterNames = new int[ parametersSize ];
|
||||||
parameterNamesLengths = new int[ parametersSize ];
|
parameterNamesLengths = new int[ parametersSize ];
|
||||||
|
|
||||||
for ( int i = 0 ; i < parametersSize ; ++i ) {
|
for ( int i = 0 ; i < parametersSize ; ++i ) {
|
||||||
int value = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
int valueLength = Leb128.unsignedLeb128Size( value );
|
|
||||||
reader.readNextByteArray( valueLength );// consume leb...
|
|
||||||
|
|
||||||
parameterNames[ i ] = value - 1;// uleb128p1
|
parameterNames[i] = leb128.asUInt32() - 1;// uleb128p1
|
||||||
|
parameterNamesLengths[i] = leb128.getLength();
|
||||||
parameterNamesLengths[ i ] = valueLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long startIndex = reader.getPointerIndex( );
|
int count = DebugInfoStateMachineReader.computeLength( reader.clone() );
|
||||||
int count = DebugInfoStateMachineReader.computeLength( reader );
|
|
||||||
reader.setPointerIndex( startIndex );
|
|
||||||
stateMachineOpcodes = reader.readNextByteArray( count );
|
stateMachineOpcodes = reader.readNextByteArray( count );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-60
@@ -15,102 +15,64 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
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 java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.LEB128;
|
||||||
|
|
||||||
class DebugInfoStateMachineReader {
|
class DebugInfoStateMachineReader {
|
||||||
|
private static final int MAX_SIZE = 0x10000; // 64k
|
||||||
|
|
||||||
static int computeLength( BinaryReader reader ) throws IOException {
|
static int computeLength( BinaryReader reader ) throws IOException {
|
||||||
int length = 0;
|
long start = reader.getPointerIndex();
|
||||||
|
|
||||||
while ( true ) {
|
while (reader.getPointerIndex() - start < MAX_SIZE) {
|
||||||
|
|
||||||
if ( length > 0x10000 ) {//don't loop forever!
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte opcode = reader.readNextByte( );
|
byte opcode = reader.readNextByte( );
|
||||||
|
|
||||||
++length;
|
|
||||||
|
|
||||||
switch( opcode ) {
|
switch( opcode ) {
|
||||||
case DebugStateMachineOpCodes.DBG_END_SEQUENCE: {
|
case DebugStateMachineOpCodes.DBG_END_SEQUENCE: {
|
||||||
return length;//done!
|
return (int) (reader.getPointerIndex() - start);//done!
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_ADVANCE_PC: {
|
case DebugStateMachineOpCodes.DBG_ADVANCE_PC: {
|
||||||
int advance = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128.readAsUInt32(reader);
|
||||||
int advanceLength = Leb128.unsignedLeb128Size( advance );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + advanceLength );
|
|
||||||
length += advanceLength;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_ADVANCE_LINE: {
|
case DebugStateMachineOpCodes.DBG_ADVANCE_LINE: {
|
||||||
int advance = Leb128.readSignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128.readAsUInt32(reader);
|
||||||
int advanceLength = Leb128.signedLeb128Size( advance );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + advanceLength );
|
|
||||||
length += advanceLength;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_START_LOCAL: {
|
case DebugStateMachineOpCodes.DBG_START_LOCAL: {
|
||||||
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int register = LEB128.readAsUInt32(reader);
|
||||||
int registerLength = Leb128.unsignedLeb128Size( register );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
|
|
||||||
length += registerLength;
|
|
||||||
|
|
||||||
//TODO uleb128p1
|
//TODO uleb128p1
|
||||||
int name = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int name = LEB128.readAsUInt32(reader);
|
||||||
int nameLength = Leb128.unsignedLeb128Size( name );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + nameLength );
|
|
||||||
length += nameLength;
|
|
||||||
|
|
||||||
//TODO uleb128p1
|
//TODO uleb128p1
|
||||||
int type = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int type = LEB128.readAsUInt32(reader);
|
||||||
int typeLength = Leb128.unsignedLeb128Size( type );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + typeLength );
|
|
||||||
length += typeLength;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_START_LOCAL_EXTENDED: {
|
case DebugStateMachineOpCodes.DBG_START_LOCAL_EXTENDED: {
|
||||||
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int register = LEB128.readAsUInt32(reader);
|
||||||
int registerLength = Leb128.unsignedLeb128Size( register );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
|
|
||||||
length += registerLength;
|
|
||||||
|
|
||||||
//TODO uleb128p1
|
//TODO uleb128p1
|
||||||
int name = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int name = LEB128.readAsUInt32(reader);
|
||||||
int nameLength = Leb128.unsignedLeb128Size( name );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + nameLength );
|
|
||||||
length += nameLength;
|
|
||||||
|
|
||||||
//TODO uleb128p1
|
//TODO uleb128p1
|
||||||
int type = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int type = LEB128.readAsUInt32(reader);
|
||||||
int typeLength = Leb128.unsignedLeb128Size( type );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + typeLength );
|
|
||||||
length += typeLength;
|
|
||||||
|
|
||||||
//TODO uleb128p1
|
//TODO uleb128p1
|
||||||
int signature = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int signature = LEB128.readAsUInt32(reader);
|
||||||
int signatureLength = Leb128.unsignedLeb128Size( signature );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + signatureLength );
|
|
||||||
length += signatureLength;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_END_LOCAL: {
|
case DebugStateMachineOpCodes.DBG_END_LOCAL: {
|
||||||
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int register = LEB128.readAsUInt32(reader);
|
||||||
int registerLength = Leb128.unsignedLeb128Size( register );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
|
|
||||||
length += registerLength;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_RESTART_LOCAL: {
|
case DebugStateMachineOpCodes.DBG_RESTART_LOCAL: {
|
||||||
int register = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int register = LEB128.readAsUInt32(reader);
|
||||||
int registerLength = Leb128.unsignedLeb128Size( register );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + registerLength );
|
|
||||||
length += registerLength;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_SET_PROLOGUE_END: {
|
case DebugStateMachineOpCodes.DBG_SET_PROLOGUE_END: {
|
||||||
@@ -121,10 +83,7 @@ class DebugInfoStateMachineReader {
|
|||||||
}
|
}
|
||||||
case DebugStateMachineOpCodes.DBG_SET_FILE: {
|
case DebugStateMachineOpCodes.DBG_SET_FILE: {
|
||||||
//TODO uleb128p1
|
//TODO uleb128p1
|
||||||
int name = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
int name = LEB128.readAsUInt32(reader);
|
||||||
int nameLength = Leb128.unsignedLeb128Size( name );
|
|
||||||
reader.setPointerIndex( reader.getPointerIndex( ) + nameLength );
|
|
||||||
length += nameLength;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -132,5 +91,7 @@ class DebugInfoStateMachineReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-13
@@ -15,31 +15,31 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
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.io.IOException;
|
||||||
import java.util.*;
|
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 {
|
public class EncodedAnnotation implements StructConverter {
|
||||||
|
|
||||||
private int typeIndex;
|
private int typeIndex;
|
||||||
private int typeIndexLength;// in bytes
|
private int typeIndexLength;// in bytes
|
||||||
private int size;
|
private int size;
|
||||||
private int sizeLength;// in bytes
|
private int sizeLength;// in bytes
|
||||||
private List< AnnotationElement > elements = new ArrayList< AnnotationElement >( );
|
private List< AnnotationElement > elements = new ArrayList< >( );
|
||||||
|
|
||||||
public EncodedAnnotation( BinaryReader reader ) throws IOException {
|
public EncodedAnnotation( BinaryReader reader ) throws IOException {
|
||||||
typeIndex = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
typeIndexLength = Leb128.unsignedLeb128Size( typeIndex );
|
typeIndex = leb128.asUInt32();
|
||||||
reader.readNextByteArray( typeIndexLength );// consume leb...
|
typeIndexLength = leb128.getLength();
|
||||||
|
|
||||||
size = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
sizeLength = Leb128.unsignedLeb128Size( size );
|
size = leb128.asUInt32();
|
||||||
reader.readNextByteArray( sizeLength );// consume leb...
|
sizeLength = leb128.getLength();
|
||||||
|
|
||||||
for ( int i = 0 ; i < size ; ++i ) {
|
for ( int i = 0 ; i < size ; ++i ) {
|
||||||
elements.add( new AnnotationElement( reader ) );
|
elements.add( new AnnotationElement( reader ) );
|
||||||
|
|||||||
+8
-10
@@ -21,7 +21,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
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.program.model.data.*;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
@@ -33,18 +33,16 @@ public class EncodedArray implements StructConverter {
|
|||||||
private byte [] values;
|
private byte [] values;
|
||||||
|
|
||||||
public EncodedArray( BinaryReader reader ) throws IOException {
|
public EncodedArray( BinaryReader reader ) throws IOException {
|
||||||
size = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
sizeLength = Leb128.unsignedLeb128Size( size );
|
size = leb128.asUInt32();
|
||||||
reader.readNextByteArray( sizeLength );// consume leb...
|
sizeLength = leb128.getLength();
|
||||||
|
|
||||||
long oldIndex = reader.getPointerIndex( );
|
BinaryReader evReader = reader.clone();
|
||||||
List< EncodedValue > valuesList = new ArrayList< EncodedValue >( );
|
List< EncodedValue > valuesList = new ArrayList< >( );
|
||||||
for ( int i = 0 ; i < size ; ++i ) {
|
for ( int i = 0 ; i < size ; ++i ) {
|
||||||
valuesList.add( new EncodedValue( reader ) );
|
valuesList.add(new EncodedValue(evReader));
|
||||||
}
|
}
|
||||||
int nBytes = (int) ( reader.getPointerIndex() - oldIndex );
|
int nBytes = (int) (evReader.getPointerIndex() - reader.getPointerIndex());
|
||||||
|
|
||||||
reader.setPointerIndex(oldIndex);
|
|
||||||
values = reader.readNextByteArray(nBytes); // Re-read the encoded values as a byte array
|
values = reader.readNextByteArray(nBytes); // Re-read the encoded values as a byte array
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-17
@@ -15,41 +15,37 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
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.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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 {
|
public class EncodedCatchHandler implements StructConverter {
|
||||||
|
|
||||||
private int size;
|
private int size;
|
||||||
private int sizeLength;// in bytes
|
private int sizeLength;// in bytes
|
||||||
private List< EncodedTypeAddressPair > handlers = new ArrayList< EncodedTypeAddressPair >( );
|
private List< EncodedTypeAddressPair > handlers = new ArrayList< >( );
|
||||||
private int catchAllAddress;
|
private int catchAllAddress;
|
||||||
private int catchAllAddressLength;
|
private int catchAllAddressLength;
|
||||||
|
|
||||||
public EncodedCatchHandler( BinaryReader reader ) throws IOException {
|
public EncodedCatchHandler( BinaryReader reader ) throws IOException {
|
||||||
size = Leb128.readSignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readSignedValue(reader);
|
||||||
sizeLength = Leb128.signedLeb128Size( size );
|
size = leb128.asInt32();
|
||||||
reader.readNextByteArray( sizeLength );// consume leb...
|
sizeLength = leb128.getLength();
|
||||||
|
|
||||||
for ( int i = 0 ; i < Math.abs( size ) ; ++i ) {
|
for ( int i = 0 ; i < Math.abs( size ) ; ++i ) {
|
||||||
handlers.add( new EncodedTypeAddressPair( reader ) );
|
handlers.add( new EncodedTypeAddressPair( reader ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( size <= 0 ) {// This element is only present if size is non-positive.
|
if ( size <= 0 ) {// This element is only present if size is non-positive.
|
||||||
catchAllAddress = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
catchAllAddressLength = Leb128.unsignedLeb128Size( catchAllAddress );
|
catchAllAddress = leb128.asUInt32();
|
||||||
reader.readNextByteArray( catchAllAddressLength );// consume leb...
|
catchAllAddressLength = leb128.getLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -15,30 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
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.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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 {
|
public class EncodedCatchHandlerList implements StructConverter {
|
||||||
|
|
||||||
private int size;
|
private int size;
|
||||||
private int sizeLength;// in bytes
|
private int sizeLength;// in bytes
|
||||||
private List< EncodedCatchHandler > handlers = new ArrayList< EncodedCatchHandler >( );
|
private List< EncodedCatchHandler > handlers = new ArrayList< >( );
|
||||||
|
|
||||||
public EncodedCatchHandlerList( BinaryReader reader ) throws IOException {
|
public EncodedCatchHandlerList( BinaryReader reader ) throws IOException {
|
||||||
size = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
sizeLength = Leb128.unsignedLeb128Size( size );
|
size = leb128.asUInt32();
|
||||||
reader.readNextByteArray( sizeLength );// consume leb...
|
sizeLength = leb128.getLength();
|
||||||
|
|
||||||
for ( int i = 0 ; i < size ; ++i ) {
|
for ( int i = 0 ; i < size ; ++i ) {
|
||||||
handlers.add( new EncodedCatchHandler( reader ) );
|
handlers.add( new EncodedCatchHandler( reader ) );
|
||||||
|
|||||||
+11
-15
@@ -15,18 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
package ghidra.file.formats.android.dex.format;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
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.ArrayDataType;
|
import ghidra.program.model.data.*;
|
||||||
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 ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class EncodedField implements StructConverter {
|
public class EncodedField implements StructConverter {
|
||||||
|
|
||||||
private long _fileOffset;
|
private long _fileOffset;
|
||||||
@@ -38,15 +34,15 @@ public class EncodedField implements StructConverter {
|
|||||||
private int accessFlagsLength;// in bytes
|
private int accessFlagsLength;// in bytes
|
||||||
|
|
||||||
public EncodedField( BinaryReader reader ) throws IOException {
|
public EncodedField( BinaryReader reader ) throws IOException {
|
||||||
_fileOffset = reader.getPointerIndex( );
|
|
||||||
|
|
||||||
fieldIndexDifference = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
fieldIndexDifferenceLength = Leb128.unsignedLeb128Size( fieldIndexDifference );
|
_fileOffset = leb128.getOffset();
|
||||||
reader.readNextByteArray( fieldIndexDifferenceLength );// consume leb...
|
fieldIndexDifference = leb128.asUInt32();
|
||||||
|
fieldIndexDifferenceLength = leb128.getLength();
|
||||||
|
|
||||||
accessFlags = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
accessFlagsLength = Leb128.unsignedLeb128Size( accessFlags );
|
accessFlags = leb128.asUInt32();
|
||||||
reader.readNextByteArray( accessFlagsLength );// consume leb...
|
accessFlagsLength = leb128.getLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFileOffset( ) {
|
public long getFileOffset( ) {
|
||||||
|
|||||||
+14
-21
@@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
package ghidra.file.formats.android.dex.format;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
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.program.model.data.*;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class EncodedMethod implements StructConverter {
|
public class EncodedMethod implements StructConverter {
|
||||||
|
|
||||||
private long _fileOffset;
|
private long _fileOffset;
|
||||||
@@ -39,29 +39,22 @@ public class EncodedMethod implements StructConverter {
|
|||||||
private CodeItem codeItem;
|
private CodeItem codeItem;
|
||||||
|
|
||||||
public EncodedMethod( BinaryReader reader ) throws IOException {
|
public EncodedMethod( BinaryReader reader ) throws IOException {
|
||||||
_fileOffset = reader.getPointerIndex( );
|
|
||||||
|
|
||||||
methodIndexDifference = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
methodIndexDifferenceLength = Leb128.unsignedLeb128Size( methodIndexDifference );
|
_fileOffset = leb128.getOffset();
|
||||||
reader.readNextByteArray( methodIndexDifferenceLength );// consume leb...
|
methodIndexDifference = leb128.asUInt32();
|
||||||
|
methodIndexDifferenceLength = leb128.getLength();
|
||||||
|
|
||||||
accessFlags = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
accessFlagsLength = Leb128.unsignedLeb128Size( accessFlags );
|
accessFlags = leb128.asUInt32();
|
||||||
reader.readNextByteArray( accessFlagsLength );// consume leb...
|
accessFlagsLength = leb128.getLength();
|
||||||
|
|
||||||
codeOffset = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
codeOffsetLength = Leb128.unsignedLeb128Size( codeOffset );
|
codeOffset = leb128.asUInt32();
|
||||||
reader.readNextByteArray( codeOffsetLength );// consume leb...
|
codeOffsetLength = leb128.getLength();
|
||||||
|
|
||||||
if ( codeOffset > 0 ) {
|
if ( codeOffset > 0 ) {
|
||||||
long oldIndex = reader.getPointerIndex( );
|
codeItem = new CodeItem( reader.clone(codeOffset) );
|
||||||
try {
|
|
||||||
reader.setPointerIndex( codeOffset );
|
|
||||||
codeItem = new CodeItem( reader );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
reader.setPointerIndex( oldIndex );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -15,18 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
package ghidra.file.formats.android.dex.format;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
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.ArrayDataType;
|
import ghidra.program.model.data.*;
|
||||||
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 ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class EncodedTypeAddressPair implements StructConverter {
|
public class EncodedTypeAddressPair implements StructConverter {
|
||||||
|
|
||||||
private int typeIndex;
|
private int typeIndex;
|
||||||
@@ -36,13 +32,13 @@ public class EncodedTypeAddressPair implements StructConverter {
|
|||||||
private int addressLength;// in bytes
|
private int addressLength;// in bytes
|
||||||
|
|
||||||
public EncodedTypeAddressPair( BinaryReader reader ) throws IOException {
|
public EncodedTypeAddressPair( BinaryReader reader ) throws IOException {
|
||||||
typeIndex = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
typeIndexLength = Leb128.unsignedLeb128Size( typeIndex );
|
typeIndex = leb128.asUInt32();
|
||||||
reader.readNextByteArray( typeIndexLength );// consume leb...
|
typeIndexLength = leb128.getLength();
|
||||||
|
|
||||||
address = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
|
leb128 = LEB128.readUnsignedValue(reader);
|
||||||
addressLength = Leb128.unsignedLeb128Size( address );
|
address = leb128.asUInt32();
|
||||||
reader.readNextByteArray( addressLength );// consume leb...
|
addressLength = leb128.getLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTypeIndex( ) {
|
public int getTypeIndex( ) {
|
||||||
|
|||||||
+30
-38
@@ -15,52 +15,41 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.file.formats.android.dex.format;
|
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.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
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 {
|
public class StringDataItem implements StructConverter {
|
||||||
|
private static final int MAX_STRING_LEN = 0x200000; // 2Mb'ish
|
||||||
|
|
||||||
private int stringLength;
|
private int stringLength;
|
||||||
private int lebLength;
|
private int lebLength;
|
||||||
private int actualLength;
|
private int actualLength;
|
||||||
private String string;
|
private String string;
|
||||||
|
|
||||||
public StringDataItem( StringIDItem stringItem, BinaryReader reader ) throws IOException {
|
public StringDataItem(StringIDItem stringItem, BinaryReader reader) throws IOException {
|
||||||
long oldIndex = reader.getPointerIndex( );
|
reader = reader.clone(stringItem.getStringDataOffset());
|
||||||
try {
|
LEB128 leb128 = LEB128.readUnsignedValue(reader);
|
||||||
reader.setPointerIndex( stringItem.getStringDataOffset( ) );
|
stringLength = leb128.asUInt32();
|
||||||
stringLength = Leb128.readUnsignedLeb128( reader.readByteArray( stringItem.getStringDataOffset( ), 5 ) );
|
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 );
|
string = ModifiedUTF8.decode(in, out);
|
||||||
|
|
||||||
byte [] stringBytes = reader.readNextByteArray( actualLength );
|
|
||||||
|
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream( stringBytes );
|
|
||||||
|
|
||||||
char [] out = new char[ stringLength ];
|
|
||||||
|
|
||||||
string = ModifiedUTF8.decode( in, out );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
reader.setPointerIndex( oldIndex );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString( ) {
|
public String getString() {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,15 +62,18 @@ public class StringDataItem implements StructConverter {
|
|||||||
return structure;
|
return structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int computeActualLength( BinaryReader reader ) throws IOException {
|
private static long getIndexOfByteValue(BinaryReader reader, long startIndex, int maxLen,
|
||||||
int count = 0;
|
byte byteValueToFind) throws IOException {
|
||||||
while ( count < 0x200000 ) {// don't run forever!
|
long maxIndex = startIndex + maxLen;
|
||||||
if ( reader.readByte( reader.getPointerIndex( ) + count ) == 0x0 ) {
|
long currentIndex = startIndex;
|
||||||
break;
|
while (currentIndex < maxIndex) {
|
||||||
|
byte b = reader.readByte(currentIndex);
|
||||||
|
if (b == byteValueToFind) {
|
||||||
|
return currentIndex;
|
||||||
}
|
}
|
||||||
++count;
|
currentIndex++;
|
||||||
}
|
}
|
||||||
return count + 1;
|
return currentIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-202
@@ -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" ) ) );
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
|
* REVIEWED: YES
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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");
|
setupRtti4_32(builder, 0x01001340L, 0, 0, 0, "0x01005364", "0x0100137c");
|
||||||
Address address = builder.addr(0x01001340L);
|
Address address = builder.addr(0x01001340L);
|
||||||
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
|
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
|
@Test
|
||||||
@@ -62,7 +63,7 @@ public class RttiModelTest extends AbstractRttiTest {
|
|||||||
setupRtti0_32(builder, 0x01001364, "0x01007700", "0x0", "stuff");
|
setupRtti0_32(builder, 0x01001364, "0x01007700", "0x0", "stuff");
|
||||||
Address address = builder.addr(0x01001340L);
|
Address address = builder.addr(0x01001340L);
|
||||||
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
|
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
|
@Test
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -31,6 +30,13 @@ public class DockingCheckBoxMenuItem extends JCheckBoxMenuItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
|
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
|
@Override
|
||||||
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
|
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
|
// 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
|
// return true; // we will take care of the action ourselves
|
||||||
|
|
||||||
// Our KeyBindingOverrideKeyEventDispatcher processes actions for us, so there is no
|
// Our KeyBindingOverrideKeyEventDispatcher processes actions for us, so there is no
|
||||||
|
|||||||
+2
@@ -2724,6 +2724,7 @@ public class FunctionDB extends DatabaseObject implements Function {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Function> getCallingFunctions(TaskMonitor monitor) {
|
public Set<Function> getCallingFunctions(TaskMonitor monitor) {
|
||||||
|
monitor = TaskMonitor.dummyIfNull(monitor);
|
||||||
Set<Function> set = new HashSet<>();
|
Set<Function> set = new HashSet<>();
|
||||||
ReferenceIterator iter = program.getReferenceManager().getReferencesTo(getEntryPoint());
|
ReferenceIterator iter = program.getReferenceManager().getReferencesTo(getEntryPoint());
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
@@ -2742,6 +2743,7 @@ public class FunctionDB extends DatabaseObject implements Function {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Function> getCalledFunctions(TaskMonitor monitor) {
|
public Set<Function> getCalledFunctions(TaskMonitor monitor) {
|
||||||
|
monitor = TaskMonitor.dummyIfNull(monitor);
|
||||||
Set<Function> set = new HashSet<>();
|
Set<Function> set = new HashSet<>();
|
||||||
Set<Reference> references = getReferencesFromBody(monitor);
|
Set<Reference> references = getReferencesFromBody(monitor);
|
||||||
for (Reference reference : references) {
|
for (Reference reference : references) {
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ public interface TaskMonitor {
|
|||||||
|
|
||||||
public static final TaskMonitor DUMMY = new StubTaskMonitor();
|
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 */
|
/** A value to indicate that this monitor has no progress value set */
|
||||||
public static final int NO_PROGRESS_VALUE = -1;
|
public static final int NO_PROGRESS_VALUE = -1;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user