mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-29 22:46:44 +08:00
GP-6225: Adds abstract interpretation via the Software and System
Verification (SSV) group @ Università Ca' Foscari's Library for Static Analysis (LiSA)
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
MODULE FILE LICENSE: lib/lisa-analyses-0.1.jar MIT
|
||||||
|
MODULE FILE LICENSE: lib/lisa-program-0.1.jar MIT
|
||||||
|
MODULE FILE LICENSE: lib/lisa-sdk-0.1.jar MIT
|
||||||
|
MODULE FILE LICENSE: lib/joda-time-2.14.0.jar Apache License 2.0
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
apply from: "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle"
|
||||||
|
//apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||||
|
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||||
|
apply from: "$rootProject.projectDir/gradle/helpProject.gradle"
|
||||||
|
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||||
|
|
||||||
|
apply plugin: 'eclipse'
|
||||||
|
eclipse.project.name = 'Xtra Lisa'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':Base')
|
||||||
|
api project(':Decompiler')
|
||||||
|
api project(':DecompilerDependent')
|
||||||
|
|
||||||
|
api("io.github.lisa-analyzer:lisa-analyses:0.1") {
|
||||||
|
exclude group: 'org.apache.commons'
|
||||||
|
exclude group: 'org.apache.logging.log4j'
|
||||||
|
exclude group: 'com.fasterxml.jackson.core'
|
||||||
|
exclude group: 'org.graphstream'
|
||||||
|
exclude group: 'org.reflections'
|
||||||
|
}
|
||||||
|
api("io.github.lisa-analyzer:lisa-program:0.1") {
|
||||||
|
exclude group: 'org.apache.commons'
|
||||||
|
exclude group: 'org.apache.logging.log4j'
|
||||||
|
exclude group: 'com.fasterxml.jackson.core'
|
||||||
|
exclude group: 'org.graphstream'
|
||||||
|
exclude group: 'org.reflections'
|
||||||
|
}
|
||||||
|
api("io.github.lisa-analyzer:lisa-sdk:0.1") {
|
||||||
|
exclude group: 'org.apache.commons'
|
||||||
|
exclude group: 'org.apache.logging.log4j'
|
||||||
|
exclude group: 'com.fasterxml.jackson.core'
|
||||||
|
exclude group: 'org.graphstream'
|
||||||
|
exclude group: 'org.reflections'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joda is needed at run time
|
||||||
|
api "joda-time:joda-time:2.14.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
##VERSION: 2.0
|
||||||
|
##MODULE IP: Apache License 2.0
|
||||||
|
##MODULE IP: MIT
|
||||||
|
Module.manifest||GHIDRA||||END|
|
||||||
|
extension.properties||GHIDRA||||END|
|
||||||
|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/LisaPlugin/LisaPlugin.html||GHIDRA||||END|
|
||||||
|
src/main/java/ghidra/lisa/pcode/analyses/README.md||GHIDRA||||END|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
name=Lisa
|
||||||
|
description=Abstract interpretation engine based on the Universita Ca Foscari's Library for Static Analysis (LiSA)
|
||||||
|
author=Ghidra Team
|
||||||
|
createdOn=9/9/2025
|
||||||
|
version=@extversion@
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Template for running analyses using Lisa (does very little, as is)
|
||||||
|
//@category PCode
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.lisa.pcode.PcodeFrontend;
|
||||||
|
import ghidra.lisa.pcode.analyses.PcodeByteBasedConstantPropagation;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import it.unive.lisa.*;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||||
|
import it.unive.lisa.program.Program;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import it.unive.lisa.symbolic.value.Identifier;
|
||||||
|
import it.unive.lisa.util.representation.StructuredRepresentation;
|
||||||
|
|
||||||
|
public class LisaLaunchScript extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
if (isRunningHeadless()) {
|
||||||
|
popup("Script is not running in GUI");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeFrontend frontend = new PcodeFrontend();
|
||||||
|
Program p = frontend.doWork(currentProgram.getListing(), currentAddress);
|
||||||
|
|
||||||
|
DefaultConfiguration conf = new DefaultConfiguration();
|
||||||
|
conf.serializeResults = true;
|
||||||
|
conf.abstractState = DefaultConfiguration.simpleState(
|
||||||
|
DefaultConfiguration.defaultHeapDomain(),
|
||||||
|
//new DefiniteDataflowDomain<>(new ConstantPropagation()),
|
||||||
|
new ValueEnvironment<>(
|
||||||
|
new PcodeByteBasedConstantPropagation(currentProgram.getLanguage())),
|
||||||
|
DefaultConfiguration.defaultTypeDomain());
|
||||||
|
LiSA lisa = new LiSA(conf);
|
||||||
|
|
||||||
|
LiSAReport report = lisa.run(p);
|
||||||
|
InterproceduralAnalysis<?> interproceduralAnalysis =
|
||||||
|
report.getConfiguration().interproceduralAnalysis;
|
||||||
|
Collection<CFG> ep = p.getEntryPoints();
|
||||||
|
for (CFG cfg : ep) {
|
||||||
|
Collection<?> results = interproceduralAnalysis.getAnalysisResultsOf(cfg);
|
||||||
|
Iterator<?> iterator = results.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object next = iterator.next();
|
||||||
|
if (next instanceof AnalyzedCFG<?> acfg) {
|
||||||
|
processCFG(cfg, acfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processCFG(CFG cfg, AnalyzedCFG<?> acfg) {
|
||||||
|
if (!cfg.getNodes().isEmpty()) {
|
||||||
|
for (Statement st : cfg.getNodes()) {
|
||||||
|
AnalysisState<?> s = acfg.getAnalysisStateAfter(st);
|
||||||
|
AbstractState<?> abs = s.getState();
|
||||||
|
if (abs instanceof SimpleAbstractState sas) {
|
||||||
|
processState(sas, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private void processState(SimpleAbstractState sas, Statement st) {
|
||||||
|
ValueEnvironment<?> valueState =
|
||||||
|
(ValueEnvironment<?>) sas.getValueState();
|
||||||
|
//DefiniteDataflowDomain valueState = (DefiniteDataflowDomain) sas.getValueState();
|
||||||
|
Map<Identifier, ?> function = valueState.function;
|
||||||
|
if (function != null) {
|
||||||
|
for (Object key : function.keySet()) {
|
||||||
|
Object val = valueState.function.get(key);
|
||||||
|
exampleAnalysis(st, key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exampleAnalysis(Statement st, Object key, Object val) {
|
||||||
|
if (val instanceof PcodeByteBasedConstantPropagation icp) {
|
||||||
|
StructuredRepresentation representation =
|
||||||
|
icp.representation();
|
||||||
|
String rep = representation.toString();
|
||||||
|
if (!rep.contains("TOP")) {
|
||||||
|
PcodeLocation loc =
|
||||||
|
(PcodeLocation) st.getLocation();
|
||||||
|
Msg.info(this, loc.getCodeLocation() +
|
||||||
|
" ==> " + key + ":" + representation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,457 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Uses overriding references and the LiSA constant propagator to resolve system calls
|
||||||
|
//@category Analysis
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.app.cmd.function.ApplyFunctionDataTypesCmd;
|
||||||
|
import ghidra.app.cmd.memory.AddUninitializedMemoryBlockCmd;
|
||||||
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
|
import ghidra.app.util.opinion.ElfLoader;
|
||||||
|
import ghidra.framework.Application;
|
||||||
|
import ghidra.lisa.pcode.PcodeFrontend;
|
||||||
|
import ghidra.lisa.pcode.analyses.PcodeByteBasedConstantPropagation;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.lang.SpaceNames;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import it.unive.lisa.*;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import it.unive.lisa.symbolic.value.Identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This script will resolve system calls for x86 or x64 Linux binaries.
|
||||||
|
* It assumes that in the x64 case, the syscall native instruction is used to make system calls,
|
||||||
|
* and in the x86 case, system calls are made via an indirect call to GS:[0x10].
|
||||||
|
* It should be straightforward to modify this script for other cases.
|
||||||
|
*/
|
||||||
|
public class Lisa_ResolveX86orX64LinuxSyscallsScript extends GhidraScript {
|
||||||
|
|
||||||
|
//disassembles to "CALL dword ptr GS:[0x10]"
|
||||||
|
private static final byte[] x86_bytes = { 0x65, -1, 0x15, 0x10, 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
|
private static final String X86 = "x86";
|
||||||
|
|
||||||
|
private static final String SYSCALL_SPACE_NAME = "syscall";
|
||||||
|
|
||||||
|
private static final int SYSCALL_SPACE_LENGTH = 0x10000;
|
||||||
|
|
||||||
|
//this is the name of the userop (aka CALLOTHER) in the pcode translation of the
|
||||||
|
//native "syscall" instruction
|
||||||
|
private static final String SYSCALL_X64_CALLOTHER = "syscall";
|
||||||
|
|
||||||
|
//a set of names of all syscalls that do not return
|
||||||
|
private static final Set<String> noreturnSyscalls = Set.of("exit", "exit_group");
|
||||||
|
|
||||||
|
//tests whether an instruction is making a system call
|
||||||
|
private Predicate<Instruction> tester;
|
||||||
|
|
||||||
|
//register holding the syscall number
|
||||||
|
private String syscallRegister;
|
||||||
|
|
||||||
|
//datatype archive containing signature of system calls
|
||||||
|
private String datatypeArchiveName;
|
||||||
|
|
||||||
|
//file containing map from syscall numbers to syscall names
|
||||||
|
//note that different architectures can have different system call numbers, even
|
||||||
|
//if they're both Linux...
|
||||||
|
private String syscallFileName;
|
||||||
|
|
||||||
|
//the type of overriding reference to apply
|
||||||
|
private RefType overrideType;
|
||||||
|
|
||||||
|
//the calling convention to use for system calls (must be defined in the appropriate .cspec file)
|
||||||
|
private String callingConvention;
|
||||||
|
|
||||||
|
private InterproceduralAnalysis<?> ipa;
|
||||||
|
|
||||||
|
private PcodeFrontend frontend;
|
||||||
|
|
||||||
|
private LiSA lisa;
|
||||||
|
|
||||||
|
private Map<Address, CFG> targets = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
|
||||||
|
if (!(currentProgram.getExecutableFormat().equals(ElfLoader.ELF_NAME) &&
|
||||||
|
currentProgram.getLanguage().getProcessor().toString().equals(X86))) {
|
||||||
|
popup("This script is intended for x86 or x64 Linux files");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//determine whether the executable is 32 or 64 bit and set fields appropriately
|
||||||
|
int size = currentProgram.getLanguage().getLanguageDescription().getSize();
|
||||||
|
if (size == 64) {
|
||||||
|
tester = Lisa_ResolveX86orX64LinuxSyscallsScript::checkX64Instruction;
|
||||||
|
syscallRegister = "RAX";
|
||||||
|
datatypeArchiveName = "generic_clib_64";
|
||||||
|
syscallFileName = "x64_linux_syscall_numbers";
|
||||||
|
overrideType = RefType.CALLOTHER_OVERRIDE_CALL;
|
||||||
|
callingConvention = "syscall";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tester = Lisa_ResolveX86orX64LinuxSyscallsScript::checkX86Instruction;
|
||||||
|
syscallRegister = "EAX";
|
||||||
|
datatypeArchiveName = "generic_clib";
|
||||||
|
syscallFileName = "x86_linux_syscall_numbers";
|
||||||
|
overrideType = RefType.CALL_OVERRIDE_UNCONDITIONAL;
|
||||||
|
callingConvention = "syscall";
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the space where the system calls live.
|
||||||
|
//If it doesn't exist, create it.
|
||||||
|
AddressSpace syscallSpace =
|
||||||
|
currentProgram.getAddressFactory().getAddressSpace(SYSCALL_SPACE_NAME);
|
||||||
|
if (syscallSpace == null) {
|
||||||
|
//don't muck with address spaces if you don't have exclusive access to the program.
|
||||||
|
if (!currentProgram.hasExclusiveAccess()) {
|
||||||
|
popup("Must have exclusive access to " + currentProgram.getName() +
|
||||||
|
" to run this script");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Address startAddr = currentProgram.getAddressFactory()
|
||||||
|
.getAddressSpace(SpaceNames.OTHER_SPACE_NAME)
|
||||||
|
.getAddress(0x0L);
|
||||||
|
AddUninitializedMemoryBlockCmd cmd = new AddUninitializedMemoryBlockCmd(
|
||||||
|
SYSCALL_SPACE_NAME, null, this.getClass().getName(), startAddr,
|
||||||
|
SYSCALL_SPACE_LENGTH, true, true, true, false, true);
|
||||||
|
if (!cmd.applyTo(currentProgram)) {
|
||||||
|
popup("Failed to create " + SYSCALL_SPACE_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
syscallSpace = currentProgram.getAddressFactory().getAddressSpace(SYSCALL_SPACE_NAME);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("AddressSpace %s found, continuing...\n", SYSCALL_SPACE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get all of the functions that contain system calls
|
||||||
|
//note that this will not find system call instructions that are not in defined functions
|
||||||
|
Map<Function, Set<Address>> funcsToCalls = getSyscallsInFunctions(currentProgram, monitor);
|
||||||
|
|
||||||
|
if (funcsToCalls.isEmpty()) {
|
||||||
|
popup("No system calls found (within defined functions)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the system call number at each callsite of a system call.
|
||||||
|
//note that this is not guaranteed to succeed at a given system call call site -
|
||||||
|
//it might be hard (or impossible) to determine a specific constant
|
||||||
|
Map<Address, Long> addressesToSyscalls =
|
||||||
|
resolveConstants(funcsToCalls, currentProgram, monitor);
|
||||||
|
|
||||||
|
if (addressesToSyscalls.isEmpty()) {
|
||||||
|
popup("Couldn't resolve any syscall constants");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the map from system call numbers to system call names
|
||||||
|
//you might have to create this yourself!
|
||||||
|
Map<Long, String> syscallNumbersToNames = getSyscallNumberMap();
|
||||||
|
|
||||||
|
//at each system call call site where a constant could be determined, create
|
||||||
|
//the system call (if not already created), then add the appropriate overriding reference
|
||||||
|
//use syscallNumbersToNames to name the created functions
|
||||||
|
//if there's not a name corresponding to the constant use a default
|
||||||
|
for (Entry<Address, Long> entry : addressesToSyscalls.entrySet()) {
|
||||||
|
Address callSite = entry.getKey();
|
||||||
|
Long offset = entry.getValue();
|
||||||
|
printerr(callSite + ":" + Long.toHexString(offset));
|
||||||
|
Address callTarget = syscallSpace.getAddress(offset);
|
||||||
|
Function callee = currentProgram.getFunctionManager().getFunctionAt(callTarget);
|
||||||
|
if (callee == null) {
|
||||||
|
String funcName = "syscall_" + String.format("%08X", offset);
|
||||||
|
if (syscallNumbersToNames.get(offset) != null) {
|
||||||
|
funcName = syscallNumbersToNames.get(offset);
|
||||||
|
}
|
||||||
|
callee = createFunction(callTarget, funcName);
|
||||||
|
if (callee == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
callee.setCallingConvention(callingConvention);
|
||||||
|
|
||||||
|
//check if the function name is one of the non-returning syscalls
|
||||||
|
if (noreturnSyscalls.contains(funcName)) {
|
||||||
|
callee.setNoReturn(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference ref = currentProgram.getReferenceManager()
|
||||||
|
.addMemoryReference(callSite, callTarget, overrideType, SourceType.USER_DEFINED,
|
||||||
|
Reference.MNEMONIC);
|
||||||
|
//overriding references must be primary to be active
|
||||||
|
currentProgram.getReferenceManager().setPrimary(ref, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//finally, open the appropriate data type archive and apply its function data types
|
||||||
|
//to the new system call space, so that the system calls have the correct signatures
|
||||||
|
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(currentProgram);
|
||||||
|
DataTypeManagerService service = mgr.getDataTypeManagerService();
|
||||||
|
List<DataTypeManager> dataTypeManagers = new ArrayList<>();
|
||||||
|
dataTypeManagers.add(service.openDataTypeArchive(datatypeArchiveName));
|
||||||
|
dataTypeManagers.add(currentProgram.getDataTypeManager());
|
||||||
|
ApplyFunctionDataTypesCmd cmd = new ApplyFunctionDataTypesCmd(dataTypeManagers,
|
||||||
|
new AddressSet(syscallSpace.getMinAddress(), syscallSpace.getMaxAddress()),
|
||||||
|
SourceType.USER_DEFINED, false, false);
|
||||||
|
cmd.applyTo(currentProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Long, String> getSyscallNumberMap() {
|
||||||
|
Map<Long, String> syscallMap = new HashMap<>();
|
||||||
|
ResourceFile rFile = Application.findDataFileInAnyModule(syscallFileName);
|
||||||
|
if (rFile == null) {
|
||||||
|
popup("Error opening syscall number file, using default names");
|
||||||
|
return syscallMap;
|
||||||
|
}
|
||||||
|
try (FileReader fReader = new FileReader(rFile.getFile(false));
|
||||||
|
BufferedReader bReader = new BufferedReader(fReader)) {
|
||||||
|
String line = null;
|
||||||
|
while ((line = bReader.readLine()) != null) {
|
||||||
|
//lines starting with # are comments
|
||||||
|
if (!line.startsWith("#")) {
|
||||||
|
String[] parts = line.trim().split(" ");
|
||||||
|
Long number = Long.parseLong(parts[0]);
|
||||||
|
syscallMap.put(number, parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.showError(this, null, "Error reading syscall map file", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return syscallMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans through all of the functions defined in {@code program} and returns
|
||||||
|
* a map which takes a function to the set of address in its body which contain
|
||||||
|
* system calls
|
||||||
|
* @param program program containing functions
|
||||||
|
* @param tMonitor monitor
|
||||||
|
* @return map function -> addresses in function containing syscalls
|
||||||
|
* @throws CancelledException if the user cancels
|
||||||
|
*/
|
||||||
|
private Map<Function, Set<Address>> getSyscallsInFunctions(Program program,
|
||||||
|
TaskMonitor tMonitor) throws CancelledException {
|
||||||
|
Map<Function, Set<Address>> funcsToCalls = new HashMap<>();
|
||||||
|
for (Function func : program.getFunctionManager().getFunctionsNoStubs(true)) {
|
||||||
|
tMonitor.checkCancelled();
|
||||||
|
for (Instruction inst : program.getListing().getInstructions(func.getBody(), true)) {
|
||||||
|
if (tester.test(inst)) {
|
||||||
|
Set<Address> callSites = funcsToCalls.get(func);
|
||||||
|
if (callSites == null) {
|
||||||
|
callSites = new HashSet<>();
|
||||||
|
funcsToCalls.put(func, callSites);
|
||||||
|
}
|
||||||
|
callSites.add(inst.getAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return funcsToCalls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the LiSA constant propagator to attempt to determine the constant value in
|
||||||
|
* the syscall register at each system call instruction
|
||||||
|
*
|
||||||
|
* @param funcsToCalls map from functions containing syscalls to address in each function of
|
||||||
|
* the system call
|
||||||
|
* @param program containing the functions
|
||||||
|
* @return map from addresses of system calls to system call numbers
|
||||||
|
* @throws CancelledException if the user cancels
|
||||||
|
*/
|
||||||
|
private Map<Address, Long> resolveConstants(Map<Function, Set<Address>> funcsToCalls,
|
||||||
|
Program program, TaskMonitor tMonitor) throws CancelledException {
|
||||||
|
initLisa();
|
||||||
|
Set<CFG> cfgs = new HashSet<>();
|
||||||
|
for (Function func : funcsToCalls.keySet()) {
|
||||||
|
CFG cfg = frontend.visitFunction(func, func.getEntryPoint());
|
||||||
|
cfgs.add(cfg);
|
||||||
|
}
|
||||||
|
it.unive.lisa.program.Program p = frontend.getProgram();
|
||||||
|
Collection<CFG> baseline = p.getAllCFGs();
|
||||||
|
for (CFG cfg : cfgs) {
|
||||||
|
if (baseline.contains(cfg)) {
|
||||||
|
p.addEntryPoint(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LiSAReport report = lisa.run(p);
|
||||||
|
ipa = report.getConfiguration().interproceduralAnalysis;
|
||||||
|
storeResults(frontend.getProgram(), ipa);
|
||||||
|
|
||||||
|
Map<Address, Long> addressesToSyscalls = new HashMap<>();
|
||||||
|
Register syscallReg = program.getLanguage().getRegister(syscallRegister);
|
||||||
|
for (Function func : funcsToCalls.keySet()) {
|
||||||
|
for (Address callSite : funcsToCalls.get(func)) {
|
||||||
|
long val = getRegisterValue(callSite, syscallReg);
|
||||||
|
if (val == -1) {
|
||||||
|
//createBookmark(callSite, "System Call",
|
||||||
|
// "Couldn't resolve value of " + syscallReg);
|
||||||
|
//printf("Couldn't resolve value of " + syscallReg + " at " + callSite + "\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addressesToSyscalls.put(callSite, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addressesToSyscalls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getRegisterValue(Address callSite, Register syscallReg) {
|
||||||
|
Set<Statement> set = frontend.getStatement(callSite);
|
||||||
|
if (set == null) {
|
||||||
|
printerr("Null set for " + callSite);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
String offset = syscallReg.getAddress().toString();
|
||||||
|
Function f = currentProgram.getListing().getFunctionContaining(callSite);
|
||||||
|
CFG cfg = targets.get(f.getEntryPoint());
|
||||||
|
if (cfg == null) {
|
||||||
|
printerr("Null cfg for " + callSite);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Collection<?> results = ipa.getAnalysisResultsOf(cfg);
|
||||||
|
Statement st = null;
|
||||||
|
for (Statement obj : set) {
|
||||||
|
PcodeLocation loc = (PcodeLocation) obj.getLocation();
|
||||||
|
if (loc.op.getOpcode() >= PcodeOp.CALL && loc.op.getOpcode() <= PcodeOp.CALLOTHER) {
|
||||||
|
st = obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (st == null) {
|
||||||
|
printerr("Null statement for " + callSite);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Iterator<?> iterator = results.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object next = iterator.next();
|
||||||
|
if (next instanceof AnalyzedCFG<?> acfg) {
|
||||||
|
AnalysisState<?> state1;
|
||||||
|
try {
|
||||||
|
state1 = acfg.getAnalysisStateBefore(st);
|
||||||
|
AbstractState<?> state2 = state1.getState();
|
||||||
|
if (state2 instanceof SimpleAbstractState sas) {
|
||||||
|
ValueEnvironment<?> valueState = (ValueEnvironment<?>) sas.getValueState();
|
||||||
|
Map<Identifier, ?> function = valueState.function;
|
||||||
|
if (function != null) {
|
||||||
|
for (Object key : function.keySet()) {
|
||||||
|
Object val = valueState.function.get(key);
|
||||||
|
if (val instanceof PcodeByteBasedConstantPropagation icp) {
|
||||||
|
String keyStr = key.toString();
|
||||||
|
if (keyStr.equals(offset)) {
|
||||||
|
String valstr = icp.representation().toString();
|
||||||
|
if (!valstr.contains("#TOP#")) {
|
||||||
|
return Long.parseLong(valstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
printerr(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLisa() {
|
||||||
|
frontend = new PcodeFrontend();
|
||||||
|
|
||||||
|
DefaultConfiguration conf = new DefaultConfiguration();
|
||||||
|
conf.serializeResults = true;
|
||||||
|
conf.abstractState = DefaultConfiguration.simpleState(
|
||||||
|
DefaultConfiguration.defaultHeapDomain(),
|
||||||
|
new ValueEnvironment<>(
|
||||||
|
new PcodeByteBasedConstantPropagation(currentProgram.getLanguage())),
|
||||||
|
DefaultConfiguration.defaultTypeDomain());
|
||||||
|
conf.serializeResults = false;
|
||||||
|
lisa = new LiSA(conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeResults(it.unive.lisa.program.Program p,
|
||||||
|
InterproceduralAnalysis<?> interproceduralAnalysis) {
|
||||||
|
Collection<CFG> ep = p.getEntryPoints();
|
||||||
|
for (CFG cfg : ep) {
|
||||||
|
Collection<Statement> entrypoints = cfg.getEntrypoints();
|
||||||
|
for (Statement st : entrypoints) {
|
||||||
|
PcodeLocation loc = (PcodeLocation) st.getLocation();
|
||||||
|
Address target = loc.op.getSeqnum().getTarget();
|
||||||
|
Function f = currentProgram.getListing().getFunctionContaining(target);
|
||||||
|
targets.put(f.getEntryPoint(), cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an x86 native instruction is a system call
|
||||||
|
* @param inst instruction to check
|
||||||
|
* @return true precisely when the instruction is a system call
|
||||||
|
*/
|
||||||
|
private static boolean checkX86Instruction(Instruction inst) {
|
||||||
|
try {
|
||||||
|
return Arrays.equals(x86_bytes, inst.getBytes());
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
Msg.info(Lisa_ResolveX86orX64LinuxSyscallsScript.class,
|
||||||
|
"MemoryAccessException at " + inst.getAddress().toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an x64 instruction is a system call
|
||||||
|
* @param inst instruction to check
|
||||||
|
* @return true precisely when the instruction is a system call
|
||||||
|
*/
|
||||||
|
private static boolean checkX64Instruction(Instruction inst) {
|
||||||
|
boolean retVal = false;
|
||||||
|
for (PcodeOp op : inst.getPcode()) {
|
||||||
|
if (op.getOpcode() == PcodeOp.CALLOTHER) {
|
||||||
|
int index = (int) op.getInput(0).getOffset();
|
||||||
|
if (inst.getProgram()
|
||||||
|
.getLanguage()
|
||||||
|
.getUserDefinedOpName(index)
|
||||||
|
.equals(SYSCALL_X64_CALLOTHER)) {
|
||||||
|
retVal = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version='1.0' encoding='ISO-8859-1'?>
|
||||||
|
<!-- See Base's TOC_Source.xml for help -->
|
||||||
|
<tocroot>
|
||||||
|
<tocref id="Ghidra Functionality">
|
||||||
|
<tocdef id="Lisa Plugin" text="Lisa Plugin"
|
||||||
|
target="help/topics/LisaPlugin/LisaPlugin.html">
|
||||||
|
|
||||||
|
<tocdef id="add_cfgs" text="Add CFGs"
|
||||||
|
target="help/topics/LisaPlugin/LisaPlugin.html#add_cfgs" />
|
||||||
|
|
||||||
|
<tocdef id="clear_cfgs" text="Clear CFGs"
|
||||||
|
target="help/topics/LisaPlugin/LisaPlugin.html#clear_cfgs" />
|
||||||
|
|
||||||
|
<tocdef id="set_taint" text="Taint"
|
||||||
|
target="help/topics/LisaPlugin/LisaPlugin.html#set_taint" />
|
||||||
|
|
||||||
|
</tocdef>
|
||||||
|
</tocref>
|
||||||
|
</tocroot>
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||||
|
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<META name="generator" content=
|
||||||
|
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||||
|
|
||||||
|
<TITLE>Abstract Interpretation: LiSA</TITLE>
|
||||||
|
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||||
|
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
|
||||||
|
</HEAD>
|
||||||
|
|
||||||
|
<BODY lang="EN-US">
|
||||||
|
<H1><A name="LisaPlugin"></A>Abstract Interpretation: LiSA</H1>
|
||||||
|
|
||||||
|
<P>The Lisa Plugin uses the "Library for Static Analysis", <B>LiSA</B>, developed and maintained
|
||||||
|
by the Software and System Verification (SSV) group at Universita Ca' Foscari in Venice, Italy,
|
||||||
|
(lisa-analyzer.github.io), to implement and run static analyzers based on the theory of Abstract Interpretation.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H2>Setup</H2>
|
||||||
|
|
||||||
|
<P>If MavenCentral is accessible, the gradle build command will download the requisite LiSA libraries.
|
||||||
|
Otherwise, you should install them and add them to the relevant build paths. Add the Lisa and
|
||||||
|
Taint plugins to your project in the usual way. (The various analyses are started using the
|
||||||
|
"default taint query" button in Decompiler.)
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H2>Options</H2>
|
||||||
|
|
||||||
|
<P>The analysis to run is chosen via the Edit → ToolOptions, which gives you a default category
|
||||||
|
<B>Abstract Interpretation</B> and two subcategories <B>Abstract Interpretation/Domains</B> and
|
||||||
|
<B>Abstract Interpretation/Output</B>. <B>Domains</b> is the most important of these, as it provides the list of available analyses.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<P>
|
||||||
|
<B>Domains</B> has three options, corresponding to the choice of <B>Heap</B>, <B>Type</B>, and <B>Value</B> domains,
|
||||||
|
the last being the option you are most likely to want to change.
|
||||||
|
</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>Heap<A name="domain_heap"></A></B>: the abstraction used to model the target program's heap.</LI>
|
||||||
|
<LI><B>Type<A name="domain_type"></A></B>: the abstraction used to model types.</LI>
|
||||||
|
<LI><B>Value<A name="domain_value"></A></B>: the abstraction used to model values. ← </LI>
|
||||||
|
</UL>
|
||||||
|
<P>
|
||||||
|
The symbolic state of an analysis is the combination of these domains.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<P>
|
||||||
|
<B>Output</B> has several options (not well-tested), which correspond to fields in the LiSAConfiguration class.
|
||||||
|
</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>WorkDir<A name="work_dir"></A></B>: the working directory to be used for output if <B>SerializeResults</B> is selected.</LI>
|
||||||
|
<LI><B>GraphFormat<A name="graph_format"></A></B>: graph format if graph output is desired.</LI>
|
||||||
|
<LI><B>SerializeResults<A name="serialize"></A></B>: causes the analysis results to be dumped as JSON.</LI>
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<P>
|
||||||
|
<B>Abstract Interpretation</B> (the base option set) has additional execution-related options, associated again with LiSAConfiguration.
|
||||||
|
</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>CallGraph<A name="call_graph"></A></B>: "Class Hierarchy" or "Rapid Type" analysis of calls.</LI>
|
||||||
|
<LI><B>DescendingPhase<A name="descending_phase"></A></B>: the descending phase applied by the fixpoint algorithm.</LI>
|
||||||
|
<LI><B>Interprocedural<A name="interprocedural"></A></B>: type of interprocedural analysis desired.</LI>
|
||||||
|
<LI><B>OpenCallPolicy<A name="open_call_policy"></A></B>: how to treat unresolved calls.</LI>
|
||||||
|
<LI><B>OptimizeResults<A name="optimize"></A></B>: whether to optimize fixpoint execution.</LI>
|
||||||
|
<LI><B>PostState<A name="post_state"></A></B>: evaluate state post- or pre-statement.</LI>
|
||||||
|
<LI><B>CallDepth<A name="call_depth"></A></B>: cfg-computation depth (-1 == unlimited).</LI>
|
||||||
|
<LI><B>Threshhold<A name="threshhold"></A></B>: applied to either widening or glb.</LI>
|
||||||
|
</UL>
|
||||||
|
<P>
|
||||||
|
For the most part, these options may be left to their defaults. However, specific analyses may benefit from different choices.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H2>Actions</H2>
|
||||||
|
|
||||||
|
<H3><A name="add_cfgs">Add CFGs</A></H3>
|
||||||
|
<P>By default, an analysis uses the current function and its callees as the basis for the analysis. Under certain circumstances,
|
||||||
|
you may want to add additional functions to the base. This action generates control-flow graphs for the current function and its
|
||||||
|
descendants without invoking a particular analysis.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H3><A name="clear_cfgs">Clear CFGs</A></H3>
|
||||||
|
<P>Successive analyses will add to the set of control-flow graphs under consideration without clearing the previous results.
|
||||||
|
To clear previous CFGs, you must explicitly do so. This action removes all active CFGs.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H3><A name="set_taint">Set Taint</A></H3>
|
||||||
|
<P>For the "Taint" and "ThreeLevelTaint" analyses, you will need to specify sources of taint. There are two ways to do this.
|
||||||
|
From the decompiler, you may use the normal taint analysis functions (or this action) to specify <B>SOURCES</B> of taint.
|
||||||
|
(<B>SINKS</B> and <B>GATES</B> may also be used, but be aware they currently correspond only to "clean" annotations in LiSA.)
|
||||||
|
Alternatively, from the disassembly view, you may use this action on a line of PCode to mark an input to the op, or on a line
|
||||||
|
of disassembly to get a dialog for entering the varnode ID by hand. The varnode should be specified using its address, e.g.
|
||||||
|
"register:00000000" for RAX.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<P>Using either method may be imprecise. In particular, the analysis currently uses the low PCode, so tokens selected in the
|
||||||
|
decompiler (high PCode) may or may not have an equivalent. Similarly, varnodes marked in the disassembly are inputs only.
|
||||||
|
Marking an input as a source does not mark it as tainted in the analysis' state. In most cases, the resulting output will
|
||||||
|
be tainted, but not future references to the input, e.g. "(unique:5) INT_ADD (register:4), 0x12" taints future references
|
||||||
|
to (unique:5) but not to (register:4).
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H2>Current Analyses (Value Domains)</H2>
|
||||||
|
<UL>
|
||||||
|
<LI><B>Numeric: Constant Propagation</B>: overflow-insensitive basic constant propagation.</LI>
|
||||||
|
<LI><B>Numeric: Interval</B>: approximating numeric values as the minimum interval containing them.</LI>
|
||||||
|
<LI><B>Numeric: Non-redundant Power Sets Of Intervals</B>: approximating numeric values as a non-redundant set of intervals.</LI>
|
||||||
|
<LI><B>Numeric: Parity</B>: tracking whether numeric values are even or odd.</LI>
|
||||||
|
<LI><B>Numeric: Pentagon</B>: capturing properties of the form of x in [a, b] ∧ x < y.</LI>
|
||||||
|
<LI><B>Numeric: Sign</B>: tracking zero, strictly positive and strictly negative values.</LI>
|
||||||
|
<LI><B>Numeric: Upper Bound</B>: capturing upper bounds on a variable.</LI>
|
||||||
|
<LI><B>Dataflow: Available Expressions</B>: expressions stored in some variable.</LI>
|
||||||
|
<LI><B>Dataflow: Constant Propagation</B>: overflow-insensitive basic constant propagation.</LI>
|
||||||
|
<LI><B>Dataflow: Reaching Definitions</B>: instruction whose targets reach the given one without an intervening assignment.</LI>
|
||||||
|
<LI><B>Dataflow: Liveness</B>: variables that are live at each point in the program.
|
||||||
|
NB: typically uses the BackwardModularWorstCase interprocedural analysis.</LI>
|
||||||
|
<LI><B>Dataflow: Taint</B>: two levels of taintedness - clean and tainted. See above re setting taint.</LI>
|
||||||
|
<LI><B>Dataflow: Three-level Taint</B>: three levels of taintedness - clean, tainted, and top. See above re setting taint.</LI>
|
||||||
|
<LI><B>Non-interference</B>: type-system based implementation of non-interference analysis.</LI>
|
||||||
|
<LI><B>Stability</B>: per-variable numerical trends.</LI>
|
||||||
|
</UL>
|
||||||
|
<P>Note: with the exception of reaching, liveness, and non-interference domains, these domains have been extended (to a greater or lesser extent)
|
||||||
|
to handle p-code. Modifications may not be in sync with the latest domain definitions in the original LiSA analysis archive.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H2>Results</H2>
|
||||||
|
|
||||||
|
<P>Results are passed, by default, via SARIF to a results table. The functionality of the table is described in more detail
|
||||||
|
in <A href="help/topics/DecompilerTaint/DecompilerTaint.html#ResultMenuActions">Decompiler Taint Operations</A>. In general, you'll want to enable the "name", "type", "value",
|
||||||
|
and "displayName" columns, as well as "Address" and possibly "location".
|
||||||
|
</P>
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.gui;
|
||||||
|
|
||||||
|
import ghidra.lisa.gui.LisaTaintState.KTV;
|
||||||
|
import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
|
||||||
|
import ghidra.program.model.data.ISF.IsfObject;
|
||||||
|
|
||||||
|
public class ExtKeyValue implements IsfObject {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String displayName;
|
||||||
|
String type;
|
||||||
|
String taintLabels;
|
||||||
|
|
||||||
|
@Exclude
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
public ExtKeyValue(KTV ktv) {
|
||||||
|
this.name = ktv.key();
|
||||||
|
this.displayName = ktv.displayName();
|
||||||
|
this.type = ktv.type();
|
||||||
|
this.taintLabels = "[" + ktv.value() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,484 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.gui;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import db.Transaction;
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import ghidra.GhidraOptions;
|
||||||
|
import ghidra.app.CorePluginPackage;
|
||||||
|
import ghidra.app.context.ProgramLocationActionContext;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.location.DefaultDecompilerLocation;
|
||||||
|
import ghidra.app.events.*;
|
||||||
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
import ghidra.app.plugin.ProgramPlugin;
|
||||||
|
import ghidra.app.plugin.core.decompiler.absint.AbstractInterpretationService;
|
||||||
|
import ghidra.app.plugin.core.decompiler.taint.*;
|
||||||
|
import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType;
|
||||||
|
import ghidra.app.script.AskDialog;
|
||||||
|
import ghidra.framework.options.OptionsChangeListener;
|
||||||
|
import ghidra.framework.options.ToolOptions;
|
||||||
|
import ghidra.framework.plugintool.*;
|
||||||
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
import ghidra.lisa.gui.LisaOptions.*;
|
||||||
|
import ghidra.lisa.pcode.PcodeFrontend;
|
||||||
|
import ghidra.program.database.SpecExtension;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
import ghidra.program.util.PcodeFieldLocation;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.DummyCancellableTaskMonitor;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import it.unive.lisa.*;
|
||||||
|
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin for tracking taint through the decompiler.
|
||||||
|
*/
|
||||||
|
//@formatter:off
|
||||||
|
@PluginInfo(
|
||||||
|
status = PluginStatus.UNSTABLE,
|
||||||
|
packageName = CorePluginPackage.NAME,
|
||||||
|
category = PluginCategoryNames.ANALYSIS,
|
||||||
|
shortDescription = "LisaTest",
|
||||||
|
description = "Plugin for abstract interpretation analysis via LiSA",
|
||||||
|
servicesProvided = { AbstractInterpretationService.class },
|
||||||
|
servicesRequired = {
|
||||||
|
TaintService.class,
|
||||||
|
},
|
||||||
|
eventsConsumed = {
|
||||||
|
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
|
||||||
|
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
|
||||||
|
ProgramClosedPluginEvent.class
|
||||||
|
})
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
public class LisaPlugin extends ProgramPlugin implements OptionsChangeListener, AbstractInterpretationService {
|
||||||
|
|
||||||
|
private static final String OPTIONS_TITLE = "Abstract Interpretation";
|
||||||
|
public HeapDomainOption heapOption = HeapDomainOption.DEFAULT;
|
||||||
|
public TypeDomainOption typeOption = TypeDomainOption.DEFAULT;
|
||||||
|
public ValueDomainOption valueOption = ValueDomainOption.DEFAULT;
|
||||||
|
|
||||||
|
private Function currentFunction;
|
||||||
|
|
||||||
|
private InterproceduralAnalysis<?> ipa;
|
||||||
|
private PcodeFrontend frontend;
|
||||||
|
private LiSA lisa;
|
||||||
|
|
||||||
|
private LisaOptions options;
|
||||||
|
private TaintPlugin taintPlugin;
|
||||||
|
private LisaTaintState taintState;
|
||||||
|
private TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||||
|
private String lastValue = "";
|
||||||
|
|
||||||
|
public LisaPlugin(PluginTool tool) {
|
||||||
|
super(tool);
|
||||||
|
setOptions(new LisaOptions());
|
||||||
|
createActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function getCurrentFunction() {
|
||||||
|
return currentFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void programActivated(Program program) {
|
||||||
|
currentProgram = program;
|
||||||
|
initOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface AddCfgsAction {
|
||||||
|
String NAME = "Add CFG";
|
||||||
|
String DESCRIPTION = "Compute called CFGs prior to analysis";
|
||||||
|
String HELP_ANCHOR = "add_cfgs";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.popupMenuPath("Abstract Interpretation", NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ClearCfgsAction {
|
||||||
|
String NAME = "Clear CFGs";
|
||||||
|
String DESCRIPTION = "Clear CFGs prior to analysis";
|
||||||
|
String HELP_ANCHOR = "clear_cfgs";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.popupMenuPath("Abstract Interpretation", NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SetTaintAction {
|
||||||
|
String NAME = "Set Taint";
|
||||||
|
String DESCRIPTION = "Set taint for given varnode";
|
||||||
|
String HELP_ANCHOR = "set_taint";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.popupMenuPath("Abstract Interpretation", NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createActions() {
|
||||||
|
|
||||||
|
AddCfgsAction.builder(this)
|
||||||
|
.withContext(ProgramLocationActionContext.class)
|
||||||
|
.onAction(this::addCfgs)
|
||||||
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
|
ClearCfgsAction.builder(this)
|
||||||
|
.withContext(ProgramLocationActionContext.class)
|
||||||
|
.onAction(this::clearCfgs)
|
||||||
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
|
SetTaintAction.builder(this)
|
||||||
|
.withContext(ProgramLocationActionContext.class)
|
||||||
|
.onAction(this::setTaint)
|
||||||
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Program getCurrentProgram() {
|
||||||
|
return currentProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processEvent(PluginEvent event) {
|
||||||
|
super.processEvent(event);
|
||||||
|
|
||||||
|
//Msg.info(this, "TaintPlugin -> processEvent: " + event.toString() );
|
||||||
|
|
||||||
|
if (event instanceof ProgramClosedPluginEvent closedEvent) {
|
||||||
|
Program program = closedEvent.getProgram();
|
||||||
|
if (currentProgram != null && currentProgram.equals(program)) {
|
||||||
|
currentProgram = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (event instanceof ProgramActivatedPluginEvent activatedEvent) {
|
||||||
|
currentProgram = activatedEvent.getActiveProgram();
|
||||||
|
if (currentProgram != null) {
|
||||||
|
SpecExtension.registerOptions(currentProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (event instanceof ProgramLocationPluginEvent locEvent) {
|
||||||
|
|
||||||
|
// user changed their location in the program; this may be a function change.
|
||||||
|
|
||||||
|
ProgramLocation location = locEvent.getLocation();
|
||||||
|
Address address = location.getAddress();
|
||||||
|
|
||||||
|
if (address.isExternalAddress()) {
|
||||||
|
// ignore external functions when it comes to taint.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentProgram != null) {
|
||||||
|
// The user loaded a program for analysis.
|
||||||
|
Listing listing = currentProgram.getListing();
|
||||||
|
Function f = listing.getFunctionContaining(address);
|
||||||
|
// We are in function f
|
||||||
|
if (currentFunction == null || !currentFunction.equals(f)) {
|
||||||
|
// In the PAST we were in a function and the program location moved us into a new function.
|
||||||
|
String cfun = "NULL";
|
||||||
|
String nfun = "NULL";
|
||||||
|
|
||||||
|
if (currentFunction != null) {
|
||||||
|
cfun = currentFunction.getEntryPoint().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f != null) {
|
||||||
|
nfun = f.getEntryPoint().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg.info(this, "Changed from function: " + cfun + " to function " + nfun);
|
||||||
|
currentFunction = f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCfgs(ProgramLocationActionContext context) {
|
||||||
|
Set<CFG> cfgs = new HashSet<>();
|
||||||
|
Address addr = context.getAddress();
|
||||||
|
FunctionManager functionManager = currentProgram.getFunctionManager();
|
||||||
|
Function f = functionManager.getFunctionContaining(addr);
|
||||||
|
addCfg(cfgs, f, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCfg(Set<CFG> cfgs, Function f, boolean recurse) {
|
||||||
|
if (frontend == null) {
|
||||||
|
initProgram();
|
||||||
|
}
|
||||||
|
int depth = recurse ? getOptions().getCfgDepth() : 0;
|
||||||
|
addFunction(cfgs, f);
|
||||||
|
addCalledFunctions(cfgs, f, depth);
|
||||||
|
|
||||||
|
it.unive.lisa.program.Program p = frontend.getProgram();
|
||||||
|
Collection<CFG> baseline = p.getAllCFGs();
|
||||||
|
for (CFG g : cfgs) {
|
||||||
|
if (baseline.contains(g)) {
|
||||||
|
p.addEntryPoint(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFunction(Set<CFG> cfgs, Function f) {
|
||||||
|
if (frontend.hasProcessed(f) || monitor.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Msg.info(this, "Adding "+f);
|
||||||
|
CFG cfg = frontend.visitFunction(f, f.getEntryPoint());
|
||||||
|
cfgs.add(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCalledFunctions(Set<CFG> cfgs, Function f, int depth) {
|
||||||
|
if (depth == 0 || frontend.hasProcessed(f) || monitor.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Function> calledFunctions = f.getCalledFunctions(new DummyCancellableTaskMonitor());
|
||||||
|
for (Function func : calledFunctions) {
|
||||||
|
Address entryPoint = func.getEntryPoint();
|
||||||
|
if (entryPoint.getAddressSpace().equals(f.getEntryPoint().getAddressSpace())) {
|
||||||
|
addFunction(cfgs, func);
|
||||||
|
addCalledFunctions(cfgs, func, depth-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearCfgs(ProgramLocationActionContext context) {
|
||||||
|
initProgram();
|
||||||
|
frontend.clearTargets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTaint(ProgramLocationActionContext context) {
|
||||||
|
if (!checkTaintState()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
taintState.clearAnnotations();
|
||||||
|
BookmarkManager bookmarkManager = currentProgram.getBookmarkManager();
|
||||||
|
try (Transaction tx = currentProgram.openTransaction("clear bookmark")) {
|
||||||
|
bookmarkManager.removeBookmarks(BookmarkType.INFO, "Taint Source", TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
throw new AssertionError("Unreachable code");
|
||||||
|
}
|
||||||
|
Address addr = context.getAddress();
|
||||||
|
FunctionManager functionManager = currentProgram.getFunctionManager();
|
||||||
|
Function f = functionManager.getFunctionContaining(addr);
|
||||||
|
ProgramLocation location = context.getLocation();
|
||||||
|
int row = location.getRow();
|
||||||
|
int offset = location.getCharOffset();
|
||||||
|
|
||||||
|
String tokenId = null;
|
||||||
|
if (location instanceof PcodeFieldLocation pfl) {
|
||||||
|
List<String> pcodeStrings = pfl.getPcodeStrings();
|
||||||
|
String test = pcodeStrings.get(row);
|
||||||
|
int lastSpace= test.lastIndexOf(" ");
|
||||||
|
int index = offset > lastSpace ? 1 : 0;
|
||||||
|
Instruction inst = currentProgram.getListing().getInstructionContaining(addr);
|
||||||
|
PcodeOp[] pcode = inst.getPcode();
|
||||||
|
PcodeOp op = pcode[row];
|
||||||
|
if (index >= op.getNumInputs()) {
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
Varnode vn = op.getInput(index);
|
||||||
|
tokenId = vn.getAddress().toString();
|
||||||
|
}
|
||||||
|
else if (location instanceof DefaultDecompilerLocation ddl) {
|
||||||
|
ClangToken token = ddl.getToken();
|
||||||
|
taintPlugin.toggleIcon(MarkType.SOURCE, token, false);
|
||||||
|
return; // taint is set via the token
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AskDialog<String> dialog = new AskDialog<>("Abstract Interpretation Taint", "Varnode address", AskDialog.STRING, lastValue);
|
||||||
|
if (dialog.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tokenId = dialog.getValueAsString();
|
||||||
|
}
|
||||||
|
try (Transaction tx = currentProgram.openTransaction("set bookmark")) {
|
||||||
|
bookmarkManager.setBookmark(addr, BookmarkType.INFO, "Taint Source", tokenId);
|
||||||
|
}
|
||||||
|
taintState.setTaint(MarkType.SOURCE, f, addr, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkTaintState() {
|
||||||
|
if (taintState == null) {
|
||||||
|
// ability to add custom margins to the decompiler view
|
||||||
|
TaintService service = tool.getService(TaintService.class);
|
||||||
|
if (service instanceof TaintPlugin taint) {
|
||||||
|
this.taintPlugin = taint;
|
||||||
|
TaintState state = taint.getTaintState();
|
||||||
|
if (state instanceof LisaTaintState ts) {
|
||||||
|
this.taintState = ts;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Function, Collection<?>> performAnalysis(TaskMonitor tm) {
|
||||||
|
this.monitor = tm;
|
||||||
|
if (currentFunction == null) {
|
||||||
|
Msg.error(this, "Not currently in a function");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
addCfg(new HashSet<>(), currentFunction, true);
|
||||||
|
initLisa();
|
||||||
|
it.unive.lisa.program.Program p = frontend.getProgram();
|
||||||
|
Map<Function, Collection<?>> combined = new HashMap<>();
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
LiSAReport report = lisa.run(p);
|
||||||
|
ipa = report.getConfiguration().interproceduralAnalysis;
|
||||||
|
|
||||||
|
FunctionManager functionManager = currentProgram.getFunctionManager();
|
||||||
|
Map<Address, CFG> targets = frontend.getTargets();
|
||||||
|
for (Address entry : targets.keySet()) {
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Function f = functionManager.getFunctionAt(entry);
|
||||||
|
CFG cfg = targets.get(entry);
|
||||||
|
Collection<?> res = ipa.getAnalysisResultsOf(cfg);
|
||||||
|
combined.put(f, res);
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
} catch (AnalysisException e) {
|
||||||
|
Msg.error(this, e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Statement> getStatements(Function f) {
|
||||||
|
addCfg(new HashSet<>(), f, false);
|
||||||
|
CFG cfg = frontend.getTarget(f.getEntryPoint());
|
||||||
|
return cfg.getNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initProgram() {
|
||||||
|
frontend = new PcodeFrontend();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void initLisa() {
|
||||||
|
LisaOptions opt = getOptions();
|
||||||
|
heapOption = opt.getHeapOption();
|
||||||
|
typeOption = opt.getTypeOption();
|
||||||
|
valueOption = opt.getValueOption();
|
||||||
|
|
||||||
|
DefaultConfiguration conf = new DefaultConfiguration();
|
||||||
|
conf.abstractState = DefaultConfiguration.simpleState(
|
||||||
|
heapOption.getDomain(),
|
||||||
|
valueOption.getDomain(currentProgram),
|
||||||
|
typeOption.getDomain());
|
||||||
|
conf.interproceduralAnalysis = opt.getInterproceduralOption().getAnalysis();
|
||||||
|
conf.descendingPhaseType = opt.getDescendingPhaseOption().getType();
|
||||||
|
conf.openCallPolicy = opt.getCallOption().getPolicy();
|
||||||
|
conf.analysisGraphs = opt.getGraphOption().getType();
|
||||||
|
conf.callGraph = opt.getCallGraphOption().getCallGraph();
|
||||||
|
conf.serializeResults = opt.isSerialize();
|
||||||
|
conf.optimize = opt.isOptimize();
|
||||||
|
String outputDir = opt.getOutputDir();
|
||||||
|
if (!outputDir.equals(LisaOptions.DEFAULT_LISA_ANALYSIS_OUTDIR)) {
|
||||||
|
conf.workdir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
lisa = new LiSA(conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initOptions() {
|
||||||
|
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||||
|
getOptions().registerOptions(this, opt, currentProgram);
|
||||||
|
|
||||||
|
opt.addOptionsChangeListener(this);
|
||||||
|
|
||||||
|
ToolOptions codeBrowserOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||||
|
codeBrowserOptions.addOptionsChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void optionsChanged(ToolOptions opts, String optionName, Object oldValue,
|
||||||
|
Object newValue) {
|
||||||
|
if (opts.getName().equals(OPTIONS_TITLE) ||
|
||||||
|
opts.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) {
|
||||||
|
doRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRefresh() {
|
||||||
|
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||||
|
getOptions().grabFromToolAndProgram(this, opt, currentProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getActiveQueryName() {
|
||||||
|
String queryName = valueOption.toString();
|
||||||
|
if (!heapOption.equals(HeapDomainOption.DEFAULT)) {
|
||||||
|
queryName += ":"+heapOption.toString();
|
||||||
|
}
|
||||||
|
if (!typeOption.equals(TypeDomainOption.DEFAULT)) {
|
||||||
|
queryName += ":"+typeOption.toString();
|
||||||
|
}
|
||||||
|
if (currentFunction != null) {
|
||||||
|
queryName += " @ " + currentFunction;
|
||||||
|
}
|
||||||
|
return queryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LisaOptions getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOptions(LisaOptions options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.gui;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.lisa.gui.LisaTaintState.KTV;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import sarif.export.*;
|
||||||
|
|
||||||
|
public class SarifKeyValueWriter extends AbstractExtWriter {
|
||||||
|
|
||||||
|
private ExtKeyValue isf;
|
||||||
|
private WrappedLogicalLocation wll;
|
||||||
|
|
||||||
|
public SarifKeyValueWriter(KTV ktv, WrappedLogicalLocation wll)
|
||||||
|
throws IOException {
|
||||||
|
super(null);
|
||||||
|
this.isf = new ExtKeyValue(ktv);
|
||||||
|
this.wll = wll;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
|
genData(monitor);
|
||||||
|
root.add("structuredObject", objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void genData(TaskMonitor monitor) {
|
||||||
|
ExtLogicalLocation lloc = wll.getLogicalLocation();
|
||||||
|
SarifObject sarif = new SarifObject(lloc.getDecoratedName(), "VALUE", lloc, getTree(isf),
|
||||||
|
wll.getAddress(), wll.getIndex());
|
||||||
|
objects.add(getTree(sarif));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.gui;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.InstLocation;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.pcode.SequenceNumber;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import sarif.export.*;
|
||||||
|
|
||||||
|
public class SarifLogicalLocationWriter extends AbstractExtWriter {
|
||||||
|
|
||||||
|
private PcodeLocation location;
|
||||||
|
|
||||||
|
private WrappedLogicalLocation lloc;
|
||||||
|
|
||||||
|
public SarifLogicalLocationWriter(String key, Function f, Statement statement)
|
||||||
|
throws IOException {
|
||||||
|
super(null);
|
||||||
|
Address addr;
|
||||||
|
String loc, op;
|
||||||
|
if (statement.getLocation() instanceof PcodeLocation ploc) {
|
||||||
|
this.location = ploc;
|
||||||
|
SequenceNumber seqnum = location.op.getSeqnum();
|
||||||
|
String seq = seqnum.getTarget() + ":" + seqnum.getTime();
|
||||||
|
loc = f.getName();
|
||||||
|
loc += "@" + f.getEntryPoint();
|
||||||
|
loc += ":" + seq;
|
||||||
|
addr = location.getAddress();
|
||||||
|
op = seq + " " + location.op.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
loc = f.getName();
|
||||||
|
InstLocation instLoc = (InstLocation) statement.getLocation();
|
||||||
|
addr = instLoc.getAddress();
|
||||||
|
loc += "@" + addr;
|
||||||
|
op = instLoc.toString();
|
||||||
|
}
|
||||||
|
ExtLogicalLocation ext = new ExtLogicalLocation(key, f, loc, op);
|
||||||
|
lloc = new WrappedLogicalLocation(ext, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
|
genData(monitor);
|
||||||
|
root.add("logicalLocation", objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void genData(TaskMonitor monitor) {
|
||||||
|
objects.add(getTree(lloc.getLogicalLocation()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrappedLogicalLocation getLogicalLocation() {
|
||||||
|
return lloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.WorkItem.PredType;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.controlFlow.ControlFlowStructure;
|
||||||
|
import it.unive.lisa.program.cfg.edge.Edge;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import it.unive.lisa.util.datastructures.graph.code.NodeList;
|
||||||
|
|
||||||
|
public class PcodeBranch extends ControlFlowStructure {
|
||||||
|
|
||||||
|
private Statement branch;
|
||||||
|
private Statement fallThrough;
|
||||||
|
|
||||||
|
protected PcodeBranch(NodeList<CFG, Statement, Edge> cfgMatrix, Statement condition) {
|
||||||
|
super(cfgMatrix, condition, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Statement> bodyStatements() {
|
||||||
|
Collection<Statement> all = new HashSet<>(getTrueBranch());
|
||||||
|
all.addAll(getFalseBranch());
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Statement> getFalseBranch() {
|
||||||
|
if (fallThrough == null) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return new HashSet<>(cfgMatrix.followersOf(fallThrough));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Statement> getTrueBranch() {
|
||||||
|
if (branch == null) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return new HashSet<>(cfgMatrix.followersOf(branch));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Statement st) {
|
||||||
|
return bodyStatements().contains(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simplify() {
|
||||||
|
// Nothing required here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "if-then-else[" + getCondition() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Statement> getTargetedStatements() {
|
||||||
|
return bodyStatements();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addStatement(Statement st, PredType type) {
|
||||||
|
if (type.equals(PredType.TRUE)) {
|
||||||
|
branch = st;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fallThrough = st;
|
||||||
|
setFirstFollower(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,391 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.WorkItem.PredType;
|
||||||
|
import ghidra.lisa.pcode.contexts.*;
|
||||||
|
import ghidra.lisa.pcode.expressions.*;
|
||||||
|
import ghidra.lisa.pcode.statements.PcodeNop;
|
||||||
|
import ghidra.program.model.listing.Listing;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.SequenceNumber;
|
||||||
|
import it.unive.lisa.program.annotations.Annotations;
|
||||||
|
import it.unive.lisa.program.cfg.*;
|
||||||
|
import it.unive.lisa.program.cfg.controlFlow.ControlFlowStructure;
|
||||||
|
import it.unive.lisa.program.cfg.edge.Edge;
|
||||||
|
import it.unive.lisa.program.cfg.edge.SequentialEdge;
|
||||||
|
import it.unive.lisa.program.cfg.statement.*;
|
||||||
|
import it.unive.lisa.program.cfg.statement.comparison.Equal;
|
||||||
|
import it.unive.lisa.program.cfg.statement.literal.*;
|
||||||
|
import it.unive.lisa.program.type.BoolType;
|
||||||
|
import it.unive.lisa.type.Type;
|
||||||
|
import it.unive.lisa.type.Untyped;
|
||||||
|
import it.unive.lisa.util.datastructures.graph.code.NodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link PcodeCodeMemberVisitor} that will parse the pcode of an function
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PcodeCodeMemberVisitor {
|
||||||
|
|
||||||
|
private final NodeList<CFG, Statement, Edge> list;
|
||||||
|
|
||||||
|
private final Collection<Statement> entrypoints;
|
||||||
|
private final Collection<ControlFlowStructure> cfs;
|
||||||
|
private final Map<String, Pair<VariableRef, Annotations>> visibleIds;
|
||||||
|
|
||||||
|
private final CFG cfg;
|
||||||
|
|
||||||
|
private final CodeMemberDescriptor descriptor;
|
||||||
|
|
||||||
|
private Listing listing;
|
||||||
|
|
||||||
|
private final Collection<String> visited;
|
||||||
|
private final Map<SequenceNumber, Statement> visitedPcode;
|
||||||
|
private Stack<WorkItem> workItems;
|
||||||
|
|
||||||
|
private UnitContext currentUnit;
|
||||||
|
|
||||||
|
private Map<String, PcodeBranch> flows;
|
||||||
|
|
||||||
|
private int varCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the visitor of an IMP method or constructor.
|
||||||
|
*
|
||||||
|
* @param descriptor the descriptor of the method or constructor
|
||||||
|
* @param listing the program listing
|
||||||
|
*/
|
||||||
|
PcodeCodeMemberVisitor(CodeMemberDescriptor descriptor, Listing listing) {
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
this.listing = listing;
|
||||||
|
list = new NodeList<>(new SequentialEdge());
|
||||||
|
entrypoints = new HashSet<>();
|
||||||
|
visited = new HashSet<>();
|
||||||
|
visitedPcode = new HashMap<>();
|
||||||
|
cfs = new LinkedList<>();
|
||||||
|
// side effects on entrypoints and matrix will affect the cfg
|
||||||
|
cfg = new CFG(descriptor, entrypoints, list);
|
||||||
|
this.flows = new HashMap<>();
|
||||||
|
|
||||||
|
visibleIds = new HashMap<>();
|
||||||
|
for (VariableTableEntry par : descriptor.getVariables()) {
|
||||||
|
visibleIds.put(par.getName(), Pair.of(par.createReference(cfg), par.getAnnotations()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits the code of a {@link UnitContext} representing the code block of
|
||||||
|
* a method or constructor.
|
||||||
|
*
|
||||||
|
* @param ctx the block context
|
||||||
|
*
|
||||||
|
* @return the {@link CFG} built from the block
|
||||||
|
*/
|
||||||
|
CFG visitCodeMember(UnitContext ctx) {
|
||||||
|
this.currentUnit = ctx;
|
||||||
|
InstructionContext entry = ctx.entry();
|
||||||
|
if (entry == null) {
|
||||||
|
throw new RuntimeException("No entry for " + ctx.function());
|
||||||
|
}
|
||||||
|
while (entry.getPcodeOps().isEmpty()) {
|
||||||
|
entry = entry.next();
|
||||||
|
}
|
||||||
|
visited.clear();
|
||||||
|
visitedPcode.clear();
|
||||||
|
workItems = new Stack<>();
|
||||||
|
workItems.add(new WorkItem(null, entry.getPcodeOp(0)));
|
||||||
|
while (!workItems.isEmpty()) {
|
||||||
|
processWorkItem(workItems.pop());
|
||||||
|
}
|
||||||
|
visitBlock(entry);
|
||||||
|
|
||||||
|
cfs.forEach(cf -> cfg.addControlFlowStructure(cf));
|
||||||
|
|
||||||
|
Ret ret = new Ret(cfg, descriptor.getLocation());
|
||||||
|
if (cfg.getNodesCount() == 0) {
|
||||||
|
// empty method, so the ret is also the entrypoint
|
||||||
|
list.addNode(ret);
|
||||||
|
entrypoints.add(ret);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// every non-throwing instruction that does not have a follower
|
||||||
|
// is ending the method
|
||||||
|
Collection<Statement> preExits = new LinkedList<>();
|
||||||
|
for (Statement st : list.getNodes())
|
||||||
|
if (!st.stopsExecution() && list.followersOf(st).isEmpty())
|
||||||
|
preExits.add(st);
|
||||||
|
list.addNode(ret);
|
||||||
|
for (Statement st : preExits)
|
||||||
|
list.addEdge(new SequentialEdge(st, ret));
|
||||||
|
|
||||||
|
for (VariableTableEntry vte : descriptor.getVariables())
|
||||||
|
if (preExits.contains(vte.getScopeEnd()))
|
||||||
|
vte.setScopeEnd(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.simplify();
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitBlock(InstructionContext entry) {
|
||||||
|
if (entry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (entry.getPcodeOps().isEmpty()) {
|
||||||
|
entry = entry.next();
|
||||||
|
}
|
||||||
|
visited.clear();
|
||||||
|
visitedPcode.clear();
|
||||||
|
workItems = new Stack<>();
|
||||||
|
workItems.add(new WorkItem(null, entry.getPcodeOp(0)));
|
||||||
|
while (!workItems.isEmpty()) {
|
||||||
|
processWorkItem(workItems.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processWorkItem(WorkItem item) {
|
||||||
|
StatementContext ctx = item.getContext();
|
||||||
|
Statement st = visitPcodeOp(ctx);
|
||||||
|
if (st == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (visited.contains(item.getKey())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Statement pred = item.getPred();
|
||||||
|
boolean entrypoint = pred == null;
|
||||||
|
cfg.addNode(st, entrypoint);
|
||||||
|
if (!entrypoint) {
|
||||||
|
Edge e = item.computeBranch(st);
|
||||||
|
if (e != null) {
|
||||||
|
cfg.addEdge(e);
|
||||||
|
}
|
||||||
|
PredType type = item.getType();
|
||||||
|
if (!type.equals(PredType.SEQ)) {
|
||||||
|
String loc = pred.getLocation().getCodeLocation();
|
||||||
|
PcodeBranch flow = flows.get(loc);
|
||||||
|
if (flow == null) {
|
||||||
|
flow = new PcodeBranch(cfg.getNodeList(), pred);
|
||||||
|
cfg.addControlFlowStructure(flow);
|
||||||
|
}
|
||||||
|
flow.addStatement(st, type);
|
||||||
|
flows.put(loc, flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entrypoints.add(st);
|
||||||
|
}
|
||||||
|
if (st instanceof Ret || st instanceof Return) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<StatementContext> branches = ctx.branch(this.listing, this.currentUnit);
|
||||||
|
for (StatementContext branch : branches) {
|
||||||
|
WorkItem n = new WorkItem(st, branch);
|
||||||
|
if (ctx.isConditional()) {
|
||||||
|
n.setType(true);
|
||||||
|
}
|
||||||
|
workItems.add(n);
|
||||||
|
}
|
||||||
|
StatementContext next = ctx.next(this.listing);
|
||||||
|
if (next != null) {
|
||||||
|
WorkItem n = new WorkItem(st, next);
|
||||||
|
if (ctx.isBranch()) {
|
||||||
|
if (ctx.isConditional()) {
|
||||||
|
n.setType(false);
|
||||||
|
workItems.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
workItems.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited.add(item.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Statement visitPcodeOp(StatementContext ctx) {
|
||||||
|
SequenceNumber key = ctx.getOp().getSeqnum();
|
||||||
|
if (visitedPcode.containsKey(key)) {
|
||||||
|
return visitedPcode.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement st;
|
||||||
|
if (ctx.isRet()) {
|
||||||
|
if (ctx.expression() != null) {
|
||||||
|
st = new Return(cfg, ctx.location(), visitExpression(ctx));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
st = new Ret(cfg, ctx.location());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ctx.isBranch()) {
|
||||||
|
if (ctx.isConditional()) {
|
||||||
|
st = visitCondition(ctx.condition());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
st = new PcodeNop(cfg, ctx.location()); // Treating these as a NOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ctx.expression() != null) {
|
||||||
|
st = visitExpression(ctx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Statement '" + ctx.toString() + "' cannot be parsed");
|
||||||
|
|
||||||
|
visitedPcode.put(key, st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Statement visitCondition(
|
||||||
|
ConditionContext ctx) {
|
||||||
|
VarnodeContext expression = ctx.expression();
|
||||||
|
CodeLocation loc = ctx.location();
|
||||||
|
if (expression == null) {
|
||||||
|
return new NoOp(cfg, loc);
|
||||||
|
}
|
||||||
|
//return visitVarnode(ctx.location(), expression, BoolType.INSTANCE, false);
|
||||||
|
Expression left = visitVarnode(loc, expression, BoolType.INSTANCE, false);
|
||||||
|
return new Equal(cfg, loc, left, new TrueLiteral(cfg, loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression visitExpression(StatementContext ctx) {
|
||||||
|
CodeLocation loc = ctx.location();
|
||||||
|
VarDefContext left = ctx.target();
|
||||||
|
PcodeContext right = ctx.expression();
|
||||||
|
|
||||||
|
int opcode = ctx.opcode();
|
||||||
|
switch (opcode) {
|
||||||
|
case PcodeOp.COPY -> {
|
||||||
|
Expression target = visitVariable(loc, left, true);
|
||||||
|
Expression expression = visitVarnode(loc, right.basicExpr(), false);
|
||||||
|
return new Assignment(cfg, loc, target, expression);
|
||||||
|
}
|
||||||
|
case PcodeOp.FLOAT_INT2FLOAT, PcodeOp.FLOAT_FLOAT2FLOAT -> {
|
||||||
|
Expression target = visitVariable(loc, left, true);
|
||||||
|
Expression expression = visitBinaryExpr(new BinaryExprContext(ctx));
|
||||||
|
return new Assignment(cfg, loc, target, expression);
|
||||||
|
}
|
||||||
|
case PcodeOp.CALL, PcodeOp.CALLIND, PcodeOp.CALLOTHER -> {
|
||||||
|
return visitCallExpr(new CallContext(ctx.getOp(), currentUnit));
|
||||||
|
}
|
||||||
|
case PcodeOp.RETURN -> {
|
||||||
|
return visitVarnode(loc, right.basicExpr(), false);
|
||||||
|
}
|
||||||
|
case PcodeOp.LOAD -> {
|
||||||
|
MemLocContext mem = new MemLocContext(ctx);
|
||||||
|
Expression target = visitVariable(loc, left, true);
|
||||||
|
Expression expression = visitVarnode(loc, mem, false);
|
||||||
|
return new Assignment(cfg, loc, target, expression);
|
||||||
|
}
|
||||||
|
case PcodeOp.STORE -> {
|
||||||
|
MemLocContext mem = new MemLocContext(ctx);
|
||||||
|
Expression target = visitVariable(loc, mem, true);
|
||||||
|
Expression expression = visitVarnode(loc, left, false);
|
||||||
|
return new Assignment(cfg, loc, target, expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right == null) {
|
||||||
|
throw new UnsupportedOperationException("Type of expression not supported: " + ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (right.getNumInputs()) {
|
||||||
|
case 1 -> {
|
||||||
|
Expression target = visitVariable(loc, left, true);
|
||||||
|
Expression expression = visitUnaryExpr(new UnaryExprContext(right));
|
||||||
|
yield new Assignment(cfg, loc, target, expression);
|
||||||
|
}
|
||||||
|
case 2 -> {
|
||||||
|
Expression target = visitVariable(loc, left, true);
|
||||||
|
Expression expression = visitBinaryExpr(new BinaryExprContext(right));
|
||||||
|
yield new Assignment(cfg, loc, target, expression);
|
||||||
|
}
|
||||||
|
default -> throw new UnsupportedOperationException(
|
||||||
|
"Type of expression not supported: " + ctx);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression visitCallExpr(CallContext ctx) {
|
||||||
|
CodeLocation loc = ctx.location();
|
||||||
|
Expression lexp = visitVarnode(loc, ctx.left, false);
|
||||||
|
return new PcodeCallExpression(cfg, ctx, lexp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression visitUnaryExpr(UnaryExprContext ctx) {
|
||||||
|
CodeLocation loc = ctx.location();
|
||||||
|
Expression lexp = visitVarnode(loc, ctx.arg, false);
|
||||||
|
return new PcodeUnaryExpression(cfg, ctx, lexp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression visitBinaryExpr(BinaryExprContext ctx) {
|
||||||
|
CodeLocation loc = ctx.location();
|
||||||
|
Expression lexp = visitVariable(loc, ctx.left, false);
|
||||||
|
Expression rexp = visitVarnode(loc, ctx.right, false);
|
||||||
|
return new PcodeBinaryExpression(cfg, ctx, lexp, rexp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression visitVarnode(CodeLocation loc, VarnodeContext ctx, Type type,
|
||||||
|
boolean define) {
|
||||||
|
if (ctx.isConstant()) {
|
||||||
|
return visitConstant(loc, ctx);
|
||||||
|
}
|
||||||
|
return visitVariable(loc, ctx, type, define);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression visitVarnode(CodeLocation loc, VarnodeContext ctx, boolean define) {
|
||||||
|
return visitVarnode(loc, ctx, Untyped.INSTANCE, define);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Literal<?> visitConstant(CodeLocation loc, VarnodeContext ctx) {
|
||||||
|
return switch (ctx.getSize()) {
|
||||||
|
case 0 -> new NullLiteral(cfg, loc);
|
||||||
|
case 1 -> new Int8Literal(cfg, loc, (byte) ctx.getOffset());
|
||||||
|
case 2 -> new Int16Literal(cfg, loc, (short) ctx.getOffset());
|
||||||
|
case 4 -> new Int32Literal(cfg, loc, (int) ctx.getOffset());
|
||||||
|
case 8 -> new Int64Literal(cfg, loc, ctx.getOffset());
|
||||||
|
default -> new Int64Literal(cfg, loc, ctx.getOffset()); // FIXME
|
||||||
|
// default -> throw new UnsupportedOperationException(
|
||||||
|
// "Type of literal not supported: " + ctx);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private VariableRef visitVariable(CodeLocation loc, VarnodeContext ctx, boolean define) {
|
||||||
|
return visitVariable(loc, ctx, Untyped.INSTANCE, define);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VariableRef visitVariable(CodeLocation loc, VarnodeContext ctx, Type type,
|
||||||
|
boolean define) {
|
||||||
|
VariableRef ref = new VariableRef(cfg, loc,
|
||||||
|
ctx.getText(), type);
|
||||||
|
if (!visibleIds.containsKey(ref.getName())) {
|
||||||
|
visibleIds.put(ref.getName(), Pair.of(ref, new Annotations()));
|
||||||
|
descriptor.addVariable(new VariableTableEntry(loc, varCount++, null, null,
|
||||||
|
ref.getName(), type));
|
||||||
|
}
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, PcodeBranch> getFlows() {
|
||||||
|
return flows;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode;
|
||||||
|
|
||||||
|
import it.unive.lisa.program.language.LanguageFeatures;
|
||||||
|
import it.unive.lisa.program.language.hierarchytraversal.HierarcyTraversalStrategy;
|
||||||
|
import it.unive.lisa.program.language.hierarchytraversal.SingleInheritanceTraversalStrategy;
|
||||||
|
import it.unive.lisa.program.language.parameterassignment.ParameterAssigningStrategy;
|
||||||
|
import it.unive.lisa.program.language.parameterassignment.PythonLikeAssigningStrategy;
|
||||||
|
import it.unive.lisa.program.language.resolution.JavaLikeMatchingStrategy;
|
||||||
|
import it.unive.lisa.program.language.resolution.ParameterMatchingStrategy;
|
||||||
|
import it.unive.lisa.program.language.validation.BaseValidationLogic;
|
||||||
|
import it.unive.lisa.program.language.validation.ProgramValidationLogic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pcode's {@link LanguageFeatures} implementation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PcodeFeatures extends LanguageFeatures {
|
||||||
|
|
||||||
|
// For the PcodeFrontend, most of the strategies are probably not relevant.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParameterMatchingStrategy getMatchingStrategy() {
|
||||||
|
return JavaLikeMatchingStrategy.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HierarcyTraversalStrategy getTraversalStrategy() {
|
||||||
|
return SingleInheritanceTraversalStrategy.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParameterAssigningStrategy getAssigningStrategy() {
|
||||||
|
return PythonLikeAssigningStrategy.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProgramValidationLogic getProgramValidationLogic() {
|
||||||
|
return new BaseValidationLogic();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.contexts.UnitContext;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.lisa.pcode.types.PcodeTypeSystem;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import it.unive.lisa.program.Program;
|
||||||
|
import it.unive.lisa.program.cfg.*;
|
||||||
|
import it.unive.lisa.program.cfg.Parameter;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import it.unive.lisa.type.Untyped;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiated {@link PcodeCodeMemberVisitor} that will parse the pcode building a
|
||||||
|
* representation that can be analyzed through LiSA.
|
||||||
|
*/
|
||||||
|
public class PcodeFrontend {
|
||||||
|
|
||||||
|
//private static final Logger log = LogManager.getLogger(PcodeFrontend.class);
|
||||||
|
|
||||||
|
private final Program program;
|
||||||
|
private Map<Address, Set<Statement>> nodeMap = new HashMap<>();
|
||||||
|
private Map<Address, CFG> targets = new HashMap<>();
|
||||||
|
private Set<CFG> cfgs = new HashSet<>();
|
||||||
|
|
||||||
|
public PcodeFrontend() {
|
||||||
|
program = new Program(new PcodeFeatures(), new PcodeTypeSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Program doWork(Listing listing, Address startAddress) {
|
||||||
|
|
||||||
|
Program p = visitListing(listing, startAddress);
|
||||||
|
|
||||||
|
Collection<CFG> baseline = p.getAllCFGs();
|
||||||
|
for (CFG cfg : cfgs) {
|
||||||
|
if (baseline.contains(cfg)) {
|
||||||
|
p.addEntryPoint(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Program visitListing(Listing listing, Address startAddress) {
|
||||||
|
Program p = getProgram();
|
||||||
|
for (Function f : listing.getFunctions(startAddress, false)) {
|
||||||
|
CFG cfg = visitFunction(f, startAddress);
|
||||||
|
cfgs.add(cfg);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CFG visitFunction(Function f, Address start) {
|
||||||
|
Program p = getProgram();
|
||||||
|
UnitContext ctx = new UnitContext(this, program, f, start);
|
||||||
|
|
||||||
|
CodeMemberDescriptor descr = mkDescriptor(ctx);
|
||||||
|
PcodeCodeMemberVisitor visitor =
|
||||||
|
new PcodeCodeMemberVisitor(descr, ctx.getListing());
|
||||||
|
CFG cfg = visitor.visitCodeMember(ctx);
|
||||||
|
targets.put(f.getEntryPoint(), cfg);
|
||||||
|
cfgs.add(cfg);
|
||||||
|
|
||||||
|
ctx.unit().addCodeMember(cfg);
|
||||||
|
p.addUnit(ctx.unit());
|
||||||
|
Collection<Statement> nodes = cfg.getNodes();
|
||||||
|
for (Statement statement : nodes) {
|
||||||
|
Address addr = toAddr(statement.getLocation());
|
||||||
|
nodeMap.computeIfAbsent(addr, a -> new HashSet<>()).add(statement);
|
||||||
|
}
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address toAddr(CodeLocation location) {
|
||||||
|
if (location instanceof PcodeLocation loc) {
|
||||||
|
return loc.op.getSeqnum().getTarget();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeMemberDescriptor mkDescriptor(UnitContext ctx) {
|
||||||
|
Parameter[] params = computeParameters(ctx);
|
||||||
|
CodeMemberDescriptor descriptor = new CodeMemberDescriptor(
|
||||||
|
ctx.location(),
|
||||||
|
ctx.unit(),
|
||||||
|
false, ctx.getText(), Untyped.INSTANCE,
|
||||||
|
params);
|
||||||
|
|
||||||
|
descriptor.setOverridable(!ctx.isFinal());
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Parameter[] computeParameters(UnitContext ctx) {
|
||||||
|
Function f = ctx.function();
|
||||||
|
ProgramContext programContext = f.getProgram().getProgramContext();
|
||||||
|
Set<Parameter> pset = new HashSet<>();
|
||||||
|
for (Register r : programContext.getRegisters()) {
|
||||||
|
RegisterValue rv = programContext.getRegisterValue(r, f.getEntryPoint());
|
||||||
|
if (rv != null && rv.hasValue()) {
|
||||||
|
Parameter p = new Parameter(ctx.location(), r.getAddress().toString());
|
||||||
|
pset.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parameter[] params = new Parameter[pset.size() + 1];
|
||||||
|
int index = 0;
|
||||||
|
params[index++] = new Parameter(ctx.location(), ctx.getText());
|
||||||
|
for (Parameter p : pset) {
|
||||||
|
params[index++] = p;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Program getProgram() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Statement> getStatement(Address addr) {
|
||||||
|
return nodeMap.get(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasProcessed(Function f) {
|
||||||
|
if (f == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return targets.containsKey(f.getEntryPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearTargets() {
|
||||||
|
targets.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CFG getTarget(Address entryPoint) {
|
||||||
|
return targets.get(entryPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Address, CFG> getTargets() {
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.contexts.StatementContext;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.cfg.edge.*;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
|
||||||
|
public class WorkItem {
|
||||||
|
|
||||||
|
public enum PredType {
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
SEQ
|
||||||
|
}
|
||||||
|
|
||||||
|
private Statement pred;
|
||||||
|
private PredType type;
|
||||||
|
private StatementContext context;
|
||||||
|
|
||||||
|
public WorkItem(Statement pred, StatementContext ctx) {
|
||||||
|
this.pred = pred;
|
||||||
|
this.context = ctx;
|
||||||
|
this.type = PredType.SEQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatementContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Statement getPred() {
|
||||||
|
return pred;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(boolean val) {
|
||||||
|
type = val ? PredType.TRUE : PredType.FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Edge computeBranch(Statement succ) {
|
||||||
|
PcodeLocation loc = (PcodeLocation) pred.getLocation();
|
||||||
|
if (loc.getOpcode() == PcodeOp.RETURN) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return switch (getType()) {
|
||||||
|
case TRUE -> new TrueEdge(pred, succ);
|
||||||
|
case FALSE -> new FalseEdge(pred, succ);
|
||||||
|
case SEQ -> new SequentialEdge(pred, succ);
|
||||||
|
default -> throw new IllegalArgumentException("Unexpected value: " + getType());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public PredType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
String key = context.getOp().getSeqnum().toString();
|
||||||
|
if (pred != null) {
|
||||||
|
key = pred.getLocation().getCodeLocation() + "=>" + key;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,453 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import it.unive.lisa.util.numeric.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interval with long bounds.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||||
|
*/
|
||||||
|
public class LongInterval implements Iterable<Long>, Comparable<LongInterval> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval {@code [-Inf, +Inf]}.
|
||||||
|
*/
|
||||||
|
public static final LongInterval INFINITY = new LongInterval();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval {@code [0, 0]}.
|
||||||
|
*/
|
||||||
|
public static final LongInterval ZERO = new LongInterval(0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval {@code [1, 1]}.
|
||||||
|
*/
|
||||||
|
public static final LongInterval ONE = new LongInterval(1, 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval {@code [-1, -1]}.
|
||||||
|
*/
|
||||||
|
public static final LongInterval MINUS_ONE = new LongInterval(-1, -1);
|
||||||
|
|
||||||
|
private final MathNumber low;
|
||||||
|
|
||||||
|
private final MathNumber high;
|
||||||
|
|
||||||
|
private LongInterval() {
|
||||||
|
this(MathNumber.MINUS_INFINITY, MathNumber.PLUS_INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new interval. Order of the bounds is adjusted (i.e., if
|
||||||
|
* {@code low} is greater then {@code high}, then the interval
|
||||||
|
* {@code [high, low]} is created).
|
||||||
|
*
|
||||||
|
* @param low the lower bound
|
||||||
|
* @param high the upper bound
|
||||||
|
*/
|
||||||
|
public LongInterval(long low, long high) {
|
||||||
|
this(new MathNumber(low), new MathNumber(high));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new interval. Order of the bounds is adjusted (i.e., if
|
||||||
|
* {@code low} is greater then {@code high}, then the interval
|
||||||
|
* {@code [high, low]} is created).
|
||||||
|
*
|
||||||
|
* @param low the lower bound (if {@code null}, -inf will be used)
|
||||||
|
* @param high the upper bound (if {@code null}, +inf will be used)
|
||||||
|
*/
|
||||||
|
public LongInterval(
|
||||||
|
Integer low,
|
||||||
|
Integer high) {
|
||||||
|
this(low == null ? MathNumber.MINUS_INFINITY : new MathNumber(low),
|
||||||
|
high == null ? MathNumber.PLUS_INFINITY : new MathNumber(high));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new interval. Order of the bounds is adjusted (i.e., if
|
||||||
|
* {@code low} is greater then {@code high}, then the interval
|
||||||
|
* {@code [high, low]} is created).
|
||||||
|
*
|
||||||
|
* @param low the lower bound
|
||||||
|
* @param high the upper bound
|
||||||
|
*/
|
||||||
|
public LongInterval(
|
||||||
|
MathNumber low,
|
||||||
|
MathNumber high) {
|
||||||
|
if (low.isNaN() || high.isNaN()) {
|
||||||
|
this.low = MathNumber.NaN;
|
||||||
|
this.high = MathNumber.NaN;
|
||||||
|
}
|
||||||
|
else if (low.compareTo(high) <= 0) {
|
||||||
|
this.low = low;
|
||||||
|
this.high = high;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.low = high;
|
||||||
|
this.high = low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields the upper bound of this interval.
|
||||||
|
*
|
||||||
|
* @return the upper bound of this interval
|
||||||
|
*/
|
||||||
|
public MathNumber getHigh() {
|
||||||
|
return high;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields the lower bound of this interval.
|
||||||
|
*
|
||||||
|
* @return the lower bound of this interval
|
||||||
|
*/
|
||||||
|
public MathNumber getLow() {
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if the lower bound of this interval is set to minus
|
||||||
|
* infinity.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean lowIsMinusInfinity() {
|
||||||
|
return low.isMinusInfinity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if the upper bound of this interval is set to plus
|
||||||
|
* infinity.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean highIsPlusInfinity() {
|
||||||
|
return high.isPlusInfinity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this is interval is not finite, that is, if at
|
||||||
|
* least one bound is set to infinity.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean isInfinite() {
|
||||||
|
return this == INFINITY || (highIsPlusInfinity() || lowIsMinusInfinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this is interval is finite, that is, if neither
|
||||||
|
* bound is set to infinity.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean isFinite() {
|
||||||
|
return !isInfinite();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this is the interval representing infinity, that
|
||||||
|
* is, {@code [-Inf, +Inf]}.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean isInfinity() {
|
||||||
|
return this == INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this is a singleton interval, that is, if the
|
||||||
|
* lower bound and the upper bound are the same.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean isSingleton() {
|
||||||
|
return isFinite() && low.equals(high);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this is a singleton interval containing only
|
||||||
|
* {@code n}.
|
||||||
|
*
|
||||||
|
* @param n the integer to test
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean is(long n) {
|
||||||
|
BigDecimal number = low.getNumber();
|
||||||
|
return isSingleton() && number != null && number.equals(new BigDecimal(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LongInterval cacheAndRound(
|
||||||
|
LongInterval i) {
|
||||||
|
if (i.is(0)) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
if (i.is(1)) {
|
||||||
|
return ONE;
|
||||||
|
}
|
||||||
|
if (i.is(-1)) {
|
||||||
|
return MINUS_ONE;
|
||||||
|
}
|
||||||
|
return new LongInterval(i.low.roundDown(), i.high.roundUp());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the interval addition between {@code this} and {@code other}.
|
||||||
|
*
|
||||||
|
* @param other the other interval
|
||||||
|
*
|
||||||
|
* @return {@code this + other}
|
||||||
|
*/
|
||||||
|
public LongInterval plus(
|
||||||
|
LongInterval other) {
|
||||||
|
if (isInfinity() || other.isInfinity()) {
|
||||||
|
return INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheAndRound(new LongInterval(low.add(other.low), high.add(other.high)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the interval subtraction between {@code this} and {@code other}.
|
||||||
|
*
|
||||||
|
* @param other the other interval
|
||||||
|
*
|
||||||
|
* @return {@code this - other}
|
||||||
|
*/
|
||||||
|
public LongInterval diff(
|
||||||
|
LongInterval other) {
|
||||||
|
if (isInfinity() || other.isInfinity()) {
|
||||||
|
return INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheAndRound(new LongInterval(low.subtract(other.high), high.subtract(other.low)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MathNumber min(
|
||||||
|
MathNumber... nums) {
|
||||||
|
if (nums.length == 0) {
|
||||||
|
throw new IllegalArgumentException("No numbers provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
MathNumber min = nums[0];
|
||||||
|
for (int i = 1; i < nums.length; i++) {
|
||||||
|
min = min.min(nums[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MathNumber max(
|
||||||
|
MathNumber... nums) {
|
||||||
|
if (nums.length == 0) {
|
||||||
|
throw new IllegalArgumentException("No numbers provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
MathNumber max = nums[0];
|
||||||
|
for (int i = 1; i < nums.length; i++) {
|
||||||
|
max = max.max(nums[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the interval multiplication between {@code this} and
|
||||||
|
* {@code other}.
|
||||||
|
*
|
||||||
|
* @param other the other interval
|
||||||
|
*
|
||||||
|
* @return {@code this * other}
|
||||||
|
*/
|
||||||
|
public LongInterval mul(
|
||||||
|
LongInterval other) {
|
||||||
|
if (is(0) || other.is(0)) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
if (isInfinity() || other.isInfinity()) {
|
||||||
|
return INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (low.compareTo(MathNumber.ZERO) >= 0 && other.low.compareTo(MathNumber.ZERO) >= 0) {
|
||||||
|
return cacheAndRound(
|
||||||
|
new LongInterval(low.multiply(other.low), high.multiply(other.high)));
|
||||||
|
}
|
||||||
|
|
||||||
|
MathNumber ll = low.multiply(other.low);
|
||||||
|
MathNumber lh = low.multiply(other.high);
|
||||||
|
MathNumber hl = high.multiply(other.low);
|
||||||
|
MathNumber hh = high.multiply(other.high);
|
||||||
|
return cacheAndRound(new LongInterval(min(ll, lh, hl, hh), max(ll, lh, hl, hh)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the interval division between {@code this} and {@code other}.
|
||||||
|
*
|
||||||
|
* @param other the other interval
|
||||||
|
* @param ignoreZero if {@code true}, causes the division to ignore the
|
||||||
|
* fact that {@code other} might contain 0, producing
|
||||||
|
* a smaller result
|
||||||
|
* @param errorOnZero whether or not an {@link ArithmeticException} should
|
||||||
|
* be thrown immediately if {@code other} contains
|
||||||
|
* zero
|
||||||
|
*
|
||||||
|
* @return {@code this / other}
|
||||||
|
*
|
||||||
|
* @throws ArithmeticException if {@code other} contains 0 and
|
||||||
|
* {@code errorOnZero} is set to
|
||||||
|
* {@code true}
|
||||||
|
*/
|
||||||
|
public LongInterval div(
|
||||||
|
LongInterval other,
|
||||||
|
boolean ignoreZero,
|
||||||
|
boolean errorOnZero) {
|
||||||
|
if (errorOnZero && (other.is(0) || other.includes(ZERO))) {
|
||||||
|
throw new ArithmeticException("IntInterval divide by zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is(0)) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other.includes(ZERO)) {
|
||||||
|
return mul(new LongInterval(MathNumber.ONE.divide(other.high),
|
||||||
|
MathNumber.ONE.divide(other.low)));
|
||||||
|
}
|
||||||
|
else if (other.high.isZero()) {
|
||||||
|
return mul(
|
||||||
|
new LongInterval(MathNumber.MINUS_INFINITY, MathNumber.ONE.divide(other.low)));
|
||||||
|
}
|
||||||
|
else if (other.low.isZero()) {
|
||||||
|
return mul(
|
||||||
|
new LongInterval(MathNumber.ONE.divide(other.high), MathNumber.PLUS_INFINITY));
|
||||||
|
}
|
||||||
|
else if (ignoreZero) {
|
||||||
|
return mul(new LongInterval(MathNumber.ONE.divide(other.low),
|
||||||
|
MathNumber.ONE.divide(other.high)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LongInterval lower =
|
||||||
|
mul(new LongInterval(MathNumber.MINUS_INFINITY, MathNumber.ONE.divide(other.low)));
|
||||||
|
LongInterval higher =
|
||||||
|
mul(new LongInterval(MathNumber.ONE.divide(other.high), MathNumber.PLUS_INFINITY));
|
||||||
|
|
||||||
|
if (lower.includes(higher)) {
|
||||||
|
return lower;
|
||||||
|
}
|
||||||
|
else if (higher.includes(lower)) {
|
||||||
|
return higher;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return cacheAndRound(
|
||||||
|
new LongInterval(lower.low.compareTo(higher.low) > 0 ? higher.low : lower.low,
|
||||||
|
lower.high.compareTo(higher.high) < 0 ? higher.high : lower.high));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this interval includes the given one.
|
||||||
|
*
|
||||||
|
* @param other the other interval
|
||||||
|
*
|
||||||
|
* @return {@code true} if it is included, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public boolean includes(
|
||||||
|
LongInterval other) {
|
||||||
|
return low.compareTo(other.low) <= 0 && high.compareTo(other.high) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields {@code true} if this interval intersects with the given one.
|
||||||
|
*
|
||||||
|
* @param other the other interval
|
||||||
|
*
|
||||||
|
* @return {@code true} if those intersects, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public boolean intersects(
|
||||||
|
LongInterval other) {
|
||||||
|
return includes(other) || other.includes(this) ||
|
||||||
|
(high.compareTo(other.low) >= 0 && high.compareTo(other.high) <= 0) ||
|
||||||
|
(other.high.compareTo(low) >= 0 && other.high.compareTo(high) <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((high == null) ? 0 : high.hashCode());
|
||||||
|
result = prime * result + ((low == null) ? 0 : low.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LongInterval other = (LongInterval) obj;
|
||||||
|
if (high == null) {
|
||||||
|
if (other.high != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!high.equals(other.high)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (low == null) {
|
||||||
|
if (other.low != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!low.equals(other.low)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + low + ", " + high + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Long> iterator() {
|
||||||
|
if (!low.isFinite() || !high.isFinite() || low.isNaN() || high.isNaN())
|
||||||
|
throw new RuntimeException(low + " is infinite or NaN", null);
|
||||||
|
try {
|
||||||
|
return new IntIntervalIterator(low.toLong(), high.toLong());
|
||||||
|
}
|
||||||
|
catch (MathNumberConversionException e) {
|
||||||
|
throw new RuntimeException("Cannot convert " + low, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(
|
||||||
|
LongInterval o) {
|
||||||
|
int cmp;
|
||||||
|
if ((cmp = low.compareTo(o.low)) != 0) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
return high.compareTo(o.high);
|
||||||
|
}
|
||||||
|
}
|
||||||
+367
@@ -0,0 +1,367 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.lisa.pcode.statements.PcodeBinaryOperator;
|
||||||
|
import ghidra.pcode.exec.BytesPcodeArithmetic;
|
||||||
|
import ghidra.pcode.utils.Utils;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.lattices.Satisfiability;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.BaseNonRelationalValueDomain;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.*;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.*;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.unary.UnaryOperator;
|
||||||
|
import it.unive.lisa.type.NumericType;
|
||||||
|
import it.unive.lisa.type.Type;
|
||||||
|
import it.unive.lisa.util.representation.StringRepresentation;
|
||||||
|
import it.unive.lisa.util.representation.StructuredRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The overflow-insensitive basic numeric constant propagation abstract domain,
|
||||||
|
* tracking if a certain numeric value has constant value or not, implemented as
|
||||||
|
* a {@link BaseNonRelationalValueDomain}, handling top and bottom values for
|
||||||
|
* the expression evaluation and bottom values for the expression
|
||||||
|
* satisfiability. Top and bottom cases for least upper bounds, widening and
|
||||||
|
* less or equals operations are handled by {@link BaseLattice} in
|
||||||
|
* {@link BaseLattice#lub}, {@link BaseLattice#widening} and
|
||||||
|
* {@link BaseLattice#lessOrEqual}, respectively.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:vincenzo.arceri@unive.it">Vincenzo Arceri</a>
|
||||||
|
*/
|
||||||
|
public class PcodeByteBasedConstantPropagation
|
||||||
|
implements PcodeNonRelationalValueDomain<PcodeByteBasedConstantPropagation> {
|
||||||
|
|
||||||
|
private static final PcodeByteBasedConstantPropagation TOP =
|
||||||
|
new PcodeByteBasedConstantPropagation(true, false);
|
||||||
|
private static final PcodeByteBasedConstantPropagation BOTTOM =
|
||||||
|
new PcodeByteBasedConstantPropagation(false, true);
|
||||||
|
private static BytesPcodeArithmetic arithmetic;
|
||||||
|
private static boolean isBigEndian;
|
||||||
|
|
||||||
|
private final boolean isTop, isBottom;
|
||||||
|
private Long value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the top abstract value.
|
||||||
|
*
|
||||||
|
* @param language base language for current program
|
||||||
|
*/
|
||||||
|
public PcodeByteBasedConstantPropagation(Language language) {
|
||||||
|
this(0L, true, false);
|
||||||
|
PcodeByteBasedConstantPropagation.arithmetic =
|
||||||
|
BytesPcodeArithmetic.forLanguage(language);
|
||||||
|
isBigEndian = language.isBigEndian();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PcodeByteBasedConstantPropagation(
|
||||||
|
Long value,
|
||||||
|
boolean isTop,
|
||||||
|
boolean isBottom) {
|
||||||
|
this.value = value;
|
||||||
|
this.isTop = isTop;
|
||||||
|
this.isBottom = isBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PcodeByteBasedConstantPropagation(
|
||||||
|
boolean isTop,
|
||||||
|
boolean isBottom) {
|
||||||
|
this(0L, isTop, isBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeByteBasedConstantPropagation(
|
||||||
|
Long value) {
|
||||||
|
this(value, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeByteBasedConstantPropagation(
|
||||||
|
Boolean value) {
|
||||||
|
this(value ? 1L : 0L, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeByteBasedConstantPropagation(
|
||||||
|
byte[] bytes,
|
||||||
|
int size,
|
||||||
|
boolean isBigEndian) {
|
||||||
|
this(Utils.bytesToLong(bytes, size, isBigEndian));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation evalNullConstant(
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation evalNonNullConstant(
|
||||||
|
Constant constant,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
Object cval = constant.getValue();
|
||||||
|
if (cval instanceof Number val) {
|
||||||
|
Type staticType = constant.getStaticType();
|
||||||
|
if (staticType != null && staticType instanceof NumericType numType) {
|
||||||
|
if (numType.isSigned()) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(Long.valueOf(val.longValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cval instanceof Long lval) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(lval);
|
||||||
|
}
|
||||||
|
if (cval instanceof Integer ival) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(Integer.toUnsignedLong(ival));
|
||||||
|
}
|
||||||
|
if (cval instanceof Short sval) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(Short.toUnsignedLong(sval));
|
||||||
|
}
|
||||||
|
if (cval instanceof Byte bval) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(Byte.toUnsignedLong(bval));
|
||||||
|
}
|
||||||
|
if (cval instanceof Boolean bval) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(bval ? 1L : 0L);
|
||||||
|
}
|
||||||
|
Msg.error(this, "Unknown type for constant: " + cval);
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation evalUnaryExpression(
|
||||||
|
UnaryOperator operator,
|
||||||
|
PcodeByteBasedConstantPropagation arg,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
|
||||||
|
if (arg.isTop()) {
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||||
|
PcodeOp op = ploc.op;
|
||||||
|
|
||||||
|
byte[] bytes = arithmetic.unaryOp(op.getOpcode(), op.getOutput().getSize(),
|
||||||
|
op.getInput(0).getSize(), arg.getValue(op.getInput(0).getSize()));
|
||||||
|
return new PcodeByteBasedConstantPropagation(bytes, op.getOutput().getSize(),
|
||||||
|
isBigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getValue(int size) {
|
||||||
|
return Utils.longToBytes(value, size, isBigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation evalBinaryExpression(
|
||||||
|
BinaryOperator operator,
|
||||||
|
PcodeByteBasedConstantPropagation left,
|
||||||
|
PcodeByteBasedConstantPropagation right,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
|
||||||
|
if ((left.isTop || right.isTop) && !left.equals(right)) {
|
||||||
|
if (left.value == 0L || right.value == 0L) {
|
||||||
|
if (operator instanceof PcodeBinaryOperator poperator &&
|
||||||
|
poperator.getOp().getOpcode() == PcodeOp.INT_MULT) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||||
|
PcodeOp op = ploc.op;
|
||||||
|
if (left.isTop) {
|
||||||
|
return specialCaseLogic(op);
|
||||||
|
}
|
||||||
|
int lsize = op.getInput(0).getSize();
|
||||||
|
int rsize = op.getInput(1).getSize();
|
||||||
|
byte[] bytes = arithmetic.binaryOp(op.getOpcode(), op.getOutput().getSize(),
|
||||||
|
lsize, left.getValue(lsize),
|
||||||
|
rsize, right.getValue(rsize));
|
||||||
|
return new PcodeByteBasedConstantPropagation(bytes, op.getOutput().getSize(),
|
||||||
|
isBigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are instances that return zero when left==right.
|
||||||
|
private PcodeByteBasedConstantPropagation specialCaseLogic(PcodeOp op) {
|
||||||
|
int opcode = op.getOpcode();
|
||||||
|
if (opcode == PcodeOp.INT_SUB || opcode == PcodeOp.FLOAT_SUB ||
|
||||||
|
opcode == PcodeOp.BOOL_XOR || opcode == PcodeOp.INT_XOR) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(0L);
|
||||||
|
}
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Satisfiability satisfiesBinaryExpression(
|
||||||
|
BinaryOperator operator,
|
||||||
|
PcodeByteBasedConstantPropagation left,
|
||||||
|
PcodeByteBasedConstantPropagation right,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
|
||||||
|
if (left.isTop() || right.isTop()) {
|
||||||
|
return Satisfiability.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operator instanceof ComparisonEq) {
|
||||||
|
return left.value == right.value
|
||||||
|
? Satisfiability.SATISFIED
|
||||||
|
: Satisfiability.NOT_SATISFIED;
|
||||||
|
}
|
||||||
|
if (operator instanceof ComparisonNe) {
|
||||||
|
return left.value != right.value
|
||||||
|
? Satisfiability.SATISFIED
|
||||||
|
: Satisfiability.NOT_SATISFIED;
|
||||||
|
}
|
||||||
|
if (operator instanceof ComparisonLe) {
|
||||||
|
return left.value <= right.value
|
||||||
|
? Satisfiability.SATISFIED
|
||||||
|
: Satisfiability.NOT_SATISFIED;
|
||||||
|
}
|
||||||
|
if (operator instanceof ComparisonLt) {
|
||||||
|
return left.value < right.value
|
||||||
|
? Satisfiability.SATISFIED
|
||||||
|
: Satisfiability.NOT_SATISFIED;
|
||||||
|
}
|
||||||
|
return Satisfiability.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueEnvironment<PcodeByteBasedConstantPropagation> assumeBinaryExpression(
|
||||||
|
ValueEnvironment<PcodeByteBasedConstantPropagation> environment,
|
||||||
|
BinaryOperator operator,
|
||||||
|
ValueExpression left,
|
||||||
|
ValueExpression right,
|
||||||
|
ProgramPoint src,
|
||||||
|
ProgramPoint dest,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
|
||||||
|
if (!(operator instanceof PcodeBinaryOperator)) {
|
||||||
|
if (operator instanceof ComparisonEq) {
|
||||||
|
if (left instanceof Identifier leftId) {
|
||||||
|
PcodeByteBasedConstantPropagation eval = eval(right, environment, src, oracle);
|
||||||
|
if (eval.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
return environment.putState(leftId, eval);
|
||||||
|
}
|
||||||
|
else if (right instanceof Identifier rightId) {
|
||||||
|
PcodeByteBasedConstantPropagation eval = eval(left, environment, src, oracle);
|
||||||
|
if (eval.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
return environment.putState(rightId, eval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operator instanceof ComparisonNe) {
|
||||||
|
if (left instanceof Identifier leftId) {
|
||||||
|
PcodeByteBasedConstantPropagation eval = eval(right, environment, src, oracle);
|
||||||
|
if (eval.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
eval.value = 1L - eval.value;
|
||||||
|
return environment.putState(leftId, eval);
|
||||||
|
}
|
||||||
|
else if (right instanceof Identifier rightId) {
|
||||||
|
PcodeByteBasedConstantPropagation eval = eval(left, environment, src, oracle);
|
||||||
|
if (eval.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
eval.value = 1L - eval.value;
|
||||||
|
return environment.putState(rightId, eval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation lubAux(PcodeByteBasedConstantPropagation other)
|
||||||
|
throws SemanticException {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lessOrEqualAux(PcodeByteBasedConstantPropagation other)
|
||||||
|
throws SemanticException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation top() {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation bottom() {
|
||||||
|
return BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
if (isBottom()) {
|
||||||
|
return Lattice.bottomRepresentation();
|
||||||
|
}
|
||||||
|
if (isTop()) {
|
||||||
|
return Lattice.topRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StringRepresentation(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(isBottom, isTop, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof PcodeByteBasedConstantPropagation other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBottom != other.isBottom) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isTop != other.isTop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Objects.equals(this.value, other.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeByteBasedConstantPropagation getValue(RegisterValue rv) {
|
||||||
|
if (rv != null) {
|
||||||
|
BigInteger val = rv.getUnsignedValue();
|
||||||
|
if (val != null) {
|
||||||
|
return new PcodeByteBasedConstantPropagation(val.longValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
}
|
||||||
+291
@@ -0,0 +1,291 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.InstLocation;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.pcode.exec.BytesPcodeArithmetic;
|
||||||
|
import ghidra.pcode.utils.Utils;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressFormatException;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import it.unive.lisa.analysis.ScopeToken;
|
||||||
|
import it.unive.lisa.analysis.SemanticException;
|
||||||
|
import it.unive.lisa.analysis.dataflow.DataflowElement;
|
||||||
|
import it.unive.lisa.analysis.dataflow.DefiniteDataflowDomain;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Assignment;
|
||||||
|
import it.unive.lisa.symbolic.SymbolicExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.*;
|
||||||
|
import it.unive.lisa.type.NumericType;
|
||||||
|
import it.unive.lisa.type.Type;
|
||||||
|
import it.unive.lisa.util.representation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the overflow-insensitive constant propagation dataflow
|
||||||
|
* analysis, that focuses only on integers.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||||
|
*/
|
||||||
|
public class PcodeDataflowConstantPropagation implements
|
||||||
|
DataflowElement<DefiniteDataflowDomain<PcodeDataflowConstantPropagation>, PcodeDataflowConstantPropagation> {
|
||||||
|
|
||||||
|
private static BytesPcodeArithmetic arithmetic;
|
||||||
|
private static boolean isBigEndian;
|
||||||
|
|
||||||
|
private final Identifier id;
|
||||||
|
private final Long constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an empty constant propagation object.
|
||||||
|
*
|
||||||
|
* @param language base language for current program
|
||||||
|
*/
|
||||||
|
public PcodeDataflowConstantPropagation(Language language) {
|
||||||
|
this(null, null);
|
||||||
|
PcodeDataflowConstantPropagation.arithmetic = BytesPcodeArithmetic.forLanguage(language);
|
||||||
|
isBigEndian = language.isBigEndian();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the new constant propagation object.
|
||||||
|
*
|
||||||
|
* @param id the constant variable
|
||||||
|
* @param v the constant value
|
||||||
|
*/
|
||||||
|
public PcodeDataflowConstantPropagation(
|
||||||
|
Identifier id,
|
||||||
|
Long v) {
|
||||||
|
this.id = id;
|
||||||
|
this.constant = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return representation().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Identifier> getInvolvedIdentifiers() {
|
||||||
|
return Collections.singleton(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] getValue(long val, int size) {
|
||||||
|
return Utils.longToBytes(val, size, isBigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Long eval(
|
||||||
|
SymbolicExpression e, ProgramPoint pp,
|
||||||
|
DefiniteDataflowDomain<PcodeDataflowConstantPropagation> domain) {
|
||||||
|
|
||||||
|
if (e instanceof Constant c) {
|
||||||
|
Object cval = c.getValue();
|
||||||
|
if (cval instanceof Number nval) {
|
||||||
|
Type staticType = c.getStaticType();
|
||||||
|
if (staticType != null && staticType instanceof NumericType numType) {
|
||||||
|
if (numType.isSigned()) {
|
||||||
|
return Long.valueOf(nval.longValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cval instanceof Long lval) {
|
||||||
|
return lval;
|
||||||
|
}
|
||||||
|
if (cval instanceof Integer ival) {
|
||||||
|
return Integer.toUnsignedLong(ival);
|
||||||
|
}
|
||||||
|
if (cval instanceof Short sval) {
|
||||||
|
return Short.toUnsignedLong(sval);
|
||||||
|
}
|
||||||
|
if (cval instanceof Byte bval) {
|
||||||
|
return Byte.toUnsignedLong(bval);
|
||||||
|
}
|
||||||
|
if (cval instanceof Boolean bval) {
|
||||||
|
return bval ? 1L : 0L;
|
||||||
|
}
|
||||||
|
Msg.error(e, "Unknown type for constant: " + cval);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof Identifier) {
|
||||||
|
for (PcodeDataflowConstantPropagation cp : domain.getDataflowElements()) {
|
||||||
|
if (cp.id.equals(e)) {
|
||||||
|
return cp.constant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(pp.getLocation() instanceof PcodeLocation ploc)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeOp op = ploc.op;
|
||||||
|
|
||||||
|
if (e instanceof UnaryExpression unary) {
|
||||||
|
Long i = eval(unary.getExpression(), pp, domain);
|
||||||
|
|
||||||
|
if (i == null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long exp = eval(unary.getExpression(), pp, domain);
|
||||||
|
byte[] bytes = arithmetic.unaryOp(op.getOpcode(), op.getOutput().getSize(),
|
||||||
|
op.getInput(0).getSize(), getValue(exp, op.getInput(0).getSize()));
|
||||||
|
return Utils.bytesToLong(bytes, op.getOutput().getSize(), isBigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof BinaryExpression binary) {
|
||||||
|
Long right = eval(binary.getRight(), pp, domain);
|
||||||
|
Long left = eval(binary.getLeft(), pp, domain);
|
||||||
|
|
||||||
|
if (right == null || left == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lsize = op.getInput(0).getSize();
|
||||||
|
int rsize = op.getInput(1).getSize();
|
||||||
|
byte[] bytes = arithmetic.binaryOp(op.getOpcode(), op.getOutput().getSize(),
|
||||||
|
lsize, getValue(left, lsize),
|
||||||
|
rsize, getValue(right, rsize));
|
||||||
|
return Utils.bytesToLong(bytes, op.getOutput().getSize(), isBigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof PushAny) {
|
||||||
|
InstLocation loc = (InstLocation) pp.getLocation();
|
||||||
|
Function f = loc.function();
|
||||||
|
try {
|
||||||
|
if (f != null && pp instanceof Assignment a) {
|
||||||
|
Program program = f.getProgram();
|
||||||
|
Address address = program.getAddressFactory()
|
||||||
|
.getRegisterSpace()
|
||||||
|
.getAddress(a.getLeft().toString());
|
||||||
|
Register r = program.getRegister(address);
|
||||||
|
if (r != null) {
|
||||||
|
RegisterValue rv =
|
||||||
|
program.getProgramContext().getRegisterValue(r, f.getEntryPoint());
|
||||||
|
if (rv != null && rv.hasValue()) {
|
||||||
|
return rv.getUnsignedValue().longValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (AddressFormatException e1) {
|
||||||
|
// IGNORE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<PcodeDataflowConstantPropagation> gen(
|
||||||
|
Identifier idg,
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
DefiniteDataflowDomain<PcodeDataflowConstantPropagation> domain) {
|
||||||
|
Set<PcodeDataflowConstantPropagation> gen = new HashSet<>();
|
||||||
|
|
||||||
|
Long v = eval(expression, pp, domain);
|
||||||
|
if (v != null) {
|
||||||
|
gen.add(new PcodeDataflowConstantPropagation(idg, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<PcodeDataflowConstantPropagation> gen(
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
DefiniteDataflowDomain<PcodeDataflowConstantPropagation> domain) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<PcodeDataflowConstantPropagation> kill(
|
||||||
|
Identifier idk,
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
DefiniteDataflowDomain<PcodeDataflowConstantPropagation> domain) {
|
||||||
|
Collection<PcodeDataflowConstantPropagation> result = new HashSet<>();
|
||||||
|
|
||||||
|
for (PcodeDataflowConstantPropagation cp : domain.getDataflowElements()) {
|
||||||
|
if (cp.id.equals(idk)) {
|
||||||
|
result.add(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<PcodeDataflowConstantPropagation> kill(
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
DefiniteDataflowDomain<PcodeDataflowConstantPropagation> domain) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeDataflowConstantPropagation other = (PcodeDataflowConstantPropagation) obj;
|
||||||
|
if (!Objects.equals(this.id, other.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Objects.equals(this.constant, other.constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
return new ListRepresentation(new StringRepresentation(id),
|
||||||
|
new StringRepresentation(constant));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeDataflowConstantPropagation pushScope(
|
||||||
|
ScopeToken scope)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodeDataflowConstantPropagation((Identifier) id.pushScope(scope), constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeDataflowConstantPropagation popScope(
|
||||||
|
ScopeToken scope)
|
||||||
|
throws SemanticException {
|
||||||
|
if (!(id instanceof OutOfScopeIdentifier)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PcodeDataflowConstantPropagation(((OutOfScopeIdentifier) id).popScope(scope),
|
||||||
|
constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+259
@@ -0,0 +1,259 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.statements.PcodeBinaryOperator;
|
||||||
|
import it.unive.lisa.analysis.SemanticException;
|
||||||
|
import it.unive.lisa.analysis.SemanticOracle;
|
||||||
|
import it.unive.lisa.analysis.nonRedundantSet.NonRedundantPowersetOfBaseNonRelationalValueDomain;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.analysis.numeric.Interval;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.Identifier;
|
||||||
|
import it.unive.lisa.symbolic.value.ValueExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.*;
|
||||||
|
import it.unive.lisa.util.numeric.MathNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The finite non redundant powerset of {@link Interval} abstract domain
|
||||||
|
* approximating numeric values as a non redundant set of interval. It is
|
||||||
|
* implemented as a {@link NonRedundantPowersetOfBaseNonRelationalValueDomain},
|
||||||
|
* which handles most of the basic operation (such as
|
||||||
|
* {@link NonRedundantPowersetOfBaseNonRelationalValueDomain#lubAux lub},
|
||||||
|
* {@link NonRedundantPowersetOfBaseNonRelationalValueDomain#glbAux glb},
|
||||||
|
* {@link NonRedundantPowersetOfBaseNonRelationalValueDomain#wideningAux
|
||||||
|
* widening} and others operations needed to calculate the previous ones).
|
||||||
|
*/
|
||||||
|
public class PcodeNonRedundantPowersetOfInterval
|
||||||
|
extends
|
||||||
|
NonRedundantPowersetOfBaseNonRelationalValueDomain<PcodeNonRedundantPowersetOfInterval, Interval> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an empty non redundant set of intervals.
|
||||||
|
*/
|
||||||
|
public PcodeNonRedundantPowersetOfInterval() {
|
||||||
|
super(new TreeSet<>(), Interval.BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a non redundant set of intervals with the given intervals.
|
||||||
|
*
|
||||||
|
* @param elements the set of intervals
|
||||||
|
*/
|
||||||
|
public PcodeNonRedundantPowersetOfInterval(
|
||||||
|
SortedSet<Interval> elements) {
|
||||||
|
super(elements, Interval.BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This specific Egli-Milner connector follows this definition:<br>
|
||||||
|
* given two subsets S<sub>1</sub> and S<sub>2</sub> of a domain of a
|
||||||
|
* lattice:
|
||||||
|
* <p>
|
||||||
|
* S<sub>1</sub> +<sub>EM</sub> S<sub>2</sub> = {s<sub>2</sub> ∋
|
||||||
|
* S<sub>2</sub> | ∃ s<sub>1</sub> ∋ S<sub>1</sub> : s<sub>1</sub>
|
||||||
|
* ≤ s<sub>2</sub>} ∪ {lub(s'<sub>1</sub>, s<sub>2</sub>) |
|
||||||
|
* s'<sub>1</sub> ∋ S<sub>1</sub>, s<sub>2</sub> ∋ S<sub>2</sub>, NOT
|
||||||
|
* ∃ s<sub>1</sub> ∋ S<sub>1</sub> : s<sub>1</sub> ≤
|
||||||
|
* s<sub>2</sub>}
|
||||||
|
* </p>
|
||||||
|
* s'<sub>1</sub> can be chosen randomly but in this case is chosen to be
|
||||||
|
* the closest interval to s<sub>2</sub> (closest based on
|
||||||
|
* {@link #middlePoint(Interval) middle point}).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected PcodeNonRedundantPowersetOfInterval EgliMilnerConnector(
|
||||||
|
PcodeNonRedundantPowersetOfInterval other)
|
||||||
|
throws SemanticException {
|
||||||
|
SortedSet<Interval> newElementsSet = new TreeSet<>();
|
||||||
|
SortedSet<Interval> notCoverSet = new TreeSet<>();
|
||||||
|
|
||||||
|
// first side of the union
|
||||||
|
for (Interval s2 : other.elementsSet) {
|
||||||
|
boolean existsLower = false;
|
||||||
|
for (Interval s1 : elementsSet) {
|
||||||
|
if (s1.lessOrEqual(s2)) {
|
||||||
|
existsLower = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (existsLower) {
|
||||||
|
newElementsSet.add(s2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
notCoverSet.add(s2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// second side of the union
|
||||||
|
for (Interval s2 : notCoverSet) {
|
||||||
|
MathNumber middlePoint = middlePoint(s2);
|
||||||
|
MathNumber closestValue = middlePoint;
|
||||||
|
MathNumber closestDiff = closestValue.subtract(middlePoint).abs();
|
||||||
|
Interval closest = Interval.TOP;
|
||||||
|
for (Interval s1 : elementsSet) {
|
||||||
|
if (closestValue.compareTo(middlePoint) == 0) {
|
||||||
|
closest = s1;
|
||||||
|
closestValue = middlePoint(s1);
|
||||||
|
closestDiff = closestValue.subtract(middlePoint).abs();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MathNumber s1Diff = middlePoint(s1).subtract(middlePoint).abs();
|
||||||
|
if (s1Diff.compareTo(closestDiff) < 0) {
|
||||||
|
closest = s1;
|
||||||
|
closestValue = middlePoint(s1);
|
||||||
|
closestDiff = closestValue.subtract(middlePoint).abs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newElementsSet.add(s2.lub(closest));
|
||||||
|
}
|
||||||
|
return new PcodeNonRedundantPowersetOfInterval(newElementsSet).removeRedundancy()
|
||||||
|
.removeOverlapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields the middle point of an {@link Interval}. If both extremes are
|
||||||
|
* non-infinite the middle point is the sum of the two divided by two. If
|
||||||
|
* only one of the two extreme is infinite the middle point is said to be
|
||||||
|
* the non-infinite extreme. If both the extremes are infinite the middle
|
||||||
|
* point is said to be 0.
|
||||||
|
*
|
||||||
|
* @param interval the interval to calculate the middle point of
|
||||||
|
*
|
||||||
|
* @return the middle point of the interval
|
||||||
|
*/
|
||||||
|
protected MathNumber middlePoint(
|
||||||
|
Interval interval) {
|
||||||
|
if (interval.interval.isFinite()) {
|
||||||
|
return interval.interval.getLow()
|
||||||
|
.add(interval.interval.getHigh())
|
||||||
|
.divide(new MathNumber(2));
|
||||||
|
}
|
||||||
|
else if (interval.interval.getHigh().isFinite() && !interval.interval.getLow().isFinite()) {
|
||||||
|
return interval.interval.getHigh();
|
||||||
|
}
|
||||||
|
else if (!interval.interval.getHigh().isFinite() && interval.interval.getLow().isFinite()) {
|
||||||
|
return interval.interval.getLow().subtract(MathNumber.ONE);
|
||||||
|
}
|
||||||
|
// both infinite
|
||||||
|
return MathNumber.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueEnvironment<PcodeNonRedundantPowersetOfInterval> assumeBinaryExpression(
|
||||||
|
ValueEnvironment<PcodeNonRedundantPowersetOfInterval> environment,
|
||||||
|
BinaryOperator operator,
|
||||||
|
ValueExpression left,
|
||||||
|
ValueExpression right,
|
||||||
|
ProgramPoint src,
|
||||||
|
ProgramPoint dest,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
Identifier id;
|
||||||
|
PcodeNonRedundantPowersetOfInterval eval;
|
||||||
|
boolean rightIsExpr;
|
||||||
|
if (left instanceof Identifier leftId) {
|
||||||
|
eval = eval(right, environment, src, oracle);
|
||||||
|
id = leftId;
|
||||||
|
rightIsExpr = true;
|
||||||
|
}
|
||||||
|
else if (right instanceof Identifier rightId) {
|
||||||
|
eval = eval(left, environment, src, oracle);
|
||||||
|
id = rightId;
|
||||||
|
rightIsExpr = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeNonRedundantPowersetOfInterval starting = environment.getState(id);
|
||||||
|
if (eval.isBottom() || starting.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
SortedSet<Interval> newSet = new TreeSet<>();
|
||||||
|
|
||||||
|
for (Interval startingInterval : starting.elementsSet)
|
||||||
|
for (Interval interval : eval.elementsSet) {
|
||||||
|
boolean lowIsMinusInfinity = interval.interval.lowIsMinusInfinity();
|
||||||
|
Interval lowInf =
|
||||||
|
new Interval(interval.interval.getLow(), MathNumber.PLUS_INFINITY);
|
||||||
|
Interval lowp1Inf = new Interval(interval.interval.getLow().add(MathNumber.ONE),
|
||||||
|
MathNumber.PLUS_INFINITY);
|
||||||
|
Interval infHigh =
|
||||||
|
new Interval(MathNumber.MINUS_INFINITY, interval.interval.getHigh());
|
||||||
|
Interval infHighm1 = new Interval(MathNumber.MINUS_INFINITY,
|
||||||
|
interval.interval.getHigh().subtract(MathNumber.ONE));
|
||||||
|
|
||||||
|
if (!(operator instanceof PcodeBinaryOperator)) {
|
||||||
|
if (operator instanceof ComparisonEq) {
|
||||||
|
newSet.add(interval);
|
||||||
|
}
|
||||||
|
else if (operator instanceof ComparisonLe) {
|
||||||
|
if (rightIsExpr) {
|
||||||
|
newSet.add(startingInterval.glb(infHigh));
|
||||||
|
}
|
||||||
|
else if (lowIsMinusInfinity) {
|
||||||
|
newSet.add(startingInterval);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newSet.add(startingInterval.glb(lowInf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (operator instanceof ComparisonLt) {
|
||||||
|
if (rightIsExpr) {
|
||||||
|
newSet.add(
|
||||||
|
lowIsMinusInfinity ? interval : startingInterval.glb(infHighm1));
|
||||||
|
}
|
||||||
|
else if (lowIsMinusInfinity) {
|
||||||
|
newSet.add(startingInterval);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newSet.add(startingInterval.glb(lowp1Inf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newSet.add(startingInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeNonRedundantPowersetOfInterval intervals =
|
||||||
|
new PcodeNonRedundantPowersetOfInterval(newSet)
|
||||||
|
.removeRedundancy()
|
||||||
|
.removeOverlapping();
|
||||||
|
if (intervals.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
return environment.putState(id, intervals);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeNonRedundantPowersetOfInterval mk(
|
||||||
|
SortedSet<Interval> elements) {
|
||||||
|
return new PcodeNonRedundantPowersetOfInterval(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodeNonRedundantPowersetOfInterval other = (PcodeNonRedundantPowersetOfInterval) obj;
|
||||||
|
if (!Objects.equals(this.elementsSet, other.elementsSet)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Objects.equals(this.valueDomain, other.valueDomain);
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.analyses;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.InstLocation;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressFormatException;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import it.unive.lisa.analysis.SemanticException;
|
||||||
|
import it.unive.lisa.analysis.SemanticOracle;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.BaseNonRelationalValueDomain;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Assignment;
|
||||||
|
import it.unive.lisa.symbolic.value.PushAny;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <T> the concrete type of this domain
|
||||||
|
*/
|
||||||
|
public interface PcodeNonRelationalValueDomain<T extends PcodeNonRelationalValueDomain<T>>
|
||||||
|
extends BaseNonRelationalValueDomain<T> {
|
||||||
|
|
||||||
|
T getValue(RegisterValue rv);
|
||||||
|
|
||||||
|
default T getValue(ProgramPoint pp) {
|
||||||
|
InstLocation loc = (InstLocation) pp.getLocation();
|
||||||
|
Function f = loc.function();
|
||||||
|
if (f != null && pp instanceof Assignment a) {
|
||||||
|
Program program = f.getProgram();
|
||||||
|
try {
|
||||||
|
Address address = program.getAddressFactory()
|
||||||
|
.getRegisterSpace()
|
||||||
|
.getAddress(a.getLeft().toString());
|
||||||
|
Register r = program.getRegister(address);
|
||||||
|
if (r != null) {
|
||||||
|
RegisterValue rv =
|
||||||
|
program.getProgramContext().getRegisterValue(r, f.getEntryPoint());
|
||||||
|
return getValue(rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (AddressFormatException e) {
|
||||||
|
// IGNORE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getValue((RegisterValue) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default T evalPushAny(
|
||||||
|
PushAny pushAny,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
T v = getValue(pp);
|
||||||
|
return v == null ? top() : v;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.BaseNonRelationalValueDomain;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.*;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.BinaryOperator;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.ComparisonEq;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.unary.UnaryOperator;
|
||||||
|
import it.unive.lisa.util.representation.StringRepresentation;
|
||||||
|
import it.unive.lisa.util.representation.StructuredRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The overflow-insensitive Parity abstract domain, tracking if a numeric value
|
||||||
|
* is even or odd, implemented as a {@link BaseNonRelationalValueDomain},
|
||||||
|
* handling top and bottom values for the expression evaluation and bottom
|
||||||
|
* values for the expression satisfiability. Top and bottom cases for least
|
||||||
|
* upper bound, widening and less or equals operations are handled by
|
||||||
|
* {@link BaseLattice} in {@link BaseLattice#lub}, {@link BaseLattice#widening}
|
||||||
|
* and {@link BaseLattice#lessOrEqual} methods, respectively.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:vincenzo.arceri@unive.it">Vincenzo Arceri</a>
|
||||||
|
*/
|
||||||
|
public class PcodeParity implements PcodeNonRelationalValueDomain<PcodeParity> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract even element.
|
||||||
|
*/
|
||||||
|
public static final PcodeParity EVEN = new PcodeParity((byte) 3);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract odd element.
|
||||||
|
*/
|
||||||
|
public static final PcodeParity ODD = new PcodeParity((byte) 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract top element.
|
||||||
|
*/
|
||||||
|
public static final PcodeParity TOP = new PcodeParity((byte) 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract bottom element.
|
||||||
|
*/
|
||||||
|
public static final PcodeParity BOTTOM = new PcodeParity((byte) 1);
|
||||||
|
|
||||||
|
private final byte parity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the parity abstract domain, representing the top of the parity
|
||||||
|
* abstract domain.
|
||||||
|
*/
|
||||||
|
public PcodeParity() {
|
||||||
|
this((byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the parity instance for the given parity value.
|
||||||
|
*
|
||||||
|
* @param parity the sign (0 = top, 1 = bottom, 2 = odd, 3 = even)
|
||||||
|
*/
|
||||||
|
public PcodeParity(
|
||||||
|
byte parity) {
|
||||||
|
this.parity = parity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity top() {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity bottom() {
|
||||||
|
return BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
if (isBottom()) {
|
||||||
|
return Lattice.bottomRepresentation();
|
||||||
|
}
|
||||||
|
if (isTop()) {
|
||||||
|
return Lattice.topRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
String repr = this == EVEN ? "Even" : "Odd";
|
||||||
|
|
||||||
|
return new StringRepresentation(repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity evalNullConstant(
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity evalNonNullConstant(
|
||||||
|
Constant constant,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
|
||||||
|
Object cval = constant.getValue();
|
||||||
|
if (cval instanceof Long lval) {
|
||||||
|
return lval % 2 == 0 ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
if (cval instanceof Integer ival) {
|
||||||
|
return ival % 2 == 0 ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
if (cval instanceof Short sval) {
|
||||||
|
return sval % 2 == 0 ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
if (cval instanceof Byte bval) {
|
||||||
|
return bval % 2 == 0 ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
if (cval instanceof Boolean bval) {
|
||||||
|
return bval ? ODD : EVEN;
|
||||||
|
}
|
||||||
|
Msg.error(this, "Unknown type for constant: " + cval);
|
||||||
|
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields whether or not this is the even parity.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean isEven() {
|
||||||
|
return this == EVEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields whether or not this is the odd parity.
|
||||||
|
*
|
||||||
|
* @return {@code true} if that condition holds
|
||||||
|
*/
|
||||||
|
public boolean isOdd() {
|
||||||
|
return this == ODD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity evalUnaryExpression(
|
||||||
|
UnaryOperator operator,
|
||||||
|
PcodeParity arg,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity evalBinaryExpression(
|
||||||
|
BinaryOperator operator,
|
||||||
|
PcodeParity left,
|
||||||
|
PcodeParity right,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle) {
|
||||||
|
if (left.isTop() || right.isTop()) {
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||||
|
PcodeOp op = ploc.op;
|
||||||
|
int opcode = op.getOpcode();
|
||||||
|
if (opcode == PcodeOp.INT_ADD || opcode == PcodeOp.FLOAT_ADD ||
|
||||||
|
opcode == PcodeOp.INT_SUB || opcode == PcodeOp.FLOAT_SUB) {
|
||||||
|
return (right.equals(left)) ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_AND || opcode == PcodeOp.BOOL_AND) {
|
||||||
|
return (right.equals(left)) ? left : EVEN;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_OR || opcode == PcodeOp.BOOL_OR) {
|
||||||
|
return (right.equals(left)) ? left : ODD;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_XOR || opcode == PcodeOp.BOOL_XOR) {
|
||||||
|
return (right.equals(left)) ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_MULT || opcode == PcodeOp.FLOAT_MULT) {
|
||||||
|
return left.isEven() || right.isEven() ? EVEN : ODD;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_DIV || opcode == PcodeOp.FLOAT_DIV) {
|
||||||
|
if (left.isOdd()) {
|
||||||
|
return right.isOdd() ? ODD : EVEN;
|
||||||
|
}
|
||||||
|
return right.isOdd() ? EVEN : TOP;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_REM || opcode == PcodeOp.INT_SREM) {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_AND) {
|
||||||
|
if (left.equals(EVEN) || right.equals(EVEN)) {
|
||||||
|
return EVEN;
|
||||||
|
}
|
||||||
|
return ODD;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_OR) {
|
||||||
|
if (left.equals(ODD) || right.equals(ODD)) {
|
||||||
|
return ODD;
|
||||||
|
}
|
||||||
|
return EVEN;
|
||||||
|
}
|
||||||
|
else if (opcode == PcodeOp.INT_XOR) {
|
||||||
|
if (left.equals(right))
|
||||||
|
return EVEN;
|
||||||
|
return ODD;
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity lubAux(
|
||||||
|
PcodeParity other)
|
||||||
|
throws SemanticException {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lessOrEqualAux(
|
||||||
|
PcodeParity other)
|
||||||
|
throws SemanticException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + parity;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodeParity other = (PcodeParity) obj;
|
||||||
|
if (parity != other.parity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueEnvironment<PcodeParity> assumeBinaryExpression(
|
||||||
|
ValueEnvironment<PcodeParity> environment,
|
||||||
|
BinaryOperator operator,
|
||||||
|
ValueExpression left,
|
||||||
|
ValueExpression right,
|
||||||
|
ProgramPoint src,
|
||||||
|
ProgramPoint dest,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
if (operator instanceof ComparisonEq) {
|
||||||
|
if (left instanceof Identifier) {
|
||||||
|
PcodeParity eval = eval(right, environment, src, oracle);
|
||||||
|
if (eval.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
return environment.putState((Identifier) left, eval);
|
||||||
|
}
|
||||||
|
else if (right instanceof Identifier) {
|
||||||
|
PcodeParity eval = eval(left, environment, src, oracle);
|
||||||
|
if (eval.isBottom()) {
|
||||||
|
return environment.bottom();
|
||||||
|
}
|
||||||
|
return environment.putState((Identifier) right, eval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeParity getValue(RegisterValue rv) {
|
||||||
|
if (rv != null) {
|
||||||
|
BigInteger val = rv.getUnsignedValue();
|
||||||
|
if (val != null) {
|
||||||
|
return new PcodeParity((byte) (val.longValue() % 2 == 0 ? 3 : 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.lattices.Satisfiability;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.analysis.value.ValueDomain;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.*;
|
||||||
|
import it.unive.lisa.util.numeric.MathNumber;
|
||||||
|
import it.unive.lisa.util.representation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /** The pentagons abstract domain, a weakly relational numeric abstract
|
||||||
|
* domain. This abstract domain captures properties of the form of x \in [a, b]
|
||||||
|
* ∧ x < y. It is more precise than the well known interval domain, but
|
||||||
|
* it is less precise than the octagon domain. It is implemented as a
|
||||||
|
* {@link ValueDomain}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||||
|
* @author <a href="mailto:vincenzo.arceri@unipr.it">Vincenzo Arceri</a>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* @see <a href=
|
||||||
|
* "https://www.sciencedirect.com/science/article/pii/S0167642309000719?ref=cra_js_challenge&fr=RR-1">Pentagons:
|
||||||
|
* A weakly relational abstract domain for the efficient validation of
|
||||||
|
* array accesses</a>
|
||||||
|
*/
|
||||||
|
public class PcodePentagon implements ValueDomain<PcodePentagon>, BaseLattice<PcodePentagon> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval environment.
|
||||||
|
*/
|
||||||
|
private final ValueEnvironment<PcodeInterval> intervals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The upper bounds environment.
|
||||||
|
*/
|
||||||
|
private final ValueEnvironment<PcodeUpperBounds> upperBounds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the PcodePentagons.
|
||||||
|
*/
|
||||||
|
public PcodePentagon() {
|
||||||
|
this.intervals = new ValueEnvironment<>(new PcodeInterval()).top();
|
||||||
|
this.upperBounds = new ValueEnvironment<>(new PcodeUpperBounds(true)).top();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the pentagons.
|
||||||
|
*
|
||||||
|
* @param intervals the interval environment
|
||||||
|
* @param upperBounds the upper bounds environment
|
||||||
|
*/
|
||||||
|
public PcodePentagon(
|
||||||
|
ValueEnvironment<PcodeInterval> intervals,
|
||||||
|
ValueEnvironment<PcodeUpperBounds> upperBounds) {
|
||||||
|
this.intervals = intervals;
|
||||||
|
this.upperBounds = upperBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon assign(
|
||||||
|
Identifier id,
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
ValueEnvironment<PcodeUpperBounds> newBounds =
|
||||||
|
getUpperBounds().assign(id, expression, pp, oracle);
|
||||||
|
ValueEnvironment<PcodeInterval> newIntervals =
|
||||||
|
getIntervals().assign(id, expression, pp, oracle);
|
||||||
|
|
||||||
|
// we add the semantics for assignments here as we have access to the
|
||||||
|
// whole assignment
|
||||||
|
if (expression instanceof BinaryExpression) {
|
||||||
|
BinaryExpression be = (BinaryExpression) expression;
|
||||||
|
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||||
|
PcodeOp op = ploc.op;
|
||||||
|
int opcode = op.getOpcode();
|
||||||
|
if (opcode == PcodeOp.INT_SUB || opcode == PcodeOp.FLOAT_SUB) {
|
||||||
|
if (be.getLeft() instanceof Identifier x) {
|
||||||
|
if (be.getRight() instanceof Constant) {
|
||||||
|
// r = x - c
|
||||||
|
newBounds = newBounds.putState(id, getUpperBounds().getState(x).add(x));
|
||||||
|
}
|
||||||
|
else if (be.getRight() instanceof Identifier) {
|
||||||
|
// r = x - y
|
||||||
|
Identifier y = (Identifier) be.getRight();
|
||||||
|
|
||||||
|
if (newBounds.getState(y).contains(x)) {
|
||||||
|
newIntervals = newIntervals.putState(id, newIntervals.getState(id)
|
||||||
|
.glb(new PcodeInterval(MathNumber.ZERO,
|
||||||
|
MathNumber.PLUS_INFINITY))); // was MathNumber.ONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((opcode == PcodeOp.INT_REM || opcode == PcodeOp.INT_SREM) &&
|
||||||
|
be.getRight() instanceof Identifier d) {
|
||||||
|
// r = u % d
|
||||||
|
MathNumber low = getIntervals().getState(d).interval.getLow();
|
||||||
|
if (low.isPositive() || low.isZero()) {
|
||||||
|
newBounds =
|
||||||
|
newBounds.putState(id, new PcodeUpperBounds(Collections.singleton(d)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newBounds = newBounds.putState(id, new PcodeUpperBounds().top());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PcodePentagon(
|
||||||
|
newIntervals,
|
||||||
|
newBounds).closure();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon smallStepSemantics(
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(
|
||||||
|
getIntervals().smallStepSemantics(expression, pp, oracle),
|
||||||
|
getUpperBounds().smallStepSemantics(expression, pp, oracle));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon assume(
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint src,
|
||||||
|
ProgramPoint dest,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(
|
||||||
|
getIntervals().assume(expression, src, dest, oracle),
|
||||||
|
getUpperBounds().assume(expression, src, dest, oracle));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon forgetIdentifier(
|
||||||
|
Identifier id)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(
|
||||||
|
getIntervals().forgetIdentifier(id),
|
||||||
|
getUpperBounds().forgetIdentifier(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon forgetIdentifiersIf(
|
||||||
|
Predicate<Identifier> test)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(
|
||||||
|
getIntervals().forgetIdentifiersIf(test),
|
||||||
|
getUpperBounds().forgetIdentifiersIf(test));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Satisfiability satisfies(
|
||||||
|
ValueExpression expression,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
return getIntervals().satisfies(expression, pp, oracle)
|
||||||
|
.glb(getUpperBounds().satisfies(expression, pp, oracle));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon pushScope(
|
||||||
|
ScopeToken token)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(getIntervals().pushScope(token),
|
||||||
|
getUpperBounds().pushScope(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon popScope(
|
||||||
|
ScopeToken token)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(getIntervals().popScope(token), getUpperBounds().popScope(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
if (isTop())
|
||||||
|
return Lattice.topRepresentation();
|
||||||
|
if (isBottom())
|
||||||
|
return Lattice.bottomRepresentation();
|
||||||
|
Map<StructuredRepresentation, StructuredRepresentation> mapping = new HashMap<>();
|
||||||
|
for (Identifier id : CollectionUtils.union(getIntervals().getKeys(),
|
||||||
|
getUpperBounds().getKeys())) {
|
||||||
|
mapping.put(new StringRepresentation(id),
|
||||||
|
new StringRepresentation(getIntervals().getState(id).toString() + ", " +
|
||||||
|
getUpperBounds().getState(id).representation()));
|
||||||
|
}
|
||||||
|
return new MapRepresentation(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon top() {
|
||||||
|
return new PcodePentagon(getIntervals().top(), getUpperBounds().top());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTop() {
|
||||||
|
return getIntervals().isTop() && getUpperBounds().isTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon bottom() {
|
||||||
|
return new PcodePentagon(getIntervals().bottom(), getUpperBounds().bottom());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBottom() {
|
||||||
|
return getIntervals().isBottom() && getUpperBounds().isBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PcodePentagon closure() throws SemanticException {
|
||||||
|
ValueEnvironment<PcodeUpperBounds> newBounds = new ValueEnvironment<PcodeUpperBounds>(
|
||||||
|
getUpperBounds().lattice, getUpperBounds().getMap());
|
||||||
|
|
||||||
|
for (Identifier id1 : getIntervals().getKeys()) {
|
||||||
|
Set<Identifier> closure = new HashSet<>();
|
||||||
|
for (Identifier id2 : getIntervals().getKeys()) {
|
||||||
|
if (!id1.equals(id2)) {
|
||||||
|
PcodeInterval state1 = getIntervals().getState(id1);
|
||||||
|
LongInterval interval1 = state1.interval;
|
||||||
|
PcodeInterval state2 = getIntervals().getState(id2);
|
||||||
|
LongInterval interval2 = state2.interval;
|
||||||
|
if (interval1 != null && interval2 != null) {
|
||||||
|
if (interval1.getHigh().compareTo(interval2.getLow()) < 0) {
|
||||||
|
closure.add(id2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.error(this, "Unexpected combination: " + state1 + " : " + state2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!closure.isEmpty()) {
|
||||||
|
// glb is the union
|
||||||
|
newBounds = newBounds.putState(id1,
|
||||||
|
newBounds.getState(id1).glb(new PcodeUpperBounds(closure)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PcodePentagon(getIntervals(), newBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon lubAux(
|
||||||
|
PcodePentagon other)
|
||||||
|
throws SemanticException {
|
||||||
|
ValueEnvironment<PcodeUpperBounds> newBounds = getUpperBounds().lub(other.getUpperBounds());
|
||||||
|
for (Entry<Identifier, PcodeUpperBounds> entry : getUpperBounds()) {
|
||||||
|
Set<Identifier> closure = new HashSet<>();
|
||||||
|
for (Identifier bound : entry.getValue()) {
|
||||||
|
PcodeInterval entryState = other.getIntervals().getState(entry.getKey());
|
||||||
|
LongInterval entryInterval = entryState.interval;
|
||||||
|
PcodeInterval boundsState = other.getIntervals().getState(bound);
|
||||||
|
LongInterval boundsInterval = boundsState.interval;
|
||||||
|
if (entryInterval != null && boundsInterval != null) {
|
||||||
|
if (entryInterval.getHigh().compareTo(boundsInterval.getLow()) < 0) {
|
||||||
|
closure.add(bound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.error(this, "Unexpected combination: " + entryState + " : " + boundsState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!closure.isEmpty()) {
|
||||||
|
// glb is the union
|
||||||
|
newBounds = newBounds.putState(entry.getKey(),
|
||||||
|
newBounds.getState(entry.getKey()).glb(new PcodeUpperBounds(closure)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry<Identifier, PcodeUpperBounds> entry : other.getUpperBounds()) {
|
||||||
|
Set<Identifier> closure = new HashSet<>();
|
||||||
|
for (Identifier bound : entry.getValue()) {
|
||||||
|
PcodeInterval entryState = getIntervals().getState(entry.getKey());
|
||||||
|
LongInterval entryInterval = entryState.interval;
|
||||||
|
PcodeInterval boundsState = getIntervals().getState(bound);
|
||||||
|
LongInterval boundsInterval = boundsState.interval;
|
||||||
|
if (entryInterval != null && boundsInterval != null) {
|
||||||
|
if (entryInterval.getHigh().compareTo(boundsInterval.getLow()) < 0) {
|
||||||
|
closure.add(bound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.error(this, "Unexpected combination: " + entryState + " : " + boundsState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!closure.isEmpty()) {
|
||||||
|
// glb is the union
|
||||||
|
newBounds = newBounds.putState(entry.getKey(),
|
||||||
|
newBounds.getState(entry.getKey()).glb(new PcodeUpperBounds(closure)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PcodePentagon(getIntervals().lub(other.getIntervals()), newBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodePentagon wideningAux(
|
||||||
|
PcodePentagon other)
|
||||||
|
throws SemanticException {
|
||||||
|
return new PcodePentagon(getIntervals().widening(other.getIntervals()),
|
||||||
|
getUpperBounds().widening(other.getUpperBounds()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lessOrEqualAux(
|
||||||
|
PcodePentagon other)
|
||||||
|
throws SemanticException {
|
||||||
|
if (!getIntervals().lessOrEqual(other.getIntervals())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Entry<Identifier, PcodeUpperBounds> entry : other.getUpperBounds()) {
|
||||||
|
for (Identifier bound : entry.getValue()) {
|
||||||
|
if (!(getUpperBounds().getState(entry.getKey()).contains(bound) ||
|
||||||
|
getIntervals().getState(entry.getKey()).interval.getHigh()
|
||||||
|
.compareTo(getIntervals().getState(bound).interval.getLow()) < 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getIntervals(), getUpperBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodePentagon other = (PcodePentagon) obj;
|
||||||
|
return Objects.equals(getIntervals(), other.getIntervals()) &&
|
||||||
|
Objects.equals(getUpperBounds(), other.getUpperBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return representation().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean knowsIdentifier(
|
||||||
|
Identifier id) {
|
||||||
|
return getIntervals().knowsIdentifier(id) || getUpperBounds().knowsIdentifier(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueEnvironment<PcodeInterval> getIntervals() {
|
||||||
|
return intervals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueEnvironment<PcodeUpperBounds> getUpperBounds() {
|
||||||
|
return upperBounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.taint.BaseTaint;
|
||||||
|
import it.unive.lisa.program.annotations.Annotation;
|
||||||
|
import it.unive.lisa.program.annotations.Annotations;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.Identifier;
|
||||||
|
import it.unive.lisa.util.representation.StringRepresentation;
|
||||||
|
import it.unive.lisa.util.representation.StructuredRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link BaseTaint} implementation with only two level of taintedness: clean
|
||||||
|
* and tainted. As such, this class distinguishes values that are always clean
|
||||||
|
* from values that are tainted in at least one execution path.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||||
|
*/
|
||||||
|
public class PcodeTaint extends BaseTaint<PcodeTaint> {
|
||||||
|
|
||||||
|
private static final PcodeTaint TAINTED = new PcodeTaint(true);
|
||||||
|
|
||||||
|
private static final PcodeTaint CLEAN = new PcodeTaint(false);
|
||||||
|
|
||||||
|
private static final PcodeTaint BOTTOM = new PcodeTaint(null);
|
||||||
|
|
||||||
|
private final Boolean taint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new instance of taint.
|
||||||
|
*/
|
||||||
|
public PcodeTaint() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeTaint(
|
||||||
|
Boolean taint) {
|
||||||
|
this.taint = taint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeTaint tainted() {
|
||||||
|
return TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeTaint clean() {
|
||||||
|
return CLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossiblyTainted() {
|
||||||
|
return this == TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAlwaysTainted() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
if (this == BOTTOM) {
|
||||||
|
return Lattice.bottomRepresentation();
|
||||||
|
}
|
||||||
|
return this == TAINTED ? new StringRepresentation("#") : new StringRepresentation("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeTaint top() {
|
||||||
|
return CLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeTaint bottom() {
|
||||||
|
return BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeTaint defaultApprox(
|
||||||
|
Identifier id,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
Annotations annots = id.getAnnotations();
|
||||||
|
if (annots.isEmpty()) {
|
||||||
|
return super.defaultApprox(id, pp, oracle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pp.getLocation() instanceof PcodeLocation ploc) {
|
||||||
|
for (Annotation annotation : annots) {
|
||||||
|
String name = annotation.getAnnotationName();
|
||||||
|
if (name.contains("@" + ploc.getAddress())) {
|
||||||
|
if (name.contains("Tainted")) {
|
||||||
|
return tainted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.contains("Clean")) {
|
||||||
|
return clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeTaint lub(PcodeTaint other) throws SemanticException {
|
||||||
|
if (other == null || other.isBottom() || this.isTop() || this == other ||
|
||||||
|
this.equals(other)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isBottom()) { // || other.isTop())
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lubAux(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeTaint lubAux(
|
||||||
|
PcodeTaint other)
|
||||||
|
throws SemanticException {
|
||||||
|
return TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeTaint wideningAux(
|
||||||
|
PcodeTaint other)
|
||||||
|
throws SemanticException {
|
||||||
|
return TAINTED; // should never happen
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lessOrEqualAux(
|
||||||
|
PcodeTaint other)
|
||||||
|
throws SemanticException {
|
||||||
|
return false; // should never happen
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((taint == null) ? 0 : taint.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodeTaint other = (PcodeTaint) obj;
|
||||||
|
if (taint == null) {
|
||||||
|
if (other.taint != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!taint.equals(other.taint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return representation().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+210
@@ -0,0 +1,210 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.taint.BaseTaint;
|
||||||
|
import it.unive.lisa.program.annotations.Annotation;
|
||||||
|
import it.unive.lisa.program.annotations.Annotations;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.Identifier;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.BinaryOperator;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.ternary.TernaryOperator;
|
||||||
|
import it.unive.lisa.util.representation.StringRepresentation;
|
||||||
|
import it.unive.lisa.util.representation.StructuredRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link BaseTaint} implementation with three level of taintedness: clean,
|
||||||
|
* tainted and top. As such, this class distinguishes values that are always
|
||||||
|
* clean, always tainted, or tainted in at least one execution path.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||||
|
*/
|
||||||
|
public class PcodeThreeLevelTaint extends BaseTaint<PcodeThreeLevelTaint> {
|
||||||
|
|
||||||
|
private static final PcodeThreeLevelTaint TOP = new PcodeThreeLevelTaint((byte) 3);
|
||||||
|
private static final PcodeThreeLevelTaint TAINTED = new PcodeThreeLevelTaint((byte) 2);
|
||||||
|
private static final PcodeThreeLevelTaint CLEAN = new PcodeThreeLevelTaint((byte) 1);
|
||||||
|
private static final PcodeThreeLevelTaint BOTTOM = new PcodeThreeLevelTaint((byte) 0);
|
||||||
|
|
||||||
|
private final byte taint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new instance of taint.
|
||||||
|
*/
|
||||||
|
public PcodeThreeLevelTaint() {
|
||||||
|
this((byte) 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PcodeThreeLevelTaint(
|
||||||
|
byte v) {
|
||||||
|
this.taint = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeThreeLevelTaint tainted() {
|
||||||
|
return TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeThreeLevelTaint clean() {
|
||||||
|
return CLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAlwaysTainted() {
|
||||||
|
return this == TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossiblyTainted() {
|
||||||
|
return this == TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
return this == BOTTOM ? Lattice.bottomRepresentation()
|
||||||
|
: this == CLEAN ? new StringRepresentation("_")
|
||||||
|
: this == TAINTED ? new StringRepresentation("#")
|
||||||
|
: Lattice.topRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeThreeLevelTaint top() {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeThreeLevelTaint bottom() {
|
||||||
|
return BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeThreeLevelTaint defaultApprox(
|
||||||
|
Identifier id,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
Annotations annots = id.getAnnotations();
|
||||||
|
if (annots.isEmpty()) {
|
||||||
|
return super.defaultApprox(id, pp, oracle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pp.getLocation() instanceof PcodeLocation ploc) {
|
||||||
|
for (Annotation annotation : annots) {
|
||||||
|
String name = annotation.getAnnotationName();
|
||||||
|
if (name.contains("@" + ploc.getAddress())) {
|
||||||
|
if (name.contains("Tainted")) {
|
||||||
|
return tainted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.contains("Clean")) {
|
||||||
|
return clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeThreeLevelTaint evalBinaryExpression(
|
||||||
|
BinaryOperator operator,
|
||||||
|
PcodeThreeLevelTaint left,
|
||||||
|
PcodeThreeLevelTaint right,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
if (left == TAINTED || right == TAINTED) {
|
||||||
|
return TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left == TOP || right == TOP) {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeThreeLevelTaint evalTernaryExpression(
|
||||||
|
TernaryOperator operator,
|
||||||
|
PcodeThreeLevelTaint left,
|
||||||
|
PcodeThreeLevelTaint middle,
|
||||||
|
PcodeThreeLevelTaint right,
|
||||||
|
ProgramPoint pp,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
if (left == TAINTED || right == TAINTED || middle == TAINTED) {
|
||||||
|
return TAINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left == TOP || right == TOP || middle == TOP) {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeThreeLevelTaint lubAux(
|
||||||
|
PcodeThreeLevelTaint other)
|
||||||
|
throws SemanticException {
|
||||||
|
// only happens with clean and tainted, that are not comparable
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeThreeLevelTaint wideningAux(
|
||||||
|
PcodeThreeLevelTaint other)
|
||||||
|
throws SemanticException {
|
||||||
|
// only happens with clean and tainted, that are not comparable
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lessOrEqualAux(
|
||||||
|
PcodeThreeLevelTaint other)
|
||||||
|
throws SemanticException {
|
||||||
|
// only happens with clean and tainted, that are not comparable
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + taint;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodeThreeLevelTaint other = (PcodeThreeLevelTaint) obj;
|
||||||
|
if (taint != other.taint) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return representation().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+245
@@ -0,0 +1,245 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: MIT
|
||||||
|
*/
|
||||||
|
package ghidra.lisa.pcode.analyses;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.statements.PcodeBinaryOperator;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.BaseNonRelationalValueDomain;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||||
|
import it.unive.lisa.symbolic.value.Identifier;
|
||||||
|
import it.unive.lisa.symbolic.value.ValueExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.*;
|
||||||
|
import it.unive.lisa.util.representation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The upper bounds abstract domain. It is implemented as a
|
||||||
|
* {@link BaseNonRelationalValueDomain}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Modified to handle pcode from original source written by:
|
||||||
|
* <p>
|
||||||
|
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||||
|
* @author <a href="mailto:vincenzo.arceri@unipr.it">Vincenzo Arceri</a>
|
||||||
|
*/
|
||||||
|
public class PcodeUpperBounds
|
||||||
|
implements PcodeNonRelationalValueDomain<PcodeUpperBounds>, Iterable<Identifier> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract top element.
|
||||||
|
*/
|
||||||
|
private static final PcodeUpperBounds TOP = new PcodeUpperBounds(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract bottom element.
|
||||||
|
*/
|
||||||
|
private static final PcodeUpperBounds BOTTOM = new PcodeUpperBounds(new TreeSet<>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag to set abstract top state.
|
||||||
|
*/
|
||||||
|
private final boolean isTop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set containing the bounds.
|
||||||
|
*/
|
||||||
|
private final Set<Identifier> bounds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the upper bounds.
|
||||||
|
*/
|
||||||
|
public PcodeUpperBounds() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the upper bounds.
|
||||||
|
*
|
||||||
|
* @param isTop {@code true} if the abstract domain is top; otherwise
|
||||||
|
* {@code false}.
|
||||||
|
*/
|
||||||
|
public PcodeUpperBounds(
|
||||||
|
boolean isTop) {
|
||||||
|
this.bounds = null;
|
||||||
|
this.isTop = isTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the upper bounds.
|
||||||
|
*
|
||||||
|
* @param bounds the bounds to set
|
||||||
|
*/
|
||||||
|
public PcodeUpperBounds(
|
||||||
|
Set<Identifier> bounds) {
|
||||||
|
this.bounds = bounds;
|
||||||
|
this.isTop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredRepresentation representation() {
|
||||||
|
if (isTop()) {
|
||||||
|
return new StringRepresentation("{}");
|
||||||
|
}
|
||||||
|
if (isBottom()) {
|
||||||
|
return Lattice.bottomRepresentation();
|
||||||
|
}
|
||||||
|
return new SetRepresentation(bounds, StringRepresentation::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUpperBounds top() {
|
||||||
|
return TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUpperBounds bottom() {
|
||||||
|
return BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBottom() {
|
||||||
|
return !isTop && bounds.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUpperBounds lubAux(
|
||||||
|
PcodeUpperBounds other)
|
||||||
|
throws SemanticException {
|
||||||
|
Set<Identifier> lub = new HashSet<>(bounds);
|
||||||
|
lub.retainAll(other.bounds);
|
||||||
|
return new PcodeUpperBounds(lub);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUpperBounds glbAux(
|
||||||
|
PcodeUpperBounds other)
|
||||||
|
throws SemanticException {
|
||||||
|
Set<Identifier> lub = new HashSet<>(bounds);
|
||||||
|
lub.addAll(other.bounds);
|
||||||
|
return new PcodeUpperBounds(lub);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lessOrEqualAux(
|
||||||
|
PcodeUpperBounds other)
|
||||||
|
throws SemanticException {
|
||||||
|
return bounds.containsAll(other.bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUpperBounds wideningAux(
|
||||||
|
PcodeUpperBounds other)
|
||||||
|
throws SemanticException {
|
||||||
|
return other.bounds.containsAll(bounds) ? other : TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(
|
||||||
|
Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodeUpperBounds other = (PcodeUpperBounds) obj;
|
||||||
|
return Objects.equals(bounds, other.bounds) && isTop == other.isTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(bounds, isTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueEnvironment<PcodeUpperBounds> assumeBinaryExpression(
|
||||||
|
ValueEnvironment<PcodeUpperBounds> environment,
|
||||||
|
BinaryOperator operator,
|
||||||
|
ValueExpression left,
|
||||||
|
ValueExpression right,
|
||||||
|
ProgramPoint src,
|
||||||
|
ProgramPoint dest,
|
||||||
|
SemanticOracle oracle)
|
||||||
|
throws SemanticException {
|
||||||
|
if (!(left instanceof Identifier x && right instanceof Identifier y)) {
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// glb is the union!
|
||||||
|
|
||||||
|
if (!(operator instanceof PcodeBinaryOperator)) {
|
||||||
|
if (operator instanceof ComparisonEq) {
|
||||||
|
// x == y
|
||||||
|
PcodeUpperBounds set = environment.getState(x).glb(environment.getState(y));
|
||||||
|
return environment.putState(x, set).putState(y, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operator instanceof ComparisonLt) {
|
||||||
|
// x < y
|
||||||
|
PcodeUpperBounds set = environment.getState(x)
|
||||||
|
.glb(environment.getState(y))
|
||||||
|
.glb(new PcodeUpperBounds(Collections.singleton(y)));
|
||||||
|
return environment.putState(x, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operator instanceof ComparisonLe) {
|
||||||
|
// x <= y
|
||||||
|
PcodeUpperBounds set = environment.getState(x).glb(environment.getState(y));
|
||||||
|
return environment.putState(x, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Identifier> iterator() {
|
||||||
|
if (bounds == null) {
|
||||||
|
return Collections.emptyIterator();
|
||||||
|
}
|
||||||
|
return bounds.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this bounds contains a specified identifier of a program
|
||||||
|
* variable.
|
||||||
|
*
|
||||||
|
* @param id the identifier to check
|
||||||
|
*
|
||||||
|
* @return {@code true} if this bounds contains the specified identifier;
|
||||||
|
* otherwise, {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean contains(
|
||||||
|
Identifier id) {
|
||||||
|
return bounds != null && bounds.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified identifier of a program variable in the bounds.
|
||||||
|
*
|
||||||
|
* @param id the identifier to add in the bounds.
|
||||||
|
*
|
||||||
|
* @return the updated bounds.
|
||||||
|
*/
|
||||||
|
public PcodeUpperBounds add(
|
||||||
|
Identifier id) {
|
||||||
|
Set<Identifier> res = new HashSet<>();
|
||||||
|
if (!isTop() && !isBottom()) {
|
||||||
|
res.addAll(bounds);
|
||||||
|
}
|
||||||
|
res.add(id);
|
||||||
|
return new PcodeUpperBounds(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUpperBounds getValue(RegisterValue rv) {
|
||||||
|
return top();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Note: files in this directory are lightly-modified versions of analyses from https://github.com/lisa-analyzer, and, as such, are included with their original comments, author credits, and MIT License.
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class BinaryExprContext {
|
||||||
|
|
||||||
|
public PcodeOp op;
|
||||||
|
public VarnodeContext left;
|
||||||
|
public VarnodeContext right;
|
||||||
|
|
||||||
|
public BinaryExprContext(PcodeContext ctx) {
|
||||||
|
this.op = ctx.op;
|
||||||
|
left = new VarnodeContext(op.getInput(0));
|
||||||
|
right = new VarnodeContext(op.getInput(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for TypeConv
|
||||||
|
public BinaryExprContext(StatementContext ctx) {
|
||||||
|
this.op = ctx.op;
|
||||||
|
left = ctx.target();
|
||||||
|
right = new VarnodeContext(op.getInput(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int opcode() {
|
||||||
|
return op.getOpcode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeLocation location() {
|
||||||
|
return new PcodeLocation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String mnemonic() {
|
||||||
|
return op.getMnemonic();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.cfg.statement.call.Call.CallType;
|
||||||
|
|
||||||
|
public class CallContext extends PcodeContext {
|
||||||
|
|
||||||
|
private Function callee = null;
|
||||||
|
public VarnodeContext left;
|
||||||
|
private boolean isTarget;
|
||||||
|
|
||||||
|
public CallContext(PcodeOp op, UnitContext ctx) {
|
||||||
|
super(op);
|
||||||
|
this.op = op;
|
||||||
|
Address address = op.getInput(0).getAddress();
|
||||||
|
Function caller = ctx.function();
|
||||||
|
left = new SymbolVarnodeContext(address);
|
||||||
|
if (address.getAddressSpace().isMemorySpace()) {
|
||||||
|
callee = caller.getProgram().getFunctionManager().getFunctionAt(address);
|
||||||
|
if (callee != null) {
|
||||||
|
left = new SymbolVarnodeContext(callee.getName(), address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return op.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCalleeName() {
|
||||||
|
return callee == null ? "UNKNOWN" : callee.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function function() {
|
||||||
|
return callee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallType type() {
|
||||||
|
if (callee == null || callee.isThunk()) {
|
||||||
|
return CallType.UNKNOWN;
|
||||||
|
}
|
||||||
|
if (op.getInput(0).getAddress().getAddressSpace().isMemorySpace()) {
|
||||||
|
return CallType.STATIC;
|
||||||
|
}
|
||||||
|
return CallType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTarget() {
|
||||||
|
return isTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsTarget(boolean isTarget) {
|
||||||
|
this.isTarget = isTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
|
||||||
|
public class ConditionContext extends PcodeContext {
|
||||||
|
|
||||||
|
public ConditionContext(PcodeOp op) {
|
||||||
|
super(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VarnodeContext expression() {
|
||||||
|
// opcode should be CBRANCH
|
||||||
|
assert (op.getInputs().length < 2);
|
||||||
|
return new VarnodeContext(op.getInput(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.InstLocation;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class InstructionContext {
|
||||||
|
|
||||||
|
private Function function;
|
||||||
|
private Instruction inst;
|
||||||
|
private List<StatementContext> ops;
|
||||||
|
private InstLocation loc;
|
||||||
|
|
||||||
|
public InstructionContext(Function function, Instruction inst) {
|
||||||
|
this.function = function;
|
||||||
|
this.inst = inst;
|
||||||
|
ops = new ArrayList<>();
|
||||||
|
for (PcodeOp op : inst.getPcode()) {
|
||||||
|
StatementContext ctx = new StatementContext(inst, op);
|
||||||
|
ops.add(ctx);
|
||||||
|
}
|
||||||
|
loc = new InstLocation(function, inst.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<StatementContext> getPcodeOps() {
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatementContext getPcodeOp(int i) {
|
||||||
|
return ops.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction getInstruction() {
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstructionContext next() {
|
||||||
|
Listing listing = inst.getProgram().getListing();
|
||||||
|
Address nextAddress = inst.getAddress().add(inst.getLength());
|
||||||
|
Instruction next = listing.getInstructionAt(nextAddress);
|
||||||
|
return next == null ? null : new InstructionContext(function, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeLocation location() {
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.AddressFactory;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
|
||||||
|
public class MemLocContext extends VarnodeContext {
|
||||||
|
|
||||||
|
private AddressSpace space;
|
||||||
|
|
||||||
|
public MemLocContext(StatementContext ctx) {
|
||||||
|
super(ctx.getOp().getInput(1));
|
||||||
|
AddressFactory addressFactory = ctx.inst.getProgram().getAddressFactory();
|
||||||
|
space = addressFactory.getAddressSpace((int) ctx.getOp().getInput(0).getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return space.getName() + "@" + vn.getAddress().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.SyntheticLocation;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class PcodeContext {
|
||||||
|
|
||||||
|
protected PcodeOp op;
|
||||||
|
|
||||||
|
public PcodeContext(PcodeOp op) {
|
||||||
|
this.op = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeOp getOp() {
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeLocation location() {
|
||||||
|
if (op == null) {
|
||||||
|
return SyntheticLocation.INSTANCE;
|
||||||
|
}
|
||||||
|
return new PcodeLocation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VarnodeContext basicExpr() {
|
||||||
|
if (op.getNumInputs() == 1) {
|
||||||
|
return new VarnodeContext(op.getInput(0));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int opcode() {
|
||||||
|
return op.getOpcode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumInputs() {
|
||||||
|
return op.getNumInputs();
|
||||||
|
}
|
||||||
|
}
|
||||||
+141
@@ -0,0 +1,141 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Instruction;
|
||||||
|
import ghidra.program.model.listing.Listing;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
import ghidra.program.model.symbol.*;
|
||||||
|
|
||||||
|
public class StatementContext extends PcodeContext {
|
||||||
|
|
||||||
|
public StatementContext otherwise;
|
||||||
|
public StatementContext then;
|
||||||
|
|
||||||
|
private int opcode;
|
||||||
|
public Instruction inst;
|
||||||
|
public VarDefContext left;
|
||||||
|
public PcodeContext right;
|
||||||
|
|
||||||
|
public StatementContext(Instruction inst, PcodeOp op) {
|
||||||
|
super(op);
|
||||||
|
this.inst = inst;
|
||||||
|
if (op != null) {
|
||||||
|
this.opcode = op.getOpcode();
|
||||||
|
if (op.getOutput() != null) {
|
||||||
|
left = new VarDefContext(op, op.getOutput());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
left = new VarDefContext(op, op.getInput(2));
|
||||||
|
}
|
||||||
|
right = new PcodeContext(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VarDefContext target() {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeContext expression() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionContext condition() {
|
||||||
|
return new ConditionContext(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRet() {
|
||||||
|
return opcode == PcodeOp.RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBranch() {
|
||||||
|
return opcode == PcodeOp.BRANCH || opcode == PcodeOp.BRANCHIND || opcode == PcodeOp.CBRANCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConditional() {
|
||||||
|
return opcode == PcodeOp.CBRANCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StatementContext> branch(Listing listing, UnitContext currentUnit) {
|
||||||
|
List<StatementContext> list = new ArrayList<>();
|
||||||
|
if (opcode == PcodeOp.BRANCH || opcode == PcodeOp.CBRANCH) {
|
||||||
|
Varnode vn = op.getInput(0);
|
||||||
|
if (vn.getAddress().isConstantAddress()) {
|
||||||
|
int order = op.getSeqnum().getTime();
|
||||||
|
order += vn.getOffset();
|
||||||
|
list.add(new StatementContext(inst, inst.getPcode()[order]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Instruction next =
|
||||||
|
listing.getInstructionAt(vn.getAddress().getNewAddress(vn.getOffset()));
|
||||||
|
if (next == null || next.getPcode().length == 0) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
list.add(new StatementContext(next, next.getPcode()[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opcode == PcodeOp.BRANCHIND) {
|
||||||
|
ReferenceManager referenceManager =
|
||||||
|
currentUnit.function().getProgram().getReferenceManager();
|
||||||
|
Reference[] refs = referenceManager.getReferencesFrom(inst.getAddress());
|
||||||
|
for (Reference ref : refs) {
|
||||||
|
Address fromAddress = ref.getToAddress();
|
||||||
|
Instruction next = listing.getInstructionAt(fromAddress);
|
||||||
|
if (next == null || next.getPcode().length == 0) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
list.add(new StatementContext(next, next.getPcode()[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatementContext next(Listing listing) {
|
||||||
|
PcodeOp[] pcode = inst.getPcode();
|
||||||
|
if (op != null) {
|
||||||
|
int order = op.getSeqnum().getTime();
|
||||||
|
if (order + 1 < pcode.length) {
|
||||||
|
return new StatementContext(inst, inst.getPcode()[order + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction next = listing.getInstructionAt(inst.getAddress().add(inst.getLength()));
|
||||||
|
while (next != null && next.getPcode().length == 0) {
|
||||||
|
next = listing.getInstructionAt(next.getAddress().add(next.getLength()));
|
||||||
|
}
|
||||||
|
if (next == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new StatementContext(next, next.getPcode()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeContext ret() {
|
||||||
|
if (opcode == PcodeOp.RETURN) {
|
||||||
|
return new PcodeContext(op);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return inst.getAddress() + ": " + inst + ":" + op;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
|
||||||
|
public class SymbolVarnodeContext extends VarnodeContext {
|
||||||
|
|
||||||
|
private String context;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class is essentially a dummy context for the case where the varnode's id is a memory address
|
||||||
|
*/
|
||||||
|
public SymbolVarnodeContext(String name, Address context) {
|
||||||
|
this(context);
|
||||||
|
this.context = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymbolVarnodeContext(Address context) {
|
||||||
|
super(null);
|
||||||
|
this.context = context.toString();
|
||||||
|
size = context.getPointerSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConstant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getOffset() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class UnaryExprContext {
|
||||||
|
|
||||||
|
public PcodeOp op;
|
||||||
|
public VarnodeContext arg;
|
||||||
|
|
||||||
|
public UnaryExprContext(PcodeContext ctx) {
|
||||||
|
this.op = ctx.op;
|
||||||
|
arg = new VarnodeContext(op.getInput(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int opcode() {
|
||||||
|
return op.getOpcode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeLocation location() {
|
||||||
|
return new PcodeLocation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String mnemonic() {
|
||||||
|
return op.getMnemonic();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.PcodeFrontend;
|
||||||
|
import ghidra.lisa.pcode.locations.InstLocation;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import it.unive.lisa.program.CodeUnit;
|
||||||
|
import it.unive.lisa.program.Program;
|
||||||
|
import it.unive.lisa.program.SyntheticLocation;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class UnitContext {
|
||||||
|
|
||||||
|
private PcodeFrontend frontend;
|
||||||
|
private Function function;
|
||||||
|
private CodeUnit unit;
|
||||||
|
private Address start;
|
||||||
|
|
||||||
|
public UnitContext(PcodeFrontend frontend, Program program, Function f) {
|
||||||
|
this.frontend = frontend;
|
||||||
|
this.function = f;
|
||||||
|
unit = new CodeUnit(SyntheticLocation.INSTANCE, program, f.getName());
|
||||||
|
start = function.getEntryPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnitContext(PcodeFrontend frontend, Program program, Function f, Address entry) {
|
||||||
|
this.frontend = frontend;
|
||||||
|
this.function = f;
|
||||||
|
unit = new CodeUnit(SyntheticLocation.INSTANCE, program,
|
||||||
|
f.getName() + ":" + f.getEntryPoint());
|
||||||
|
start = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeFrontend getFrontend() {
|
||||||
|
return frontend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeUnit unit() {
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return function.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFinal() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Listing getListing() {
|
||||||
|
return function.getProgram().getListing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeLocation location() {
|
||||||
|
return new InstLocation(function, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function function() {
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstructionContext entry() {
|
||||||
|
Instruction inst = getListing().getInstructionAt(start);
|
||||||
|
if (inst == null) {
|
||||||
|
inst = getListing().getInstructionAfter(start);
|
||||||
|
}
|
||||||
|
return inst == null ? null : new InstructionContext(function, inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Address target) {
|
||||||
|
return function.getBody().contains(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
|
public class VarDefContext extends VarnodeContext {
|
||||||
|
|
||||||
|
private PcodeOp op;
|
||||||
|
|
||||||
|
public VarDefContext(PcodeOp op, Varnode vn) {
|
||||||
|
super(vn);
|
||||||
|
this.op = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConstant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PcodeOp getOp() {
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return vn.getAddress().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.contexts;
|
||||||
|
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
|
public class VarnodeContext {
|
||||||
|
|
||||||
|
protected Varnode vn;
|
||||||
|
|
||||||
|
public VarnodeContext(Varnode vn) {
|
||||||
|
this.vn = vn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConstant() {
|
||||||
|
return vn.isConstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return vn.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getOffset() {
|
||||||
|
return vn.getOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return vn.getAddress().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.expressions;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.contexts.BinaryExprContext;
|
||||||
|
import ghidra.lisa.pcode.statements.PcodeBinaryOperator;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Expression;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import it.unive.lisa.symbolic.SymbolicExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.BinaryExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.binary.*;
|
||||||
|
|
||||||
|
public class PcodeBinaryExpression extends it.unive.lisa.program.cfg.statement.BinaryExpression {
|
||||||
|
|
||||||
|
private BinaryOperator operator;
|
||||||
|
|
||||||
|
public PcodeBinaryExpression(
|
||||||
|
CFG cfg,
|
||||||
|
BinaryExprContext ctx,
|
||||||
|
Expression left,
|
||||||
|
Expression right) {
|
||||||
|
super(cfg, ctx.location(), ctx.mnemonic(),
|
||||||
|
cfg.getDescriptor().getUnit().getProgram().getTypes().getIntegerType(), left, right);
|
||||||
|
|
||||||
|
this.operator = switch (ctx.op.getOpcode()) {
|
||||||
|
case PcodeOp.BOOL_AND -> LogicalAnd.INSTANCE;
|
||||||
|
case PcodeOp.BOOL_OR -> LogicalOr.INSTANCE;
|
||||||
|
case PcodeOp.INT_EQUAL, PcodeOp.FLOAT_EQUAL -> ComparisonEq.INSTANCE;
|
||||||
|
case PcodeOp.INT_NOTEQUAL, PcodeOp.FLOAT_NOTEQUAL -> ComparisonNe.INSTANCE;
|
||||||
|
case PcodeOp.INT_LESSEQUAL, PcodeOp.FLOAT_LESSEQUAL -> ComparisonLe.INSTANCE;
|
||||||
|
case PcodeOp.INT_LESS, PcodeOp.FLOAT_LESS -> ComparisonLt.INSTANCE;
|
||||||
|
default -> new PcodeBinaryOperator(ctx.op);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int compareSameClassAndParams(
|
||||||
|
Statement o) {
|
||||||
|
return 0; // no extra fields to compare
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends AbstractState<A>> AnalysisState<A> fwdBinarySemantics(
|
||||||
|
InterproceduralAnalysis<A> interprocedural,
|
||||||
|
AnalysisState<A> state,
|
||||||
|
SymbolicExpression left,
|
||||||
|
SymbolicExpression right,
|
||||||
|
StatementStore<A> expressions)
|
||||||
|
throws SemanticException {
|
||||||
|
|
||||||
|
return state.smallStepSemantics(
|
||||||
|
new BinaryExpression(
|
||||||
|
getStaticType(),
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
operator,
|
||||||
|
getLocation()),
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.expressions;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.contexts.CallContext;
|
||||||
|
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.lang.PrototypeModel;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.ProgramContext;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||||
|
import it.unive.lisa.analysis.value.ValueDomain;
|
||||||
|
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Expression;
|
||||||
|
import it.unive.lisa.program.cfg.statement.call.UnresolvedCall;
|
||||||
|
import it.unive.lisa.symbolic.value.Variable;
|
||||||
|
|
||||||
|
public class PcodeCallExpression extends UnresolvedCall {
|
||||||
|
|
||||||
|
private Function function;
|
||||||
|
private Set<Varnode> unaffected = new HashSet<>();
|
||||||
|
private Set<Varnode> killedByCall = new HashSet<>();
|
||||||
|
|
||||||
|
public PcodeCallExpression(
|
||||||
|
CFG cfg,
|
||||||
|
CallContext ctx,
|
||||||
|
Expression expression) {
|
||||||
|
super(cfg, ctx.location(), ctx.type(), null, ctx.getCalleeName(), expression);
|
||||||
|
function = ctx.function();
|
||||||
|
if (function != null) {
|
||||||
|
PrototypeModel callingConvention = function.getCallingConvention();
|
||||||
|
if (callingConvention != null) {
|
||||||
|
Varnode[] unaffectedList = callingConvention.getUnaffectedList();
|
||||||
|
for (Varnode varnode : unaffectedList) {
|
||||||
|
unaffected.add(varnode);
|
||||||
|
}
|
||||||
|
Varnode[] killedByCallList = callingConvention.getKilledByCallList();
|
||||||
|
for (Varnode varnode : killedByCallList) {
|
||||||
|
killedByCall.add(varnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <A extends AbstractState<A>> AnalysisState<A> forwardSemantics(
|
||||||
|
AnalysisState<A> entryState,
|
||||||
|
InterproceduralAnalysis<A> interprocedural,
|
||||||
|
StatementStore<A> expressions)
|
||||||
|
throws SemanticException {
|
||||||
|
|
||||||
|
// TODO: This triggers the processing of the callee, but we effectively clears the state.
|
||||||
|
// We're using the slightly-sanitized entry state instead. Quite possible, the return
|
||||||
|
// value from the function is being dropped here - not clear how this works.
|
||||||
|
super.forwardSemantics(entryState, interprocedural, expressions);
|
||||||
|
|
||||||
|
if (unaffected.isEmpty()) {
|
||||||
|
return entryState;
|
||||||
|
}
|
||||||
|
ProgramContext programContext = function.getProgram().getProgramContext();
|
||||||
|
Address entryPoint = function.getEntryPoint();
|
||||||
|
if (entryState.getState() instanceof SimpleAbstractState sas) {
|
||||||
|
ValueDomain<?> vDomain = sas.getValueState();
|
||||||
|
if (vDomain instanceof ValueEnvironment vEnv) {
|
||||||
|
if (vEnv.function == null) {
|
||||||
|
return entryState;
|
||||||
|
}
|
||||||
|
for (Object key : vEnv.function.keySet()) {
|
||||||
|
Object val = vEnv.function.get(key);
|
||||||
|
if (key instanceof Variable var && val instanceof Lattice lat) {
|
||||||
|
if (var.getCodeLocation() instanceof PcodeLocation loc) {
|
||||||
|
Varnode output = loc.op.getOutput();
|
||||||
|
if (output != null &&
|
||||||
|
output.getAddress().getAddressSpace().isRegisterSpace()) {
|
||||||
|
if (unaffected.contains(output)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (killedByCall.contains(output)) {
|
||||||
|
vEnv.function.put(key, lat.top());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for (Register r : programContext.getRegisters()) {
|
||||||
|
// RegisterValue rv = programContext.getRegisterValue(r, entryPoint);
|
||||||
|
// if (rv != null && rv.hasAnyValue()) {
|
||||||
|
// Variable v = new Variable(Untyped.INSTANCE,
|
||||||
|
// r.getAddress().toString(), new InstLocation(entryPoint));
|
||||||
|
// if (vEnv.lattice instanceof PcodeNonRelationalValueDomain pcodeDomain) {
|
||||||
|
// PcodeNonRelationalValueDomain<?> value = pcodeDomain.getValue(rv);
|
||||||
|
// if (value != null) {
|
||||||
|
// vEnv.function.put(v, value);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (vEnv.lattice instanceof PcodeDataflowConstantPropagation pcodeDomain) {
|
||||||
|
// PcodeDataflowConstantPropagation value = pcodeDomain.getValue(v, rv);
|
||||||
|
// if (value != null) {
|
||||||
|
// vEnv.function.put(v, value);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entryState;
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.expressions;
|
||||||
|
|
||||||
|
import ghidra.lisa.pcode.contexts.UnaryExprContext;
|
||||||
|
import ghidra.lisa.pcode.statements.PcodeUnaryOperator;
|
||||||
|
import it.unive.lisa.analysis.*;
|
||||||
|
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||||
|
import it.unive.lisa.program.cfg.CFG;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Expression;
|
||||||
|
import it.unive.lisa.program.cfg.statement.Statement;
|
||||||
|
import it.unive.lisa.symbolic.SymbolicExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.UnaryExpression;
|
||||||
|
import it.unive.lisa.symbolic.value.operator.unary.UnaryOperator;
|
||||||
|
|
||||||
|
public class PcodeUnaryExpression extends it.unive.lisa.program.cfg.statement.UnaryExpression {
|
||||||
|
|
||||||
|
private UnaryOperator operator;
|
||||||
|
|
||||||
|
public PcodeUnaryExpression(
|
||||||
|
CFG cfg,
|
||||||
|
UnaryExprContext ctx,
|
||||||
|
Expression expression) {
|
||||||
|
super(cfg, ctx.location(), ctx.mnemonic(), expression);
|
||||||
|
this.operator = new PcodeUnaryOperator(ctx.op);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int compareSameClassAndParams(
|
||||||
|
Statement o) {
|
||||||
|
return 0; // no extra fields to compare
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends AbstractState<A>> AnalysisState<A> fwdUnarySemantics(
|
||||||
|
InterproceduralAnalysis<A> interprocedural,
|
||||||
|
AnalysisState<A> state,
|
||||||
|
SymbolicExpression expr,
|
||||||
|
StatementStore<A> expressions)
|
||||||
|
throws SemanticException {
|
||||||
|
|
||||||
|
return state.smallStepSemantics(
|
||||||
|
new UnaryExpression(
|
||||||
|
expr.getStaticType(),
|
||||||
|
expr,
|
||||||
|
operator,
|
||||||
|
getLocation()),
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.locations;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class InstLocation implements CodeLocation {
|
||||||
|
|
||||||
|
private Function function;
|
||||||
|
private Address addr;
|
||||||
|
|
||||||
|
public InstLocation(Function function, Address addr) {
|
||||||
|
this.function = function;
|
||||||
|
this.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(CodeLocation o) {
|
||||||
|
if (o instanceof InstLocation i) {
|
||||||
|
return addr.compareTo(i.addr);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCodeLocation() {
|
||||||
|
return addr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function function() {
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getAddress() {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.pcode.locations;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import it.unive.lisa.program.cfg.CodeLocation;
|
||||||
|
|
||||||
|
public class PcodeLocation implements CodeLocation {
|
||||||
|
|
||||||
|
public PcodeOp op;
|
||||||
|
|
||||||
|
public PcodeLocation(PcodeOp op) {
|
||||||
|
this.op = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(CodeLocation o) {
|
||||||
|
if (o instanceof PcodeLocation pl) {
|
||||||
|
return op.getSeqnum().compareTo(pl.op.getSeqnum());
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCodeLocation() {
|
||||||
|
return op.getSeqnum().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOpcode() {
|
||||||
|
return op.getOpcode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getAddress() {
|
||||||
|
return op.getSeqnum().getTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof PcodeLocation ploc) {
|
||||||
|
return this.op.getSeqnum().equals(ploc.op.getSeqnum());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return op.getSeqnum().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getCodeLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user