mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00

Refactored ProjectDataTypeManager to extend StandaloneDataTypeManager. Added actions to datatype tree to allow setting archive architecture. Added use of storage translators when switching architectures. Allow FunctionDefinition to accept arbitrary calling convention names and many other misc changes.
152 lines
5.2 KiB
Java
152 lines
5.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.
|
|
*/
|
|
//Upgrade DEX program(s) that have function prototypes layed down prior to Ghidra 7.1
|
|
//@category Upgrade
|
|
|
|
import java.util.Map;
|
|
|
|
import ghidra.app.cmd.function.ApplyFunctionSignatureCmd;
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.framework.model.*;
|
|
import ghidra.framework.plugintool.PluginTool;
|
|
import ghidra.program.model.address.AddressSpace;
|
|
import ghidra.program.model.data.FunctionDefinitionDataType;
|
|
import ghidra.program.model.lang.*;
|
|
import ghidra.program.model.listing.*;
|
|
import ghidra.program.model.pcode.Varnode;
|
|
import ghidra.program.model.symbol.SourceType;
|
|
import ghidra.util.exception.*;
|
|
|
|
/**
|
|
* There was a major rearrangement of registers in the Dalvik.slaspec from 7.0 -> 7.1 which invalidates function prototypes
|
|
* laid down by "Android DEX Header Format" analyzer. This script repairs the prototypes to match the new register layout
|
|
* If run with a Program already up, the script will make all the changes, letting the user decide if they want to
|
|
* save (or undo) the changes. If the script is run from an empty code browser, it will search for all Dalvik programs
|
|
* in the current project and automatically upgrade and save the function prototypes.
|
|
*
|
|
*/
|
|
public class UpgradeDexToGhidra71Script extends GhidraScript {
|
|
|
|
@Override
|
|
public void run() throws Exception {
|
|
|
|
if ( currentProgram != null ) {
|
|
processProgram(currentProgram);
|
|
return;
|
|
}
|
|
|
|
PluginTool tool = state.getTool();
|
|
Project project = tool.getProject();
|
|
ProjectData projectData = project.getProjectData();
|
|
DomainFolder rootFolder = projectData.getRootFolder();
|
|
recurseProjectFolder( rootFolder );
|
|
}
|
|
|
|
private void recurseProjectFolder( DomainFolder domainFolder ) throws Exception {
|
|
DomainFile[] files = domainFolder.getFiles();
|
|
for ( DomainFile domainFile : files ) {
|
|
monitor.checkCanceled();
|
|
try {
|
|
processDomainFile( domainFile );
|
|
} catch(Exception ex) {
|
|
printerr(ex.getMessage());
|
|
}
|
|
}
|
|
DomainFolder[] folders = domainFolder.getFolders();
|
|
for ( DomainFolder folder : folders ) {
|
|
monitor.checkCanceled();
|
|
recurseProjectFolder( folder );
|
|
}
|
|
}
|
|
|
|
private void processDomainFile(DomainFile domainFile ) throws Exception {
|
|
Map<String, String> metadata = domainFile.getMetadata();
|
|
if (metadata == null) {
|
|
return;
|
|
}
|
|
String formatString = metadata.get("Executable Format");
|
|
if (formatString == null) {
|
|
return;
|
|
}
|
|
if (!formatString.equals("Dalvik Executable (DEX)")) {
|
|
return;
|
|
}
|
|
DomainObject domainObject = domainFile.getDomainObject(this, true, true, monitor);
|
|
try {
|
|
Program program = (Program) domainObject;
|
|
processProgram(program);
|
|
saveProgram(program);
|
|
} finally {
|
|
domainObject.release(this);
|
|
}
|
|
}
|
|
|
|
private void processProgram(Program program) throws CancelledException {
|
|
println("Updating program: "+program.getName());
|
|
int id = program.startTransaction("Update DEX parameters");
|
|
boolean success = false;
|
|
try {
|
|
for (Function func : program.getFunctionManager().getFunctions(true)) {
|
|
monitor.checkCanceled();
|
|
processFunction(func);
|
|
}
|
|
success = true;
|
|
} finally {
|
|
program.endTransaction(id, success);
|
|
}
|
|
}
|
|
|
|
private void processFunction(Function func) {
|
|
monitor.setMessage("Updating: "+func.getName());
|
|
FunctionDefinitionDataType sig = new FunctionDefinitionDataType(func,false);
|
|
try {
|
|
sig.setCallingConvention(CompilerSpec.CALLING_CONVENTION_stdcall);
|
|
}
|
|
catch (InvalidInputException e) {
|
|
throw new AssertException(e);
|
|
}
|
|
func.setCustomVariableStorage(false);
|
|
ApplyFunctionSignatureCmd cmd = new ApplyFunctionSignatureCmd(func.getEntryPoint(),sig,SourceType.ANALYSIS);
|
|
cmd.applyTo(func.getProgram());
|
|
|
|
Program program = func.getProgram();
|
|
Language language = program.getLanguage();
|
|
AddressSpace registerSpace = program.getAddressFactory().getRegisterSpace();
|
|
Variable[] localVariables = func.getLocalVariables();
|
|
|
|
for (Variable var : localVariables) {
|
|
Varnode varnode = var.getFirstStorageVarnode();
|
|
if (!varnode.isRegister()) {
|
|
continue;
|
|
}
|
|
if (varnode.getOffset() >= 0x1000)
|
|
{
|
|
continue; // Already converted
|
|
}
|
|
long offset = varnode.getOffset() + 0x1000 - 8;
|
|
int size = varnode.getSize();
|
|
Register localRegister = language.getRegister(registerSpace, offset, size);
|
|
try {
|
|
LocalVariableImpl newlocal = new LocalVariableImpl( var.getName(), 0, var.getDataType(), localRegister, func.getProgram() );
|
|
func.removeVariable(var);
|
|
func.addLocalVariable(newlocal, SourceType.ANALYSIS);
|
|
} catch (InvalidInputException e) {
|
|
} catch (DuplicateNameException e) {
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|