ghidra/Ghidra/Features/Base/ghidra_scripts/FixElfExternalOffsetDataRelocationScript.java

215 lines
7.2 KiB
Java

/* ###
* 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.
*/
//
// With ELF imports performed with Ghidra 10.0 and 10.1 certain data relocations
// which corresponded to symbol pointers with an additional offset produced an
// ERROR bookmark and was not applied. With Ghidra 10.2 such locations now
// get the relocation applied and should utilize a Pointer-Typedef with an
// pointer offset setting. Use of a normal pointer will produce an invalid
// reference which was the original reason we avoided applying the relocation.
//
// This script applies the correct relocaton by modifying the memory bytes
// at all bookmarked locations and applies a WARNING bookmark. If data has been
// applied script will attempt to correct using an offset pointer-typedef.
//
// Script may be constrained by a selection.
//
//@category ELF Relocations
import java.util.Iterator;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.format.elf.relocation.ElfRelocationHandler;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.*;
import ghidra.util.GhidraDataConverter;
import ghidra.util.Msg;
public class FixElfExternalOffsetDataRelocationScript extends GhidraScript {
private static final String EXT_RELO_BOOKMARK_CATEGORY = "EXTERNAL Relocation";
private static final String EXT_RELO_BOOKMARK_TEXT_PREFIX =
"Unsupported EXTERNAL Data Elf Relocation: External Location =";
@Override
protected void run() throws Exception {
if (currentProgram == null) {
popup("No active program");
return;
}
MessageLog log = new MessageLog(); // throw-away log
Iterator<Bookmark> errorBookmarks =
currentProgram.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR);
while (errorBookmarks.hasNext()) {
Bookmark b = errorBookmarks.next();
if (EXT_RELO_BOOKMARK_CATEGORY.equals(b.getCategory()) &&
b.getComment().startsWith(EXT_RELO_BOOKMARK_TEXT_PREFIX)) {
if (currentSelection != null && !currentSelection.contains(b.getAddress())) {
continue;
}
try {
if (!updateExternalDataRelocation(b, log)) {
Msg.error(this,
"Failed to update EXTERNAL relocation at " + b.getAddress());
}
}
catch (Exception e) {
Msg.error(this, "Error occured while updating EXTERNAL relocation at " +
b.getAddress() + ": " + e.getMessage());
}
}
}
}
private boolean updateExternalDataRelocation(Bookmark relocErrorBookmark, MessageLog log) throws Exception {
Address address = relocErrorBookmark.getAddress();
String bookmarkComment = relocErrorBookmark.getComment();
int byteSize = address.getAddressSpace().getPointerSize();
int index = bookmarkComment.lastIndexOf("0x");
if (index < 0) {
return false;
}
char signChar = bookmarkComment.charAt(index - 1);
int offset;
try {
offset = Integer.parseInt(bookmarkComment.substring(index + 2), 16);
}
catch (NumberFormatException e) {
return false;
}
if (signChar == '-') {
offset = -offset;
}
else if (signChar != '+') {
return false;
}
Memory memory = currentProgram.getMemory();
DumbMemBufferImpl buf = new DumbMemBufferImpl(memory, address);
Address symbolAddr = PointerDataType.getAddressValue(buf, byteSize, address.getAddressSpace());
if (symbolAddr == null) {
return false; // invalid pointer data
}
String symbolName = bookmarkComment.substring(EXT_RELO_BOOKMARK_TEXT_PREFIX.length(), index - 1).trim();
if (currentProgram.getSymbolTable().getSymbol(symbolName, symbolAddr, null) == null) {
return false; // EXTERNAL block symbol not found at stored address
}
Listing listing = currentProgram.getListing();
Data data = listing.getDataContaining(address);
if (data == null) {
return false; // possible instruction applied
}
DataType dt = data.getDataType();
boolean isDefaultTypeApplied = address.equals(data.getAddress()) &&
(Undefined.isUndefined(dt) || isDefaultPointer(dt));
int componentOffset = (int) address.subtract(data.getAddress());
if (!isDefaultTypeApplied &&
!canFixupStructure(dt, componentOffset, address.getPointerSize())) {
return false; // unsupported datatype applied
}
long newValue = symbolAddr.getOffset() + offset;
GhidraDataConverter converter = GhidraDataConverter.getInstance(buf.isBigEndian());
byte[] bytes = new byte[byteSize];
converter.putValue(newValue, byteSize, bytes, 0);
memory.setBytes(address, bytes);
currentProgram.getBookmarkManager().removeBookmark(relocErrorBookmark);
ElfRelocationHandler.warnExternalOffsetRelocation(currentProgram, address, symbolAddr, symbolName, offset, log);
DataType offsetPtrDt =
currentProgram.getDataTypeManager()
.resolve(new PointerTypedef(null, null, -1, currentProgram.getDataTypeManager(),
offset), null);
if (isDefaultTypeApplied) {
// Replace undefined/default data with offset-pointer
DataUtilities.createData(currentProgram, address, offsetPtrDt, -1,
ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
}
else {
Structure s = (Structure) dt;
DataTypeComponent dtc = s.getComponentAt(componentOffset);
if (!offsetPtrDt.isEquivalent(dtc.getDataType())) {
s.replace(dtc.getOrdinal(), offsetPtrDt, -1);
}
ReferenceManager refMgr = currentProgram.getReferenceManager();
Reference ref = refMgr.getPrimaryReferenceFrom(address, 0);
if (ref != null && symbolAddr.equals(ref.getToAddress())) {
// Replace reference with default offset reference
refMgr.delete(ref);
refMgr.addOffsetMemReference(address, symbolAddr, true, offset, RefType.DATA,
SourceType.DEFAULT, 0);
}
}
return true;
}
private boolean isDefaultPointer(DataType dt) {
if (dt instanceof Pointer) {
DataType refDt = ((Pointer) dt).getDataType();
return refDt == null || refDt == DataType.DEFAULT;
}
return false;
}
private boolean canFixupStructure(DataType dt, int componentOffset, int pointerLength) {
if (!(dt instanceof Structure)) {
return false;
}
Structure s = (Structure) dt;
DataTypeComponent dtc = s.getComponentAt(componentOffset);
if (dtc.getLength() != pointerLength) {
return false;
}
DataType cdt = dtc.getDataType();
if (cdt instanceof Pointer) {
return true;
}
// Check for case where structure may already have been modified
if (cdt instanceof TypeDef) {
TypeDef td = (TypeDef) cdt;
if (!(td.getDataType() instanceof Pointer)) {
return false;
}
if (!td.isAutoNamed()) {
return false;
}
return true;
}
return false;
}
}