150 lines
5.3 KiB
Java
150 lines
5.3 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.data.GenericCallingConvention;
|
|
import ghidra.program.model.lang.Language;
|
|
import ghidra.program.model.lang.Register;
|
|
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);
|
|
sig.setGenericCallingConvention(GenericCallingConvention.stdcall);
|
|
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) {
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|