mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-09 14:08:03 -05:00
302 lines
9.8 KiB
Java
302 lines
9.8 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.
|
|
*/
|
|
package ghidra.lisa;
|
|
|
|
import static org.junit.Assert.*;
|
|
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
|
|
import org.junit.Rule;
|
|
import org.junit.experimental.categories.Categories.ExcludeCategory;
|
|
import org.junit.rules.TestName;
|
|
|
|
import com.contrastsecurity.sarif.SarifSchema210;
|
|
|
|
import db.Transaction;
|
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
|
import ghidra.app.plugin.assembler.*;
|
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
|
import ghidra.app.plugin.core.decompile.DecompilePlugin;
|
|
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
|
import ghidra.app.plugin.core.decompiler.taint.*;
|
|
import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType;
|
|
import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType;
|
|
import ghidra.app.plugin.core.disassembler.DisassemblerPlugin;
|
|
import ghidra.app.services.ProgramManager;
|
|
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
|
import ghidra.framework.model.DomainFolder;
|
|
import ghidra.framework.model.DomainObject;
|
|
import ghidra.framework.plugintool.PluginTool;
|
|
import ghidra.lisa.gui.*;
|
|
import ghidra.lisa.gui.LisaOptions.*;
|
|
import ghidra.program.database.ProgramDB;
|
|
import ghidra.program.disassemble.Disassembler;
|
|
import ghidra.program.model.address.Address;
|
|
import ghidra.program.model.address.AddressSet;
|
|
import ghidra.program.model.data.*;
|
|
import ghidra.program.model.lang.*;
|
|
import ghidra.program.model.listing.*;
|
|
import ghidra.program.model.symbol.SourceType;
|
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
|
import ghidra.test.TestEnv;
|
|
import ghidra.util.*;
|
|
import ghidra.util.exception.CancelledException;
|
|
import ghidra.util.task.ConsoleTaskMonitor;
|
|
import sarif.SarifController;
|
|
import sarif.SarifService;
|
|
import sarif.model.SarifDataFrame;
|
|
|
|
@ExcludeCategory(AbstractLisaTest.class)
|
|
|
|
public class AbstractLisaTest extends AbstractGhidraHeadedIntegrationTest {
|
|
|
|
protected TestEnv env;
|
|
protected PluginTool tool;
|
|
|
|
protected ProgramManager programManager;
|
|
protected Program program;
|
|
private Function f = null;
|
|
protected final ConsoleTaskMonitor monitor = new ConsoleTaskMonitor();
|
|
|
|
CodeBrowserPlugin codeBrowserPlugin;
|
|
ListingPanel staticListing;
|
|
ListingPanel dynamicListing;
|
|
DecompilerProvider decompilerProvider;
|
|
DecompilerPanel decompilerPanel;
|
|
TaintPlugin taint;
|
|
LisaPlugin lisa;
|
|
|
|
protected TaintOptions taintOptions;
|
|
protected LisaOptions lisaOptions;
|
|
protected String taintTarget = null;
|
|
protected String taintAddr = null;
|
|
|
|
protected HashMap<String, List<String>> types;
|
|
protected HashMap<String, List<String>> values;
|
|
|
|
@Rule
|
|
public TestName name = new TestName();
|
|
|
|
public AbstractLisaTest() {
|
|
init();
|
|
}
|
|
|
|
protected String getProgramName() {
|
|
return "static-" + getClass().getCanonicalName() + "." + name.getMethodName();
|
|
}
|
|
|
|
public void init() {
|
|
try {
|
|
env = new TestEnv();
|
|
tool = env.getTool();
|
|
programManager = tool.getService(ProgramManager.class);
|
|
env.showTool();
|
|
addPlugins();
|
|
f = createSimpleProgramX86_64();
|
|
}
|
|
catch (Throwable e) {
|
|
Msg.error(this, e.getMessage());
|
|
}
|
|
programManager.openProgram(program);
|
|
taintOptions = taint.getOptions();
|
|
lisaOptions = lisa.getOptions();
|
|
lisaOptions.setHeapDomain(HeapDomainOption.DEFAULT);
|
|
lisaOptions.setTypeDomain(TypeDomainOption.DEFAULT);
|
|
lisaOptions.setValueDomain(ValueDomainOption.DEFAULT);
|
|
lisaOptions.setShowTop(true);
|
|
lisaOptions.setShowUnique(true);
|
|
}
|
|
|
|
public void runTest() {
|
|
LisaTaintState state = (LisaTaintState) taint.getTaintState();
|
|
state.setSuppressTop(false);
|
|
if (taintTarget != null) {
|
|
state.setTaint(MarkType.SOURCE, f, program.getAddressFactory().getAddress(taintAddr),
|
|
taintTarget);
|
|
}
|
|
monitor.initialize(program.getFunctionManager().getFunctionCount());
|
|
state.queryIndex(program, tool, QueryType.DEFAULT);
|
|
SarifSchema210 data = state.getData();
|
|
SarifService sarifService = taint.getSarifService();
|
|
SarifController controller = sarifService.getController();
|
|
SarifDataFrame df = new SarifDataFrame(data, controller, false);
|
|
List<Map<String, Object>> results = df.getTableResults();
|
|
types = new HashMap<>();
|
|
values = new HashMap<>();
|
|
for (Map<String, Object> map : results) {
|
|
String key = (String) map.get("location");
|
|
key = key.substring(key.indexOf(":") + 1);
|
|
String type = (String) map.get("type");
|
|
String value = (String) map.get("value");
|
|
List<String> list = types.get(key);
|
|
if (list == null) {
|
|
list = new ArrayList<>();
|
|
types.put(key, list);
|
|
}
|
|
list.add(type);
|
|
list = values.get(key);
|
|
if (list == null) {
|
|
list = new ArrayList<>();
|
|
values.put(key, list);
|
|
}
|
|
list.add(value);
|
|
}
|
|
monitor.clearCancelled();
|
|
}
|
|
|
|
protected void createProgram(Language lang, CompilerSpec cSpec) throws IOException {
|
|
program = new ProgramDB(getProgramName(), lang, cSpec, this);
|
|
}
|
|
|
|
protected void createProgram(String languageID, String cSpecID) throws IOException {
|
|
Language language = getLanguageService().getLanguage(new LanguageID(languageID));
|
|
CompilerSpec cSpec = cSpecID == null ? language.getDefaultCompilerSpec()
|
|
: language.getCompilerSpecByID(new CompilerSpecID(cSpecID));
|
|
createProgram(language, cSpec);
|
|
}
|
|
|
|
public static void waitForDomainObject(DomainObject object) {
|
|
object.flushEvents();
|
|
waitForSwing();
|
|
}
|
|
|
|
protected void intoProject(DomainObject obj) {
|
|
waitForDomainObject(obj);
|
|
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
|
|
waitForCondition(() -> {
|
|
try {
|
|
rootFolder.createFile(obj.getName(), obj, monitor);
|
|
return true;
|
|
}
|
|
catch (InvalidNameException | CancelledException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
catch (IOException e) {
|
|
// Usually "object is busy". Try again.
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void addPlugins() throws Throwable {
|
|
codeBrowserPlugin = addPlugin(tool, CodeBrowserPlugin.class);
|
|
staticListing = codeBrowserPlugin.getProvider().getListingPanel();
|
|
addPlugin(tool, DisassemblerPlugin.class);
|
|
addPlugin(tool, DecompilePlugin.class);
|
|
taint = addPlugin(tool, TaintPlugin.class);
|
|
lisa = addPlugin(tool, LisaPlugin.class);
|
|
TaintState state = TaintState.newInstance(taint, "lisa");
|
|
taint.setTaintState(state);
|
|
|
|
decompilerProvider = waitForComponentProvider(DecompilerProvider.class);
|
|
decompilerPanel = decompilerProvider.getDecompilerPanel();
|
|
}
|
|
|
|
protected Function createSimpleProgramX86_64() throws Throwable {
|
|
createProgram("x86:LE:64:default", "gcc");
|
|
intoProject(program);
|
|
|
|
try (Transaction tx = program.openTransaction("Assemble")) {
|
|
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
|
Structure structure = new StructureDataType("MyStruct", 0, dtm);
|
|
structure.add(DWordDataType.dataType, "f1", "");
|
|
structure.add(DWordDataType.dataType, "f2", "");
|
|
structure.add(QWordDataType.dataType, "f3", "");
|
|
structure =
|
|
(Structure) dtm.addDataType(structure, DataTypeConflictHandler.DEFAULT_HANDLER);
|
|
|
|
Address entry = addr(program, 0x00400000);
|
|
program.getMemory()
|
|
.createInitializedBlock(".text", entry, 0x1000, (byte) 0, monitor, false);
|
|
|
|
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
|
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
|
|
|
buf.assemble("PUSH RBP");
|
|
buf.assemble("MOV RBP, RSP");
|
|
buf.assemble("MOV RAX, 0x4");
|
|
buf.assemble("SUB AX, 0x5");
|
|
buf.assemble("MOV RDX, RAX");
|
|
buf.assemble("RET");
|
|
Address end = buf.getNext();
|
|
|
|
program.getMemory().setBytes(entry, buf.getBytes());
|
|
|
|
Disassembler dis = Disassembler.getDisassembler(program, monitor, null);
|
|
dis.disassemble(entry, null);
|
|
|
|
Function funFillStruct = program.getFunctionManager()
|
|
.createFunction("simple", entry, new AddressSet(entry, end.previous()),
|
|
SourceType.ANALYSIS);
|
|
funFillStruct.addLocalVariable(new LocalVariableImpl("s", structure, -0x18, program),
|
|
SourceType.ANALYSIS);
|
|
|
|
return funFillStruct;
|
|
}
|
|
}
|
|
|
|
public static final AssemblySelector NO_16BIT_CALLS = new AssemblySelector() {
|
|
@Override
|
|
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
|
throws AssemblySemanticException {
|
|
for (AssemblyResolvedPatterns res : filterCompatibleAndSort(rr, ctx)) {
|
|
byte[] ins = res.getInstruction().getVals();
|
|
// HACK to avoid 16-bit CALL.... TODO: Why does this happen?
|
|
if (ins.length >= 2 && ins[0] == (byte) 0x66 && ins[1] == (byte) 0xe8) {
|
|
Msg.error(this,
|
|
"Filtered 16-bit call " + NumericUtilities.convertBytesToString(ins));
|
|
continue;
|
|
}
|
|
return new Selection(res.getInstruction().fillMask(), res.getContext());
|
|
}
|
|
throw new AssemblySemanticException(semanticErrors);
|
|
}
|
|
};
|
|
|
|
protected static Address addr(Program program, long offset) {
|
|
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
|
}
|
|
|
|
protected String typeOf(String key) {
|
|
return of(types, key);
|
|
}
|
|
|
|
protected String valueOf(String key) {
|
|
return of(values, key);
|
|
}
|
|
|
|
private String of(Map<String, List<String>> map, String key) {
|
|
List<String> list = map.get(key);
|
|
if (list == null) {
|
|
return null;
|
|
}
|
|
if (list.size() == 1) {
|
|
return list.get(0);
|
|
}
|
|
String[] sorted = new String[list.size()];
|
|
list.toArray(sorted);
|
|
Arrays.sort(sorted);
|
|
return Arrays.toString(sorted);
|
|
}
|
|
|
|
protected void equalsAssert(Object actual, Object expected) {
|
|
assertEquals(expected, actual);
|
|
}
|
|
|
|
}
|