mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-trackng branch 'origin/patch'
This commit is contained in:
commit
01652d9ba1
4 changed files with 119 additions and 10 deletions
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
package ghidra.pcode.struct.sub;
|
package ghidra.pcode.struct.sub;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
|
@ -25,8 +26,7 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighException;
|
import ghidra.app.plugin.processors.sleigh.SleighException;
|
||||||
import ghidra.pcode.exec.PcodeUseropLibrary;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.pcode.exec.SleighPcodeUseropDefinition;
|
|
||||||
import ghidra.pcode.struct.StructuredSleigh;
|
import ghidra.pcode.struct.StructuredSleigh;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
@ -238,4 +238,16 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
|
||||||
// TODO: Test that the generated code compiles in a slaspec file.
|
// TODO: Test that the generated code compiles in a slaspec file.
|
||||||
// It's rejected for injects because "return" is not valid there.
|
// It's rejected for injects because "return" is not valid there.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmpty() throws Exception {
|
||||||
|
StructuredSleigh ss = new TestStructuredSleigh() {
|
||||||
|
@StructuredUserop
|
||||||
|
public void my_userop() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
|
||||||
|
PcodeProgram program = myUserop.programFor(null, List.of(), PcodeUseropLibrary.nil());
|
||||||
|
assertTrue(program.getCode().isEmpty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,7 +474,7 @@ public class PcodeExecutor<T> {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
branchToOffset(op, target.getOffset(), frame);
|
branchToOffset(op, target.getOffset(), frame);
|
||||||
branchToAddress(op, target);
|
branchToAddress(op, checkInjectedTarget(target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,6 +530,28 @@ public class PcodeExecutor<T> {
|
||||||
return op.getInput(0);
|
return op.getInput(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and correct the given target address, if it resides in "NO ADDRESS" space.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* At some point, we made a change to set the "target address" of compiled p-code userops to
|
||||||
|
* {@link Address#NO_ADDRESS} instead of pretending its at {@code ram:00000000}. This is
|
||||||
|
* philosophically cleaner, but leads to a practical issue in that the p-code compiler sets the
|
||||||
|
* target address of any branch to be in the same space, which for injects, will wind up in "NO
|
||||||
|
* ADDRESS." I don't know the use case for having target addresses anywhere but default space,
|
||||||
|
* so I'll maintain that behavior, but if it ever lands in "NO ADDRESS," we're going to assume
|
||||||
|
* it was an inject, and that the intended target was the default space.
|
||||||
|
*
|
||||||
|
* @param target the proposed target address
|
||||||
|
* @return the same or corrected target address
|
||||||
|
*/
|
||||||
|
protected Address checkInjectedTarget(Address target) {
|
||||||
|
if (target.getAddressSpace() != Address.NO_ADDRESS.getAddressSpace()) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
return language.getDefaultSpace().getAddress(target.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the actual logic of an indirect branch p-code op
|
* Perform the actual logic of an indirect branch p-code op
|
||||||
*
|
*
|
||||||
|
@ -548,7 +570,7 @@ public class PcodeExecutor<T> {
|
||||||
|
|
||||||
long concrete = arithmetic.toLong(offset, Purpose.BRANCH);
|
long concrete = arithmetic.toLong(offset, Purpose.BRANCH);
|
||||||
Address target = op.getSeqnum().getTarget().getNewAddress(concrete, true);
|
Address target = op.getSeqnum().getTarget().getNewAddress(concrete, true);
|
||||||
branchToAddress(op, target);
|
branchToAddress(op, checkInjectedTarget(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -576,7 +598,7 @@ public class PcodeExecutor<T> {
|
||||||
public void executeCall(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
public void executeCall(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
||||||
Address target = getBranchTarget(op);
|
Address target = getBranchTarget(op);
|
||||||
branchToOffset(op, target.getOffset(), frame);
|
branchToOffset(op, target.getOffset(), frame);
|
||||||
branchToAddress(op, target);
|
branchToAddress(op, checkInjectedTarget(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.sleigh.*;
|
import ghidra.app.plugin.processors.sleigh.*;
|
||||||
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
|
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.template.OpTpl;
|
||||||
import ghidra.pcode.utils.MessageFormattingUtils;
|
import ghidra.pcode.utils.MessageFormattingUtils;
|
||||||
import ghidra.pcodeCPort.pcoderaw.VarnodeData;
|
import ghidra.pcodeCPort.pcoderaw.VarnodeData;
|
||||||
import ghidra.pcodeCPort.sleighbase.SleighBase;
|
import ghidra.pcodeCPort.sleighbase.SleighBase;
|
||||||
|
@ -149,6 +150,9 @@ public enum SleighProgramCompiler {
|
||||||
*/
|
*/
|
||||||
public static ConstructTpl compileTemplate(Language language, PcodeParser parser,
|
public static ConstructTpl compileTemplate(Language language, PcodeParser parser,
|
||||||
String sourceName, String source) {
|
String sourceName, String source) {
|
||||||
|
if (source.isBlank()) {
|
||||||
|
return new ConstructTpl(new OpTpl[] {});
|
||||||
|
}
|
||||||
return parser.compilePcode(source, sourceName, 1);
|
return parser.compilePcode(source, sourceName, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +289,8 @@ public enum SleighProgramCompiler {
|
||||||
* evaluator p-code program uses its own library as a means of capturing the result; however,
|
* evaluator p-code program uses its own library as a means of capturing the result; however,
|
||||||
* userop libraries are easily composed. It should be easy to add that feature if needed.
|
* userop libraries are easily composed. It should be easy to add that feature if needed.
|
||||||
*
|
*
|
||||||
* @param language the languge of the target p-code machine
|
* @param parser a parser for the given language
|
||||||
|
* @param language the language of the target p-code machine
|
||||||
* @param expression the Sleigh expression to be evaluated
|
* @param expression the Sleigh expression to be evaluated
|
||||||
* @return a p-code program whose {@link PcodeExpression#evaluate(PcodeExecutor)} method will
|
* @return a p-code program whose {@link PcodeExpression#evaluate(PcodeExecutor)} method will
|
||||||
* evaluate the expression on the given executor and its state.
|
* evaluate the expression on the given executor and its state.
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.pcode.emu;
|
package ghidra.pcode.emu;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -359,4 +358,75 @@ public abstract class AbstractPcodeEmulatorTest extends AbstractGTest {
|
||||||
assertEquals(1,
|
assertEquals(1,
|
||||||
arithmetic.toLong(thread.getState().getVar(r1, Reason.INSPECT), Purpose.INSPECT));
|
arithmetic.toLong(thread.getState().getVar(r1, Reason.INSPECT), Purpose.INSPECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInjectedBranch() throws Exception {
|
||||||
|
PcodeEmulator emu = createEmulator(getLanguage(LANGID_TOY_BE));
|
||||||
|
PcodeArithmetic<byte[]> arithmetic = emu.getArithmetic();
|
||||||
|
AddressSpace space = emu.getLanguage().getDefaultSpace();
|
||||||
|
AssemblyBuffer asm = new AssemblyBuffer(Assemblers.getAssembler(emu.getLanguage()),
|
||||||
|
space.getAddress(0x00400000));
|
||||||
|
|
||||||
|
asm.assemble("imm r1, #1");
|
||||||
|
Address inject = asm.getNext();
|
||||||
|
asm.assemble("add r1, #1");
|
||||||
|
Address target = asm.getNext();
|
||||||
|
|
||||||
|
byte[] bytes = asm.getBytes();
|
||||||
|
emu.getSharedState().setVar(asm.getEntry(), bytes.length, false, bytes);
|
||||||
|
PcodeThread<byte[]> thread = emu.newThread();
|
||||||
|
thread.overrideCounter(asm.getEntry());
|
||||||
|
|
||||||
|
emu.inject(inject, "goto 0x%08x;".formatted(target.getOffset()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.run();
|
||||||
|
fail("Should have crashed on decode error");
|
||||||
|
}
|
||||||
|
catch (DecodePcodeExecutionException e) {
|
||||||
|
// Space assertion is subsumed by counter assertion
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r1 = emu.getLanguage().getRegister("r1");
|
||||||
|
assertEquals(1,
|
||||||
|
arithmetic.toLong(thread.getState().getVar(r1, Reason.INSPECT), Purpose.INSPECT));
|
||||||
|
assertEquals(target, thread.getCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInjectedIndirectBranch() throws Exception {
|
||||||
|
PcodeEmulator emu = createEmulator(getLanguage(LANGID_TOY_BE));
|
||||||
|
PcodeArithmetic<byte[]> arithmetic = emu.getArithmetic();
|
||||||
|
AddressSpace space = emu.getLanguage().getDefaultSpace();
|
||||||
|
AssemblyBuffer asm = new AssemblyBuffer(Assemblers.getAssembler(emu.getLanguage()),
|
||||||
|
space.getAddress(0x00400000));
|
||||||
|
|
||||||
|
asm.assemble("imm r1, #1");
|
||||||
|
Address inject = asm.getNext();
|
||||||
|
asm.assemble("add r1, #1");
|
||||||
|
Address target = asm.getNext();
|
||||||
|
|
||||||
|
byte[] bytes = asm.getBytes();
|
||||||
|
emu.getSharedState().setVar(asm.getEntry(), bytes.length, false, bytes);
|
||||||
|
PcodeThread<byte[]> thread = emu.newThread();
|
||||||
|
thread.overrideCounter(asm.getEntry());
|
||||||
|
|
||||||
|
emu.inject(inject, """
|
||||||
|
r2 = 0x%08x;
|
||||||
|
goto [r2];
|
||||||
|
""".formatted(target.getOffset()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.run();
|
||||||
|
fail("Should have crashed on decode error");
|
||||||
|
}
|
||||||
|
catch (DecodePcodeExecutionException e) {
|
||||||
|
// Space assertion is subsumed by counter assertion
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r1 = emu.getLanguage().getRegister("r1");
|
||||||
|
assertEquals(1,
|
||||||
|
arithmetic.toLong(thread.getState().getVar(r1, Reason.INSPECT), Purpose.INSPECT));
|
||||||
|
assertEquals(target, thread.getCounter());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue