mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-1208: Implement linux-x86/-amd64 system call simulators
This commit is contained in:
parent
4b600847eb
commit
b33800ecba
144 changed files with 12712 additions and 804 deletions
|
@ -0,0 +1,179 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
//An example emulation script that uses a stand-alone emulator.
|
||||
//It provides the set-up code and then demonstrates some use cases.
|
||||
//@author
|
||||
//@category Emulation
|
||||
//@keybinding
|
||||
//@menupath
|
||||
//@toolbar
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.pcode.emu.PcodeEmulator;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
public class StandAloneEmuExampleScript extends GhidraScript {
|
||||
private final static Charset UTF8 = Charset.forName("utf8");
|
||||
private SleighLanguage language;
|
||||
private PcodeEmulator emulator;
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
/*
|
||||
* Create an emulator and start a thread
|
||||
*/
|
||||
language = (SleighLanguage) getLanguage(new LanguageID("x86:LE:64:default"));
|
||||
emulator = new PcodeEmulator(language) {
|
||||
@Override
|
||||
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
|
||||
return new DemoPcodeUseropLibrary(language, StandAloneEmuExampleScript.this);
|
||||
}
|
||||
|
||||
// Uncomment this to see instructions printed as they are decoded
|
||||
/*
|
||||
protected BytesPcodeThread createThread(String name) {
|
||||
return new BytesPcodeThread(name, this) {
|
||||
@Override
|
||||
protected SleighInstructionDecoder createInstructionDecoder(
|
||||
PcodeExecutorState<byte[]> sharedState) {
|
||||
return new SleighInstructionDecoder(language, sharedState) {
|
||||
@Override
|
||||
public Instruction decodeInstruction(Address address,
|
||||
RegisterValue context) {
|
||||
Instruction instruction = super.decodeInstruction(address, context);
|
||||
println("Decoded " + address + ": " + instruction);
|
||||
return instruction;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
*/
|
||||
};
|
||||
PcodeThread<byte[]> thread = emulator.newThread();
|
||||
// The emulator composes the full library for each thread
|
||||
PcodeUseropLibrary<byte[]> library = thread.getUseropLibrary();
|
||||
AddressSpace dyn = language.getDefaultSpace();
|
||||
|
||||
/*
|
||||
* Assemble a little test program and write it into the emulator
|
||||
*
|
||||
* We're not really going to implement system calls here. We're just using it to demonstrate
|
||||
* the implementation of a language-defined userop.
|
||||
*/
|
||||
Address entry = dyn.getAddress(0x00400000);
|
||||
Assembler asm = Assemblers.getAssembler(language);
|
||||
CodeBuffer buffer = new CodeBuffer(asm, entry);
|
||||
buffer.assemble("MOV RCX, 0xdeadbeef");
|
||||
Address injectHere = buffer.getNext();
|
||||
buffer.assemble("MOV RAX, 1");
|
||||
buffer.assemble("SYSCALL");
|
||||
buffer.assemble("MOV RAX, 2"); // Induce the interrupt we need to terminate
|
||||
buffer.assemble("SYSCALL");
|
||||
byte[] code = buffer.getBytes();
|
||||
emulator.getSharedState().setVar(dyn, entry.getOffset(), code.length, true, code);
|
||||
|
||||
/*
|
||||
* Initialize other parts of the emulator and thread state. Note the use of the L suffix on
|
||||
* 0xdeadbeefL, because Java with sign extend the (negative) int to a long otherwise.
|
||||
*/
|
||||
byte[] hw = "Hello, World!\n".getBytes(UTF8);
|
||||
emulator.getSharedState().setVar(dyn, 0xdeadbeefL, hw.length, true, hw);
|
||||
PcodeProgram init = SleighProgramCompiler.compileProgram(language, "init", List.of(
|
||||
"RIP = 0x" + entry + ";",
|
||||
"RSP = 0x00001000;"),
|
||||
library);
|
||||
thread.getExecutor().execute(init, library);
|
||||
thread.overrideContextWithDefault();
|
||||
thread.reInitialize();
|
||||
|
||||
/*
|
||||
* Inject a call to our custom print userop. Otherwise, the language itself will never
|
||||
* invoke it.
|
||||
*/
|
||||
emulator.inject(injectHere, List.of(
|
||||
"print_utf8(RCX);",
|
||||
"emu_exec_decoded();"));
|
||||
|
||||
/*
|
||||
* Run the experiment: This should interrupt on the second SYSCALL, because any value other
|
||||
* than 1 calls emu_swi.
|
||||
*/
|
||||
try {
|
||||
thread.stepInstruction(10);
|
||||
printerr("We should not have completed 10 steps!");
|
||||
}
|
||||
catch (InterruptPcodeExecutionException e) {
|
||||
println("Terminated via interrupt. Good.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Inspect the machine. You can always do this by accessing the state directly, but for
|
||||
* anything other than simple variables, you may find compiling an expression more
|
||||
* convenient.
|
||||
*/
|
||||
println("RCX = " +
|
||||
Utils.bytesToLong(thread.getState().getVar(language.getRegister("RCX")), 8,
|
||||
language.isBigEndian()));
|
||||
|
||||
println("RCX = " + Utils.bytesToLong(
|
||||
SleighProgramCompiler.compileExpression(language, "RCX").evaluate(thread.getExecutor()),
|
||||
8, language.isBigEndian()));
|
||||
|
||||
println("RCX+4 = " +
|
||||
Utils.bytesToLong(SleighProgramCompiler.compileExpression(language, "RCX+4")
|
||||
.evaluate(thread.getExecutor()),
|
||||
8, language.isBigEndian()));
|
||||
}
|
||||
|
||||
public static class CodeBuffer {
|
||||
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
private final Assembler asm;
|
||||
private final Address entry;
|
||||
|
||||
public CodeBuffer(Assembler asm, Address entry) {
|
||||
this.asm = asm;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public Address getNext() {
|
||||
return entry.add(baos.size());
|
||||
}
|
||||
|
||||
public byte[] assemble(String line)
|
||||
throws AssemblySyntaxException, AssemblySemanticException, IOException {
|
||||
byte[] bytes = asm.assembleLine(getNext(), line);
|
||||
baos.write(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue