mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
GT-2842 correct minor bugs and improve handling of ASCII char strings
when char-size is greater than 1
This commit is contained in:
parent
b90e7cf868
commit
64deecced9
@ -1,236 +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.plugin.core.analysis;
|
||||
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class AbstractCInitAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
private static final String DESCRIPTION =
|
||||
"Initializes the .bss uninitilized memory using data from the .cinit section.";
|
||||
|
||||
private String[] supportProcessors;
|
||||
|
||||
private static final String CINIT = ".cinit";
|
||||
|
||||
public AbstractCInitAnalyzer(String analyzerName, String... supportProcessors) {
|
||||
super(analyzerName, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
||||
this.supportProcessors = supportProcessors;
|
||||
setDefaultEnablement(true);
|
||||
setPriority(AnalysisPriority.FORMAT_ANALYSIS.before().before());
|
||||
}
|
||||
|
||||
private boolean isSupportedProcessor(Processor processor) {
|
||||
String processorName = processor.toString();
|
||||
for (String pname : supportProcessors) {
|
||||
if (processorName.equals(pname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program p) {
|
||||
if (!isSupportedProcessor(p.getLanguage().getProcessor())) {
|
||||
return false;
|
||||
}
|
||||
Memory mem = p.getMemory();
|
||||
MemoryBlock cinitBlock = mem.getBlock(CINIT);
|
||||
if (cinitBlock == null || !cinitBlock.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class CInitRecord {
|
||||
|
||||
private final Address cinitRecordAddr;
|
||||
private final Address sourceDataAddr;
|
||||
private final Address targetAddr;
|
||||
private final int dataLength;
|
||||
private final Address nextRecordAddr;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param cinitRecordAddr start of record address
|
||||
* @param sourceDataAddr address within .cinit section where initialization
|
||||
* data will be copied from
|
||||
* @param targetAddr address within .bss section where initialization
|
||||
* data will be copied to. A null value will cause this record
|
||||
* to be skipped.
|
||||
* @param dataLength number of bytes contained within initialization data
|
||||
* @param nextRecordAddr address of next .cinit record
|
||||
*/
|
||||
public CInitRecord(Address cinitRecordAddr, Address sourceDataAddr, Address targetAddr,
|
||||
int dataLength, Address nextRecordAddr) {
|
||||
this.cinitRecordAddr = cinitRecordAddr;
|
||||
this.sourceDataAddr = sourceDataAddr;
|
||||
this.targetAddr = targetAddr;
|
||||
this.dataLength = dataLength;
|
||||
this.nextRecordAddr = nextRecordAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return start of record address
|
||||
*/
|
||||
public Address getStartOfRecord() {
|
||||
return cinitRecordAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if record is terminal and contains no initialization
|
||||
* data
|
||||
*/
|
||||
public boolean isTerminalRecord() {
|
||||
return getDataLength() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return address within .bss section where initialization
|
||||
* data will be copied to. A null value will cause this record
|
||||
* to be skipped.
|
||||
*/
|
||||
public Address getTargetAddress() {
|
||||
return targetAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return address within .cinit section where initialization
|
||||
* data will be copied from
|
||||
*/
|
||||
public Address getSourceDataAddress() {
|
||||
return sourceDataAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of bytes contained within initialization data
|
||||
*/
|
||||
public int getDataLength() {
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return address of next .cinit record
|
||||
*/
|
||||
public Address getNextRecordAddress() {
|
||||
return nextRecordAddr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get .cinint record and apply record data structure to program
|
||||
* @param program
|
||||
* @param cinitRecordAddr address of a .cinit record
|
||||
* @return record object
|
||||
*/
|
||||
protected abstract CInitRecord getCinitRecord(Program program, Address cinitRecordAddr)
|
||||
throws CodeUnitInsertionException, AddressOverflowException;
|
||||
|
||||
public synchronized boolean added(Program p, AddressSetView set, TaskMonitor monitor,
|
||||
MessageLog log) {
|
||||
|
||||
Memory mem = p.getMemory();
|
||||
MemoryBlock cinitBlock = mem.getBlock(CINIT);
|
||||
if (cinitBlock == null || !set.contains(cinitBlock.getStart())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BookmarkManager bookmarkManager = p.getBookmarkManager();
|
||||
|
||||
Address addr = cinitBlock.getStart();
|
||||
|
||||
MemoryBlock block = null;
|
||||
try {
|
||||
while (addr.compareTo(cinitBlock.getEnd()) < 0) {
|
||||
|
||||
CInitRecord initRec = getCinitRecord(p, addr);
|
||||
if (initRec.isTerminalRecord()) {
|
||||
break;
|
||||
}
|
||||
|
||||
addr = initRec.getNextRecordAddress();
|
||||
|
||||
Address dataAddr = initRec.getTargetAddress();
|
||||
if (dataAddr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Address initDataAddr = initRec.getSourceDataAddress();
|
||||
int byteLen = initRec.getDataLength();
|
||||
|
||||
block = mem.getBlock(dataAddr);
|
||||
if (block == null) {
|
||||
Msg.error(this, "Failed to initialize data at " + dataAddr +
|
||||
" - no memory defined");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!block.isInitialized()) {
|
||||
mem.convertToInitialized(block, (byte) 0);
|
||||
block.setWrite(true);
|
||||
}
|
||||
|
||||
// Read .cinit bytes and copy to intended target address
|
||||
byte[] bytes = new byte[byteLen];
|
||||
if (byteLen != cinitBlock.getBytes(initDataAddr, bytes)) {
|
||||
// we created data - should not have problem reading bytes
|
||||
throw new MemoryAccessException("unexpected end of .cinit block");
|
||||
}
|
||||
|
||||
Msg.debug(this, byteLen + "-bytes at " + dataAddr +
|
||||
" initialized from .cinit data at " + initDataAddr);
|
||||
|
||||
block.putBytes(dataAddr, bytes);
|
||||
|
||||
bookmarkManager.setBookmark(dataAddr, BookmarkType.ANALYSIS, "Data Initilized",
|
||||
byteLen + "-bytes initialized from .cinit data at " + initDataAddr);
|
||||
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
Msg.error(this, "Error occured during block initialization: " + e.getMessage());
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
Msg.error(this, "Failed to create .cinit data structure: " + e.getMessage());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
Msg.error(this, "Unexpected end of .cinit block");
|
||||
}
|
||||
catch (LockException e) {
|
||||
Msg.showError(this, null, getName() + " Failed", getName() +
|
||||
" requires exclusive check-out to perform " + block.getName() +
|
||||
" memory block initialization");
|
||||
return false;
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
throw new AssertException(e); // Unexpected
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -507,7 +507,9 @@ public class StringsAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
for (int i = 0; i < padLength; i++) {
|
||||
address = address.next();
|
||||
|
||||
if (address == null) {
|
||||
return 0;
|
||||
}
|
||||
CodeUnit cu = listing.getCodeUnitContaining(address);
|
||||
if (cu == null) {
|
||||
return 0; // null implies there cannot be data here
|
||||
|
@ -187,11 +187,10 @@ public class StringDataInstance {
|
||||
this.buf = buf;
|
||||
this.charsetName = getCharsetNameFromDataTypeOrSettings(dataType, settings);
|
||||
this.charSize = CharsetInfo.getInstance().getCharsetCharSize(charsetName);
|
||||
// TODO: determine padding of char data type from the dataOrg()
|
||||
this.paddedCharSize = charSize; // stringDataType.getPaddedCharSize(charSize);
|
||||
|
||||
// NOTE: for now only handle padding for charSize == 1
|
||||
this.paddedCharSize =
|
||||
charSize == 1 ? getDataOrganization(dataType).getCharSize() : charSize;
|
||||
this.stringLayout = getLayoutFromDataType(dataType);
|
||||
|
||||
this.showTranslation = TRANSLATION.isShowTranslated(settings);
|
||||
this.translatedValue = TRANSLATION.getTranslatedValue(settings);
|
||||
this.renderSetting = RENDER.getEnumValue(settings);
|
||||
@ -214,6 +213,17 @@ public class StringDataInstance {
|
||||
this.endianSetting = copyFrom.endianSetting;
|
||||
}
|
||||
|
||||
private static DataOrganization getDataOrganization(DataType dataType) {
|
||||
// The dataType should be correspond to the target program
|
||||
if (dataType != null) {
|
||||
DataTypeManager dtm = dataType.getDataTypeManager();
|
||||
if (dtm != null) {
|
||||
return dtm.getDataOrganization();
|
||||
}
|
||||
}
|
||||
return DataOrganizationImpl.getDefaultOrganization();
|
||||
}
|
||||
|
||||
private static StringLayoutEnum getLayoutFromDataType(DataType dataType) {
|
||||
if (dataType instanceof AbstractStringDataType) {
|
||||
return ((AbstractStringDataType) dataType).getStringLayout();
|
||||
|
@ -66,7 +66,7 @@ public class AcyclicCallGraphBuilder {
|
||||
public AcyclicCallGraphBuilder(Program program, Collection<Function> functions,
|
||||
boolean killThunks) {
|
||||
this.program = program;
|
||||
functionSet = new HashSet<Address>();
|
||||
functionSet = new HashSet<>();
|
||||
for (Function function : functions) {
|
||||
if (killThunks) {
|
||||
if (function.isThunk()) {
|
||||
@ -87,9 +87,9 @@ public class AcyclicCallGraphBuilder {
|
||||
public DependencyGraph<Address> getDependencyGraph(TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
DependencyGraph<Address> graph = new DependencyGraph<Address>();
|
||||
DependencyGraph<Address> graph = new DependencyGraph<>();
|
||||
Deque<Address> startPoints = findStartPoints();
|
||||
Set<Address> unprocessed = new TreeSet<Address>(functionSet); // reliable processing order
|
||||
Set<Address> unprocessed = new TreeSet<>(functionSet); // reliable processing order
|
||||
|
||||
while (!unprocessed.isEmpty()) {
|
||||
monitor.checkCanceled();
|
||||
@ -111,7 +111,7 @@ public class AcyclicCallGraphBuilder {
|
||||
}
|
||||
|
||||
private Deque<Address> findStartPoints() {
|
||||
Deque<Address> startPoints = new LinkedList<Address>();
|
||||
Deque<Address> startPoints = new LinkedList<>();
|
||||
|
||||
// populate startPoints with functions that have no callers or are an entry point
|
||||
for (Address address : functionSet) {
|
||||
@ -131,7 +131,7 @@ public class AcyclicCallGraphBuilder {
|
||||
node.children[0] = thunkedfunc.getEntryPoint();
|
||||
return;
|
||||
}
|
||||
ArrayList<Address> children = new ArrayList<Address>();
|
||||
ArrayList<Address> children = new ArrayList<>();
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
AddressIterator referenceSourceIterator =
|
||||
referenceManager.getReferenceSourceIterator(function.getBody(), true);
|
||||
@ -142,7 +142,7 @@ public class AcyclicCallGraphBuilder {
|
||||
Address toAddr = ref.getToAddress();
|
||||
if (ref.getReferenceType().isCall()) {
|
||||
Function childfunc = fmanage.getFunctionAt(toAddr);
|
||||
if (killThunks) {
|
||||
if (childfunc != null && killThunks) {
|
||||
if (childfunc.isThunk()) {
|
||||
childfunc = childfunc.getThunkedFunction(true);
|
||||
toAddr = childfunc.getEntryPoint();
|
||||
@ -205,7 +205,7 @@ public class AcyclicCallGraphBuilder {
|
||||
|
||||
private static Set<Address> findFunctions(Program program, AddressSetView set,
|
||||
boolean killThunks) {
|
||||
Set<Address> functionStarts = new HashSet<Address>();
|
||||
Set<Address> functionStarts = new HashSet<>();
|
||||
|
||||
FunctionIterator functions = program.getFunctionManager().getFunctions(set, true);
|
||||
for (Function function : functions) {
|
||||
@ -227,14 +227,15 @@ public class AcyclicCallGraphBuilder {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return address == null ? "" : address.toString() +
|
||||
(children == null ? " <no children>" : " " + Arrays.toString(children));
|
||||
return address == null ? ""
|
||||
: address.toString() +
|
||||
(children == null ? " <no children>" : " " + Arrays.toString(children));
|
||||
}
|
||||
}
|
||||
|
||||
private static class VisitStack {
|
||||
private Set<Address> inStack = new HashSet<Address>();
|
||||
private Deque<StackNode> stack = new LinkedList<StackNode>();
|
||||
private Set<Address> inStack = new HashSet<>();
|
||||
private Deque<StackNode> stack = new LinkedList<>();
|
||||
|
||||
public VisitStack(Address functionEntry) {
|
||||
push(functionEntry);
|
||||
|
Loading…
Reference in New Issue
Block a user