GP-1681: Adds basic Frida functionality to Ghidra in the form of a

debugger model.
This commit is contained in:
d-millar 2022-03-02 11:16:06 -05:00 committed by Ryan Kurtz
parent 65a2cc42dc
commit aa5bbe93bd
282 changed files with 21013 additions and 1260 deletions

View File

@ -1,125 +0,0 @@
/* ###
* 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 agent.dbgeng.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import agent.dbgeng.gadp.DbgEngGadpServer;
import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.gadp.client.GadpClient;
import ghidra.dbg.gadp.client.GadpClientTestHelper;
import ghidra.dbg.gadp.protocol.Gadp;
import ghidra.util.Msg;
public class GadpForDbgTest extends AbstractModelForDbgTest {
static class DbgGadpModelHost implements ModelHost {
final DbgEngGadpServer server;
final SocketAddress addr;
final AsynchronousSocketChannel socket;
final GadpClient client;
DbgGadpModelHost() throws Exception {
server = DbgEngGadpServer.newInstance(new InetSocketAddress("localhost", 0));
server.startDbgEng(new String[] {})
.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
addr = server.getLocalAddress();
socket = AsynchronousSocketChannel.open();
client = new GadpClient("Test", socket);
}
@Override
public CompletableFuture<Void> init() {
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
Msg.debug(this, "Connecting...");
AsyncUtils.completable(TypeSpec.VOID, socket::connect, addr).handle(seq::next);
}).then(seq -> {
Msg.debug(this, "Negotiating...");
client.connect().handle(seq::next);
}).finish();
}
@Override
public DebuggerObjectModel getModel() {
return client;
}
@Override
public void close() throws Exception {
// Not too eww
Msg.debug(this, "Disconnecting...");
try {
waitOn(client.close());
}
catch (Exception e) {
throw e;
}
catch (Throwable e) {
throw new AssertionError(e);
}
server.terminate();
}
}
@Override
protected DbgGadpModelHost modelHost() throws Exception {
return new DbgGadpModelHost();
}
@Test
public void testBadRequest() throws Throwable {
try (DbgGadpModelHost m = modelHost()) {
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
m.init().handle(seq::next);
}).then(seq -> {
Msg.debug(this, "Sending bogus message...");
GadpClientTestHelper.sendChecked(m.client, Gadp.ErrorRequest.newBuilder(), null)
.handle(seq::nextIgnore);
}).finish());
fail("Exception expected");
}
catch (AssertionError e) {
assertEquals(
"Client implementation sent an invalid request: " +
"BAD_REQUEST: Unrecognized request: ERROR_REQUEST",
e.getMessage());
}
}
@Test
public void testPing() throws Throwable {
try (DbgGadpModelHost m = modelHost()) {
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
m.init().handle(seq::next);
}).then(seq -> {
Msg.debug(this, "Pinging...");
m.client.ping("Hello, Ghidra Async Debugging!").handle(seq::next);
}).finish());
}
}
}

View File

@ -1,54 +0,0 @@
/* ###
* 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 agent.dbgeng.model;
import java.util.concurrent.CompletableFuture;
import agent.dbgeng.model.impl.DbgModelImpl;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.util.Msg;
public class ModelForDbgTest extends AbstractModelForDbgTest {
static class DbgGadpModelHost implements ModelHost {
final DbgModelImpl model;
DbgGadpModelHost() {
model = new DbgModelImpl();
}
@Override
public CompletableFuture<Void> init() {
Msg.debug(this, "Starting dbgeng...");
return model.startDbgEng(new String[] {});
}
@Override
public DebuggerObjectModel getModel() {
return model;
}
@Override
public void close() throws Exception {
model.terminate();
}
}
@Override
protected ModelHost modelHost() throws Exception {
return new DbgGadpModelHost();
}
}

View File

@ -0,0 +1,27 @@
Random Notes on the Implementation of Debugger-agent-frida
- Building libfrida-core.so:
You can download libfrida-core.a for Frida by grabbing the latest frida-core-devkit for your OS from https://github.com/frida/frida/releases or by downloading the Frida source and running:
python3 devkit.py frida-core linux-x86_64 DEVKIT
from the "releng" directory. Ghidra needs a dynamically-loadable version of this which you can generate by something like:
ar -x libfrida-core.a
rm meson-generated_.._.._.._gum_gumenumtypes.c.o
g++ -shared -o libfrida-core.so *.o -ldl -lm -latomic -lrt -lpthread -lresolv -pthread -fuse-ld=gold -Wl,--export-dynamic,--icf=all,--gc-sections,-z,noexecstack -static-libgcc -ffunction-sections -fPIC -fdata-sections -m64 -Os -pipe -g3 -lstdc++
Libfrida-core.so should then be added to the jna.library.path or put someplace like /usr/lib/x86_64-linux-gnu, where it will get picked up by Native.load().
- Frida Functionality
The most interesting bits of Frida are available as "methods" from the Objects Tree. For instance, if you select a function and hit "M", you will get a dialog with available methods. Selecting, for example, "intercept" will bring up a second dialog with the relevant parameters. For many of these, you will want to provide your own Javascript "on" functions, e.g. onEnter for the Interceptor. Stalking is available on Threads and the individual thread entries. Scan, protect, and watch functions are available on Memory. You can also redirect the output to GhidraScript, although this relies on a bit of a hack. If your Javascript "Name" parameter is something like "interpreter", prepend "interpreter<=" to the output from your Javascript, and the results will be passed to both the console and the script.
- State in Frida:
Commands in Frida are, generally speaking, not state-dependent, i.e. they do not depend on whether the target is running or not, only on whether the frida-agent thread is running. Many of the gum-based commands do, however, depend on ptrace. If you have a ptrace-based debugger attached to the target, they will time out. You can attach a debugger after Frida, but you will have to detach it to regain the gum-based functionality. "Detach" in most debuggers includes "resume", so it is difficult to get state other than the "initial" state from the frida-agent injection point. It would be nice if "disconnect" worked, but "disconnect" (i.e. detach without resuming) also leaves Frida in a partially disabled state.
- Errors in Frida
The cloaking logic in Frida, e.g. in gum_cloak_add_thread and gum_cloak_index_of_thread, is broken as of the writing of this note. Gum_cloak_add_thread is called for every thread, and gum_cloak_index_of_thread returns a non-negative result for every call but the first. As a result, every thread but one is cloaked, and enumerateThreads returns only a single thread. This is documented in Issue #625 for the frida-gum project. A quick fix is to comment out the cloaking call in frida-gum/gum/gumprocess.c::gum_emit_thread_if_not_cloaked. Obviously, this may have other undesirable effects, but...
The logic in the ordering of exception handlers also appears to be broken (Issue #627). New handlers are appended to the queue, in most cases after gum_exceptor_handle_scope_exception and gum_quick_core_handle_crashed_js. Gum_exceptor_handle_scope_exception almost always returns TRUE, breaking out of the queue and causing any remaining handlers to be ignored. This means any handler added with Process.setExceptionHandler is likely to be ignored. A quick fix is to modify gum_exceptor_add to use g_slist_prepend instead ot g_slist_append.
Not really an error, but worth noting: building libfrida-core.so from the source may result in a library with glib2.0 dependencies that are incompatible with the current version of Eclipse. The not-so-simple solution is to build Eclipse on the machine that you used to build libfrida-core.

View File

@ -0,0 +1,107 @@
/* ###
* 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/javaProject.gradle"
apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
apply plugin: 'eclipse'
eclipse.project.name = 'Debug Debugger-agent-frida'
dependencies {
api project(':Framework-AsyncComm')
api project(':Framework-Debugging')
api project(':Debugger')
api project(':Debugger-gadp')
testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts')
testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts')
testImplementation project(path: ':Debugger-gadp', configuration: 'testArtifacts')
}
def boolean filterJar(File jarfile) {
if (jarfile.name.contains("gradle-api")) {
return false
} else if (jarfile.name.contains("groovy-all")) {
return false
} else if (jarfile.name.contains("gradle-installation-beacon")) {
return false
}
return true
}
jar {
manifest {
attributes['Main-Class'] = 'agent.lldb.gadp.FridaGadpServer'
}
}
task configureNodepJar {
doLast {
configurations.default.files.forEach {
if (filterJar(it)) {
nodepJar.from(zipTree(it))
}
}
}
}
task nodepJar(type: Jar) {
inputs.file(file(jar.archivePath))
dependsOn(configureNodepJar)
dependsOn(jar)
archiveAppendix = 'nodep'
manifest {
attributes['Main-Class'] = 'agent.lldb.gadp.FridaGadpServer'
}
from(zipTree(jar.archivePath))
}
task executableJar {
ext.execsh = file("src/main/sh/execjar.sh")
ext.jarfile = file(nodepJar.archivePath)
ext.outjar = file("${buildDir}/bin/gadp-agent-frida")
dependsOn(nodepJar)
inputs.file(execsh)
inputs.file(jarfile)
outputs.file(outjar)
doLast {
outjar.parentFile.mkdirs()
outjar.withOutputStream { output ->
execsh.withInputStream { input ->
output << input
}
jarfile.withInputStream { input ->
output << input
}
}
exec {
commandLine("chmod", "+x", outjar)
}
}
}
test {
if ("linux_x86_64".equals(getCurrentPlatformName())) {
dependsOn(":Framework-Debugging:testSpecimenLinux_x86_64")
}
if ("mac_x86_64".equals(getCurrentPlatformName())) {
dependsOn(":Framework-Debugging:testSpecimenMac_x86_64")
}
}

View File

@ -0,0 +1,14 @@
##VERSION: 2.0
##MODULE IP: Apache License 2.0
##MODULE IP: Apache License 2.0 with LLVM Exceptions
.classpath||NONE||reviewed||END|
.project||NONE||reviewed||END|
FridaNotes.txt||GHIDRA||||END|
Module.manifest||GHIDRA||||END|
build.gradle||GHIDRA||||END|
data/scripts/onAccess.js||GHIDRA||||END|
data/scripts/onAccessExt.js||GHIDRA||||END|
data/scripts/onCallSummary.js||GHIDRA||||END|
data/scripts/onEnter.js||GHIDRA||||END|
data/scripts/onLeave.js||GHIDRA||||END|
data/scripts/onReceive.js||GHIDRA||||END|

View File

@ -0,0 +1,5 @@
onAccess: function(details) {
console.log(details.from + " " + details.operation + " " + details.address);
console.log("pages: completed=" + details.pagesCompleted + " total=" + details.pageTotal);
}

View File

@ -0,0 +1,18 @@
function monitorMemory(base, length, interceptedInstructions = new Set()) {
const baseAddress = ptr(base.toString());
MemoryAccessMonitor.enable({ base: baseAddress, size: length }, {
onAccess: function(details) {
console.log(details.from + " " + details.operation + " " + details.address);
let instruction = Instruction.parse(details.from);
const nextInstr = ptr(instruction.next.toString());
if (interceptedInstructions.has(nextInstr.toString())) {
return;
}
interceptedInstructions.add(nextInstr.toString());
Interceptor.attach(nextInstr, function(_) {
monitorMemory(baseAddress, length, inteceptedInstructions);
});
}
});
}

View File

@ -0,0 +1 @@
onCallSummary(summary) { console.log(JSON.stringify(summary)); }

View File

@ -0,0 +1,2 @@
onEnter: function(args) {console.log("entering");}

View File

@ -0,0 +1,2 @@
onEnter: function(args) {console.log("intercept=>leaving");}

View File

@ -0,0 +1 @@
onReceive(events) { console.log(JSON.stringify(events)); }

View File

@ -0,0 +1,24 @@
/* ###
* 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 ghidra.app.script.GhidraScript;
public class FridaTestScript extends GhidraScript {
@Override
protected void run() throws Exception {
String[] args = getScriptArgs();
System.err.println("scriptOutput: "+args[0]);
}
}

View File

@ -0,0 +1,49 @@
/* ###
* 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 agent.frida;
import java.util.concurrent.CompletableFuture;
import agent.frida.model.impl.FridaModelImpl;
import ghidra.dbg.DebuggerModelFactory;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
import ghidra.util.classfinder.ExtensionPointProperties;
/**
* Note this is in the testing source because it's not meant to be shipped in the release.... That
* may change if it proves stable, though, no?
*/
@FactoryDescription( //
brief = "IN-VM Frida local debugger", //
htmlDetails = "Launch a Frida session in this same JVM" //
)
@ExtensionPointProperties(priority = 80)
public class FridaInJvmDebuggerModelFactory implements DebuggerModelFactory {
@Override
public CompletableFuture<? extends DebuggerObjectModel> build() {
FridaModelImpl model = new FridaModelImpl();
return model.startFrida(new String[] {}).thenApply(__ -> model);
}
@Override
public boolean isCompatible() {
String osname = System.getProperty("os.name");
return osname.contains("Mac OS X") || osname.contains("Linux") || osname.contains("Windows");
}
}

View File

@ -0,0 +1,392 @@
/* ###
* 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 agent.frida.frida;
import java.util.List;
import java.util.Map;
import agent.frida.manager.*;
import ghidra.comm.util.BitmaskUniverse;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
/**
* A wrapper for FridaDebugger
*/
public interface FridaClient extends FridaClientReentrant {
/**
* Create a debug client.
*
* @return a new client
*/
public static FridaClient debugCreate() {
return new FridaClientImpl();
}
public static enum ExecutionState {
RUNNING, STOPPED;
}
public static enum DebugStatus {
NO_CHANGE(false, null, 13), //
GO(true, ExecutionState.RUNNING, 10), //
GO_HANDLED(true, ExecutionState.RUNNING, 9), //
GO_NOT_HANDLED(true, ExecutionState.RUNNING, 8), //
STEP_OVER(true, ExecutionState.RUNNING, 7), //
STEP_INTO(true, ExecutionState.RUNNING, 5), //
BREAK(false, ExecutionState.STOPPED, 0), //
NO_DEBUGGEE(true, null, 1), // shouldWait is true to handle process creation
STEP_BRANCH(true, ExecutionState.RUNNING, 6), //
IGNORE_EVENT(false, null, 11), //
RESTART_REQUESTED(true, null, 12), //
REVERSE_GO(true, null, 0xff), //
REVERSE_STEP_BRANCH(true, null, 0xff), //
REVERSE_STEP_OVER(true, null, 0xff), //
REVERSE_STEP_INTO(true, null, 0xff), //
OUT_OF_SYNC(false, null, 2), //
WAIT_INPUT(false, null, 3), //
TIMEOUT(false, null, 4), //
;
public static final long MASK = 0xaf;
public static final long INSIDE_WAIT = 0x100000000L;
public static final long WAIT_TIMEOUT = 0x200000000L;
DebugStatus(boolean shouldWait, ExecutionState threadState, int precedence) {
this.shouldWait = shouldWait;
this.threadState = threadState;
this.precedence = precedence;
}
public final boolean shouldWait;
public final ExecutionState threadState;
public final int precedence; // 0 is highest
public static DebugStatus fromArgument(FridaState state) {
if (state == null) {
return DebugStatus.NO_DEBUGGEE;
}
switch (state) {
case FRIDA_THREAD_UNINTERRUPTIBLE:
return DebugStatus.GO;
case FRIDA_THREAD_STOPPED:
return DebugStatus.BREAK;
case FRIDA_THREAD_RUNNING:
return DebugStatus.GO;
//case 7: // eStateStepping
// return DebugStatus.STEP_INTO;
case FRIDA_THREAD_HALTED:
case FRIDA_THREAD_WAITING:
return DebugStatus.NO_DEBUGGEE;
default:
return DebugStatus.NO_CHANGE;
}
}
}
public static enum SessionStatus {
ACTIVE, //
END_SESSION_ACTIVE_TERMINATE,//
END_SESSION_ACTIVE_DETACH, //
END_SESSION_PASSIVE, //
END, //
REBOOT, //
HIBERNATE, //
FAILURE, //
;
}
public static enum ChangeSessionState implements BitmaskUniverse {
SESSION_ALL(0xffffffff), //
;
private ChangeSessionState(long eBroadcastBitBreakpointChanged) {
this.mask = (int) eBroadcastBitBreakpointChanged;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum ChangeProcessState implements BitmaskUniverse {
PROCESS_ALL(0xffffffff), //
;
private ChangeProcessState(long eBroadcastBitStateChanged) {
this.mask = (int) eBroadcastBitStateChanged;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum ChangeThreadState implements BitmaskUniverse {
THREAD_ALL(0xffffffff), //
;
private ChangeThreadState(long eBroadcastBitStackChanged) {
this.mask = (int) eBroadcastBitStackChanged;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum DebugAttachFlags implements BitmaskUniverse {
DEFAULT(0), //
NONINVASIVE(1 << 0), //
EXISTING(1 << 1), //
NONINVASIVE_NO_SUSPEND(1 << 2), //
INVASIVE_NO_INITIAL_BREAK(1 << 3), //
INVASIVE_RESUME_PROCESS(1 << 4), //
NONINVASIVE_ALLOW_PARTIAL(1 << 5), //
;
DebugAttachFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum DebugCreateFlags implements BitmaskUniverse {
LAUNCH_DEFAULT(0), //
LAUNCH_EXEC(1 << 0), //
LAUNCH_DEBUG(1 << 1), //
LAUNCH_STOP_AT_ENTRY(1 << 2), //
LAUNCH_DISABLE_ASLR(1 << 3), //
LAUNCH_DISABLE_STDIO(1 << 4), //
LAUNCH_IN_TTY(1 << 5), //
LAUNCH_IN_SHELL(1 << 6), //
LAUNCH_IN_SEP_GROUP(1 << 7), //
LAUNCH_DONT_SET_EXIT_STATUS(1 << 8), //
LAUNCH_DETACH_ON_ERROR(1 << 9), //
LAUNCH_SHELL_EXPAND_ARGS(1 << 10), //
LAUNCH_CLOSE_TTY_ON_EXIT(1 << 11), //
LAUNCH_INHERIT_FROM_PARENT(1 << 12) //
;
DebugCreateFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public enum DebugEndSessionFlags {
DEBUG_END_PASSIVE(0x00000000),
DEBUG_END_ACTIVE_TERMINATE(0x00000001),
DEBUG_END_ACTIVE_DETACH(0x00000002),
DEBUG_END_REENTRANT(0x00000003),
DEBUG_END_DISCONNECT(0x00000004);
DebugEndSessionFlags(int value) {
this.value = value;
}
private final int value;
public long getValue() {
return value;
}
}
public enum DebugOutputFlags {
DEBUG_OUTPUT_NORMAL(0x1), //
DEBUG_OUTPUT_ERROR(0x2), //
DEBUG_OUTPUT_WARNING(0x4), //
DEBUG_OUTPUT_VERBOSE(0x8), //
DEBUG_OUTPUT_PROMPT(0x10), //
DEBUG_OUTPUT_PROMPT_REGISTERS(0x20), //
DEBUG_OUTPUT_EXTENSION_WARNING(0x40), //
DEBUG_OUTPUT_DEBUGGEE(0x80), //
DEBUG_OUTPUT_DEBUGGEE_PROMPT(0x100), //
DEBUG_OUTPUT_SYMBOLS(0x200);
DebugOutputFlags(int value) {
this.value = value;
}
private final int value;
public long getValue() {
return value;
}
}
public static String getModelKey(Object modelObject) {
if (modelObject == null) {
return null;
}
return modelObject.getClass() + ":" + getId(modelObject);
}
public static String getId(Object modelObject) {
if (modelObject instanceof FridaTarget) {
FridaTarget target = (FridaTarget) modelObject;
return Long.toHexString(target.getID());
}
if (modelObject instanceof FridaSession) { // global
FridaSession session = (FridaSession) modelObject;
return Integer.toHexString(session.getProcess().getPID().intValue());
}
if (modelObject instanceof FridaProcess) { // global
FridaProcess process = (FridaProcess) modelObject;
return Long.toHexString(process.getPID());
}
if (modelObject instanceof FridaThread) { // global
FridaThread thread = (FridaThread) modelObject;
return Long.toHexString(thread.getTid());
}
if (modelObject instanceof FridaFrame) {
FridaFrame frame = (FridaFrame) modelObject;
return Long.toString(frame.getFrameID());
}
if (modelObject instanceof FridaValue) {
FridaValue val = (FridaValue) modelObject;
return val.getKey();
}
if (modelObject instanceof FridaModule) {
FridaModule module = (FridaModule) modelObject;
return module.getName();
}
if (modelObject instanceof FridaSection) {
FridaSection section = (FridaSection) modelObject;
return section.getRangeAddress();
}
if (modelObject instanceof FridaMemoryRegionInfo) {
FridaMemoryRegionInfo region = (FridaMemoryRegionInfo) modelObject;
return region.getRangeAddress();
}
if (modelObject instanceof FridaSymbol) {
FridaSymbol sym = (FridaSymbol) modelObject;
return sym.getName();
}
if (modelObject instanceof FridaImport) {
FridaImport imp = (FridaImport) modelObject;
return imp.getName();
}
if (modelObject instanceof FridaExport) {
FridaExport exp = (FridaExport) modelObject;
return exp.getName();
}
if (modelObject instanceof FridaFunction) {
FridaFunction fn = (FridaFunction) modelObject;
return fn.getFunctionName();
}
if (modelObject instanceof FridaFileSpec) {
FridaFileSpec spec = (FridaFileSpec) modelObject;
return spec.getPath();
}
throw new RuntimeException("Unknown object " + modelObject.getClass());
}
public static TargetExecutionState convertState(FridaState state) {
switch (state) {
case FRIDA_THREAD_RUNNING:
return TargetExecutionState.RUNNING;
case FRIDA_THREAD_WAITING:
return TargetExecutionState.INACTIVE;
case FRIDA_THREAD_UNINTERRUPTIBLE:
return TargetExecutionState.ALIVE;
case FRIDA_THREAD_STOPPED:
return TargetExecutionState.STOPPED;
case FRIDA_THREAD_HALTED:
return TargetExecutionState.TERMINATED;
default:
return TargetExecutionState.STOPPED;
}
}
public static FridaState convertState(TargetExecutionState state) {
switch (state) {
case RUNNING:
return FridaState.FRIDA_THREAD_RUNNING;
case INACTIVE:
return FridaState.FRIDA_THREAD_WAITING;
case ALIVE:
return FridaState.FRIDA_THREAD_UNINTERRUPTIBLE;
case STOPPED:
return FridaState.FRIDA_THREAD_STOPPED;
case TERMINATED:
return FridaState.FRIDA_THREAD_HALTED;
default:
return FridaState.FRIDA_THREAD_STOPPED;
}
}
/**
* The the ID for the local server
*
* @return the ID
*/
FridaServerId getLocalServer();
FridaSession attachProcess(FridaServerId si, int keyType, String key, boolean wait, boolean async);
FridaSession createProcess(FridaServerId si, String fileName);
FridaSession createProcess(FridaServerId si, String fileName,
List<String> args, List<String> envp, String workingDir);
FridaSession createProcess(FridaServerId si, String fileName, List<String> args, List<String> envp,
List<String> pathsIO,
String workingDir, long createFlags, boolean stopAtEntry);
void terminateCurrentProcess(FridaTarget target);
void destroyCurrentProcess(FridaTarget target);
void detachCurrentProcess(FridaTarget target);
FridaTarget connectSession(String commandLine);
Map<String, FridaSession> listSessions();
void endSession(FridaTarget target, DebugEndSessionFlags flags);
DebugStatus getExecutionStatus();
void processEvent(FridaEvent<?> fridaEvt);
boolean getInterrupt();
public void setManager(FridaManager manager);
}

View File

@ -0,0 +1,203 @@
/* ###
* 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 agent.frida.frida;
import java.math.BigInteger;
import java.util.*;
import agent.frida.manager.*;
import ghidra.util.Msg;
public class FridaClientImpl implements FridaClient {
private FridaManager manager;
private FridaDebugger d;
private FridaTarget initialTarget;
private List<FridaTarget> targets;
public FridaClientImpl() {
}
@Override
public FridaClient createClient() {
d = FridaEng.init();
targets = new ArrayList<>();
initialTarget = FridaEng.createTarget(d);
targets.add(initialTarget);
//cmd = sbd.GetCommandInterpreter();
return this;
}
public FridaDebugger getDebugger() {
return d;
}
@Override
public FridaServerId getLocalServer() {
return new FridaServerId(0);
}
@Override
public FridaSession attachProcess(FridaServerId si, int keyType, String key, boolean wait,
boolean async) {
FridaError error = new FridaError();
FridaTarget target = createNullSession();
targets.add(target);
int radix = 10;
if (key.startsWith("0x")) {
key = key.substring(2);
radix = 16;
}
BigInteger processId = new BigInteger(key, radix);
FridaSession session = target.attach(processId, error);
if (!error.success()) {
Msg.error(this, "Error while attaching to " + key);
Msg.error(this, error.getDescription());
return null;
}
manager.updateState(session);
target.setSession(session);
return session;
}
@Override
public FridaSession createProcess(FridaServerId si, String fileName) {
return createProcess(si, fileName, new ArrayList<String>(), new ArrayList<String>(), "");
}
@Override
public FridaSession createProcess(FridaServerId si, String fileName,
List<String> args, List<String> envp, String workingDir) {
FridaError error = new FridaError();
String[] argArr = args.toArray(new String[args.size()]);
String[] envArr = envp.isEmpty() ? null : envp.toArray(new String[envp.size()]);
FridaSession session = manager.getCurrentTarget().launchSimple(argArr, envArr, workingDir);
if (!error.success()) {
Msg.error(this, "Error for create process");
Msg.error(this, error.getDescription());
return null;
}
manager.updateState(session);
return session;
}
@Override
public FridaSession createProcess(FridaServerId si, String fileName,
List<String> args, List<String> envp, List<String> pathsIO,
String workingDir, long createFlags, boolean stopAtEntry) {
FridaTarget target = manager.getCurrentTarget();
String[] argArr = args.toArray(new String[args.size()]);
// null for envp means use the default environment
String[] envArr = envp.isEmpty() ? null : envp.toArray(new String[envp.size()]);
String pathSTDIN = pathsIO.get(0);
String pathSTDOUT = pathsIO.get(1);
String pathSTDERR = pathsIO.get(2);
FridaError error = new FridaError();
FridaSession session = target.launch(fileName, argArr, envArr,
pathSTDIN, pathSTDOUT, pathSTDERR, workingDir, createFlags, stopAtEntry, error);
//FridaProcess process = session.Launch(listener, null, null, "", "", "", "", 0, true, error);
if (!error.success()) {
Msg.error(this, "Error while launching " + fileName);
Msg.error(this, error.getDescription());
return null;
}
manager.updateState(session);
return session;
}
@Override
public void terminateCurrentProcess(FridaTarget target) {
FridaProcess process = target.getProcess();
if (process != null) {
FridaError error = process.kill();
if (!error.success()) {
Msg.error(this, error.getDescription());
}
}
}
@Override
public void destroyCurrentProcess(FridaTarget target) {
FridaProcess process = target.getProcess();
FridaError error = process.destroy();
if (!error.success()) {
Msg.error(this, error.getDescription());
}
}
@Override
public void detachCurrentProcess(FridaTarget target) {
FridaProcess process = target.getProcess();
FridaError error = process.destroy();
if (!error.success()) {
Msg.error(this, error.getDescription());
}
}
public FridaTarget createNullSession() {
return FridaEng.createTarget(d);
}
@Override
public FridaTarget connectSession(String fileName) {
return FridaEng.createTarget(d);
}
@Override
public Map<String, FridaSession> listSessions() {
Map<String, FridaSession> map = new HashMap<>();
//List<FridaTarget> targets = FridaEng.enumerateDevices(d);
for (FridaTarget t : targets) {
FridaSession session = t.getSession();
if (session != null) {
map.put(FridaClient.getId(session), session);
}
}
return map;
}
@Override
public void endSession(FridaTarget target, DebugEndSessionFlags flags) {
}
@Override
public void processEvent(FridaEvent<?> fridaEvt) {
manager.processEvent(fridaEvt);
}
@Override
public DebugStatus getExecutionStatus() {
FridaState state = manager.getState();
return DebugStatus.fromArgument(state);
}
@Override
public boolean getInterrupt() {
return false;
}
@Override
public void setManager(FridaManager manager) {
this.manager = manager;
manager.setCurrentTarget(initialTarget);
}
}

View File

@ -0,0 +1,31 @@
/* ###
* 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 agent.frida.frida;
/**
* An interface containing the subset of {@link FridaClient} methods which are reentrant.
*
* All other methods should be called only by the thread which created the client.
*/
public interface FridaClientReentrant {
/**
* Create a new client for the calling thread, connected to the same session as this client.
*
* @return the new client
*/
FridaClient createClient();
}

View File

@ -0,0 +1,332 @@
/* ###
* 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 agent.frida.frida;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.LongByReference;
import agent.frida.jna.FridaNative;
import agent.frida.manager.*;
import ghidra.comm.util.BitmaskSet;
import ghidra.util.Msg;
/**
* A wrapper for Microsoft's {@code dbgeng.dll} that presents a Java-friendly interface.
*
* This is the "root interface" from which all other interfaces to {@code dbgeng.dll} are generated.
* Not every method listed in the documentation, nor every method present in the header, is
* implemented. Only those that were necessary to implement the SCTL adapter. However, the class and
* interface hierarchy was designed so that adding the remaining methods should be fairly
* straightforward. This wrapper attempts to obtain the most capable COM interfaces for the debug
* client that it knows. Again, newer interfaces should be fairly straightforward to add.
*
* Methods that are "obviously" wrappers for a COM method are left undocumented, unless there is
* some nuance to how it has been wrapped. In many cases, a parameter which is an integer in the COM
* method may be presented as an {@code enum} or {@link BitmaskSet} by the wrapper. Consult the MSDN
* for the meaning of the various values and bit flags.
*
* Each wrapper interface is implemented by several COM interface wrappers: one for each known COM
* interface version. The wrapper is optimistic, in that it declares wrapper methods even for COM
* methods that are only available in later versions. The implementations limited to earlier COM
* interfaces should either emulate the operation, or throw an
* {@link UnsupportedOperationException}. Where a newer method is provided by a newer interface, a
* wrapper implementation should prefer the latest. For example, one series of interfaces introduces
* {@code *Wide} variants of existing methods. Since Java also uses a UTF-16-like string encoding
* internally, JNA permits wide strings to be passed by reference. Thus, the wide variant is always
* preferred.
*
* Pay careful attention to the threading requirements imposed by {@code dbgeng.dll} these can be
* found in the MSDN. As a general rule of thumb, if the method is reentrant (i.e., it can be called
* from any thread), it is declared in the {@code *Reentrant} variant of the wrapper interface.
* There are few of these. Unless the documentation explicitly lists the method as reentrant, do not
* declare it there. Many methods appear to execute successfully from the wrong thread, but cause
* latent issues. A practice to prevent accidental use of non-reentrant methods outside of the
* client's owning thread is to ensure that only the owning thread can see the full interface. All
* other threads should only have access to the reentrant interface.
*
* If you implement methods that introduce a new callback class, use the existing callback type
* hierarchies as a model. There are many classes to implement. Furthermore, be sure to keep a
* reference to any active callback instances within the wrapper that uses them. The JNA has no way
* of knowing whether or not the instance is still being used by the external C/C++ library. If you
* do not store a reference, the JVM will think it's garbage and free it, even though COM is still
* using it. Drop the reference only when you are certain nothing external has a reference to it.
*/
public class FridaEng {
private static final NativeLong FRIDA_REALM_NATIVE = new NativeLong(0);
/**
* Create a debug client.
*
* @return a pointer to the device manager
*/
public static FridaDebugger init() {
FridaNative.INSTANCE.frida_init();
return new FridaDebugger(FridaNative.INSTANCE.frida_device_manager_new());
}
public static FridaTarget createTarget(FridaDebugger d) {
Pointer deviceManager = d.getPointer();
FridaError err = new FridaError();
Pointer localDevice = FridaNative.INSTANCE.frida_device_manager_find_device_by_type_sync(deviceManager, new NativeLong(0), new NativeLong(10), null, err.error);
if (localDevice == null) {
Msg.error(d, err);
return null;
}
return new FridaTarget(localDevice);
}
public static List<FridaTarget> enumerateDevices(FridaDebugger d) {
Pointer deviceManager = d.getPointer();
FridaError err = new FridaError();
Pointer deviceList = FridaNative.INSTANCE.frida_device_manager_enumerate_devices_sync(deviceManager, null, err.error);
if (deviceList == null) {
Msg.error(d, err);
return null;
}
Integer numDevices = FridaNative.INSTANCE.frida_device_list_size(deviceList);
List<FridaTarget> targetList = new ArrayList<>(numDevices);
for (int i = 0; i != numDevices; i++) {
Pointer device = FridaNative.INSTANCE.frida_device_list_get(deviceList, i);
String name = FridaNative.INSTANCE.frida_device_get_name(device);
FridaTarget t = new FridaTarget(device);
t.setName(name);
targetList.add(t);
}
return targetList;
}
public static List<FridaProcess> enumerateProcesses(FridaTarget t) {
Pointer device = t.getPointer();
FridaError err = new FridaError();
Pointer list = FridaNative.INSTANCE.frida_device_enumerate_processes_sync(device, null, null, err.error);
if (list == null) {
Msg.error(t, err);
return null;
}
Integer numProcesses = FridaNative.INSTANCE.frida_process_list_size(list);
List<FridaProcess> processList = new ArrayList<>(numProcesses);
for (int i = 0; i != numProcesses; i++) {
Pointer process = FridaNative.INSTANCE.frida_process_list_get(list, i);
NativeLong pid = FridaNative.INSTANCE.frida_process_get_pid(process);
String name = FridaNative.INSTANCE.frida_process_get_name(process);
FridaProcess p = new FridaProcess(process, pid);
p.setName(name);
processList.add(p);
}
return processList;
}
public static List<FridaProcess> enumerateApplications(FridaTarget t) {
Pointer device = t.getPointer();
FridaError err = new FridaError();
Pointer list = FridaNative.INSTANCE.frida_device_enumerate_applications_sync(device, null, null, err.error);
if (list == null) {
Msg.error(t, err);
return null;
}
Integer numApplications = FridaNative.INSTANCE.frida_process_list_size(list);
List<FridaProcess> processList = new ArrayList<>(numApplications);
for (int i = 0; i != numApplications; i++) {
Pointer application = FridaNative.INSTANCE.frida_application_list_get(list, i);
NativeLong pid = FridaNative.INSTANCE.frida_application_get_pid(application);
String name = FridaNative.INSTANCE.frida_application_get_name(application);
String identifier = FridaNative.INSTANCE.frida_application_get_identifier(application);
FridaProcess p = new FridaProcess(application, pid);
p.setName(name);
p.setIdentifier(identifier);
processList.add(p);
}
return processList;
}
public static FridaSession attach(FridaTarget t, NativeLong pid, FridaError err) {
Pointer localDevice = t.getPointer();
FridaNative.GError.ByReference ref = new FridaNative.GError.ByReference();
Pointer session = FridaNative.INSTANCE.frida_device_attach_sync(localDevice, pid, FridaEng.FRIDA_REALM_NATIVE, null, ref);
if (session == null) {
Msg.error(t, ref);
return null;
}
Pointer process = FridaNative.INSTANCE.frida_device_get_process_by_pid_sync(localDevice, pid, null, null, err.error);
if (process == null) {
Msg.error(t, err);
return null;
}
FridaProcess p = new FridaProcess(process, pid);
FridaSession s = new FridaSession(session, p);
p.setSession(s);
s.setTarget(t);
t.setSession(s);
return s;
}
public static FridaSession spawn(FridaTarget t, String fileName, FridaError err) {
Pointer localDevice = t.getPointer();
NativeLong pid = FridaNative.INSTANCE.frida_device_spawn_sync(localDevice, fileName, FridaEng.FRIDA_REALM_NATIVE, null, err.error);
if (!err.success()) {
Msg.error(t, err);
return null;
}
return attach(t, pid, err);
}
public static void resume(FridaTarget t, NativeLong pid, FridaError err) {
Pointer localDevice = t.getPointer();
FridaNative.INSTANCE.frida_device_resume_sync(localDevice, pid, null, err.error);
if (!err.success()) {
Msg.error(t, err);
}
}
public static void kill(FridaTarget t, NativeLong pid, FridaError err) {
Pointer localDevice = t.getPointer();
FridaNative.INSTANCE.frida_device_kill_sync(localDevice, pid, null, err.error);
if (!err.success()) {
Msg.error(t, err);
}
}
public static void detach(FridaSession s, FridaError err) {
Pointer session = s.getPointer();
FridaNative.INSTANCE.frida_session_detach_sync(session, null, err.error);
if (!err.success()) {
Msg.error(s, err);
}
}
public static void resume(FridaSession s, FridaError err) {
Pointer session = s.getPointer();
FridaNative.INSTANCE.frida_session_resume_sync(session, null, err.error);
if (!err.success()) {
Msg.error(s, err);
}
}
public static NativeLong connectSignal(FridaScript s, String signal, FridaNative.MessageCallback cb, Pointer userData) {
Pointer script = s.getPointer();
try {
return FridaNative.INSTANCE._frida_g_signal_connect_data(script, signal, cb, userData, null, new NativeLong(0));
} catch (UnsatisfiedLinkError e) {
/* IGNORE */
}
try {
return FridaNative.INSTANCE.g_signal_connect_data(script, signal, cb, userData, null, new NativeLong(0));
} catch (UnsatisfiedLinkError e) { /* IGNORE */ }
return new NativeLong(-1);
}
public static void disconnectSignal(FridaScript s, NativeLong signal) {
Pointer script = s.getPointer();
try {
FridaNative.INSTANCE._frida_g_signal_handler_disconnect(script, signal);
return;
} catch (UnsatisfiedLinkError e) { /* IGNORE */ }
try {
FridaNative.INSTANCE.g_signal_handler_disconnect(script, signal);
return;
} catch (UnsatisfiedLinkError e) { /* IGNORE */ }
}
public static NativeLong createSignal(String signal) {
return FridaNative.INSTANCE.g_signal_new(
signal,
FridaNative.INSTANCE.frida_bus_session_get_type(), // type_from_class
new NativeLong(2), // G_SIGNAL_RUN_LAST
new NativeLong(0), // class_ofset
null, // accumulator
null, // accu_data
null, // closure
new NativeLong(1), // G_TYPE_NULL
new NativeLong(1), // 1 param
new NativeLong(16) // G_TYPE_STRING
);
}
public static void emitSignal(FridaSession s, String signal) {
Pointer script = s.getPointer();
FridaNative.INSTANCE.g_signal_emit_by_name(script, signal);
}
public static NativeLong getBusType() {
return FridaNative.INSTANCE.frida_bus_session_get_type();
}
public static FridaScript createScript(FridaSession s, String commands, Pointer options) {
if (s == null) {
Msg.error(s, "null session");
return null;
}
Pointer session = s.getPointer();
FridaError err = new FridaError();
Pointer script = FridaNative.INSTANCE.frida_session_create_script_sync(session, commands, options, null, err.error);
if (script == null) {
Msg.error(s, "Unable to create script: " + commands);
return null;
}
return new FridaScript(script);
}
public static void unref(FridaScript s) {
Pointer script = s.getPointer();
FridaNative.INSTANCE.frida_unref(script);
}
public static void loadScript(FridaScript s) {
Pointer script = s.getPointer();
FridaError err = new FridaError();
FridaNative.INSTANCE.frida_script_load_sync(script, null, err.error);
if (!err.success()) {
Msg.error(s, err);
}
}
public static void unloadScript(FridaScript s) {
Pointer script = s.getPointer();
FridaError err = new FridaError();
FridaNative.INSTANCE.frida_script_unload_sync(script, null, err.error);
if (!err.success()) {
Msg.error(s, err);
}
}
public static Pointer createOptions(String name) {
Pointer options = FridaNative.INSTANCE.frida_script_options_new();
FridaNative.INSTANCE.frida_script_options_set_name(options, name);
FridaNative.INSTANCE.frida_script_options_set_runtime(options, new NativeLong(0L));
return options;
}
public static void enableDebugger(FridaSession s, NativeLong port) {
Pointer session = s.getPointer();
FridaError err = new FridaError();
FridaNative.INSTANCE.frida_session_enable_debugger_sync(session, port, null, err.error);
if (!err.success()) {
Msg.error(s, err);
}
}
}

View File

@ -0,0 +1,84 @@
/* ###
* 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 agent.frida.frida;
import java.util.HashMap;
import java.util.Map;
import agent.frida.manager.*;
/**
* Information about a module (program or library image).
*
* The fields correspond to the parameters taken by {@code LoadModule} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class FridaModuleInfo {
private FridaProcess process;
private long numModules;
private Map<Integer, FridaModule> modules = new HashMap<>();
public FridaModuleInfo(FridaProcess process, FridaModule module) {
this.process = process;
numModules = 1;
modules.put(0, module);
}
public FridaModuleInfo(FridaKernelModule module) {
this.process = null;
numModules = 1;
modules.put(0, module);
}
public Long getNumberOfModules() {
return numModules;
}
public FridaModule getModule(int index) {
return modules.get(index);
}
public String toString(int index) {
FridaModule module = modules.get(index);
return module.toString();
}
public String getModuleName(int index) {
FridaModule module = modules.get(index);
return FridaClient.getId(module);
}
public void setModuleName(int index, String moduleName) {
FridaModule module = modules.get(index);
module.setName(moduleName);
}
public String getImageName(int index) {
FridaModule module = modules.get(index);
return module.getPath();
}
public void setImageName(int index, String dirName, String imageName) {
FridaModule module = modules.get(index);
module.setPath(imageName);
}
public FridaProcess getProcess() {
return process;
}
}

View File

@ -0,0 +1,43 @@
/* ###
* 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 agent.frida.frida;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.FridaState;
/**
* Information about a module (program or library image).
*
* The fields correspond to the parameters taken by {@code LoadModule} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class FridaProcessInfo {
public FridaProcess process;
public FridaState state;
public String id;
public FridaProcessInfo(FridaProcess process) {
this.process = process;
this.id = FridaClient.getId(process);
}
public String toString() {
return id;
}
}

View File

@ -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 agent.frida.frida;
import java.util.HashMap;
import java.util.Map;
import agent.frida.manager.*;
/**
*
* The fields correspond to the parameters taken by {@code LoadModule} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class FridaRegionInfo {
private FridaProcess process;
private long numRegions;
private Map<Integer, FridaMemoryRegionInfo> regions = new HashMap<>();
public FridaRegionInfo(FridaProcess process, FridaMemoryRegionInfo region) {
this.process = process;
numRegions = 1;
regions.put(0, region);
}
public FridaRegionInfo(FridaKernelMemoryRegionInfo region) {
this.process = null;
numRegions = 1;
regions.put(0, region);
}
public Long getNumberOfRegions() {
return numRegions;
}
public FridaMemoryRegionInfo getRegion(int index) {
return regions.get(index);
}
public String toString(int index) {
FridaMemoryRegionInfo region = regions.get(index);
return region.toString();
}
public String getRegionName(int index) {
FridaMemoryRegionInfo region = regions.get(index);
return FridaClient.getId(region);
}
public void setRegionName(int index, String regionName) {
FridaMemoryRegionInfo region = regions.get(index);
FridaFileSpec filespec = region.getFileSpec();
if (filespec != null) {
filespec.setPath(regionName);
}
}
public FridaProcess getProcess() {
return process;
}
}

View File

@ -0,0 +1,58 @@
/* ###
* 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 agent.frida.frida;
/**
* The ID of a debug server.
*
* Each server to which a client is connected is assigned a server ID. The local server, to which
* every client is connected by default, has the ID 0. This is essentially just a boxed integer, but
* having an explicit data type prevents confusion with other integral values.
*/
public class FridaServerId implements Comparable<FridaServerId> {
public final long id;
public FridaServerId(long id) {
this.id = id;
}
@Override
public int hashCode() {
return Long.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof FridaServerId)) {
return false;
}
FridaServerId that = (FridaServerId) obj;
if (this.id != that.id) {
return false;
}
return true;
}
@Override
public int compareTo(FridaServerId that) {
return Long.compare(this.id, that.id);
}
@Override
public String toString() {
return "<Frida Server ID " + id + ">";
}
}

View File

@ -0,0 +1,40 @@
/* ###
* 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 agent.frida.frida;
import agent.frida.manager.FridaSession;
/**
* Information about a session.
*
* The fields correspond to the parameters taken by {@code LoadModule} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class FridaSessionInfo {
public FridaSession session;
public String id;
public FridaSessionInfo(FridaSession session) {
this.session = session;
this.id = FridaClient.getId(session);
}
public String toString() {
return id;
}
}

View File

@ -0,0 +1,41 @@
/* ###
* 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 agent.frida.frida;
import agent.frida.manager.FridaFrame;
import agent.frida.manager.FridaThread;
/**
* Information about a module (program or library image).
*
* The fields correspond to the parameters taken by {@code LoadModule} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class FridaThreadInfo {
public FridaThread thread;
public String id;
public FridaThreadInfo(FridaThread thread) {
this.thread = thread;
this.id = FridaClient.getId(thread);
}
public String toString() {
return id;
}
}

View File

@ -0,0 +1,188 @@
/* ###
* 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 agent.frida.gadp;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import agent.frida.gadp.impl.FridaGadpServerImpl;
import ghidra.dbg.agent.AgentWindow;
import ghidra.util.Msg;
public interface FridaGadpServer extends AutoCloseable {
/**
* The entry point for the Frida server in stand-alone mode
*
* Run it to see help.
*
* @param args the command-line arguments
* @throws IOException if an I/O error occurs
* @throws ExecutionException
* @throws InterruptedException
*/
public static void main(String[] args) throws Exception {
new FridaRunner().run(args);
}
/**
* Create a new instance of the server
*
* @param addr the address to bind the SCTL server to
* @param busId the client ID the server should use on the bus for synthesized commands
* @return the server instance
* @throws IOException
*/
public static FridaGadpServer newInstance(SocketAddress addr) throws IOException {
return new FridaGadpServerImpl(addr);
}
/**
* Runs the server from the command line
*/
public class FridaRunner {
protected InetSocketAddress bindTo;
protected List<String> fridaArgs = new ArrayList<>();
protected byte busId = 1;
protected String remote = null;
public FridaRunner() {
}
public void run(String args[])
throws IOException, InterruptedException, ExecutionException {
parseArguments(args);
try (FridaGadpServer server = newInstance(bindTo)) {
//TODO: fix/test the debugConnect case when args are passed
server.startFrida(fridaArgs.toArray(new String[] {})).exceptionally(e -> {
Msg.error(this, "Error starting Frida/GADP", e);
System.exit(-1);
return null;
});
new AgentWindow("Frida Agent for Ghidra", server.getLocalAddress());
while (server.isRunning()) {
Thread.sleep(1000);
}
System.exit(0);
}
}
protected void parseArguments(String[] args) {
String iface = "localhost";
int port = 12345;
// NOTE: Maybe commons-cli or Argparse4j?
Iterator<String> ait = Arrays.asList(args).iterator();
while (ait.hasNext()) {
String a = ait.next();
if ("-h".equals(a) || "--help".equals(a)) {
printUsage();
System.exit(0);
}
else if ("-p".equals(a) || "--port".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected PORT");
printUsage();
System.exit(-1);
}
String portStr = ait.next();
try {
port = Integer.parseInt(portStr);
}
catch (NumberFormatException e) {
System.err.println("Integer required. Got " + portStr);
printUsage();
System.exit(-1);
}
}
else if ("-H".equals(a) || "--host".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected HOST/ADDR");
printUsage();
System.exit(-1);
}
iface = ait.next();
}
else {
System.err.println("Unknown option: " + a);
printUsage();
System.exit(-1);
}
}
bindTo = new InetSocketAddress(iface, port);
}
protected void printUsage() {
System.out.println("This is the GADP server for Frida. Usage:");
System.out.println();
System.out.println(" [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] [-r REMOTE]");
System.out.println();
System.out.println("Options:");
System.out.println(
" --host/-H The address of the interface on which to listen.");
System.out.println(" Default is localhost");
System.out.println(
" --port/-p The TCP port on which to listen for GADP. Default is 12345");
}
}
/**
* Start the debugging server
*
* @return a future that completes when the server is ready
*/
CompletableFuture<Void> startFrida(String[] args);
/**
* Get the local address to which the SCTL server is bound.
*
* @return the local socket address
*/
SocketAddress getLocalAddress();
/**
* Close all connections and ports, GADP and Process Server, and terminate the server
*
* @throws IOException if an I/O error occurs
*/
public void terminate() throws IOException;
/**
* Check if the server is running
*
* This will return false: 1) Before the server has been started, 2) After a call to
* {@link #terminate()}, or 3) When an error occurs causing the server to terminate
* unexpectedly. Otherwise, it returns true.
*
* @returns true if the server is currently running.
*/
public boolean isRunning();
/**
* Calls {@link #terminate()}
*
* @throws IOException if an I/O error occurs
*/
@Override
default void close() throws IOException {
terminate();
}
}

View File

@ -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 agent.frida.gadp;
import java.util.List;
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
import ghidra.util.classfinder.ExtensionPointProperties;
@FactoryDescription( //
brief = "Frida local agent via GADP/TCP", //
htmlDetails = "Launch a new agent using Frida." //
)
@ExtensionPointProperties(priority = 100)
public class FridaLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
protected String remote = "none"; // Require user to start server
@FactoryOption("DebugConnect options (.server)")
public final Property<String> agentRemoteOption =
Property.fromAccessors(String.class, this::getAgentRemote, this::setAgentRemote);
protected String transport = "none"; // Require user to start server
@FactoryOption("Remote process server options (untested)")
public final Property<String> agentTransportOption =
Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport);
@Override
public boolean isCompatible() {
String osname = System.getProperty("os.name");
return osname.contains("Mac OS X") || osname.contains("Linux") || osname.contains("Windows");
}
public String getAgentTransport() {
return transport;
}
public void setAgentTransport(String transport) {
this.transport = transport;
}
public String getAgentRemote() {
return remote;
}
public void setAgentRemote(String remote) {
this.remote = remote;
}
@Override
protected String getThreadName() {
return "Local Frida Agent stdout";
}
protected Class<?> getServerClass() {
return FridaGadpServer.class;
}
@Override
protected void completeCommandLine(List<String> cmd) {
cmd.add(getServerClass().getCanonicalName());
cmd.addAll(List.of("-H", host));
cmd.addAll(List.of("-p", Integer.toString(port)));
}
}

View File

@ -0,0 +1,199 @@
/* ###
* 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 agent.frida.gadp.impl;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import agent.frida.frida.FridaClient;
import agent.frida.frida.FridaClient.DebugStatus;
import agent.frida.manager.FridaManager;
import ghidra.util.Msg;
/**
* A single-threaded executor which creates and exclusively accesses the {@code frida} client.
*
* <p>
* The executor also has a priority mechanism, so that callbacks may register follow-on handlers
* which take precedence over other tasks in the queue (which could trigger additional callbacks).
* This is required since certain operation are not allowed during normal callback processing. For
* example, changing the current process is typically not allowed, but it is necessary to retrieve a
* thread's context.
*/
public abstract class AbstractClientThreadExecutor extends AbstractExecutorService {
private static final int DEFAULT_PRIORITY = 10;
protected FridaClient client;
protected boolean shuttingDown = false;
protected final Queue<Entry> queue = new PriorityQueue<>();
protected Thread thread = new Thread(this::run, "DebugClient");
protected final AtomicBoolean waitRegistered = new AtomicBoolean();
protected abstract void init();
public static class Entry implements Comparable<Entry> {
final int priority;
public final Runnable command;
public Entry(int priority, Runnable command) {
this.priority = priority;
this.command = command;
}
@Override
public int compareTo(Entry that) {
return Integer.compare(this.priority, that.priority);
}
}
/**
* Obtain a reference to the client, only if the calling thread is this executor's thread.
*
* @return the client
*/
public FridaClient getClient() {
return client;
}
public void cancelWait() {
waitRegistered.set(false);
}
public void registerWait() {
waitRegistered.set(true);
}
private Entry pollQueue() {
synchronized (queue) {
return queue.poll();
}
}
private void run() {
/**
* The general idea is to run indefinitely, taking every precaution to protect this thread's
* life, since only it can access the client. Granted, if it turns out to be too difficult,
* we can always create a new thread and client, using the existing client's reentrant
* methods.
*/
try {
init();
while (!shuttingDown) {
Entry next;
while (null != (next = pollQueue())) {
if (shuttingDown) {
return;
}
try {
//System.out.println("Executing: " + next);
next.command.run();
//System.out.println("Done");
}
catch (Throwable t) {
Msg.error(this, "Task in executor threw: " + t);
}
}
DebugStatus status = client.getExecutionStatus();
if (status.shouldWait && status != DebugStatus.NO_DEBUGGEE ||
waitRegistered.get()) {
waitRegistered.set(false);
}
}
}
catch (Throwable t) {
Msg.error(this, "Non-respawnable executor terminated unexpectedly", t);
shuttingDown = true;
}
}
@Override
public void shutdown() {
shuttingDown = true;
}
@Override
public List<Runnable> shutdownNow() {
shuttingDown = true;
thread.interrupt();
List<Runnable> left = new ArrayList<>(queue.size());
for (Entry ent : queue) {
left.add(ent.command);
}
return left;
}
@Override
public boolean isShutdown() {
return shuttingDown;
}
@Override
public boolean isTerminated() {
return !thread.isAlive();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
long millis = TimeUnit.MILLISECONDS.convert(timeout, unit);
thread.join(millis);
return !thread.isAlive();
}
@Override
public void execute(Runnable command) {
execute(DEFAULT_PRIORITY, command);
}
/**
* Schedule a task with a given priority.
*
* @param priority the priority
* @param command the task
*/
public void execute(int priority, Runnable command) {
if (shuttingDown) {
throw new RejectedExecutionException("Executor is shutting down");
}
if (!thread.isAlive()) {
throw new RejectedExecutionException("Executor has terminated");
}
synchronized (queue) {
queue.add(new Entry(priority, command));
}
}
public boolean isCurrentThread() {
return thread.equals(Thread.currentThread());
}
/**
* Schedule a task with the given priority, taking a reference to the client.
*
* @param priority the priority
* @param command the task
*/
public void execute(int priority, Consumer<FridaClient> command) {
execute(priority, () -> command.accept(client));
}
public abstract FridaManager getManager();
public abstract void setManager(FridaManager manager);
}

View File

@ -0,0 +1,64 @@
/* ###
* 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 agent.frida.gadp.impl;
import java.util.function.Supplier;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaManager;
/**
* A single-threaded executor which creates and exclusively accesses the {@code Frida} client.
*
* The executor also has a priority mechanism, so that callbacks may register follow-on handlers
* which take precedence over other tasks in the queue (which could trigger additional callbacks).
* This is required since certain operation are not allowed during normal callback processing. For
* example, changing the current process is typically not allowed, but it is necessary to retrieve a
* thread's context.
*/
public class FridaClientThreadExecutor extends AbstractClientThreadExecutor {
private final Supplier<FridaClient> makeClient;
private FridaManager manager;
/**
* Instantiate a new executor, providing a means of creating the client
*
* @param makeClient the callback to create the client
*/
public FridaClientThreadExecutor(Supplier<FridaClient> makeClient) {
this.makeClient = makeClient;
thread.setDaemon(true);
thread.start();
}
@Override
protected void init() {
this.client = makeClient.get();
client.setManager(manager);
}
@Override
public FridaManager getManager() {
return manager;
}
@Override
public void setManager(FridaManager manager) {
this.manager = manager;
}
}

View File

@ -0,0 +1,64 @@
/* ###
* 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 agent.frida.gadp.impl;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import agent.frida.gadp.FridaGadpServer;
import agent.frida.model.AbstractFridaModel;
import agent.frida.model.impl.FridaModelImpl;
import ghidra.dbg.gadp.server.AbstractGadpServer;
public class FridaGadpServerImpl implements FridaGadpServer {
public class GadpSide extends AbstractGadpServer {
public GadpSide(AbstractFridaModel model, SocketAddress addr)
throws IOException {
super(model, addr);
}
}
protected final AbstractFridaModel model;
protected final GadpSide server;
public FridaGadpServerImpl(SocketAddress addr) throws IOException {
super();
this.model = new FridaModelImpl();
this.server = new GadpSide(model, addr);
}
@Override
public CompletableFuture<Void> startFrida(String[] args) {
return model.startFrida(args).thenCompose(__ -> server.launchAsyncService());
}
@Override
public SocketAddress getLocalAddress() {
return server.getLocalAddress();
}
@Override
public boolean isRunning() {
return model.isRunning();
}
@Override
public void terminate() throws IOException {
model.terminate();
server.terminate();
}
}

View File

@ -0,0 +1,285 @@
/* ###
* 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 agent.frida.jna;
import java.util.List;
import com.sun.jna.*;
import com.sun.jna.platform.linux.LibC;
import com.sun.jna.ptr.PointerByReference;
public interface FridaNative extends LibC {
FridaNative INSTANCE = Native.load("frida-core", FridaNative.class); //??
public static final int GUM_MAX_PATH = 261;
public static final int GUM_MAX_SYMBOL_NAME = 2049;
public static final int GUM_MAX_BACKTRACE_DEPTH = 16;
public enum GumOs {
GUM_OS_WINDOWS("windows"),
GUM_OS_MACOS("macos"),
GUM_OS_LINUX("linux"),
GUM_OS_IOS("ios"),
GUM_OS_ANDROID("android"),
GUM_OS_FREEBSD("freebsd"),
GUM_OS_QNX("qnx");
final String str;
GumOs(String str) {
this.str = str;
}
@Override
public String toString() {
return str;
}
}
public enum GumThreadState {
GUM_THREAD_RUNNING("running"),
GUM_THREAD_STOPPED("stopped"),
GUM_THREAD_WAITING("waiting"),
GUM_THREAD_UNINTERRUPTIBLE("uninterruptible"),
GUM_THREAD_HALTED("halted");
final String str;
GumThreadState(String str) {
this.str = str;
}
@Override
public String toString() {
return str;
}
}
public static class GError extends Structure {
public static class ByReference extends GError
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("domain", "code", "message");
public volatile int domain;
public volatile int code;
public volatile String message;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumMemoryRange extends Structure {
public static class ByReference extends GumMemoryRange
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("address", "size");
public NativeLong address;
public NativeLong size;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumFileMapping extends Structure {
public static class ByReference extends GumFileMapping
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("path", "offset", "size");
public Pointer path;
public NativeLong offset;
public NativeLong size;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumModuleDetails extends Structure {
public static class ByReference extends GumModuleDetails
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("name", "range", "path");
public Pointer name;
public GumMemoryRange.ByReference range;
public Pointer path;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumRangeDetails extends Structure {
public static class ByReference extends GumRangeDetails
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("range", "protection", "file");
public GumMemoryRange.ByReference range;
public NativeLong protection;
public GumFileMapping.ByReference file;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumReturnAddressDetails extends Structure {
public static class ByReference extends GumReturnAddressDetails
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("address",
"moduleName", "functionName", "fileName", "lineNumber");
public NativeLong address;
public byte[] moduleName = new byte[GUM_MAX_PATH];
public byte[] functionName = new byte[GUM_MAX_SYMBOL_NAME];
public byte[] fileName = new byte[GUM_MAX_PATH];
public NativeLong lineNumber;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumReturnAddressArray extends Structure {
public static class ByReference extends GumReturnAddressArray
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("len", "items");
public NativeLong len;
public NativeLong[] items = new NativeLong[GUM_MAX_BACKTRACE_DEPTH];
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class GumMallocRangeDetails extends Structure {
public static class ByReference extends GumMallocRangeDetails
implements Structure.ByReference {
// NO CODE
}
public static final List<String> FIELDS = createFieldsOrder("range");
public GumMemoryRange.ByReference range;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static interface ExceptionCallback extends Callback {
void invoke(Pointer details);
}
public static interface MessageCallback extends Callback {
void invoke(Pointer script, String message, Pointer data, Pointer userData);
}
void frida_init();
Pointer frida_device_manager_new();
void frida_device_manager_close_sync(Pointer manager, Pointer cancellable, GError.ByReference error);
Pointer frida_device_manager_find_device_by_type_sync(Pointer manager, NativeLong type, NativeLong timeout, Pointer cancellable, GError.ByReference error);
Pointer frida_device_manager_enumerate_devices_sync(Pointer manager, Pointer cancellable, GError.ByReference error);
Integer frida_device_list_size(Pointer deviceList);
Pointer frida_device_list_get(Pointer deviceList, int i);
String frida_device_get_name(Pointer device);
Pointer frida_device_enumerate_processes_sync(Pointer device, NativeLong options, Pointer cancellable, GError.ByReference error);
Integer frida_process_list_size(Pointer processList);
Pointer frida_process_list_get(Pointer processList, int i);
NativeLong frida_process_get_pid(Pointer process);
String frida_process_get_name(Pointer process);
Pointer frida_process_get_parameters(Pointer process);
Pointer frida_device_enumerate_applications_sync(Pointer device, NativeLong options, Pointer cancellable, GError.ByReference error);
Integer frida_application_list_size(Pointer processList);
Pointer frida_application_list_get(Pointer processList, int i);
NativeLong frida_application_get_pid(Pointer process);
String frida_application_get_name(Pointer process);
String frida_application_get_identifier(Pointer process);
Pointer frida_application_get_parameters(Pointer process);
Pointer frida_device_attach_sync(Pointer localDevice, NativeLong pid, NativeLong options, Pointer cancellable, GError.ByReference error);
NativeLong frida_device_spawn_sync(Pointer localDevice, String fileName, NativeLong options, Pointer cancellable, GError.ByReference error);
NativeLong frida_session_get_pid(Pointer session);
Pointer frida_device_get_process_by_pid_sync(Pointer localDevice, NativeLong pid, Pointer options, Pointer cancellable, GError.ByReference error);
Pointer frida_device_resume_sync(Pointer localDevice, NativeLong pid, Pointer cancellable, GError.ByReference error);
Pointer frida_device_kill_sync(Pointer localDevice, NativeLong pid, Pointer cancellable, GError.ByReference error);
boolean frida_session_is_detached(Pointer session);
void frida_session_detach_sync(Pointer session, Pointer cancellable, GError.ByReference error);
void frida_session_resume_sync(Pointer session, Pointer cancellable, GError.ByReference error);
Pointer frida_script_options_new();
void frida_script_options_set_name(Pointer options, String name);
void frida_script_options_set_runtime (Pointer options, NativeLong runtime);
Pointer frida_session_create_script_sync(Pointer session, String commands, Pointer options, Pointer cancellable, GError.ByReference error);
void frida_unref(Pointer script);
void frida_script_load_sync(Pointer script, Pointer cancellable, GError.ByReference error);
void frida_script_unload_sync(Pointer script, Pointer cancellable, GError.ByReference error);
void frida_session_enable_debugger_sync(Pointer session, NativeLong port, Pointer cancellable, GError.ByReference error);
NativeLong frida_bus_session_get_type();
// These are equivalent but version-dependent
NativeLong _frida_g_signal_connect_data(Pointer script, String signal, MessageCallback closure, Pointer data, Pointer notify, NativeLong after);
NativeLong g_signal_connect_data(Pointer script, String signal, MessageCallback closure, Pointer data, Pointer notify, NativeLong after);
// These are equivalent but version-dependent
void _frida_g_signal_handler_disconnect(Pointer script, NativeLong signalHandle);
void g_signal_handler_disconnect(Pointer script, NativeLong signalHandle);
void g_signal_emit_by_name(Pointer instance, String detailed_signal);
NativeLong g_signal_new(String signal_name, NativeLong itype, NativeLong signal_flags,
NativeLong class_offset, Pointer accumulator, Pointer accu_data,
Pointer c_marshaller, NativeLong return_type, NativeLong n_params, NativeLong ptype);
}

View File

@ -0,0 +1,52 @@
/* ###
* 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 agent.frida.manager;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
public class FridaApplication extends FridaPointer {
private NativeLong pid;
private String name;
private String identifier;
public FridaApplication(Pointer process, NativeLong pid) {
super(process);
this.pid = pid;
}
public Long getPID() {
return pid.longValue();
}
public String getIdentifier() {
return identifier;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
}

View File

@ -0,0 +1,33 @@
/* ###
* 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 agent.frida.manager;
import agent.frida.manager.cmd.FridaPendingCommand;
/**
* Identifies the cause of an event emitted by frida
*
* This is not a concept native to frida. Rather, it is a means to distinguish events that result
* from commands issued by the {@link FridaManager} from those issued by the user or some other means.
* For example, a call to {@link FridaManager#addProcess()} will emit a
* {@link FridaEventsListener#processAdded(FridaProcess, FridaCause)} event, identifying the
* {@link FridaPendingCommand} as the cause.
*/
public interface FridaCause {
public enum Causes implements FridaCause {
UNCLAIMED;
}
}

View File

@ -0,0 +1,82 @@
/* ###
* 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 agent.frida.manager;
import java.util.concurrent.CompletableFuture;
import agent.frida.manager.cmd.FridaPendingCommand;
/**
* The interface for frida command implementations
*
* Commands are executed by frida in serial. In order to distinguish the likely cause of events, the
* manager will wait to issue each command until it has seen a prompt. Thus, commands are queued up,
* and the manager uses the {@link CompletableFuture} pattern to "return" results after execution
* completes.
*
* To begin executing the command, the manager invokes the command, using {@link #invoke()}. Any
* event that occurs during command execution is given to
* {@link #handle(FridaEvent, FridaPendingCommand)}. The implementor then has the option to "claim" or
* "steal" the event. When claimed, any subsequent event processor or listener is provided this
* command as the event's cause. When stolen, no subsequent event processors are called. The
* implementation ought to keep a list of claimed and stolen events. Once frida has finished executing
* the command, the manager calls {@link #complete(FridaPendingCommand)}, allowing the implementation
* to process its claimed and stolen events and return the result of the command.
*
* @param <T> the type of object "returned" by the command
*/
public interface FridaCommand<T> {
/**
* Check if this command can be executed given frida's current state
*
* @param state frida's state
* @return true if it can be executed, false otherwise
*/
public boolean validInState(FridaState state);
/**
* Invoke the command
*/
public void invoke();
/**
* Parse the command results
* @param result string returned
* @param data unused
*/
public void parse(String result, Object data);
/**
* Handle an event that occurred during the execution of this command
*
* @param evt the event
* @param pending a copy of the executing command instance
* @return true if the command is now ready to be completed
*/
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending);
/**
* Called when the manager believes this command is finished executing
*
* This is presumed when the manager receives the prompt after issuing the encoded command
*
* @param pending a copy of the now-finished-executing command instance
* @return the object "returned" by the command
*/
public T complete(FridaPendingCommand<?> pending);
}

View File

@ -0,0 +1,42 @@
/* ###
* 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 agent.frida.manager;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class FridaContext {
private Map<String, String> map;
public FridaContext(JsonObject cpuContext) {
this.map = new HashMap<>();
for (Entry<String, JsonElement> element : cpuContext.entrySet()) {
JsonPrimitive primitive = element.getValue().getAsJsonPrimitive();
map.put(element.getKey(), primitive.getAsString());
}
}
public Map<String, String> getChildren() {
return map;
}
}

View File

@ -0,0 +1,26 @@
/* ###
* 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 agent.frida.manager;
import com.sun.jna.Pointer;
public class FridaDebugger extends FridaPointer {
public FridaDebugger(Pointer deviceManager) {
super(deviceManager);
}
}

View File

@ -0,0 +1,45 @@
/* ###
* 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 agent.frida.manager;
import agent.frida.jna.FridaNative;
public class FridaError {
public FridaNative.GError.ByReference error;
public FridaError() {
error = new FridaNative.GError.ByReference();
}
public long getValue() {
return error.code;
}
public boolean success() {
return getValue() == 0L;
}
public String getDescription() {
return error.code + ":" +error.message;
}
public String toString() {
return getDescription();
}
}

View File

@ -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 agent.frida.manager;
import agent.frida.manager.cmd.FridaPendingCommand;
public interface FridaEvent<T> {
/**
* Get the information detailing the event
*
* @return the information
*/
public T getInfo();
/**
* Use {@link FridaPendingCommand#claim(FridaEvent)} instead
*
* @param cause the cause
*/
public void claim(FridaPendingCommand<?> cause);
/**
* If claimed, get the cause of this event
*
* @return the cause
*/
public FridaCause getCause();
/**
* Use {@link FridaPendingCommand#steal(FridaEvent)} instead
*/
public void steal();
/**
* Check if this event is stolen
*
* A stolen event should not be processed further, except by the thief
*
* @return true if stolen, false otherwise
*/
public boolean isStolen();
/**
* If this event implies a new frida state, get that state
*
* @return the new state, or null for no change
*/
public FridaState newState();
}

View File

@ -0,0 +1,224 @@
/* ###
* 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 agent.frida.manager;
import agent.frida.frida.FridaRegionInfo;
import agent.frida.frida.FridaModuleInfo;
public interface FridaEventsListener {
/**
* A session has been added
*
* @param session a handle to the new session
* @param cause the cause of this event
*/
void sessionAdded(FridaSession session, FridaCause cause);
/**
* A session has been replaced
*
* @param session a handle to the new session
* @param cause the cause of this event
*/
void sessionReplaced(FridaSession session, FridaCause cause);
/**
* A session has been removed
*
* @param sessionId the ID of the now-defunct session
* @param cause the cause of this event
*/
void sessionRemoved(String sessionId, FridaCause cause);
/**
* A different session has been selected (gained focus)
*
* @param session a handle to the selected session
* @param cause the cause of this event
*/
void sessionSelected(FridaSession session, FridaCause cause);
/**
* An Process has been added to the session
*
* @param process a handle to the new process
* @param cause the cause of this event
*/
void processAdded(FridaProcess process, FridaCause cause);
/**
* An Process has been replaced in the session
*
* @param process a handle to the new process
* @param cause the cause of this event
*/
void processReplaced(FridaProcess process, FridaCause cause);
/**
* An process has been removed from the session
*
* @param processId the ID of the now-defunct process
* @param cause the cause of this event
*/
void processRemoved(String processId, FridaCause cause);
/**
* A different process has been selected (gained focus)
*
* @param process a handle to the selected process
* @param cause the cause of this event
*/
void processSelected(FridaProcess process, FridaCause cause);
/**
* Execution has been started in an process
*
* @param process a handle to the now-executing process
* @param cause the cause of this event
*/
void processStarted(FridaProcess process, FridaCause cause);
/**
* Execution has terminated in an process
*
* @param process a handle to the now-stopped process
* @param cause the cause of this event
*/
void processExited(FridaProcess process, FridaCause cause);
/**
* A thread has been created
*
* Use {@link FridaThread#getProcess()} to get a handle to the process in which the thread was
* created.
*
* @param thread a handle to the new thread
* @param cause the cause of this event
*/
void threadCreated(FridaThread thread, FridaCause cause);
/**
* A thread has been replaced
*
* Use {@link FridaThread#getProcess()} to get a handle to the process in which the thread was
* created.
*
* @param thread a handle to the new thread
* @param cause the cause of this event
*/
void threadReplaced(FridaThread thread, FridaCause cause);
/**
* A thread's state has changed
*
* @param thread a handle to the thread whose state has changed
* @param state the state to which the thread changed
* @param cause the cause of this event
* @param reason the reason for the state change
*/
void threadStateChanged(FridaThread thread, FridaState state, FridaCause cause, FridaReason reason);
/**
* A thread has exited
*
* @param thread the now-defunct thread
* @param process a handle to the process to which the thread belonged
* @param cause the cause of this event
*/
void threadExited(FridaThread thread, FridaProcess process, FridaCause cause);
/**
* A different thread has been selected (gained focus)
*
* @param thread a handle to the selected thread
* @param frame a handle to the current frame
* @param cause the cause of this event
*/
void threadSelected(FridaThread thread, FridaFrame frame, FridaCause cause);
/**
* A module has been loaded by an process
*
* @param process a handle to the process which loaded the module
* @param info the name of the module on the target
* @param index in-order index
* @param cause the cause of this event
*/
void moduleLoaded(FridaProcess process, FridaModuleInfo info, int index, FridaCause cause);
/**
* A module has been loaded by an process
*
* @param process a handle to the process which loaded the module
* @param info the name of the module on the target
* @param index in-order index
* @param cause the cause of this event
*/
void moduleReplaced(FridaProcess process, FridaModuleInfo info, int index, FridaCause cause);
/**
* A module has been unloaded from an process
*
* @param process a handle to the process which unloaded the module
* @param info the name of the module on the target
* @param index in-order index
* @param cause the cause of this event
*/
void moduleUnloaded(FridaProcess process, FridaModuleInfo info, int index, FridaCause cause);
/**
* A module has been loaded by an process
*
* @param process a handle to the process which loaded the module
* @param info the name of the region on the target
* @param index in-order index
* @param cause the cause of this event
*/
void regionAdded(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause);
/**
* A module has been loaded by an process
*
* @param process a handle to the process which loaded the module
* @param info the name of the region on the target
* @param index in-order index
* @param cause the cause of this event
*/
void regionReplaced(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause);
/**
* A module has been unloaded from an process
*
* @param process a handle to the process which unloaded the module
* @param info the name of the region on the target
* @param index in-order index
* @param cause the cause of this event
*/
void regionRemoved(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause);
/**
* @param output console output
* @param mask class of output
*/
void consoleOutput(String output, int mask);
/**
* @param prompt for console output
*/
void promptChanged(String prompt);
}

View File

@ -0,0 +1,146 @@
/* ###
* 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 agent.frida.manager;
import agent.frida.frida.FridaRegionInfo;
import agent.frida.frida.FridaModuleInfo;
public interface FridaEventsListenerAdapter extends FridaEventsListener {
@Override
public default void sessionAdded(FridaSession session, FridaCause cause) {
// Extension point
}
@Override
public default void sessionReplaced(FridaSession session, FridaCause cause) {
// Extension point
}
@Override
public default void sessionRemoved(String sessionId, FridaCause cause) {
// Extension point
}
@Override
public default void sessionSelected(FridaSession session, FridaCause cause) {
// Extension point
}
@Override
public default void processAdded(FridaProcess process, FridaCause cause) {
// Extension point
}
@Override
public default void processReplaced(FridaProcess process, FridaCause cause) {
// Extension point
}
@Override
public default void processRemoved(String processId, FridaCause cause) {
// Extension point
}
@Override
public default void processSelected(FridaProcess process, FridaCause cause) {
// Extension point
}
@Override
public default void processStarted(FridaProcess process, FridaCause cause) {
// Extension point
}
@Override
public default void processExited(FridaProcess process, FridaCause cause) {
// Extension point
}
@Override
public default void threadCreated(FridaThread thread, FridaCause cause) {
// Extension point
}
@Override
public default void threadReplaced(FridaThread thread, FridaCause cause) {
// Extension point
}
@Override
public default void threadStateChanged(FridaThread thread, FridaState state, FridaCause cause,
FridaReason reason) {
// Extension point
}
@Override
public default void threadExited(FridaThread thread, FridaProcess process, FridaCause cause) {
// Extension point
}
@Override
public default void threadSelected(FridaThread thread, FridaFrame frame, FridaCause cause) {
// Extension point
}
@Override
public default void moduleLoaded(FridaProcess process, FridaModuleInfo info, int index,
FridaCause cause) {
// Extension point
}
@Override
public default void moduleReplaced(FridaProcess process, FridaModuleInfo info, int index,
FridaCause cause) {
// Extension point
}
@Override
public default void moduleUnloaded(FridaProcess process, FridaModuleInfo info, int index,
FridaCause cause) {
// Extension point
}
@Override
public default void regionAdded(FridaProcess process, FridaRegionInfo info, int index,
FridaCause cause) {
// Extension point
}
@Override
public default void regionReplaced(FridaProcess process, FridaRegionInfo info, int index,
FridaCause cause) {
// Extension point
}
@Override
public default void regionRemoved(FridaProcess process, FridaRegionInfo info, int index,
FridaCause cause) {
// Extension point
}
@Override
public default void consoleOutput(String output, int mask) {
// Extension point
}
@Override
public default void promptChanged(String prompt) {
// Extension point
}
}

View File

@ -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.
*/
package agent.frida.manager;
public class FridaExport {
private FridaModule module;
private String type;
private String name;
private String address;
public FridaExport(FridaModule module) {
this.module = module;
}
public FridaModule getModule() {
return module;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type == null ? "" : type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? "" : name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address == null ? "" : address;
}
}

View File

@ -0,0 +1,62 @@
/* ###
* 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 agent.frida.manager;
public class FridaFileSpec {
private String path;
private Long offset;
private Long size;
public FridaFileSpec(String path) {
this.path = path;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getFilename() {
int index = path.lastIndexOf("/");
return index < 0 ? path : path.substring(index+1);
}
public String getDirectory() {
int index = path.lastIndexOf("/");
return index < 0 ? path : path.substring(0, index);
}
public Long getOffset() {
return offset;
}
public void setOffset(Long offset) {
this.offset = offset;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
}

View File

@ -0,0 +1,66 @@
/* ###
* 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 agent.frida.manager;
import java.util.Map;
import com.google.gson.JsonElement;
public class FridaFrame {
private String address;
private int frameId;
private FridaFunction function;
public FridaFrame(Map<String, JsonElement> map, int n) {
this.address = map.get("address").getAsString();
this.frameId = n;
this.function = new FridaFunction(map);
}
public int getFrameID() {
return frameId;
}
public String getModuleName() {
return function.getModuleName();
}
public String getFunctionName() {
return function.getFunctionName();
}
public String getFileName() {
return function.getFileName();
}
public long getLineNumber() {
return function.getLineNumber();
}
public FridaFunction getFunction() {
return function;
}
public String getAddress() {
return address;
}
public Long getPC() {
return Long.decode(address);
}
}

View File

@ -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 agent.frida.manager;
import java.util.Map;
import com.google.gson.JsonElement;
public class FridaFunction {
private String moduleName;
private String functionName;
private String fileName;
private Long lineNumber;
public FridaFunction(Map<String, JsonElement> map) {
this.setModuleName(map.get("moduleName").getAsString());
this.setFunctionName(map.get("name").getAsString());
this.setFileName(map.get("fileName").getAsString());
this.setLineNumber(map.get("lineNumber").getAsLong());
}
public String getModuleName() {
return moduleName;
}
public void setModuleName(String moduleName) {
this.moduleName = moduleName;
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Long getLineNumber() {
return lineNumber;
}
public void setLineNumber(Long lineNumber) {
this.lineNumber = lineNumber;
}
}

View File

@ -0,0 +1,75 @@
/* ###
* 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 agent.frida.manager;
public class FridaImport {
private FridaModule module;
private String type;
private String slot = "";
private String mod;
private String name = "";
private String address;
public FridaImport(FridaModule module) {
this.module = module;
}
public FridaModule getModule() {
return module;
}
public String getSlot() {
return slot;
}
public void setSlot(String slot) {
this.slot = slot == null ? "" : slot;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type == null ? "" : type;
}
public String getMod() {
return mod;
}
public void setMod(String module) {
this.mod = module == null ? "" : module;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? "" : name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address == null ? "" : address;
}
}

View File

@ -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 agent.frida.manager;
public class FridaKernelMemoryRegionInfo extends FridaMemoryRegionInfo {
public FridaKernelMemoryRegionInfo() {
super(null);
}
public String getFilePath() {
return "";
}
public Long getFileOffset() {
return 0L;
}
public Long getFileSize() {
return 0L;
}
}

View File

@ -0,0 +1,28 @@
/* ###
* 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 agent.frida.manager;
public class FridaKernelModule extends FridaModule {
public FridaKernelModule() {
super(null);
}
public String getPath() {
return "";
}
}

View File

@ -0,0 +1,396 @@
/* ###
* 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 agent.frida.manager;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.tuple.Pair;
import com.google.gson.JsonElement;
import agent.frida.frida.FridaClient.DebugStatus;
import agent.frida.manager.impl.FridaManagerImpl;
public interface FridaManager extends AutoCloseable {
static FridaManager newInstance() {
return new FridaManagerImpl();
}
/**
* Launch Frida
*
* @param args cmd plus args
* @return a future which completes when Frida is ready to accept commands
*/
CompletableFuture<Void> start(String[] args);
/**
* Terminate Frida
*/
void terminate();
/**
* Check if Frida is alive
*
* Note this is not about the state of inferiors in Frida. If the Frida controlling process is
* alive, Frida is alive.
*
* @return true if Frida is alive, false otherwise
*/
boolean isRunning();
/**
* Add a listener for Frida's state
*
* @see #getState()
* @param listener the listener to add
*/
void addStateListener(FridaStateListener listener);
/**
* Remove a listener for Frida's state
*
* @see #getState()
* @param listener the listener to remove
*/
void removeStateListener(FridaStateListener listener);
/**
* Add a listener for events on processes
*
* @param listener the listener to add
*/
void addEventsListener(FridaEventsListener listener);
/**
* Remove a listener for events on inferiors
*
* @param listener the listener to remove
*/
void removeEventsListener(FridaEventsListener listener);
/**
* Get a thread by its Frida-assigned ID
*
* Frida numbers its threads using a global counter. These IDs are unrelated to the OS-assigned
* TID. This method can retrieve a thread by its ID no matter which inferior it belongs to.
*
* @param process wrapper for Frida pointer
* @param id the Frida-asigned thread ID
* @return a handle to the thread, if it exists
*/
FridaThread getThread(FridaProcess process, String id);
/**
* Get an process by its Frida-assigned ID
*
* Frida numbers processes incrementally. All inferiors and created and destroyed by the user.
* See {@link #addProcess()}.
*
* @param session wrapper for Frida pointer
* @param id the process ID
* @return a handle to the process, if it exists
*/
FridaProcess getProcess(FridaSession session, String id);
/**
* Get an session by its Frida-assigned ID
*
* Frida numbers processes incrementally. All inferiors and created and destroyed by the user.
* See {@link #addSession()}.
*
* @param id the process ID
* @return a handle to the process, if it exists
*/
FridaSession getSession(String id);
/**
* Get an session by its Frida-assigned ID
*
* Frida numbers processes incrementally. All inferiors and created and destroyed by the user.
* See {@link #addSession()}.
*
* @param process wrapper for Frida pointer
* @param id the process ID
* @return a handle to the process, if it exists
*/
FridaModule getModule(FridaProcess process, String id);
/**
* Get an memory region by its Frida-assigned ID
*
* Frida numbers processes incrementally. All inferiors and created and destroyed by the user.
* See {@link #addSession()}.
*
* @param process wrapper for Frida pointer
* @param id the process ID
* @return a handle to the process, if it exists
*/
FridaMemoryRegionInfo getMemoryRegion(FridaProcess process, String id);
/**
* Get all threads known to the manager
*
* This does not ask Frida to lists its known threads. Rather it returns a read-only view of the
* manager's understanding of the current threads based on its tracking of Frida events.
*
* @param process wrapper for Frida pointer
* @return a map of Frida-assigned thread IDs to corresponding thread handles
*/
Map<String, FridaThread> getKnownThreads(FridaProcess process);
/**
* Get all processes known to the manager
*
* This does not ask Frida to list its processes. Rather it returns a read-only view of the
* manager's understanding of the current processes based on its tracking of Frida events.
*
* @param session wrapper for Frida pointer
* @return a map of process IDs to corresponding process handles
*/
Map<String, FridaProcess> getKnownProcesses(FridaSession session);
/**
* Get all sessions known to the manager
*
* This does not ask Frida to list its processes. Rather it returns a read-only view of the
* manager's understanding of the current inferiors based on its tracking of Frida events.
*
* @return a map of session IDs to corresponding session handles
*/
Map<String, FridaSession> getKnownSessions();
/**
* Get all sessions known to the manager
*
* This does not ask Frida to list its processes. Rather it returns a read-only view of the
* manager's understanding of the current inferiors based on its tracking of Frida events.
*
* @param process wrapper for Frida pointer
* @return a map of session IDs to corresponding session handles
*/
Map<String, FridaModule> getKnownModules(FridaProcess process);
/**
* Get all sessions known to the manager
*
* This does not ask Frida to list its processes. Rather it returns a read-only view of the
* manager's understanding of the current inferiors based on its tracking of Frida events.
*
* @param process wrapper for Frida pointer
* @return a map of session IDs to corresponding session handles
*/
Map<String, FridaMemoryRegionInfo> getKnownRegions(FridaProcess process);
/**
* Get the state of the Frida session
*
* In all-stop mode, if any thread is running, Frida is said to be in the running state and is
* unable to process commands. Otherwise, if all threads are stopped, then Frida is said to be in
* the stopped state and can accept and process commands. This manager has not been tested in
* non-stop mode.
*
* @return the state
*/
FridaState getState();
/**
* Add a process
*
* @return a future which completes with the handle to the new process
*/
CompletableFuture<FridaProcess> addProcess();
/**
* Remove a process
*
* @param process the process to remove
* @return a future which completes then Frida has executed the command
*/
CompletableFuture<Void> removeProcess(FridaProcess process);
/**
* Add a session
*
* @return a future which completes with the handle to the new process
*/
CompletableFuture<FridaSession> addSession();
/**
* Execute an arbitrary CLI command, printing output to the CLI console
*
* @param command the command to execute
* @return a future that completes when Frida has executed the command
*/
CompletableFuture<Void> console(String command);
/**
* Execute an arbitrary CLI command, capturing its console output
*
* The output will not be printed to the CLI console.
*
* @param command the command to execute
* @return a future that completes with the captured output when Frida has executed the command
*/
CompletableFuture<String> consoleCapture(String command);
/**
* List Frida's threads
*
* @param process wrapper for Frida pointer
* @return a future that completes with a map of process IDs to process handles
*/
CompletableFuture<Void> listThreads(FridaProcess process);
/**
* List Frida's processes
*
* @param session wrapper for Frida pointer
* @return a future that completes with a map of process IDs to process handles
*/
CompletableFuture<Map<String, FridaProcess>> listProcesses(FridaSession session);
/**
* List the available processes on target
*
* @return a future that completes with a list of PIDs
*/
CompletableFuture<List<Pair<String, String>>> listAvailableProcesses();
/**
* List Frida's sessions
*
* @return a future that completes with a map of session IDs to session handles
*/
CompletableFuture<Map<String, FridaSession>> listSessions();
/**
* List Frida's stack frames
*
* @param thread wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
CompletableFuture<Map<String, FridaFrame>> listStackFrames(FridaThread thread);
/**
* List Frida's stack frame registers
*
* @param thread wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
CompletableFuture<Map<String, String>> listRegisters(FridaThread thread);
/**
* List Frida's modules
*
* @param process wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Void> listModules(FridaProcess process);
/**
* List Frida's module sections
*
* @param module wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Map<String, FridaSection>> listModuleSections(FridaModule module);
/**
* List Frida's module symbols
*
* @param module wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Map<String, FridaSymbol>> listModuleSymbols(FridaModule module);
/**
* List Frida's module imports
*
* @param module wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Map<String, FridaImport>> listModuleImports(FridaModule module);
/**
* List Frida's module exports
*
* @param module wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Map<String, FridaExport>> listModuleExports(FridaModule module);
/**
* List Frida's memory
*
* @param process wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Void> listMemory(FridaProcess process);
/**
* List Frida's heap memory
*
* @param process wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Void> listHeapMemory(FridaProcess process);
/**
* List Frida's heap memory
*
* @param process wrapper for Frida pointer
* @return a future that completes with a map of session IDs to session handles
*/
public CompletableFuture<Void> setExceptionHandler(FridaProcess process);
CompletableFuture<?> attach(String pid);
CompletableFuture<?> launch(String fileName, List<String> args);
CompletableFuture<?> launch(Map<String, ?> args);
/********** NEEDED FOR TESTING ************/
/**
* Returns the current process
*
* @return the current process
*/
FridaProcess currentProcess();
CompletableFuture<Void> waitForPrompt();
<T> CompletableFuture<T> execute(FridaCommand<? extends T> cmd);
DebugStatus processEvent(FridaEvent<?> evt);
DebugStatus getStatus();
void updateState(FridaSession session);
FridaTarget getCurrentTarget();
void setCurrentTarget(FridaTarget target);
CompletableFuture<Void> getSessionAttributes(FridaSession session);
void enableDebugger(FridaSession session, int port);
}

View File

@ -0,0 +1,99 @@
/* ###
* 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 agent.frida.manager;
public class FridaMemoryRegionInfo {
private FridaProcess process;
private String protection;
private String rangeAddress;
private Long rangeSize;
private FridaFileSpec fileSpec;
public FridaMemoryRegionInfo(FridaProcess process) {
this.process = process;
}
public String getProtection() {
return protection;
}
public void setProtection(String protection) {
this.protection = protection;
}
public String getRangeAddress() {
return rangeAddress;
}
public void setRangeAddress(String rangeAddress) {
this.rangeAddress = rangeAddress;
}
public Long getRangeSize() {
return rangeSize;
}
public void setRangeSize(Long rangeSize) {
this.rangeSize = rangeSize;
}
public String getFilePath() {
return fileSpec.getPath();
}
public void setFilePath(String filePath) {
this.fileSpec = new FridaFileSpec(filePath);
}
public Long getFileOffset() {
return fileSpec.getOffset();
}
public void setFileOffset(Long fileOffset) {
fileSpec.setOffset(fileOffset);
}
public Long getFileSize() {
return fileSpec.getSize();
}
public void setFileSize(Long fileSize) {
fileSpec.setSize(fileSize);
}
public Boolean isReadable() {
return protection.contains("r");
}
public Boolean isWritable() {
return protection.contains("w");
}
public Boolean isExecutable() {
return protection.contains("x");
}
public FridaFileSpec getFileSpec() {
return fileSpec;
}
public FridaProcess getProcess() {
return process;
}
}

View File

@ -0,0 +1,66 @@
/* ###
* 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 agent.frida.manager;
public class FridaModule {
private FridaProcess process;
private String name;
private String path;
private String rangeAddress;
private Long rangeSize;
public FridaModule(FridaProcess process) {
this.process = process;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? "" : name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path == null ? "" : path;
}
public String getRangeAddress() {
return rangeAddress;
}
public void setRangeAddress(String rangeAddress) {
this.rangeAddress = rangeAddress == null ? "0" : rangeAddress;
}
public Long getRangeSize() {
return rangeSize;
}
public void setRangeSize(Long rangeSize) {
this.rangeSize = rangeSize;
}
public FridaProcess getProcess() {
return process;
}
}

View File

@ -0,0 +1,43 @@
/* ###
* 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 agent.frida.manager;
import com.sun.jna.NativeLong;
public enum FridaOS {
FRIDA_OS_WINDOWS("windows"),
FRIDA_OS_MACOS("macos"),
FRIDA_OS_LINUX("linux"),
FRIDA_OS_IOS("ios"),
FRIDA_OS_ANDROID("android"),
FRIDA_OS_FREEBSD("freebsd"),
FRIDA_OS_QNX("qnx");
final String str;
FridaOS(String str) {
this.str = str;
}
@Override
public String toString() {
return str;
}
public static FridaOS getOS(NativeLong state) {
return FridaOS.values()[state.intValue()];
}
}

View File

@ -0,0 +1,35 @@
/* ###
* 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 agent.frida.manager;
import com.sun.jna.Pointer;
public class FridaPointer {
Pointer pointer;
public FridaPointer(Pointer pointer) {
this.pointer = pointer;
}
public Pointer getPointer() {
return pointer;
}
public void setPointer(Pointer pointer) {
this.pointer = pointer;
}
}

View File

@ -0,0 +1,96 @@
/* ###
* 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 agent.frida.manager;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
public class FridaProcess extends FridaPointer {
private NativeLong pid;
private String name;
private FridaSession session;
private String identifier;
public FridaProcess(Pointer process, NativeLong pid) {
super(process);
this.pid = pid;
}
// Kernel-case
public FridaProcess() {
super(null);
this.pid = new NativeLong(-1L);
}
public Long getPID() {
return pid.longValue();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public FridaSession getSession() {
return session;
}
public void setSession(FridaSession session) {
this.session = session;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public FridaTarget getTarget() {
return session.getTarget();
}
public FridaError resume() {
FridaTarget target = getTarget();
FridaError error = new FridaError();
target.resumeProcess(pid, error);
return error;
}
public FridaError destroy() {
FridaTarget target = getTarget();
FridaError error = new FridaError();
target.killProcess(pid, error);
return error;
}
public FridaError kill() {
FridaTarget target = getTarget();
FridaError error = new FridaError();
target.killProcess(pid, error);
return error;
}
public String getDescription() {
return getName();
}
}

View File

@ -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 agent.frida.manager;
public interface FridaReason {
/**
* Reasons other than those given by Frida
*/
enum Reasons implements FridaReason {
/**
* No reason was given
*/
NONE,
/**
* A reason was given, but the manager does not understand it
*/
UNKNOWN;
@Override
public String desc() {
return "Unknown";
}
}
static FridaReason getReason(String info) {
return Reasons.UNKNOWN;
}
public String desc();
}

View File

@ -0,0 +1,37 @@
/* ###
* 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 agent.frida.manager;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
public class FridaScript extends FridaPointer {
private NativeLong signal;
public FridaScript(Pointer script) {
super(script);
}
public NativeLong getSignal() {
return signal;
}
public void setSignal(NativeLong signal) {
this.signal = signal;
}
}

View File

@ -0,0 +1,98 @@
/* ###
* 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 agent.frida.manager;
public class FridaSection {
private FridaModule module;
private String protection;
private String rangeAddress;
private Long rangeSize;
private FridaFileSpec fileSpec;
public FridaSection(FridaModule module) {
this.module = module;
}
public String getProtection() {
return protection;
}
public void setProtection(String protection) {
this.protection = protection;
}
public String getRangeAddress() {
return rangeAddress;
}
public void setRangeAddress(String rangeAddress) {
this.rangeAddress = rangeAddress;
}
public Long getRangeSize() {
return rangeSize;
}
public void setRangeSize(Long rangeSize) {
this.rangeSize = rangeSize;
}
public String getFilePath() {
return fileSpec.getPath();
}
public void setFilePath(String filePath) {
this.fileSpec = new FridaFileSpec(filePath);
}
public Long getFileOffset() {
return fileSpec.getOffset();
}
public void setFileOffset(Long fileOffset) {
fileSpec.setOffset(fileOffset);
}
public Long getFileSize() {
return fileSpec.getSize();
}
public void setFileSize(Long fileSize) {
fileSpec.setSize(fileSize);
}
public Boolean isReadable() {
return protection.contains("r");
}
public Boolean isWritable() {
return protection.contains("w");
}
public Boolean isExecutable() {
return protection.contains("x");
}
public FridaFileSpec getFileSpec() {
return fileSpec;
}
public FridaModule getModule() {
return module;
}
}

View File

@ -0,0 +1,75 @@
/* ###
* 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 agent.frida.manager;
import java.util.Map;
import com.sun.jna.Pointer;
import agent.frida.frida.FridaEng;
public class FridaSession extends FridaPointer {
private FridaTarget target;
private FridaProcess process;
private Map<String, Object> attributes;
public FridaSession(Pointer session, FridaProcess process) {
super(session);
this.process = process;
}
public FridaTarget getTarget() {
return target;
}
public void setTarget(FridaTarget target) {
this.target = target;
}
public FridaProcess getProcess() {
return process;
}
public void setProcess(FridaProcess process) {
this.process = process;
}
public String getAttribute(String key) {
Object object = attributes.get(key);
if (object == null) {
return "N/A";
}
return object.toString();
}
public void setAttributes(Map<String, Object> attributes) {
this.attributes = attributes;
}
public FridaError detach() {
FridaError error = new FridaError();
FridaEng.detach(this, error);
return error;
}
public FridaError resume() {
FridaError error = new FridaError();
FridaEng.resume(this, error);
return error;
}
}

View File

@ -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 agent.frida.manager;
import com.sun.jna.NativeLong;
import ghidra.util.Msg;
public enum FridaState {
FRIDA_THREAD_RUNNING("running"),
FRIDA_THREAD_STOPPED("stopped"),
FRIDA_THREAD_WAITING("waiting"),
FRIDA_THREAD_UNINTERRUPTIBLE("uninterruptible"),
FRIDA_THREAD_HALTED("halted");
final String str;
FridaState(String str) {
this.str = str;
}
@Override
public String toString() {
return str;
}
public static FridaState byValue(String val) {
for (FridaState state : values()) {
if (state.str.equals(val)) {
return state;
}
}
Msg.warn(FridaState.class, "No such value: " + val);
return null;
}
public static FridaState getState(NativeLong state) {
return FridaState.values()[state.intValue()];
}
}

View File

@ -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 agent.frida.manager;
import ghidra.util.TriConsumer;
/**
* A listener for changes in Frida's state
*/
public interface FridaStateListener extends TriConsumer<FridaState, FridaState, FridaCause> {
/**
* The state has changed because of the given cause
*
* @param state the new state
* @param cause the reason for the change
*/
void stateChanged(FridaState state, FridaCause cause);
@Override
default void accept(FridaState oldVal, FridaState newVal, FridaCause u) {
stateChanged(newVal, u);
}
}

View File

@ -0,0 +1,84 @@
/* ###
* 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 agent.frida.manager;
public class FridaSymbol {
private FridaModule module;
private boolean isGlobal;
private String type;
private String sectionId;
private String name;
private String address;
private Long size;
public FridaSymbol(FridaModule module) {
this.module = module;
}
public FridaModule getModule() {
return module;
}
public boolean isGlobal() {
return isGlobal;
}
public void setGlobal(boolean isGlobal) {
this.isGlobal = isGlobal;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSectionId() {
return sectionId;
}
public void setSectionId(String section) {
this.sectionId = section;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
}

View File

@ -0,0 +1,80 @@
/* ###
* 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 agent.frida.manager;
import java.math.BigInteger;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import agent.frida.frida.FridaEng;
public class FridaTarget extends FridaPointer {
private String name;
private FridaSession session;
public FridaTarget(Pointer localDevice) {
super(localDevice);
}
public Long getID() {
return 0L;
}
public FridaSession attach(BigInteger processId, FridaError error) {
return FridaEng.attach(this, new NativeLong(processId.longValue()), error);
}
public FridaSession launchSimple(String[] argArr, String[] envArr, String workingDir) {
return FridaEng.spawn(this, argArr[0], new FridaError());
}
public FridaSession launch(String fileName, String[] argArr, String[] envArr, String pathSTDIN,
String pathSTDOUT, String pathSTDERR, String workingDir, long createFlags, boolean stopAtEntry,
FridaError error) {
return FridaEng.spawn(this, fileName, error);
}
public void resumeProcess(NativeLong processId, FridaError error) {
FridaEng.resume(this, processId, error);
}
public void killProcess(NativeLong processId, FridaError error) {
FridaEng.kill(this, processId, error);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public FridaSession getSession() {
return session;
}
public void setSession(FridaSession session) {
this.session = session;
}
public FridaProcess getProcess() {
return session == null ? null : session.getProcess();
}
}

View File

@ -0,0 +1,61 @@
/* ###
* 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 agent.frida.manager;
public class FridaThread {
private FridaProcess process;
private Long tid;
private FridaState state;
private FridaContext context;
public FridaThread(FridaProcess process) {
this.process = process;
}
public Long getTid() {
return tid;
}
public void setTid(Long tid) {
this.tid = tid;
}
public FridaState getState() {
return state;
}
public void setState(FridaState state) {
this.state = state;
}
public FridaContext getContext() {
return context;
}
public void setContext(FridaContext context) {
this.context = context;
}
public FridaProcess getProcess() {
return process;
}
public String getDescription() {
return Long.toString(getTid());
}
}

View File

@ -0,0 +1,47 @@
/* ###
* 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 agent.frida.manager;
import com.google.gson.JsonElement;
import com.sun.jna.NativeLong;
public class FridaValue {
private String key;
private String value;
public FridaValue(String key, String value) {
this.key = key;
this.value = value;
}
public String getValue() {
return value;
}
public String getKey() {
return key;
}
public int getByteSize() {
return 8;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -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 agent.frida.manager.cmd;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import agent.frida.frida.FridaEng;
import agent.frida.manager.FridaCommand;
import agent.frida.manager.FridaEvent;
import agent.frida.manager.FridaScript;
import agent.frida.manager.FridaState;
import agent.frida.manager.evt.FridaCommandDoneEvent;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.util.Msg;
/**
* A base class for interacting with specific Frida commands
*
* @param <T> the type of object "returned" by the command
*/
public abstract class AbstractFridaCommand<T> implements FridaCommand<T> {
protected final FridaManagerImpl manager;
private String name;
private FridaScript script;
/**
* Construct a new command to be executed by the given manager
*
* @param manager the manager to execute the command
*/
protected AbstractFridaCommand(FridaManagerImpl manager) {
this.manager = manager;
}
@Override
public boolean validInState(FridaState state) {
return true; // With dual interpreters, shouldn't have to worry.
}
@Override
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending) {
if (evt instanceof FridaCommandDoneEvent) {
if (pending.getCommand().equals(((FridaCommandDoneEvent) evt).getCmd())) {
return true;
}
}
return false;
}
@Override
public T complete(FridaPendingCommand<?> pending) {
return null;
}
@Override
public void invoke() {
// Nothing
}
@Override
public void parse(String result, Object data) {
JsonObject jobj = JsonParser.parseString(result).getAsJsonObject();
if (jobj.has("type")) {
String type = jobj.get("type").getAsString();
if (type.equals("error")) {
String desc = jobj.get("description").getAsString();
manager.getEventListeners().fire.consoleOutput(desc+"\n", 0);
Msg.error(this, desc);
return;
}
}
if (jobj.has("payload")) {
Object object = jobj.get("payload");
if (!(object instanceof JsonPrimitive)) {
manager.getEventListeners().fire.consoleOutput(object+" not a String\n", 0);
Msg.error(this, object);
return;
}
String value = ((JsonPrimitive) object).getAsString();
if (!value.startsWith("{")) {
manager.getEventListeners().fire.consoleOutput(object+"\n", 0);
return;
}
JsonElement res = JsonParser.parseString(value);
if (res instanceof JsonObject) {
JsonObject keyValue = (JsonObject) res;
JsonElement element = keyValue.get("key");
if (element != null) {
res = keyValue.get("value");
String key = element.getAsString();
if (!key.equals(name)) {
manager.getEventListeners().fire.consoleOutput(res+"\n", 0);
return;
}
} else {
manager.getEventListeners().fire.consoleOutput(object+"\n", 0);
}
} else {
manager.getEventListeners().fire.consoleOutput(object+"\n", 0);
}
if (res.equals("[]")) {
Msg.error(this, "nothing returned for "+this);
}
if (res instanceof JsonArray) {
JsonArray arr = (JsonArray) res;
for (JsonElement l : arr) {
parseSpecifics(l);
}
} else {
parseSpecifics(res);
}
}
cleanup();
}
public void parseSpecifics(JsonElement object) {
// Nothing
}
public void cleanup() {
if (script != null) {
FridaEng.unloadScript(script);
FridaEng.disconnectSignal(script, script.getSignal());
FridaEng.unref(script);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setScript(FridaScript script) {
this.script = script;
}
}

View File

@ -0,0 +1,37 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.manager.FridaManager;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaManager#addProcess()}
*/
public class FridaAddProcessCommand extends AbstractFridaCommand<FridaProcess> {
public FridaAddProcessCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public FridaProcess complete(FridaPendingCommand<?> pending) {
// Not apparent this is needed
return null;
}
}

View File

@ -0,0 +1,37 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.manager.FridaManager;
import agent.frida.manager.FridaSession;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaManager#addSession()}
*/
public class FridaAddSessionCommand extends AbstractFridaCommand<FridaSession> {
public FridaAddSessionCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public FridaSession complete(FridaPendingCommand<?> pending) {
// Not apparent this is needed
return null;
}
}

View File

@ -0,0 +1,91 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.math.BigInteger;
import java.util.LinkedHashSet;
import java.util.Set;
import agent.frida.frida.FridaClient;
import agent.frida.frida.FridaThreadInfo;
import agent.frida.manager.*;
import agent.frida.manager.evt.*;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaTarget#attach(BigInteger processId, FridaError error)}
*/
public class FridaAttachCommand extends AbstractFridaCommand<Set<FridaThread>> {
private FridaProcessCreatedEvent created = null;
private boolean completed = false;
private String key;
private int keyType = 0;
private boolean wait = true;
private boolean async = false;
public FridaAttachCommand(FridaManagerImpl manager, String key) {
this(manager, key, true, false);
this.keyType = 0;
}
public FridaAttachCommand(FridaManagerImpl manager, String key, boolean wait) {
this(manager, key, wait, false);
this.keyType = 1;
}
public FridaAttachCommand(FridaManagerImpl manager, String key, boolean wait, boolean async) {
super(manager);
this.key = key;
this.wait = wait;
this.async = async;
this.keyType = 2;
}
@Override
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending) {
if (evt instanceof AbstractFridaCompletedCommandEvent && pending.getCommand().equals(this)) {
completed = true;
}
else if (evt instanceof FridaProcessCreatedEvent) {
created = (FridaProcessCreatedEvent) evt;
}
else if (evt instanceof FridaThreadCreatedEvent) {
pending.claim(evt);
}
else if (evt instanceof FridaStoppedEvent) {
pending.claim(evt);
}
return completed && (created != null);
}
@Override
public Set<FridaThread> complete(FridaPendingCommand<?> pending) {
Set<FridaThread> threads = new LinkedHashSet<>();
for (FridaThreadCreatedEvent adds : pending.findAllOf(FridaThreadCreatedEvent.class)) {
FridaThreadInfo info = adds.getInfo();
threads.add(info.thread);
}
return threads;
}
@Override
public void invoke() {
FridaClient client = manager.getClient();
client.attachProcess(client.getLocalServer(), keyType, key, wait, async);
// NB: manager.waitForEventEx - embedded in attach
}
}

View File

@ -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 agent.frida.manager.cmd;
import agent.frida.manager.FridaCommand;
/**
* Exception generated by the default "^error" handler
*/
public class FridaCommandError extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -812711163503400398L;
private final Object info;
/**
* Construct an error with the given details
*
* @param info the detail information
* @param cmd source of error
*/
public FridaCommandError(Object info, FridaCommand<?> cmd) {
super(cmd + " caused '" + info + "'");
this.info = info;
}
/**
* Construct an error with the given message
*
* @param message the message
*/
public FridaCommandError(String message) {
super(message);
this.info = null;
}
/**
* Get the details, if present
*
* @return the details, or null
*/
public Object getInfo() {
return info;
}
}

View File

@ -0,0 +1,79 @@
/* ###
* 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 agent.frida.manager.cmd;
import com.google.gson.JsonElement;
import agent.frida.manager.FridaEvent;
import agent.frida.manager.FridaManager;
import agent.frida.manager.evt.*;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaManager#console(String)} and similar
*/
public class FridaConsoleExecCommand extends AbstractFridaCommand<String> {
public enum Output {
CONSOLE, CAPTURE;
}
private String command;
private Output to;
public FridaConsoleExecCommand(FridaManagerImpl manager, String command, Output to) {
super(manager);
this.command = command;
this.to = to;
}
@Override
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending) {
if (evt instanceof AbstractFridaCompletedCommandEvent && pending.getCommand().equals(this)) {
return true;
}
else if (evt instanceof FridaConsoleOutputEvent && to == Output.CAPTURE) {
pending.steal(evt);
}
return false;
}
@Override
public String complete(FridaPendingCommand<?> pending) {
if (to == Output.CONSOLE) {
return null;
}
StringBuilder builder = new StringBuilder();
for (FridaConsoleOutputEvent out : pending.findAllOf(FridaConsoleOutputEvent.class)) {
builder.append(out.getOutput());
}
return builder.toString();
}
@Override
public void invoke() {
if (!command.isEmpty()) {
manager.loadScript(this, "exec", command);
}
}
@Override
public void parseSpecifics(JsonElement element) {
String res = element.isJsonPrimitive() ? element.getAsString() : "";
manager.getClient().processEvent(new FridaConsoleOutputEvent(0, res));
//manager.getEventListeners().fire.consoleOutput(object+"\n", 0);
}
}

View File

@ -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 agent.frida.manager.cmd;
import agent.frida.manager.*;
import agent.frida.manager.evt.*;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.util.Msg;
/**
* Implementation of {@link FridaManager#continue()}
*/
public class FridaContinueCommand extends AbstractFridaCommand<Void> {
private FridaProcess process;
public FridaContinueCommand(FridaManagerImpl manager, FridaProcess process) {
super(manager);
this.process = process;
}
public FridaContinueCommand(FridaManagerImpl manager) {
super(manager);
this.process = null;
}
@Override
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending) {
if (evt instanceof AbstractFridaCompletedCommandEvent && pending.getCommand().equals(this)) {
pending.claim(evt);
boolean b = evt instanceof FridaCommandErrorEvent ||
!pending.findAllOf(FridaRunningEvent.class).isEmpty();
return b;
}
else if (evt instanceof FridaRunningEvent) {
// Event happens no matter which interpreter received the command
pending.claim(evt);
boolean b = !pending.findAllOf(AbstractFridaCompletedCommandEvent.class).isEmpty();
return b;
}
return false;
}
@Override
public Void complete(FridaPendingCommand<?> pending) {
return null;
}
@Override
public void invoke() {
FridaError res = process == null ? manager.getCurrentSession().resume() : process.resume();
if (!res.success()) {
Msg.error(this, res.getDescription());
}
TargetExecutionState state = TargetExecutionState.RUNNING;
manager.getClient().processEvent(new FridaStateChangedEvent(process, state));
}
}

View File

@ -0,0 +1,38 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.frida.FridaClient;
import agent.frida.manager.evt.FridaProcessExitedEvent;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaManager#destroy()}
*/
public class FridaDestroyCommand extends AbstractFridaCommand<Void> {
public FridaDestroyCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public void invoke() {
FridaClient client = manager.getClient();
// NB: process the event before terminating
manager.processEvent(new FridaProcessExitedEvent(0));
client.terminateCurrentProcess(manager.getCurrentTarget());
//client.detachCurrentProcess();
}
}

View File

@ -0,0 +1,60 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import agent.frida.frida.FridaClient;
import agent.frida.manager.*;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.util.Msg;
/**
* Implementation of {@link FridaProcess#detach()}
*/
public class FridaDetachCommand extends AbstractFridaCommand<Void> {
private final FridaSession session;
public FridaDetachCommand(FridaManagerImpl manager, FridaSession session) {
super(manager);
this.session = session;
}
@Override
public Void complete(FridaPendingCommand<?> pending) {
FridaProcess process = session.getProcess();
String pid = FridaClient.getId(process);
Map<String, FridaThread> threads = manager.getKnownThreads(process);
List<FridaThread> list= new ArrayList<>();
list.addAll(threads.values());
for (FridaThread thread : list) {
manager.removeThread(pid, FridaClient.getId(thread));
}
manager.getEventListeners().fire.processRemoved(pid, FridaCause.Causes.UNCLAIMED);
return null;
}
@Override
public void invoke() {
FridaError res = session.detach();
if (!res.success()) {
Msg.error(this, res.getDescription());
}
}
}

View File

@ -0,0 +1,69 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.JsonElement;
import agent.frida.manager.FridaSession;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaGetSessionAttributesCommand extends AbstractFridaCommand<Void> {
protected final FridaSession session;
private Map<String, Object> attributes = new HashMap<>();
public FridaGetSessionAttributesCommand(FridaManagerImpl manager, FridaSession session) {
super(manager);
this.session = session;
}
@Override
public void invoke() {
manager.setCurrentSession(session);
manager.loadScript(this, "get_session_attributes",
"var d = {};" +
"d['version'] = Frida.version;" +
"d['heapSize'] = Frida.heapSize;" +
"d['id'] = Process.id;" +
"d['arch'] = Process.arch;" +
"d['os'] = Process.platform;" +
"d['pageSize'] = Process.pageSize;" +
"d['pointerSize'] = Process.pointerSize;" +
"d['codeSigning'] = Process.codeSigningPolicy;" +
"d['debugger'] = Process.isDebuggerAttached();" +
"d['runtime'] = Script.runtime;" +
"d['kernel'] = Kernel.available;" +
"if (Kernel.available) {" +
" d['kbase'] = Kernel.base;" +
" d['kPageSize'] = Kernel.pageSize;" +
"}" +
"result = d;");
session.setAttributes(attributes);
}
@Override
public void parseSpecifics(JsonElement element) {
attributes = new HashMap<>();
for (Entry<String, JsonElement> entry : element.getAsJsonObject().entrySet()) {
attributes.put(entry.getKey(), entry.getValue().getAsString());
}
}
}

View File

@ -0,0 +1,70 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.io.*;
import java.util.Map;
import com.google.gson.JsonElement;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.util.Msg;
public class FridaInterceptFunctionCommand extends AbstractFridaCommand<Void> {
private String address;
private Map<String, ?> arguments;
public FridaInterceptFunctionCommand(FridaManagerImpl manager, String address, Map<String, ?> arguments) {
super(manager);
this.address = address;
this.arguments = arguments;
}
@Override
public void invoke() {
String cmd = "Interceptor.attach(ptr(" + address + "), {";
try {
String onEnter = (String) arguments.get("OnEnter");
FileInputStream fis;
if (!onEnter.isEmpty()) {
fis = new FileInputStream(new File(onEnter));
byte[] bytes = fis.readAllBytes();
String str = new String(bytes);
cmd += str + ",";
}
String onLeave = (String) arguments.get("OnLeave");
if (!onLeave.isEmpty()) {
fis = new FileInputStream(new File(onLeave));
byte[] bytes = fis.readAllBytes();
String str = new String(bytes);
cmd += str + ",";
}
cmd = cmd.substring(0, cmd.length()-1) + "});";
}
catch (IOException e) {
e.printStackTrace();
return;
}
manager.loadPermanentScript(this, (String) arguments.get("Name"), cmd);
}
@Override
public void parseSpecifics(JsonElement element) {
Msg.info(this, element.getAsString());
}
}

View File

@ -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 agent.frida.manager.cmd;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.evt.FridaProcessExitedEvent;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaProcess#kill()}
*/
public class FridaKillCommand extends AbstractFridaCommand<Void> {
public FridaKillCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public void invoke() {
FridaClient client = manager.getClient();
// NB: process the event before terminating
manager.processEvent(new FridaProcessExitedEvent(0));
client.terminateCurrentProcess(manager.getCurrentTarget());
//client.detachCurrentProcess();
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import agent.frida.frida.FridaClient;
import agent.frida.manager.*;
import agent.frida.manager.evt.AbstractFridaCompletedCommandEvent;
import agent.frida.manager.evt.FridaProcessCreatedEvent;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaProcess#launch(String)}
*/
public class FridaLaunchProcessCommand extends AbstractFridaCommand<FridaThread> {
private FridaProcessCreatedEvent created = null;
private boolean completed = false;
private String fileName;
private List<String> args;
private List<String> envp;
private List<String> pathsIO;
private String wdir;
private long flags;
private boolean stopAtEntry;
public FridaLaunchProcessCommand(FridaManagerImpl manager, String fileName, List<String> args) {
this(manager, fileName, args, null, null, "", 0L, true);
}
public FridaLaunchProcessCommand(FridaManagerImpl manager, String fileName, List<String> args,
List<String> envp,
List<String> pathsIO, String workingDirectory, long flags, boolean stopAtEntry) {
super(manager);
this.fileName = fileName;
this.args = args == null ? new ArrayList<>() : args;
this.envp = envp == null ? new ArrayList<>() : envp;
this.pathsIO = pathsIO;
if (pathsIO == null) {
this.pathsIO = new ArrayList<>();
this.pathsIO.add("");
this.pathsIO.add("");
this.pathsIO.add("");
}
this.wdir = workingDirectory;
this.flags = flags;
this.stopAtEntry = stopAtEntry;
}
@Override
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending) {
if (evt instanceof AbstractFridaCompletedCommandEvent && pending.getCommand().equals(this)) {
completed = true;
}
else if (evt instanceof FridaProcessCreatedEvent) {
created = (FridaProcessCreatedEvent) evt;
}
return completed && (created != null);
}
@Override
public FridaThread complete(FridaPendingCommand<?> pending) {
return manager.getCurrentThread();
}
@Override
public void invoke() {
FridaClient client = manager.getClient();
//client.createProcess(client.getLocalServer(), fileName);
client.createProcess(client.getLocalServer(), fileName, args, envp, pathsIO, wdir, flags,
stopAtEntry);
}
}

View File

@ -0,0 +1,124 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import agent.frida.frida.FridaClient;
import agent.frida.frida.FridaClient.DebugCreateFlags;
import agent.frida.manager.*;
import agent.frida.manager.evt.AbstractFridaCompletedCommandEvent;
import agent.frida.manager.evt.FridaProcessCreatedEvent;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaTarget#launch(String)}
*/
public class FridaLaunchProcessWithOptionsCommand extends AbstractFridaCommand<FridaThread> {
private FridaProcessCreatedEvent created = null;
private boolean completed = false;
private String fileName;
private List<String> args;
private List<String> envp;
private List<String> pathsIO;
private String wdir;
private long flags;
private boolean stopAtEntry;
public FridaLaunchProcessWithOptionsCommand(FridaManagerImpl manager, Map<String, ?> args) {
super(manager);
this.fileName = (String) args.get("File");
String argstr = (String) args.get("Args");
this.args = argstr.equals("") ? new ArrayList<String>() : Arrays.asList(argstr.split(" "));
String envstr = (String) args.get("Env");
this.envp = envstr.equals("") ? new ArrayList<String>() : Arrays.asList(envstr.split(" "));
this.pathsIO = new ArrayList<>();
this.pathsIO.add((String)args.get("STDIN"));
this.pathsIO.add((String)args.get("STDOUT"));
this.pathsIO.add((String)args.get("STDERR"));
this.wdir = (String) args.get("Dir");
this.flags = DebugCreateFlags.LAUNCH_DEFAULT.getMask();
this.stopAtEntry = false;
if ((boolean) args.get("Exec")) {
flags |= DebugCreateFlags.LAUNCH_EXEC.getMask();
}
if ((boolean) args.get("BreakOnLaunch")) {
flags |= DebugCreateFlags.LAUNCH_DEBUG.getMask();
}
if ((boolean) args.get("BreakOnEntry")) {
flags |= DebugCreateFlags.LAUNCH_STOP_AT_ENTRY.getMask();
stopAtEntry = true;
}
if ((boolean) args.get("ASLR")) {
flags |= DebugCreateFlags.LAUNCH_DISABLE_ASLR.getMask();
}
if ((boolean) args.get("STDIO")) {
flags |= DebugCreateFlags.LAUNCH_DISABLE_STDIO.getMask();
}
if ((boolean) args.get("NewTTY")) {
flags |= DebugCreateFlags.LAUNCH_IN_TTY.getMask();
}
if ((boolean) args.get("Shell")) {
flags |= DebugCreateFlags.LAUNCH_IN_SHELL.getMask();
}
if ((boolean) args.get("NewGroup")) {
flags |= DebugCreateFlags.LAUNCH_IN_SEP_GROUP.getMask();
}
if ((boolean) args.get("ExitRace")) {
flags |= DebugCreateFlags.LAUNCH_DONT_SET_EXIT_STATUS.getMask();
}
if ((boolean) args.get("Detach")) {
flags |= DebugCreateFlags.LAUNCH_DETACH_ON_ERROR.getMask();
}
if ((boolean) args.get("ExpandArgs")) {
flags |= DebugCreateFlags.LAUNCH_SHELL_EXPAND_ARGS.getMask();
}
if ((boolean) args.get("CloseTTY")) {
flags |= DebugCreateFlags.LAUNCH_CLOSE_TTY_ON_EXIT.getMask();
}
if ((boolean) args.get("Inherit")) {
flags |= DebugCreateFlags.LAUNCH_INHERIT_FROM_PARENT.getMask();
}
}
@Override
public boolean handle(FridaEvent<?> evt, FridaPendingCommand<?> pending) {
if (evt instanceof AbstractFridaCompletedCommandEvent && pending.getCommand().equals(this)) {
completed = true;
}
else if (evt instanceof FridaProcessCreatedEvent) {
created = (FridaProcessCreatedEvent) evt;
}
return completed && (created != null);
}
@Override
public FridaThread complete(FridaPendingCommand<?> pending) {
return manager.getCurrentThread();
}
@Override
public void invoke() {
FridaClient client = manager.getClient();
client.createProcess(client.getLocalServer(), fileName, args, envp, pathsIO, wdir, flags,
stopAtEntry);
}
}

View File

@ -0,0 +1,51 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import agent.frida.frida.FridaEng;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListAvailableProcessesCommand
extends AbstractFridaCommand<List<Pair<String, String>>> {
private List<FridaProcess> processList;
public FridaListAvailableProcessesCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public List<Pair<String, String>> complete(FridaPendingCommand<?> pending) {
List<Pair<String, String>> result = new ArrayList<>();
for (FridaProcess p : processList) {
result.add(new ImmutablePair<String,String>(Long.toString(p.getPID()), p.getName()));
}
return result;
}
@Override
public void invoke() {
processList = FridaEng.enumerateProcesses(manager.getCurrentTarget());
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.manager.FridaMemoryRegionInfo;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListHeapMemoryRegionsCommand extends AbstractFridaCommand<Void> {
private FridaProcess process;
private List<FridaMemoryRegionInfo> regions = new ArrayList<>();
public FridaListHeapMemoryRegionsCommand(FridaManagerImpl manager, FridaProcess process) {
super(manager);
this.process = process;
}
/*
@Override
public List<FridaMemoryRegionInfo> complete(FridaPendingCommand<?> pending) {
return memoryRegions;
}
*/
@Override
public void invoke() {
manager.loadScript(this, "list_malloc_ranges",
"result = Process.enumerateMallocRanges('---');");
for (FridaMemoryRegionInfo region : regions) {
manager.addMemoryRegionIfAbsent(process, region);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaMemoryRegionInfo region = new FridaMemoryRegionInfo(process);
JsonObject memDetails = element.getAsJsonObject();
region.setRangeAddress(memDetails.get("base").getAsString());
region.setRangeSize(memDetails.get("size").getAsLong());
regions.add(region);
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.manager.FridaKernelMemoryRegionInfo;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListKernelMemoryRegionsCommand extends AbstractFridaCommand<Void> {
private List<FridaKernelMemoryRegionInfo> regions = new ArrayList<>();
public FridaListKernelMemoryRegionsCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public void invoke() {
manager.loadScript(this, "list_ranges",
"result = Kernel.enumerateRanges('---');");
for (FridaKernelMemoryRegionInfo region : regions) {
manager.addKernelMemoryRegionIfAbsent(region);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaKernelMemoryRegionInfo region = new FridaKernelMemoryRegionInfo();
JsonObject memDetails = element.getAsJsonObject();
region.setRangeAddress(memDetails.get("base").getAsString());
region.setRangeSize(memDetails.get("size").getAsLong());
region.setProtection(memDetails.get("protection").getAsString());
regions.add(region);
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.manager.FridaKernelModule;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListKernelModulesCommand extends AbstractFridaCommand<Void> {
private List<FridaKernelModule> modules = new ArrayList<>();
public FridaListKernelModulesCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public void invoke() {
manager.loadScript(this, "list_modules",
"result = Kernel.enumerateModules();");
for (FridaKernelModule module : modules) {
manager.addKernelModuleIfAbsent(module);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaKernelModule module = new FridaKernelModule();
JsonObject modDetails = element.getAsJsonObject();
module.setName(modDetails.get("name").getAsString());
module.setRangeAddress(modDetails.get("base").getAsString());
module.setRangeSize(modDetails.get("size").getAsLong());
modules.add(module);
}
}

View File

@ -0,0 +1,62 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.manager.FridaMemoryRegionInfo;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListMemoryRegionsCommand extends AbstractFridaCommand<Void> {
private FridaProcess process;
private List<FridaMemoryRegionInfo> regions = new ArrayList<>();
public FridaListMemoryRegionsCommand(FridaManagerImpl manager, FridaProcess process) {
super(manager);
this.process = process;
}
@Override
public void invoke() {
manager.loadScript(this, "list_ranges",
"result = Process.enumerateRanges('---');");
for (FridaMemoryRegionInfo region : regions) {
manager.addMemoryRegionIfAbsent(process, region);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaMemoryRegionInfo region = new FridaMemoryRegionInfo(process);
JsonObject memDetails = element.getAsJsonObject();
region.setRangeAddress(memDetails.get("base").getAsString());
region.setRangeSize(memDetails.get("size").getAsLong());
region.setProtection(memDetails.get("protection").getAsString());
JsonObject memFile = (JsonObject) memDetails.get("file");
if (memFile != null) {
region.setFilePath(memFile.get("path").getAsString());
region.setFileOffset(memFile.get("offset").getAsLong());
region.setFileSize(memFile.get("size").getAsLong());
}
regions.add(region);
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaExport;
import agent.frida.manager.FridaModule;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListModuleExportsCommand extends AbstractFridaCommand<Map<String, FridaExport>> {
protected final FridaModule module;
private Map<String, FridaExport> exports;
public FridaListModuleExportsCommand(FridaManagerImpl manager, FridaModule module) {
super(manager);
this.module = module;
}
@Override
public Map<String, FridaExport> complete(FridaPendingCommand<?> pending) {
return exports;
}
@Override
public void invoke() {
exports = new HashMap<>();
manager.loadScript(this, "list_module_Exports",
"result = Process.findModuleByAddress('"+module.getRangeAddress()+"').enumerateExports();");
for (FridaExport imp : exports.values()) {
exports.put(FridaClient.getId(imp), imp);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaExport export = new FridaExport(module);
JsonObject symDetails = element.getAsJsonObject();
export.setAddress(symDetails.get("address").getAsString());
export.setName(symDetails.get("name").getAsString());
export.setType(symDetails.get("type").getAsString());
exports.put(FridaClient.getId(export), export);
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaImport;
import agent.frida.manager.FridaModule;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListModuleImportsCommand extends AbstractFridaCommand<Map<String, FridaImport>> {
protected final FridaModule module;
private Map<String, FridaImport> imports;
public FridaListModuleImportsCommand(FridaManagerImpl manager, FridaModule module) {
super(manager);
this.module = module;
}
@Override
public Map<String, FridaImport> complete(FridaPendingCommand<?> pending) {
return imports;
}
@Override
public void invoke() {
imports = new HashMap<>();
manager.loadScript(this, "list_module_imports",
"result = Process.findModuleByAddress('"+module.getRangeAddress()+"').enumerateImports();");
for (FridaImport imp : imports.values()) {
imports.put(FridaClient.getId(imp), imp);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaImport imp = new FridaImport(module);
JsonObject symDetails = element.getAsJsonObject();
imp.setName(symDetails.get("name").getAsString());
imp.setAddress(symDetails.get("address").getAsString());
imp.setType(symDetails.get("type").getAsString());
imp.setMod(symDetails.get("module").getAsString());
JsonElement slot = symDetails.get("slot");
if (slot != null) {
imp.setSlot(slot.getAsString());
}
imports.put(FridaClient.getId(imp), imp);
}
}

View File

@ -0,0 +1,69 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaModule;
import agent.frida.manager.FridaSection;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListModuleSectionsCommand extends AbstractFridaCommand<Map<String, FridaSection>> {
protected final FridaModule module;
private Map<String, FridaSection> sections;
public FridaListModuleSectionsCommand(FridaManagerImpl manager, FridaModule module) {
super(manager);
this.module = module;
}
@Override
public Map<String, FridaSection> complete(FridaPendingCommand<?> pending) {
return sections;
}
@Override
public void invoke() {
sections = new HashMap<>();
manager.loadScript(this, "list_module_ranges",
"result = Process.findModuleByAddress('"+module.getRangeAddress()+"').enumerateRanges('---');");
for (FridaSection section : sections.values()) {
sections.put(FridaClient.getId(section), section);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaSection section = new FridaSection(module);
JsonObject sectionDetails = element.getAsJsonObject();
section.setRangeAddress((String) sectionDetails.get("base").getAsString());
section.setRangeSize((Long) sectionDetails.get("size").getAsLong());
section.setProtection((String) sectionDetails.get("protection").getAsString());
JsonObject sectionFile = (JsonObject) sectionDetails.get("file");
if (sectionFile != null) {
section.setFilePath(sectionFile.get("path").getAsString());
section.setFileOffset(sectionFile.get("offset").getAsLong());
section.setFileSize(sectionFile.get("size").getAsLong());
}
sections.put(FridaClient.getId(section), section);
}
}

View File

@ -0,0 +1,70 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaModule;
import agent.frida.manager.FridaSymbol;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListModuleSymbolsCommand extends AbstractFridaCommand<Map<String, FridaSymbol>> {
protected final FridaModule module;
private Map<String, FridaSymbol> symbols;
public FridaListModuleSymbolsCommand(FridaManagerImpl manager, FridaModule module) {
super(manager);
this.module = module;
}
@Override
public Map<String, FridaSymbol> complete(FridaPendingCommand<?> pending) {
return symbols;
}
@Override
public void invoke() {
symbols = new HashMap<>();
manager.loadScript(this, "list_module_symbols",
"result = Process.findModuleByAddress('"+module.getRangeAddress()+"').enumerateSymbols();");
for (FridaSymbol symbol : symbols.values()) {
symbols.put(FridaClient.getId(symbol), symbol);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaSymbol symbol = new FridaSymbol(module);
JsonObject symDetails = element.getAsJsonObject();
symbol.setAddress(symDetails.get("address").getAsString());
symbol.setSize(symDetails.get("size").getAsLong());
symbol.setType(symDetails.get("type").getAsString());
symbol.setName(symDetails.get("name").getAsString());
symbol.setGlobal(symDetails.get("isGlobal").getAsBoolean());
Object sect = symDetails.get("section");
if (sect != null) {
JsonObject section = (JsonObject) sect;
symbol.setSectionId(section.get("id").getAsString());
}
symbols.put(FridaClient.getId(symbol), symbol);
}
}

View File

@ -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.
*/
package agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.manager.FridaModule;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListModulesCommand extends AbstractFridaCommand<Void> {
protected final FridaProcess process;
private List<FridaModule> modules = new ArrayList<>();
public FridaListModulesCommand(FridaManagerImpl manager, FridaProcess process) {
super(manager);
this.process = process;
}
@Override
public void invoke() {
manager.loadScript(this, "list_modules",
"result = Process.enumerateModules();");
for (FridaModule module : modules) {
manager.addModuleIfAbsent(process, module);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaModule module = new FridaModule(process);
JsonObject modDetails = element.getAsJsonObject();
module.setName(modDetails.get("name").getAsString());
module.setPath(modDetails.get("path").getAsString());
module.setRangeAddress(modDetails.get("base").getAsString());
module.setRangeSize(modDetails.get("size").getAsLong());
modules.add(module);
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.util.*;
import agent.frida.frida.FridaClient;
import agent.frida.manager.*;
import agent.frida.manager.FridaCause.Causes;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaManager#listProcesses(FridaSession session)}
*/
public class FridaListProcessesCommand extends AbstractFridaCommand<Map<String, FridaProcess>> {
private Map<String, FridaProcess> updatedProcesses;
private FridaSession session;
public FridaListProcessesCommand(FridaManagerImpl manager, FridaSession session) {
super(manager);
this.session = session;
}
@Override
public Map<String, FridaProcess> complete(FridaPendingCommand<?> pending) {
Map<String, FridaProcess> allProcesses = manager.getKnownProcesses(session);
Set<String> cur = allProcesses.keySet();
for (String id : updatedProcesses.keySet()) {
if (cur.contains(id)) {
continue; // Do nothing, we're in sync
}
manager.addProcessIfAbsent(session, updatedProcesses.get(id));
}
String sessionId = FridaClient.getId(session);
for (String id : new ArrayList<>(cur)) {
if (updatedProcesses.containsKey(id)) {
continue; // Do nothing, we're in sync
}
manager.removeProcess(sessionId, id, Causes.UNCLAIMED);
}
return allProcesses;
}
@Override
public void invoke() {
FridaProcess p = session.getProcess();
updatedProcesses = new HashMap<>();
updatedProcesses.put(FridaClient.getId(p), p);
}
}

View File

@ -0,0 +1,45 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.Map;
import com.google.gson.JsonElement;
import agent.frida.manager.FridaContext;
import agent.frida.manager.FridaThread;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListRegistersCommand extends AbstractFridaCommand<Map<String, String>> {
protected final FridaThread thread;
private Map<String, String> result;
public FridaListRegistersCommand(FridaManagerImpl manager, FridaThread thread) {
super(manager);
this.thread = thread;
}
@Override
public Map<String, String> complete(FridaPendingCommand<?> pending) {
return result;
}
@Override
public void invoke() {
FridaContext context = thread.getContext();
result = context.getChildren();
}
}

View File

@ -0,0 +1,61 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import agent.frida.manager.FridaCause.Causes;
import agent.frida.manager.FridaManager;
import agent.frida.manager.FridaSession;
import agent.frida.manager.impl.FridaManagerImpl;
/**
* Implementation of {@link FridaManager#listSessions()}
*/
public class FridaListSessionsCommand extends AbstractFridaCommand<Map<String, FridaSession>> {
private Map<String, FridaSession> updatedSessions;
public FridaListSessionsCommand(FridaManagerImpl manager) {
super(manager);
}
@Override
public Map<String, FridaSession> complete(FridaPendingCommand<?> pending) {
Map<String, FridaSession> allSessions = manager.getKnownSessions();
Set<String> cur = allSessions.keySet();
for (String id : updatedSessions.keySet()) {
if (cur.contains(id)) {
continue; // Do nothing, we're in sync
}
manager.addSessionIfAbsent(updatedSessions.get(id));
}
for (String id : new ArrayList<>(cur)) {
if (updatedSessions.containsKey(id)) {
continue; // Do nothing, we're in sync
}
manager.removeSession(id, Causes.UNCLAIMED);
}
return allSessions;
}
@Override
public void invoke() {
updatedSessions = manager.getClient().listSessions();
}
}

View File

@ -0,0 +1,62 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.frida.FridaClient;
import agent.frida.manager.FridaFrame;
import agent.frida.manager.FridaThread;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaListStackFramesCommand extends AbstractFridaCommand<Map<String, FridaFrame>> {
protected final FridaThread thread;
private Map<String, FridaFrame> frames = new HashMap<>();
private int frameCount = 0;
public FridaListStackFramesCommand(FridaManagerImpl manager, FridaThread thread) {
super(manager);
this.thread = thread;
}
@Override
public Map<String, FridaFrame> complete(FridaPendingCommand<?> pending) {
return frames;
}
@Override
public void invoke() {
manager.loadScript(this, "list_stack_frames",
"result = Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress);");
//"console.log(JSON.stringify(Thread.backtrace(this.context, Backtracer.ACCURATE)));");
}
@Override
public void parseSpecifics(JsonElement element) {
JsonObject jobj = element.getAsJsonObject();
Map<String, JsonElement> map = new HashMap<>();
for (Entry<String, JsonElement> l : jobj.entrySet()) {
map.put(l.getKey(), l.getValue());
}
FridaFrame frame = new FridaFrame(map, frameCount++);
frames.put(FridaClient.getId(frame), frame);
}
}

View File

@ -0,0 +1,65 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import agent.frida.manager.FridaContext;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.FridaState;
import agent.frida.manager.FridaThread;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.util.Msg;
public class FridaListThreadsCommand extends AbstractFridaCommand<Void> {
protected final FridaProcess process;
private List<FridaThread> threads = new ArrayList<>();
public FridaListThreadsCommand(FridaManagerImpl manager, FridaProcess process) {
super(manager);
this.process = process;
}
@Override
public void invoke() {
manager.loadScript(this, "list_threads", "result = Process.enumerateThreads();");
for (FridaThread thread : threads) {
manager.addThreadIfAbsent(process, thread);
}
}
@Override
public void parseSpecifics(JsonElement element) {
FridaThread thread = new FridaThread(process);
if (element.isJsonPrimitive()) {
Msg.error(this, element.getAsString());
return;
}
JsonObject modDetails = element.getAsJsonObject();
thread.setTid(modDetails.get("id").getAsLong());
String state = modDetails.get("state").getAsString();
thread.setState(FridaState.byValue(state));
JsonObject cpuContext = (JsonObject) modDetails.get("context");
thread.setContext(new FridaContext(cpuContext));
threads.add(thread);
}
}

View File

@ -0,0 +1,211 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import agent.frida.manager.FridaCause;
import agent.frida.manager.FridaCommand;
import agent.frida.manager.FridaEvent;
import agent.frida.manager.evt.AbstractFridaCompletedCommandEvent;
import agent.frida.manager.evt.FridaCommandErrorEvent;
/**
* A command queued on the frida manager
*
* A {@link FridaCommand} is queued by wrapping it in a {@link FridaPendingCommand} and submitting it
* to the manager implementation's executor. This object also keep track of claimed/stolen events
* and provides convenience methods for sifting through them.
*
* @param <T> the type "returned" by the command
*/
public class FridaPendingCommand<T> extends CompletableFuture<T> implements FridaCause {
private final FridaCommand<? extends T> cmd;
private final Set<FridaEvent<?>> evts = new LinkedHashSet<>();
/**
* Wrap a command for execution
*
* @param cmd the command
*/
public FridaPendingCommand(FridaCommand<? extends T> cmd) {
this.cmd = cmd;
}
/**
* Get the command being executed
*
* @return cmd
*/
public FridaCommand<? extends T> getCommand() {
return cmd;
}
/**
* Finish the execution of this command
*/
public void finish() {
//Msg.trace(this, "Finishing " + cmd);
try {
T result = cmd.complete(this);
complete(result);
}
catch (Throwable e) {
completeExceptionally(e);
}
}
/**
* Handle an event
*
* This gives the command implementation the first chance to claim or steal an event
*
* @param evt the event
* @return true if the command is ready to be completed
*/
public boolean handle(FridaEvent<?> evt) {
return cmd.handle(evt, this);
}
/**
* Claim an event
*
* This stores the event for later retrieval and processing.
*
* @param evt the event
*/
public void claim(FridaEvent<?> evt) {
evt.claim(this);
evts.add(evt);
}
/**
* Steal an event
*
* This stores the event for later retrieval and processing.
*
* @param evt the event
*/
public void steal(FridaEvent<?> evt) {
claim(evt);
evt.steal();
}
/**
* Assume a single event of particular type was claimed/stolen, and get that event
*
* @param cls the type of the event
* @return the event cast to the type
* @throws IllegalStateException if more than one event was claimed/stolen
* @throws ClassCastException if the event cannot be cast to the given type
*/
public <E extends FridaEvent<?>> E castSingleEvent(Class<E> cls) {
if (evts.size() != 1) {
throw new IllegalStateException("Command did not claim exactly one event");
}
return cls.cast(evts.iterator().next());
}
/**
* Find the first claimed/stolen event of a given type
*
* @param cls the type of the event
* @return the event cast to the type
* @throws IllegalStateException if no event of the given type was claimed/stolen
*/
public <E extends FridaEvent<?>> E findFirstOf(Class<E> cls) {
for (FridaEvent<?> evt : evts) {
if (cls.isAssignableFrom(evt.getClass())) {
return cls.cast(evt);
}
}
throw new IllegalStateException("Command did not claim any " + cls);
}
/**
* Find all events claimed/stolen of a given type
*
* @param cls the type of the events
* @return the list of events cast to the type
*/
public <E extends FridaEvent<?>> List<E> findAllOf(Class<E> cls) {
List<E> found = new ArrayList<>();
for (FridaEvent<?> evt : evts) {
if (cls.isAssignableFrom(evt.getClass())) {
found.add(cls.cast(evt));
}
}
return found;
}
/**
* Assume exactly one event of the given type was claimed/stolen, and get that event
*
* @param cls the type of the event
* @return the event cast to the type
* @throws IllegalStateException if more than one event matches
*/
public <E extends FridaEvent<?>> E findSingleOf(Class<E> cls) {
List<E> found = findAllOf(cls);
if (found.size() != 1) {
throw new IllegalStateException(
"Command did not claim exactly one " + cls + ". Have " + evts);
}
return found.get(0);
}
/**
* Check that the command completed with one of the given results
*
* {@link FridaCommandErrorEvent} need not be listed. This method will handle it as a special
* case already. To avoid the special treatment, list it explicitly.
*
* @param classes the completion type to accept
* @return the completion event, cast to the greatest common subclass
*/
@SafeVarargs
public final <E extends AbstractFridaCompletedCommandEvent> E checkCompletion(
Class<E>... classes) {
AbstractFridaCompletedCommandEvent completion =
findSingleOf(AbstractFridaCompletedCommandEvent.class);
// Allow query for exact class to override error interpretation
for (Class<E> cls : classes) {
if (cls == completion.getClass()) {
return cls.cast(completion);
}
}
if (completion instanceof FridaCommandErrorEvent) {
throw new FridaCommandError(completion.getInfo(), cmd);
}
for (Class<E> cls : classes) {
if (cls.isAssignableFrom(completion.getClass())) {
return cls.cast(completion);
}
}
throw new IllegalStateException(
"Command completed with " + completion + ", not any of " + Arrays.asList(classes));
}
@Override
public String toString() {
return super.toString() + "(" + cmd + ")";
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.nio.ByteBuffer;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
//import com.sun.jna.Pointer;
//import com.sun.jna.ptr.PointerByReference;
import com.google.gson.JsonElement;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.program.model.address.Address;
import ghidra.util.NumericUtilities;
public class FridaReadKernelMemoryCommand extends AbstractFridaCommand<RangeSet<Long>> {
private final Address addr;
private final ByteBuffer buf;
private final int len;
public FridaReadKernelMemoryCommand(FridaManagerImpl manager, Address addr, ByteBuffer buf, int len) {
super(manager);
this.addr = addr;
this.buf = buf;
this.len = len;
}
@Override
public RangeSet<Long> complete(FridaPendingCommand<?> pending) {
RangeSet<Long> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closedOpen(addr.getOffset(), addr.getOffset() + len));
return rangeSet;
}
@Override
public void invoke() {
manager.loadScript(this, "read_memory",
"var buf = Kernel.readByteArray(ptr(0x"+addr+")"+len+"); result = hexdump(buf, {header:false});");
}
@Override
public void parseSpecifics(JsonElement element) {
String payload = element.getAsString();
String[] lines = payload.split("\n");
int n = 0;
for (String l : lines) {
String[] split = l.split(" ");
byte[] bytes = NumericUtilities.convertStringToBytes(split[1]);
for (int i = 0; i < 16; i++) {
buf.put(n+i, bytes[i]);
}
n += 16;
}
}
}

View File

@ -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 agent.frida.manager.cmd;
import java.nio.ByteBuffer;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
//import com.sun.jna.Pointer;
//import com.sun.jna.ptr.PointerByReference;
import com.google.gson.JsonElement;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.program.model.address.Address;
import ghidra.util.NumericUtilities;
public class FridaReadMemoryCommand extends AbstractFridaCommand<RangeSet<Long>> {
private final Address addr;
private final ByteBuffer buf;
private final int len;
public FridaReadMemoryCommand(FridaManagerImpl manager, Address addr, ByteBuffer buf, int len) {
super(manager);
this.addr = addr;
this.buf = buf;
this.len = len;
}
@Override
public RangeSet<Long> complete(FridaPendingCommand<?> pending) {
RangeSet<Long> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closedOpen(addr.getOffset(), addr.getOffset() + len));
return rangeSet;
}
@Override
public void invoke() {
manager.loadScript(this, "read_memory",
"var buf = ptr(0x"+addr+").readByteArray("+len+"); result = hexdump(buf, {header:false});");
}
@Override
public void parseSpecifics(JsonElement element) {
String payload = element.getAsString();
String[] lines = payload.split("\n");
int n = 0;
for (String l : lines) {
String[] split = l.split(" ");
byte[] bytes = NumericUtilities.convertStringToBytes(split[1]);
for (int i = 0; i < 16; i++) {
buf.put(n+i, bytes[i]);
}
n += 16;
}
}
}

View File

@ -0,0 +1,35 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.manager.FridaSession;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaRemoveProcessCommand extends AbstractFridaCommand<Void> {
private FridaSession session;
private String id;
public FridaRemoveProcessCommand(FridaManagerImpl manager, FridaSession session, String id) {
super(manager);
this.session = session;
this.id = id;
}
@Override
public void invoke() {
manager.removeProcess(manager.getProcess(session, id));
}
}

View File

@ -0,0 +1,65 @@
/* ###
* 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 agent.frida.manager.cmd;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;
import com.google.gson.JsonElement;
import agent.frida.manager.impl.FridaManagerImpl;
import ghidra.util.Msg;
public class FridaRepeatableWatchMemoryCommand extends AbstractFridaCommand<Void> {
private Map<String, ?> arguments;
public FridaRepeatableWatchMemoryCommand(FridaManagerImpl manager, Map<String, ?> arguments) {
super(manager);
this.arguments = arguments;
}
@Override
public void invoke() {
String addr = (String) arguments.get("Address");
if (!addr.startsWith("0x")) {
addr = "0x" + addr;
}
Long size = (Long) arguments.get("Size");
String cmd;
try {
String onAccess = (String) arguments.get("OnAccess");
FileInputStream fis = new FileInputStream(new File(onAccess));
byte[] bytes = fis.readAllBytes();
String str = new String(bytes);
cmd = str + "; monitorMemory( ptr(" + addr + "), " +size + ");";
}
catch (IOException e) {
e.printStackTrace();
return;
}
manager.loadPermanentScript(this, (String) arguments.get("Name"), cmd);
}
@Override
public void parseSpecifics(JsonElement element) {
Msg.info(this, element.getAsString());
//manager.unloadPermanentScript(getName());
}
}

View File

@ -0,0 +1,46 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.manager.impl.FridaManagerImpl;
import agent.frida.model.iface1.FridaModelTargetActiveScope;
import ghidra.dbg.target.TargetObject;
public class FridaRequestActivationCommand extends AbstractFridaCommand<Void> {
private FridaModelTargetActiveScope activator;
private TargetObject obj;
/**
* Set focus for the current ref
*
* @param manager the manager to execute the command
* @param activator in most cases the root object (must be an ancestor for the ref)
* @param obj the desired object to be made active
*/
public FridaRequestActivationCommand(FridaManagerImpl manager,
FridaModelTargetActiveScope activator,
TargetObject obj) {
super(manager);
this.activator = activator;
this.obj = obj;
}
@Override
public void invoke() {
activator.doRequestActivation(obj);
}
}

View File

@ -0,0 +1,45 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.manager.impl.FridaManagerImpl;
import agent.frida.model.iface1.FridaModelTargetFocusScope;
import ghidra.dbg.target.TargetObject;
public class FridaRequestFocusCommand extends AbstractFridaCommand<Void> {
private FridaModelTargetFocusScope scope;
private TargetObject obj;
/**
* Set focus for the current ref
*
* @param manager the manager to execute the command
* @param scope in most cases the root object (must be an ancestor for the ref)
* @param obj the desired focus
*/
public FridaRequestFocusCommand(FridaManagerImpl manager, FridaModelTargetFocusScope scope,
TargetObject obj) {
super(manager);
this.scope = scope;
this.obj = obj;
}
@Override
public void invoke() {
scope.doRequestFocus(obj);
}
}

View File

@ -0,0 +1,37 @@
/* ###
* 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 agent.frida.manager.cmd;
import agent.frida.manager.FridaProcess;
import agent.frida.manager.impl.FridaManagerImpl;
public class FridaSetExceptionHandlerCommand extends AbstractFridaCommand<Void> {
protected final FridaProcess process;
public FridaSetExceptionHandlerCommand(FridaManagerImpl manager, FridaProcess process) {
super(manager);
this.process = process;
}
@Override
public void invoke() {
manager.loadPermanentScript(this, "set_exception_handler",
"Process.setExceptionHandler(ex => {send(ex, null);});");
}
}

Some files were not shown because too many files have changed in this diff Show More