mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 20:57:08 +08:00
Merge remote-tracking branch 'origin/GP-5583_emteere_DefaultCaseFlowAndLabels' into patch
This commit is contained in:
+177
-99
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -48,8 +48,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
protected DecompInterface decompiler;
|
protected DecompInterface decompiler;
|
||||||
private boolean useArraysForSwitchTables = false;
|
private boolean useArraysForSwitchTables = false;
|
||||||
|
|
||||||
public DecompilerSwitchAnalysisCmd(DecompileResults decopmileResults) {
|
public DecompilerSwitchAnalysisCmd(DecompileResults decompileResults) {
|
||||||
this.decompilerResults = decopmileResults;
|
this.decompilerResults = decompileResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -71,20 +71,18 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
|
|
||||||
Function f = decompilerResults.getFunction();
|
Function f = decompilerResults.getFunction();
|
||||||
HighFunction hfunction = decompilerResults.getHighFunction();
|
HighFunction hfunction = decompilerResults.getHighFunction();
|
||||||
processBranchIND(f, hfunction, monitor);
|
|
||||||
|
|
||||||
monitor.checkCancelled();
|
|
||||||
|
|
||||||
String errMsg = getStatusMsg();
|
String errMsg = getStatusMsg();
|
||||||
if (decompilerResults.getHighFunction() == null) {
|
if (hfunction == null) {
|
||||||
String msg = (errMsg != null && errMsg.length() != 0) ? (": " + errMsg) : "";
|
String msg = (errMsg != null && errMsg.length() != 0) ? (": " + errMsg) : "";
|
||||||
Msg.debug(this, " Failed to decompile function: " + f.getName() + msg);
|
Msg.debug(this, " Failed to decompile function: " + f.getName() + msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processBranchIND(f, hfunction, monitor);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
if (!monitor.isCancelled()) {
|
if (!monitor.isCancelled()) {
|
||||||
@@ -114,34 +112,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
continue; // skip switch owned by a different defined function
|
continue; // skip switch owned by a different defined function
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressSetView containingBody =
|
|
||||||
containingFunction != null ? containingFunction.getBody() : null;
|
|
||||||
|
|
||||||
Reference[] referencesFrom = instr.getReferencesFrom();
|
|
||||||
Address[] tableDest = table.getCases();
|
|
||||||
|
|
||||||
boolean foundNotThere = false;
|
|
||||||
int tableIndx;
|
|
||||||
for (tableIndx = 0; tableIndx < tableDest.length; tableIndx++) {
|
|
||||||
monitor.checkCancelled();
|
|
||||||
boolean foundit = false;
|
|
||||||
if (containingBody != null && !containingBody.contains(tableDest[tableIndx])) {
|
|
||||||
// switch case missing from owner function's body
|
|
||||||
foundNotThere = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (Reference element : referencesFrom) {
|
|
||||||
if (element.getToAddress().equals(tableDest[tableIndx])) {
|
|
||||||
foundit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundit) {
|
|
||||||
foundNotThere = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// references already there, ignore this table
|
// references already there, ignore this table
|
||||||
if (!foundNotThere) {
|
if (hasAllReferences(monitor, table, instr, containingFunction)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,64 +130,158 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
labelSwitch(table, monitor);
|
labelSwitch(table, monitor);
|
||||||
|
|
||||||
// disassemble the table
|
// disassemble the table
|
||||||
// pull out the current context so we can flow anything that needs to flow
|
disassembleTable(monitor, table, instr, flowType);
|
||||||
ProgramContext programContext = program.getProgramContext();
|
|
||||||
Register baseContextRegister = programContext.getBaseContextRegister();
|
|
||||||
RegisterValue switchContext = null;
|
|
||||||
if (baseContextRegister != null) {
|
|
||||||
// Use disassembler context based upon context register value at switch address (i.e., computed jump)
|
|
||||||
// Only use flowing context bits
|
|
||||||
switchContext = programContext.getRegisterValue(baseContextRegister, switchAddr);
|
|
||||||
switchContext = programContext.getFlowValue(switchContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
|
||||||
Address[] cases = table.getCases();
|
|
||||||
AddressSet disSetList = new AddressSet();
|
|
||||||
for (Address caseStart : cases) {
|
|
||||||
monitor.checkCancelled();
|
|
||||||
instr.addMnemonicReference(caseStart, flowType, SourceType.ANALYSIS);
|
|
||||||
|
|
||||||
// if conflict skip case
|
|
||||||
if (listing.getUndefinedDataAt(caseStart) == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// already done
|
|
||||||
if (disSetList.contains(caseStart)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
setSwitchTargetContext(programContext, caseStart, switchContext);
|
|
||||||
}
|
|
||||||
catch (ContextChangeException e) {
|
|
||||||
// This can occur when two or more threads are working on the same function
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
disSetList.add(caseStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do all cases at one time
|
|
||||||
if (!disSetList.isEmpty()) {
|
|
||||||
DisassembleCommand cmd = new DisassembleCommand(disSetList, null, true);
|
|
||||||
cmd.applyTo(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixup the function body
|
// fixup the function body
|
||||||
// make sure this case isn't the result of an undefined function, that somehow one of the cases found a real function.
|
fixupFunction(f, monitor, instr);
|
||||||
Function fixupFunc = f;
|
|
||||||
if (fixupFunc instanceof UndefinedFunction) {
|
|
||||||
Function realFunc =
|
|
||||||
program.getFunctionManager().getFunctionContaining(instr.getMinAddress());
|
|
||||||
if (realFunc != null) {
|
|
||||||
fixupFunc = realFunc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Instruction funcStartInstr =
|
|
||||||
program.getListing().getInstructionAt(fixupFunc.getEntryPoint());
|
|
||||||
CreateFunctionCmd.fixupFunctionBody(program, funcStartInstr, monitor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix the functions body with any newly reached code from the switch recovery
|
||||||
|
*/
|
||||||
|
private void fixupFunction(Function f, TaskMonitor monitor, Instruction instr)
|
||||||
|
throws CancelledException {
|
||||||
|
Function fixupFunc = f;
|
||||||
|
|
||||||
|
// Make sure this case isn't the result of an undefined function,
|
||||||
|
// that somehow one of the cases found a real function.
|
||||||
|
if (fixupFunc instanceof UndefinedFunction) {
|
||||||
|
Function realFunc =
|
||||||
|
program.getFunctionManager().getFunctionContaining(instr.getMinAddress());
|
||||||
|
if (realFunc != null) {
|
||||||
|
fixupFunc = realFunc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction funcStartInstr =
|
||||||
|
program.getListing().getInstructionAt(fixupFunc.getEntryPoint());
|
||||||
|
CreateFunctionCmd.fixupFunctionBody(program, funcStartInstr, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disassemble all code reached from the table.
|
||||||
|
* Also adds the case flow references to the switching instruction.
|
||||||
|
*/
|
||||||
|
private void disassembleTable(TaskMonitor monitor, JumpTable table,
|
||||||
|
Instruction instr, FlowType flowType) throws CancelledException {
|
||||||
|
|
||||||
|
Address switchAddr = table.getSwitchAddress();
|
||||||
|
|
||||||
|
// pull out the current context so we can flow anything that needs to flow
|
||||||
|
ProgramContext programContext = program.getProgramContext();
|
||||||
|
Register baseContextRegister = programContext.getBaseContextRegister();
|
||||||
|
RegisterValue switchContext = null;
|
||||||
|
if (baseContextRegister != null) {
|
||||||
|
// Use disassembler context based upon context register value at switch address (i.e., computed jump)
|
||||||
|
// Only use flowing context bits
|
||||||
|
switchContext = programContext.getRegisterValue(baseContextRegister, switchAddr);
|
||||||
|
switchContext = programContext.getFlowValue(switchContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
Address[] cases = table.getCases();
|
||||||
|
Integer[] caseValues = table.getLabelValues();
|
||||||
|
AddressSet disSetList = new AddressSet();
|
||||||
|
|
||||||
|
for (int caseIndex = 0; caseIndex < cases.length; caseIndex++) {
|
||||||
|
Address caseStart = cases[caseIndex];
|
||||||
|
monitor.checkCancelled();
|
||||||
|
|
||||||
|
if (!isDefaultCase(caseValues, caseIndex)) {
|
||||||
|
// only non-default cases should be added to the switching instruction
|
||||||
|
instr.addMnemonicReference(caseStart, flowType, SourceType.ANALYSIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if conflict skip case
|
||||||
|
if (listing.getUndefinedDataAt(caseStart) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// already done
|
||||||
|
if (disSetList.contains(caseStart)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setSwitchTargetContext(programContext, caseStart, switchContext);
|
||||||
|
}
|
||||||
|
catch (ContextChangeException e) {
|
||||||
|
// This can occur when two or more threads are working on the same function
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
disSetList.add(caseStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do all cases at one time
|
||||||
|
if (!disSetList.isEmpty()) {
|
||||||
|
DisassembleCommand cmd = new DisassembleCommand(disSetList, null, true);
|
||||||
|
cmd.applyTo(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if this case index is a default case.
|
||||||
|
*
|
||||||
|
* In general, each case target address should have an associated caseValue.
|
||||||
|
* A case is default if it is first case to not have a case value, or has a magic case value.
|
||||||
|
* It is possible that there could be more than one case without a value. The code shouldn't
|
||||||
|
* blow up if this is the case.
|
||||||
|
*
|
||||||
|
* TODO: Should this check if the default case already has a reference to it
|
||||||
|
* from a conditional jump?
|
||||||
|
*/
|
||||||
|
private boolean isDefaultCase(Integer[] caseValues, int caseIndex) {
|
||||||
|
return (caseIndex == caseValues.length) ||
|
||||||
|
(caseIndex < caseValues.length && caseValues[caseIndex] == DEFAULT_CASE_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the switching instruction has all switch references already.
|
||||||
|
* Extra check for default case target as part of the table, when it shouldn't be.
|
||||||
|
*/
|
||||||
|
public boolean hasAllReferences(TaskMonitor monitor, JumpTable table, Instruction instr,
|
||||||
|
Function containingFunction) throws CancelledException {
|
||||||
|
AddressSetView containingBody =
|
||||||
|
containingFunction != null ? containingFunction.getBody() : null;
|
||||||
|
|
||||||
|
Reference[] referencesFrom = instr.getReferencesFrom();
|
||||||
|
Address[] tableDest = table.getCases();
|
||||||
|
Integer[] caseValues = table.getLabelValues();
|
||||||
|
|
||||||
|
// check that all cases are already a reference on the instruction, except default
|
||||||
|
for (int caseIndex = 0; caseIndex < tableDest.length; caseIndex++) {
|
||||||
|
monitor.checkCancelled();
|
||||||
|
|
||||||
|
// a case is default if it is first case to not have a value, or has a magic case value
|
||||||
|
boolean isDefaultCase = isDefaultCase(caseValues, caseIndex);
|
||||||
|
|
||||||
|
if (containingBody != null && !containingBody.contains(tableDest[caseIndex])) {
|
||||||
|
// switch case missing from owner function's body
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean foundit = false;
|
||||||
|
for (Reference element : referencesFrom) {
|
||||||
|
if (element.getToAddress().equals(tableDest[caseIndex])) {
|
||||||
|
foundit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDefaultCase) {
|
||||||
|
// default case should not be on switching instruction
|
||||||
|
if (foundit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!foundit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the context that should flow to the target so that target instruction will disassemble correctly
|
||||||
|
*/
|
||||||
private void setSwitchTargetContext(ProgramContext programContext, Address targetStart, RegisterValue switchContext) throws ContextChangeException {
|
private void setSwitchTargetContext(ProgramContext programContext, Address targetStart, RegisterValue switchContext) throws ContextChangeException {
|
||||||
if (switchContext == null) {
|
if (switchContext == null) {
|
||||||
return;
|
return;
|
||||||
@@ -236,6 +302,9 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
program.getProgramContext().setRegisterValue(targetStart, targetStart, switchContext);
|
program.getProgramContext().setRegisterValue(targetStart, targetStart, switchContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Label switch table, cases, default with labels in namespace of the switch
|
||||||
|
*/
|
||||||
private void labelSwitch(JumpTable table, TaskMonitor monitor) throws CancelledException {
|
private void labelSwitch(JumpTable table, TaskMonitor monitor) throws CancelledException {
|
||||||
AddLabelCmd tableNameLabel =
|
AddLabelCmd tableNameLabel =
|
||||||
new AddLabelCmd(table.getSwitchAddress(), "switchD", SourceType.ANALYSIS);
|
new AddLabelCmd(table.getSwitchAddress(), "switchD", SourceType.ANALYSIS);
|
||||||
@@ -266,18 +335,24 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
tableNameLabel.applyTo(program);
|
tableNameLabel.applyTo(program);
|
||||||
|
|
||||||
Address[] switchCases = table.getCases();
|
Address[] switchCases = table.getCases();
|
||||||
Integer[] caseLabels = table.getLabelValues();
|
Integer[] caseValues = table.getLabelValues();
|
||||||
Symbol[] caseSymbols = new Symbol[caseLabels.length];
|
Symbol[] caseSymbols = new Symbol[caseValues.length];
|
||||||
SymbolTable symTable = program.getSymbolTable();
|
SymbolTable symTable = program.getSymbolTable();
|
||||||
for (int i = 0; i < switchCases.length; i++) {
|
|
||||||
|
for (int caseIndex = 0; caseIndex < switchCases.length; caseIndex++) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
int offset = (i >= caseLabels.length ? i : caseLabels[i]);
|
|
||||||
String caseName = "caseD_" + Integer.toHexString(offset);
|
// if there are more switchCases than switch values, just use the caseIndex
|
||||||
if (offset == DEFAULT_CASE_VALUE) { // magic constant to indicate default case
|
int caseValue = (caseIndex < caseValues.length) ? caseValues[caseIndex] : caseIndex;
|
||||||
|
|
||||||
|
boolean isDefaultCase = isDefaultCase(caseValues, caseIndex);
|
||||||
|
|
||||||
|
String caseName = "caseD_" + Integer.toHexString(caseValue);
|
||||||
|
if (isDefaultCase) {
|
||||||
caseName = "default";
|
caseName = "default";
|
||||||
}
|
}
|
||||||
AddLabelCmd lcmd =
|
AddLabelCmd lcmd =
|
||||||
new AddLabelCmd(switchCases[i], caseName, space, SourceType.ANALYSIS);
|
new AddLabelCmd(switchCases[caseIndex], caseName, space, SourceType.ANALYSIS);
|
||||||
|
|
||||||
Symbol oldSym = symTable.getPrimarySymbol(lcmd.getLabelAddr());
|
Symbol oldSym = symTable.getPrimarySymbol(lcmd.getLabelAddr());
|
||||||
if (oldSym != null && oldSym.getSource() == SourceType.ANALYSIS &&
|
if (oldSym != null && oldSym.getSource() == SourceType.ANALYSIS &&
|
||||||
@@ -285,8 +360,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
// cleanup AddressTableAnalyzer label
|
// cleanup AddressTableAnalyzer label
|
||||||
oldSym.delete();
|
oldSym.delete();
|
||||||
}
|
}
|
||||||
if (lcmd.applyTo(program) && i < caseSymbols.length) {
|
if (lcmd.applyTo(program) && caseIndex < caseSymbols.length) {
|
||||||
caseSymbols[i] = symTable.getSymbol(caseName, switchCases[i], space);
|
caseSymbols[caseIndex] = symTable.getSymbol(caseName, switchCases[caseIndex], space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,6 +413,9 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
|||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* put labels on the switch table used to compute the target addresses of the switch.
|
||||||
|
*/
|
||||||
private void labelLoadTable(JumpTable.LoadTable loadtable, Address[] switchCases,
|
private void labelLoadTable(JumpTable.LoadTable loadtable, Address[] switchCases,
|
||||||
Symbol[] caseSymbols, Namespace space, TaskMonitor monitor) throws CancelledException {
|
Symbol[] caseSymbols, Namespace space, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
|||||||
+227
@@ -0,0 +1,227 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.decompile;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import generic.test.AbstractGenericTest;
|
||||||
|
import ghidra.app.plugin.core.analysis.*;
|
||||||
|
import ghidra.framework.cmd.Command;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSet;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.program.util.SymbolicPropogator.Value;
|
||||||
|
import ghidra.test.TestEnv;
|
||||||
|
import ghidra.util.TaskUtilities;
|
||||||
|
import ghidra.util.exception.NotFoundException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import junit.framework.AssertionFailedError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of DecompilerSwitchAnalyzer
|
||||||
|
*/
|
||||||
|
public class DecompilerSwitchAnalyzerTest extends AbstractGenericTest {
|
||||||
|
|
||||||
|
private ProgramBuilder builder;
|
||||||
|
private Program program;
|
||||||
|
|
||||||
|
private DecompilerSwitchAnalyzer analyzer;
|
||||||
|
|
||||||
|
public DecompilerSwitchAnalyzerTest() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setAnalysisOptions(String optionName) {
|
||||||
|
int txId = program.startTransaction("Analyze");
|
||||||
|
Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||||
|
analysisOptions.setBoolean(optionName, false);
|
||||||
|
program.endTransaction(txId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultSwitchLabelAndFlow() throws Exception {
|
||||||
|
|
||||||
|
builder = new ProgramBuilder("SwitchDefaultTest", "x86:LE:64:default", "gcc", null);
|
||||||
|
|
||||||
|
//
|
||||||
|
// void main(undefined4 param_1)
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// switch(param_1) {
|
||||||
|
// case 0:
|
||||||
|
// puts("case 0");
|
||||||
|
// break;
|
||||||
|
// case 1:
|
||||||
|
// puts("case 1");
|
||||||
|
// break;
|
||||||
|
// case 2:
|
||||||
|
// puts("case 2");
|
||||||
|
// break;
|
||||||
|
// case 3:
|
||||||
|
// puts("case 3");
|
||||||
|
// break;
|
||||||
|
// case 4:
|
||||||
|
// puts("case 4");
|
||||||
|
// break;
|
||||||
|
// case 5:
|
||||||
|
// puts("case 5");
|
||||||
|
// break;
|
||||||
|
// case 6:
|
||||||
|
// puts("case 6");
|
||||||
|
// break;
|
||||||
|
// case 7:
|
||||||
|
// puts("case 7");
|
||||||
|
// break;
|
||||||
|
// case 8:
|
||||||
|
// puts("case 8");
|
||||||
|
// break;
|
||||||
|
// case 9:
|
||||||
|
// puts("case 9");
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// puts("default");
|
||||||
|
// }
|
||||||
|
// /* WARNING: Subroutine does not return */
|
||||||
|
// exit(0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
builder.setBytes("0x101169",
|
||||||
|
"f30f1efa554889" +
|
||||||
|
"e54883ec10897dfc488975f0837dfc09" +
|
||||||
|
"0f87d70000008b45fc488d1485000000" +
|
||||||
|
"00488d05bc0e00008b04024898488d15" +
|
||||||
|
"b00e00004801d03effe0488d05530e00" +
|
||||||
|
"004889c7e8a7feffffe9ae000000488d" +
|
||||||
|
"05460e00004889c7e893feffffe99a00" +
|
||||||
|
"0000488d05390e00004889c7e87ffeff" +
|
||||||
|
"ffe986000000488d052c0e00004889c7" +
|
||||||
|
"e86bfeffffeb75488d05220e00004889" +
|
||||||
|
"c7e85afeffffeb64488d05180e000048" +
|
||||||
|
"89c7e849feffffeb53488d050e0e0000" +
|
||||||
|
"4889c7e838feffffeb42488d05040e00" +
|
||||||
|
"004889c7e827feffffeb31488d05fa0d" +
|
||||||
|
"00004889c7e816feffffeb20488d05f0" +
|
||||||
|
"0d00004889c7e805feffffeb0f488d05" +
|
||||||
|
"e60d00004889c7e8f4fdffffbf000000" +
|
||||||
|
"00e8fafdffff");
|
||||||
|
|
||||||
|
// switch table
|
||||||
|
builder.setBytes("0x102054",
|
||||||
|
"56f1ffff6af1ffff7ef1ffff" +
|
||||||
|
"92f1ffffa3f1ffffb4f1ffffc5f1ffff" +
|
||||||
|
"d6f1ffffe7f1fffff8f1ffff011b033b");
|
||||||
|
|
||||||
|
builder.disassemble("0x101169", 64);
|
||||||
|
|
||||||
|
builder.createFunction("0x101169");
|
||||||
|
|
||||||
|
analyzer = new DecompilerSwitchAnalyzer();
|
||||||
|
|
||||||
|
program = builder.getProgram();
|
||||||
|
program.startTransaction("Test");
|
||||||
|
|
||||||
|
Address codeStart = addr("0x101169");
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
assertNotNull("Bad instruction disassembly", listing.getInstructionAt(codeStart));
|
||||||
|
|
||||||
|
AddressSet addressSet = new AddressSet(codeStart, codeStart.add(0x200));
|
||||||
|
analyze(addressSet);
|
||||||
|
|
||||||
|
Symbol primarySymbol = program.getSymbolTable().getPrimarySymbol(addr("0x0010125d"));
|
||||||
|
assertEquals("Default label set", primarySymbol.getName(), "default");
|
||||||
|
assertEquals("Switch space set", primarySymbol.getParentNamespace().getName(), "switchD_001011a7");
|
||||||
|
|
||||||
|
primarySymbol = program.getSymbolTable().getPrimarySymbol(addr("0x0010124c"));
|
||||||
|
assertEquals("Case label set", primarySymbol.getName(), "caseD_9");
|
||||||
|
assertEquals("Switch space set", primarySymbol.getParentNamespace().getName(), "switchD_001011a7");
|
||||||
|
|
||||||
|
Instruction instr = listing.getInstructionAt(addr("0x001011a7"));
|
||||||
|
assertNoOperandReference(0, instr);
|
||||||
|
assertNoOperandReference(1, instr);
|
||||||
|
assertNumMnemonicReferences(instr, 10);
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x001011aa"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x001011be"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x001011d2"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x001011e6"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x001011f7"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x00101208"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x00101219"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x0010122a"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x0010123b"));
|
||||||
|
assertMnemonicReferenceTo(instr, addr("0x0010124c"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoOperandReference(int opIndex, Instruction instr) {
|
||||||
|
Reference[] refs = instr.getOperandReferences(opIndex);
|
||||||
|
assertEquals("No reference on operand " + opIndex, 0, refs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNumOperandReferences(int opIndex, Instruction instr, int num) {
|
||||||
|
Reference[] refs = instr.getOperandReferences(opIndex);
|
||||||
|
assertEquals("Operand " + opIndex + " num refs", num, refs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNumMnemonicReferences(Instruction instr, int num) {
|
||||||
|
Reference[] refs = instr.getMnemonicReferences();
|
||||||
|
assertEquals("Mnemonic num refs", num, refs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMnemonicReferenceTo(Instruction instr, Address to) {
|
||||||
|
Reference[] refs = instr.getMnemonicReferences();
|
||||||
|
boolean found = false;
|
||||||
|
for (Reference reference : refs) {
|
||||||
|
if (reference.getToAddress().equals(to)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue("Missing Mnemonic Reference " + to + " on " + instr, found);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertOperandReferenceTo(int opIndex, Instruction instr, Address to) {
|
||||||
|
Reference[] refs = instr.getOperandReferences(opIndex);
|
||||||
|
boolean found = false;
|
||||||
|
for (Reference reference : refs) {
|
||||||
|
if (reference.getToAddress().equals(to)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue("Missing Reference " + to + " on " + instr, found);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyze(AddressSet addrs) throws Exception {
|
||||||
|
analyzer.added(program, addrs, TaskMonitor.DUMMY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address addr(String address) {
|
||||||
|
return builder.addr(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user