mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-1157 Created an example script to show how to do simple program exporting using original file bytes.
This commit is contained in:
parent
19fa121b30
commit
e96af04bea
1 changed files with 168 additions and 0 deletions
168
Ghidra/Features/Base/ghidra_scripts/ExportProgramScript.java
Normal file
168
Ghidra/Features/Base/ghidra_scripts/ExportProgramScript.java
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
//Example script to show how to export the current program in its original binary format by
|
||||
//looking at the original bytes compared to the current bytes and exporting the original if they
|
||||
//are the same and the current bytes if they are different. If the changed bytes are relocations,
|
||||
//the original bytes are used. The script only handles simple use cases.
|
||||
//@category Examples
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.reloc.Relocation;
|
||||
import ghidra.program.model.reloc.RelocationTable;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class ExportProgramScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("Must have an open program");
|
||||
return;
|
||||
}
|
||||
|
||||
File outBinaryFile = askFile("Select Binary Output File", "Binary File");
|
||||
|
||||
if (outBinaryFile.exists()) {
|
||||
if (!askYesNo("Binary File Already Exists",
|
||||
"The binary file already exists.\nDo you want to overwrite it?")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// make address set of relocation byte addresses
|
||||
AddressSet relocationAddrs = new AddressSet();
|
||||
|
||||
RelocationTable relocTable = currentProgram.getRelocationTable();
|
||||
Iterator<Relocation> iter = relocTable.getRelocations();
|
||||
while (iter.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Relocation reloc = iter.next();
|
||||
Address relocStart = reloc.getAddress();
|
||||
|
||||
int rlocLen = reloc.getBytes().length;
|
||||
relocationAddrs.add(relocStart, relocStart.add(rlocLen));
|
||||
}
|
||||
|
||||
// create an output stream
|
||||
OutputStream out = new FileOutputStream(outBinaryFile);
|
||||
|
||||
// get all program file bytes
|
||||
Memory memory = currentProgram.getMemory();
|
||||
List<FileBytes> allFileBytes = memory.getAllFileBytes();
|
||||
if (allFileBytes.isEmpty()) {
|
||||
println(
|
||||
"Cannot access original file bytes. Either the program was imported before Ghidra " +
|
||||
"started saving original file bytes or the program was not imported directly from the " +
|
||||
"original binary.");
|
||||
out.close();
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: update to handle multiple case once FileBytes adds new method to support it
|
||||
if (allFileBytes.size() > 1) {
|
||||
println(
|
||||
"*** This program was created using multiple imported programs. This script will currently only work if the program was created with a single imported file.");
|
||||
out.close();
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the original import file bytes
|
||||
FileBytes fileBytes = allFileBytes.get(0);
|
||||
|
||||
long size = fileBytes.getSize();
|
||||
println(
|
||||
"Exporting current program to a new binary file including any changes to the original " +
|
||||
"except for relocation changes...");
|
||||
|
||||
// compare each original imported byte with the current program byte at the equivalent location
|
||||
// if the current byte is different and is not a relocation, export that byte instead of
|
||||
// the original
|
||||
for (long i = 0; i < size; i++) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
byte originalByte = fileBytes.getOriginalByte(i);
|
||||
byte currentByte = fileBytes.getModifiedByte(i);
|
||||
|
||||
int original = originalByte & 0xff;
|
||||
int current = currentByte & 0xff;
|
||||
|
||||
if (originalByte != currentByte) {
|
||||
|
||||
// TODO: once new method is created to use the fileBytes and offset to retrieve the
|
||||
// address, update this to use the correct method and lift the above restriction
|
||||
// on only handling one imported file.
|
||||
List<Address> addresses = memory.locateAddressesForFileOffset(i);
|
||||
|
||||
|
||||
// NOTE: It is rare that there would be more than one address in the program corresponding
|
||||
// to a given offset but check anyway and if any of them are a relocation then
|
||||
// output the original byte
|
||||
if (!addressSetContainsAnyInList(relocationAddrs, addresses)) {
|
||||
|
||||
println(addresses +
|
||||
": Writing out changed byte since it is not a relocation. File offset = " +
|
||||
i + ", originalByte = " + Integer.toHexString(original) +
|
||||
", changed to = " + Integer.toHexString(current));
|
||||
println("****** ");
|
||||
out.write(current);
|
||||
|
||||
}
|
||||
else {
|
||||
// Not writing out change since it is a relocation
|
||||
out.write(original);
|
||||
}
|
||||
|
||||
}
|
||||
// same in current and original
|
||||
else {
|
||||
out.write(original);
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
println("Done!");
|
||||
}
|
||||
|
||||
private boolean addressSetContainsAnyInList(AddressSet set, List<Address> list)
|
||||
throws CancelledException {
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (set.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Address address : list) {
|
||||
monitor.checkCanceled();
|
||||
if (set.contains(address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue