/* ### * 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. */ import java.math.BigInteger; import java.util.*; import java.util.Map.Entry; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.script.GhidraScript; import ghidra.debug.api.emulation.EmulatorFactory; import ghidra.debug.api.emulation.PcodeDebuggerAccess; import ghidra.lifecycle.Unfinished; import ghidra.pcode.emu.*; import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor; import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory; import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeArithmetic.Purpose; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.trace.TraceEmulationIntegration.AbstractSimplePropertyBasedPieceHandler; import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer; import ghidra.pcode.struct.StructuredSleigh; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.*; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; public class ModelingScript extends GhidraScript { // ---------------------- public static class JavaStdLibPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { private final AddressSpace space; private final Register regRSP; private final Register regRAX; private final Register regRDI; private final Register regRSI; public JavaStdLibPcodeUseropLibrary(SleighLanguage language) { space = language.getDefaultSpace(); regRSP = language.getRegister("RSP"); regRAX = language.getRegister("RAX"); regRDI = language.getRegister("RDI"); regRSI = language.getRegister("RSI"); } @PcodeUserop public void __x86_64_RET( @OpExecutor PcodeExecutor executor, @OpState PcodeExecutorState state) { PcodeArithmetic arithmetic = state.getArithmetic(); T tRSP = state.getVar(regRSP, Reason.EXECUTE_READ); long lRSP = arithmetic.toLong(tRSP, Purpose.OTHER); T tReturn = state.getVar(space, lRSP, 8, true, Reason.EXECUTE_READ); long lReturn = arithmetic.toLong(tReturn, Purpose.BRANCH); state.setVar(regRSP, arithmetic.fromConst(lRSP + 8, 8)); ((PcodeThreadExecutor) executor).getThread() .overrideCounter(space.getAddress(lReturn)); } @PcodeUserop public void __libc_strlen(@OpState PcodeExecutorState state) { PcodeArithmetic arithmetic = state.getArithmetic(); T tStr = state.getVar(regRDI, Reason.EXECUTE_READ); long lStr = arithmetic.toLong(tStr, Purpose.OTHER); T tMaxlen = state.getVar(regRSI, Reason.EXECUTE_READ); long lMaxlen = arithmetic.toLong(tMaxlen, Purpose.OTHER); for (int i = 0; i < lMaxlen; i++) { T tChar = state.getVar(space, lStr + i, 1, false, Reason.EXECUTE_READ); if (arithmetic.toLong(tChar, Purpose.OTHER) == 0) { state.setVar(regRAX, arithmetic.fromConst(Integer.toUnsignedLong(i), 8)); break; } } } } // ---------------------- public static class SleighStdLibPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { private static final String SRC_RET = """ RIP = *:8 RSP; RSP = RSP + 8; return [RIP]; """; private static final String SRC_STRLEN = """ __result = 0; if (*:1 (str+__result) == 0 || __result >= maxlen) goto ; __result = __result + 1; goto ; """; private final Register regRAX; private final Register regRDI; private final Register regRSI; private final Varnode vnRAX; private final Varnode vnRDI; private final Varnode vnRSI; private PcodeProgram progRet; private PcodeProgram progStrlen; public SleighStdLibPcodeUseropLibrary(SleighLanguage language) { regRAX = language.getRegister("RAX"); regRDI = language.getRegister("RDI"); regRSI = language.getRegister("RSI"); vnRAX = new Varnode(regRAX.getAddress(), regRAX.getMinimumByteSize()); vnRDI = new Varnode(regRDI.getAddress(), regRDI.getMinimumByteSize()); vnRSI = new Varnode(regRSI.getAddress(), regRSI.getMinimumByteSize()); } @PcodeUserop public void __x86_64_RET(@OpExecutor PcodeExecutor executor, @OpLibrary PcodeUseropLibrary library) { if (progRet == null) { progRet = SleighProgramCompiler.compileUserop(executor.getLanguage(), "__x86_64_RET", List.of(), SRC_RET, PcodeUseropLibrary.nil(), List.of()); } progRet.execute(executor, library); } @PcodeUserop public void __libc_strlen(@OpExecutor PcodeExecutor executor, @OpLibrary PcodeUseropLibrary library) { if (progStrlen == null) { progStrlen = SleighProgramCompiler.compileUserop(executor.getLanguage(), "__libc_strlen", List.of("__result", "str", "maxlen"), SRC_STRLEN, PcodeUseropLibrary.nil(), List.of(vnRAX, vnRDI, vnRSI)); } progStrlen.execute(executor, library); } } // ---------------------- public static class StructuredStdLibPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { public StructuredStdLibPcodeUseropLibrary(CompilerSpec cs) { new MyStructuredPart(cs).generate(ops); } public static class MyStructuredPart extends StructuredSleigh { protected MyStructuredPart(CompilerSpec cs) { super(cs); } @StructuredUserop public void __x86_64_RET() { Var RSP = lang("RSP", type("void **")); Var RIP = lang("RIP", type("void *")); RIP.set(RSP.deref()); RSP.addiTo(8); _return(RIP); } @StructuredUserop public void __libc_strlen() { Var result = lang("RAX", type("long")); Var str = lang("RDI", type("char *")); Var maxlen = lang("RSI", type("long")); _for(result.set(0), result.ltiu(maxlen).andb(str.index(result).deref().eq(0)), result.inc(), () -> { }); } } } // ---------------------- interface Expr { int size(); } interface UnExpr extends Expr { Expr u(); } interface BinExpr extends Expr { Expr l(); Expr r(); } record LitExpr(BigInteger val, int size) implements Expr {} record VarExpr(Varnode vn) implements Expr { public VarExpr(AddressSpace space, long offset, int size) { this(space.getAddress(offset), size); } public VarExpr(Address address, int size) { this(new Varnode(address, size)); } @Override public int size() { return vn.getSize(); } } record InvExpr(Expr u, int size) implements UnExpr {} record AddExpr(Expr l, Expr r, int size) implements BinExpr {} record SubExpr(Expr l, Expr r, int size) implements BinExpr {} // ---------------------- public enum ExprPcodeArithmetic implements PcodeArithmetic { BE(Endian.BIG), LE(Endian.LITTLE); public static ExprPcodeArithmetic forEndian(Endian endian) { return endian.isBigEndian() ? BE : LE; } public static ExprPcodeArithmetic forLanguage(Language language) { return language.isBigEndian() ? BE : LE; } private final Endian endian; private ExprPcodeArithmetic(Endian endian) { this.endian = endian; } @Override public Class getDomain() { return Expr.class; } @Override public Endian getEndian() { return endian; } @Override public Expr unaryOp(int opcode, int sizeout, int sizein1, Expr in1) { return switch (opcode) { case PcodeOp.INT_NEGATE -> new InvExpr(in1, sizeout); default -> throw new UnsupportedOperationException(PcodeOp.getMnemonic(opcode)); }; } @Override public Expr binaryOp(int opcode, int sizeout, int sizein1, Expr in1, int sizein2, Expr in2) { return switch (opcode) { case PcodeOp.INT_ADD -> new AddExpr(in1, in2, sizeout); case PcodeOp.INT_SUB -> new SubExpr(in1, in2, sizeout); default -> throw new UnsupportedOperationException(PcodeOp.getMnemonic(opcode)); }; } @Override public Expr modBeforeStore(int sizeinOffset, AddressSpace space, Expr inOffset, int sizeinValue, Expr inValue) { return inValue; } @Override public Expr modAfterLoad(int sizeinOffset, AddressSpace space, Expr inOffset, int sizeinValue, Expr inValue) { return inValue; } @Override public Expr fromConst(byte[] value) { if (endian.isBigEndian()) { return new LitExpr(new BigInteger(1, value), value.length); } byte[] reversed = Arrays.copyOf(value, value.length); ArrayUtils.reverse(reversed); return new LitExpr(new BigInteger(1, reversed), reversed.length); } @Override public Expr fromConst(BigInteger value, int size, boolean isContextreg) { return new LitExpr(value, size); } @Override public Expr fromConst(long value, int size) { return fromConst(BigInteger.valueOf(value), size); } @Override public byte[] toConcrete(Expr value, Purpose purpose) { throw new UnsupportedOperationException(); } @Override public long sizeOf(Expr value) { return value.size(); } } // ---------------------- public static class ExprSpace { protected final NavigableMap map = new TreeMap<>(Long::compareUnsigned); protected final ExprPcodeExecutorStatePiece piece; protected final AddressSpace space; protected ExprSpace(AddressSpace space, ExprPcodeExecutorStatePiece piece) { this.space = space; this.piece = piece; } public void clear() { map.clear(); } public void set(long offset, int size, Expr val, PcodeStateCallbacks cb) { // TODO: Handle overlaps / offcut gets and sets map.put(offset, val); cb.dataWritten(piece, space.getAddress(offset), size, val); } public Expr get(long offset, int size, PcodeStateCallbacks cb) { // TODO: Handle overlaps / offcut gets and sets Expr expr = map.get(offset); if (expr == null) { byte[] aOffset = piece.getAddressArithmetic().fromConst(offset, space.getPointerSize()); if (cb.readUninitialized(piece, space, aOffset, size) != 0) { return map.get(offset); } } return null; } public Entry getNextEntry(long offset) { return map.ceilingEntry(offset); } } public static class ExprPcodeExecutorStatePiece extends AbstractLongOffsetPcodeExecutorStatePiece { protected final Map spaceMap = new HashMap<>(); public ExprPcodeExecutorStatePiece(Language language, PcodeStateCallbacks cb) { super(language, BytesPcodeArithmetic.forLanguage(language), ExprPcodeArithmetic.forLanguage(language), cb); } @Override public MemBuffer getConcreteBuffer(Address address, Purpose purpose) { throw new UnsupportedOperationException(); } @Override public void clear() { for (ExprSpace space : spaceMap.values()) { space.clear(); } } @Override protected ExprSpace getForSpace(AddressSpace space, boolean toWrite) { if (toWrite) { return spaceMap.computeIfAbsent(space, s -> new ExprSpace(s, this)); } return spaceMap.get(space); } @Override public Entry getNextEntryInternal(AddressSpace space, long offset) { ExprSpace s = getForSpace(space, false); if (s == null) { return null; } return s.getNextEntry(offset); } @Override protected void setInSpace(ExprSpace space, long offset, int size, Expr val, PcodeStateCallbacks cb) { space.set(offset, size, val, cb); } @Override protected Expr getFromSpace(ExprSpace space, long offset, int size, Reason reason, PcodeStateCallbacks cb) { return space.get(offset, size, cb); } @Override protected Map getRegisterValuesFromSpace(ExprSpace s, List registers) { throw new UnsupportedOperationException(); } } public static class BytesExprPcodeExecutorState extends PairedPcodeExecutorState { public BytesExprPcodeExecutorState(PcodeExecutorStatePiece concrete, PcodeStateCallbacks cb) { super(new PairedPcodeExecutorStatePiece<>(concrete, new ExprPcodeExecutorStatePiece(concrete.getLanguage(), cb))); } } // ---------------------- public enum BytesExprEmulatorPartsFactory implements AuxEmulatorPartsFactory { INSTANCE; @Override public PcodeArithmetic getArithmetic(Language language) { return ExprPcodeArithmetic.forLanguage(language); } @Override public PcodeUseropLibrary> createSharedUseropLibrary( AuxPcodeEmulator emulator) { return PcodeUseropLibrary.nil(); } @Override public PcodeUseropLibrary> createLocalUseropStub( AuxPcodeEmulator emulator) { return PcodeUseropLibrary.nil(); } @Override public PcodeUseropLibrary> createLocalUseropLibrary( AuxPcodeEmulator emulator, PcodeThread> thread) { return PcodeUseropLibrary.nil(); } @Override public PcodeExecutorState> createSharedState( AuxPcodeEmulator emulator, BytesPcodeExecutorStatePiece concrete, PcodeStateCallbacks cb) { return new BytesExprPcodeExecutorState(concrete, cb); } @Override public PcodeExecutorState> createLocalState( AuxPcodeEmulator emulator, PcodeThread> thread, BytesPcodeExecutorStatePiece concrete, PcodeStateCallbacks cb) { return new BytesExprPcodeExecutorState(concrete, cb); } } public static class BytesExprPcodeEmulator extends AuxPcodeEmulator { public BytesExprPcodeEmulator(Language language, PcodeEmulationCallbacks> cb) { super(language, cb); } public BytesExprPcodeEmulator(Language language) { this(language, PcodeEmulationCallbacks.none()); } @Override protected AuxEmulatorPartsFactory getPartsFactory() { return BytesExprEmulatorPartsFactory.INSTANCE; } } // ---------------------- public static class ExprPieceHandler extends AbstractSimplePropertyBasedPieceHandler { @Override public Class getAddressDomain() { return byte[].class; } @Override public Class getValueDomain() { return Expr.class; } @Override protected String getPropertyName() { return "Expr"; } @Override protected Class getPropertyType() { return String.class; } @Override protected Expr decode(String propertyValue) { return Unfinished.TODO("Left as an exercise"); } @Override protected String encode(Expr value) { return Unfinished.TODO("Left as an exercise"); } } public static class BytesExprEmulatorFactory implements EmulatorFactory { @Override public String getTitle() { return "Expr"; } @Override public PcodeMachine create(PcodeDebuggerAccess access, Writer writer) { writer.putHandler(new ExprPieceHandler()); return new BytesExprPcodeEmulator(access.getLanguage(), writer.callbacks()); } } // ---------------------- @Override protected void run() throws Exception { BytesExprPcodeEmulator emu = new BytesExprPcodeEmulator(currentProgram.getLanguage()); // TODO: Initialize the machine PcodeExecutorState> state = emu.getSharedState(); state.setVar(currentAddress, 4, true, Pair.of(new byte[] { 1, 2, 3, 4 }, new VarExpr(currentAddress, 4))); PcodeThread> thread = emu.newThread(); // TODO: Initialize the thread while (true) { monitor.checkCancelled(); thread.stepInstruction(100); } } }